Skip to content
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 @@ -704,6 +704,7 @@ This page lists all the individual contributions to the project by their author.
- Global default value for `DefaultToGuardArea`
- Weapon range finding in cylinder
- Allow jumpjet climbing ignore building height
- Extra threat
- **solar-III (凤九歌)**
- Target scanning delay customization (documentation)
- Skip target scanning function calling for unarmed technos (documentation)
Expand Down
22 changes: 11 additions & 11 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,17 @@ AllowAirstrike= ; boolean
AirstrikeTargets=buildings ; List of Affected Target Enumeration (none|infantry|units|buildings|all)
```

### Allow techno type considered as other type when recruiting techno for teams

- It is now possible to make techno type considered as other type when recruiting techno for teams, both for AI team recruitment and `Create Team` action.
- Only affect techno that's presented on the map. Cannot make AI produce this type of techno if it doesn't have any.

In `rulesmd.ini`:
```ini
[SOMETECHNO] ; TechnoType
TeamMember.ConsideredAs= ; List of TechnoTypes
```

### Alternate FLH customizations

- `AlternateFLH.OnTurret` can be used to customize whether or not `AlternateFLH` used for `OpenTopped` transport firing coordinates, multiple mind control link offsets etc. is calculated relative to the unit's turret if available or body.
Expand Down Expand Up @@ -1896,17 +1907,6 @@ HeightShadowScaling.MinScale=0.0 ; floating point value
ShadowSizeCharacteristicHeight= ; integer, height in leptons
```

### Allow techno type considered as other type when recruiting techno for teams

- It is now possible to make techno type considered as other type when recruiting techno for teams, both for AI team recruitment and `Create Team` action.
- Only affect techno that's presented on the map. Cannot make AI produce this type of techno if it doesn't have any.

In `rulesmd.ini`:
```ini
[SOMETECHNO] ; TechnoType
TeamMember.ConsideredAs= ; List of TechnoTypes
```

## Terrains

### Animated TerrainTypes
Expand Down
34 changes: 34 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,40 @@ RateDown.Cover.Value=0 ; integer
RateDown.Cover.AmmoBelow=-2 ; integer
```

### Extra threat

- Now you can adjust the techno's evaluation of the threat posed by the target in more ways. This will help the techno in auto - targeting.
- When the target poses a threat to the techno, it will receive an additional threat value defined by `ExtraThreat.IsThreat`.
- Generally speaking, "Posing a threat" means the target can fire at the techno.
- Using `AlwaysConsideredThreat` makes the target always considered to be a threat.
- When the target is within the techno's range, it will receive an additional threat value defined by `ExtraThreat.InRange`.
- When the target is within the techno's range, it will receive an additional threat value equal to `ExtraThreatCoefficient.InRangeDistance` multiplied by the distance (in cells) from the target to the techno.
- Only considering in-range is because the vanilla flag `TargetDistanceCoefficientDefault` only considers outside-range. This flag is complementary to that.
- The target will receive an additional threat value equal to `ExtraThreatCoefficient.Facing` multiplied by the difference in facing from the techno's current firing-facing to the target's facing.
- "Firing-facing" refers to the facing the techno uses to check if it "is already facing the target and can fire". Infantry doesn't check the facing when firing, so this is also invalid for infantry.
- The unit of facing is the in-game internal numerical scale. A full circle corresponds to 65536.
- The target will receive an additional threat value equal to `ExtraThreatCoefficient.DistanceToLastTarget` multiplied by the distance (in cells) from the target to the techno's last target.
- Each techno will record its current target as the "last target" per frame. This record will be retained for at most 15 frames after the target becomes invalid.
- If the techno doesn't have a "last target", then this will not take effect.

In `rulesmd.ini`:
```ini
[General]
ExtraThreat.IsThreat=0.0 ; double
ExtraThreat.InRange=0.0 ; double
ExtraThreatCoefficient.InRangeDistance=0.0 ; double
ExtraThreatCoefficient.Facing=0.0 ; double
ExtraThreatCoefficient.DistanceToLastTarget=0.0 ; double

[SOMETECHNO] ; TechnoType
AlwaysConsideredThreat=false
ExtraThreat.IsThreat= ; double, default to the flag in [General] with same name
ExtraThreat.InRange= ; double, default to the flag in [General] with same name
ExtraThreatCoefficient.InRangeDistance= ; double, default to the flag in [General] with same name
ExtraThreatCoefficient.Facing= ; double, default to the flag in [General] with same name
ExtraThreatCoefficient.DistanceToLastTarget= ; double, default to the flag in [General] with same name
```

### Firing offsets for specific Burst shots

- You can now specify separate firing offsets for each of the shots fired by weapon with `Burst` via using `(Elite)(Prone/Deployed)PrimaryFire|SecondaryFire|WeaponX|FLH.BurstN` keys, depending on which weapons your TechnoType makes use of. *N* in `BurstN` is zero-based burst shot index, and the values are parsed sequentially until no value for either regular or elite weapon is present, with elite weapon defaulting to regular weapon FLH if only it is missing. If no burst-index specific value is available, value from the base key (f.ex `PrimaryFireFLH`) is used.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ Vanilla fixes:
- Fixed the bug in AI scripts 56 and 57 that forced the launch of superweapons with index numbers 3 and 4 (by FlyStar)
- Buildings with `NeedsEngineer=true` are now considered to have threat value of 0 under ownership of `MultiplayPassive=true` houses regardless of their `ThreatPosed` value (by Starkku)
- Vehicles overlapping `Wall=true` OverlayTypes no longer display sell cursor and cannot be sold (by CnCRAZER & Starkku)
- Extra threat (by TaranDahl)

Phobos fixes:
- Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi)
Expand Down
11 changes: 11 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,12 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)

this->UnitsUnsellable.Read(exINI, GameStrings::General, "UnitsUnsellable");

this->ExtraThreat_IsThreat.Read(exINI, GameStrings::General, "ExtraThreat.IsThreat");
this->ExtraThreat_InRange.Read(exINI, GameStrings::General, "ExtraThreat.InRange");
this->ExtraThreatCoefficient_InRangeDistance.Read(exINI, GameStrings::General, "ExtraThreatCoefficient.InRangeDistance");
this->ExtraThreatCoefficient_Facing.Read(exINI, GameStrings::General, "ExtraThreatCoefficient.Facing");
this->ExtraThreatCoefficient_DistanceToLastTarget.Read(exINI, GameStrings::General, "ExtraThreatCoefficient.DistanceToLastTarget");

// Section AITargetTypes
int itemsCount = pINI->GetKeyCount("AITargetTypes");
for (int i = 0; i < itemsCount; ++i)
Expand Down Expand Up @@ -673,6 +679,11 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->CylinderRangefinding)
.Process(this->PenetratesTransport_Level)
.Process(this->UnitsUnsellable)
.Process(this->ExtraThreat_IsThreat)
.Process(this->ExtraThreat_InRange)
.Process(this->ExtraThreatCoefficient_InRangeDistance)
.Process(this->ExtraThreatCoefficient_Facing)
.Process(this->ExtraThreatCoefficient_DistanceToLastTarget)
;
}

Expand Down
12 changes: 12 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ class RulesExt

Valueable<bool> UnitsUnsellable;

Valueable<double> ExtraThreat_IsThreat;
Valueable<double> ExtraThreat_InRange;
Valueable<double> ExtraThreatCoefficient_InRangeDistance;
Valueable<double> ExtraThreatCoefficient_Facing;
Valueable<double> ExtraThreatCoefficient_DistanceToLastTarget;

ExtData(RulesClass* OwnerObject) : Extension<RulesClass>(OwnerObject)
, Storage_TiberiumIndex { -1 }
, HarvesterDumpAmount { 0.0f }
Expand Down Expand Up @@ -583,6 +589,12 @@ class RulesExt
, PenetratesTransport_Level { 10 }

, UnitsUnsellable { false }

, ExtraThreat_IsThreat { 0.0 }
, ExtraThreat_InRange { 0.0 }
, ExtraThreatCoefficient_InRangeDistance { 0.0 }
, ExtraThreatCoefficient_Facing { 0.0 }
, ExtraThreatCoefficient_DistanceToLastTarget { 0.0 }
{ }

virtual ~ExtData() = default;
Expand Down
28 changes: 28 additions & 0 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ void TechnoExt::ExtData::OnEarlyUpdate()

if (this->AttackMoveFollowerTempCount)
this->AttackMoveFollowerTempCount--;

this->UpdateLastTargetCrd();
}

void TechnoExt::ExtData::ApplyInterceptor()
Expand Down Expand Up @@ -2169,3 +2171,29 @@ void TechnoExt::ExtData::UpdateTintValues()
calculateTint(Drawing::RGB_To_Int(pShieldType->Tint_Color), static_cast<int>(pShieldType->Tint_Intensity * 1000), pShieldType->Tint_VisibleToHouses);
}
}

void TechnoExt::ExtData::UpdateLastTargetCrd()
{
if (!this->TypeExtData->ExtraThreat_Enabled)
return;

auto const pThis = this->OwnerObject();
auto pTimer = &this->LastTargetCrdClearTimer;

if (pThis->Target)
{
this->LastTargetCrd = pThis->Target->GetCoords();
pTimer->Stop();
}
else
{
if (!pTimer->IsTicking())
pTimer->Start(15);

if (pTimer->Completed())
{
this->LastTargetCrd = CoordStruct::Empty;
pTimer->Stop();
}
}
}
2 changes: 2 additions & 0 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,8 @@ void TechnoExt::ExtData::Serialize(T& Stm)
.Process(this->SpecialTracked)
.Process(this->FallingDownTracked)
.Process(this->JumpjetStraightAscend)
.Process(this->LastTargetCrd)
.Process(this->LastTargetCrdClearTimer)
;
}

Expand Down
6 changes: 6 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ class TechnoExt

bool JumpjetStraightAscend; // Is set to true jumpjet units will ascend straight and do not adjust rotation or position during it.

CoordStruct LastTargetCrd;
CDTimerClass LastTargetCrdClearTimer;

ExtData(TechnoClass* OwnerObject) : Extension<TechnoClass>(OwnerObject)
, TypeExtData { nullptr }
, Shield {}
Expand Down Expand Up @@ -166,6 +169,8 @@ class TechnoExt
, SpecialTracked { false }
, FallingDownTracked { false }
, JumpjetStraightAscend { false }
, LastTargetCrd { CoordStruct::Empty }
, LastTargetCrdClearTimer {}
{ }

void OnEarlyUpdate();
Expand Down Expand Up @@ -206,6 +211,7 @@ class TechnoExt
void UpdateTintValues();

void AmmoAutoConvertActions();
void UpdateLastTargetCrd();

virtual ~ExtData() override;
virtual void InvalidatePointer(void* ptr, bool bRemoved) override;
Expand Down
133 changes: 133 additions & 0 deletions src/Ext/Techno/Hooks.TargetEvaluation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,137 @@ static Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis,
}
DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0CC, InfantryClass__WhatAction_Wrapper)

#pragma endregion

#pragma region ThreatEvaluation

// Current target may hurt me.
static inline bool IsAThreatToMe(TechnoClass* const pTechno, AbstractClass* const pTarget, int weaponIndex = -1)
{
if (const auto pTechnoTarget = abstract_cast<TechnoClass*>(pTarget))
{
auto pTypeExt = TechnoExt::ExtMap.Find(pTechnoTarget)->TypeExtData;

if (pTypeExt->AlwaysConsideredThreat)
return true;

if (weaponIndex < 0)
weaponIndex = pTechnoTarget->SelectWeapon(pTechno);

if (!pTechnoTarget->GetWeapon(weaponIndex)->WeaponType)
return false;

const auto error = pTechnoTarget->GetFireError(pTechno, weaponIndex, true);
return pTechnoTarget->WhatAmI() == AbstractType::Building ? (error != FireError::ILLEGAL) && (error != FireError::RANGE) : (error != FireError::ILLEGAL);
}

return false;
}

// Decide the facing to check for firing.
static inline FacingClass* GetFireFacing(TechnoClass* const pTechno)
{
if (!pTechno)
return nullptr;

switch (pTechno->WhatAmI())
{
case AbstractType::Building:
return &pTechno->PrimaryFacing;
case AbstractType::Unit:
{
if (pTechno->GetTechnoType()->Turret)
return &pTechno->SecondaryFacing;
else
return &pTechno->PrimaryFacing;
}
case AbstractType::Infantry:
return nullptr;
case AbstractType::Aircraft:
return &pTechno->SecondaryFacing;
default:
return nullptr;
}
}

DEFINE_HOOK(0x70CF87, TechnoClass_ThreatCoefficient_CanAttackMeThreatBonus, 0x9)
{
GET(TechnoClass* const, pThis, EDI);
GET(TechnoClass* const, pTarget, ESI);
REF_STACK(double, totalThreat, STACK_OFFSET(0x58, -0x48));

auto pExt = TechnoExt::ExtMap.Find(pThis);
auto pTypeExt = pExt->TypeExtData;

if (!pTypeExt->ExtraThreat_Enabled)
return 0;

auto ApplyIsThreatBonus = [pTypeExt, pThis, pTarget, &totalThreat]()
{
double bonus = pTypeExt->ExtraThreat_IsThreat.Get(RulesExt::Global()->ExtraThreat_IsThreat);

if (bonus == 0.0)
return;

if (!IsAThreatToMe(pThis, pTarget))
return;

totalThreat += bonus;
};
ApplyIsThreatBonus();

auto ApplyInRangeBonus = [pTypeExt, pThis, pTarget, &totalThreat]()
{
double bonus1 = pTypeExt->ExtraThreat_InRange.Get(RulesExt::Global()->ExtraThreat_InRange);
double dist = pThis->DistanceFrom(pTarget) / 256.0;
double bonus2 = dist * pTypeExt->ExtraThreatCoefficient_InRangeDistance.Get(RulesExt::Global()->ExtraThreatCoefficient_InRangeDistance);
double bonus = bonus1 + bonus2;

if (bonus == 0.0)
return;

if (!pThis->IsCloseEnoughToAttack(pTarget))
return;

totalThreat += bonus;
};
ApplyInRangeBonus();

auto ApplyFacingBonus = [pTypeExt, pThis, pTarget, &totalThreat]()
{
auto pFacing = GetFireFacing(pThis);

if (!pFacing)
return;

double bonus = pTypeExt->ExtraThreatCoefficient_Facing.Get(RulesExt::Global()->ExtraThreatCoefficient_Facing);

if (bonus == 0.0)
return;

DirStruct dir = DirStruct();
int deltaFacing = std::abs(pThis->GetTargetDirection(&dir, pTarget)->Raw - pFacing->Current().Raw);
totalThreat += deltaFacing * bonus;
};
ApplyFacingBonus();

auto ApplyLastTargetDistanceBonus = [pExt, pTypeExt, pThis, pTarget, &totalThreat]()
{
double bonus = pTypeExt->ExtraThreatCoefficient_DistanceToLastTarget.Get(RulesExt::Global()->ExtraThreatCoefficient_DistanceToLastTarget);

if (bonus == 0.0)
return;

if (pExt->LastTargetCrd == CoordStruct::Empty)
return;

double distToLastTarget = pTarget->GetCoords().DistanceFrom(pExt->LastTargetCrd) / 256.0;
totalThreat += distToLastTarget * bonus;
};
ApplyLastTargetDistanceBonus();

return 0;
}


#pragma endregion
20 changes: 20 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,18 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)

this->JumpjetClimbIgnoreBuilding.Read(exINI, pSection, "JumpjetClimbIgnoreBuilding");

this->ExtraThreat_IsThreat.Read(exINI, pSection, "ExtraThreat.IsThreat");
this->AlwaysConsideredThreat.Read(exINI, pSection, "AlwaysConsideredThreat");
this->ExtraThreat_InRange.Read(exINI, pSection, "ExtraThreat.InRange");
this->ExtraThreatCoefficient_InRangeDistance.Read(exINI, pSection, "ExtraThreatCoefficient.InRangeDistance");
this->ExtraThreatCoefficient_Facing.Read(exINI, pSection, "ExtraThreatCoefficient.Facing");
this->ExtraThreatCoefficient_DistanceToLastTarget.Read(exINI, pSection, "ExtraThreatCoefficient.DistanceToLastTarget");
this->ExtraThreat_Enabled = ExtraThreat_IsThreat.Get(RulesExt::Global()->ExtraThreat_IsThreat) != 0
|| !ExtraThreat_InRange.Get(RulesExt::Global()->ExtraThreat_InRange) != 0
|| ExtraThreatCoefficient_InRangeDistance.Get(RulesExt::Global()->ExtraThreatCoefficient_InRangeDistance) != 0
|| ExtraThreatCoefficient_Facing.Get(RulesExt::Global()->ExtraThreatCoefficient_Facing) != 0
|| ExtraThreatCoefficient_DistanceToLastTarget.Get(RulesExt::Global()->ExtraThreatCoefficient_DistanceToLastTarget) != 0;

// Ares 0.2
this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius");

Expand Down Expand Up @@ -1876,6 +1888,14 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->JumpjetClimbIgnoreBuilding)

.Process(this->Unsellable)

.Process(this->ExtraThreat_Enabled)
.Process(this->ExtraThreat_IsThreat)
.Process(this->AlwaysConsideredThreat)
.Process(this->ExtraThreat_InRange)
.Process(this->ExtraThreatCoefficient_InRangeDistance)
.Process(this->ExtraThreatCoefficient_Facing)
.Process(this->ExtraThreatCoefficient_DistanceToLastTarget)
;
}
void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
Expand Down
Loading