From 4390f7ce485cbd0aa61c07efbb2eca79e26b1e99 Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Tue, 8 Apr 2025 22:23:39 +0800 Subject: [PATCH 1/7] initial --- CREDITS.md | 1 + docs/Fixed-or-Improved-Logics.md | 33 ++++++++++ docs/Whats-New.md | 1 + src/Ext/Rules/Body.cpp | 20 ++++++ src/Ext/Rules/Body.h | 12 ++++ src/Ext/Techno/Body.cpp | 89 +++++++++++++++++++++++++++ src/Ext/Techno/Body.h | 2 + src/Ext/TechnoType/Body.cpp | 20 ++++++ src/Ext/TechnoType/Body.h | 20 ++++++ src/Ext/Unit/Hooks.Crushing.cpp | 102 ++++++++++++++++++++++++++++++- 10 files changed, 297 insertions(+), 3 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 9d48cec401..ace1ba54d6 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -366,6 +366,7 @@ This page lists all the individual contributions to the project by their author. - Allow voxel projectiles to use `AnimPalette` and `FirersPalette` - Customize damaged speed ratio of drive/ship loco - Customize overpower logic + - Crush level system - **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 3dcf06949a..63cc90bf89 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -564,6 +564,39 @@ ProneSpeed.NoCrawls=1.5 ; floating point value, multiplier ProneSpeed= ; floating point value, multiplier, by default, use the corresponding global value according to Crawls ``` +## Unit + +### Crush level system + +- It's possible to customize crush level and crushable level for now. Rolling is only allowed when the CrushLevel of the compactor is greater than the CrushableLevel of the crushed person. + +In `rulesmd.ini` +```ini +[General] +CrusherLevel=5 ; integer +CrushableLevel=5 ; integer +OmniCrusherLevel=10 ; integer +OmniCrushResistantLevel=10 ; integer + +[WallModel] +WallCrushableLevel=10 ; integer + +[SOMEUNIT] ; Crusher +CrushLevel= ; integer +CrushLevel.Veteran= ; integer +CrushLevel.Elite= ; integer + +[SOMETECHNO] ; infantry, unit, aircraft +CrushableLevel= ; integer +CrushableLevel.Veteran= ; integer +CrushableLevel.Elite= ; integer + +[SOMEINFANTRY] +DeployedCrushableLevel= ; integer +DeployedCrushableLevel.Veteran= ; integer +DeployedCrushableLevel.Elite= ; integer +``` + ## Particle systems ### Fire particle target coordinate adjustment when firer rotates diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 56b825d6fa..ec112d5666 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -355,6 +355,7 @@ New: - [Prone speed customization](Fixed-or-Improved-Logics.md#prone-speed-customization) (by TaranDahl) - [Customize damaged speed ratio of drive/ship loco](Fixed-or-Improved-Logics.md#damaged-speed-customization) (by NetsuNegi) - [Customize overpower logic](Fixed-or-Improved-Logics.md#customize-overpower-logic) (by NetsuNegi) +- Crush level system (by NetsuNegi) Vanilla fixes: - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 3178c0ab06..12ce071c6f 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -251,6 +251,11 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->DamagedSpeed.Read(exINI, GameStrings::General, "DamagedSpeed"); + this->CrusherLevel.Read(exINI, GameStrings::General, "CrusherLevel"); + this->CrushableLevel.Read(exINI, GameStrings::General, "CrushableLevel"); + this->OmniCrusherLevel.Read(exINI, GameStrings::General, "OmniCrusherLevel"); + this->OmniCrushResistantLevel.Read(exINI, GameStrings::General, "OmniCrushResistantLevel"); + // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); for (int i = 0; i < itemsCount; ++i) @@ -464,6 +469,11 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->ProneSpeed_Crawls) .Process(this->ProneSpeed_NoCrawls) .Process(this->DamagedSpeed) + .Process(this->CrusherLevel) + .Process(this->CrushableLevel) + .Process(this->OmniCrusherLevel) + .Process(this->OmniCrushResistantLevel) + .Process(this->WallCrushableLevel) ; } @@ -647,3 +657,13 @@ DEFINE_HOOK(0x6744E4, RulesClass_ReadJumpjetControls_Extra, 0x7) // skip vanilla JumpjetControls and make it earlier load // DEFINE_JUMP(LJMP, 0x668EB5, 0x668EBD); // RulesClass_Process_SkipJumpjetControls // Really necessary? won't hurt to read again + +DEFINE_HOOK(0x66D242, RulesClass_ReadWallModel_CrushableLevel, 0x5) +{ + GET(CCINIClass*, pINI, EDI); + INI_EX exINI(pINI); + + RulesExt::Global()->WallCrushableLevel.Read(exINI, "WallModel", "WallCrushableLevel"); + + return 0; +} diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b4a5d764c2..5b7ed3fe93 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -207,6 +207,12 @@ class RulesExt Valueable DamagedSpeed; + Valueable CrusherLevel; + Valueable CrushableLevel; + Valueable OmniCrusherLevel; + Valueable OmniCrushResistantLevel; + Valueable WallCrushableLevel; + ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } , InfantryGainSelfHealCap {} @@ -362,6 +368,12 @@ class RulesExt , ProneSpeed_NoCrawls { 1.5 } , DamagedSpeed { 0.75 } + + , CrusherLevel { 5 } + , CrushableLevel { 5 } + , OmniCrusherLevel { 10 } + , OmniCrushResistantLevel { 10 } + , WallCrushableLevel { 10 } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 7f262d8fb8..20a19fd7be 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -435,6 +435,95 @@ bool TechnoExt::IsTypeImmune(TechnoClass* pThis, TechnoClass* pSource) return false; } +int TechnoExt::GetCrushLevel(FootClass* pThis) +{ + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + + switch (pThis->Veterancy.GetRemainingLevel()) + { + case Rank::Elite: + if (pTypeExt->Elite_CrushLevel.isset()) + return pTypeExt->Elite_CrushLevel.Get(); + + case Rank::Veteran: + if (pTypeExt->Vet_CrushLevel.isset()) + return pTypeExt->Vet_CrushLevel.Get(); + + default: + if (pTypeExt->CrushLevel.isset()) + return pTypeExt->CrushLevel.Get(); + } + + const auto pType = pThis->GetTechnoType(); + + if (pType->OmniCrusher) + return RulesExt::Global()->OmniCrusherLevel; + + if (pType->Crusher || pThis->HasAbility(Ability::Crusher)) + return RulesExt::Global()->CrusherLevel; + + return 0; +} + +int TechnoExt::GetCrushableLevel(FootClass* pThis) +{ + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + + const auto rank = pThis->Veterancy.GetRemainingLevel(); + const auto pInfantry = specific_cast(pThis); + + if (pInfantry && pInfantry->IsDeployed()) + { + switch (rank) + { + case Rank::Elite: + if (pTypeExt->Elite_DeployedCrushableLevel.isset()) + return pTypeExt->Elite_DeployedCrushableLevel.Get(); + + case Rank::Veteran: + if (pTypeExt->Vet_DeployedCrushableLevel.isset()) + return pTypeExt->Vet_DeployedCrushableLevel.Get(); + + default: + if (pTypeExt->DeployedCrushableLevel.isset()) + return pTypeExt->DeployedCrushableLevel.Get(); + } + + if (pInfantry->Type->OmniCrushResistant) + return RulesExt::Global()->OmniCrushResistantLevel; + + if (!pInfantry->Type->DeployedCrushable) + return RulesExt::Global()->CrushableLevel; + } + else + { + switch (rank) + { + case Rank::Elite: + if (pTypeExt->Elite_CrushableLevel.isset()) + return pTypeExt->Elite_CrushableLevel.Get(); + + case Rank::Veteran: + if (pTypeExt->Vet_CrushableLevel.isset()) + return pTypeExt->Vet_CrushableLevel.Get(); + + default: + if (pTypeExt->CrushableLevel.isset()) + return pTypeExt->CrushableLevel.Get(); + } + + const auto pType = pThis->GetTechnoType(); + + if (pType->OmniCrushResistant) + return RulesExt::Global()->OmniCrushResistantLevel; + + if (!pType->Crushable) + return RulesExt::Global()->CrushableLevel; + } + + return 0; +} + /// /// Gets whether or not techno has listed AttachEffect types active on it /// diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a6f49c4fad..f5df174a7f 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -196,6 +196,8 @@ class TechnoExt static bool ConvertToType(FootClass* pThis, TechnoTypeClass* toType); static bool CanDeployIntoBuilding(UnitClass* pThis, bool noDeploysIntoDefaultValue = false); static bool IsTypeImmune(TechnoClass* pThis, TechnoClass* pSource); + static int GetCrushLevel(FootClass* pThis); + static int GetCrushableLevel(FootClass* pThis); static int GetTintColor(TechnoClass* pThis, bool invulnerability, bool airstrike, bool berserk); static int GetCustomTintColor(TechnoClass* pThis); static int GetCustomTintIntensity(TechnoClass* pThis); diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 8ce245f491..e781380776 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -508,6 +508,16 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->DamagedSpeed.Read(exINI, pSection, "DamagedSpeed"); + this->CrushLevel.Read(exINI, pSection, "CrushLevel"); + this->Vet_CrushLevel.Read(exINI, pSection, "CrushLevel.Veteran"); + this->Elite_CrushLevel.Read(exINI, pSection, "CrushLevel.Elite"); + this->CrushableLevel.Read(exINI, pSection, "CrushableLevel"); + this->Vet_CrushableLevel.Read(exINI, pSection, "CrushableLevel.Veteran"); + this->Elite_CrushableLevel.Read(exINI, pSection, "CrushableLevel.Elite"); + this->DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel"); + this->Vet_DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel.Veteran"); + this->Elite_DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel.Elite"); + this->SuppressKillWeapons.Read(exINI, pSection, "SuppressKillWeapons"); this->SuppressKillWeapons_Types.Read(exINI, pSection, "SuppressKillWeapons.Types"); @@ -936,6 +946,16 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->DamagedSpeed) + .Process(this->CrushLevel) + .Process(this->Vet_CrushLevel) + .Process(this->Elite_CrushLevel) + .Process(this->CrushableLevel) + .Process(this->Vet_CrushableLevel) + .Process(this->Elite_CrushableLevel) + .Process(this->DeployedCrushableLevel) + .Process(this->Vet_DeployedCrushableLevel) + .Process(this->Elite_DeployedCrushableLevel) + .Process(this->SuppressKillWeapons) .Process(this->SuppressKillWeapons_Types) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 5fc3e70e98..a81009c505 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -283,6 +283,16 @@ class TechnoTypeExt Nullable DamagedSpeed; + Nullable CrushLevel; + Nullable Vet_CrushLevel; + Nullable Elite_CrushLevel; + Nullable CrushableLevel; + Nullable Vet_CrushableLevel; + Nullable Elite_CrushableLevel; + Nullable DeployedCrushableLevel; + Nullable Vet_DeployedCrushableLevel; + Nullable Elite_DeployedCrushableLevel; + struct LaserTrailDataEntry { ValueableIdx idxType; @@ -561,6 +571,16 @@ class TechnoTypeExt , DamagedSpeed { } + , CrushLevel {} + , Vet_CrushLevel {} + , Elite_CrushLevel {} + , CrushableLevel {} + , Vet_CrushableLevel {} + , Elite_CrushableLevel {} + , DeployedCrushableLevel {} + , Vet_DeployedCrushableLevel {} + , Elite_DeployedCrushableLevel {} + , SuppressKillWeapons { false } , SuppressKillWeapons_Types {} diff --git a/src/Ext/Unit/Hooks.Crushing.cpp b/src/Ext/Unit/Hooks.Crushing.cpp index 978a4f8bb7..352b44e78a 100644 --- a/src/Ext/Unit/Hooks.Crushing.cpp +++ b/src/Ext/Unit/Hooks.Crushing.cpp @@ -1,12 +1,108 @@ #include #include #include +#include -#include +#include #include #include -DEFINE_HOOK(0x073B05B, UnitClass_PerCellProcess_TiltWhenCrushes, 0x6) +#define Hook_IsCrusher(addr, name, mode, size, reg, retn, retn2) \ +DEFINE_HOOK(addr, ##name##_IsCrusher##mode##, size) \ +{ \ + GET(FootClass*, pThis, reg); \ + return TechnoExt::GetCrushLevel(pThis) > 0 ? retn : retn2; \ +} + +Hook_IsCrusher(0x4B19B8, DriveLocomotionClass_ProcessMoving, , 0x8, ECX, 0x4B19D8, 0x4B1A04) +Hook_IsCrusher(0x5B1034, MechLocomotionClass_ProcessMoving, , 0x8, ECX, 0x5B1054, 0x5B108A) +Hook_IsCrusher(0x5B1418, MechLocomotionClass_ProcessMoving, 2, 0x8, ECX, 0x5B1438, 0x5B146E) +Hook_IsCrusher(0x6A1044, ShipLocomotionClass_ProcessMoving, , 0x8, ECX, 0x6A1064, 0x6A10B9) +Hook_IsCrusher(0x73AFEB, UnitClass_PerCellProcess, , 0x6, EBP, 0x73B002, 0x73B074) +Hook_IsCrusher(0x73FC6C, UnitClass_IsCellOccupied, , 0x6, EBX, 0x73FD37, 0x73FC91) +Hook_IsCrusher(0x741733, UnitClass_CrushCell, , 0x6, EDI, 0x741754, 0x74195E) + +#undef Hook_IsCrusher + +DEFINE_JUMP(LJMP, 0x73FB2A, 0x73FB47)// Skip Crusher check before call ObjectClass::IsCrushable() + +DEFINE_JUMP(LJMP, 0x73FE5F, 0x73FE7C)// Skip Crusher check before call ObjectClass::IsCrushable() +DEFINE_JUMP(LJMP, 0x741529, 0x741546)// Skip Crusher check before call ObjectClass::IsCrushable() + +DEFINE_HOOK(0x741603, UnitClass_ApproachTarget_OmniCrusher, 0x6) +{ + enum { IsOmniCrusher = 0x741613, NotOmniCrusher = 0x741685 }; + + GET(UnitClass*, pThis, ESI); + + return TechnoExt::GetCrushLevel(pThis) >= RulesExt::Global()->OmniCrusherLevel ? IsOmniCrusher : NotOmniCrusher; +} + +DEFINE_JUMP(LJMP, 0x7438F7, 0x743918)// Skip Crusher check before call ObjectClass::IsCrushable() + +DEFINE_HOOK(0x73B013, UnitClass_PerCellProcess_CrusherWall, 0x6) +{ + enum { CanCrush = 0x73B036, CannotCrush = 0x73B074 }; + + GET(OverlayTypeClass*, pOverlay, ESI); + + if (pOverlay->Crushable) + return CanCrush; + + if (!pOverlay->Wall) + return CannotCrush; + + GET(UnitClass*, pThis, EBP); + + return pThis->Type->MovementZone == MovementZone::CrusherAll || TechnoExt::GetCrushLevel(pThis) > RulesExt::Global()->WallCrushableLevel ? CanCrush : CannotCrush; +} + +DEFINE_HOOK(0x73F42E, UnitClass_IsCellOccupied_CrushWall, 0x6) +{ + enum { CanCrush = 0x73F46E, CannotCrush = 0x73F483 }; + + GET(UnitClass*, pThis, EBX); + + return pThis->Type->MovementZone == MovementZone::CrusherAll || TechnoExt::GetCrushLevel(pThis) > RulesExt::Global()->WallCrushableLevel ? CanCrush : CannotCrush; +} + +DEFINE_HOOK(0x4B1A1B, DriveLocomotionClass_4B0F20_CrusherAll, 0x8) +{ + enum { CanCrush = 0x4B1A2C, CannotCrush = 0x4B1A77 }; + + GET(FootClass*, pLinkedTo, ECX); + + return pLinkedTo->GetTechnoType()->MovementZone == MovementZone::CrusherAll || TechnoExt::GetCrushLevel(pLinkedTo) > RulesExt::Global()->OmniCrusherLevel ? CanCrush : CannotCrush; +} + +DEFINE_HOOK(0x5F6CD0, ObjectClass_IsCrushable, 0x6) +{ + enum { SkipGameCode = 0x5F6D90 }; + + GET(ObjectClass*, pThis, ECX); + GET_STACK(FootClass*, pCrusher, 0x4); + bool result = false; + + if (pThis && pCrusher && pThis != pCrusher) + { + if (pThis->AbstractFlags & AbstractFlags::Techno) + { + const auto pFoot = abstract_cast(pThis); + + if (pFoot && !pCrusher->Owner->IsAlliedWith(pFoot) && !pFoot->IsIronCurtained()) + result = TechnoExt::GetCrushLevel(pCrusher) > TechnoExt::GetCrushableLevel(pFoot); + } + else + { + result = pThis->GetType()->Crushable; + } + } + + R->AL(result); + return SkipGameCode; +} + +DEFINE_HOOK(0x73B05B, UnitClass_PerCellProcess_TiltWhenCrushes, 0x6) { enum { SkipGameCode = 0x73B074 }; @@ -22,7 +118,7 @@ DEFINE_HOOK(0x073B05B, UnitClass_PerCellProcess_TiltWhenCrushes, 0x6) return SkipGameCode; } -DEFINE_HOOK(0x0741941, UnitClass_OverrunSquare_TiltWhenCrushes, 0x6) +DEFINE_HOOK(0x741941, UnitClass_OverrunSquare_TiltWhenCrushes, 0x6) { enum { SkipGameCode = 0x74195E }; From f6f092ddc5fc4a71c5ac1f7a3f3d81ea504a5171 Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Tue, 8 Apr 2025 22:28:13 +0800 Subject: [PATCH 2/7] docs align --- docs/Fixed-or-Improved-Logics.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 63cc90bf89..6213c2427b 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -573,27 +573,27 @@ ProneSpeed= ; floating point value, multiplier, by default, us In `rulesmd.ini` ```ini [General] -CrusherLevel=5 ; integer -CrushableLevel=5 ; integer -OmniCrusherLevel=10 ; integer +CrusherLevel=5 ; integer +CrushableLevel=5 ; integer +OmniCrusherLevel=10 ; integer OmniCrushResistantLevel=10 ; integer [WallModel] -WallCrushableLevel=10 ; integer +WallCrushableLevel=10 ; integer -[SOMEUNIT] ; Crusher -CrushLevel= ; integer -CrushLevel.Veteran= ; integer -CrushLevel.Elite= ; integer +[SOMEUNIT] ; Crusher +CrushLevel= ; integer +CrushLevel.Veteran= ; integer +CrushLevel.Elite= ; integer -[SOMETECHNO] ; infantry, unit, aircraft -CrushableLevel= ; integer -CrushableLevel.Veteran= ; integer -CrushableLevel.Elite= ; integer +[SOMETECHNO] ; infantry, unit, aircraft +CrushableLevel= ; integer +CrushableLevel.Veteran= ; integer +CrushableLevel.Elite= ; integer [SOMEINFANTRY] -DeployedCrushableLevel= ; integer -DeployedCrushableLevel.Veteran= ; integer +DeployedCrushableLevel= ; integer +DeployedCrushableLevel.Veteran= ; integer DeployedCrushableLevel.Elite= ; integer ``` From fe1f34460c2e4be317145ffcd12e177cf0455b90 Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Fri, 11 Apr 2025 17:31:49 +0800 Subject: [PATCH 3/7] update hooks --- src/Ext/Unit/Hooks.Crushing.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Ext/Unit/Hooks.Crushing.cpp b/src/Ext/Unit/Hooks.Crushing.cpp index 352b44e78a..05257b031e 100644 --- a/src/Ext/Unit/Hooks.Crushing.cpp +++ b/src/Ext/Unit/Hooks.Crushing.cpp @@ -14,10 +14,14 @@ DEFINE_HOOK(addr, ##name##_IsCrusher##mode##, size) \ return TechnoExt::GetCrushLevel(pThis) > 0 ? retn : retn2; \ } -Hook_IsCrusher(0x4B19B8, DriveLocomotionClass_ProcessMoving, , 0x8, ECX, 0x4B19D8, 0x4B1A04) -Hook_IsCrusher(0x5B1034, MechLocomotionClass_ProcessMoving, , 0x8, ECX, 0x5B1054, 0x5B108A) -Hook_IsCrusher(0x5B1418, MechLocomotionClass_ProcessMoving, 2, 0x8, ECX, 0x5B1438, 0x5B146E) +Hook_IsCrusher(0x4B3504, DriveLocomotionClass_PassableCheck, , 0x8, ECX, 0x4B3516, 0x4B3525) +Hook_IsCrusher(0x4B414F, DriveLocomotionClass_PassableCheck, 2, 0x8, ECX, 0x4B4161, 0x4B4179) +Hook_IsCrusher(0x5B098E, MechLocomotionClass_ProcessMoving, , 0x8, ECX, 0x5B09A0, 0x5B09AF) +Hook_IsCrusher(0x5B1034, MechLocomotionClass_ProcessMoving, 2, 0x8, ECX, 0x5B1054, 0x5B108A) +Hook_IsCrusher(0x5B1418, MechLocomotionClass_ProcessMoving, 3, 0x8, ECX, 0x5B1438, 0x5B146E) Hook_IsCrusher(0x6A1044, ShipLocomotionClass_ProcessMoving, , 0x8, ECX, 0x6A1064, 0x6A10B9) +Hook_IsCrusher(0x6A2B53, ShipLocomotionClass_PassableCheck, , 0x8, ECX, 0x6A2B65, 0x6A2B74) +Hook_IsCrusher(0x6A377B, ShipLocomotionClass_PassableCheck, 2, 0x8, ECX, 0x6A378D, 0x6A37A5) Hook_IsCrusher(0x73AFEB, UnitClass_PerCellProcess, , 0x6, EBP, 0x73B002, 0x73B074) Hook_IsCrusher(0x73FC6C, UnitClass_IsCellOccupied, , 0x6, EBX, 0x73FD37, 0x73FC91) Hook_IsCrusher(0x741733, UnitClass_CrushCell, , 0x6, EDI, 0x741754, 0x74195E) @@ -66,7 +70,17 @@ DEFINE_HOOK(0x73F42E, UnitClass_IsCellOccupied_CrushWall, 0x6) return pThis->Type->MovementZone == MovementZone::CrusherAll || TechnoExt::GetCrushLevel(pThis) > RulesExt::Global()->WallCrushableLevel ? CanCrush : CannotCrush; } -DEFINE_HOOK(0x4B1A1B, DriveLocomotionClass_4B0F20_CrusherAll, 0x8) +DEFINE_HOOK(0x4B19B8, DriveLocomotionClass_ProcessMoving_CrushWall, 0x8) +{ + enum { CanCrush = 0x4B19E2, CannotCrush = 0x4B1A04 }; + + GET(FootClass*, pLinkedTo, ECX); + GET(OverlayTypeClass*, pOverlay, ESI); + + return pOverlay->Wall && TechnoExt::GetCrushLevel(pLinkedTo) > RulesExt::Global()->WallCrushableLevel ? CanCrush : CannotCrush; +} + +DEFINE_HOOK(0x4B1A1B, DriveLocomotionClass_ProcessMoving_CrusherAll, 0x8) { enum { CanCrush = 0x4B1A2C, CannotCrush = 0x4B1A77 }; From 92a14da95d7549ff69c29648bcc70f83b13f4b49 Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Sat, 12 Apr 2025 10:08:25 +0800 Subject: [PATCH 4/7] fix docs --- 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 527a9a2440..b7023cd90a 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -577,7 +577,7 @@ ProneSpeed= ; floating point value, multiplier, by default, us ### Crush level system -- It's possible to customize crush level and crushable level for now. Rolling is only allowed when the CrushLevel of the compactor is greater than the CrushableLevel of the crushed person. +- It's possible to customize crush level and crushable level for now. Rolling is only allowed when the CrushLevel of the compactor is greater than the CrushableLevel of the crushed object. In `rulesmd.ini` ```ini From 20cb48574230d16c84277f0e1dd4165ac00ccc3a Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Sat, 12 Apr 2025 11:54:17 +0800 Subject: [PATCH 5/7] fix the permanent deceleration bug after crushing sandbags --- src/Ext/Unit/Hooks.Crushing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Unit/Hooks.Crushing.cpp b/src/Ext/Unit/Hooks.Crushing.cpp index 05257b031e..cd61732e41 100644 --- a/src/Ext/Unit/Hooks.Crushing.cpp +++ b/src/Ext/Unit/Hooks.Crushing.cpp @@ -50,7 +50,7 @@ DEFINE_HOOK(0x73B013, UnitClass_PerCellProcess_CrusherWall, 0x6) GET(OverlayTypeClass*, pOverlay, ESI); - if (pOverlay->Crushable) + if (!pOverlay->ArrayIndex || pOverlay->Crushable) return CanCrush; if (!pOverlay->Wall) From 7346b0117648693589879e26b5fcfcc011fbb18d Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Mon, 21 Apr 2025 21:00:50 +0800 Subject: [PATCH 6/7] update --- src/Ext/Techno/Body.cpp | 10 ++++------ src/Ext/TechnoType/Body.h | 4 ++-- src/Ext/Unit/Hooks.Crushing.cpp | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index eca5336cf1..f77a87887e 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -433,7 +433,8 @@ bool TechnoExt::IsTypeImmune(TechnoClass* pThis, TechnoClass* pSource) int TechnoExt::GetCrushLevel(FootClass* pThis) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + const auto pType = pThis->GetTechnoType(); + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); switch (pThis->Veterancy.GetRemainingLevel()) { @@ -450,8 +451,6 @@ int TechnoExt::GetCrushLevel(FootClass* pThis) return pTypeExt->CrushLevel.Get(); } - const auto pType = pThis->GetTechnoType(); - if (pType->OmniCrusher) return RulesExt::Global()->OmniCrusherLevel; @@ -463,7 +462,8 @@ int TechnoExt::GetCrushLevel(FootClass* pThis) int TechnoExt::GetCrushableLevel(FootClass* pThis) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + const auto pType = pThis->GetTechnoType(); + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); const auto rank = pThis->Veterancy.GetRemainingLevel(); const auto pInfantry = specific_cast(pThis); @@ -508,8 +508,6 @@ int TechnoExt::GetCrushableLevel(FootClass* pThis) return pTypeExt->CrushableLevel.Get(); } - const auto pType = pThis->GetTechnoType(); - if (pType->OmniCrushResistant) return RulesExt::Global()->OmniCrushResistantLevel; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 0d8b93972c..217fcae595 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -293,12 +293,12 @@ class TechnoTypeExt Valueable SinkSpeed; Nullable ProneSpeed; - Nullable DamagedSpeed; + Nullable DamagedSpeed; Nullable Promote_VeteranAnimation; Nullable Promote_EliteAnimation; - Nullable CrushLevel; + Nullable CrushLevel; Nullable Vet_CrushLevel; Nullable Elite_CrushLevel; Nullable CrushableLevel; diff --git a/src/Ext/Unit/Hooks.Crushing.cpp b/src/Ext/Unit/Hooks.Crushing.cpp index cd61732e41..9a3d68f53f 100644 --- a/src/Ext/Unit/Hooks.Crushing.cpp +++ b/src/Ext/Unit/Hooks.Crushing.cpp @@ -50,6 +50,7 @@ DEFINE_HOOK(0x73B013, UnitClass_PerCellProcess_CrusherWall, 0x6) GET(OverlayTypeClass*, pOverlay, ESI); + // WW's code, ArrayIndex=0 is GASAND in default case, I guess it means sand bag is crushable if (!pOverlay->ArrayIndex || pOverlay->Crushable) return CanCrush; From 365957898a874335f2aa930cf19c38d5d31f05c2 Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Mon, 21 Apr 2025 21:42:11 +0800 Subject: [PATCH 7/7] use Promotable as template --- src/Ext/Techno/Body.cpp | 38 ++++++++++++++++++------------------- src/Ext/TechnoType/Body.cpp | 18 +++--------------- src/Ext/TechnoType/Body.h | 24 ++++++----------------- 3 files changed, 28 insertions(+), 52 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index f77a87887e..9372c7b693 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -439,16 +439,16 @@ int TechnoExt::GetCrushLevel(FootClass* pThis) switch (pThis->Veterancy.GetRemainingLevel()) { case Rank::Elite: - if (pTypeExt->Elite_CrushLevel.isset()) - return pTypeExt->Elite_CrushLevel.Get(); + if (pTypeExt->CrushLevel.Elite >= 0) + return pTypeExt->CrushLevel.Elite; case Rank::Veteran: - if (pTypeExt->Vet_CrushLevel.isset()) - return pTypeExt->Vet_CrushLevel.Get(); + if (pTypeExt->CrushLevel.Veteran >= 0) + return pTypeExt->CrushLevel.Veteran; default: - if (pTypeExt->CrushLevel.isset()) - return pTypeExt->CrushLevel.Get(); + if (pTypeExt->CrushLevel.Rookie >= 0) + return pTypeExt->CrushLevel.Rookie; } if (pType->OmniCrusher) @@ -468,21 +468,21 @@ int TechnoExt::GetCrushableLevel(FootClass* pThis) const auto rank = pThis->Veterancy.GetRemainingLevel(); const auto pInfantry = specific_cast(pThis); - if (pInfantry && pInfantry->IsDeployed()) + if (pInfantry && pInfantry->Uncrushable) { switch (rank) { case Rank::Elite: - if (pTypeExt->Elite_DeployedCrushableLevel.isset()) - return pTypeExt->Elite_DeployedCrushableLevel.Get(); + if (pTypeExt->DeployedCrushableLevel.Elite >= 0) + return pTypeExt->DeployedCrushableLevel.Elite; case Rank::Veteran: - if (pTypeExt->Vet_DeployedCrushableLevel.isset()) - return pTypeExt->Vet_DeployedCrushableLevel.Get(); + if (pTypeExt->DeployedCrushableLevel.Veteran >= 0) + return pTypeExt->DeployedCrushableLevel.Veteran; default: - if (pTypeExt->DeployedCrushableLevel.isset()) - return pTypeExt->DeployedCrushableLevel.Get(); + if (pTypeExt->DeployedCrushableLevel.Rookie >= 0) + return pTypeExt->DeployedCrushableLevel.Rookie; } if (pInfantry->Type->OmniCrushResistant) @@ -496,16 +496,16 @@ int TechnoExt::GetCrushableLevel(FootClass* pThis) switch (rank) { case Rank::Elite: - if (pTypeExt->Elite_CrushableLevel.isset()) - return pTypeExt->Elite_CrushableLevel.Get(); + if (pTypeExt->CrushableLevel.Elite >= 0) + return pTypeExt->CrushableLevel.Elite; case Rank::Veteran: - if (pTypeExt->Vet_CrushableLevel.isset()) - return pTypeExt->Vet_CrushableLevel.Get(); + if (pTypeExt->CrushableLevel.Veteran >= 0) + return pTypeExt->CrushableLevel.Veteran; default: - if (pTypeExt->CrushableLevel.isset()) - return pTypeExt->CrushableLevel.Get(); + if (pTypeExt->CrushableLevel.Rookie >= 0) + return pTypeExt->CrushableLevel.Rookie; } if (pType->OmniCrushResistant) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index e2719a2708..2640c05b09 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -520,15 +520,9 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->DamagedSpeed.Read(exINI, pSection, "DamagedSpeed"); this->ProneSpeed.Read(exINI, pSection, "ProneSpeed"); - this->CrushLevel.Read(exINI, pSection, "CrushLevel"); - this->Vet_CrushLevel.Read(exINI, pSection, "CrushLevel.Veteran"); - this->Elite_CrushLevel.Read(exINI, pSection, "CrushLevel.Elite"); - this->CrushableLevel.Read(exINI, pSection, "CrushableLevel"); - this->Vet_CrushableLevel.Read(exINI, pSection, "CrushableLevel.Veteran"); - this->Elite_CrushableLevel.Read(exINI, pSection, "CrushableLevel.Elite"); - this->DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel"); - this->Vet_DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel.Veteran"); - this->Elite_DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel.Elite"); + this->CrushLevel.Read(exINI, pSection, "CrushLevel.%s"); + this->CrushableLevel.Read(exINI, pSection, "CrushableLevel.%s"); + this->DeployedCrushableLevel.Read(exINI, pSection, "DeployedCrushableLevel.%s"); this->SuppressKillWeapons.Read(exINI, pSection, "SuppressKillWeapons"); this->SuppressKillWeapons_Types.Read(exINI, pSection, "SuppressKillWeapons.Types"); @@ -976,14 +970,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->ProneSpeed) .Process(this->CrushLevel) - .Process(this->Vet_CrushLevel) - .Process(this->Elite_CrushLevel) .Process(this->CrushableLevel) - .Process(this->Vet_CrushableLevel) - .Process(this->Elite_CrushableLevel) .Process(this->DeployedCrushableLevel) - .Process(this->Vet_DeployedCrushableLevel) - .Process(this->Elite_DeployedCrushableLevel) .Process(this->SuppressKillWeapons) .Process(this->SuppressKillWeapons_Types) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 217fcae595..322154f0d9 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -298,15 +298,9 @@ class TechnoTypeExt Nullable Promote_VeteranAnimation; Nullable Promote_EliteAnimation; - Nullable CrushLevel; - Nullable Vet_CrushLevel; - Nullable Elite_CrushLevel; - Nullable CrushableLevel; - Nullable Vet_CrushableLevel; - Nullable Elite_CrushableLevel; - Nullable DeployedCrushableLevel; - Nullable Vet_DeployedCrushableLevel; - Nullable Elite_DeployedCrushableLevel; + Promotable CrushLevel; + Promotable CrushableLevel; + Promotable DeployedCrushableLevel; struct LaserTrailDataEntry { @@ -600,15 +594,9 @@ class TechnoTypeExt , ProneSpeed { } , DamagedSpeed { } - , CrushLevel {} - , Vet_CrushLevel {} - , Elite_CrushLevel {} - , CrushableLevel {} - , Vet_CrushableLevel {} - , Elite_CrushableLevel {} - , DeployedCrushableLevel {} - , Vet_DeployedCrushableLevel {} - , Elite_DeployedCrushableLevel {} + , CrushLevel { -1 } + , CrushableLevel { -1 } + , DeployedCrushableLevel { -1 } , SuppressKillWeapons { false } , SuppressKillWeapons_Types { }