diff --git a/CREDITS.md b/CREDITS.md index bdb498d967..ea82980344 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -386,6 +386,8 @@ This page lists all the individual contributions to the project by their author. - Fix an unusual use of DeployFireWeapon for InfantryType - Fix the fact that when the selected unit is in a rearmed state, it can unconditionally use attack mouse on the target - Units can customize the attack voice that plays when using more weapons + - When `Speed=0` or the TechnoTypes cell cannot move due to `MovementRestrictedTo`, vehicles cannot attack targets beyond the weapon's range. `Area Guard` and `Hunt` missions will also become ineffective + - When the vehicle loses its target, you can customize whether to align the turret direction with the vehicle body - **NetsuNegi**: - Forbidding parallel AI queues by type - Jumpjet crash speed fix when crashing onto building diff --git a/YRpp b/YRpp index 2d19944aa0..d5a6fc5634 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 2d19944aa0846a098826b4cdd2e0ac00f36915b0 +Subproject commit d5a6fc5634283b45bec97c8cc42135dc6e63bd71 diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 8546dcbab1..002d9eb617 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -253,6 +253,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed the bug that Locomotor warhead won't stop working when firer (except for vehicle) stop firing. - Fixed the bug that hover vehicle will sink if destroyed on bridge. - Fixed the fact that when the selected unit is in a rearmed state, it can unconditionally use attack mouse on the target. +- When `Speed=0` or the TechnoTypes cell cannot move due to `MovementRestrictedTo`, vehicles cannot attack targets beyond the weapon's range. `Area Guard` and `Hunt` missions will also become ineffective. ## Fixes / interactions with other extensions diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 0ed79088c9..223cb88740 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2075,6 +2075,17 @@ FireUp= ; integer FireUp.ResetInRetarget=true ; boolean ``` +### Turret Response + +- When the vehicle loses its target, you can customize whether to align the turret direction with the vehicle body. + - When `Speed=0` or TechnoTypes cells cannot move due to `MovementRestrictedTo`, the default value is no; in other cases, it is yes. + +In `rulesmd.ini`: +```ini +[SOMEVEHICLE] ; VehicleType +TurretResponse= ; boolean +``` + ## Warheads ```{hint} diff --git a/docs/Whats-New.md b/docs/Whats-New.md index e6dee4bdbe..54dd17665b 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -428,6 +428,7 @@ New: - [Units can customize the attack voice that plays when using more weapons](New-or-Enhanced-Logics.md#multi-voiceattack) (by FlyStar) - Customize squid grapple animation (by NetsuNegi) - [Auto deploy for GI-like infantry](Fixed-or-Improved-Logics.md#auto-deploy-for-gi-like-infantry) (by TaranDahl) +- When the vehicle loses its target, you can customize whether to align the turret direction with the vehicle body (by FlyStar) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) @@ -444,6 +445,7 @@ Vanilla fixes: Phobos fixes: - Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi) +- When `Speed=0` or the TechnoTypes cell cannot move due to `MovementRestrictedTo`, vehicles cannot attack targets beyond the weapon's range. `Area Guard` and `Hunt` missions will also become ineffective (by FlyStar) Fixes / interactions with other extensions: - Allowed `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades (by Ollerus) diff --git a/src/Ext/Aircraft/Hooks.cpp b/src/Ext/Aircraft/Hooks.cpp index ffc3aacbdb..4c7495aa23 100644 --- a/src/Ext/Aircraft/Hooks.cpp +++ b/src/Ext/Aircraft/Hooks.cpp @@ -824,32 +824,3 @@ DEFINE_HOOK(0x4157EB, AircraftClass_Mission_SpyPlaneOverfly_MaxCount, 0x6) return 0; } - -DEFINE_HOOK(0x708FC0, TechnoClass_ResponseMove_Pickup, 0x5) -{ - enum { SkipResponse = 0x709015 }; - - GET(TechnoClass*, pThis, ECX); - - if (auto const pAircraft = abstract_cast(pThis)) - { - auto const pType = pAircraft->Type; - - if (pType->Carryall - && pAircraft->HasAnyLink() - && generic_cast(pAircraft->Destination)) - { - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - - if (pTypeExt->VoicePickup.isset()) - { - pThis->QueueVoice(pTypeExt->VoicePickup.Get()); - - R->EAX(1); - return SkipResponse; - } - } - } - - return 0; -} diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 7521101d2f..a32d1a52b3 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -731,6 +731,35 @@ bool TechnoExt::IsHealthInThreshold(TechnoClass* pObject, double min, double max return hp <= max && hp >= min; } +bool TechnoExt::CannotMove(UnitClass* pThis) +{ + const auto pType = pThis->Type; + + if (pType->Speed == 0) + return true; + + if (!pThis->IsInAir()) + { + LandType landType = pThis->GetCell()->LandType; + const LandType movementRestrictedTo = pType->MovementRestrictedTo; + + if (pThis->OnBridge + && (landType == LandType::Water || landType == LandType::Beach)) + { + landType = LandType::Road; + } + + if (movementRestrictedTo != LandType::None && + movementRestrictedTo != landType && + landType != LandType::Tunnel) + { + return true; + } + } + + return false; +} + // ============================= // load / save diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 6493623e47..9e02a64317 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -265,6 +265,7 @@ class TechnoExt static bool IsHealthInThreshold(TechnoClass* pObject, double min, double max); static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit); static AircraftTypeClass* GetAircraftTypeExtra(AircraftClass* pAircraft); + static bool CannotMove(UnitClass* pThis); // WeaponHelpers.cpp static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true); diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index e29baed8f5..b4e8ca3d57 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -1202,3 +1202,41 @@ DEFINE_HOOK(0x4DF3A6, FootClass_UpdateAttackMove_Follow, 0x6) } #pragma endregion + +DEFINE_HOOK(0x708FC0, TechnoClass_ResponseMove_Pickup, 0x5) +{ + enum { SkipResponse = 0x709015 }; + + GET(TechnoClass*, pThis, ECX); + + const AbstractType rtti = pThis->WhatAmI(); + + if (rtti == AbstractType::Aircraft) + { + auto const pAircraft = static_cast(pThis); + auto const pType = pAircraft->Type; + + if (pType->Carryall && pAircraft->HasAnyLink() + && generic_cast(pAircraft->Destination)) + { + auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (pTypeExt->VoicePickup.isset()) + { + pThis->QueueVoice(pTypeExt->VoicePickup.Get()); + + R->EAX(1); + return SkipResponse; + } + } + } + else if (rtti == AbstractType::Unit) + { + auto const pUnit = static_cast(pThis); + + if (TechnoExt::TechnoExt::CannotMove(pUnit)) + return SkipResponse; + } + + return 0; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index f32b4669f9..c7adfdca52 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -956,7 +956,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AttackMove_PursuitTarget.Read(exINI, pSection, "AttackMove.PursuitTarget"); this->InfantryAutoDeploy.Read(exINI, pSection, "InfantryAutoDeploy"); - + // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -1190,14 +1190,14 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) if (GeneralUtils::IsValidString(pThis->PaletteFile) && !pThis->Palette) Debug::Log("[Developer warning] [%s] has Palette=%s set but no palette file was loaded (missing file or wrong filename). Missing palettes cause issues with lighting recalculations.\n", pArtSection, pThis->PaletteFile); - this->LoadFromINIByWhatAmI(exArtINI, pArtSection); + this->LoadFromINIByWhatAmI(exINI, pSection, exArtINI, pArtSection); // VoiceIFVRepair from Ares 0.2 this->VoiceIFVRepair.Read(exINI, pSection, "VoiceIFVRepair"); this->ParseVoiceWeaponAttacks(exINI, pSection, this->VoiceWeaponAttacks, this->VoiceEliteWeaponAttacks); } -void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exArtINI, const char* pArtSection) +void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection) { AbstractType abs = this->OwnerObject()->WhatAmI(); @@ -1207,6 +1207,7 @@ void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exArtINI, const char* { this->FireUp.Read(exArtINI, pArtSection, "FireUp"); this->FireUp_ResetInRetarget.Read(exArtINI, pArtSection, "FireUp.ResetInRetarget"); + this->TurretResponse.Read(exINI, pSection, "TurretResponse"); //this->SecondaryFire.Read(exArtINI, pArtSection, "SecondaryFire"); break; } @@ -1591,6 +1592,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->VoiceEliteWeaponAttacks) .Process(this->InfantryAutoDeploy) + + .Process(this->TurretResponse) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 75d46d3169..72ea4be13d 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -418,6 +418,8 @@ class TechnoTypeExt Nullable InfantryAutoDeploy; + Nullable TurretResponse; + ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) , HealthBar_Hide { false } , UIDescription {} @@ -785,6 +787,8 @@ class TechnoTypeExt , VoiceEliteWeaponAttacks {} , InfantryAutoDeploy {} + + , TurretResponse {} { } virtual ~ExtData() = default; @@ -796,7 +800,7 @@ class TechnoTypeExt virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; - void LoadFromINIByWhatAmI(INI_EX& exArtINI, const char* pArtSection); + void LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection); void ApplyTurretOffset(Matrix3D* mtx, double factor = 1.0); void CalculateSpawnerRange(); diff --git a/src/Ext/Unit/Hooks.DisallowMoving.cpp b/src/Ext/Unit/Hooks.DisallowMoving.cpp index 72187c7748..0dcd22af7a 100644 --- a/src/Ext/Unit/Hooks.DisallowMoving.cpp +++ b/src/Ext/Unit/Hooks.DisallowMoving.cpp @@ -4,48 +4,48 @@ #include "UnitClass.h" #include -#include +#include DEFINE_HOOK(0x740A93, UnitClass_Mission_Move_DisallowMoving, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x740AEF : 0; + return TechnoExt::TechnoExt::CannotMove(pThis) ? 0x740AEF : 0; } DEFINE_HOOK(0x741AA7, UnitClass_Assign_Destination_DisallowMoving, 0x6) { GET(UnitClass*, pThis, EBP); - return pThis->Type->Speed == 0 ? 0x743173 : 0; + return TechnoExt::CannotMove(pThis) ? 0x743173 : 0; } DEFINE_HOOK(0x743B4B, UnitClass_Scatter_DisallowMoving, 0x6) { GET(UnitClass*, pThis, EBP); - return pThis->Type->Speed == 0 ? 0x74408E : 0; + return TechnoExt::CannotMove(pThis) ? 0x74408E : 0; } DEFINE_HOOK(0x74038F, UnitClass_What_Action_ObjectClass_DisallowMoving_1, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x7403A3 : 0; + return TechnoExt::CannotMove(pThis) ? 0x7403A3 : 0; } DEFINE_HOOK(0x7403B7, UnitClass_What_Action_ObjectClass_DisallowMoving_2, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x7403C1 : 0; + return TechnoExt::CannotMove(pThis) ? 0x7403C1 : 0; } DEFINE_HOOK(0x740709, UnitClass_What_Action_DisallowMoving_1, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x740727 : 0; + return TechnoExt::CannotMove(pThis) ? 0x740727 : 0; } DEFINE_HOOK(0x740744, UnitClass_What_Action_DisallowMoving_2, 0x6) @@ -55,7 +55,7 @@ DEFINE_HOOK(0x740744, UnitClass_What_Action_DisallowMoving_2, 0x6) GET(UnitClass*, pThis, ESI); GET_STACK(Action, result, 0x30); - if (pThis->Type->Speed == 0) + if (TechnoExt::CannotMove(pThis)) { if (result == Action::Move) return ReturnNoMove; @@ -72,12 +72,83 @@ DEFINE_HOOK(0x736B60, UnitClass_Rotation_AI_DisallowMoving, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x736AFB : 0; + return !TechnoTypeExt::ExtMap.Find(pThis->Type)->TurretResponse.Get(!TechnoExt::CannotMove(pThis)) ? 0x736AFB : 0; } DEFINE_HOOK(0x73891D, UnitClass_Active_Click_With_DisallowMoving, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x738927 : 0; + return TechnoExt::CannotMove(pThis) ? 0x738927 : 0; +} + +DEFINE_HOOK_AGAIN(0x744103, UnitClass_Mission_DisallowMoving, 0x6) // UnitClass::Mission_AreaGuard +DEFINE_HOOK(0x73EFC4, UnitClass_Mission_DisallowMoving, 0x6) // UnitClass::Mission_Hunt +{ + GET(UnitClass*, pThis, ESI); + + if (TechnoExt::CannotMove(pThis)) + { + pThis->QueueMission(Mission::Guard, false); + pThis->NextMission(); + + R->EAX(pThis->Mission_Guard()); + return R->Origin() == 0x744103 ? 0x744173 : 0x73F091; + } + + return 0; +} + +DEFINE_HOOK(0x74132B, UnitClass_GetFireError_DisallowMoving, 0x7) +{ + GET(UnitClass*, pThis, ESI); + GET(FireError, result, EAX); + + if (result == FireError::RANGE && TechnoExt::CannotMove(pThis)) + R->EAX(FireError::ILLEGAL); + + return 0; +} + +namespace UnitApproachTargetTemp +{ + int WeaponIndex; +} + +DEFINE_HOOK(0x7414E0, UnitClass_ApproachTarget_DisallowMoving, 0xA) +{ + GET(UnitClass*, pThis, ECX); + + int weaponIndex = -1; + + if (TechnoExt::CannotMove(pThis)) + { + const auto pTarget = pThis->Target; + weaponIndex = pThis->SelectWeapon(pTarget); + + if (!pThis->IsCloseEnough(pTarget, weaponIndex)) + { + pThis->SetTarget(nullptr); + return 0x741690; + } + } + + UnitApproachTargetTemp::WeaponIndex = weaponIndex; + return 0; +} + +DEFINE_HOOK(0x7415A9, UnitClass_ApproachTarget_SetWeaponIndex, 0x6) +{ + if (UnitApproachTargetTemp::WeaponIndex != -1) + { + GET(UnitClass*, pThis, ESI); + + R->EDI(pThis); + R->EAX(UnitApproachTargetTemp::WeaponIndex); + UnitApproachTargetTemp::WeaponIndex = -1; + + return 0x7415BA; + } + + return 0; }