From a5c7185f0253ec12d9b264cbcb49f9705e71f77b Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 4 Jun 2025 14:29:30 +0200 Subject: [PATCH 1/6] Initial Commit --- CREDITS.md | 2 + src/Ext/Building/Body.cpp | 1 + src/Ext/Building/Body.h | 2 + src/Ext/BulletType/Body.cpp | 2 + src/Ext/BulletType/Body.h | 3 ++ src/Ext/Techno/Hooks.Firing.cpp | 81 +++++++++++++++++++++++++++++++++ src/Ext/WeaponType/Body.cpp | 3 ++ src/Ext/WeaponType/Body.h | 2 + 8 files changed, 96 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index bc567ff11f..d4b543edaa 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -141,6 +141,8 @@ This page lists all the individual contributions to the project by their author. - Map Events 604 & 605 for checking if a specific Techno enters in a cell - Warhead that can not kill - `Pips.HideIfNoStrength` and `SelfHealing.EnabledBy` additions for shields + - `Ammo` support in superweapons with `EMPulseCannon=yes` + - `EMPulseCannon.InaccurateRadius` for projectiles in `EMPulseCannon=yes` superweapons - **Starkku**: - Misc. minor bugfixes & improvements - AI script actions: diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index ffa3f1c6e0..bebcbc70f8 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -465,6 +465,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 2f706ed11d..17efff00fa 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->AirburstWeapon_SourceScatterMin.Read(exINI, pSection, "AirburstWeapon.SourceScatterMin"); this->AirburstWeapon_SourceScatterMax.Read(exINI, pSection, "AirburstWeapon.SourceScatterMax"); 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"); @@ -164,6 +165,7 @@ void BulletTypeExt::ExtData::Serialize(T& Stm) .Process(this->AirburstWeapon_SourceScatterMin) .Process(this->AirburstWeapon_SourceScatterMax) .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 58ac6cf9ee..946cade7f0 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -67,6 +67,8 @@ class BulletTypeExt Valueable BombParachute; + Valueable EMPulseCannon_InaccurateRadius; + // Ares 0.7 Nullable BallisticScatter_Min; Nullable BallisticScatter_Max; @@ -112,6 +114,7 @@ class BulletTypeExt , AirburstWeapon_SourceScatterMin { Leptons(0) } , AirburstWeapon_SourceScatterMax { Leptons(0) } , BombParachute { } + , EMPulseCannon_InaccurateRadius { 0 } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index e62671d6bc..18d6432d24 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #pragma region TechnoClass_SelectWeapon @@ -943,3 +945,82 @@ DEFINE_HOOK(0x5223B3, InfantryClass_Approach_Target_DeployFireWeapon, 0x6) R->EDI(pThis->Type->DeployFireWeapon == -1 ? pThis->SelectWeapon(pThis->Target) : pThis->Type->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; + + 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 455ec8c472..82a2746e58 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -128,6 +128,8 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Beam_Amplitude.Read(exINI, pSection, "Beam.Amplitude"); this->Beam_IsHouseColor.Read(exINI, pSection, "Beam.IsHouseColor"); this->LaserThickness.Read(exINI, pSection, "LaserThickness"); + + this->Ammo.Read(exINI, pSection, "Ammo"); } template @@ -185,6 +187,7 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->Beam_Amplitude) .Process(this->Beam_IsHouseColor) .Process(this->LaserThickness) + .Process(this->Ammo) ; }; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 323910a811..a1fbbd9070 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -74,6 +74,7 @@ class WeaponTypeExt Valueable Beam_Amplitude; Valueable Beam_IsHouseColor; Valueable LaserThickness; + Nullable Ammo; ExtData(WeaponTypeClass* OwnerObject) : Extension(OwnerObject) , DiskLaser_Radius { DiskLaserClass::Radius } @@ -127,6 +128,7 @@ class WeaponTypeExt , Beam_Amplitude { 40.0 } , Beam_IsHouseColor { false } , LaserThickness { 3 } + , Ammo { } { } int GetBurstDelay(int burstIndex) const; From c37e82a99afa84fa63ce20ff65e233577d6d0987 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 4 Jun 2025 16:22:59 +0200 Subject: [PATCH 2/6] docs --- docs/New-or-Enhanced-Logics.md | 11 ++++++++--- docs/Whats-New.md | 1 + src/Ext/Techno/Hooks.Firing.cpp | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index daa9a8d48c..4452c042c2 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1022,12 +1022,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 c9aa477958..3a869f2faa 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -381,6 +381,7 @@ New: - Dehardcoded 255 limit of `OverlayType` (by secsome) - [Customizable airstrike flare colors](Fixed-or-Improved-Logics.md#airstrike-flare-customizations) (by Starkku) - Allowed player's self-healing effects to be benefited by allied or `PlayerControl=true` houses (by Ollerus) +- 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/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 18d6432d24..d96559e65c 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -983,6 +983,7 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu // Innacurate random strike Area calculation int radius = BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->EMPulseCannon_InaccurateRadius; + radius = radius < 0 ? 0 : radius; if (radius > 0) { From 9aff7e13dc282307a3031aedf9f86598d84fe12e Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 4 Jun 2025 16:26:04 +0200 Subject: [PATCH 3/6] finish docs --- CREDITS.md | 3 +-- docs/Whats-New.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index d4b543edaa..3583cae390 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -141,8 +141,7 @@ This page lists all the individual contributions to the project by their author. - Map Events 604 & 605 for checking if a specific Techno enters in a cell - Warhead that can not kill - `Pips.HideIfNoStrength` and `SelfHealing.EnabledBy` additions for shields - - `Ammo` support in superweapons with `EMPulseCannon=yes` - - `EMPulseCannon.InaccurateRadius` for projectiles in `EMPulseCannon=yes` superweapons + - `EMPulseCannon.InaccurateRadius` for projectiles in `EMPulseCannon=yes` superweapons and `Ammo` support - **Starkku**: - Misc. minor bugfixes & improvements - AI script actions: diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 3a869f2faa..86acf09b81 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -381,7 +381,7 @@ New: - Dehardcoded 255 limit of `OverlayType` (by secsome) - [Customizable airstrike flare colors](Fixed-or-Improved-Logics.md#airstrike-flare-customizations) (by Starkku) - Allowed player's self-healing effects to be benefited by allied or `PlayerControl=true` houses (by Ollerus) -- +- `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) From 7b9acff90554cafaa6b42593a023bd50a4eb10df Mon Sep 17 00:00:00 2001 From: FS-21 Date: Thu, 20 Aug 2026 08:59:51 +0200 Subject: [PATCH 4/6] replaced ammo by Empulse.Burst tag --- YRpp | 2 +- src/Ext/Building/Body.cpp | 2 + src/Ext/Building/Body.h | 4 ++ src/Ext/Building/Hooks.cpp | 32 +++++++++------- src/Ext/SWType/Body.cpp | 2 + src/Ext/SWType/Body.h | 2 + src/Ext/Techno/Hooks.Firing.cpp | 66 +++++++++++++++++++-------------- 7 files changed, 68 insertions(+), 42 deletions(-) diff --git a/YRpp b/YRpp index 5af96790ce..c6c522f77b 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 5af96790ce73e4ea068a390c60c124dccbc220e1 +Subproject commit c6c522f77b58508588d9a8652c53356e4aef7a96 diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index ee937713d6..25d818e56b 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -482,6 +482,8 @@ void BuildingExt::ExtData::Serialize(T& Stm) .Process(this->PoweredUpToLevel) .Process(this->CurrentEMPulseSW) .Process(this->RandomEMPTarget) + .Process(this->EMPulseBurstIndex) + .Process(this->EMPulseBurst) ; } diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index c8dcab4bbc..97425b4a8c 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -39,6 +39,8 @@ class BuildingExt 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* CurrentEMPulseSW; CellStruct RandomEMPTarget; + int EMPulseBurstIndex; + int EMPulseBurst; ExtData(BuildingClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } @@ -54,6 +56,8 @@ class BuildingExt , PoweredUpToLevel { 0 } , CurrentEMPulseSW {} , RandomEMPTarget { CellStruct::Empty } + , EMPulseBurstIndex { 0 } + , EMPulseBurst { 0 } { } void DisplayIncomeString(); diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 976d9f9236..432caec599 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -167,27 +167,31 @@ DEFINE_HOOK(0x44CEEC, BuildingClass_Mission_Missile_EMPulseSelectWeapon, 0x6) } } - if (pSWExt->EMPulse_SuspendOthers) + // Only clean up on the last burst shot + if (pExt->EMPulseBurstIndex + 1 >= pExt->EMPulseBurst) { - auto const pHouseExt = HouseExt::ExtMap.Find(pOwner); - const int index = pExt->CurrentEMPulseSW->Type->ArrayIndex; - - if (pHouseExt->SuspendedEMPulseSWs.count(index)) + if (pSWExt->EMPulse_SuspendOthers) { - auto& supers = pOwner->Supers; + auto const pHouseExt = HouseExt::ExtMap.Find(pOwner); + const int index = pExt->CurrentEMPulseSW->Type->ArrayIndex; - for (auto const& swidx : pHouseExt->SuspendedEMPulseSWs[index]) + if (pHouseExt->SuspendedEMPulseSWs.count(index)) { - auto const super = supers[swidx]; - super->IsSuspended = false; - } + auto& supers = pOwner->Supers; + + for (auto const& swidx : pHouseExt->SuspendedEMPulseSWs[index]) + { + auto const super = supers[swidx]; + super->IsSuspended = false; + } - pHouseExt->SuspendedEMPulseSWs[index].clear(); - pHouseExt->SuspendedEMPulseSWs.erase(index); + pHouseExt->SuspendedEMPulseSWs[index].clear(); + pHouseExt->SuspendedEMPulseSWs.erase(index); + } } - } - pExt->CurrentEMPulseSW = nullptr; + pExt->CurrentEMPulseSW = nullptr; + } EMPulseCannonTemp::weaponIndex = weaponIndex; R->EAX(pThis->GetWeapon(weaponIndex)); return SkipGameCode; diff --git a/src/Ext/SWType/Body.cpp b/src/Ext/SWType/Body.cpp index f230746dc7..18ebb91e61 100644 --- a/src/Ext/SWType/Body.cpp +++ b/src/Ext/SWType/Body.cpp @@ -87,6 +87,7 @@ void SWTypeExt::ExtData::Serialize(T& Stm) .Process(this->EMPulse_SuspendOthers) .Process(this->EMPulse_Cannons) .Process(this->EMPulse_TargetSelf) + .Process(this->EMPulse_Burst) .Process(this->SW_Link) .Process(this->SW_Link_Grant) .Process(this->SW_Link_Ready) @@ -157,6 +158,7 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->EMPulse_SuspendOthers.Read(exINI, pSection, "EMPulse.SuspendOthers"); this->EMPulse_Cannons.Read(exINI, pSection, "EMPulse.Cannons"); this->EMPulse_TargetSelf.Read(exINI, pSection, "EMPulse.TargetSelf"); + this->EMPulse_Burst.Read(exINI, pSection, "EMPulse.Burst"); char tempBuffer[32]; // LimboDelivery.RandomWeights diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index e4af1c3879..7406c90815 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -103,6 +103,7 @@ class SWTypeExt Valueable EMPulse_SuspendOthers; ValueableVector EMPulse_Cannons; Valueable EMPulse_TargetSelf; + Valueable EMPulse_Burst; ValueableIdxVector SW_Link; Valueable SW_Link_Grant; @@ -179,6 +180,7 @@ class SWTypeExt , EMPulse_SuspendOthers { false } , EMPulse_Cannons {} , EMPulse_TargetSelf { false } + , EMPulse_Burst { 1 } , SW_Link {} , SW_Link_Grant { false } , SW_Link_Ready { false } diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 6f01be8625..8fab412a13 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -1057,6 +1057,22 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu if (!pThis->Type->EMPulseCannon) return 0; + const auto pExt = BuildingExt::ExtMap.Find(pThis); + + // Pause firing sequence if the structure is unpowered or offline + const bool isOffline = !pThis->IsPowerOnline() + || !pThis->StuffEnabled + || pThis->Deactivated + || pThis->IsUnderEMP() + || pExt->TechnoExtData->AE.DisableWeapons; + + if (isOffline) + { + pThis->MissionStatus = 0; + // Return to game loop to wait without advancing state + return 0; + } + HouseClass* pHouse = pThis->Owner; const auto pCell = MapClass::Instance.TryGetCellAt(pHouse->EMPTarget); @@ -1065,11 +1081,11 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu // Obtain the weapon used by the EMP weapon int weaponIndex = 0; - const auto pExt = BuildingExt::ExtMap.Find(pThis); if (pExt->CurrentEMPulseSW) { const auto pSWExt = SWTypeExt::ExtMap.Find(pExt->CurrentEMPulseSW->Type); + pExt->EMPulseBurst = pSWExt->EMPulse_Burst; if (pSWExt->EMPulse_WeaponIndex >= 0) { @@ -1088,7 +1104,7 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu const auto pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType; - // Innacurate random strike Area calculation + // Inaccurate random strike area calculation int radius = BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->EMPulseCannon_InaccurateRadius; radius = radius < 0 ? 0 : radius; @@ -1096,16 +1112,19 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu { 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)); + CoordStruct targetCoords = CellClass::Cell2Coord(pHouse->EMPTarget); + int maxDistanceLeptons = radius * Unsorted::LeptonsPerCell; + int distanceLeptons = ScenarioClass::Instance->Random.RandomRanged(0, maxDistanceLeptons); + CoordStruct randomCoords = MapClass::GetRandomCoordsNear(targetCoords, distanceLeptons, false); + CellStruct randomCell = CellClass::Coord2Cell(randomCoords); + + if (MapClass::Instance.IsWithinUsableArea(randomCell, false)) + pExt->RandomEMPTarget = randomCell; + else + pExt->RandomEMPTarget = pHouse->EMPTarget; } - pHouse->EMPTarget = pExt->RandomEMPTarget; // Value overwrited every frame + pHouse->EMPTarget = pExt->RandomEMPTarget; // Value overwritten every frame } if (pThis->MissionStatus != 3) @@ -1113,24 +1132,17 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu 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) - { - const int ammo = WeaponTypeExt::ExtMap.Find(pWeapon)->Ammo.Get(1); - - for (int i = 0; i < ammo; i++) - { - pThis->DecreaseAmmo(); // Use vanilla's ammo handler - } - - if (pThis->Ammo >= ammo) - pThis->MissionStatus = 0; + // Increment burst index and restart if there are burst shots remaining + pExt->EMPulseBurstIndex++; - 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); + if (pExt->EMPulseBurstIndex < pExt->EMPulseBurst) + { + pThis->MissionStatus = 0; + } + else + { + pExt->EMPulseBurstIndex = 0; + pExt->EMPulseBurst = 0; } return 0; From 0907d24c9a6b1a9c2bd25164a50225934004a09a Mon Sep 17 00:00:00 2001 From: FS-21 Date: Thu, 20 Aug 2026 09:10:30 +0200 Subject: [PATCH 5/6] Cleaning code --- src/Ext/Building/Body.cpp | 1 - src/Ext/Building/Body.h | 2 -- src/Ext/Building/Hooks.cpp | 2 +- src/Ext/Techno/Hooks.Firing.cpp | 6 +++--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 25d818e56b..55be6eb17f 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -483,7 +483,6 @@ void BuildingExt::ExtData::Serialize(T& Stm) .Process(this->CurrentEMPulseSW) .Process(this->RandomEMPTarget) .Process(this->EMPulseBurstIndex) - .Process(this->EMPulseBurst) ; } diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 97425b4a8c..d6cb4dc78d 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -40,7 +40,6 @@ class BuildingExt SuperClass* CurrentEMPulseSW; CellStruct RandomEMPTarget; int EMPulseBurstIndex; - int EMPulseBurst; ExtData(BuildingClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } @@ -57,7 +56,6 @@ class BuildingExt , CurrentEMPulseSW {} , RandomEMPTarget { CellStruct::Empty } , EMPulseBurstIndex { 0 } - , EMPulseBurst { 0 } { } void DisplayIncomeString(); diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 432caec599..120a816992 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -168,7 +168,7 @@ DEFINE_HOOK(0x44CEEC, BuildingClass_Mission_Missile_EMPulseSelectWeapon, 0x6) } // Only clean up on the last burst shot - if (pExt->EMPulseBurstIndex + 1 >= pExt->EMPulseBurst) + if (pExt->EMPulseBurstIndex + 1 >= pSWExt->EMPulse_Burst) { if (pSWExt->EMPulse_SuspendOthers) { diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 8fab412a13..8a219d3a68 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -1081,11 +1081,12 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu // Obtain the weapon used by the EMP weapon int weaponIndex = 0; + int totalBurst = 1; if (pExt->CurrentEMPulseSW) { const auto pSWExt = SWTypeExt::ExtMap.Find(pExt->CurrentEMPulseSW->Type); - pExt->EMPulseBurst = pSWExt->EMPulse_Burst; + totalBurst = pSWExt->EMPulse_Burst; if (pSWExt->EMPulse_WeaponIndex >= 0) { @@ -1135,14 +1136,13 @@ DEFINE_HOOK(0x44CD18, BuildingClass_MissionMissile_EMPulseCannon_InaccurateRadiu // Increment burst index and restart if there are burst shots remaining pExt->EMPulseBurstIndex++; - if (pExt->EMPulseBurstIndex < pExt->EMPulseBurst) + if (pExt->EMPulseBurstIndex < totalBurst) { pThis->MissionStatus = 0; } else { pExt->EMPulseBurstIndex = 0; - pExt->EMPulseBurst = 0; } return 0; From 3a45eb9d91edba3e799aaf675e79ff59a430c9bb Mon Sep 17 00:00:00 2001 From: FS-21 Date: Thu, 20 Aug 2026 09:18:01 +0200 Subject: [PATCH 6/6] updated docs --- docs/New-or-Enhanced-Logics.md | 8 +++++--- docs/Whats-New.md | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 339bee1ef2..9666c1c4d6 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1129,17 +1129,19 @@ 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. +- It is now possible to specify how many shots are fired in a volley/burst when launching a `Type=EMPulse` superweapon by setting `EMPulse.Burst` on the superweapon. + - If the firing structure loses power, is deactivated, EMP'd, or disabled during a burst sequence, firing will pause and automatically resume once power or functionality is restored. +- `EMPulseCannon.InaccurateRadius` defines a circular 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 +EMPulse.Burst=1 ; integer, number of shots fired in the burst [SOMEPROJECTILE] ; Projectile -EMPulseCannon.InaccurateRadius=0 ; integer +EMPulseCannon.InaccurateRadius=0 ; integer (distance in cells) ``` ```{note} diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a07dba242b..556df44391 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -468,7 +468,7 @@ New: - [CellSpread damage check if victim is in air or on floor](New-or-Enhanced-Logics.md#cellspread-enhancement) (by TaranDahl) - OpenTopped range bonus and damage multiplier customization for passengers (by Ollerus) - AutoDeath upon ownership change (by Ollerus) -- `EMPulseCannon.InaccurateRadius` for projectiles in `EMPulseCannon=yes` superweapons and `Ammo` support (by FS-21) +- [`EMPulseCannon.InaccurateRadius` and `EMPulse.Burst` for `Type=EMPulse` superweapons](New-or-Enhanced-Logics.md#empulse-settings) (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)