Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forcing specific weapon by range #1407

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ This page lists all the individual contributions to the project by their author.
- **Ollerus**
- Build limit group enhancement
- Customizable rocker amplitude
- Forcing specific weapon by range
- **handama** - AI script action to jump back to previous script
- **TaranDahl (航味麻酱)**
- Skirmish AI "sell all buildings and set all technos to hunt" behavior dehardcode
Expand Down
28 changes: 17 additions & 11 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ ReflectDamage.Multiplier=1.0 ; floating point value, perce
ReflectDamage.AffectsHouses=all ; list of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
DisableWeapons=false ; boolean
Groups= ; comma-separated list of strings (group IDs)

[SOMETECHNO] ; TechnoType
AttachEffect.AttachTypes= ; List of AttachEffectTypes
AttachEffect.DurationOverrides= ; integer - duration overrides (comma-separated) for AttachTypes in order from first to last.
Expand All @@ -134,7 +134,7 @@ AttachEffect.InitialDelays= ; integer - initial delays (c
AttachEffect.RecreationDelays= ; integer - recreation delays (comma-separated) for AttachTypes in order from first to last.
OpenTopped.UseTransportRangeModifiers=false ; boolean
OpenTopped.CheckTransportDisableWeapons=false ; boolean

[SOMEWEAPON] ; WeaponType
AttachEffect.RequiredTypes= ; List of AttachEffectTypes
AttachEffect.DisallowedTypes= ; List of AttachEffectTypes
Expand All @@ -146,8 +146,8 @@ AttachEffect.DisallowedMinCounts= ; integer - minimum disallowe
AttachEffect.DisallowedMaxCounts= ; integer - maximum disallowed instance count (comma-separated) for cumulative types in order from first to last.
AttachEffect.IgnoreFromSameSource=false ; boolean
AttachEffect.CheckOnFirer=false ; boolean
[SOMEWARHEAD]

[SOMEWARHEAD]
AttachEffect.AttachTypes= ; List of AttachEffectTypes
AttachEffect.CumulativeRefreshAll=false ; boolean
AttachEffect.CumulativeRefreshAll.OnAttach=false ; boolean
Expand Down Expand Up @@ -511,7 +511,7 @@ DetachedReport= ; sound entry
- `Adjacent.Allowed` lists BuildingTypes this BuildingType can be placed off (within distance defined by `Adjacent`). If empty, any BuildingType not listed in `Adjacent.Disallowed` is okay.
- `Adjacent.Disallowed` lists BuildingTypes this BuildingType cannot be placed next to. If empty, any BuildingTypes are okay as long as `Adjacent.Allowed` is empty or they are listed on it.
- If `NoBuildAreaOnBuildup` is set to true, no building can be built next to this building regardless of any other settings if it is currently displaying its buildup animation.

In `rulesmd.ini`:
```ini
[SOMEBUILDING] ; BuildingType
Expand Down Expand Up @@ -544,7 +544,7 @@ PowersUp.Buildings= ; list of BuildingTypes

- It is possible to make buildings be considered pathfinding obstacles that can be destroyed by setting `IsDestroyableBlockage` to true. What this does is make the building be considered impassable and impenetrable pathfinding obstacle to every unit that is not flying or have appropriate `MovementZone` (ones that allow destroyable obstacles to be overcome, e.g `(Infantry|Amphibious)Destroyer`) akin to wall overlays and TerrainTypes.
- Keep in mind that if an unit has appropriate `MovementZone` but no means to actually destroy an obstacle (such as a weapon that can fire and deal damage at them), they will get stuck trying to go through them instead of pathing around.

In `rulesmd.ini`:
```ini
[SOMEBUILDING] ; BuildingType
Expand Down Expand Up @@ -1133,13 +1133,19 @@ FLHKEY.BurstN= ; integer - Forward,Lateral,Height. FLHKey refers to weapon-spec
- `ForceWeapon.Naval.Decloaked` forces specified weapon to be used against uncloaked naval targets. Useful if your naval unit has one weapon only for underwater and another weapon for surface targets.
- `ForceWeapon.Cloaked` forces specified weapon to be used against any cloaked targets.
- `ForceWeapon.Disguised` forces specified weapon to be used against any disguised targets.
- `ForceWeapon.InRange` forces specified a list of weapons to be used once the target is within their `Range`. The first weapon in the listed order satisfied will be selected.
- `ForceWeapon.InRange.Overrides` overrides the range when decides which weapon to use. Value from position matching the position from `ForceWeapon.InRange` is used if found, or the weapon's own `Range` if not found or set to a value below 0. Specifically, if a position has `ForceWeapon.InRange` set to -1 and `ForceWeapon.InRange.Overrides` set to a positive value, it'll use default weapon selection logic once satisfied.
- If `ForceWeapon.InRange.ApplyRangeModifiers` is set to true, any applicable weapon range modifiers from the firer are applied to the decision range.

In `rulesmd.ini`:
```ini
[SOMETECHNO] ; TechnoType
ForceWeapon.Naval.Decloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
ForceWeapon.Cloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
ForceWeapon.Disguised=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
[SOMETECHNO] ; TechnoType
ForceWeapon.Naval.Decloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
ForceWeapon.Cloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
ForceWeapon.Disguised=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
ForceWeapon.InRange= ; list of integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable
ForceWeapon.InRange.Overrides= ; list of floating point value
ForceWeapon.InRange.ApplyRangeModifiers=false ; boolean
```

### Make units try turning to target when firing with `OmniFire=yes`
Expand Down Expand Up @@ -1700,7 +1706,7 @@ FeedbackWeapon= ; WeaponType
- `Strafing.Shots` controls the number of times the weapon is fired during a single strafe run, defaults to 5 if not set. `Ammo` is only deducted at the end of the strafe run, regardless of the number of shots fired.
- `Strafing.SimulateBurst` controls whether or not the shots fired during strafing simulate behavior of `Burst`, allowing for alternating firing offset. Only takes effect if weapon has `Burst` set to 1 or undefined.
- `Strafing.UseAmmoPerShot`, if set to `true` overrides the usual behaviour of only deducting ammo after a strafing run and instead doing it after each individual shot.
- There is a special case for aircraft spawned by `Type=SpyPlane` superweapons on `SpyPlane Approach` or `SpyPlane Overfly` mission where `Strafing.Shots` only if explicitly set on its primary weapon, determines the maximum number of times the map revealing effect can activate irregardless of other factors.
- There is a special case for aircraft spawned by `Type=SpyPlane` superweapons on `SpyPlane Approach` or `SpyPlane Overfly` mission where `Strafing.Shots` only if explicitly set on its primary weapon, determines the maximum number of times the map revealing effect can activate irregardless of other factors.

In `rulesmd.ini`:
```ini
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ New:
- Toggle to disallow buildings from providing build area during buildup (by Starkku)
- Allow customizing which building types provide build area for a building (by Starkku)
- `Scorch` / `Flamer` fire animation customization (by Starkku)
- Forcing specific weapon by range (by Ollerus)

Vanilla fixes:
- Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy)
Expand Down
1 change: 1 addition & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,5 @@ class TechnoExt
static WeaponTypeClass* GetCurrentWeapon(TechnoClass* pThis, int& weaponIndex, bool getSecondary = false);
static WeaponTypeClass* GetCurrentWeapon(TechnoClass* pThis, bool getSecondary = false);
static int GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* pWallOverlayType);
static int ApplyForceWeaponInRange(TechnoClass* pThis, std::vector<int> weaponIndices, std::vector<double> rangeOverrides, bool applyRangeModifiers, int currentDistance);
};
24 changes: 17 additions & 7 deletions src/Ext/Techno/Hooks.Firing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,20 @@ DEFINE_HOOK(0x6F3428, TechnoClass_WhatWeaponShouldIUse_ForceWeapon, 0x6)

GET(TechnoClass*, pThis, ECX);

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

int forceWeaponIndex = -1;
auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType());

if (pThis->Target)
{
auto const pTarget = abstract_cast<TechnoClass*>(pThis->Target);

if (!pTarget)
return 0;

int forceWeaponIndex = -1;
auto const pTargetType = pTarget->GetTechnoType();
auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType());

if (pTypeExt->ForceWeapon_Naval_Decloaked >= 0 &&
pTargetType->Cloakable && pTargetType->Naval &&
Expand All @@ -102,14 +106,20 @@ DEFINE_HOOK(0x6F3428, TechnoClass_WhatWeaponShouldIUse_ForceWeapon, 0x6)
{
forceWeaponIndex = pTypeExt->ForceWeapon_Disguised;
}

if (forceWeaponIndex >= 0)
else if (!pTypeExt->ForceWeapon_InRange.empty())
{
R->EAX(forceWeaponIndex);
return UseWeaponIndex;
int currentDistance = pThis->DistanceFrom(pTarget);
forceWeaponIndex = TechnoExt::ApplyForceWeaponInRange(pThis, pTypeExt->ForceWeapon_InRange, pTypeExt->ForceWeapon_InRange_Overrides,
pTypeExt->ForceWeapon_InRange_ApplyRangeModifiers, currentDistance);
}
}

if (forceWeaponIndex >= 0)
{
R->EAX(forceWeaponIndex);
return UseWeaponIndex;
}

return 0;
}

Expand Down
41 changes: 41 additions & 0 deletions src/Ext/Techno/WeaponHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,44 @@ int TechnoExt::GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* p

return weaponIndex;
}

int TechnoExt::ApplyForceWeaponInRange(TechnoClass* pThis, std::vector<int> weaponIndices, std::vector<double> rangeOverrides, bool applyRangeModifiers, int currentDistance)
{
int forceWeaponIndex = -1;

for (size_t i = 0; i < weaponIndices.size(); i++)
{
int distance = 0;

// Value below 0 means Range won't be overriden
if (i < rangeOverrides.size() && rangeOverrides[i] > 0)
distance = static_cast<int>(rangeOverrides[i] * 256.0);

if (weaponIndices[i] >= 0)
{
auto const pWeapon = pThis->GetWeapon(weaponIndices[i])->WeaponType;
distance = distance > 0 ? distance : pWeapon->Range;

if (applyRangeModifiers)
distance = WeaponTypeExt::GetRangeWithModifiers(pWeapon, pThis, distance);

if (currentDistance <= distance)
{
forceWeaponIndex = weaponIndices[i];
break;
}
}
else
{
// Apply range modifiers regardless of weapon
if (applyRangeModifiers)
distance = WeaponTypeExt::GetRangeWithModifiers(nullptr, pThis, distance);

// Don't force weapon if range satisfied
if (currentDistance <= distance)
break;
}
}

return forceWeaponIndex;
}
6 changes: 6 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,9 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->ForceWeapon_Naval_Decloaked.Read(exINI, pSection, "ForceWeapon.Naval.Decloaked");
this->ForceWeapon_Cloaked.Read(exINI, pSection, "ForceWeapon.Cloaked");
this->ForceWeapon_Disguised.Read(exINI, pSection, "ForceWeapon.Disguised");
this->ForceWeapon_InRange.Read(exINI, pSection, "ForceWeapon.InRange");
this->ForceWeapon_InRange_Overrides.Read(exINI, pSection, "ForceWeapon.InRange.Overrides");
this->ForceWeapon_InRange_ApplyRangeModifiers.Read(exINI, pSection, "ForceWeapon.InRange.ApplyRangeModifiers");
this->Ammo_Shared.Read(exINI, pSection, "Ammo.Shared");
this->Ammo_Shared_Group.Read(exINI, pSection, "Ammo.Shared.Group");
this->SelfHealGainType.Read(exINI, pSection, "SelfHealGainType");
Expand Down Expand Up @@ -740,6 +743,9 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->ForceWeapon_Naval_Decloaked)
.Process(this->ForceWeapon_Cloaked)
.Process(this->ForceWeapon_Disguised)
.Process(this->ForceWeapon_InRange)
.Process(this->ForceWeapon_InRange_Overrides)
.Process(this->ForceWeapon_InRange_ApplyRangeModifiers)
.Process(this->Ammo_Shared)
.Process(this->Ammo_Shared_Group)
.Process(this->SelfHealGainType)
Expand Down
6 changes: 6 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ class TechnoTypeExt
Valueable<int> ForceWeapon_Naval_Decloaked;
Valueable<int> ForceWeapon_Cloaked;
Valueable<int> ForceWeapon_Disguised;
ValueableVector<int> ForceWeapon_InRange;
ValueableVector<double> ForceWeapon_InRange_Overrides;
Valueable<bool> ForceWeapon_InRange_ApplyRangeModifiers;

Valueable<bool> Ammo_Shared;
Valueable<int> Ammo_Shared_Group;
Expand Down Expand Up @@ -367,6 +370,9 @@ class TechnoTypeExt
, ForceWeapon_Naval_Decloaked { -1 }
, ForceWeapon_Cloaked { -1 }
, ForceWeapon_Disguised { -1 }
, ForceWeapon_InRange {}
, ForceWeapon_InRange_Overrides {}
, ForceWeapon_InRange_ApplyRangeModifiers { false }

, Ammo_Shared { false }
, Ammo_Shared_Group { -1 }
Expand Down
4 changes: 2 additions & 2 deletions src/Ext/WeaponType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,10 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF
if (type->WeaponRange_Multiplier == 1.0 && type->WeaponRange_ExtraRange == 0.0)
continue;

if (type->WeaponRange_AllowWeapons.size() > 0 && !type->WeaponRange_AllowWeapons.Contains(pThis))
if (type->WeaponRange_AllowWeapons.size() > 0 && pThis && !type->WeaponRange_AllowWeapons.Contains(pThis))
continue;

if (type->WeaponRange_DisallowWeapons.size() > 0 && type->WeaponRange_DisallowWeapons.Contains(pThis))
if (type->WeaponRange_DisallowWeapons.size() > 0 && pThis && type->WeaponRange_DisallowWeapons.Contains(pThis))
continue;

range = static_cast<int>(range * Math::max(type->WeaponRange_Multiplier, 0.0));
Expand Down