Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -619,6 +619,7 @@ This page lists all the individual contributions to the project by their author.
- AutoDeath upon ownership change
- Maximum amount for power plant enhancer
- Return warhead
- Attached effect for transfering damage to invoker
- **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes
- **handama** - AI script action to `16005 Jump Back To Previous Script`
- **TaranDahl (航味麻酱)**:
Expand Down
28 changes: 27 additions & 1 deletion docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,18 @@ This page describes all the engine features that are either new and introduced b
- `RevengeWeapon` can be used to temporarily grant the specified weapon as a [revenge weapon](#revenge-weapon) for the attached object.
- `RevengeWeapon.AffectsHouse` customizes which houses can trigger the revenge weapon.
- `RevengeWeapon.UseInvokerAsOwner` can be used to set the house and TechnoType that created the effect (e.g firer of the weapon that applied it) as the weapon's owner & invoker instead of the object the effect is attached to.
- `ReflectDamage` can be set to true to have any positive damage dealt to the object the effect is attached to be reflected back to the attacker. `ReflectDamage.Warhead` determines which Warhead is used to deal the damage, defaults to `[CombatDamage] -> C4Warhead`. If `ReflectDamage.Warhead.Detonate` is set to true, the Warhead is fully detonated instead of used to simply deal damage. `ReflectDamage.Chance` determines the chance of reflection. `ReflectDamage.Multiplier` is a multiplier to the damage received and then reflected back, while `ReflectDamage.Override` directly overrides the damage. Already reflected damage cannot be further reflected back.
- `ReflectDamage` can be set to true to have any positive damage dealt to the object the effect is attached to be reflected back to the attacker.
- `ReflectDamage.Warhead` determines which Warhead is used to deal the damage, defaults to `[CombatDamage] -> C4Warhead`. If `ReflectDamage.Warhead.Detonate` is set to true, the Warhead is fully detonated instead of used to simply deal damage. If `ReflectDamage.UseOriginalWarhead` is set to true, it'll reuse the Warhead that dealt damage to the object.
- `ReflectDamage.Chance` determines the chance of reflection, while `ReflectDamage.AffectsHouse` customizes which houses can trigger the reflect damage.
- `ReflectDamage.Multiplier` is a multiplier to the damage received and then reflected back, while `ReflectDamage.Override` directly overrides the damage. Already reflected damage cannot be further reflected back.
- Warheads can prevent reflect damage from occuring by setting `SuppressReflectDamage` to true. `SuppressReflectDamage.Types` can control which AttachEffectTypes' reflect damage is suppressed, if none are listed then all of them are suppressed. `SuppressReflectDamage.Groups` does the same thing but for all AttachEffectTypes in the listed groups.
- `ReflectDamage.UseInvokerAsOwner` can be used to set the house and TechnoType that created the effect (e.g firer of the weapon that applied it) as the reflected damage's owner & invoker instead of the object the effect is attached to.
- `TransferDamage` can be set to true to have any damage dealt to the object the effect is attached to be transfered to the invoker of the effect if it's alive.
- `TransferDamage.SelfMultiplier` and `TransferDamage.InvokerMultiplier` are multuipliers of damage that'll be applied to the attached object and the invoker if a transfer happens. If an object has multiple effects with `TransferDamage`, the damage will be equally divided and calculated for each effect independently. `TransferDamage.Minimum` and `TransferDamage.Maximum` further restrict the bound of damage transfering to the invoker.
- `TransferDamage.Warhead` determines which Warhead is used to deal the damage, defaults to `[CombatDamage] -> C4Warhead`. If `TransferDamage.Warhead.Detonate` is set to true, the Warhead is fully detonated instead of used to simply deal damage. If `TransferDamage.UseOriginalWarhead` is set to true, it'll reuse the Warhead that dealt damage to the object.
- `TransferDamage.Chance` determines the chance of transfer, while `TransferDamage.AffectsHouse` customizes which houses can trigger the transfer damage. `TransferDamage.Invoker.AbovePercent` and `TransferDamage.Invoker.BelowPercent` determine if the transfer can happen when the invoker's health is above or below the given values.
- Warheads can prevent transfer damage from occuring by setting `SuppressTransferDamage` to true. `SuppressTransferDamage.Types` can control which AttachEffectTypes' transfer damage is suppressed, if none are listed then all of them are suppressed. `SuppressTransferDamage.Groups` does the same thing but for all AttachEffectTypes in the listed groups.
- `SuppressTransferDamage.SelfOwned` can be used to set the house and TechnoType that the object the effect is attached to as the transfered damage's owner & invoker. Otherwise, it'll be set to the original damage dealer that trigger this transfer.
- `DisableWeapons` can be used to disable ability to fire any and all weapons.
- On TechnoTypes with `OpenTopped=true`, `OpenTopped.CheckTransportDisableWeapons` can be set to true to make passengers not be able to fire out if transport's weapons are disabled by `DisableWeapons`.
- `Unkillable` can be used to prevent the techno from being killed by taken damage (minimum health will be 1).
Expand Down Expand Up @@ -142,11 +151,25 @@ RevengeWeapon.UseInvokerAsOwner=false ; boolean
ReflectDamage=false ; boolean
ReflectDamage.Warhead= ; WarheadType
ReflectDamage.Warhead.Detonate=false ; WarheadType
ReflectDamage.UseOriginalWarhead=false ; boolean
ReflectDamage.Multiplier=1.0 ; floating point value, percents or absolute
ReflectDamage.AffectsHouse=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
ReflectDamage.Chance=1.0 ; floating point value
ReflectDamage.Override= ; integer
ReflectDamage.UseInvokerAsOwner=false ; boolean
TransferDamage=false ; boolean
TransferDamage.SelfMultiplier=0.5 ; floating point value
TransferDamage.InvokerMultiplier=0.5 ; floating point value
TransferDamage.Minimum=-2147483648 ; integer
TransferDamage.Maximum=2147483647 ; integer
TransferDamage.Warhead= ; WarheadType
TransferDamage.Warhead.Detonate=false ; WarheadType
TransferDamage.UseOriginalWarhead=false ; boolean
TransferDamage.AffectsHouse=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
TransferDamage.Chance=1.0 ; floating point value
TransferDamage.Invoker.AbovePercent=0.0 ; floating point value
TransferDamage.Invoker.BelowPercent=1.0 ; floating point value
TransferDamage.SelfOwned=false ; boolean
DisableWeapons=false ; boolean
Unkillable=false ; boolean
LaserTrail.Type= ; LaserTrailType
Expand Down Expand Up @@ -187,6 +210,9 @@ AttachEffect.DurationOverrides= ; integer - duration override
SuppressReflectDamage=false ; boolean
SuppressReflectDamage.Types= ; List of AttachEffectTypes
SuppressReflectDamage.Groups= ; comma-separated list of strings (group IDs)
SuppressTransferDamage=false ; boolean
SuppressTransferDamage.Types= ; List of AttachEffectTypes
SuppressTransferDamage.Groups= ; comma-separated list of strings (group IDs)
```

### Custom Radiation Types
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ New:
- Option to scale `PowerSurplus` setting if enabled to current power drain with `PowerSurplus.ScaleToDrainAmount` (by Starkku)
- Global default value for `DefaultToGuardArea` (by TaranDahl)
- [Weapon range finding in cylinder](New-or-Enhanced-Logics.md#range-finding-in-cylinder) (by TaranDahl)
- Attached effect for transfering damage to invoker (by Ollerus)

Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
bool hasRangeModifier = false;
bool hasTint = false;
bool reflectsDamage = false;
int transferDamageCount = 0;
bool hasOnFireDiscardables = false;
bool hasRestrictedArmorMultipliers = false;
bool hasCritModifiers = false;
Expand All @@ -2056,6 +2057,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
hasRangeModifier |= (type->WeaponRange_ExtraRange != 0.0 || type->WeaponRange_Multiplier != 0.0);
hasTint |= type->HasTint();
reflectsDamage |= type->ReflectDamage;
transferDamageCount += type->TransferDamage ? 1 : 0;
hasOnFireDiscardables |= (type->DiscardOn & DiscardCondition::Firing) != DiscardCondition::None;
hasCritModifiers |= (type->Crit_Multiplier != 1.0 || type->Crit_ExtraChance != 0.0);
}
Expand All @@ -2071,6 +2073,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
pAE.HasRangeModifier = hasRangeModifier;
pAE.HasTint = hasTint;
pAE.ReflectDamage = reflectsDamage;
pAE.TransferDamageCount = transferDamageCount;
pAE.HasOnFireDiscardables = hasOnFireDiscardables;
pAE.HasRestrictedArmorMultipliers = hasRestrictedArmorMultipliers;
pAE.HasCritModifiers = hasCritModifiers;
Expand Down
99 changes: 92 additions & 7 deletions src/Ext/Techno/Hooks.ReceiveDamage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
GET(TechnoClass*, pThis, ECX);
LEA_STACK(args_ReceiveDamage*, args, 0x4);

const auto pWHExt = WarheadTypeExt::ExtMap.Find(args->WH);
const auto pWarhead = args->WH;
const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead);
int& damage = *args->Damage;

// AffectsAbove/BelowPercent & AffectsNeutral can ignore IgnoreDefenses like AffectsAllies/Enmies/Owner
Expand All @@ -28,19 +29,20 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
}

const auto pExt = TechnoExt::ExtMap.Find(pThis);
const auto pSource = args->Attacker;
const auto pSourceHouse = args->SourceHouse;
const auto pTargetHouse = pThis->Owner;

// Apply warhead effects
if (damage && !pWHExt->ApplyPerTargetEffectsOnDetonate.Get(RulesExt::Global()->ApplyPerTargetEffectsOnDetonate))
pWHExt->DetonateOnOneUnit(args->SourceHouse, pThis, args->Attacker);
pWHExt->DetonateOnOneUnit(args->SourceHouse, pThis, pSource);

// Calculate Damage Multiplier
if (!args->IgnoreDefenses && damage)
{
double multiplier = 1.0;

if (args->Attacker && args->Attacker->Berzerk)
if (pSource && pSource->Berzerk)
{
if (!pSourceHouse || !pTargetHouse || !pSourceHouse->IsAlliedWith(pTargetHouse))
multiplier = pWHExt->DamageEnemiesMultiplier_Berzerk.Get(RulesExt::Global()->DamageEnemiesMultiplier_Berzerk.Get(RulesExt::Global()->DamageEnemiesMultiplier));
Expand Down Expand Up @@ -128,11 +130,93 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
raiseCombatAlert();
}

// Shield Receive Damage
// Transfer Damage and Shield Receive Damage
if (!args->IgnoreDefenses)
{
int nDamageLeft = damage;

if (pExt->AE.TransferDamageCount > 0 && !pWHExt->Transfered)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to write a comment to explain that checking Transfered is to prevent circular calls.

{
const auto& suppressType = pWHExt->SuppressTransferDamage_Types;
const auto& suppressGroup = pWHExt->SuppressTransferDamage_Groups;
const bool suppress = pWHExt->SuppressTransferDamage;
const bool suppressByType = suppressType.size() > 0;
const bool suppressByGroup = suppressGroup.size() > 0;

if (!suppress || suppressByType || suppressByGroup)
{
const int baseDamage = static_cast<int>(static_cast<double>(damage) / pExt->AE.TransferDamageCount);
auto& random = ScenarioClass::Instance->Random;
nDamageLeft = 0;

for (const auto& attachEffect : pExt->AttachedEffects)
{
const auto pType = attachEffect->GetType();

if (!pType->TransferDamage)
continue;

if (pType->TransferDamage_Chance < random.RandomDouble())
{
nDamageLeft += baseDamage;
continue;
}

const auto pInvoker = attachEffect->GetInvoker();

if (!pInvoker || TechnoExt::IsHealthInThreshold(pInvoker, pType->TransferDamage_Invoker_BelowPercent, pType->TransferDamage_Invoker_AbovePercent))
{
nDamageLeft += baseDamage;
continue;
}

if (suppress)
{
if (suppressByType && suppressType.Contains(pType))
{
nDamageLeft += baseDamage;
continue;
}

if (suppressByGroup && pType->HasGroups(suppressGroup, false))
{
nDamageLeft += baseDamage;
continue;
}
}

if (!EnumFunctions::CanTargetHouse(pType->TransferDamage_AffectsHouse, pSourceHouse, pTargetHouse))
{
nDamageLeft += baseDamage;
continue;
}

int invokerDamage = std::clamp(static_cast<int>(baseDamage * pType->TransferDamage_InvokerMultiplier), pType->TransferDamage_Minimum.Get(), pType->TransferDamage_Maximum.Get());
auto const pWH = pType->TransferDamage_UseOriginalWarhead ? pWarhead : pType->TransferDamage_Warhead.Get(RulesClass::Instance->C4Warhead);
auto const pWHExtRef = WarheadTypeExt::ExtMap.Find(pWH);
pWHExtRef->Transfered = true;

if (pType->TransferDamage_SelfOwned)
{
if (pType->TransferDamage_Warhead_Detonate)
WarheadTypeExt::DetonateAt(pWH, pInvoker, pThis, invokerDamage, pTargetHouse);
else
pInvoker->ReceiveDamage(&invokerDamage, 0, pWH, pThis, false, false, pTargetHouse);
}
else
{
if (pType->TransferDamage_Warhead_Detonate)
WarheadTypeExt::DetonateAt(pWH, pInvoker, pSource, invokerDamage, pSourceHouse);
else
pInvoker->ReceiveDamage(&invokerDamage, 0, pWH, pSource, false, false, pSourceHouse);
}

pWHExtRef->Transfered = false;
nDamageLeft += static_cast<int>(baseDamage * pType->TransferDamage_SelfMultiplier);
}
}
}

if (const auto pShieldData = pExt->Shield.get())
{
if (pShieldData->IsActive())
Expand Down Expand Up @@ -373,15 +457,14 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7)
continue;
}

auto const pWH = pType->ReflectDamage_Warhead.Get(RulesClass::Instance->C4Warhead);
int damage = pType->ReflectDamage_Override.Get(static_cast<int>(*pDamage * pType->ReflectDamage_Multiplier));

if (pType->ReflectDamage_UseInvokerAsOwner)
{
auto const pInvoker = attachEffect->GetInvoker();

if (pInvoker && EnumFunctions::CanTargetHouse(pType->ReflectDamage_AffectsHouse, pInvoker->Owner, pSourceHouse))
{
int damage = pType->ReflectDamage_Override.Get(static_cast<int>(*pDamage * pType->ReflectDamage_Multiplier));
auto const pWH = pType->ReflectDamage_UseOriginalWarhead ? pWarhead : pType->ReflectDamage_Warhead.Get(RulesClass::Instance->C4Warhead);
auto const pWHExtRef = WarheadTypeExt::ExtMap.Find(pWH);
pWHExtRef->Reflected = true;

Expand All @@ -395,6 +478,8 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7)
}
else if (EnumFunctions::CanTargetHouse(pType->ReflectDamage_AffectsHouse, pThis->Owner, pSourceHouse))
{
int damage = pType->ReflectDamage_Override.Get(static_cast<int>(*pDamage * pType->ReflectDamage_Multiplier));
auto const pWH = pType->ReflectDamage_UseOriginalWarhead ? pWarhead : pType->ReflectDamage_Warhead.Get(RulesClass::Instance->C4Warhead);
auto const pWHExtRef = WarheadTypeExt::ExtMap.Find(pWH);
pWHExtRef->Reflected = true;

Expand Down
7 changes: 7 additions & 0 deletions src/Ext/WarheadType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->SuppressReflectDamage.Read(exINI, pSection, "SuppressReflectDamage");
this->SuppressReflectDamage_Types.Read(exINI, pSection, "SuppressReflectDamage.Types");
exINI.ParseStringList(this->SuppressReflectDamage_Groups, pSection, "SuppressReflectDamage.Groups");
this->SuppressTransferDamage.Read(exINI, pSection, "SuppressTransferDamage");
this->SuppressTransferDamage_Types.Read(exINI, pSection, "SuppressTransferDamage.Types");
exINI.ParseStringList(this->SuppressTransferDamage_Groups, pSection, "SuppressTransferDamage.Groups");

this->BuildingSell.Read(exINI, pSection, "BuildingSell");
this->BuildingSell_IgnoreUnsellable.Read(exINI, pSection, "BuildingSell.IgnoreUnsellable");
Expand Down Expand Up @@ -565,6 +568,9 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm)
.Process(this->SuppressReflectDamage)
.Process(this->SuppressReflectDamage_Types)
.Process(this->SuppressReflectDamage_Groups)
.Process(this->SuppressTransferDamage)
.Process(this->SuppressTransferDamage_Types)
.Process(this->SuppressTransferDamage_Groups)

.Process(this->AffectsBelowPercent)
.Process(this->AffectsAbovePercent)
Expand Down Expand Up @@ -646,6 +652,7 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm)
.Process(this->RemainingAnimCreationInterval)
.Process(this->PossibleCellSpreadDetonate)
.Process(this->Reflected)
.Process(this->Transfered)
.Process(this->DamageAreaTarget)
;
}
Expand Down
8 changes: 8 additions & 0 deletions src/Ext/WarheadType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ class WarheadTypeExt
Valueable<bool> SuppressReflectDamage;
ValueableVector<AttachEffectTypeClass*> SuppressReflectDamage_Types;
std::vector<std::string> SuppressReflectDamage_Groups;
Valueable<bool> SuppressTransferDamage;
ValueableVector<AttachEffectTypeClass*> SuppressTransferDamage_Types;
std::vector<std::string> SuppressTransferDamage_Groups;

Valueable<bool> BuildingSell;
Valueable<bool> BuildingSell_IgnoreUnsellable;
Expand Down Expand Up @@ -239,6 +242,7 @@ class WarheadTypeExt
bool WasDetonatedOnAllMapObjects;
bool Splashed;
bool Reflected;
bool Transfered;
int RemainingAnimCreationInterval;
bool PossibleCellSpreadDetonate;
bool HealthCheck;
Expand Down Expand Up @@ -411,6 +415,9 @@ class WarheadTypeExt
, SuppressReflectDamage { false }
, SuppressReflectDamage_Types {}
, SuppressReflectDamage_Groups {}
, SuppressTransferDamage { false }
, SuppressTransferDamage_Types {}
, SuppressTransferDamage_Groups {}

, BuildingSell { false }
, BuildingSell_IgnoreUnsellable { false }
Expand Down Expand Up @@ -445,6 +452,7 @@ class WarheadTypeExt
, WasDetonatedOnAllMapObjects { false }
, Splashed { false }
, Reflected { false }
, Transfered { false }
, RemainingAnimCreationInterval { 0 }
, PossibleCellSpreadDetonate { false }
, HealthCheck { false }
Expand Down
2 changes: 2 additions & 0 deletions src/New/Entity/AttachEffectClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ struct AttachEffectTechnoProperties
bool HasRangeModifier;
bool HasTint;
bool ReflectDamage;
int TransferDamageCount;
bool HasOnFireDiscardables;
bool HasRestrictedArmorMultipliers;
bool HasCritModifiers;
Expand All @@ -133,6 +134,7 @@ struct AttachEffectTechnoProperties
, HasRangeModifier { false }
, HasTint { false }
, ReflectDamage { false }
, TransferDamageCount { 0 }
, HasOnFireDiscardables { false }
, HasRestrictedArmorMultipliers { false }
, HasCritModifiers { false }
Expand Down
Loading