From fc877c9add9ce79e3f9a476484256425ea2f69f2 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Wed, 13 Mar 2024 12:57:38 +0800 Subject: [PATCH 01/43] Fix Carryall direction on landing --- src/Ext/Aircraft/Body.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index 45186b137f..916dee4c84 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -71,11 +71,9 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) if (pDock || pThis->HasAnyLink()) { auto pBuilding = pDock; + auto pLink = pThis->GetNthLink(0); - if (!pDock) - pBuilding = abstract_cast(pThis->GetNthLink(0)); - - if (pBuilding) + if (auto pBuilding = pDock ? pDock : abstract_cast(pLink)) { auto const pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); int docks = pBuilding->Type->NumberOfDocks; @@ -89,6 +87,8 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) else if (docks > 0 && !pBuildingTypeExt->AircraftDockingDirs[0].empty()) return pBuildingTypeExt->AircraftDockingDirs[0].get(); } + else if (!pThis->Type->AirportBound) + return pLink->PrimaryFacing.Current().GetDir(); } else if (!pThis->Type->AirportBound) { From 50562f3e9dec69f0445e490f07042499972d203c Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Fri, 15 Mar 2024 19:12:55 +0800 Subject: [PATCH 02/43] Remove jumpjet deceleration when crashing onto buildings requested by @reedom114514 Co-Authored-By: Netsu_Negi <71598172+NetsuNegi@users.noreply.github.com> --- CREDITS.md | 4 +++- docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 1 + src/Ext/Aircraft/Body.cpp | 1 - src/Ext/Unit/Hooks.Jumpjet.cpp | 10 +++++++--- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 62c6c64b8b..da8a223005 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -299,7 +299,9 @@ This page lists all the individual contributions to the project by their author. - **FlyStar** - Campaign load screen PCX support - New condition for automatic self-destruction logic when TechnoTypes exist/don't exist -- **NetsuNegi** - Forbidding parallel AI queues by type +- **NetsuNegi** + - Forbidding parallel AI queues by type + - Jumpjet crash speed fix when crashing onto building - **Apollo** - Translucent SHP drawing patches - **ststl** - Customizable ShowTimer priority of superweapons diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 08a0122712..df1c8c2e35 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -142,6 +142,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fix [EIP 00529A14](https://modenc.renegadeprojects.com/Internal_Error/YR#eip_00529A14) when attempting to read `[Header]` section of campaign maps. - Units will no longer rotate its turret under EMP. - Jumpjets will no longer wobble under EMP. +- Removed jumpjet units' deceleration when crashing onto buildings. - Fixed `AmbientDamage` when used with `IsRailgun=yes` being cut off by elevation changes. - Fixed railgun and fire particles being cut off by elevation changes. - Fixed teleport units' (for example CLEG) frozen-still timer being cleared after load game. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 8847e25e45..84dd67d450 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -418,6 +418,7 @@ Vanilla fixes: - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction (by Starkku) - Spawned aircraft now align with the spawner's facing when landing (by Starkku) - Fixed infantries attempted to entering buildings when waypointing together with engineer/agent/occupier/etc (by Trsdy) +- Fixed jumpjet crash speed when crashing onto buildings (by NetsuNegi) Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index 916dee4c84..faa33ed39a 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -70,7 +70,6 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) if (pDock || pThis->HasAnyLink()) { - auto pBuilding = pDock; auto pLink = pThis->GetNthLink(0); if (auto pBuilding = pDock ? pDock : abstract_cast(pLink)) diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index 43cea1aee1..5a837e316a 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -79,7 +79,13 @@ DEFINE_HOOK(0x736EE9, UnitClass_UpdateFiring_FireErrorIsOK, 0x6) return 0; } -DEFINE_HOOK(0x54D208, JumpjetLocomotionClass_ProcessMove_EMPWobble, 0x5) +DEFINE_HOOK(0x54D326, JumpjetLocomotionClass_MovementAI_CrashSpeedFix, 0x6) +{ + GET(JumpjetLocomotionClass*, pThis, ESI); + return pThis->LinkedTo->IsCrashing ? 0x54D350 : 0; +} + +DEFINE_HOOK(0x54D208, JumpjetLocomotionClass_MovementAI_EMPWobble, 0x5) { GET(JumpjetLocomotionClass* const, pThis, ESI); enum { ZeroWobble = 0x54D22C }; @@ -183,8 +189,6 @@ FireError __stdcall JumpjetLocomotionClass_Can_Fire(ILocomotion* pThis) DEFINE_JUMP(VTABLE, 0x7ECDF4, GET_OFFSET(JumpjetLocomotionClass_Can_Fire)); -//TODO : Issue #690 #655 - // Fix initial facing when jumpjet locomotor is being attached DEFINE_HOOK(0x54AE44, JumpjetLocomotionClass_LinkToObject_FixFacing, 0x7) { From 98f3d74819c8c277ae0eac7b424cd5c6dadcb59c Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 17 Mar 2024 19:11:52 +0200 Subject: [PATCH 03/43] Fix ApplyModifiersOnNegativeDamage not working correctly --- src/Ext/WarheadType/Hooks.cpp | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Ext/WarheadType/Hooks.cpp b/src/Ext/WarheadType/Hooks.cpp index 15b4a76143..bb1c4ad5cf 100644 --- a/src/Ext/WarheadType/Hooks.cpp +++ b/src/Ext/WarheadType/Hooks.cpp @@ -183,16 +183,50 @@ DEFINE_HOOK(0x48A4F3, SelectDamageAnimation_NegativeZeroDamage, 0x6) return SkipGameCode; } -DEFINE_HOOK(0x4891AF, GetTotalDamage_NegativeDamageModifiers, 0x6) +#pragma region NegativeDamageModifiers + +namespace NegativeDamageTemp +{ + bool ApplyNegativeDamageModifiers = false; +} + +DEFINE_HOOK(0x4891AF, GetTotalDamage_NegativeDamageModifiers1, 0x6) { enum { ApplyModifiers = 0x4891C6 }; GET(WarheadTypeClass* const, pWarhead, EDI); + GET(int, damage, ESI); auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); - if (pWHExt->ApplyModifiersOnNegativeDamage) + if (damage < 0 && pWHExt->ApplyModifiersOnNegativeDamage) + { + NegativeDamageTemp::ApplyNegativeDamageModifiers = true; return ApplyModifiers; + } return 0; } + +DEFINE_HOOK(0x48922D, GetTotalDamage_NegativeDamageModifiers2, 0x5) +{ + enum { SkipGameCode = 0x489235 }; + + GET(int, damage, ESI); + + if (NegativeDamageTemp::ApplyNegativeDamageModifiers) + { + NegativeDamageTemp::ApplyNegativeDamageModifiers = false; + R->ECX(damage); + + } + else + { + R->ECX(damage < 0 ? 0 : damage); + } + + + return SkipGameCode; +} + +#pragma endregion From 196a4b30a6ef53d4c4dd801f4c4cabce072b763d Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 17 Mar 2024 20:47:49 +0200 Subject: [PATCH 04/43] Add VoicePickup for carryall aircraft --- docs/Fixed-or-Improved-Logics.md | 10 ++++++++++ docs/Whats-New.md | 1 + src/Ext/Techno/Hooks.cpp | 27 +++++++++++++++++++++++++++ src/Ext/TechnoType/Body.cpp | 7 ++++--- src/Ext/TechnoType/Body.h | 5 ++++- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index df1c8c2e35..ee533457e9 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -159,6 +159,16 @@ This page describes all ingame logics that are fixed or improved in Phobos witho ## Aircraft +### Carryall pickup voice + +- It is now possible to override `VoiceMove` for `Carryall=true` aircraft for when commanding it to pick up vehicles by setting `VoicePickup`. + +In `rulesmd.ini`: +```ini +[SOMEAIRCRAFT] ; AircraftType +VoicePickup= ; Sound +``` + ### Fixed spawn distance & spawn height for airstrike / SpyPlane aircraft - It is now possible to have aircraft spawned from `(Elite)AirstrikeTeamType` or `Type=SpyPlane` superweapons to be created at fixed distance from their intended target/destination instead of from edge of the map by setting `SpawnDistanceFromTarget`. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 84dd67d450..65f75c7ae0 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -360,6 +360,7 @@ New: - Allow customizing aircraft landing direction per aircraft or per dock (by Starkku) - Allow animations to play sounds detached from audio event handler (by Starkku) - Game save option when starting campaigns (by Trsdy) +- Carryall pickup voice (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 283774e5d9..e8cd5cf778 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -1,3 +1,4 @@ +#include #include #include "Body.h" @@ -452,3 +453,29 @@ DEFINE_HOOK(0x5F46AE, ObjectClass_Select, 0x7) return 0; } + +DEFINE_HOOK(0x708FC0, TechnoClass_ResponseMove_Pickup, 0x5) +{ + enum { SkipResponse = 0x709015 }; + + GET(TechnoClass*, pThis, ECX); + + if (auto const pAircraft = abstract_cast(pThis)) + { + if (pAircraft->Type->Carryall && pAircraft->HasAnyLink() && + pAircraft->Destination && (pAircraft->Destination->AbstractFlags & AbstractFlags::Foot) != AbstractFlags::None) + { + auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pAircraft->Type); + + if (pTypeExt->VoicePickup.isset()) + { + pThis->QueueVoice(pTypeExt->VoicePickup.Get()); + + R->EAX(1); + return SkipResponse; + } + } + } + + return 0; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 725c5e870d..41cba2c52a 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -159,8 +159,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Ammo_DeployUnlockMinimumAmount.Read(exINI, pSection, "Ammo.DeployUnlockMinimumAmount"); this->Ammo_DeployUnlockMaximumAmount.Read(exINI, pSection, "Ammo.DeployUnlockMaximumAmount"); - this->VoiceCantDeploy.Read(exINI, pSection, "VoiceCantDeploy"); - this->AutoDeath_Behavior.Read(exINI, pSection, "AutoDeath.Behavior"); this->AutoDeath_VanishAnimation.Read(exINI, pSection, "AutoDeath.VanishAnimation"); this->AutoDeath_OnAmmoDepletion.Read(exINI, pSection, "AutoDeath.OnAmmoDepletion"); @@ -180,6 +178,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->EVA_Sold.Read(exINI, pSection, "EVA.Sold"); this->VoiceCreated.Read(exINI, pSection, "VoiceCreated"); + this->VoicePickup.Read(exINI, pSection, "VoicePickup"); + this->CameoPriority.Read(exINI, pSection, "CameoPriority"); this->WarpOut.Read(exINI, pSection, "WarpOut"); @@ -274,6 +274,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->SpawnHeight.Read(exINI, pSection, "SpawnHeight"); this->LandingDir.Read(exINI, pSection, "LandingDir"); + // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -455,7 +456,6 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->Ammo_AutoDeployMaximumAmount) .Process(this->Ammo_DeployUnlockMinimumAmount) .Process(this->Ammo_DeployUnlockMaximumAmount) - .Process(this->VoiceCantDeploy) .Process(this->AutoDeath_Behavior) .Process(this->AutoDeath_VanishAnimation) @@ -476,6 +476,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->EVA_Sold) .Process(this->VoiceCreated) + .Process(this->VoicePickup) .Process(this->WarpOut) .Process(this->WarpIn) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 384b7caf3c..03df138158 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -60,7 +60,6 @@ class TechnoTypeExt Valueable Ammo_AutoDeployMaximumAmount; Valueable Ammo_DeployUnlockMinimumAmount; Valueable Ammo_DeployUnlockMaximumAmount; - NullableIdx VoiceCantDeploy; Nullable AutoDeath_Behavior; Nullable AutoDeath_VanishAnimation; @@ -81,6 +80,7 @@ class TechnoTypeExt NullableIdx EVA_Sold; NullableIdx VoiceCreated; + NullableIdx VoicePickup; // Used by carryalls instead of VoiceMove if set. Nullable WarpOut; Nullable WarpIn; @@ -307,6 +307,9 @@ class TechnoTypeExt , EVA_Sold {} , EnemyUIName {} + , VoiceCreated {} + , VoicePickup {} + , ForceWeapon_Naval_Decloaked { -1 } , ForceWeapon_Cloaked { -1 } , ForceWeapon_Disguised { -1 } From dba2ec30bb9c205ecf550582a4b37725fcb10f49 Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 17 Mar 2024 21:32:12 +0200 Subject: [PATCH 05/43] Fix LandingDir for non-AirportBound aircraft --- src/Ext/Aircraft/Body.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index faa33ed39a..f1672abc78 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -66,8 +66,6 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) if (auto pOwner = pThis->SpawnOwner) return pOwner->PrimaryFacing.Current().GetDir(); - bool isAirportBound = true; - if (pDock || pThis->HasAnyLink()) { auto pLink = pThis->GetNthLink(0); @@ -89,18 +87,12 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) else if (!pThis->Type->AirportBound) return pLink->PrimaryFacing.Current().GetDir(); } - else if (!pThis->Type->AirportBound) - { - isAirportBound = false; - } int landingDir = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType())->LandingDir.Get((int)poseDir); - if (isAirportBound) - return static_cast(Math::clamp(landingDir, 0, 255)); - else if (landingDir < 0) + if (!pThis->Type->AirportBound && landingDir < 0) return pThis->PrimaryFacing.Current().GetDir(); - return poseDir; + return static_cast(Math::clamp(landingDir, 0, 255)); } From 397e687269973c579fd04414919aea056bef6404 Mon Sep 17 00:00:00 2001 From: Starkku Date: Mon, 18 Mar 2024 14:12:27 +0200 Subject: [PATCH 06/43] Option to have `Grinding.Weapon` require accumulated credits from grinding --- docs/Fixed-or-Improved-Logics.md | 2 ++ docs/Whats-New.md | 1 + src/Ext/Building/Body.cpp | 8 ++++++-- src/Ext/Building/Body.h | 2 ++ src/Ext/BuildingType/Body.cpp | 2 ++ src/Ext/BuildingType/Body.h | 2 ++ 6 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index ee533457e9..218a1ea69b 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -320,6 +320,7 @@ ConsideredVehicle= ; boolean - `Grinding.PlayDieSound` controls if the units' `DieSound` and `VoiceDie` are played when entering the grinder. Default to `yes`. - `Grinding.Sound` is a sound played by when object is grinded by the building. If not set, defaults to `[AudioVisual]`->`EnterGrinderSound`. - `Grinding.Weapon` is a weapon fired at the building & by the building when it grinds an object. Will only be fired if at least weapon's `ROF` amount of frames have passed since it was last fired. + - `Grinding.Weapon.RequiredCredits` can be set to have the weapon require accumulated credits from grinding to fire. Accumulated credits for this purpose are reset every time when the weapon fires. - For money string indication upon grinding, please refer to [`DisplayIncome`](User-Interface.md/#Visual-indication-of-income-from-grinders-and-refineries). In `rulesmd.ini`: @@ -332,6 +333,7 @@ Grinding.DisallowTypes= ; List of InfantryTypes / VehicleTypes Grinding.PlayDieSound=true ; boolean Grinding.Sound= ; Sound Grinding.Weapon= ; WeaponType +Grinding.Weapon.RequiredCredits=0 ; integer ``` ### Customizable selling buildup sequence length for buildings that can undeploy diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 65f75c7ae0..c257cfa8f6 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -361,6 +361,7 @@ New: - Allow animations to play sounds detached from audio event handler (by Starkku) - Game save option when starting campaigns (by Trsdy) - Carryall pickup voice (by Starkku) +- Option to have `Grinding.Weapon` require accumulated credits from grinding (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index a61eed4fa4..e6158b4116 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -245,12 +245,15 @@ bool BuildingExt::DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechn auto const pTypeExt = pExt->TypeExtData; pExt->AccumulatedIncome += refund; + pExt->GrindingWeapon_AccumulatedCredits += refund; - if (pTypeExt->Grinding_Weapon.isset() - && Unsorted::CurrentFrame >= pExt->GrindingWeapon_LastFiredFrame + pTypeExt->Grinding_Weapon.Get()->ROF) + if (pTypeExt->Grinding_Weapon.isset() && + Unsorted::CurrentFrame >= pExt->GrindingWeapon_LastFiredFrame + pTypeExt->Grinding_Weapon.Get()->ROF && + pExt->GrindingWeapon_AccumulatedCredits >= pTypeExt->Grinding_Weapon_RequiredCredits) { TechnoExt::FireWeaponAtSelf(pBuilding, pTypeExt->Grinding_Weapon.Get()); pExt->GrindingWeapon_LastFiredFrame = Unsorted::CurrentFrame; + pExt->GrindingWeapon_AccumulatedCredits = 0; } if (pTypeExt->Grinding_Sound.isset()) @@ -338,6 +341,7 @@ void BuildingExt::ExtData::Serialize(T& Stm) .Process(this->IsCreatedFromMapFile) .Process(this->LimboID) .Process(this->GrindingWeapon_LastFiredFrame) + .Process(this->GrindingWeapon_AccumulatedCredits) .Process(this->CurrentAirFactory) .Process(this->AccumulatedIncome) .Process(this->CurrentLaserWeaponIndex) diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 13f00c90bb..5f20ae79f2 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -31,6 +31,7 @@ class BuildingExt bool IsCreatedFromMapFile; int LimboID; int GrindingWeapon_LastFiredFrame; + int GrindingWeapon_AccumulatedCredits; BuildingClass* CurrentAirFactory; int AccumulatedIncome; OptionalStruct CurrentLaserWeaponIndex; @@ -42,6 +43,7 @@ class BuildingExt , IsCreatedFromMapFile { false } , LimboID { -1 } , GrindingWeapon_LastFiredFrame { 0 } + , GrindingWeapon_AccumulatedCredits { 0 } , CurrentAirFactory { nullptr } , AccumulatedIncome { 0 } , CurrentLaserWeaponIndex {} diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index edd4713fa4..a5060d2a2e 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -139,6 +139,7 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Grinding_Sound.Read(exINI, pSection, "Grinding.Sound"); this->Grinding_PlayDieSound.Read(exINI, pSection, "Grinding.PlayDieSound"); this->Grinding_Weapon.Read(exINI, pSection, "Grinding.Weapon"); + this->Grinding_Weapon_RequiredCredits.Read(exINI, pSection, "Grinding.Weapon.RequiredCredits"); this->DisplayIncome.Read(exINI, pSection, "DisplayIncome"); this->DisplayIncome_Houses.Read(exINI, pSection, "DisplayIncome.Houses"); @@ -240,6 +241,7 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->Grinding_Sound) .Process(this->Grinding_PlayDieSound) .Process(this->Grinding_Weapon) + .Process(this->Grinding_Weapon_RequiredCredits) .Process(this->DisplayIncome) .Process(this->DisplayIncome_Houses) .Process(this->DisplayIncome_Offset) diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index d796be94dc..aaecb9bd01 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -35,6 +35,7 @@ class BuildingTypeExt NullableIdx Grinding_Sound; Nullable Grinding_Weapon; + Valueable Grinding_Weapon_RequiredCredits; ValueableVector Grinding_AllowTypes; ValueableVector Grinding_DisallowTypes; Valueable Grinding_AllowAllies; @@ -82,6 +83,7 @@ class BuildingTypeExt , Grinding_Sound {} , Grinding_PlayDieSound { true } , Grinding_Weapon {} + , Grinding_Weapon_RequiredCredits { 0 } , DisplayIncome { } , DisplayIncome_Houses { } , DisplayIncome_Offset { { 0,0 } } From 24fc2f5961a67b579236495c250fb846f41d7d73 Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 19 Mar 2024 10:54:19 +0200 Subject: [PATCH 07/43] Allow amphibious/water infantry to use any amphibious/water MovementZone --- docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 3 ++- src/Misc/Hooks.BugFixes.cpp | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 218a1ea69b..3e9bf33596 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -151,6 +151,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction. - Spawned aircraft now align with the spawner's facing when landing. - Fixed the bug that waypointing unarmed infantries with agent/engineer/occupier to a spyable/capturable/occupiable building triggers EnteredBy event by executing capture mission. +- Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone`. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index c257cfa8f6..11af4f927f 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -421,8 +421,9 @@ Vanilla fixes: - Spawned aircraft now align with the spawner's facing when landing (by Starkku) - Fixed infantries attempted to entering buildings when waypointing together with engineer/agent/occupier/etc (by Trsdy) - Fixed jumpjet crash speed when crashing onto buildings (by NetsuNegi) +- Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone` (by Starkku) -Phobos fixes: +- Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) - Add `ImmuneToCrit` for shields (by Trsdy) - Reimplemented the bugfix for jumpjet units' facing when firing, discard the inappropriate `JumpjetTurnToTarget` tag (by Trsdy) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index fe567754aa..a21f79db88 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -809,3 +809,21 @@ DEFINE_JUMP(LJMP, 0x719CBC, 0x719CD8);//Teleport, notorious CLEG frozen state re DEFINE_JUMP(LJMP, 0x72A16A, 0x72A186);//Tunnel, not a big deal DEFINE_JUMP(LJMP, 0x663428, 0x663445);//Rocket, not a big deal DEFINE_JUMP(LJMP, 0x5170CE, 0x5170E0);//Hover, not a big deal + +// Allow infantry to use all amphibious/water movement zones and still display sequence correctly. +DEFINE_HOOK(0x51D793, InfantryClass_DoAction_MovementZoneCheck, 0x6) +{ + enum { Amphibious = 0x51D7A6, NotAmphibious = 0x51D8BF }; + + GET(InfantryClass*, pThis, ESI); + + auto const mZone = pThis->Type->MovementZone; + + if (mZone == MovementZone::Amphibious || mZone == MovementZone::AmphibiousDestroyer || mZone == MovementZone::AmphibiousCrusher || + mZone == MovementZone::Water || mZone == MovementZone::WaterBeach) + { + return Amphibious; + } + + return NotAmphibious; +} From 5a845b79fa2b79581480fdf6b464ec9f793bec4f Mon Sep 17 00:00:00 2001 From: Kerbiter Date: Tue, 19 Mar 2024 13:57:18 +0200 Subject: [PATCH 08/43] Disable CodeRabbit autoreview and other annoyances --- .coderabbit.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000000..f3afcbfca5 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,10 @@ +reviews: + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: false + collapse_walkthrough: false + auto_review: + enabled: false +chat: + auto_reply: false \ No newline at end of file From d9db475f51ba94748ae57bac4fe3e792f85fec04 Mon Sep 17 00:00:00 2001 From: Kirill Andriiashin Date: Tue, 19 Mar 2024 17:42:03 +0300 Subject: [PATCH 09/43] Veinholes and Weeders (#1163) * Veinholes and Weeders Prettify Minor fixes Rewrote tiberium pip code Clean up some things Removed controversial stuff Removed leftovers, started work on docs Docs Requested changes Updated docs, added named constant Re-enabled oregath Apply suggestions from code review Co-authored-by: Kerbiter Added braces around nested loops Teleporting weeders now work * Fix after rebase * Add changelog entries * Fix not enough pips being displayed. Fix compiler warnings * Fix spelling * Change docs * Remove duplicate string * Update docs * Use note syntax for compatibility notice * Fix spelling and formatting * Apply suggestions from code review Co-authored-by: Kerbiter * More review suggestions * Add links to docs * Improve docs * Rename constant --------- Co-authored-by: Kerbiter --- CREDITS.md | 3 + Phobos.vcxproj | 4 + docs/Fixed-or-Improved-Logics.md | 101 +++++++- docs/Whats-New.md | 7 +- src/Ext/Anim/Body.cpp | 38 ++- src/Ext/Anim/Body.h | 2 + src/Ext/ParticleType/Body.cpp | 104 +++++++++ src/Ext/ParticleType/Body.h | 52 +++++ src/Ext/ParticleType/Hooks.cpp | 26 +++ src/Ext/Rules/Body.cpp | 10 +- src/Ext/Rules/Body.h | 11 +- src/Ext/SWType/Body.cpp | 9 + src/Ext/SWType/Body.h | 9 + src/Ext/SWType/Hooks.cpp | 108 +++++++++ src/Ext/Techno/Hooks.Pips.cpp | 97 ++++++-- src/Misc/Hooks.VeinholeMonster.cpp | 363 +++++++++++++++++++++++++++++ 16 files changed, 915 insertions(+), 29 deletions(-) create mode 100644 src/Ext/ParticleType/Body.cpp create mode 100644 src/Ext/ParticleType/Body.h create mode 100644 src/Ext/ParticleType/Hooks.cpp create mode 100644 src/Misc/Hooks.VeinholeMonster.cpp diff --git a/CREDITS.md b/CREDITS.md index da8a223005..fd236d94c2 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -320,6 +320,9 @@ This page lists all the individual contributions to the project by their author. - Flashing Technos on selecting - **ZivDero** - Allow giving ownership of buildings to players in Skirmish and MP using + - Re-enable the Veinhole Monster and Weeds from TS + - Recreate the weed-charging of SWs like the TS Chemical Missile + - Allow to change the speed of gas particles - **Ares developers** - YRpp and Syringe which are used, save/load, project foundation and generally useful code from Ares - unfinished RadTypes code diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 7cd780d98d..1ff7abb8a7 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -39,6 +39,8 @@ + + @@ -61,6 +63,7 @@ + @@ -189,6 +192,7 @@ + diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 3e9bf33596..6547d7d674 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -359,6 +359,18 @@ In `rulesmd.ini`: AdjustTargetCoordsOnRotation=true ; boolean ``` +## Particles + +### Customizable gas particle speed + +- Gas particles can now drift at a custom speed. + +In `rulesmd.ini`: +```ini +[GASPARTICLE] ; Particle with BehavesLike=Gas +Gas.MaxDriftSpeed=2 ; integer (TS default is 5) +``` + ## Projectiles ### Cluster scatter distance customization @@ -367,7 +379,7 @@ AdjustTargetCoordsOnRotation=true ; boolean In `rulesmd.ini`: ```ini -[SOMEPROJECTILE] ; Projectile +[SOMEPROJECTILE] ; Projectile ClusterScatter.Min=1.0 ; float, distance in cells ClusterScatter.Max=2.0 ; float, distance in cells ``` @@ -626,6 +638,8 @@ Powered.KillSpawns=false ; boolean - `Pips.Tiberiums.Frames` can be used to list frames (zero-based) of `pips.shp` (for buildings) or `pips2.shp` (for others) used for tiberium types, in the listed order corresponding to tiberium type index. Defaults to 5 for tiberium type index 1, otherwise 2. - `Pips.Tiberiums.EmptyFrame` can be used to set the frame for empty slots, defaults to 0. - `Pips.Tiberiums.DisplayOrder` controls in which order the tiberium type pips are displayed, takes a list of tiberium type indices. Any tiberium type not listed will be displayed in sequential order after the listed ones. + - `Pips.Tiberiums.WeedFrame` controls which frame is displayed on Technos with `Weeder=yes`, takes a (zero-based) index of a frame in `pips.shp` (for buildings) or `pips2.shp` (for others). Defaults to 1. + - `Pips.Tiberiums.WeedEmptyFrame` can be used to set the frame for empty weed slots, defaults to 0. In `rulesmd.ini`: ```ini @@ -637,6 +651,8 @@ Pips.Ammo.Buildings.Size=4,2 ; X,Y, increment in pixels to next pip Pips.Tiberiums.EmptyFrame=0 ; integer, frame of pips.shp (buildings) or pips2.shp (others) (zero-based) Pips.Tiberiums.Frames=2,5,2,2 ; list of integers, frames of pips.shp (buildings) or pips2.shp (others) (zero-based) Pips.Tiberiums.DisplayOrder=0,2,3,1 ; list of integers, tiberium type indices +Pips.Tiberiums.WeedEmptyFrame=0 ; integer, frame of pips.shp (buildings) or pips2.shp (others) (zero-based) +Pips.Tiberiums.WeedFrame=1 ; integer, frame of pips.shp (buildings) or pips2.shp (others) (zero-based) [SOMETECHNO] ; TechnoType AmmoPipFrame=13 ; integer, frame of pips2.shp (zero-based) @@ -863,6 +879,89 @@ Ammo.AddOnDeploy=0 ; integer ``` +## Veinholes & Weeds + +### Veinholes + +- Veinhole monsters now work like they used to in Tiberian Sun. +- Their core parameters are still loaded from `[General]` +- The Warhead used by veins is specified under `[CombatDamage]`. The warhead has to be properly listed under `[Warheads]` as well. The warhead has to have `Veinhole=yes` set. +- Veinholes are hardcoded to use several overlay types. +- The vein attack animation specified under `[AudioVisual]` is what deals the damage. The animation has to be properly listed under `[Animations]` as well. +- Units can be made immune to veins the same way as in Tiberian Sun. +- The monster itself is represented by the `VEINTREE` TerrainType, which has `IsVeinhole=true` set. Its strength is what determines the strength of the Veinhole. + +```{note} +Everything listed below functions identically to Tiberian Sun. +Many of the tags from Tiberian Sun have been re-enabled. The values provided below are identical to those found in TS and YR rules. You can read more about them on ModENC: +[VeinholeGrowthRate](https://modenc.renegadeprojects.com/VeinholeGrowthRate), [VeinholeShrinkRate](https://modenc.renegadeprojects.com/VeinholeShrinkRate), [MaxVeinholeGrowth](https://modenc.renegadeprojects.com/MaxVeinholeGrowth), [VeinDamage](https://modenc.renegadeprojects.com/VeinDamage), [VeinholeTypeClass](https://modenc.renegadeprojects.com/VeinholeTypeClass), +[VeinholeWarhead](https://modenc.renegadeprojects.com/VeinholeWarhead), [Veinhole](https://modenc.renegadeprojects.com/Veinhole), [VeinAttack](https://modenc.renegadeprojects.com/VeinAttack), [ImmuneToVeins](https://modenc.renegadeprojects.com/ImmuneToVeins), [IsVeinhole](https://modenc.renegadeprojects.com/IsVeinhole) +``` + +In `rulesmd.ini`: +```ini +[General] +VeinholeGrowthRate=300 ; integer +VeinholeShrinkRate=100 ; integer +MaxVeinholeGrowth=2000 ; integer +VeinDamage=5 ; integer +VeinholeTypeClass=VEINTREE ; TerrainType + +[CombatDamage] +VeinholeWarhead=VeinholeWH ; Warhead + +[VeinholeWH] +Veinhole=yes + +[AudioVisual] +VeinAttack=VEINATAC ; Animation + +[TechnoType] +EliteAbilities=VEIN_PROOF +ImmuneToVeins=yes + +[VEINTREE] +IsVeinhole=true +Strength=1000 ; integer - the strength of the Veinhole +``` + +```{warning} +The game expects certain overlays related to Veinholes to have certain indices, they are listed below. +``` + +In `rulesmd.ini`: +```ini +[OverlayTypes] +126=VEINS ; The veins (weeds) +167=VEINHOLE ; The Veinhole itself +178=VEINHOLEDUMMY ; A technical overlay +``` + + +### Weeds & Weed Eaters + +- Vehicles with `Weeder=yes` can now collect weeds. The weeds can then be deposited into a building with `Weeder=yes`. +- Weeds are not stored in a building's storage, but rather in a House's storage. The weed capacity is listed under `[General]->WeedCapacity`. +- Weeders now show the ore gathering animation. It can be customized the same way as for harvesters. +- Weeders can use the Teleport locomotor like chrono miners. + +### Weed-consuming superweapons + +- Superweapons can consume weeds to recharge, like the Chemical Missile special in Tiberian Sun. + +```{note} +As the code for the Chemical Missile had been removed, setting `Type=ChemMissile` will not work. +``` + +In `rulesmd.ini`: +```ini +[SuperWeaponType] +UseWeeds=no ; boolean - should the SW use weeds to recharge? +UseWeeds.Amount= ; integer - how many? default is General->WeedCapacity +UseWeeds.StorageTimer=no ; boolean - should the counter on the sidebar display the % of weeds stored? +UseWeeds.ReadinessAnimationPercentage=0.9 ; double - when this many weeds % are stored, the SW will show it's ready on the building (open nuke/open chrono, etc.) +``` + ## VoxelAnims ### Customizable debris & meteor impact and warhead detonation behaviour diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 11af4f927f..40e7ea8544 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -1,6 +1,6 @@ # What's New -This page lists the history of changes across stable Phobos releases and also all the stuff that requires modders to change something in their mods to accomodate. +This page lists the history of changes across stable Phobos releases and also all the stuff that requires modders to change something in their mods to accommodate. ## Migrating @@ -44,7 +44,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - `Gravity=0` is not supported anymore as it will cause the projectile to fly backwards and be unable to hit the target which is not at the same height. Use `Straight` Trajectory instead. See [here](New-or-Enhanced-Logics.md#projectile-trajectories). - Automatic self-destruction logic logic has been reimplemented, `Death.NoAmmo`, `Death.Countdown` and `Death.Peaceful` tags have been remade/renamed and require adjustments to function. - `DetachedFromOwner` on weapons is deprecated. This has been replaced by `AllowDamageOnSelf` on warheads. -- Timed jump script actions now take the time measured in ingame seconds instead of frames. Divide your value by 15 to accomodate to this change. +- Timed jump script actions now take the time measured in ingame seconds instead of frames. Divide your value by 15 to accommodate to this change. - [Placement Preview](User-Interface.md#placement-preview) logic has been adjusted, `BuildingPlacementPreview.DefaultTranslucentLevel`, `BuildingPlacementGrid.TranslucentLevel`, `PlacementPreview.Show`, `PlacementPreview.TranslucentLevel` and `ShowBuildingPlacementPreview` tags have been remade/renamed and require adjustments to function. In addition, you must explicitly enable this feature by specifying `[AudioVisual]->PlacementPreview=yes`. - Existing script actions were renumbered, please use the migration utility to change the numbers to the correct ones. - `DiskLaser.Radius` values were misinterpreted by a factor of 1/2π. The default radius is now 240, please multiply your customized radii by 2π. @@ -362,6 +362,9 @@ New: - Game save option when starting campaigns (by Trsdy) - Carryall pickup voice (by Starkku) - Option to have `Grinding.Weapon` require accumulated credits from grinding (by Starkku) +- Re-enable the Veinhole Monster and Weeds from TS (by ZivDero) +- Recreate the weed-charging of SWs like the TS Chemical Missile (by ZivDero) +- Allow to change the speed of gas particles (by ZivDero) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 5e107ee536..3b90aef9ca 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -97,6 +97,42 @@ HouseClass* AnimExt::GetOwnerHouse(AnimClass* pAnim, HouseClass* pDefaultOwner) return pTechnoOwner ? pTechnoOwner : pDefaultOwner; } +void AnimExt::VeinAttackAI(AnimClass* pAnim) +{ + CellStruct pCoordinates = pAnim->GetMapCoords(); + CellClass* pCell = MapClass::Instance->GetCellAt(pCoordinates); + ObjectClass* pOccupier = pCell->FirstObject; + constexpr unsigned char fullyFlownWeedStart = 0x30; // Weeds starting from this overlay frame are fully grown + constexpr unsigned int weedOverlayIndex = 126; + + if (!pOccupier || pOccupier->GetHeight() > 0 || pCell->OverlayTypeIndex != weedOverlayIndex + || pCell->OverlayData < fullyFlownWeedStart || pCell->SlopeIndex) + { + pAnim->UnableToContinue = true; + } + + if (Unsorted::CurrentFrame % 2 == 0) + { + while (pOccupier != nullptr) + { + ObjectClass* pNext = pOccupier->NextObject; + int damage = RulesClass::Instance->VeinDamage; + + abstract_cast(pOccupier); + + TechnoClass* pTechno = abstract_cast(pOccupier); + + if (pTechno && !pTechno->GetTechnoType()->ImmuneToVeins && !pTechno->HasAbility(Ability::VeinProof) + && pTechno->Health > 0 && pTechno->IsAlive && pTechno->GetHeight() <= 5) + { + pTechno->ReceiveDamage(&damage, 0, RulesExt::Global()->VeinholeWarhead, nullptr, false, false, nullptr); + } + + pOccupier = pNext; + } + } +} + void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage, CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom) { @@ -132,7 +168,7 @@ void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWak if (pWakeAnim) pWakeAnimToUse = pWakeAnim; - if (splashAnims.size() > 0) + if (!splashAnims.empty()) { auto nIndexR = (splashAnims.size() - 1); auto nIndex = splashAnimsPickRandom ? diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 8643979102..4db09de6d2 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -93,6 +93,8 @@ class AnimExt static bool SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner = true, bool defaultToInvokerOwner = false); static HouseClass* GetOwnerHouse(AnimClass* pAnim, HouseClass* pDefaultOwner = nullptr); + static void VeinAttackAI(AnimClass* pAnim); + static void HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage, CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom); }; diff --git a/src/Ext/ParticleType/Body.cpp b/src/Ext/ParticleType/Body.cpp new file mode 100644 index 0000000000..e9c8a2bfaf --- /dev/null +++ b/src/Ext/ParticleType/Body.cpp @@ -0,0 +1,104 @@ +#include "Body.h" + +ParticleTypeExt::ExtContainer ParticleTypeExt::ExtMap; + +// ============================= +// load / save + +template +void ParticleTypeExt::ExtData::Serialize(T& Stm) +{ + Stm + .Process(this->Gas_MaxDriftSpeed) + ; +} + +void ParticleTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +{ + auto pThis = this->OwnerObject(); + const char* pSection = pThis->ID; + + if (!pINI->GetSection(pSection)) + return; + + INI_EX exINI(pINI); + + this->Gas_MaxDriftSpeed.Read(exINI, pSection, "Gas.MaxDriftSpeed"); +} + +void ParticleTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +{ + Extension::LoadFromStream(Stm); + this->Serialize(Stm); +} + +void ParticleTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +{ + Extension::SaveToStream(Stm); + this->Serialize(Stm); +} + +bool ParticleTypeExt::LoadGlobals(PhobosStreamReader& Stm) +{ + return Stm + .Success(); +} + +bool ParticleTypeExt::SaveGlobals(PhobosStreamWriter& Stm) +{ + return Stm + .Success(); +} + +// ============================= +// container + +ParticleTypeExt::ExtContainer::ExtContainer() : Container("ParticleTypeClass") { } +ParticleTypeExt::ExtContainer::~ExtContainer() = default; + +// ============================= +// container hooks + +DEFINE_HOOK(0x644DBB, ParticleTypeClass_CTOR, 0x5) +{ + GET(ParticleTypeClass*, pItem, ESI); + + ParticleTypeExt::ExtMap.TryAllocate(pItem); + + return 0; +} + +DEFINE_HOOK_AGAIN(0x6457A0, ParticleTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x645660, ParticleTypeClass_SaveLoad_Prefix, 0x7) +{ + GET_STACK(ParticleTypeClass*, pItem, 0x4); + GET_STACK(IStream*, pStm, 0x8); + + ParticleTypeExt::ExtMap.PrepareStream(pItem, pStm); + + return 0; +} + +DEFINE_HOOK(0x64578C, ParticleTypeClass_Load_Suffix, 0x5) +{ + ParticleTypeExt::ExtMap.LoadStatic(); + + return 0; +} + +DEFINE_HOOK(0x64580A, ParticleTypeClass_Save_Suffix, 0x7) +{ + ParticleTypeExt::ExtMap.SaveStatic(); + + return 0; +} + +DEFINE_HOOK(0x6453FF, ParticleTypeClass_LoadFromINI, 0x6) +{ + GET(ParticleTypeClass*, pItem, ESI); + GET_STACK(CCINIClass*, pINI, STACK_OFFSET(0xDC, 0x4)); + + ParticleTypeExt::ExtMap.LoadFromINI(pItem, pINI); + + return 0; +} diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h new file mode 100644 index 0000000000..91f24b4298 --- /dev/null +++ b/src/Ext/ParticleType/Body.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include +#include +#include +#include + +class ParticleTypeExt +{ +public: + using base_type = ParticleTypeClass; + + static constexpr DWORD Canary = 0xEAFEEAFE; + static constexpr size_t ExtPointerOffset = 0x18; + + class ExtData final : public Extension + { + public: + Valueable Gas_MaxDriftSpeed; + + ExtData(ParticleTypeClass* OwnerObject) : Extension(OwnerObject) + , Gas_MaxDriftSpeed { 2 } + { } + + virtual ~ExtData() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + + virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + + private: + template + void Serialize(T& Stm); + }; + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static bool LoadGlobals(PhobosStreamReader& Stm); + static bool SaveGlobals(PhobosStreamWriter& Stm); +}; diff --git a/src/Ext/ParticleType/Hooks.cpp b/src/Ext/ParticleType/Hooks.cpp new file mode 100644 index 0000000000..5c17c2280d --- /dev/null +++ b/src/Ext/ParticleType/Hooks.cpp @@ -0,0 +1,26 @@ +#include "Body.h" + +#include + +DEFINE_HOOK(0x62BE30, ParticleClass_Gas_AI_DriftSpeed, 0x5) +{ + enum { ContinueAI = 0x62BE60 }; + + GET(ParticleClass*, pParticle, EBP); + + auto pExt = ParticleTypeExt::ExtMap.Find(pParticle->Type); + int maxDriftSpeed = pExt->Gas_MaxDriftSpeed; + int minDriftSpeed = -maxDriftSpeed; + + if (pParticle->Velocity.X > maxDriftSpeed) + pParticle->Velocity.X = maxDriftSpeed; + else if (pParticle->Velocity.X < minDriftSpeed) + pParticle->Velocity.X = minDriftSpeed; + + if (pParticle->Velocity.Y > maxDriftSpeed) + pParticle->Velocity.Y = maxDriftSpeed; + else if (pParticle->Velocity.Y < minDriftSpeed) + pParticle->Velocity.Y = minDriftSpeed; + + return ContinueAI; +} diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 3daed98a6e..ecd6f08a5f 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -80,6 +80,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->RadWarhead_Detonate.Read(exINI, GameStrings::Radiation, "RadSiteWarhead.Detonate"); this->RadHasOwner.Read(exINI, GameStrings::Radiation, "RadHasOwner"); this->RadHasInvoker.Read(exINI, GameStrings::Radiation, "RadHasInvoker"); + this->VeinholeWarhead.Read(exINI, GameStrings::CombatDamage, "VeinholeWarhead"); this->MissingCameo.Read(pINI, GameStrings::AudioVisual, "MissingCameo"); this->PlacementGrid_Translucency.Read(exINI, GameStrings::AudioVisual, "PlacementGrid.Translucency"); @@ -103,9 +104,11 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->Pips_Generic_Buildings_Size.Read(exINI, GameStrings::AudioVisual, "Pips.Generic.Buildings.Size"); this->Pips_Ammo_Size.Read(exINI, GameStrings::AudioVisual, "Pips.Ammo.Size"); this->Pips_Ammo_Buildings_Size.Read(exINI, GameStrings::AudioVisual, "Pips.Ammo.Buildings.Size"); - this->Pips_Tiberiums_EmptyFrame.Read(exINI, GameStrings::AudioVisual, "Pips.Tiberiums.EmptyFrame"); this->Pips_Tiberiums_Frames.Read(exINI, GameStrings::AudioVisual, "Pips.Tiberiums.Frames"); + this->Pips_Tiberiums_EmptyFrame.Read(exINI, GameStrings::AudioVisual, "Pips.Tiberiums.EmptyFrame"); this->Pips_Tiberiums_DisplayOrder.Read(exINI, GameStrings::AudioVisual, "Pips.Tiberiums.DisplayOrder"); + this->Pips_Tiberiums_WeedFrame.Read(exINI, GameStrings::AudioVisual, "Pips.Tiberiums.WeedFrame"); + this->Pips_Tiberiums_WeedEmptyFrame.Read(exINI, GameStrings::AudioVisual, "Pips.Tiberiums.WeedEmptyFrame"); this->ToolTip_Background_Color.Read(exINI, GameStrings::AudioVisual, "ToolTip.Background.Color"); this->ToolTip_Background_Opacity.Read(exINI, GameStrings::AudioVisual, "ToolTip.Background.Opacity"); this->ToolTip_Background_BlurSize.Read(exINI, GameStrings::AudioVisual, "ToolTip.Background.BlurSize"); @@ -230,6 +233,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->RadHasInvoker) .Process(this->JumpjetCrash) .Process(this->JumpjetNoWobbles) + .Process(this->VeinholeWarhead) .Process(this->MissingCameo) .Process(this->PlacementGrid_Translucency) .Process(this->PlacementGrid_TranslucencyWithPreview) @@ -251,9 +255,11 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->Pips_Generic_Buildings_Size) .Process(this->Pips_Ammo_Size) .Process(this->Pips_Ammo_Buildings_Size) - .Process(this->Pips_Tiberiums_EmptyFrame) .Process(this->Pips_Tiberiums_Frames) + .Process(this->Pips_Tiberiums_EmptyFrame) .Process(this->Pips_Tiberiums_DisplayOrder) + .Process(this->Pips_Tiberiums_WeedFrame) + .Process(this->Pips_Tiberiums_WeedEmptyFrame) .Process(this->AllowParallelAIQueues) .Process(this->ForbidParallelAIQueues_Aircraft) .Process(this->ForbidParallelAIQueues_Building) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 00ef1f8bb8..d908b28b7c 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -47,6 +47,8 @@ class RulesExt Valueable JumpjetCrash; Valueable JumpjetNoWobbles; + Nullable VeinholeWarhead; + PhobosFixedString<32u> MissingCameo; TranslucencyLevel PlacementGrid_Translucency; @@ -70,9 +72,11 @@ class RulesExt Valueable Pips_Generic_Buildings_Size; Valueable Pips_Ammo_Size; Valueable Pips_Ammo_Buildings_Size; - Valueable Pips_Tiberiums_EmptyFrame; ValueableVector Pips_Tiberiums_Frames; + Valueable Pips_Tiberiums_EmptyFrame; ValueableVector Pips_Tiberiums_DisplayOrder; + Valueable Pips_Tiberiums_WeedFrame; + Valueable Pips_Tiberiums_WeedEmptyFrame; Valueable AllowParallelAIQueues; Valueable ForbidParallelAIQueues_Aircraft; @@ -127,6 +131,7 @@ class RulesExt , RadHasInvoker { false } , JumpjetCrash { 5.0 } , JumpjetNoWobbles { false } + , VeinholeWarhead {} , MissingCameo { GameStrings::XXICON_SHP() } , PlacementGrid_Translucency { 0 } @@ -149,9 +154,11 @@ class RulesExt , Pips_Generic_Buildings_Size { { 4, 2 } } , Pips_Ammo_Size { { 4, 0 } } , Pips_Ammo_Buildings_Size { { 4, 2 } } - , Pips_Tiberiums_EmptyFrame { 0 } , Pips_Tiberiums_Frames {} + , Pips_Tiberiums_EmptyFrame { 0 } , Pips_Tiberiums_DisplayOrder {} + , Pips_Tiberiums_WeedFrame { 1 } + , Pips_Tiberiums_WeedEmptyFrame { 0 } , AllowParallelAIQueues { true } , ForbidParallelAIQueues_Aircraft { false } , ForbidParallelAIQueues_Building { false } diff --git a/src/Ext/SWType/Body.cpp b/src/Ext/SWType/Body.cpp index 18366f0f45..6058134f8e 100644 --- a/src/Ext/SWType/Body.cpp +++ b/src/Ext/SWType/Body.cpp @@ -45,6 +45,10 @@ void SWTypeExt::ExtData::Serialize(T& Stm) .Process(this->ShowTimer_Priority) .Process(this->Convert_Pairs) .Process(this->ShowDesignatorRange) + .Process(this->UseWeeds) + .Process(this->UseWeeds_Amount) + .Process(this->UseWeeds_StorageTimer) + .Process(this->UseWeeds_ReadinessAnimationPercentage) ; } @@ -144,6 +148,11 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) TypeConvertGroup::Parse(this->Convert_Pairs, exINI, pSection, AffectedHouse::Owner); this->ShowDesignatorRange.Read(exINI, pSection, "ShowDesignatorRange"); + + this->UseWeeds.Read(exINI, pSection, "UseWeeds"); + this->UseWeeds_Amount.Read(exINI, pSection, "UseWeeds.Amount"); + this->UseWeeds_StorageTimer.Read(exINI, pSection, "UseWeeds.StorageTimer"); + this->UseWeeds_ReadinessAnimationPercentage.Read(exINI, pSection, "UseWeeds.ReadinessAnimationPercentage"); } void SWTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 945cd2154a..6c82e61bea 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -63,6 +63,11 @@ class SWTypeExt std::vector Convert_Pairs; + Valueable UseWeeds; + Valueable UseWeeds_Amount; + Valueable UseWeeds_StorageTimer; + Valueable UseWeeds_ReadinessAnimationPercentage; + ExtData(SuperWeaponTypeClass* OwnerObject) : Extension(OwnerObject) , Money_Amount { 0 } , SW_Inhibitors {} @@ -98,6 +103,10 @@ class SWTypeExt , ShowTimer_Priority { 0 } , Convert_Pairs {} , ShowDesignatorRange { true } + , UseWeeds { false } + , UseWeeds_Amount { RulesClass::Instance->WeedCapacity } + , UseWeeds_StorageTimer { false } + , UseWeeds_ReadinessAnimationPercentage { 0.9 } { } // Ares 0.A functions diff --git a/src/Ext/SWType/Hooks.cpp b/src/Ext/SWType/Hooks.cpp index 3e0fc992dd..cb0369c5ef 100644 --- a/src/Ext/SWType/Hooks.cpp +++ b/src/Ext/SWType/Hooks.cpp @@ -74,3 +74,111 @@ DEFINE_HOOK(0x6DBE74, Tactical_SuperLinesCircles_ShowDesignatorRange, 0x7) return 0; } + +DEFINE_HOOK(0x6CBEF4, SuperClass_AnimStage_UseWeeds, 0x6) +{ + enum + { + Ready = 0x6CBFEC, + NotReady = 0x6CC064, + ProgressInEax = 0x6CC066 + }; + + constexpr int maxCounterFrames = 54; + + GET(SuperClass*, pSuper, ECX); + GET(SuperWeaponTypeClass*, pSWType, EBX); + + auto pExt = SWTypeExt::ExtMap.Find(pSWType); + + if (pExt->UseWeeds) + { + if (pSuper->IsReady) + return Ready; + + if (pExt->UseWeeds_StorageTimer) + { + int progress = static_cast(pSuper->Owner->OwnedWeed.GetTotalAmount() * maxCounterFrames / pExt->UseWeeds_Amount); + if (progress > maxCounterFrames) + progress = maxCounterFrames; + + R->EAX(progress); + return ProgressInEax; + } + else + { + return NotReady; + } + } + + return 0; +} + +DEFINE_HOOK(0x6CBD2C, SuperClass_AI_UseWeeds, 0x6) +{ + enum + { + NothingChanged = 0x6CBE9D, + SomethingChanged = 0x6CBD48, + Charged = 0x6CBD73 + }; + + enum + { + SWReadyTimer = 0, + SWAlmostReadyTimer = 15, + SWNotReadyTimer = 915 + }; + + GET(SuperClass*, pSuper, ESI); + + auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type); + + if (pExt->UseWeeds) + { + if (pSuper->Type->ShowTimer) + pSuper->Type->ShowTimer = false; + + if (pSuper->Owner->OwnedWeed.GetTotalAmount() >= pExt->UseWeeds_Amount) + { + pSuper->Owner->OwnedWeed.RemoveAmount(static_cast(pExt->UseWeeds_Amount), 0); + pSuper->RechargeTimer.Start(SWReadyTimer); // The Armageddon is here + return Charged; + } + + if (pSuper->Owner->OwnedWeed.GetTotalAmount() >= pExt->UseWeeds_ReadinessAnimationPercentage * pExt->UseWeeds_Amount) + { + pSuper->RechargeTimer.Start(SWAlmostReadyTimer); // The end is nigh! + } + else + { + pSuper->RechargeTimer.Start(SWNotReadyTimer); // 61 seconds > 60 seconds (animation activation threshold) + } + + int animStage = pSuper->AnimStage(); + if (pSuper->CameoChargeState != animStage) + { + pSuper->CameoChargeState = animStage; + return SomethingChanged; + } + + return NothingChanged; + } + + return 0; +} + +// This is pointless for SWs using weeds because their charge is tied to weed storage. +DEFINE_HOOK(0x6CC1E6, SuperClass_SetSWCharge_UseWeeds, 0x5) +{ + enum { Skip = 0x6CC251 }; + + GET(SuperClass*, pSuper, EDI); + + auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type); + + if (pExt->UseWeeds) + return Skip; + + return 0; +} diff --git a/src/Ext/Techno/Hooks.Pips.cpp b/src/Ext/Techno/Hooks.Pips.cpp index fc9bb61879..064274c7d7 100644 --- a/src/Ext/Techno/Hooks.Pips.cpp +++ b/src/Ext/Techno/Hooks.Pips.cpp @@ -162,44 +162,99 @@ DEFINE_HOOK(0x70A1F6, TechnoClass_DrawPips_Tiberium, 0x6) GET(int, yOffset, ESI); Point2D position = { offset->X, offset->Y }; - int totalStorage = pThis->GetTechnoType()->Storage; + const int totalStorage = pThis->GetTechnoType()->Storage; - std::vector tibPipCounts(TiberiumClass::Array.get()->Count); + std::vector pipsToDraw; - for (size_t i = 0; i < tibPipCounts.size(); i++) + bool isWeeder = false; + switch (pThis->WhatAmI()) { - tibPipCounts[i] = static_cast(pThis->Tiberium.GetAmount(i) / totalStorage * maxPips + 0.5); + case AbstractType::Building: + isWeeder = static_cast(pThis)->Type->Weeder; + break; + case AbstractType::Unit: + isWeeder = static_cast(pThis)->Type->Weeder; + break; + default: + break; } - auto const& tibDisplayOrders = RulesExt::Global()->Pips_Tiberiums_DisplayOrder.size() ? RulesExt::Global()->Pips_Tiberiums_DisplayOrder : std::vector { 0, 2, 3, 1 }; - auto const& tibFrames = RulesExt::Global()->Pips_Tiberiums_Frames; + if (isWeeder) + { + const int fullWeedFrames = pThis->WhatAmI() == AbstractType::Building ? + static_cast(pThis->Owner->GetWeedStoragePercentage() * maxPips + 0.5) : + static_cast(pThis->Tiberium.GetTotalAmount() / totalStorage * maxPips + 0.5); - for (int i = 0; i < maxPips; i++) + for (int i = 0; i < maxPips; i++) + { + if (i < fullWeedFrames) + pipsToDraw.push_back(RulesExt::Global()->Pips_Tiberiums_WeedFrame); + else + pipsToDraw.push_back(RulesExt::Global()->Pips_Tiberiums_WeedEmptyFrame); + } + } + else { - int frame = RulesExt::Global()->Pips_Tiberiums_EmptyFrame; + std::vector tiberiumPipCounts(TiberiumClass::Array->Count); + + for (size_t i = 0; i < tiberiumPipCounts.size(); i++) + { + tiberiumPipCounts[i] = static_cast(pThis->Tiberium.GetAmount(i) / totalStorage * maxPips + 0.5); + } + + auto const rawPipOrder = RulesExt::Global()->Pips_Tiberiums_DisplayOrder.empty() ? std::vector{ 0, 2, 3, 1 } : RulesExt::Global()->Pips_Tiberiums_DisplayOrder; + auto const& pipFrames = RulesExt::Global()->Pips_Tiberiums_Frames; + int const emptyFrame = RulesExt::Global()->Pips_Tiberiums_EmptyFrame; + + std::vector pipOrder; - for (size_t orderIndex = 0; orderIndex < tibPipCounts.size(); orderIndex++) + // First make a new vector, removing all the duplicate and invalid tiberiums + for (int index : rawPipOrder) { - size_t tibTypeIndex = orderIndex; + if (std::find(pipOrder.begin(), pipOrder.end(), index) == pipOrder.end() && + index >= 0 && index < TiberiumClass::Array->Count) + { + pipOrder.push_back(index); + } + } - if (orderIndex < tibDisplayOrders.size()) - tibTypeIndex = tibDisplayOrders.at(orderIndex); + // Then add any tiberium types that are missing + for (int i = 0; i < TiberiumClass::Array->Count; i++) + { + if (std::find(pipOrder.begin(), pipOrder.end(), i) == pipOrder.end()) + { + pipOrder.push_back(i); + } + } + - if (tibPipCounts[tibTypeIndex] > 0) + for (int i = 0; i < maxPips; i++) + { + for (const int index : pipOrder) { - tibPipCounts[tibTypeIndex]--; + if (tiberiumPipCounts[index] > 0) + { + tiberiumPipCounts[index]--; - if (tibTypeIndex >= tibFrames.size()) - frame = tibTypeIndex == 1 ? 5 : 2; - else - frame = tibFrames.at(tibTypeIndex); + if (static_cast(index) >= pipFrames.size()) + pipsToDraw.push_back(index == 1 ? 5 : 2); + else + pipsToDraw.push_back(pipFrames.at(index)); - break; + break; + } } + + if (pipsToDraw.size() <= static_cast(i)) + pipsToDraw.push_back(emptyFrame); } + } - DSurface::Temp->DrawSHP(FileSystem::PALETTE_PAL, shape, frame, - &position, rect, BlitterFlags(0x600), 0, 0, ZGradient::Ground, 1000, 0, 0, 0, 0, 0); + for (int pip : pipsToDraw) + { + DSurface::Temp->DrawSHP(FileSystem::PALETTE_PAL, shape, pip, + &position, rect, BlitterFlags::Centered | BlitterFlags::bf_400, 0, 0, + ZGradient::Ground, 1000, 0, nullptr, 0, 0, 0); position.X += offset->Width; position.Y += yOffset; diff --git a/src/Misc/Hooks.VeinholeMonster.cpp b/src/Misc/Hooks.VeinholeMonster.cpp new file mode 100644 index 0000000000..649608cf27 --- /dev/null +++ b/src/Misc/Hooks.VeinholeMonster.cpp @@ -0,0 +1,363 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/// +/// Veinhole Monster +/// + +// Loads the veinhole monster art +// Call removed from YR by WW +DEFINE_HOOK(0x4AD097, DisplayClass_ReadIni_LoadVeinholeArt, 0x5) +{ + enum { ContinueReadIni = 0x4AD0A8 }; + + int theater = static_cast(ScenarioClass::Instance->Theater); + SmudgeTypeClass::LoadFromIniList(theater); + VeinholeMonsterClass::LoadVeinholeArt(theater); + + return ContinueReadIni; +} + +// Applies damage to the veinhole monster +DEFINE_HOOK(0x489671, Damage_at_Cell_Update_Veinhole, 0x6) +{ + enum + { + ContinueDrawWall = 0x48967B, + ContinueNotWall = 0x4896B2 + }; + + GET(OverlayTypeClass*, pOverlay, EAX); + GET(WarheadTypeClass*, pWH, ESI); + GET_STACK(CellStruct, pCell, STACK_OFFSET(0xE0, -0x4C)); + GET_STACK(int, damage, STACK_OFFSET(0xE0, -0xBC)); + GET_STACK(ObjectClass*, pAttacker, STACK_OFFSET(0xE0, 0x8)); + GET_STACK(HouseClass*, pAttackingHouse, STACK_OFFSET(0xE0, 0x14)); + + if (pOverlay->IsVeinholeMonster) + { + if (VeinholeMonsterClass* pVeinhole = VeinholeMonsterClass::GetVeinholeMonsterFrom(&pCell)) + pVeinhole->ReceiveDamage(&damage, 0, pWH, pAttacker, false, false, pAttackingHouse); + } + + return pOverlay->Wall ? ContinueDrawWall : ContinueNotWall; +} + +DEFINE_HOOK(0x6D4656, TacticalClass_Draw_Veinhole, 0x5) +{ + enum { ContinueDraw = 0x6D465B }; + + VeinholeMonsterClass::DrawAll(); + IonBlastClass::DrawAll(); + + return ContinueDraw; +} + +DEFINE_HOOK(0x5349A5, Map_ClearVectors_Veinhole, 0x5) +{ + VeinholeMonsterClass::DeleteAll(); + VeinholeMonsterClass::DeleteVeinholeGrowthData(); + return 0; +} + +DEFINE_HOOK(0x55B4E1, LogicClass_Update_Veinhole, 0x5) +{ + VeinholeMonsterClass::UpdateAllVeinholes(); + return 0; +} + +// Handles the veins' attack animation +DEFINE_HOOK(0x4243BC, AnimClass_Update_VeinholeAttack, 0x6) +{ + enum + { + ContinueDrawTiberium = 0x4243CC, + ContinueNotTiberium = 0x42442E + }; + + GET(AnimClass*, pAnim, ESI); + + if (pAnim->Type->IsVeins) + AnimExt::VeinAttackAI(pAnim); + + GET(AnimClass*, pAnim2, ESI); + + return pAnim2->Type->IsTiberium ? + ContinueDrawTiberium : ContinueNotTiberium; +} + +/// +/// Weeder +/// + +// These 2 I am not sure, maybe they have smth to do with AI, maybe they are for the unit queue at the refinery +DEFINE_HOOK(0x736823, UnitClass_Update_WeederMissionMove, 0x6) +{ + enum + { + Continue = 0x736831, + Skip = 0x736981 + }; + + GET(UnitTypeClass*, pUnitType, EAX); + + if (pUnitType->Harvester || pUnitType->Weeder) + return Continue; + + return Skip; +} + +DEFINE_HOOK(0x7368C6, UnitClass_Update_WeederMissionMove2, 0x6) +{ + enum + { + Continue = 0x7368D4, + Skip = 0x736981 + }; + + GET(BuildingTypeClass*, pBuildingType, EDX); + + if (pBuildingType->Refinery || pBuildingType->Weeder) + return Continue; + + return Skip; +} + +// Not sure if necessary +/* +// These 2 have something to do with ZAdjustment when unloading +DEFINE_HOOK(0x7043E7, TechnoClass_Get_ZAdjustment_Weeder, 0x6) +{ + enum + { + Continue = 0x7043F1, + Skip = 0x704421 + }; + + GET(UnitTypeClass*, pUnitType, ECX); + + if (pUnitType->Harvester || pUnitType->Weeder) + return Continue; + + return Skip; +} + +DEFINE_HOOK(0x70440C, TechnoClass_Get_ZAdjustment_Weeder2, 0x6) +{ + enum + { + Continue = 0x704416, + Skip = 0x704421 + }; + + GET(BuildingTypeClass*, pBuildingType, EAX); + + if (pBuildingType->Refinery || pBuildingType->Weeder) + return Continue; + + return Skip; +} + +DEFINE_HOOK(0x741C32, UnitClass_SetDestination_SpecialAnim_Weeder, 0x6) +{ + enum + { + CheckSpecialAnimExists = 0x741C3C, + Skip = 0x741C4F + }; + + GET(UnitTypeClass*, pUnitType, ECX); + + if (pUnitType->Harvester || pUnitType->Weeder) + return CheckSpecialAnimExists; + + return Skip; +} +*/ + +DEFINE_HOOK(0x73D0DB, UnitClass_DrawAt_Weeder_Oregath, 0x6) +{ + enum + { + DrawOregath = 0x73D0E9, + Skip = 0x73D298 + }; + + GET(UnitClass*, pUnit, ESI); + + if (pUnit->Type->Harvester || pUnit->Type->Weeder || pUnit->IsHarvesting) + return DrawOregath; + + return Skip; +} +/* +DEFINE_HOOK(0x73D2A6, UnitClass_DrawAt_Weeder_UnloadingClass, 0x6) +{ + enum + { + ShowUnloadingClass = 0x73D2B0, + Skip = 0x73D2CA + }; + + GET(UnitTypeClass*, pUnitType, EAX); + + if (pUnitType->Harvester || pUnitType->Weeder) + return ShowUnloadingClass; + + return Skip; +} +*/ + +// Enables the weeder to harvest veins +DEFINE_HOOK(0x73D49E, UnitClass_Harvesting_Weeder, 0x7) +{ + enum + { + Harvest = 0x73D4DA, + Skip = 0x73D5FE + }; + + GET(UnitClass*, pUnit, ESI); + GET(CellClass*, pCell, EBP); + constexpr unsigned char weedOverlayData = 0x30; + + bool harvesterCanHarvest = pUnit->Type->Harvester && pCell->LandType == LandType::Tiberium; + bool weederCanWeed = pUnit->Type->Weeder && pCell->LandType == LandType::Weeds && pCell->OverlayData >= weedOverlayData; + + + if ((harvesterCanHarvest || weederCanWeed) && pUnit->GetStoragePercentage() < 1.0) + return Harvest; + + return Skip; +} + +// Not sure if necessary +/* +DEFINE_HOOK(0x73E005, UnitClass_Unload_WeederAnim, 0x6) +{ + enum + { + ProceedWithAnim = 0x73E013, + Skip = 0x73E093 + }; + + GET(UnitTypeClass*, pUnitType, ECX); + + if (pUnitType->Harvester || pUnitType->Weeder) + return ProceedWithAnim; + + return Skip; +} +*/ + +// This lets the weeder actually enter the waste facility and unload +// WW removed weeders from this check in YR +DEFINE_HOOK(0x43C788, BuildingClass_ReceivedRadioCommand_Weeder_CompleteEnter, 0x6) +{ + enum + { + CompleteEnter = 0x43C796, + Skip = 0x43CE43 + }; + + GET(BuildingTypeClass*, pBuildingType, EAX); + + if (pBuildingType->DockUnload || pBuildingType->Weeder) + return CompleteEnter; + + return Skip; +} + +// This assigns the weeder to the "Harvest" mission when it is granted as a free unit +// Ares made the weeder receive the "Guard" command instead +DEFINE_HOOK(0x446EAD, BuildingClass_GrandOpening_FreeWeeder_Mission, 0x6) +{ + GET(UnitClass*, pUnit, EDI); + + if (pUnit->Type->Weeder) + pUnit->ForceMission(Mission::Harvest); + + pUnit->NextMission(); + + return 0x446EB7; +} + +// Teleport cooldown for weeders +DEFINE_HOOK(0x719580, TeleportLocomotion_Weeder, 0x6) +{ + enum + { + Skip = 0x7195A0, + TeleportChargeTimer = 0x7195BC + }; + + GET(TeleportLocomotionClass*, pTeleport, ESI); + + if (pTeleport->Owner->WhatAmI() == AbstractType::Unit) + { + UnitClass* pUnit = (UnitClass*)pTeleport->Owner; + if (pUnit->Type->Harvester || pUnit->Type->Weeder) + return Skip; + } + + return TeleportChargeTimer; +} + +// DockUnload bypass for Weeders when teleporting +DEFINE_HOOK(0x7424BD, UnitClass_AssignDestination_Weeder_Teleport, 0x6) +{ + GET(BuildingTypeClass*, pDestination, ECX); + + return pDestination->DockUnload || pDestination->Weeder ? 0x7424CB : 0x7425DB; +} + +//// Skip check for Weeder so that weeders go through teleport stuff +//DEFINE_JUMP(LJMP, 0x73E844, 0x73E793) +// +// +//DEFINE_HOOK(0x73E84A, UnitClass_Mission_Harvest, 0x6) +//{ +// GET(UnitClass*, pUnit, EBP); +// +// bool isOnTiberium; +// if (pUnit->Type->Weeder) +// isOnTiberium = pUnit->MoveToWeed(RulesClass::Instance->TiberiumLongScan / Unsorted::LeptonsPerCell); +// else +// isOnTiberium = pUnit->MoveToTiberium(RulesClass::Instance->TiberiumLongScan / Unsorted::LeptonsPerCell); +// +// R->EBX(isOnTiberium); +// return 0x73E86B; +//} + +DEFINE_HOOK(0x73E9A0, UnitClass_Weeder_StopHarvesting, 0x6) +{ + enum + { + StopHarvesting = 0x73E9CA, + Skip = 0x73EA8D + }; + + GET(UnitClass*, pUnit, EBP); + + if ((pUnit->Type->Harvester || pUnit->Type->Weeder) && pUnit->GetStoragePercentage() == 1.0) + { + return StopHarvesting; + } + + return Skip; +} From 3948f049aa97afa4a5112c7598bad9088d15695d Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Wed, 20 Mar 2024 15:50:22 +0800 Subject: [PATCH 10/43] Update .coderabbit.yaml can't be discarded idk why --- .coderabbit.yaml | 18 +++++++++--------- docs/Fixed-or-Improved-Logics.md | 6 ++---- docs/Whats-New.md | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index f3afcbfca5..965dbb2866 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,10 +1,10 @@ -reviews: - request_changes_workflow: false - high_level_summary: true - poem: false - review_status: false - collapse_walkthrough: false - auto_review: - enabled: false -chat: +reviews: + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: false + collapse_walkthrough: false + auto_review: + enabled: false +chat: auto_reply: false \ No newline at end of file diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 6547d7d674..33c6d34558 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -59,8 +59,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - `IsSimpleDeployer` units now only play `DeploySound` and `UndeploySound` once, when done with (un)deploying instead of repeating it over duration of turning and/or `DeployingAnim`. - AITrigger can now recognize Building Upgrades as legal condition. - `EWGates` and `NSGates` now will link walls like `xxGateOne` and `xxGateTwo` do. -- Fixed the bug when occupied building's `MuzzleFlashX` is drawn on the center of the building when `X` goes past 10. -- Fixed jumpjet units that are `Crashable` not crashing to ground properly if destroyed while being pulled by a `Locomotor` warhead. - Fixed interaction of `UnitAbsorb` & `InfantryAbsorb` with `Grinding` buildings. The keys will now make the building only accept appropriate types of objects. - Fixed missing 'no enter' cursor for VehicleTypes being unable to enter a `Grinding` building. - Fixed Engineers being able to enter `Grinding` buildings even when they shouldn't (such as ally building at full HP). @@ -885,7 +883,7 @@ Ammo.AddOnDeploy=0 ; integer - Veinhole monsters now work like they used to in Tiberian Sun. - Their core parameters are still loaded from `[General]` -- The Warhead used by veins is specified under `[CombatDamage]`. The warhead has to be properly listed under `[Warheads]` as well. The warhead has to have `Veinhole=yes` set. +- The Warhead used by veins is specified under `[CombatDamage]`. The warhead has to be properly listed under `[Warheads]` as well. The warhead has to have `Veinhole=yes` set. - Veinholes are hardcoded to use several overlay types. - The vein attack animation specified under `[AudioVisual]` is what deals the damage. The animation has to be properly listed under `[Animations]` as well. - Units can be made immune to veins the same way as in Tiberian Sun. @@ -908,7 +906,7 @@ VeinDamage=5 ; integer VeinholeTypeClass=VEINTREE ; TerrainType [CombatDamage] -VeinholeWarhead=VeinholeWH ; Warhead +VeinholeWarhead=VeinholeWH ; Warhead [VeinholeWH] Veinhole=yes diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 40e7ea8544..7f2e6abcd2 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -424,7 +424,7 @@ Vanilla fixes: - Spawned aircraft now align with the spawner's facing when landing (by Starkku) - Fixed infantries attempted to entering buildings when waypointing together with engineer/agent/occupier/etc (by Trsdy) - Fixed jumpjet crash speed when crashing onto buildings (by NetsuNegi) -- Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone` (by Starkku) +- Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone` (by Starkku) - Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) From 477ee2afd9f41c772bb30baeeaba0b3820f75442 Mon Sep 17 00:00:00 2001 From: Starkku Date: Wed, 20 Mar 2024 11:45:51 +0200 Subject: [PATCH 11/43] Fix a minor error introduced in changelog --- docs/Whats-New.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 7f2e6abcd2..4c5621c609 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -426,7 +426,7 @@ Vanilla fixes: - Fixed jumpjet crash speed when crashing onto buildings (by NetsuNegi) - Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone` (by Starkku) -- Phobos fixes: +Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) - Add `ImmuneToCrit` for shields (by Trsdy) - Reimplemented the bugfix for jumpjet units' facing when firing, discard the inappropriate `JumpjetTurnToTarget` tag (by Trsdy) From 26f1b8a5e81c86c4ea5a8f92445283d13e09c52f Mon Sep 17 00:00:00 2001 From: Otamaa Date: Sat, 23 Mar 2024 16:32:07 +0700 Subject: [PATCH 12/43] - Fix crash that happen when `Unit` using `DroppodLocomotor` because of `DroppodType` is exclusive to `Infantry` - Fix crash when `TypeConvertGroup::Convert` is called with `nullptr` Owner and get checked with `EnumFunctions::CanTargetHouse` --- src/Ext/TechnoType/Body.cpp | 14 ++++---------- src/New/Type/Affiliated/TypeConvertGroup.cpp | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 41cba2c52a..272f923ffa 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -407,16 +407,10 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->InterceptorType.reset(); } - if (this->OwnerObject()->WhatAmI() == AbstractType::InfantryType) - { - if (this->DroppodType == nullptr) - this->DroppodType = std::make_unique(); - this->DroppodType->LoadFromINI(pINI, pSection); - } - else - { - this->DroppodType.reset(); - } + if (this->DroppodType == nullptr) + this->DroppodType = std::make_unique(); + + this->DroppodType->LoadFromINI(pINI, pSection); } template diff --git a/src/New/Type/Affiliated/TypeConvertGroup.cpp b/src/New/Type/Affiliated/TypeConvertGroup.cpp index 9cd91be5c7..d0ce1a70ee 100644 --- a/src/New/Type/Affiliated/TypeConvertGroup.cpp +++ b/src/New/Type/Affiliated/TypeConvertGroup.cpp @@ -7,7 +7,7 @@ void TypeConvertGroup::Convert(FootClass* pTargetFoot, const std::vectorOwner)) + if (pOwner && !EnumFunctions::CanTargetHouse(affectedHouses, pOwner, pTargetFoot->Owner)) continue; if (fromTypes.size()) From c51c92a8ec5b64913107fdbef9b208f14e88160c Mon Sep 17 00:00:00 2001 From: Starkku Date: Sat, 23 Mar 2024 12:09:06 +0200 Subject: [PATCH 13/43] Fix a regression that occured at some point with tunnels + shield anim handling --- src/Ext/Techno/Body.Update.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 33477166ee..771f7a73a8 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -22,7 +22,14 @@ void TechnoExt::ExtData::OnEarlyUpdate() if (!this->TypeExtData || this->TypeExtData->OwnerObject() != pType) this->UpdateTypeData(pType); - this->IsInTunnel = false; // TechnoClass::AI is only called when not in tunnel. + // Update tunnel state on exit, TechnoClass::AI is only called when not in tunnel. + if (this->IsInTunnel) + { + this->IsInTunnel = false; + + if (const auto pShieldData = this->Shield.get()) + pShieldData->SetAnimationVisibility(true); + } if (this->CheckDeathConditions()) return; From 96df1d39ddeaa37b753f1e2dc426407e34417156 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 23 Mar 2024 22:03:19 +0800 Subject: [PATCH 14/43] Revert and consider backward compatibility This reverts commit 26f1b8a5e81c86c4ea5a8f92445283d13e09c52f. --- src/Ext/TechnoType/Body.cpp | 14 ++++++++++---- src/New/Type/Affiliated/DroppodTypeClass.cpp | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 272f923ffa..41cba2c52a 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -407,10 +407,16 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->InterceptorType.reset(); } - if (this->DroppodType == nullptr) - this->DroppodType = std::make_unique(); - - this->DroppodType->LoadFromINI(pINI, pSection); + if (this->OwnerObject()->WhatAmI() == AbstractType::InfantryType) + { + if (this->DroppodType == nullptr) + this->DroppodType = std::make_unique(); + this->DroppodType->LoadFromINI(pINI, pSection); + } + else + { + this->DroppodType.reset(); + } } template diff --git a/src/New/Type/Affiliated/DroppodTypeClass.cpp b/src/New/Type/Affiliated/DroppodTypeClass.cpp index e60737de2a..676232925c 100644 --- a/src/New/Type/Affiliated/DroppodTypeClass.cpp +++ b/src/New/Type/Affiliated/DroppodTypeClass.cpp @@ -70,7 +70,11 @@ DEFINE_HOOK(0x4B5B70, DroppodLocomotionClass_ILoco_Process, 0x5) __assume(iloco != nullptr); auto const lThis = static_cast(iloco); auto const pLinked = lThis->LinkedTo; - const auto podType = TechnoTypeExt::ExtMap.Find(pLinked->GetTechnoType())->DroppodType.get(); + auto const linkedExt = TechnoExt::ExtMap.Find(pLinked); + const auto podType = linkedExt->TypeExtData->DroppodType.get(); + + if (!podType) + return 0;//You're not welcome CoordStruct oldLoc = pLinked->Location; @@ -137,6 +141,10 @@ DEFINE_HOOK(0x4B5B70, DroppodLocomotionClass_ILoco_Process, 0x5) if (dWpn && podType->Weapon_HitLandOnly) WeaponTypeExt::DetonateAt(dWpn, pLinked->Location, pLinked, pLinked->Owner); + auto& vec = linkedExt->LaserTrails; + if (!vec.empty()) + vec.erase(std::remove_if(vec.begin(), vec.end(), [](auto& trail) { return trail.Type->DroppodOnly; })); + pLinked->Mark(MarkType::Down); pLinked->SetHeight(0); pLinked->EnterIdleMode(false, true); @@ -162,13 +170,17 @@ DEFINE_HOOK(0x4B607D, DroppodLocomotionClass_ILoco_MoveTo, 0x8) GET(ILocomotion*, iloco, EDI); REF_STACK(CoordStruct, to, STACK_OFFSET(0x1C, 0x8)); __assume(iloco != nullptr); + auto const lThis = static_cast(iloco); auto const pLinked = lThis->LinkedTo; + const auto podType= TechnoTypeExt::ExtMap.Find(pLinked->GetTechnoType())->DroppodType.get(); + + if (!podType) + return 0; lThis->DestinationCoords = to; lThis->DestinationCoords.Z = MapClass::Instance->GetCellFloorHeight(to); - const auto podType = TechnoTypeExt::ExtMap.Find(pLinked->GetTechnoType())->DroppodType.get(); const int height = podType->Height.Get(RulesClass::Instance->DropPodHeight); const double angle = podType->Angle.Get(RulesClass::Instance->DropPodAngle); From 89753880edd04cf56761de69c7e61e89d1f1e576 Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 24 Mar 2024 16:58:32 +0200 Subject: [PATCH 15/43] Allow building upgrade anims to use power controls - Also add building upgrades to dump object command --- docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 1 + src/Commands/ObjectInfo.cpp | 13 +++++ src/Ext/BuildingType/Hooks.Upgrade.cpp | 68 ++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 33c6d34558..9db8ff734d 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -150,6 +150,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Spawned aircraft now align with the spawner's facing when landing. - Fixed the bug that waypointing unarmed infantries with agent/engineer/occupier to a spyable/capturable/occupiable building triggers EnteredBy event by executing capture mission. - Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone`. +- `PowerUpN` building animations can now use `Powered` & `PoweredLight/Effect/Special` keys. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 4c5621c609..1ebd7a08c4 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -365,6 +365,7 @@ New: - Re-enable the Veinhole Monster and Weeds from TS (by ZivDero) - Recreate the weed-charging of SWs like the TS Chemical Missile (by ZivDero) - Allow to change the speed of gas particles (by ZivDero) +- Allow upgrade animations to use `Powered` & `PoweredLight/Effect/Special` keys (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Commands/ObjectInfo.cpp b/src/Commands/ObjectInfo.cpp index 79ef8e8c36..aafd0e5002 100644 --- a/src/Commands/ObjectInfo.cpp +++ b/src/Commands/ObjectInfo.cpp @@ -158,6 +158,19 @@ void ObjectInfoCommandClass::Execute(WWKey eInput) const append("\n"); } + if (pBuilding->Type->Upgrades) + { + append("Upgrades (%d/%d): ", pBuilding->UpgradeLevel, pBuilding->Type->Upgrades); + for (int i = 0; i < 3; i++) + { + if (i != 0) + append(", "); + + append("Slot %d = %s", i+1, pBuilding->Upgrades[i] ? pBuilding->Upgrades[i]->get_ID() : ""); + } + append("\n"); + } + if (pBuilding->Type->Ammo > 0) append("Ammo = (%d / %d)\n", pBuilding->Ammo, pBuilding->Type->Ammo); diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index 4074ef5015..26446a3dba 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -122,3 +122,71 @@ DEFINE_HOOK(0x4F7877, HouseClass_CanBuild_UpgradesInteraction_WithoutAres, 0x5) } #pragma endregion + +#pragma region UpgradeAnimLogic + +// Parse Powered(Light|Effect|Special) keys for upgrade anims. +DEFINE_HOOK(0x4648B3, BuildingTypeClass_ReadINI_PowerUpAnims, 0x5) +{ + GET(BuildingTypeClass*, pThis, EBP); + GET(int, index, EBX); + + auto const pINI = &CCINIClass::INI_Art(); + auto const animData = &pThis->BuildingAnim[index - 1]; + + char buffer[0x20]; + + sprintf_s(buffer, "PowerUp%01dPowered", index); + animData->Powered = pINI->ReadBool(pThis->ImageFile, buffer, animData->Powered); + + sprintf_s(buffer, "PowerUp%01dPoweredLight", index); + animData->PoweredLight = pINI->ReadBool(pThis->ImageFile, buffer, animData->PoweredLight); + + sprintf_s(buffer, "PowerUp%01dPoweredEffect", index); + animData->PoweredEffect = pINI->ReadBool(pThis->ImageFile, buffer, animData->PoweredEffect); + + sprintf_s(buffer, "PowerUp%01dPoweredSpecial", index); + animData->PoweredSpecial = pINI->ReadBool(pThis->ImageFile, buffer, animData->PoweredSpecial); + + return 0; +} + +// Don't allow upgrade anims to be created if building is not upgraded or they require power to be shown and the building isn't powered. +static __forceinline bool AllowUpgradeAnim(BuildingClass* pBuilding, BuildingAnimSlot anim) +{ + auto const pType = pBuilding->Type; + + if (pType->Upgrades != 0 && anim >= BuildingAnimSlot::Upgrade1 && anim <= BuildingAnimSlot::Upgrade3 && !pBuilding->Anims[int(anim)]) + { + int upgradeLevel = pBuilding->UpgradeLevel - 1; + + if (upgradeLevel < 0 || (int)anim != upgradeLevel) + return false; + + auto const animData = pType->BuildingAnim[int(anim)]; + + if (((pType->Powered && pType->PowerDrain > 0 && (animData.PoweredLight || animData.PoweredEffect)) || + (pType->PoweredSpecial && animData.PoweredSpecial)) && + !(pBuilding->CurrentMission != Mission::Construction && pBuilding->CurrentMission != Mission::Selling && pBuilding->IsPowerOnline())) + { + return false; + } + } + + return true; +} + +DEFINE_HOOK(0x45189D, BuildingClass_AnimUpdate_Upgrades, 0x6) +{ + enum { SkipAnim = 0x451B2C }; + + GET(BuildingClass*, pThis, ESI); + GET_STACK(BuildingAnimSlot, anim, STACK_OFFSET(0x34, 0x8)); + + if (!AllowUpgradeAnim(pThis, anim)) + return SkipAnim; + + return 0; +} + +#pragma endregion From d9ad39400e89a5820328823ee7410b0869600749 Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 24 Mar 2024 18:04:22 +0200 Subject: [PATCH 16/43] Disable other projectile logics if projectile type has Trajectory set and output warning in log --- docs/New-or-Enhanced-Logics.md | 2 +- src/Ext/BulletType/Body.cpp | 36 ++++++++++++++++++++++++++++++++++ src/Ext/BulletType/Body.h | 2 ++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index d4273b4715..ceb1f54f5f 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -484,7 +484,7 @@ Currently interceptor weapons with projectiles that do not have `Inviso=true` wi ### Projectile trajectories - Projectiles can now have customizable trajectories. - - `Trajectory` should not be combined with original game's projectile trajectory logics (`Arcing`, `ROT` or `Inviso`). + - `Trajectory` should not be combined with original game's projectile trajectory logics (`Arcing`, `ROT`, `Vertical` or `Inviso`). Attempt to do so will result in the other logics being disabled and a warning being written to log file. - Initial speed of the projectile is defined by `Trajectory.Speed`, which unlike `Speed` used by `ROT` > 0 projectiles is defined on projectile not weapon. In `rulesmd.ini`: diff --git a/src/Ext/BulletType/Body.cpp b/src/Ext/BulletType/Body.cpp index 91278c3cb2..f7caa01a8c 100644 --- a/src/Ext/BulletType/Body.cpp +++ b/src/Ext/BulletType/Body.cpp @@ -63,6 +63,42 @@ void BulletTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) pSection = pThis->ImageFile; this->LaserTrail_Types.Read(exArtINI, pSection, "LaserTrail.Types"); + + this->TrajectoryValidation(); +} + +void BulletTypeExt::ExtData::TrajectoryValidation() const +{ + auto pThis = this->OwnerObject(); + const char* pSection = pThis->ID; + + // Trajectory validation combined with other projectile behaviour. + if (this->TrajectoryType) + { + if (pThis->Arcing) + { + Debug::Log("[Developer warning] [%s] has Trajectory set together with Arcing. Arcing has been set to false.\n", pSection); + pThis->Arcing = false; + } + + if (pThis->Inviso) + { + Debug::Log("[Developer warning] [%s] has Trajectory set together with Inviso. Inviso has been set to false.\n", pSection); + pThis->Inviso = false; + } + + if (pThis->ROT) + { + Debug::Log("[Developer warning] [%s] has Trajectory set together with ROT value other than 0. ROT has been set to 0.\n", pSection); + pThis->ROT = 0; + } + + if (pThis->Vertical) + { + Debug::Log("[Developer warning] [%s] has Trajectory set together with Vertical. Vertical has been set to false.\n", pSection); + pThis->Vertical = false; + } + } } template diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index fc259f94f5..1d17d39626 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -86,6 +86,8 @@ class BulletTypeExt private: template void Serialize(T& Stm); + + void TrajectoryValidation() const; }; class ExtContainer final : public Container { From b3fa1e26cb882e23cdfe2d155524d937750b7a55 Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 24 Mar 2024 23:28:32 +0200 Subject: [PATCH 17/43] Fix AAOnly not working as intended --- src/Ext/Techno/Hooks.Firing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index dded71a8bc..68f9e78d1f 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -273,7 +273,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) CellClass* pTargetCell = nullptr; // AAOnly doesn't need to be checked if LandTargeting=1. - if ((!pTechno || pTechno->GetTechnoType()->LandTargeting != LandTargetingType::Land_Not_OK) && pWeapon->Projectile->AA && pTarget && !pTarget->IsInAir()) + if (pThis->GetTechnoType()->LandTargeting != LandTargetingType::Land_Not_OK && pWeapon->Projectile->AA && pTarget && !pTarget->IsInAir()) { auto const pBulletTypeExt = BulletTypeExt::ExtMap.Find(pWeapon->Projectile); From 7ad350609382ca1da2100601843275e49bd7cdb0 Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 26 Mar 2024 14:17:57 +0200 Subject: [PATCH 18/43] Revert 24fc2f5961a67b579236495c250fb846f41d7d73 Infantry cannot handle pathfinding around TerrainTypes properly on MovementZones other than Infantry(Destroyer) and AmphibiousDestroyer --- YRpp | 2 +- docs/Fixed-or-Improved-Logics.md | 1 - docs/Whats-New.md | 1 - src/Ext/TerrainType/Hooks.Passable.cpp | 2 +- src/Misc/Hooks.BugFixes.cpp | 18 ------------------ 5 files changed, 2 insertions(+), 22 deletions(-) diff --git a/YRpp b/YRpp index 95aa94ee31..599fb0c1c1 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 95aa94ee3106e13e35da7be7d515a85aa5b5eb9b +Subproject commit 599fb0c1c12e962d8417b88b12713715f34b58b0 diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 9db8ff734d..b7c9a1277a 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -149,7 +149,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction. - Spawned aircraft now align with the spawner's facing when landing. - Fixed the bug that waypointing unarmed infantries with agent/engineer/occupier to a spyable/capturable/occupiable building triggers EnteredBy event by executing capture mission. -- Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone`. - `PowerUpN` building animations can now use `Powered` & `PoweredLight/Effect/Special` keys. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 1ebd7a08c4..01a2b653ba 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -425,7 +425,6 @@ Vanilla fixes: - Spawned aircraft now align with the spawner's facing when landing (by Starkku) - Fixed infantries attempted to entering buildings when waypointing together with engineer/agent/occupier/etc (by Trsdy) - Fixed jumpjet crash speed when crashing onto buildings (by NetsuNegi) -- Fixed infantry requiring `MovementZone=AmphibiousDestroyer` specifically to be able to use water sequences instead of any amphibious / water `MovementZone` (by Starkku) Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) diff --git a/src/Ext/TerrainType/Hooks.Passable.cpp b/src/Ext/TerrainType/Hooks.Passable.cpp index 9b3124df47..aad2fe8902 100644 --- a/src/Ext/TerrainType/Hooks.Passable.cpp +++ b/src/Ext/TerrainType/Hooks.Passable.cpp @@ -68,7 +68,7 @@ DEFINE_HOOK(0x483D87, CellClass_CheckPassability_PassableTerrain, 0x5) { if (pTypeExt->IsPassable) { - pThis->Passability = 0; + pThis->Passability = PassabilityType::Passable; return ReturnFromFunction; } } diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index a21f79db88..fe567754aa 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -809,21 +809,3 @@ DEFINE_JUMP(LJMP, 0x719CBC, 0x719CD8);//Teleport, notorious CLEG frozen state re DEFINE_JUMP(LJMP, 0x72A16A, 0x72A186);//Tunnel, not a big deal DEFINE_JUMP(LJMP, 0x663428, 0x663445);//Rocket, not a big deal DEFINE_JUMP(LJMP, 0x5170CE, 0x5170E0);//Hover, not a big deal - -// Allow infantry to use all amphibious/water movement zones and still display sequence correctly. -DEFINE_HOOK(0x51D793, InfantryClass_DoAction_MovementZoneCheck, 0x6) -{ - enum { Amphibious = 0x51D7A6, NotAmphibious = 0x51D8BF }; - - GET(InfantryClass*, pThis, ESI); - - auto const mZone = pThis->Type->MovementZone; - - if (mZone == MovementZone::Amphibious || mZone == MovementZone::AmphibiousDestroyer || mZone == MovementZone::AmphibiousCrusher || - mZone == MovementZone::Water || mZone == MovementZone::WaterBeach) - { - return Amphibious; - } - - return NotAmphibious; -} From 326ee96f4e3c02ee7c79ca8a35568fa703efe86d Mon Sep 17 00:00:00 2001 From: Starkku Date: Wed, 27 Mar 2024 11:17:08 +0200 Subject: [PATCH 19/43] Fix regression in veinhole code making CellAnim not work correctly --- src/Misc/Hooks.VeinholeMonster.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Misc/Hooks.VeinholeMonster.cpp b/src/Misc/Hooks.VeinholeMonster.cpp index 649608cf27..6bc3585578 100644 --- a/src/Misc/Hooks.VeinholeMonster.cpp +++ b/src/Misc/Hooks.VeinholeMonster.cpp @@ -95,9 +95,7 @@ DEFINE_HOOK(0x4243BC, AnimClass_Update_VeinholeAttack, 0x6) if (pAnim->Type->IsVeins) AnimExt::VeinAttackAI(pAnim); - GET(AnimClass*, pAnim2, ESI); - - return pAnim2->Type->IsTiberium ? + return pAnim->Type->IsAnimatedTiberium ? ContinueDrawTiberium : ContinueNotTiberium; } From cec9374e84df4996be38661041d0263f244c2597 Mon Sep 17 00:00:00 2001 From: Kirill Andriiashin Date: Thu, 28 Mar 2024 13:58:38 +0300 Subject: [PATCH 20/43] Refactor hooks in veinhole code (#1233) --- src/Ext/Anim/Body.cpp | 3 --- src/Misc/Hooks.VeinholeMonster.cpp | 25 ++++--------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 3b90aef9ca..e5d3ec467e 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -117,9 +117,6 @@ void AnimExt::VeinAttackAI(AnimClass* pAnim) { ObjectClass* pNext = pOccupier->NextObject; int damage = RulesClass::Instance->VeinDamage; - - abstract_cast(pOccupier); - TechnoClass* pTechno = abstract_cast(pOccupier); if (pTechno && !pTechno->GetTechnoType()->ImmuneToVeins && !pTechno->HasAbility(Ability::VeinProof) diff --git a/src/Misc/Hooks.VeinholeMonster.cpp b/src/Misc/Hooks.VeinholeMonster.cpp index 6bc3585578..257129fcd4 100644 --- a/src/Misc/Hooks.VeinholeMonster.cpp +++ b/src/Misc/Hooks.VeinholeMonster.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -22,26 +21,17 @@ // Loads the veinhole monster art // Call removed from YR by WW -DEFINE_HOOK(0x4AD097, DisplayClass_ReadIni_LoadVeinholeArt, 0x5) +DEFINE_HOOK(0x4AD097, DisplayClass_ReadIni_LoadVeinholeArt, 0x6) { - enum { ContinueReadIni = 0x4AD0A8 }; - int theater = static_cast(ScenarioClass::Instance->Theater); - SmudgeTypeClass::LoadFromIniList(theater); VeinholeMonsterClass::LoadVeinholeArt(theater); - return ContinueReadIni; + return 0; } // Applies damage to the veinhole monster DEFINE_HOOK(0x489671, Damage_at_Cell_Update_Veinhole, 0x6) { - enum - { - ContinueDrawWall = 0x48967B, - ContinueNotWall = 0x4896B2 - }; - GET(OverlayTypeClass*, pOverlay, EAX); GET(WarheadTypeClass*, pWH, ESI); GET_STACK(CellStruct, pCell, STACK_OFFSET(0xE0, -0x4C)); @@ -55,7 +45,7 @@ DEFINE_HOOK(0x489671, Damage_at_Cell_Update_Veinhole, 0x6) pVeinhole->ReceiveDamage(&damage, 0, pWH, pAttacker, false, false, pAttackingHouse); } - return pOverlay->Wall ? ContinueDrawWall : ContinueNotWall; + return 0; } DEFINE_HOOK(0x6D4656, TacticalClass_Draw_Veinhole, 0x5) @@ -84,19 +74,12 @@ DEFINE_HOOK(0x55B4E1, LogicClass_Update_Veinhole, 0x5) // Handles the veins' attack animation DEFINE_HOOK(0x4243BC, AnimClass_Update_VeinholeAttack, 0x6) { - enum - { - ContinueDrawTiberium = 0x4243CC, - ContinueNotTiberium = 0x42442E - }; - GET(AnimClass*, pAnim, ESI); if (pAnim->Type->IsVeins) AnimExt::VeinAttackAI(pAnim); - return pAnim->Type->IsAnimatedTiberium ? - ContinueDrawTiberium : ContinueNotTiberium; + return 0; } /// From 5e1960f8bd7491331220471f571f0ca79e92b026 Mon Sep 17 00:00:00 2001 From: Starkku Date: Fri, 29 Mar 2024 15:47:08 +0200 Subject: [PATCH 21/43] Fix issue causing overlay custom palette being reset on load --- src/Ext/OverlayType/Body.cpp | 7 ++++++- src/Ext/OverlayType/Body.h | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Ext/OverlayType/Body.cpp b/src/Ext/OverlayType/Body.cpp index f30da5352f..ae9ff93bc6 100644 --- a/src/Ext/OverlayType/Body.cpp +++ b/src/Ext/OverlayType/Body.cpp @@ -15,7 +15,6 @@ void OverlayTypeExt::ExtData::Serialize(T& Stm) { Stm .Process(this->PaletteFile) - .Process(this->Palette) ; } @@ -35,6 +34,11 @@ void OverlayTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->PaletteFile.Read(pArtINI, pArtSection, "Palette"); + BuildPalette(); +} + +void OverlayTypeExt::ExtData::BuildPalette() +{ if (GeneralUtils::IsValidString(this->PaletteFile)) { char pFilename[0x20]; @@ -48,6 +52,7 @@ void OverlayTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { Extension::LoadFromStream(Stm); this->Serialize(Stm); + this->BuildPalette(); } void OverlayTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index eb41abc9aa..c659a52beb 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -18,7 +18,7 @@ class OverlayTypeExt { public: PhobosFixedString<32u> PaletteFile; - DynamicVectorClass* Palette; + DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. ExtData(OverlayTypeClass* OwnerObject) : Extension(OwnerObject) , PaletteFile {} @@ -37,6 +37,7 @@ class OverlayTypeExt private: template void Serialize(T& Stm); + void BuildPalette(); }; class ExtContainer final : public Container From 5143aa1832e1005ebb46c38312c535bdb6bf0b9b Mon Sep 17 00:00:00 2001 From: Starkku Date: Fri, 29 Mar 2024 16:10:40 +0200 Subject: [PATCH 22/43] Allow toggling Explodes to not work during buildup/being sold --- docs/Fixed-or-Improved-Logics.md | 6 +++++- docs/Whats-New.md | 1 + src/Ext/Techno/Hooks.cpp | 10 ++++++++-- src/Ext/TechnoType/Body.cpp | 3 ++- src/Ext/TechnoType/Body.h | 2 ++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index b7c9a1277a..c33eb8d961 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -565,14 +565,18 @@ In `rulesmd.ini`: Storage.TiberiumIndex=-1 ; integer, [Tiberiums] list index ``` -### Exploding unit passenger killing customization +### Exploding object customizations - By default `Explodes=true` TechnoTypes have all of their passengers killed when they are destroyed. This behaviour can now be disabled by setting `Explodes.KillPassengers=false`. +- BuildingTypes with `Explodes=true` can by default explode even when they are still being built or sold. This can be disabled by setting `Explodes.DuringBuildup` to false. This causes them to behave as if `Explodes` was set to false while being built up or sold. In `rulesmd.ini`: ```ini [SOMETECHNO] ; TechnoType Explodes.KillPassengers=true ; boolean + +[SOMEBUILDING] ; BuildingType +Explodes.DuringBuildup=true ; boolean ``` ### IronCurtain effects on organics customization diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 01a2b653ba..2ae8bf0c68 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -366,6 +366,7 @@ New: - Recreate the weed-charging of SWs like the TS Chemical Missile (by ZivDero) - Allow to change the speed of gas particles (by ZivDero) - Allow upgrade animations to use `Powered` & `PoweredLight/Effect/Special` keys (by Starkku) +- Toggle for `Explodes=true` BuildingTypes to not explode during buildup or being sold (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index e8cd5cf778..cd647ab6e4 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -159,14 +159,20 @@ DEFINE_HOOK(0x701DFF, TechnoClass_ReceiveDamage_FlyingStrings, 0x7) return 0; } -DEFINE_HOOK(0x70265F, TechnoClass_ReceiveDamage_Explodes, 0x6) +DEFINE_HOOK(0x702603, TechnoClass_ReceiveDamage_Explodes, 0x6) { - enum { SkipKillingPassengers = 0x702669 }; + enum { SkipExploding = 0x702672, SkipKillingPassengers = 0x702669 }; GET(TechnoClass*, pThis, ESI); const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + if (pThis->WhatAmI() == AbstractType::Building) + { + if (!pTypeExt->Explodes_DuringBuildup && (pThis->CurrentMission == Mission::Construction || pThis->CurrentMission == Mission::Selling)) + return SkipExploding; + } + if (!pTypeExt->Explodes_KillPassengers) return SkipKillingPassengers; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 41cba2c52a..155108dc7b 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -241,6 +241,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->IronCurtain_KillWarhead.Read(exINI, pSection, "IronCurtain.KillWarhead"); this->Explodes_KillPassengers.Read(exINI, pSection, "Explodes.KillPassengers"); + this->Explodes_DuringBuildup.Read(exINI, pSection, "Explodes.DuringBuildup"); this->DeployFireWeapon.Read(exINI, pSection, "DeployFireWeapon"); this->TargetZoneScanType.Read(exINI, pSection, "TargetZoneScanType"); @@ -532,7 +533,6 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->SelfHealGainType) .Process(this->Passengers_SyncOwner) .Process(this->Passengers_SyncOwner_RevertOnExit) - .Process(this->Explodes_KillPassengers) .Process(this->PronePrimaryFireFLH) .Process(this->ProneSecondaryFireFLH) @@ -548,6 +548,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->IronCurtain_KillWarhead) .Process(this->Explodes_KillPassengers) + .Process(this->Explodes_DuringBuildup) .Process(this->DeployFireWeapon) .Process(this->TargetZoneScanType) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 03df138158..13356cb630 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -149,6 +149,7 @@ class TechnoTypeExt Nullable IronCurtain_Effect; Nullable IronCurtain_KillWarhead; Valueable Explodes_KillPassengers; + Valueable Explodes_DuringBuildup; Nullable DeployFireWeapon; Valueable TargetZoneScanType; @@ -331,6 +332,7 @@ class TechnoTypeExt , IronCurtain_KillWarhead {} , Explodes_KillPassengers { true } + , Explodes_DuringBuildup { true } , DeployFireWeapon {} , TargetZoneScanType { TargetZoneScanType::Same } From 7d0722cfffb6d001dd8851865e60ed2d922a20a2 Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 2 Apr 2024 17:31:16 +0300 Subject: [PATCH 23/43] Simplify a TerrainType passability hook --- src/Ext/TerrainType/Hooks.Passable.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Ext/TerrainType/Hooks.Passable.cpp b/src/Ext/TerrainType/Hooks.Passable.cpp index aad2fe8902..9dffa82953 100644 --- a/src/Ext/TerrainType/Hooks.Passable.cpp +++ b/src/Ext/TerrainType/Hooks.Passable.cpp @@ -55,28 +55,23 @@ DEFINE_HOOK(0x7002E9, TechnoClass_WhatAction_PassableTerrain, 0x5) } // Passable TerrainTypes Hook #3 - Count passable TerrainTypes as completely passable. -DEFINE_HOOK(0x483D87, CellClass_CheckPassability_PassableTerrain, 0x5) +DEFINE_HOOK(0x483DDF, CellClass_CheckPassability_PassableTerrain, 0x6) { - enum { SkipToNextObject = 0x483DCD, ReturnFromFunction = 0x483E25, BreakFromLoop = 0x483DDF }; + enum { ReturnFromFunction = 0x483E25 }; GET(CellClass*, pThis, EDI); - GET(ObjectClass*, pObject, ESI); + GET(TerrainClass*, pTerrain, ESI); - if (auto const pTerrain = abstract_cast(pObject)) + if (auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pTerrain->Type)) { - if (auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pTerrain->Type)) + if (pTypeExt->IsPassable) { - if (pTypeExt->IsPassable) - { - pThis->Passability = PassabilityType::Passable; - return ReturnFromFunction; - } + pThis->Passability = PassabilityType::Passable; + return ReturnFromFunction; } - - return BreakFromLoop; } - return SkipToNextObject; + return 0; } // Passable TerrainTypes Hook #4 - Make passable for vehicles. From 59bf7487bb6142416bc5e1490eab9e724bd92f78 Mon Sep 17 00:00:00 2001 From: ststl <103478314+ststl-s@users.noreply.github.com> Date: Tue, 9 Apr 2024 00:07:11 +0800 Subject: [PATCH 24/43] Fix bug with center alignment and negative spacing (#1166) Co-authored-by: ststl --- src/New/Type/DigitalDisplayTypeClass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/New/Type/DigitalDisplayTypeClass.cpp b/src/New/Type/DigitalDisplayTypeClass.cpp index 3124a53a49..8d9929c9c1 100644 --- a/src/New/Type/DigitalDisplayTypeClass.cpp +++ b/src/New/Type/DigitalDisplayTypeClass.cpp @@ -137,8 +137,8 @@ void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int va } case TextAlign::Center: { - position.X -= valueString.length() * spacing.X / 2; - position.Y += valueString.length() * spacing.Y / 2; + position.X -= static_cast(valueString.length()) * spacing.X / 2; + position.Y += static_cast(valueString.length()) * spacing.Y / 2; break; } case TextAlign::Right: From b6dfbc34c222836194f2907c35025f52f626385b Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Tue, 9 Apr 2024 00:31:20 +0800 Subject: [PATCH 25/43] Jumpjet & Fly loco visual tilt fix --- CREDITS.md | 4 +-- docs/Fixed-or-Improved-Logics.md | 4 +-- docs/New-or-Enhanced-Logics.md | 2 +- src/Ext/Aircraft/Hooks.cpp | 22 ++++++++++++++ src/Ext/Building/Body.cpp | 2 +- src/Ext/Unit/Hooks.Jumpjet.cpp | 50 +++++++++++++++++++------------- 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index fd236d94c2..5ac7d407a4 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -251,7 +251,7 @@ This page lists all the individual contributions to the project by their author. - Facing towards target even if not omni-firing - Turret direction in idle state fix - Sensor fix - - Allow to tilt on ground + - Allow to tilt regardless of TiltCrashJumpjet - Forbid firing when crashing - OmniFire.TurnToTarget - Object Self-destruction logic @@ -290,7 +290,7 @@ This page lists all the individual contributions to the project by their author. - TechnoType conversion placeholder - EIP 00529A14 crash fix on Linux - Teleport timer reset after load game fix - - Teleport and Tunnel loco visual tilt fix + - Teleport, Tunnel and Fly loco visual tilt fix - Skip units' turret rotation and jumpjets' wobbling under EMP - Droppod properties dehardcode - Waypoint entering building together with engineer/agent bug fix diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index c33eb8d961..2336d4d5d6 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -100,7 +100,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Animation with `Tiled=yes` now supports `CustomPalette`. - Attempted to avoid units from retaining previous orders (attack,grind,garrison,etc) after changing ownership (mind-control,abduction,etc). - Fixed buildings' `NaturalParticleSystem` being created for in-map pre-placed structures. -- Fixed jumpjet units being unable to visually tilt or be flipped over on the ground if `TiltCrashJumpjet=no`. +- Fixed jumpjet units being unable to visually tilt or be flipped if `TiltCrashJumpjet=no`. - Unlimited (more than 5) `AlternateFLH` entries for units. - Warheads spawning debris now use `MaxDebris` as an actual cap for number of debris to spawn instead of `MaxDebris` - 1. If both `Primary` and `Secondary` weapons can fire at air targets (projectile has `AA=true`), `Primary` can now be picked instead of always forcing `Secondary`. Also applies to `IsGattling=true`, with odd-numbered and even-numbered `WeaponX` slots instead of `Primary` and `Secondary`, respectively. @@ -145,7 +145,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed railgun and fire particles being cut off by elevation changes. - Fixed teleport units' (for example CLEG) frozen-still timer being cleared after load game. - Fixed teleport units being unable to visually tilt on slopes. -- Fixed units with Teleport or Tunnel locomotor being unable to be visually flipped like other locomotors do. +- Fixed units with Teleport, Tunnel or Fly locomotor being unable to be visually flipped like other locomotors do. - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction. - Spawned aircraft now align with the spawner's facing when landing. - Fixed the bug that waypointing unarmed infantries with agent/engineer/occupier to a spyable/capturable/occupiable building triggers EnteredBy event by executing capture mission. diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index ceb1f54f5f..5f1933200e 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -822,7 +822,7 @@ ForceWeapon.Disguised=-1 ; integer. 0 for primary weapon, 1 for secondary ### Make units try turning to target when firing with `OmniFire=yes` - The unit will try to turn the body to target even firing with `OmniFire=yes` - - Recommended for jumpjets if you want it to turn to target when firing. + - Jumpjets are recommended to have the same value of body `ROT` and `JumpjetTurnRate` In `rulesmd.ini`: ```ini diff --git a/src/Ext/Aircraft/Hooks.cpp b/src/Ext/Aircraft/Hooks.cpp index 19719a31c8..c63bde4033 100644 --- a/src/Ext/Aircraft/Hooks.cpp +++ b/src/Ext/Aircraft/Hooks.cpp @@ -167,3 +167,25 @@ DEFINE_HOOK(0x44402E, BuildingClass_ExitObject_PoseDir2, 0x5) #pragma endregion +DEFINE_HOOK(0x4CF68D, FlyLocomotionClass_DrawMatrix_OnAirport, 0x5) +{ + GET(ILocomotion*, iloco, ESI); + __assume(iloco != nullptr); + auto loco = static_cast(iloco); + auto pThis = static_cast(loco->LinkedTo); + if (loco->AirportBound && loco->CurrentSpeed == 0.0 && pThis->GetHeight() <= 0) + { + float ars = pThis->AngleRotatedSideways; + float arf = pThis->AngleRotatedForwards; + if (std::abs(ars) > 0.005 || std::abs(arf) > 0.005) + { + LEA_STACK(Matrix3D*, mat, STACK_OFFSET(0x38, -0x30)); + mat->TranslateZ(float(std::abs(Math::sin(ars)) * pThis->Type->VoxelScaleX + + std::abs(Math::sin(arf)) * pThis->Type->VoxelScaleY)); + R->ECX(pThis); + return 0x4CF6AD; + } + } + + return 0; +} diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index e6158b4116..07a52c24fe 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -417,7 +417,7 @@ DEFINE_HOOK(0x453E20, BuildingClass_SaveLoad_Prefix, 0x5) return 0; } -DEFINE_HOOK(0x454174, BuildingClass_Load, 0xA) +DEFINE_HOOK(0x454174, BuildingClass_Load_LightSource, 0xA) { GET(BuildingClass*, pThis, EDI); diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index 5a837e316a..c2e73dccfd 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -4,9 +4,10 @@ #include #include -// Bugfix: Jumpjet turn to target when attacking - -// Jumpjets stuck at FireError::FACING because WW didn't use a correct facing +// Misc jumpjet facing, turning, drawing fix, Misc loco drawing fix -- Author: Trsdy +// Jumpjets stuck at FireError::FACING because Jumpjet has its own facing just for JumpjetTurnRate +// We should not touch the linked unit's PrimaryFacing when it's moving and just let the loco sync this shit in 54D692 +// The body facing never actually turns, it just syncs DEFINE_HOOK(0x736F78, UnitClass_UpdateFiring_FireErrorIsFACING, 0x6) { GET(UnitClass* const, pThis, ESI); @@ -79,6 +80,17 @@ DEFINE_HOOK(0x736EE9, UnitClass_UpdateFiring_FireErrorIsOK, 0x6) return 0; } +void __stdcall JumpjetLocomotionClass_DoTurn(ILocomotion* iloco, DirStruct dir) +{ + __assume(iloco != nullptr); + // This seems to be used only when unloading shit on the ground + // Rewrite just in case + auto pThis = static_cast(iloco); + pThis->LocomotionFacing.SetDesired(dir); + pThis->LinkedTo->PrimaryFacing.SetDesired(dir); +} +DEFINE_JUMP(VTABLE, 0x7ECDB4, GET_OFFSET(JumpjetLocomotionClass_DoTurn)) + DEFINE_HOOK(0x54D326, JumpjetLocomotionClass_MovementAI_CrashSpeedFix, 0x6) { GET(JumpjetLocomotionClass*, pThis, ESI); @@ -115,10 +127,9 @@ DEFINE_HOOK(0x736BA3, UnitClass_UpdateRotation_TurretFacing_Jumpjet, 0x6) // When jumpjets arrived at their FootClass::Destination, they seems stuck at the Move mission // and therefore the turret facing was set to DirStruct{atan2(0,0)}==DirType::East at 0x736BBB // that's why they will come back to normal when giving stop command explicitly - auto pType = pThis->Type; // so the best way is to fix the Mission if necessary, but I don't know how to do it - // so I skipped jumpjets check temporarily, and in most cases Jumpjet/BallonHover should cover most of it - if (!pType->TurretSpins && (pType->JumpJet || pType->BalloonHover)) + // so I skipped jumpjets check temporarily + if (!pThis->Type->TurretSpins && locomotion_cast(pThis->Locomotor)) return SkipCheckDestination; return 0; @@ -153,30 +164,29 @@ DEFINE_HOOK(0x54CB0E, JumpjetLocomotionClass_State5_CrashSpin, 0x7) return pTypeExt->JumpjetRotateOnCrash ? 0 : 0x54CB3E; } - -// These are subject to changes if someone wants to properly implement jumpjet tilting -DEFINE_HOOK(0x54DCCF, JumpjetLocomotionClass_DrawMatrix_TiltCrashJumpjet, 0x5) +// We no longer explicitly check TiltCrashJumpjet when drawing, do it when crashing +DEFINE_HOOK(0x70B649, TechnoClass_RigidBodyDynamics_NoTiltCrashBlyat, 0x6) { - GET(ILocomotion*, iloco, ESI); - __assume(iloco != nullptr); - //if (static_cast(iloco)->State < JumpjetLocomotionClass::State::Crashing) - if (static_cast(iloco)->State == JumpjetLocomotionClass::State::Grounded) - return 0x54DCE8; + GET(FootClass*, pThis, ESI); + + if (locomotion_cast(pThis->Locomotor) && !pThis->GetTechnoType()->TiltCrashJumpjet) + return 0x70BCA4; return 0; } -/* +DEFINE_JUMP(LJMP, 0x54DCCF, 0x54DCE8);//JumpjetLocomotionClass_DrawMatrix_NoTiltCrashJumpjetHereBlyat +// and the tilt center looked bad visually DEFINE_HOOK(0x54DD3D, JumpjetLocomotionClass_DrawMatrix_AxisCenterInAir, 0x5) { GET(ILocomotion*, iloco, ESI); __assume(iloco != nullptr); - auto state = static_cast(iloco)->State; - if (state && state < JumpjetLocomotionClass::State::Crashing) - return 0x54DE88; - return 0; + + if (static_cast(iloco)->State == JumpjetLocomotionClass::State::Grounded) + return 0; + + return 0x54DE88; } -*/ FireError __stdcall JumpjetLocomotionClass_Can_Fire(ILocomotion* pThis) { From ca339aa69437734f742853d65c95ad34842ed9d6 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Tue, 9 Apr 2024 00:42:06 +0800 Subject: [PATCH 26/43] Type conversion upon ownership change (#1215) --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 10 ++++++++++ docs/Whats-New.md | 1 + src/Ext/House/Hooks.cpp | 11 +++++++++++ src/Ext/TechnoType/Body.cpp | 4 ++++ src/Ext/TechnoType/Body.h | 5 +++++ 6 files changed, 32 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 5ac7d407a4..d932ea33a6 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -288,6 +288,7 @@ This page lists all the individual contributions to the project by their author. - Permanent healthbar display on units targeted by temporal weapons fix - Powered anims on buildings cease playing upon capture by different house fix - TechnoType conversion placeholder + - TechnoType conversion upon ownership change - EIP 00529A14 crash fix on Linux - Teleport timer reset after load game fix - Teleport, Tunnel and Fly loco visual tilt fix diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 5f1933200e..ecbe892777 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1102,6 +1102,16 @@ Convert.To= ; TechnoType Convert.AffectedHouses=all ; list of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) ``` +### Convert TechnoType on owner house change +- You can now change a unit's type when changing ownership from human to computer or from computer to human. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] +Convert.HumanToComputer = ; TechnoType +Convert.ComputerToHuman = ; TechnoType +``` + ### Custom 'SplashList' on Warheads ![image](_static/images/splashlist-01.gif) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 2ae8bf0c68..5be1d519e4 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -320,6 +320,7 @@ New: - `UndeploysInto` building selling buildup sequence length customization (by Starkku) - Allow overriding `Shield.AffectTypes` for each Warhead shield interaction (by Starkku) - TechnoType conversion warhead & superweapon (by Morton) +- TechnoType conversion on ownership change (by Trsdy) - Unlimited skirmish colors (by Morton) - Example custom locomotor that circles around the target (*NOTE: For developer use only*) (by Kerbiter, CCHyper, with help from Otamaa; based on earlier experiment by CnCVK) - Vehicle voxel turret shadows & body multi-section shadows (by TwinkleStar) diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index 4ec1639554..91fa947181 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -222,6 +222,17 @@ DEFINE_HOOK(0x7015C9, TechnoClass_Captured_UpdateTracking, 0x6) pNewOwnerExt->OwnedAutoDeathObjects.push_back(pExt); } + if (auto pMe = generic_cast(pThis)) + { + bool I_am_human = pThis->Owner->IsControlledByHuman(); + bool You_are_human = pNewOwner->IsControlledByHuman(); + auto pConvertTo = (I_am_human && !You_are_human) ? pExt->TypeExtData->Convert_HumanToComputer.Get() : + (!I_am_human && You_are_human) ? pExt->TypeExtData->Convert_ComputerToHuman.Get() : nullptr; + + if (pConvertTo && pConvertTo->WhatAmI() == pType->WhatAmI()) + TechnoExt::ConvertToType(pMe, pConvertTo); + } + return 0; } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 155108dc7b..92115f0baa 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -275,6 +275,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->SpawnHeight.Read(exINI, pSection, "SpawnHeight"); this->LandingDir.Read(exINI, pSection, "LandingDir"); + this->Convert_HumanToComputer.Read(exINI, pSection, "Convert.HumanToComputer"); + this->Convert_ComputerToHuman.Read(exINI, pSection, "Convert.ComputerToHuman"); // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -585,6 +587,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->SpawnHeight) .Process(this->LandingDir) .Process(this->DroppodType) + .Process(this->Convert_HumanToComputer) + .Process(this->Convert_ComputerToHuman) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 13356cb630..9622c4ae67 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -186,6 +186,9 @@ class TechnoTypeExt Nullable SpawnHeight; Nullable LandingDir; + Valueable Convert_HumanToComputer; + Valueable Convert_ComputerToHuman; + struct LaserTrailDataEntry { ValueableIdx idxType; @@ -369,6 +372,8 @@ class TechnoTypeExt , SpawnHeight {} , LandingDir {} , DroppodType {} + , Convert_HumanToComputer { } + , Convert_ComputerToHuman { } { } virtual ~ExtData() = default; From 02d948ab80f506847955fd100ca76cf755909078 Mon Sep 17 00:00:00 2001 From: Starkku Date: Mon, 8 Apr 2024 19:52:17 +0300 Subject: [PATCH 27/43] Voxel turret & multi-section shadow reimplementation (#1209) * rewrite again * Improvements & customization options to shadow scaling plus docs * exponentially shrink the shadow with Pade 2/2 should be smarter than WW's LUTs --------- Co-authored-by: chaserli <914137150@qq.com> --- CREDITS.md | 1 + docs/Fixed-or-Improved-Logics.md | 25 ++- docs/Whats-New.md | 3 +- src/Ext/Rules/Body.cpp | 10 + src/Ext/Rules/Body.h | 9 + src/Ext/TechnoType/Body.cpp | 20 +- src/Ext/TechnoType/Body.h | 7 +- src/Ext/TechnoType/Hooks.cpp | 358 ++++++++++++++++++++++++++++--- src/Utilities/GeneralUtils.h | 2 +- 9 files changed, 395 insertions(+), 40 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index d932ea33a6..c839802f1e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -292,6 +292,7 @@ This page lists all the individual contributions to the project by their author. - EIP 00529A14 crash fix on Linux - Teleport timer reset after load game fix - Teleport, Tunnel and Fly loco visual tilt fix + - Turret/Barrel/NoSpawnAlt/Multi-section voxel shadow, dynamic voxel shadow - Skip units' turret rotation and jumpjets' wobbling under EMP - Droppod properties dehardcode - Waypoint entering building together with engineer/agent bug fix diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 2336d4d5d6..a40bbc48b8 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -688,11 +688,30 @@ NoWobbles=false ; boolean ### Voxel body multi-section shadows - It is also now possible for vehicles and aircraft to display shadows for multiple sections of the voxel body at once, instead of just one section specified by `ShadowIndex`, by specifying the section indices in `ShadowIndices` (which defaults to `ShadowIndex`) in unit's `artmd.ini` entry. + - `ShadowIndex.Frame` and `ShadowIndices.Frame` can be used to customize which frame of the HVA animation for the section from `ShadowIndex` and `ShadowIndices` is used to display the shadow, respectively. -1 is special value which means currently shown frame is used, and `ShadowIndices.Frame` defaults to this. In `artmd.ini`: ```ini -[SOMETECHNO] ; TechnoType -ShadowIndices= ; list of integers (voxel section indices) +[SOMETECHNO] ; TechnoType +ShadowIndices= ; list of integers (voxel section indices) +ShadowIndex.Frame=0 ; integer (HVA animation frame index) +ShadowIndices.Frame= ; list of integers (HVA animation frame indices) +``` + +### Voxel shadow scaling in air + +- It is now possible to adjust how voxel air units (`VehicleType` & `AircraftType`) shadows scale in air. By default the shadows scale by `AirShadowBaseScale` (defaults to 0.5) amount if unit is `ConsideredAircraft=true`. + - If `HeightShadowScaling=true`, the shadow is scaled by amount that is determined by following formula: `Max(AirShadowBaseScale ^ (currentHeight / ShadowSizeCharacteristicHeight), HeightShadowScaling.MinScale)`, where `currentHeight` is unit's current height in leptons, `ShadowSizeCharacteristicHeight` overrideable value that defaults to the maximum cruise height (`JumpjetHeight`, `FlightLevel` etc) and `HeightShadowScaling.MinScale` sets a floor for the scale. + +In `rulesmd.ini`: +```ini +[AudioVisual] +AirShadowBaseScale=0.5 ; floating point value +HeightShadowScaling=false ; boolean +HeightShadowScaling.MinScale=0.0 ; floating point value + +[SOMETECHNO] ; TechnoType +ShadowSizeCharacteristicHeight= ; integer, height in leptons ``` ### Forbid parallel AI queues @@ -837,6 +856,8 @@ IronCurtain.KeptOnDeploy= ; boolean, default to [CombatDamage]->IronCurtain.K ### Voxel turret shadow - Vehicle voxel turrets can now draw shadows if `[AudioVisual]` -> `DrawTurretShadow` is set to true. This can be overridden per VehicleType by setting `TurretShadow` in the vehicle's `artmd.ini` section. + - If you don't want to render the body's shadow at all, set `ShadowIndex` to an invalid number. + In `rulesmd.ini`: ```ini [AudioVisual] diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 5be1d519e4..79ec5b882a 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -323,7 +323,7 @@ New: - TechnoType conversion on ownership change (by Trsdy) - Unlimited skirmish colors (by Morton) - Example custom locomotor that circles around the target (*NOTE: For developer use only*) (by Kerbiter, CCHyper, with help from Otamaa; based on earlier experiment by CnCVK) -- Vehicle voxel turret shadows & body multi-section shadows (by TwinkleStar) +- Vehicle voxel turret shadows & body multi-section shadows (by TwinkleStar & Trsdy) - Crushing tilt and slowdown customization (by Starkku) - Extra warhead detonations on weapon (by Starkku) - Chrono sparkle animation display customization and improvements (by Starkku) @@ -368,6 +368,7 @@ New: - Allow to change the speed of gas particles (by ZivDero) - Allow upgrade animations to use `Powered` & `PoweredLight/Effect/Special` keys (by Starkku) - Toggle for `Explodes=true` BuildingTypes to not explode during buildup or being sold (by Starkku) +- Toggleable height-based shadow scaling for voxel air units (by Trsdy & Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index ecd6f08a5f..58bb324be9 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -117,6 +117,13 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->AnimRemapDefaultColorScheme.Read(exINI, GameStrings::AudioVisual, "AnimRemapDefaultColorScheme"); this->TimerBlinkColorScheme.Read(exINI, GameStrings::AudioVisual, "TimerBlinkColorScheme"); this->ShowDesignatorRange.Read(exINI, GameStrings::AudioVisual, "ShowDesignatorRange"); + NullableAirShadowBaseScale; + AirShadowBaseScale.Read(exINI, GameStrings::AudioVisual, "AirShadowBaseScale"); + if (AirShadowBaseScale.isset() && AirShadowBaseScale.Get() > 0) + this->AirShadowBaseScale_log = -std::log(std::min(AirShadowBaseScale.Get(), 1.0)); + + this->HeightShadowScaling.Read(exINI, GameStrings::AudioVisual, "HeightShadowScaling"); + this->HeightShadowScaling_MinScale.Read(exINI, GameStrings::AudioVisual, "HeightShadowScaling.MinScale"); this->AllowParallelAIQueues.Read(exINI, "GlobalControls", "AllowParallelAIQueues"); this->ForbidParallelAIQueues_Aircraft.Read(exINI, "GlobalControls", "ForbidParallelAIQueues.Infantry"); @@ -260,6 +267,9 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->Pips_Tiberiums_DisplayOrder) .Process(this->Pips_Tiberiums_WeedFrame) .Process(this->Pips_Tiberiums_WeedEmptyFrame) + .Process(this->AirShadowBaseScale_log) + .Process(this->HeightShadowScaling) + .Process(this->HeightShadowScaling_MinScale) .Process(this->AllowParallelAIQueues) .Process(this->ForbidParallelAIQueues_Aircraft) .Process(this->ForbidParallelAIQueues_Building) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index d908b28b7c..c134e46ed8 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -78,6 +78,10 @@ class RulesExt Valueable Pips_Tiberiums_WeedFrame; Valueable Pips_Tiberiums_WeedEmptyFrame; + Valueable HeightShadowScaling; + Valueable HeightShadowScaling_MinScale; + double AirShadowBaseScale_log; + Valueable AllowParallelAIQueues; Valueable ForbidParallelAIQueues_Aircraft; Valueable ForbidParallelAIQueues_Building; @@ -159,6 +163,11 @@ class RulesExt , Pips_Tiberiums_DisplayOrder {} , Pips_Tiberiums_WeedFrame { 1 } , Pips_Tiberiums_WeedEmptyFrame { 0 } + + , HeightShadowScaling { false } + , HeightShadowScaling_MinScale { 0.0 } + , AirShadowBaseScale_log { 0.693376137 } + , AllowParallelAIQueues { true } , ForbidParallelAIQueues_Aircraft { false } , ForbidParallelAIQueues_Building { false } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 92115f0baa..28780eac46 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -220,6 +220,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->NoSecondaryWeaponFallback_AllowAA.Read(exINI, pSection, "NoSecondaryWeaponFallback.AllowAA"); this->JumpjetRotateOnCrash.Read(exINI, pSection, "JumpjetRotateOnCrash"); + this->ShadowSizeCharacteristicHeight.Read(exINI, pSection, "ShadowSizeCharacteristicHeight"); this->DeployingAnim_AllowAnyDirection.Read(exINI, pSection, "DeployingAnim.AllowAnyDirection"); this->DeployingAnim_KeepUnitVisible.Read(exINI, pSection, "DeployingAnim.KeepUnitVisible"); @@ -327,7 +328,21 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->TurretOffset.Read(exArtINI, pArtSection, "TurretOffset"); this->TurretShadow.Read(exArtINI, pArtSection, "TurretShadow"); - this->ShadowIndices.Read(exArtINI, pArtSection, "ShadowIndices"); + ValueableVector shadow_indices; + shadow_indices.Read(exArtINI, pArtSection, "ShadowIndices"); + ValueableVector shadow_indices_frame; + shadow_indices_frame.Read(exArtINI, pArtSection, "ShadowIndices.Frame"); + if (shadow_indices_frame.size() != shadow_indices.size()) + { + if (!shadow_indices_frame.empty()) + Debug::LogGame("[Developer warning] %s ShadowIndices.Frame size (%d) does not match ShadowIndices size (%d) \n" + , pSection, shadow_indices_frame.size(), shadow_indices.size()); + shadow_indices_frame.resize(shadow_indices.size(), -1); + } + for (size_t i = 0; i < shadow_indices.size(); i++) + this->ShadowIndices[shadow_indices[i]] = shadow_indices_frame[i]; + + this->ShadowIndex_Frame.Read(exArtINI, pArtSection, "ShadowIndex.Frame"); this->LaserTrailData.clear(); for (size_t i = 0; ; ++i) @@ -440,6 +455,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->TurretOffset) .Process(this->TurretShadow) .Process(this->ShadowIndices) + .Process(this->ShadowIndex_Frame) .Process(this->Spawner_LimitRange) .Process(this->Spawner_ExtraLimitRange) .Process(this->Spawner_DelayFrames) @@ -520,7 +536,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->NoAmmoWeapon) .Process(this->NoAmmoAmount) .Process(this->JumpjetRotateOnCrash) - + .Process(this->ShadowSizeCharacteristicHeight) .Process(this->DeployingAnim_AllowAnyDirection) .Process(this->DeployingAnim_KeepUnitVisible) .Process(this->DeployingAnim_ReverseForUndeploy) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 9622c4ae67..1e0c798b0d 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -38,7 +38,8 @@ class TechnoTypeExt Valueable> TurretOffset; Nullable TurretShadow; - ValueableVector ShadowIndices; + Valueable ShadowIndex_Frame; + std::map ShadowIndices; Valueable Spawner_LimitRange; Valueable Spawner_ExtraLimitRange; Nullable Spawner_DelayFrames; @@ -127,6 +128,7 @@ class TechnoTypeExt Valueable NoAmmoAmount; Valueable JumpjetRotateOnCrash; + Nullable ShadowSizeCharacteristicHeight; Valueable DeployingAnim_AllowAnyDirection; Valueable DeployingAnim_KeepUnitVisible; @@ -229,6 +231,7 @@ class TechnoTypeExt , TurretOffset { { 0, 0, 0 } } , TurretShadow { } , ShadowIndices { } + , ShadowIndex_Frame { 0 } , Spawner_LimitRange { false } , Spawner_ExtraLimitRange { 0 } , Spawner_DelayFrames {} @@ -280,7 +283,7 @@ class TechnoTypeExt , NoAmmoWeapon { -1 } , NoAmmoAmount { 0 } , JumpjetRotateOnCrash { true } - + , ShadowSizeCharacteristicHeight { } , DeployingAnim_AllowAnyDirection { false } , DeployingAnim_KeepUnitVisible { false } , DeployingAnim_ReverseForUndeploy { true } diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 21f5c84f0b..5c9d2f8a59 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -16,6 +16,8 @@ #include #include +#include +#include DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) { @@ -334,32 +336,153 @@ DEFINE_HOOK(0x6B0C2C, SlaveManagerClass_FreeSlaves_SlavesFreeSound, 0x5) return 0x6B0C65; } -DEFINE_HOOK(0x4DB157, FootClass_DrawVoxelShadow_TurretShadow, 0x8) +// 2nd order Pade approximant just in case someone complains about performance +constexpr double Pade2_2(double in) { - GET(FootClass*, pThis, ESI); - GET_STACK(Point2D, pos, STACK_OFFSET(0x18, 0x28)); - GET_STACK(Surface*, pSurface, STACK_OFFSET(0x18, 0x24)); - GET_STACK(bool, a9, STACK_OFFSET(0x18, 0x20)); - GET_STACK(Matrix3D*, pMatrix, STACK_OFFSET(0x18, 0x1C)); - GET_STACK(RectangleStruct*, bound, STACK_OFFSET(0x18, 0x14)); - GET_STACK(Point2D, a3, STACK_OFFSET(0x18, -0x10)); - GET_STACK(decltype(ObjectTypeClass::VoxelShadowCache)*, shadow_cache, STACK_OFFSET(0x18, 0x10)); - GET_STACK(VoxelIndexKey, index_key, STACK_OFFSET(0x18, 0xC)); - GET_STACK(int, shadow_index, STACK_OFFSET(0x18, 0x8)); - GET_STACK(VoxelStruct*, main_vxl, STACK_OFFSET(0x18, 0x4)); + const double s = in - static_cast(in); + return GeneralUtils::FastPow(0.36787944117144233, static_cast(in)) + * (12. - 6 * s + s * s) / (12. + 6 * s + s * s); +} - auto pType = pThis->GetTechnoType(); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); +DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) +{ + GET(UnitClass*, pThis, EBP); + enum { SkipDrawing = 0x73C5C9 }; + auto const loco = pThis->Locomotor.GetInterfacePtr(); + if (!loco->Is_To_Have_Shadow()) + return SkipDrawing; + + REF_STACK(Matrix3D, shadow_matrix, STACK_OFFSET(0x1C4, -0x130)); + GET_STACK(VoxelIndexKey, vxl_index_key, STACK_OFFSET(0x1C4, -0x1B0)); + LEA_STACK(RectangleStruct*, bounding, STACK_OFFSET(0x1C4, 0xC)); + LEA_STACK(Point2D*, floor, STACK_OFFSET(0x1C4, -0x1A4)); + GET_STACK(Surface* const, surface, STACK_OFFSET(0x1C4, -0x1A8)); + + GET(UnitTypeClass*, pType, EBX); + // This is not necessarily pThis->Type : UnloadingClass or WaterImage + // This is the very reason I need to do this here, there's no less hacky way to get this Type from those inner calls + + const auto uTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto jjloco = locomotion_cast(loco); + const auto height = pThis->GetHeight(); + const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; // -ln(baseScale) precomputed + + if (RulesExt::Global()->HeightShadowScaling && height > 0) + { + const double minScale = RulesExt::Global()->HeightShadowScaling_MinScale; + if (jjloco) + { + const float cHeight = (float)uTypeExt->ShadowSizeCharacteristicHeight.Get(jjloco->Height); + + if (cHeight > 0) + { + shadow_matrix.Scale((float)std::max(Pade2_2(baseScale_log * height / cHeight), minScale)); + + if (jjloco->State != JumpjetLocomotionClass::State::Hovering) + vxl_index_key = std::bit_cast(-1); + } + } + else + { + const float cHeight = (float)uTypeExt->ShadowSizeCharacteristicHeight.Get(RulesClass::Instance->CruiseHeight); + if (cHeight > 0) + { + shadow_matrix.Scale((float)std::max(Pade2_2(baseScale_log * height / cHeight), minScale)); + vxl_index_key = std::bit_cast(-1); + } + } + } + else if (!RulesExt::Global()->HeightShadowScaling && pThis->Type->ConsideredAircraft) + { + shadow_matrix.Scale((float)Pade2_2(baseScale_log)); + } // We need to handle Ares turrets/barrels struct DummyExtHere { - char before[0xA4]; + char _[0xA4]; std::vector ChargerTurrets; std::vector ChargerBarrels; + char __[0x120]; + UnitTypeClass* WaterImage; + VoxelStruct NoSpawnAltVXL; }; + auto GetMainVoxel = [&]() + { + if (pType->NoSpawnAlt && pThis->SpawnManager && pThis->SpawnManager->CountDockedSpawns() == 0) + { + if (CAN_USE_ARES && AresHelper::CanUseAres) + { + vxl_index_key = std::bit_cast(-1);// I'd just assume most of the time we have spawn + return &reinterpret_cast(pType->align_2FC)->NoSpawnAltVXL; + } + return &pType->TurretVoxel; + } + return &pType->MainVoxel; + }; + + auto const main_vxl = GetMainVoxel(); + + // TODO : adjust shadow point according to height + // There was a bit deviation that I cannot decipher, might need help with that + // But it turns out it has basically no visual difference + + auto shadow_point = loco->Shadow_Point(); + auto why = *floor + shadow_point; + + float arf = pThis->AngleRotatedForwards; + float ars = pThis->AngleRotatedSideways; + // lazy, don't want to hook inside Shadow_Matrix + if (std::abs(ars) >= 0.005 || std::abs(arf) >= 0.005) + { + // index key is already invalid + shadow_matrix.TranslateX(float(Math::sgn(arf) * pType->VoxelScaleX * (1 - Math::cos(arf)))); + shadow_matrix.TranslateY(float(Math::sgn(-ars) * pType->VoxelScaleY * (1 - Math::cos(ars)))); + shadow_matrix.ScaleX((float)Math::cos(arf)); + shadow_matrix.ScaleY((float)Math::cos(ars)); + } + + auto mtx = Matrix3D::VoxelDefaultMatrix() * shadow_matrix; + + if (uTypeExt->ShadowIndices.empty()) + { + if (pType->ShadowIndex >= 0 && pType->ShadowIndex < main_vxl->HVA->LayerCount) + pThis->DrawVoxelShadow( + main_vxl, + pType->ShadowIndex, + vxl_index_key, + &pType->VoxelShadowCache, + bounding, + &why, + &mtx, + true, + surface, + shadow_point + ); + } + else + { + for (auto& [index, _] : uTypeExt->ShadowIndices) + pThis->DrawVoxelShadow( + main_vxl, + index, + vxl_index_key, + &pType->VoxelShadowCache, + bounding, + &why, + &mtx, + true, + surface, + shadow_point + ); + } + + if (!uTypeExt->TurretShadow.Get(RulesExt::Global()->DrawTurretShadow) || main_vxl == &pType->TurretVoxel) + return SkipDrawing; + + auto GetTurretVoxel = [pType](int idx) ->VoxelStruct* { if (pType->TurretCount == 0 || pType->IsGattling || idx < 0) @@ -394,35 +517,206 @@ DEFINE_HOOK(0x4DB157, FootClass_DrawVoxelShadow_TurretShadow, 0x8) return nullptr; }; + Matrix3D rot = Matrix3D::GetIdentity(); + uTypeExt->ApplyTurretOffset(&rot, Pixel_Per_Lepton); + rot.RotateZ(static_cast(pThis->SecondaryFacing.Current().GetRadian<32>() - pThis->PrimaryFacing.Current().GetRadian<32>())); + auto tur_mtx = mtx * rot; // unfortunately we won't have TurretVoxelScaleX/Y given the amount of work + auto tur = GetTurretVoxel(pThis->CurrentTurretNumber); - if (tur && pTypeExt->TurretShadow.Get(RulesExt::Global()->DrawTurretShadow) && tur->VXL && tur->HVA) - { - auto mtx = Matrix3D::GetIdentity(); - pTypeExt->ApplyTurretOffset(&mtx, Pixel_Per_Lepton); - mtx.TranslateZ(-tur->HVA->Matrixes[0].GetZVal()); - mtx.RotateZ(static_cast(pThis->SecondaryFacing.Current().GetRadian<32>() - pThis->PrimaryFacing.Current().GetRadian<32>())); - mtx = *pMatrix * mtx; + // sorry but you're fucked + if (tur && tur->VXL && tur->HVA) + pThis->DrawVoxelShadow( + tur, + 0, + std::bit_cast(-1), // no cache, no use for valid key + nullptr, // no cache atm + bounding, + &why, + &tur_mtx, + false, + surface, + shadow_point + ); + + auto bar = GetBarrelVoxel(pThis->CurrentTurretNumber); + + // and you are utterly fucked + if (bar && bar->VXL && bar->HVA) + pThis->DrawVoxelShadow( + bar, + 0, + std::bit_cast(-1), // no cache, no use + nullptr,//no cache atm + bounding, + &why, + &tur_mtx, + false, + surface, + shadow_point + ); + + // Add caches in Ext if necessary, remember not to serialize these shit + // IndexClass VoxelTurretShadowCache {}; + // IndexClass VoxelBarrelShadowCache {}; + + return SkipDrawing; +} - pThis->DrawVoxelShadow(tur, 0, index_key, nullptr, bound, &a3, &mtx, a9, pSurface, pos); +DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) +{ + GET(AircraftClass*, pThis, EBP); + GET(const int, height, EBX); + REF_STACK(VoxelIndexKey, key, STACK_OFFSET(0xCC, -0xBC)); + REF_STACK(Point2D, flor, STACK_OFFSET(0xCC, -0xAC)); + GET_STACK(RectangleStruct*, bound, STACK_OFFSET(0xCC, 0x10)); + enum { FinishDrawing = 0x4148A5 }; + + const auto loco = locomotion_cast(pThis->Locomotor); + if (!loco || !loco->Is_To_Have_Shadow() || pThis->IsSinking) + return FinishDrawing; + + const auto aTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto shadow_mtx = loco->Shadow_Matrix(&key); + const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; + + if (RulesExt::Global()->HeightShadowScaling) + { + const double minScale = RulesExt::Global()->HeightShadowScaling_MinScale; + const float cHeight = (float)aTypeExt->ShadowSizeCharacteristicHeight.Get(loco->FlightLevel); - auto bar = GetBarrelVoxel(pThis->CurrentTurretNumber); + if (cHeight > 0) + { + shadow_mtx.Scale((float)std::max(Pade2_2(baseScale_log* height / cHeight), minScale)); + key = std::bit_cast(-1); // I'm sorry + } + } + else if (pThis->Type->ConsideredAircraft) + { + shadow_mtx.Scale((float)Pade2_2(baseScale_log)); + } - if (bar && bar->VXL && bar->HVA) - pThis->DrawVoxelShadow(bar, 0, index_key, nullptr, bound, &a3, &mtx, a9, pSurface, pos); + if (pThis->IsCrashing) + { + double arf = pThis->AngleRotatedForwards; + if (loco->CurrentSpeed > pThis->Type->PitchSpeed) + arf += pThis->Type->PitchAngle; + shadow_mtx.ScaleY((float)Math::cos(pThis->AngleRotatedSideways)); + shadow_mtx.ScaleX((float)Math::cos(arf)); } - if (!pTypeExt->ShadowIndices.size()) + shadow_mtx = Matrix3D::VoxelDefaultMatrix() * shadow_mtx; + Point2D why = flor + loco->Shadow_Point(); + auto const main_vxl = &pThis->Type->MainVoxel; + + if (aTypeExt->ShadowIndices.empty()) { - pThis->DrawVoxelShadow(main_vxl, shadow_index, index_key, shadow_cache, bound, &a3, pMatrix, a9, pSurface, pos); + auto const shadow_index = pThis->Type->ShadowIndex; + if (shadow_index >= 0 && shadow_index < main_vxl->HVA->LayerCount) + pThis->DrawVoxelShadow(main_vxl, + shadow_index, + key, + &pThis->Type->VoxelShadowCache, + bound, + &why, + &shadow_mtx, + true, + nullptr, + { 0, 0 } + ); } else { - for (auto index : pTypeExt->ShadowIndices) + for (auto& [index, _] : aTypeExt->ShadowIndices) + pThis->DrawVoxelShadow(main_vxl, + index, + key, + &pThis->Type->VoxelShadowCache, + bound, + &why, + &shadow_mtx, + true, + nullptr, + { 0, 0 } + ); + } + + return FinishDrawing; +} + +DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7) +{ + GET(FootClass*, pThis, EBX);//Maybe Techno later + GET(VoxelStruct*, pVXL, EBP); + GET_STACK(Matrix3D*, pMat, STACK_OFFSET(0xE8, 0xC)); + GET_STACK(int, shadow_index_now, STACK_OFFSET(0xE8, 0x18));// it's used later, otherwise I could have chosen the frame index earlier + + REF_STACK(Matrix3D, matRet, STACK_OFFSET(0xE8, -0x60)); + auto pType = pThis->GetTechnoType(); + + auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + auto hva = pVXL->HVA; + + auto ChooseFrame = [&]()->int //Don't want to use goto + { + // Turret or Barrel + if (pVXL != &pType->MainVoxel) { - pThis->DrawVoxelShadow(main_vxl, index, index_key, shadow_cache, bound, &a3, pMatrix, a9, pSurface, pos); + // verify just in case: + auto who_are_you = reinterpret_cast(reinterpret_cast(pVXL) - (offsetof(TechnoTypeClass, MainVoxel))); + if (who_are_you[0] == UnitTypeClass::AbsVTable) + pType = reinterpret_cast(who_are_you);//you are someone else + else + return pThis->TurretAnimFrame % hva->FrameCount; + // you might also be SpawnAlt voxel, but I can't know + // otherwise what would you expect me to do, shift back to ares typeext base and check if ownerobject is technotype? } - } - return 0x4DB195; + // Main body sections + auto& shadowIndices = pTypeExt->ShadowIndices; + if (shadowIndices.empty()) + { + // Only ShadowIndex + if (pType->ShadowIndex == shadow_index_now) + { + int shadow_index_frame = pTypeExt->ShadowIndex_Frame; + if (shadow_index_frame > -1) + return shadow_index_frame % hva->FrameCount; + } + else + { + // WHO THE HELL ARE YOU??? + return 0; + } + } + else + { + int idx_of_now = shadowIndices[shadow_index_now]; + if (idx_of_now > -1) + return idx_of_now % hva->FrameCount; + } + + return pThis->WalkedFramesSoFar % hva->FrameCount; + }; + + + Matrix3D hvamat = hva->Matrixes[shadow_index_now + hva->LayerCount * ChooseFrame()]; + + // TO TEST : Check if this is the proper Z offset to shift the sections to the same level + hvamat.TranslateZ( + -hvamat.GetZVal() + - pVXL->VXL->TailerData->Bounds[0].Z + ); + + matRet = *pMat * hvamat; + + // Recover vanilla instructions + if (pThis->GetTechnoType()->UseBuffer) + *reinterpret_cast(0xB43180) = 1; + + REF_STACK(Matrix3D, b, STACK_OFFSET(0xE8, -0x90)); + b.MakeIdentity();// we don't do scaling here anymore + + return 0x707331; } diff --git a/src/Utilities/GeneralUtils.h b/src/Utilities/GeneralUtils.h index 5a075cd96f..ea244a1a4f 100644 --- a/src/Utilities/GeneralUtils.h +++ b/src/Utilities/GeneralUtils.h @@ -38,7 +38,7 @@ class GeneralUtils static void DisplayDamageNumberString(int damage, DamageDisplayType type, CoordStruct coords, int& offset); template - static T FastPow(T x, size_t n) + static constexpr T FastPow(T x, size_t n) { // Real fast pow calc x^n in O(log(n)) T result = 1; From bebb139dcb130f9042d0e70d244f3650c671cd8d Mon Sep 17 00:00:00 2001 From: Starkku Date: Mon, 8 Apr 2024 20:07:55 +0300 Subject: [PATCH 28/43] Increment devbuild number --- src/Phobos.version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 10eed04f5c..457826cbd2 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -23,7 +23,7 @@ #pragma endregion // Build number. Incremented on each released build. -#define BUILD_NUMBER 38 +#define BUILD_NUMBER 39 // Nightly defines GIT_COMMIT and GIT_BRANCH in GH Actions From de64a7dec68e8f14dccd4270db8f0378882b98b6 Mon Sep 17 00:00:00 2001 From: Otamaa Date: Tue, 9 Apr 2024 02:55:59 +0700 Subject: [PATCH 29/43] Fixed crash caused by garbage pointer used on hook `FlyLocomotionClass_DrawMatrix_OnAirport --- src/Ext/Aircraft/Hooks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Ext/Aircraft/Hooks.cpp b/src/Ext/Aircraft/Hooks.cpp index c63bde4033..4f2eaa596e 100644 --- a/src/Ext/Aircraft/Hooks.cpp +++ b/src/Ext/Aircraft/Hooks.cpp @@ -172,7 +172,10 @@ DEFINE_HOOK(0x4CF68D, FlyLocomotionClass_DrawMatrix_OnAirport, 0x5) GET(ILocomotion*, iloco, ESI); __assume(iloco != nullptr); auto loco = static_cast(iloco); - auto pThis = static_cast(loco->LinkedTo); + //FlyLocomotionClass mainly use `Owner` + //`LinkedTo` is usually filled with garbage value + //please check it on debugger properly before use ! + auto pThis = static_cast(loco->Owner); if (loco->AirportBound && loco->CurrentSpeed == 0.0 && pThis->GetHeight() <= 0) { float ars = pThis->AngleRotatedSideways; From 9cf6344d08816c15b7e354ca0c28ae7aa84ed279 Mon Sep 17 00:00:00 2001 From: Starkku Date: Mon, 8 Apr 2024 23:33:29 +0300 Subject: [PATCH 30/43] Fix extra shadow drawing being always skipped --- src/Ext/Anim/Hooks.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ext/Anim/Hooks.cpp b/src/Ext/Anim/Hooks.cpp index 414f7d421e..7326337bfb 100644 --- a/src/Ext/Anim/Hooks.cpp +++ b/src/Ext/Anim/Hooks.cpp @@ -280,6 +280,8 @@ DEFINE_HOOK(0x423365, AnimClass_DrawIt_ExtraShadow, 0x8) if (!pTypeExt->ExtraShadow) return SkipExtraShadow; + + return DrawExtraShadow; } return SkipExtraShadow; From bde13aa50b8fd5b94d03a80f4e523978e652b0e0 Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 9 Apr 2024 00:25:18 +0300 Subject: [PATCH 31/43] Fix AutoDeath & parasite interaction causing crashes --- docs/Whats-New.md | 1 + src/Ext/Techno/Body.Update.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 79ec5b882a..0b81d8170a 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -460,6 +460,7 @@ Phobos fixes: - Fixed a desync error caused by air/top layer sorting (by Starkku) - Fixed heal / repair weapons being unable to remove parasites from shielded targets if they were unable to heal / repair the parent unit (by Starkku) - Fixed `Inviso=true` interceptor projectiles applying damage on interceptable, armor type-having projectiles twice (by Starkku) +- Fixed `AutoDeath` causing crashes when used to kill a parasite unit inside an another unit (by Starkku) Fixes / interactions with other extensions: - All forms of type conversion (including Ares') now correctly update `OpenTopped` state of passengers in transport that is converted (by Starkku) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 771f7a73a8..9bbab3d268 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -647,6 +647,13 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, Anim { if (isInLimbo) { + // Remove parasite units first before deleting them. + if (auto const pFoot = abstract_cast(pThis)) + { + if (pFoot->ParasiteImUsing && pFoot->ParasiteImUsing->Victim) + pFoot->ParasiteImUsing->ExitUnit(); + } + pThis->RegisterKill(pThis->Owner); pThis->UnInit(); return; From e9bf03a29bace3b916d9c920e2f32d91dc8b9022 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Tue, 9 Apr 2024 15:57:52 +0800 Subject: [PATCH 32/43] Revert de64a7dec68e8f14dccd4270db8f0378882b98b6 don't inflict flyloco to non-aircrafts, no one did that yet --- src/Ext/Aircraft/Hooks.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Ext/Aircraft/Hooks.cpp b/src/Ext/Aircraft/Hooks.cpp index 4f2eaa596e..c63bde4033 100644 --- a/src/Ext/Aircraft/Hooks.cpp +++ b/src/Ext/Aircraft/Hooks.cpp @@ -172,10 +172,7 @@ DEFINE_HOOK(0x4CF68D, FlyLocomotionClass_DrawMatrix_OnAirport, 0x5) GET(ILocomotion*, iloco, ESI); __assume(iloco != nullptr); auto loco = static_cast(iloco); - //FlyLocomotionClass mainly use `Owner` - //`LinkedTo` is usually filled with garbage value - //please check it on debugger properly before use ! - auto pThis = static_cast(loco->Owner); + auto pThis = static_cast(loco->LinkedTo); if (loco->AirportBound && loco->CurrentSpeed == 0.0 && pThis->GetHeight() <= 0) { float ars = pThis->AngleRotatedSideways; From 1e18c1600776870c2ab49a0e00f1ab10771d5193 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Tue, 9 Apr 2024 16:34:26 +0800 Subject: [PATCH 33/43] Hide debug hotkeys if not enabled at all --- src/Commands/Commands.cpp | 28 +++++++++++++++++----------- src/Phobos.INI.cpp | 11 ----------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 1aebdff24f..215c38b232 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -1,3 +1,4 @@ +#include #include "Commands.h" #include "ObjectInfo.h" @@ -14,21 +15,26 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) { // Load it after Ares' - MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); - - MakeCommand(); - MakeCommand>(); // Single step in - MakeCommand>(); // Speed 1 - MakeCommand>(); // Speed 2 - MakeCommand>(); // Speed 3 - MakeCommand>(); // Speed 4 - MakeCommand>(); // Speed 5 +#ifndef DEBUG + Phobos::Config::DevelopmentCommands = CCINIClass::INI_Rules->ReadBool("GlobalControls", "DebugKeysEnabled", Phobos::Config::DevelopmentCommands); +#endif + if (Phobos::Config::DevelopmentCommands) + { + MakeCommand(); + MakeCommand(); + MakeCommand(); + MakeCommand(); + MakeCommand>(); // Single step in + MakeCommand>(); // Speed 1 + MakeCommand>(); // Speed 2 + MakeCommand>(); // Speed 3 + MakeCommand>(); // Speed 4 + MakeCommand>(); // Speed 5 + } return 0; } diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index b504863125..68b9586dfa 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -187,17 +187,6 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) return 0; } -DEFINE_HOOK(0x66E9DF, RulesClass_Process_Phobos, 0x8) -{ -#ifndef DEBUG - GET(CCINIClass*, rulesINI, EDI); - - Phobos::Config::DevelopmentCommands = rulesINI->ReadBool("GlobalControls", "DebugKeysEnabled", Phobos::Config::DevelopmentCommands); -#endif - - return 0; -} - DEFINE_HOOK(0x55DBF5, MainLoop_SaveGame, 0xA) { return Phobos::Config::SaveGameOnScenarioStart ? 0 : 0x55DC99; From 01b4ab2a4e89b0848ff245a2e7e8de0fa10f5e48 Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 9 Apr 2024 11:58:06 +0300 Subject: [PATCH 34/43] Allow toggling showing briefing on mission start in user settings --- docs/AI-Scripting-and-Mapping.md | 8 +++++++- src/Misc/Hooks.UI.cpp | 2 +- src/Phobos.INI.cpp | 2 ++ src/Phobos.h | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/AI-Scripting-and-Mapping.md b/docs/AI-Scripting-and-Mapping.md index f23f26aff6..29aac42aaf 100644 --- a/docs/AI-Scripting-and-Mapping.md +++ b/docs/AI-Scripting-and-Mapping.md @@ -69,7 +69,7 @@ Ranking.OverParMessage= ; CSF entry key ### Show briefing dialog on startup -- You can now have the briefing dialog screen show up on singleplayer campaign mission startup by setting `ShowBriefing` to true in map file's `[Basic]` section, or in the map file's section in `missionmd.ini` (latter takes precedence over former if available). +- You can now have the briefing dialog screen show up on singleplayer campaign mission startup by setting `ShowBriefing` to true in map file's `[Basic]` section, or in the map file's section in `missionmd.ini` (latter takes precedence over former if available). This can be disabled by user by setting `ShowBriefing` to false in `Ra2MD.ini`. - `BriefingTheme` (In order of precedence from highest to lowest: `missionmd.ini`, map file, side entry in `rulesmd.ini`) can be used to define a custom theme to play on this briefing screen. If not set, the loading screen theme will keep playing until the scenario starts properly. - String labels for the startup briefing dialog screen's resume button as well as the button's status bar text can be customized by setting `ShowBriefingResumeButtonLabel` and `ShowBriefingResumeButtonStatusLabel` respectively. They default to the same labels used by the briefing screen dialog when opened otherwise. @@ -100,6 +100,12 @@ ShowBriefingResumeButtonLabel=GUI:Resume ; CSF entry key ShowBriefingResumeButtonStatusLabel=STT:BriefingButtonReturn ; CSF entry key ``` +In `Ra2MD.ini`: +```ini +[Phobos] +ShowBriefing=true ; boolean +``` + ## Script Actions ### `10000-10999` Ingame Actions diff --git a/src/Misc/Hooks.UI.cpp b/src/Misc/Hooks.UI.cpp index 8afd4f7c71..01a1f24deb 100644 --- a/src/Misc/Hooks.UI.cpp +++ b/src/Misc/Hooks.UI.cpp @@ -257,7 +257,7 @@ DEFINE_HOOK(0x683E41, ScenarioClass_Start_ShowBriefing, 0x6) GET_STACK(bool, showBriefing, STACK_OFFSET(0xFC, -0xE9)); // Don't show briefing dialog for non-campaign games etc. - if (!ScenarioExt::Global()->ShowBriefing || !showBriefing || !SessionClass::IsCampaign()) + if (!Phobos::Config::ShowBriefing || !ScenarioExt::Global()->ShowBriefing || !showBriefing || !SessionClass::IsCampaign()) return 0; BriefingTemp::ShowBriefing = true; diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 68b9586dfa..cfe8ba463f 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -43,6 +43,7 @@ bool Phobos::Config::SkirmishUnlimitedColors = false; bool Phobos::Config::ShowDesignatorRange = false; bool Phobos::Config::SaveVariablesOnScenarioEnd = false; bool Phobos::Config::SaveGameOnScenarioStart = true; +bool Phobos::Config::ShowBriefing = true; bool Phobos::Misc::CustomGS = false; int Phobos::Misc::CustomGS_ChangeInterval[7] = { -1, -1, -1, -1, -1, -1, -1 }; @@ -59,6 +60,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD->ReadBool("Phobos", "RealTimeTimers.Adaptive", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD->ReadBool("Phobos", "DigitalDisplay.Enable", false); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD->ReadBool("Phobos", "SaveGameOnScenarioStart", true); + Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD->ReadBool("Phobos", "ShowBriefing", true); CCINIClass* pINI_UIMD = CCINIClass::LoadINIFile(GameStrings::UIMD_INI); diff --git a/src/Phobos.h b/src/Phobos.h index 6090d14a48..ce57102e09 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -80,6 +80,7 @@ class Phobos static bool ShowDesignatorRange; static bool SaveVariablesOnScenarioEnd; static bool SaveGameOnScenarioStart; + static bool ShowBriefing; }; class Misc From b891cbf4767a7a716295619801cd1beb3e07121f Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 9 Apr 2024 12:17:20 +0300 Subject: [PATCH 35/43] Add list of user setting keys on what's new page of docs --- docs/AI-Scripting-and-Mapping.md | 2 +- docs/Miscellanous.md | 2 +- docs/User-Interface.md | 6 +++--- docs/Whats-New.md | 19 +++++++++++++++++++ .../zh_CN/LC_MESSAGES/User-Interface.po | 4 ++-- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/docs/AI-Scripting-and-Mapping.md b/docs/AI-Scripting-and-Mapping.md index 29aac42aaf..6573c23de7 100644 --- a/docs/AI-Scripting-and-Mapping.md +++ b/docs/AI-Scripting-and-Mapping.md @@ -100,7 +100,7 @@ ShowBriefingResumeButtonLabel=GUI:Resume ; CSF entry key ShowBriefingResumeButtonStatusLabel=STT:BriefingButtonReturn ; CSF entry key ``` -In `Ra2MD.ini`: +In `RA2MD.ini`: ```ini [Phobos] ShowBriefing=true ; boolean diff --git a/docs/Miscellanous.md b/docs/Miscellanous.md index c1a859d254..0d2556c904 100644 --- a/docs/Miscellanous.md +++ b/docs/Miscellanous.md @@ -106,7 +106,7 @@ CustomGSN.DefaultDelay=N ; integer between 0 and 6 ; where N = 0, 1, 2, 3, 4, 5, 6 ``` -In `ra2md.ini`: +In `RA2MD.ini`: ```ini [Phobos] CampaignDefaultGameSpeed=4 ; integer diff --git a/docs/User-Interface.md b/docs/User-Interface.md index df4632fa99..3d990b4d16 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -84,7 +84,7 @@ DigitalDisplay.Disable=false ; boolean DigitalDisplayTypes= ; list of DigitalDisplayTypes ``` -In `Ra2MD.ini`: +In `RA2MD.ini`: ```ini [Phobos] DigitalDisplay.Enable=false ; boolean @@ -110,7 +110,7 @@ ShowDesignatorRange=true ; boolean ShowDesignatorRange=true ; boolean ``` -In `Ra2MD.ini`: +In `RA2MD.ini`: ```ini [Phobos] ShowDesignatorRange=false ; boolean @@ -208,7 +208,7 @@ The `PlacementPreview.Palette` option is not used when `PlacementPreview.Remap=y - This behavior is designed to be toggleable by users. For now you can only do that externally via client or manually. -In `ra2md.ini`: +In `RA2MD.ini`: ```ini [Phobos] ShowPlacementPreview=yes ; boolean diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 0b81d8170a..c3afa0bcb2 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -60,6 +60,25 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - Key `rulesmd.ini->[SOMETECHNOTYPE]->Deployed.RememberTarget` is deprecated and can be removed now, the bugfix for `DeployToFire` deployers is now always on. +### New user settings in RA2MD.ini + +- These are new user setting keys added by various features in Phobos. Most of them can be found in either in [user inteface](User-Interface.md) or [miscellaneous](Miscellanous.md) sections. Search functionality can be used to find them quickly if needed. + +```ini +[Phobos] +CampaignDefaultGameSpeed=4 ; integer +ShowBriefing=true ; boolean +DigitalDisplay.Enable=false ; boolean +ShowDesignatorRange=false ; boolean +PrioritySelectionFiltering=true ; boolean +ShowPlacementPreview=yes ; boolean +RealTimeTimers=false ; boolean +RealTimeTimers.Adaptive=false ; boolean +ToolTipDescriptions=true ; boolean +ToolTipBlur=false ; boolean +SaveGameOnScenarioStart=true ; boolean +``` + ### For Map Editor (Final Alert 2)
diff --git a/docs/locale/zh_CN/LC_MESSAGES/User-Interface.po b/docs/locale/zh_CN/LC_MESSAGES/User-Interface.po index 05b933ae7e..e775db7bc9 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/User-Interface.po +++ b/docs/locale/zh_CN/LC_MESSAGES/User-Interface.po @@ -204,8 +204,8 @@ msgid "" msgstr "`PlacementPreview.ShapeFrame`默认为建筑art节中`Buildup`的最后一非影子帧。如果没有`Buildup`则会选取默认图像第一帧(其中不会包含动画和Bibs)。" #: ../User-Interface.md:91 -msgid "In `ra2md.ini`:" -msgstr "在`ra2md.ini`中:" +msgid "In `RA2MD.ini`:" +msgstr "在`RA2MD.ini`中:" #: ../User-Interface.md:97 msgid "Hotkey Commands" From bb8c92f5ea499485c084a4748afa6d0724be0109 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Wed, 10 Apr 2024 01:44:00 +0800 Subject: [PATCH 36/43] Remove CheckDebugDeactivated for new hotkey classes not needed anymore --- src/Commands/Commands.h | 19 ------------------- src/Commands/DamageDisplay.cpp | 3 --- src/Commands/DamageDisplay.h | 2 +- src/Commands/FrameByFrame.cpp | 3 --- src/Commands/FrameByFrame.h | 2 +- src/Commands/FrameStep.h | 2 +- src/Commands/NextIdleHarvester.h | 2 +- src/Commands/ObjectInfo.cpp | 3 --- src/Commands/ObjectInfo.h | 2 +- src/Commands/QuickSave.h | 2 +- src/Commands/SaveVariablesToFile.cpp | 3 --- src/Commands/SaveVariablesToFile.h | 2 +- src/Commands/ToggleDesignatorRange.h | 2 +- src/Commands/ToggleDigitalDisplay.h | 2 +- 14 files changed, 9 insertions(+), 40 deletions(-) diff --git a/src/Commands/Commands.h b/src/Commands/Commands.h index bcdebcff82..939ec73225 100644 --- a/src/Commands/Commands.h +++ b/src/Commands/Commands.h @@ -7,25 +7,6 @@ #include #include -class PhobosCommandClass : public CommandClass -{ -protected: - bool CheckDebugDeactivated() const - { - if (!Phobos::Config::DevelopmentCommands) - { - if (const wchar_t* text = StringTable::LoadString("TXT_COMMAND_DISABLED")) - { - wchar_t msg[0x100] = L"\0"; - wsprintfW(msg, text, this->GetUIName()); - MessageListClass::Instance->PrintMessage(msg); - } - return true; - } - return false; - } -}; - template void MakeCommand() { diff --git a/src/Commands/DamageDisplay.cpp b/src/Commands/DamageDisplay.cpp index 488eb30ae6..33ae3548de 100644 --- a/src/Commands/DamageDisplay.cpp +++ b/src/Commands/DamageDisplay.cpp @@ -25,8 +25,5 @@ const wchar_t* DamageDisplayCommandClass::GetUIDescription() const void DamageDisplayCommandClass::Execute(WWKey eInput) const { - if (this->CheckDebugDeactivated()) - return; - Phobos::DisplayDamageNumbers = !Phobos::DisplayDamageNumbers; } diff --git a/src/Commands/DamageDisplay.h b/src/Commands/DamageDisplay.h index 7aae06cee7..a42204eeb8 100644 --- a/src/Commands/DamageDisplay.h +++ b/src/Commands/DamageDisplay.h @@ -3,7 +3,7 @@ #include "Commands.h" // Display damage strings -class DamageDisplayCommandClass : public PhobosCommandClass +class DamageDisplayCommandClass : public CommandClass { public: virtual const char* GetName() const override; diff --git a/src/Commands/FrameByFrame.cpp b/src/Commands/FrameByFrame.cpp index b618143b91..b53ba32167 100644 --- a/src/Commands/FrameByFrame.cpp +++ b/src/Commands/FrameByFrame.cpp @@ -31,9 +31,6 @@ const wchar_t* FrameByFrameCommandClass::GetUIDescription() const void FrameByFrameCommandClass::Execute(WWKey eInput) const { - if (this->CheckDebugDeactivated()) - return; - if (!SessionClass::IsSingleplayer()) return; diff --git a/src/Commands/FrameByFrame.h b/src/Commands/FrameByFrame.h index 898624ccec..29c190b7ac 100644 --- a/src/Commands/FrameByFrame.h +++ b/src/Commands/FrameByFrame.h @@ -2,7 +2,7 @@ #include "Commands.h" -class FrameByFrameCommandClass : public PhobosCommandClass +class FrameByFrameCommandClass : public CommandClass { public: static size_t FrameStepCount; diff --git a/src/Commands/FrameStep.h b/src/Commands/FrameStep.h index 94a9d0ecef..370cc742ef 100644 --- a/src/Commands/FrameStep.h +++ b/src/Commands/FrameStep.h @@ -5,7 +5,7 @@ #include "FrameByFrame.h" template -class FrameStepCommandClass : public PhobosCommandClass +class FrameStepCommandClass : public CommandClass { virtual const char* GetName() const override; virtual const wchar_t* GetUIName() const override; diff --git a/src/Commands/NextIdleHarvester.h b/src/Commands/NextIdleHarvester.h index 598dc0629d..c8bdfcfb83 100644 --- a/src/Commands/NextIdleHarvester.h +++ b/src/Commands/NextIdleHarvester.h @@ -3,7 +3,7 @@ #include "Commands.h" // Select next idle harvester -class NextIdleHarvesterCommandClass : public PhobosCommandClass +class NextIdleHarvesterCommandClass : public CommandClass { public: // CommandClass diff --git a/src/Commands/ObjectInfo.cpp b/src/Commands/ObjectInfo.cpp index aafd0e5002..3c73192f19 100644 --- a/src/Commands/ObjectInfo.cpp +++ b/src/Commands/ObjectInfo.cpp @@ -36,9 +36,6 @@ const wchar_t* ObjectInfoCommandClass::GetUIDescription() const void ObjectInfoCommandClass::Execute(WWKey eInput) const { - if (this->CheckDebugDeactivated()) - return; - char buffer[0x800] = { 0 }; auto append = [&buffer](const char* pFormat, ...) diff --git a/src/Commands/ObjectInfo.h b/src/Commands/ObjectInfo.h index 4af44b52c5..d82a685480 100644 --- a/src/Commands/ObjectInfo.h +++ b/src/Commands/ObjectInfo.h @@ -3,7 +3,7 @@ #include "Commands.h" // #53 New debug feature for AI scripts -class ObjectInfoCommandClass : public PhobosCommandClass +class ObjectInfoCommandClass : public CommandClass { public: // CommandClass diff --git a/src/Commands/QuickSave.h b/src/Commands/QuickSave.h index 8804081736..b82ce35995 100644 --- a/src/Commands/QuickSave.h +++ b/src/Commands/QuickSave.h @@ -3,7 +3,7 @@ #include "Commands.h" // Quicksave current game -class QuickSaveCommandClass : public PhobosCommandClass +class QuickSaveCommandClass : public CommandClass { public: // CommandClass diff --git a/src/Commands/SaveVariablesToFile.cpp b/src/Commands/SaveVariablesToFile.cpp index 96ea84179f..8e4d93b7a9 100644 --- a/src/Commands/SaveVariablesToFile.cpp +++ b/src/Commands/SaveVariablesToFile.cpp @@ -25,9 +25,6 @@ const wchar_t* SaveVariablesToFileCommandClass::GetUIDescription() const void SaveVariablesToFileCommandClass::Execute(WWKey eInput) const { - if (this->CheckDebugDeactivated()) - return; - MessageListClass::Instance->PrintMessage( L"Variables saved.", RulesClass::Instance->MessageDelay, diff --git a/src/Commands/SaveVariablesToFile.h b/src/Commands/SaveVariablesToFile.h index 226bd5f878..ef7b747c05 100644 --- a/src/Commands/SaveVariablesToFile.h +++ b/src/Commands/SaveVariablesToFile.h @@ -3,7 +3,7 @@ #include "Commands.h" // Display damage strings -class SaveVariablesToFileCommandClass : public PhobosCommandClass +class SaveVariablesToFileCommandClass : public CommandClass { public: virtual const char* GetName() const override; diff --git a/src/Commands/ToggleDesignatorRange.h b/src/Commands/ToggleDesignatorRange.h index bd758c2862..1f1f5eeb01 100644 --- a/src/Commands/ToggleDesignatorRange.h +++ b/src/Commands/ToggleDesignatorRange.h @@ -3,7 +3,7 @@ #include "Commands.h" // Display damage strings -class ToggleDesignatorRangeCommandClass : public PhobosCommandClass +class ToggleDesignatorRangeCommandClass : public CommandClass { public: virtual const char* GetName() const override; diff --git a/src/Commands/ToggleDigitalDisplay.h b/src/Commands/ToggleDigitalDisplay.h index 4ee18cae30..4146a0d6e9 100644 --- a/src/Commands/ToggleDigitalDisplay.h +++ b/src/Commands/ToggleDigitalDisplay.h @@ -3,7 +3,7 @@ #include "Commands.h" // Display damage strings -class ToggleDigitalDisplayCommandClass : public PhobosCommandClass +class ToggleDigitalDisplayCommandClass : public CommandClass { public: virtual const char* GetName() const override; From ff1605e91e21e11973b084bb18ae71a722244736 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Wed, 10 Apr 2024 23:34:15 +0800 Subject: [PATCH 37/43] temporary backward-compatibility for single part vxls --- src/Ext/SWType/FireSuperWeapon.cpp | 2 +- src/Ext/TechnoType/Hooks.cpp | 10 ++++++---- src/Ext/WarheadType/Detonate.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Ext/SWType/FireSuperWeapon.cpp b/src/Ext/SWType/FireSuperWeapon.cpp index d3baed400a..19c80762d2 100644 --- a/src/Ext/SWType/FireSuperWeapon.cpp +++ b/src/Ext/SWType/FireSuperWeapon.cpp @@ -247,7 +247,7 @@ void SWTypeExt::ExtData::ApplySWNext(SuperClass* pSW, const CellStruct& cell) int oldstart = pSuper->RechargeTimer.StartTime; int oldleft = pSuper->RechargeTimer.TimeLeft; pSuper->SetReadiness(true); - pSuper->Launch(cell, true); + pSuper->Launch(cell, pHouse->IsCurrentPlayer()); pSuper->Reset(); if (!this->SW_Next_RealLaunch) { diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 5c9d2f8a59..c065e2dc82 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -703,11 +703,13 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7) Matrix3D hvamat = hva->Matrixes[shadow_index_now + hva->LayerCount * ChooseFrame()]; + // A nasty temporary backward compatibility option + if (hva->LayerCount > 1 || pType->Turret) // TO TEST : Check if this is the proper Z offset to shift the sections to the same level - hvamat.TranslateZ( - -hvamat.GetZVal() - - pVXL->VXL->TailerData->Bounds[0].Z - ); + hvamat.TranslateZ( + -hvamat.GetZVal() + - pVXL->VXL->TailerData->Bounds[0].Z + ); matRet = *pMat * hvamat; diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 34647180ff..662be0d040 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -83,7 +83,7 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse, // and therefore it will reuse the vanilla routine, which will crash inside of it pSuper->SetReadiness(true); // TODO: Can we use ClickFire instead of Launch? - pSuper->Launch(cell, true); + pSuper->Launch(cell, pHouse->IsCurrentPlayer()); pSuper->Reset(); if (!this->LaunchSW_RealLaunch) From b7e0ff85fabec676714ff4a9df2cdd9321e6a244 Mon Sep 17 00:00:00 2001 From: Starkku Date: Thu, 11 Apr 2024 11:17:14 +0300 Subject: [PATCH 38/43] Clarify Crit.Affects documentation --- docs/New-or-Enhanced-Logics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index ecbe892777..39b35cfb82 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1034,7 +1034,7 @@ RemoveMindControl=false ; boolean - Warheads can now apply additional chance-based damage or Warhead detonation ('critical hits') with the ability to customize chance, damage, affected targets, affected target HP threshold and animations of critical hit. - `Crit.Chance` determines chance for a critical hit to occur. By default this is checked once when the Warhead is detonated and every target that is susceptible to critical hits will be affected. If `Crit.ApplyChancePerTarget` is set, then whether or not the chance roll is successful is determined individually for each target. - `Crit.ExtraDamage` determines the damage dealt by the critical hit. If `Crit.Warhead` is set, the damage is used to detonate the specified Warhead on each affected target, otherwise the damage is directly dealt based on current Warhead's `Verses` settings. - - `Crit.Affects` can be used to customize types of targets that this Warhead can deal critical hits against. + - `Crit.Affects` can be used to customize types of targets that this Warhead can deal critical hits against. Critical hits cannot affect empty cells or cells containing only TerrainTypes, overlays etc. - `Crit.AffectsHouses` can be used to customize houses that this Warhead can deal critical hits against. - `Crit.AffectBelowPercent` can be used to set minimum percentage of their maximum `Strength` that targets must have left to be affected by a critical hit. - `Crit.AnimList` can be used to set a list of animations used instead of Warhead's `AnimList` if Warhead deals a critical hit to even one target. If `Crit.AnimList.PickRandom` is set (defaults to `AnimList.PickRandom`) then the animation is chosen randomly from the list. @@ -1049,7 +1049,7 @@ Crit.Chance=0.0 ; floating point value, percents or absolute Crit.ApplyChancePerTarget=false ; boolean Crit.ExtraDamage=0 ; integer Crit.Warhead= ; Warhead -Crit.Affects=all ; list of Affected Target Enumeration (none|land|water|empty|infantry|units|buildings|all) +Crit.Affects=all ; list of Affected Target Enumeration (none|land|water|infantry|units|buildings|all) Crit.AffectsHouses=all ; list of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) Crit.AffectBelowPercent=1.0 ; floating point value, percents or absolute (0.0-1.0) Crit.AnimList= ; list of animations From 391c3a10f92fa6331daf69d185de5241978c6b18 Mon Sep 17 00:00:00 2001 From: Starkku Date: Thu, 11 Apr 2024 11:31:30 +0300 Subject: [PATCH 39/43] Allow customizing extra warhead detonation chances --- docs/New-or-Enhanced-Logics.md | 10 ++++++---- docs/Whats-New.md | 1 + src/Ext/Bullet/Hooks.DetonateLogics.cpp | 16 ++++++++++++++-- src/Ext/WeaponType/Body.cpp | 2 ++ src/Ext/WeaponType/Body.h | 2 ++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 39b35cfb82..eec32d170d 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1275,13 +1275,15 @@ Burst.FireWithinSequence=false ; boolean ### Extra warhead detonations - It is now possible to have same weapon detonate multiple Warheads on impact by listing `ExtraWarheads`. The warheads are detonated at same location as the main one, after it in listed order. This only works in cases where a projectile has been fired by a weapon and still remembers it when it is detonated (due to currently existing technical limitations, this excludes `AirburstWeapon`). - - `ExtraWarheads.DamageOverrides` can be used to override the weapon's `Damage` for the extra Warhead detonations. Value from position matching the position from `ExtraWarheads` is used if found. If not, weapon `Damage` is used. + - `ExtraWarheads.DamageOverrides` can be used to override the weapon's `Damage` for the extra Warhead detonations. Value from position matching the position from `ExtraWarheads` is used if found, or last listed value if not found. If list is empty, WeaponType `Damage` is used. + - `ExtraWarheads.DetonationChances` can be used to customize the chance of each extra Warhead detonation occuring. Value from position matching the position from `ExtraWarheads` is used if found, or last listed value if not found. If list is empty, every extra Warhead detonation is guaranteed to occur. In `rulesmd.ini`: ```ini -[SOMEWEAPON] ; WeaponType -ExtraWarheads= ; list of WarheadTypes -ExtraWarheads.DamageOverrides= ; list of integers +[SOMEWEAPON] ; WeaponType +ExtraWarheads= ; list of WarheadTypes +ExtraWarheads.DamageOverrides= ; list of integers +ExtraWarheads.DetonationChances= ; list of floating-point values (percentage or absolute) ``` ### Feedback weapon diff --git a/docs/Whats-New.md b/docs/Whats-New.md index c3afa0bcb2..561b8a3e30 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -19,6 +19,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] #### From post-0.3 devbuilds +- `ExtraWarheads.DamageOverrides` now falls back to last listed value if list is shorter than `ExtraWarheads` for all Warhead detonations exceeding the length. - Air and Top layer contents are no longer sorted, animations in these layers no longer respect `YSortAdjust`. Animations attached to flying units now get their layer updated immediately after parent unit, if they are on same layer they will draw above the parent unit. - `AnimList.ShowOnZeroDamage` has been renamed to `CreateAnimsOnZeroDamage` to make it more clear it applies to both `AnimList` and splash animations. - INI inclusion and inheritance are now turned off by default and need to be turned on via command line flags `-Include` and `-Inheritance`. diff --git a/src/Ext/Bullet/Hooks.DetonateLogics.cpp b/src/Ext/Bullet/Hooks.DetonateLogics.cpp index 17a2d21264..ebfc0cb2e5 100644 --- a/src/Ext/Bullet/Hooks.DetonateLogics.cpp +++ b/src/Ext/Bullet/Hooks.DetonateLogics.cpp @@ -272,11 +272,23 @@ DEFINE_HOOK(0x46A290, BulletClass_Logics_Extras, 0x5) auto const pWH = pWeaponExt->ExtraWarheads[i]; auto const pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; int damage = defaultDamage; + size_t size = pWeaponExt->ExtraWarheads_DamageOverrides.size(); - if (pWeaponExt->ExtraWarheads_DamageOverrides.size() > i) + if (size > i) damage = pWeaponExt->ExtraWarheads_DamageOverrides[i]; + else if (size > 0) + damage = pWeaponExt->ExtraWarheads_DamageOverrides[size - 1]; - WarheadTypeExt::DetonateAt(pWH, *coords, pThis->Owner, damage, pOwner); + bool detonate = true; + size = pWeaponExt->ExtraWarheads_DetonationChances.size(); + + if (size > i) + detonate = pWeaponExt->ExtraWarheads_DetonationChances[i] >= ScenarioClass::Instance->Random.RandomDouble(); + if (size > 0) + detonate = pWeaponExt->ExtraWarheads_DetonationChances[size - 1] >= ScenarioClass::Instance->Random.RandomDouble(); + + if (detonate) + WarheadTypeExt::DetonateAt(pWH, *coords, pThis->Owner, damage, pOwner); } } diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 6fbf40d362..646e392fdc 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -59,6 +59,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->OmniFire_TurnToTarget.Read(exINI, pSection, "OmniFire.TurnToTarget"); this->ExtraWarheads.Read(exINI, pSection, "ExtraWarheads"); this->ExtraWarheads_DamageOverrides.Read(exINI, pSection, "ExtraWarheads.DamageOverrides"); + this->ExtraWarheads_DetonationChances.Read(exINI, pSection, "ExtraWarheads.DetonationChances"); this->AmbientDamage_Warhead.Read(exINI, pSection, "AmbientDamage.Warhead"); this->AmbientDamage_IgnoreTarget.Read(exINI, pSection, "AmbientDamage.IgnoreTarget"); } @@ -86,6 +87,7 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->OmniFire_TurnToTarget) .Process(this->ExtraWarheads) .Process(this->ExtraWarheads_DamageOverrides) + .Process(this->ExtraWarheads_DetonationChances) .Process(this->AmbientDamage_Warhead) .Process(this->AmbientDamage_IgnoreTarget) ; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 955a31bef5..484655352a 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -39,6 +39,7 @@ class WeaponTypeExt Valueable OmniFire_TurnToTarget; ValueableVector ExtraWarheads; ValueableVector ExtraWarheads_DamageOverrides; + ValueableVector ExtraWarheads_DetonationChances; Nullable AmbientDamage_Warhead; Valueable AmbientDamage_IgnoreTarget; @@ -62,6 +63,7 @@ class WeaponTypeExt , OmniFire_TurnToTarget { false } , ExtraWarheads {} , ExtraWarheads_DamageOverrides {} + , ExtraWarheads_DetonationChances {} , AmbientDamage_Warhead {} , AmbientDamage_IgnoreTarget { false } { } From dbc18c9869822279f54392d4472ffca512837b47 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Mon, 15 Apr 2024 15:06:41 +0800 Subject: [PATCH 40/43] Shift shadow height by 1 pixel Remove the hint to make it a secret --- docs/Fixed-or-Improved-Logics.md | 1 - src/Commands/Dummy.h | 2 +- src/Ext/Building/Hooks.cpp | 6 +----- src/Ext/TechnoType/Hooks.cpp | 4 +++- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index a40bbc48b8..e05417a9c5 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -856,7 +856,6 @@ IronCurtain.KeptOnDeploy= ; boolean, default to [CombatDamage]->IronCurtain.K ### Voxel turret shadow - Vehicle voxel turrets can now draw shadows if `[AudioVisual]` -> `DrawTurretShadow` is set to true. This can be overridden per VehicleType by setting `TurretShadow` in the vehicle's `artmd.ini` section. - - If you don't want to render the body's shadow at all, set `ShadowIndex` to an invalid number. In `rulesmd.ini`: ```ini diff --git a/src/Commands/Dummy.h b/src/Commands/Dummy.h index 69e3c0e837..c0527e9c45 100644 --- a/src/Commands/Dummy.h +++ b/src/Commands/Dummy.h @@ -3,7 +3,7 @@ #include "Commands.h" // The example command class -class DummyCommandClass : public PhobosCommandClass +class DummyCommandClass : public CommandClass { public: // CommandClass diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index e8acb056ca..509bb0da51 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -192,11 +192,7 @@ DEFINE_HOOK(0x4502F4, BuildingClass_Update_Factory_Phobos, 0x6) break; } - if (!currFactory) - { - Game::RaiseError(E_POINTER); - } - else if (!*currFactory) + if (!*currFactory) { *currFactory = pThis; return 0; diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index c065e2dc82..45ec0a5ae8 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -445,6 +445,8 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) } auto mtx = Matrix3D::VoxelDefaultMatrix() * shadow_matrix; + if (pThis->GetHeight() > 0) + shadow_point.Y += 1; if (uTypeExt->ShadowIndices.empty()) { @@ -705,7 +707,7 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7) // A nasty temporary backward compatibility option if (hva->LayerCount > 1 || pType->Turret) - // TO TEST : Check if this is the proper Z offset to shift the sections to the same level + // NEEDS IMPROVEMENT : Choose the proper Z offset to shift the sections to the same level hvamat.TranslateZ( -hvamat.GetZVal() - pVXL->VXL->TailerData->Bounds[0].Z From a4cdffa034c640284bdd17d56817268eabbf1720 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Thu, 18 Apr 2024 19:19:39 +0800 Subject: [PATCH 41/43] draw shadow for rockets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I forgot that aircrafts can be rocket too, désolé --- docs/Fixed-or-Improved-Logics.md | 1 + docs/New-or-Enhanced-Logics.md | 19 ++++----- src/Ext/TechnoType/Hooks.cpp | 69 +++++++++++++++++++------------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index e05417a9c5..5fc88d0f48 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -145,6 +145,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed railgun and fire particles being cut off by elevation changes. - Fixed teleport units' (for example CLEG) frozen-still timer being cleared after load game. - Fixed teleport units being unable to visually tilt on slopes. +- Fixed rockets' shadow location. - Fixed units with Teleport, Tunnel or Fly locomotor being unable to be visually flipped like other locomotors do. - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction. - Spawned aircraft now align with the spawner's facing when landing. diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index eec32d170d..590596e1cc 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -995,6 +995,16 @@ IsVoiceCreatedGlobal=false ; boolean VoiceCreated= ; sound entry ``` +### Convert TechnoType on owner house change +- You can now change a unit's type when changing ownership from human to computer or from computer to human. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] +Convert.HumanToComputer = ; TechnoType +Convert.ComputerToHuman = ; TechnoType +``` + ## Terrain ### Destroy animation & sound @@ -1102,15 +1112,6 @@ Convert.To= ; TechnoType Convert.AffectedHouses=all ; list of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) ``` -### Convert TechnoType on owner house change -- You can now change a unit's type when changing ownership from human to computer or from computer to human. - -In `rulesmd.ini`: -```ini -[SOMETECHNO] -Convert.HumanToComputer = ; TechnoType -Convert.ComputerToHuman = ; TechnoType -``` ### Custom 'SplashList' on Warheads diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 45ec0a5ae8..436c775bfc 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -18,6 +18,7 @@ #include #include #include +#include DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) { @@ -349,7 +350,7 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) GET(UnitClass*, pThis, EBP); enum { SkipDrawing = 0x73C5C9 }; auto const loco = pThis->Locomotor.GetInterfacePtr(); - if (!loco->Is_To_Have_Shadow()) + if (pThis->Type->NoShadow || !loco->Is_To_Have_Shadow()) return SkipDrawing; REF_STACK(Matrix3D, shadow_matrix, STACK_OFFSET(0x1C4, -0x130)); @@ -445,7 +446,7 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) } auto mtx = Matrix3D::VoxelDefaultMatrix() * shadow_matrix; - if (pThis->GetHeight() > 0) + if (height > 0) shadow_point.Y += 1; if (uTypeExt->ShadowIndices.empty()) @@ -570,45 +571,55 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) GET(AircraftClass*, pThis, EBP); GET(const int, height, EBX); REF_STACK(VoxelIndexKey, key, STACK_OFFSET(0xCC, -0xBC)); - REF_STACK(Point2D, flor, STACK_OFFSET(0xCC, -0xAC)); + GET_STACK(Point2D, flor, STACK_OFFSET(0xCC, -0xAC)); GET_STACK(RectangleStruct*, bound, STACK_OFFSET(0xCC, 0x10)); enum { FinishDrawing = 0x4148A5 }; - const auto loco = locomotion_cast(pThis->Locomotor); - if (!loco || !loco->Is_To_Have_Shadow() || pThis->IsSinking) + const auto loco = pThis->Locomotor.GetInterfacePtr(); + if (pThis->Type->NoShadow || !loco->Is_To_Have_Shadow() || pThis->IsSinking) return FinishDrawing; - const auto aTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); auto shadow_mtx = loco->Shadow_Matrix(&key); - const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; + const auto aTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); - if (RulesExt::Global()->HeightShadowScaling) + if (auto const flyLoco = locomotion_cast(loco)) { - const double minScale = RulesExt::Global()->HeightShadowScaling_MinScale; - const float cHeight = (float)aTypeExt->ShadowSizeCharacteristicHeight.Get(loco->FlightLevel); + const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; - if (cHeight > 0) + if (RulesExt::Global()->HeightShadowScaling) { - shadow_mtx.Scale((float)std::max(Pade2_2(baseScale_log* height / cHeight), minScale)); - key = std::bit_cast(-1); // I'm sorry + const double minScale = RulesExt::Global()->HeightShadowScaling_MinScale; + const float cHeight = (float)aTypeExt->ShadowSizeCharacteristicHeight.Get(flyLoco->FlightLevel); + + if (cHeight > 0) + { + shadow_mtx.Scale((float)std::max(Pade2_2(baseScale_log * height / cHeight), minScale)); + key = std::bit_cast(-1); // I'm sorry + } + } + else if (pThis->Type->ConsideredAircraft) + { + shadow_mtx.Scale((float)Pade2_2(baseScale_log)); } - } - else if (pThis->Type->ConsideredAircraft) - { - shadow_mtx.Scale((float)Pade2_2(baseScale_log)); - } - if (pThis->IsCrashing) + if (pThis->IsCrashing) + { + double arf = pThis->AngleRotatedForwards; + if (flyLoco->CurrentSpeed > pThis->Type->PitchSpeed) + arf += pThis->Type->PitchAngle; + shadow_mtx.ScaleY((float)Math::cos(pThis->AngleRotatedSideways)); + shadow_mtx.ScaleX((float)Math::cos(arf)); + } + } + else if (height > 0) { - double arf = pThis->AngleRotatedForwards; - if (loco->CurrentSpeed > pThis->Type->PitchSpeed) - arf += pThis->Type->PitchAngle; - shadow_mtx.ScaleY((float)Math::cos(pThis->AngleRotatedSideways)); - shadow_mtx.ScaleX((float)Math::cos(arf)); + // You must be Rocket, otherwise GO FUCK YOURSELF + shadow_mtx.ScaleX((float)Math::cos(static_cast(loco)->CurrentPitch)); + key = std::bit_cast(-1); } shadow_mtx = Matrix3D::VoxelDefaultMatrix() * shadow_mtx; - Point2D why = flor + loco->Shadow_Point(); + auto const main_vxl = &pThis->Type->MainVoxel; if (aTypeExt->ShadowIndices.empty()) @@ -620,7 +631,7 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) key, &pThis->Type->VoxelShadowCache, bound, - &why, + &flor, &shadow_mtx, true, nullptr, @@ -635,7 +646,7 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) key, &pThis->Type->VoxelShadowCache, bound, - &why, + &flor, &shadow_mtx, true, nullptr, @@ -646,6 +657,10 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) return FinishDrawing; } +// Shadow_Point of RocketLoco was forgotten to be set to {0,0}. It was an oversight. +// Anyway we don't need to call it from Fly or Rocket loco anymore +// DEFINE_JUMP(VTABLE, 0x7F0B4C, 0x4CF940); + DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7) { GET(FootClass*, pThis, EBX);//Maybe Techno later From ba85ae02ac2692e6748c02a9a8e20f0aedd1aa30 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 20 Apr 2024 16:47:56 +0800 Subject: [PATCH 42/43] default string for MSG:NotAvailableInMultiplayer --- src/Commands/QuickSave.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/QuickSave.cpp b/src/Commands/QuickSave.cpp index 5e756bd7a9..3af97cd4ca 100644 --- a/src/Commands/QuickSave.cpp +++ b/src/Commands/QuickSave.cpp @@ -64,6 +64,6 @@ void QuickSaveCommandClass::Execute(WWKey eInput) const } else { - PrintMessage(StringTable::LoadString("MSG:NotAvailableInMultiplayer")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:NotAvailableInMultiplayer", L"QuickSave is not available in multiplayer")); } } From 92b980fead3e36641081a6bc032762f066d26594 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 26 Apr 2024 18:13:29 +0300 Subject: [PATCH 43/43] Add clarification to Veinhole part of the docs regarding indices --- docs/Fixed-or-Improved-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 5fc88d0f48..86728fc259 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -949,7 +949,7 @@ Strength=1000 ; integer - the strength of the Veinhole ``` ```{warning} -The game expects certain overlays related to Veinholes to have certain indices, they are listed below. +The game expects certain overlays related to Veinholes to have certain indices, they are listed below. Please keep in mind that the indices in the OverlayTypes list are 0-based, formed internally by the game, and the identifiers left of "=" don't matter. Vanilla `rulesmd.ini` already has the required overlays listed at the correct indices. ``` In `rulesmd.ini`: