Skip to content

Commit

Permalink
v2.0 and tag rename
Browse files Browse the repository at this point in the history
Now isn't required to have Burst > 1 in the weapon.
  • Loading branch information
FS-21 committed Sep 4, 2023
1 parent 9d7b854 commit ba2e450
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 72 deletions.
15 changes: 9 additions & 6 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1117,17 +1117,20 @@ Burst.Delays=-1 ; integer - burst delays (comma-separated) for s
Burst.FireWithinSequence=false ; boolean
```

### Burst projectile retargeting
- Weapons with `Burst=2` or higher enables the probability to pick a new target for the new projectile.
- A techno as target is required.
- It was designed for standard missiles (no splits, airbusts, etc).
### Projectile's random target
- The firer will pick targets randomly.
- Works with missiles (no splits, airbusts, etc), cannons, lasers & spawners.
- A valid techno is required for trigger the logic.
- `OmniFire=yes` will make selectable any targets around the firer, limited by the weapon range.
- `OmniFire=no` will force the firer to pick targets in an area composed by the firer's weapon range around the original target intersected with the firer's weapon range around the firer.
- `RandomTarget.Spawners.MultipleTargets=true` gives each spawner it's own target.
- This logic should be used only in one weapon of the object.

In `rulesmd.ini`:
```ini
[SOMEWEAPON] ; WeaponType
Burst.Retarget=0.0 ; double or percentage
[SOMEWEAPON] ; WeaponType
RandomTarget=0.0 ; double or percentage
RandomTarget.Spawners.MultipleTargets=false ; boolean
```

### Feedback weapon
Expand Down
195 changes: 195 additions & 0 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <ScenarioClass.h>

#include <Ext/House/Body.h>
#include <Ext/WeaponType/Body.h>
#include <Ext/Script/Body.h>

template<> const DWORD Extension<TechnoClass>::Canary = 0x55555555;
TechnoExt::ExtContainer TechnoExt::ExtMap;
Expand Down Expand Up @@ -200,6 +202,197 @@ bool TechnoExt::AllowedTargetByZone(TechnoClass* pThis, TechnoClass* pTarget, Ta
return true;
}

bool TechnoExt::UpdateRandomTarget(TechnoClass* pThis)
{
if (!pThis)
return false;

int weaponIndex = pThis->SelectWeapon(pThis->Target);
auto pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType;
if (!pWeapon)
return false;

const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon);
if (!pWeaponExt || pWeaponExt->RandomTarget <= 0.0)
return false;

const auto pExt = TechnoExt::ExtMap.Find(pThis);
if (!pExt)
return false;

if (pExt->CurrentRandomTarget && ScriptExt::IsUnitAvailable(pExt->CurrentRandomTarget, false) && pThis->SpawnManager)
return false;

if (!pThis->Target && !ScriptExt::IsUnitAvailable(abstract_cast<TechnoClass*>(pExt->OriginalTarget), false))
{
pExt->OriginalTarget = nullptr;
return false;
}

if (pThis->GetCurrentMission() != Mission::Attack)
{
pExt->OriginalTarget = nullptr;
return false;
}

if (!pThis->Target)
return false;

if (pThis->DistanceFrom(pExt->OriginalTarget) > pWeapon->Range)
pThis->SetTarget(pExt->OriginalTarget);

if (pThis->DistanceFrom(pThis->Target) > pWeapon->Range)
{
pThis->SetTarget(pExt->OriginalTarget);
return false;
}

auto pRandomTarget = GetRandomTarget(pThis);

if (!pRandomTarget)
return false;

pExt->OriginalTarget = !pExt->OriginalTarget ? pThis->Target : pExt->OriginalTarget;
pExt->CurrentRandomTarget = pRandomTarget;
pThis->Target = pRandomTarget;

if (pThis->SpawnManager)
{
bool isFirstSpawn = true;

for (auto pSpawn : pThis->SpawnManager->SpawnedNodes)
{
if (!pSpawn->Unit)
continue;

TechnoClass* pSpawnTarget = nullptr;

auto pSpawnExt = TechnoExt::ExtMap.Find(pSpawn->Unit);
if (!pSpawnExt)
continue;

if (isFirstSpawn)
{
pSpawnTarget = pExt->CurrentRandomTarget;

if (pWeaponExt->RandomTarget_Spawners_MultipleTargets)
isFirstSpawn = false;
}
else
{
pSpawnTarget = GetRandomTarget(pThis);

if (!pSpawnTarget)
pSpawnTarget = abstract_cast<TechnoClass*>(pExt->OriginalTarget);
}

pSpawnExt->CurrentRandomTarget = pSpawnTarget;
pSpawnExt->OriginalTarget = pExt->OriginalTarget;
}
}

return true;
}

TechnoClass* TechnoExt::GetRandomTarget(TechnoClass* pThis)
{
TechnoClass* selection = nullptr;

if (!pThis && !pThis->Target)
return selection;

int weaponIndex = pThis->SelectWeapon(pThis->Target);
auto pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType;
if (!pWeapon)
return selection;

const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon);
if (!pWeaponExt || pWeaponExt->RandomTarget <= 0.0)
return selection;

const auto pExt = TechnoExt::ExtMap.Find(pThis);
if (!pExt)
return selection;

int retargetProbability = std::min((int)round(pWeaponExt->RandomTarget * 100), 100);
int dice = ScenarioClass::Instance->Random.RandomRanged(1, 100);

if (retargetProbability < dice)
return selection;

auto pThisType = pThis->GetTechnoType();
int minimumRange = pWeapon->MinimumRange;
int range = pWeapon->Range;
int airRange = pWeapon->Range + pThisType->AirRangeBonus;
bool omniFire = pWeapon->OmniFire;
std::vector<TechnoClass*> candidates;
auto originalTarget = abstract_cast<TechnoClass*>(!pExt->OriginalTarget ? pThis->Target : pExt->OriginalTarget);
bool friendlyFire = pThis->Owner->IsAlliedWith(originalTarget);

// Looking for all valid targeting candidates
for (auto pTarget : *TechnoClass::Array)
{
if (pTarget == pThis
|| !ScriptExt::IsUnitAvailable(pTarget, true)
|| pThisType->Immune
|| !EnumFunctions::IsTechnoEligible(pTarget, pWeaponExt->CanTarget, true)
|| (!pWeapon->Projectile->AA && pTarget->IsInAir())
|| (!pWeapon->Projectile->AG && !pTarget->IsInAir())
|| (!friendlyFire && (pThis->Owner->IsAlliedWith(pTarget) || ScriptExt::IsUnitMindControlledFriendly(pThis->Owner, pTarget)))
|| pTarget->TemporalTargetingMe
|| pTarget->BeingWarpedOut
|| (pTarget->GetTechnoType()->Underwater && pTarget->GetTechnoType()->NavalTargeting == NavalTargetingType::Underwater_Never)
|| (pTarget->GetTechnoType()->Naval && pTarget->GetTechnoType()->NavalTargeting == NavalTargetingType::Naval_None)
|| (pTarget->CloakState == CloakState::Cloaked && !pThisType->Naval)
|| (pTarget->InWhichLayer() == Layer::Underground))
{
continue;
}

int distanceFromAttacker = pThis->DistanceFrom(pTarget);
if (distanceFromAttacker < minimumRange)
continue;

if (omniFire)
{
if (pTarget->IsInAir())
{
if (distanceFromAttacker <= airRange)
candidates.push_back(pTarget);
}
else
{
if (distanceFromAttacker <= range)
candidates.push_back(pTarget);
}
}
else
{
int distanceFromOriginalTarget = pTarget->DistanceFrom(originalTarget);

if (pTarget->IsInAir())
{
if (distanceFromAttacker <= airRange && distanceFromOriginalTarget <= airRange)
candidates.push_back(pTarget);
}
else
{
if (distanceFromAttacker <= range && distanceFromOriginalTarget <= range)
candidates.push_back(pTarget);
}
}
}

if (candidates.size() == 0)
return selection;

// Pick one new target from the list of targets inside the weapon range
dice = ScenarioClass::Instance->Random.RandomRanged(0, candidates.size() - 1);
selection = candidates.at(dice);

return selection;
}

// =============================
// load / save

Expand All @@ -221,6 +414,8 @@ void TechnoExt::ExtData::Serialize(T& Stm)
.Process(this->IsInTunnel)
.Process(this->DeployFireTimer)
.Process(this->ForceFullRearmDelay)
.Process(this->OriginalTarget)
.Process(this->CurrentRandomTarget)
;
}

Expand Down
7 changes: 7 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <Utilities/Container.h>
#include <Utilities/TemplateDef.h>
#include <Utilities/Macro.h>
#include <Utilities/EnumFunctions.h>
#include <New/Entity/ShieldClass.h>
#include <New/Entity/LaserTrailClass.h>

Expand Down Expand Up @@ -34,6 +35,8 @@ class TechnoExt
bool IsInTunnel;
CDTimerClass DeployFireTimer;
bool ForceFullRearmDelay;
AbstractClass* OriginalTarget;
TechnoClass* CurrentRandomTarget;

// Used for Passengers.SyncOwner.RevertOnExit instead of TechnoClass::InitialOwner / OriginallyOwnedByHouse,
// as neither is guaranteed to point to the house the TechnoClass had prior to entering transport and cannot be safely overridden.
Expand All @@ -56,6 +59,8 @@ class TechnoExt
, IsInTunnel { false }
, DeployFireTimer {}
, ForceFullRearmDelay { false }
, OriginalTarget { nullptr }
, CurrentRandomTarget { nullptr }
{ }

void ApplyInterceptor();
Expand Down Expand Up @@ -120,6 +125,8 @@ class TechnoExt
static CoordStruct PassengerKickOutLocation(TechnoClass* pThis, FootClass* pPassenger, int maxAttempts);
static bool AllowedTargetByZone(TechnoClass* pThis, TechnoClass* pTarget, TargetZoneScanType zoneScanType, WeaponTypeClass* pWeapon = nullptr, bool useZone = false, int zone = -1);
static void UpdateAttachedAnimLayers(TechnoClass* pThis);
static bool UpdateRandomTarget(TechnoClass* pThis = nullptr);
static TechnoClass* GetRandomTarget(TechnoClass* pThis = nullptr);

// WeaponHelpers.cpp
static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true);
Expand Down
Loading

0 comments on commit ba2e450

Please sign in to comment.