diff --git a/CREDITS.md b/CREDITS.md index 0fb9954050..94c775c83c 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -149,6 +149,7 @@ This page lists all the individual contributions to the project by their author. - Warhead activation target health thresholds enhancements - Event 606: AttachEffect is attaching to a Techno - Linked superweapons + - `EMPulseCannon.InaccurateRadius` for projectiles in `EMPulseCannon=yes` superweapons and `Ammo` support - **Starkku**: - Misc. minor bugfixes & improvements - AI script actions: diff --git a/YRpp b/YRpp index 1a4e510539..2d19944aa0 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 1a4e510539459bc3535b477e2b1a873fc62821f0 +Subproject commit 2d19944aa0846a098826b4cdd2e0ac00f36915b0 diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index de57186283..fa518bb3cd 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1092,12 +1092,17 @@ TabIndex=1 ; integer - Note that if you fire another `Type=EMPulse` superweapon with different weapon index that the same building is capable of launching before the first weapon was fired, the latter superweapon's settings will take precedence. - Additionally, due to technical limitations the targeting constraints will always default to primary weapon's `Range/MinimumRange` unless `SW.RangeMinimum` / `SW.RangeMaximum` are explicitly set. - Is is now also possible to have all other `Type=EMPulse` superweapons that can be fired by same buildings as current one be put on hold until first of the buildings currently set to fire goes off if the firing superweapon has `EMPulse.SuspendOthers=true`. +- Structures with `Ammo` now fire repeatedly until the weapon no longer has enough ammo. +- `EMPulseCannon.InaccurateRadius` defines an area around the target location where the projectile can land randomly. In `rulesmd.ini`: ```ini -[SOMESW] ; SuperWeaponType -EMPulse.WeaponIndex=0 ; integer, weapon slot index -EMPulse.SuspendOthers=false ; boolean +[SOMESW] ; SuperWeaponType +EMPulse.WeaponIndex=0 ; integer, weapon slot index +EMPulse.SuspendOthers=false ; boolean + +[SOMEPROJECTILE] ; Projectile +EMPulseCannon.InaccurateRadius=0 ; integer ``` ```{note} diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 007025be34..5f12e979f8 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -425,6 +425,7 @@ New: - [Damaged aircraft image changes](New-or-Enhanced-Logics.md#damaged-aircraft-image-changes) (by Fryone) - [Additional attached animation position customizations](Fixed-or-Improved-Logics.md#attached-animation-position-customization) (by Starkku) - Use `SkipCrushSlowdown=true` to avoid the bug related to `Accelerates=true` and `MovementZone=CrushAll` (by TaranDahl) +- `EMPulseCannon.InaccurateRadius` for projectiles in `EMPulseCannon=yes` superweapons and `Ammo` support (by FS-21) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 79757c55eb..9ebf22f8d4 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -470,6 +470,7 @@ void BuildingExt::ExtData::Serialize(T& Stm) .Process(this->CurrentLaserWeaponIndex) .Process(this->PoweredUpToLevel) .Process(this->EMPulseSW) + .Process(this->RandomEMPTarget); ; } diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 6d0d562907..8fb53b3fa5 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -38,6 +38,7 @@ class BuildingExt std::optional CurrentLaserWeaponIndex; int PoweredUpToLevel; // Distinct from UpgradeLevel, and set to highest PowersUpToLevel out of applied upgrades regardless of how many are currently applied to this building. SuperClass* EMPulseSW; + CellStruct RandomEMPTarget; ExtData(BuildingClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } @@ -52,6 +53,7 @@ class BuildingExt , CurrentLaserWeaponIndex {} , PoweredUpToLevel { 0 } , EMPulseSW {} + , RandomEMPTarget { CellStruct::Empty } { } void DisplayIncomeString(); diff --git a/src/Ext/BulletType/Body.cpp b/src/Ext/BulletType/Body.cpp index c18113fe30..8d3a212427 100644 --- a/src/Ext/BulletType/Body.cpp +++ b/src/Ext/BulletType/Body.cpp @@ -72,6 +72,7 @@ void BulletTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Parachuted_FallRate.Read(exINI, pSection, "Parachuted.FallRate"); this->Parachuted_MaxFallRate.Read(exINI, pSection, "Parachuted.MaxFallRate"); this->BombParachute.Read(exINI, pSection, "BombParachute"); + this->EMPulseCannon_InaccurateRadius.Read(exINI, pSection, "EMPulseCannon.InaccurateRadius"); // Ares 0.7 this->BallisticScatter_Min.Read(exINI, pSection, "BallisticScatter.Min"); @@ -168,6 +169,7 @@ void BulletTypeExt::ExtData::Serialize(T& Stm) .Process(this->Parachuted_FallRate) .Process(this->Parachuted_MaxFallRate) .Process(this->BombParachute) + .Process(this->EMPulseCannon_InaccurateRadius) .Process(this->TrajectoryType) // just keep this shit at last ; diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 6f13b084f5..6f30140694 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -71,6 +71,8 @@ class BulletTypeExt Nullable Parachuted_MaxFallRate; Nullable BombParachute; + Valueable EMPulseCannon_InaccurateRadius; + // Ares 0.7 Nullable BallisticScatter_Min; Nullable BallisticScatter_Max; @@ -120,6 +122,7 @@ class BulletTypeExt , Parachuted_FallRate { 1 } , Parachuted_MaxFallRate {} , BombParachute {} + , EMPulseCannon_InaccurateRadius { 0 } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 1799af3112..8a55b976e8 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #pragma region TechnoClass_SelectWeapon @@ -924,3 +926,83 @@ DEFINE_HOOK(0x5223B3, InfantryClass_Approach_Target_DeployFireWeapon, 0x6) R->EDI(deployFireWeapon == -1 ? pThis->SelectWeapon(pThis->Target) : deployFireWeapon); return 0x5223B9; } + +DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadius, 0x6) +{ + GET(BuildingClass*, pThis, ESI); + + HouseClass* pHouse = pThis->Owner; + const auto pCell = MapClass::Instance.TryGetCellAt(pHouse->EMPTarget); + + if (!pThis->Type->EMPulseCannon || !pCell) + return 0; + + // Obtain the weapon used by the EMP weapon + int weaponIndex = 0; + const auto pExt = BuildingExt::ExtMap.Find(pThis); + + if (pExt->EMPulseSW) + { + const auto pSWExt = SWTypeExt::ExtMap.Find(pExt->EMPulseSW->Type); + + if (pSWExt->EMPulse_WeaponIndex >= 0) + { + weaponIndex = pSWExt->EMPulse_WeaponIndex; + } + else + { + AbstractClass* pTarget = pCell; + + if (const auto pObject = pCell->GetContent()) + pTarget = pObject; + + weaponIndex = pThis->SelectWeapon(pTarget); + } + } + + const auto pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType; + + // Innacurate random strike Area calculation + int radius = BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->EMPulseCannon_InaccurateRadius; + radius = radius < 0 ? 0 : radius; + + if (radius > 0) + { + if (pExt->RandomEMPTarget == CellStruct::Empty) + { + // Calculate a new valid random target coordinate + do + { + pExt->RandomEMPTarget.X = (short)ScenarioClass::Instance->Random.RandomRanged(pHouse->EMPTarget.X - radius, pHouse->EMPTarget.X + radius); + pExt->RandomEMPTarget.Y = (short)ScenarioClass::Instance->Random.RandomRanged(pHouse->EMPTarget.Y - radius, pHouse->EMPTarget.Y + radius); + } + while (!MapClass::Instance.IsWithinUsableArea(pExt->RandomEMPTarget, false)); + } + + pHouse->EMPTarget = pExt->RandomEMPTarget; // Value overwrited every frame + } + + if (pThis->MissionStatus != 3) + return 0; + + pExt->RandomEMPTarget = CellStruct::Empty; + + // Restart the super weapon firing process if there is enough ammo set for the current weapon + if (pThis->Type->Ammo > 0 && pThis->Ammo > 0) + { + int ammo = WeaponTypeExt::ExtMap.Find(pWeapon)->Ammo.Get(1); + pThis->Ammo -= ammo; + pThis->Ammo = pThis->Ammo < 0 ? 0 : pThis->Ammo; + + if (pThis->Ammo >= ammo) + pThis->MissionStatus = 0; + + if (!pThis->ReloadTimer.InProgress()) + pThis->ReloadTimer.Start(pThis->Type->Reload); + + if (pThis->Ammo == 0 && pThis->Type->EmptyReload >= 0 && pThis->ReloadTimer.GetTimeLeft() > pThis->Type->EmptyReload) + pThis->ReloadTimer.Start(pThis->Type->EmptyReload); + } + + return 0; +} diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 78cbed50c2..dcfce004cc 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -160,6 +160,8 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) { this->SkipWeaponPicking = false; } + + this->Ammo.Read(exINI, pSection, "Ammo"); } template @@ -233,6 +235,7 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->DelayedFire_OnlyOnInitialBurst) .Process(this->DelayedFire_AnimOffset) .Process(this->DelayedFire_AnimOnTurret) + .Process(this->Ammo) ; }; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index b7aa6e16a3..6cf4ce6c85 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -89,6 +89,7 @@ class WeaponTypeExt Valueable DelayedFire_OnlyOnInitialBurst; Nullable DelayedFire_AnimOffset; Valueable DelayedFire_AnimOnTurret; + Nullable Ammo; bool SkipWeaponPicking; @@ -160,6 +161,7 @@ class WeaponTypeExt , DelayedFire_OnlyOnInitialBurst { false } , DelayedFire_AnimOffset {} , DelayedFire_AnimOnTurret { true } + , Ammo { } { } int GetBurstDelay(int burstIndex) const;