Skip to content

AE attach/discard by health & ExtraWarheads & Aux/FeedbackWeapon & other tweaks #1643

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

Open
wants to merge 17 commits 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
3 changes: 3 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,9 @@ This page lists all the individual contributions to the project by their author.
- Power plant damage factor
- Allow faking digital display for `InfoType=Health` at disguise
- Display banner improvement and doc
- Attached effect attach/discard by health
- Attached effect with `ExtraWarheads`, `KillWeapon` and `FeedbackWeapon`
- `AuxWeapon`
- **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes
- **handama** - AI script action to `16005 Jump Back To Previous Script`
- **TaranDahl (航味麻酱)**:
Expand Down
96 changes: 87 additions & 9 deletions docs/New-or-Enhanced-Logics.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,9 @@ New:
- Several attackmove related enhancement (by TaranDahl)
- Ground line for select box (by NetsuNegi)
- Support for more optional weapons (by FlyStar)
- Attached effect attach/discard by health (by Ollerus)
- Attached effect with `ExtraWarheads`, `KillWeapon` and `FeedbackWeapon` (by Ollerus)
- [AuxWeapon](New-or-Enhanced-Logics.md#auxiliary-weapon) (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
62 changes: 62 additions & 0 deletions src/Ext/Bullet/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,68 @@ void BulletExt::ExtData::InitializeLaserTrails()
this->LaserTrails.emplace_back(std::make_unique<LaserTrailClass>(LaserTrailTypeClass::Array[idxTrail].get(), pOwner));
}

void BulletExt::ExtData::ApplyExtraWarheads(const std::vector<WarheadTypeClass*>& exWH, const std::vector<int>& exWHOverrides, const std::vector<double>& exWHChances, const std::vector<bool>& exWHFull, const std::vector<bool>& exWHOwner, const CoordStruct& coords, HouseClass* pOwner, TechnoClass* pInvoker)
{
auto const pThis = this->OwnerObject();
const int defaultDamage = pThis->WeaponType ? pThis->WeaponType->Damage : 0;

for (size_t i = 0; i < exWH.size(); i++)
{
auto const pWH = exWH[i];
auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH);
auto const pTarget = abstract_cast<TechnoClass*>(pThis->Target); // must be check in every loop

if (pTarget && !pWHExt->IsHealthInThreshold(pTarget))
continue;

int damage = defaultDamage;
size_t size = exWHOverrides.size();

if (size > i)
damage = exWHOverrides[i];
else if (size > 0)
damage = exWHOverrides[size - 1];

bool detonate = true;
size = exWHChances.size();

if (size > i)
detonate = exWHChances[i] >= ScenarioClass::Instance->Random.RandomDouble();
else if (size > 0)
detonate = exWHChances[size - 1] >= ScenarioClass::Instance->Random.RandomDouble();

if (!detonate)
continue;

auto pFirer = pThis->Owner;
auto pHouse = pOwner;

if (pInvoker)
{
size = exWHOwner.size();

if ((size > i && exWHOwner[i]) || (size > 0 && exWHOwner[size - 1]))
{
pFirer = pInvoker;
pHouse = pInvoker->Owner;
}
}

bool isFull = true;
size = exWHFull.size();

if (size > i)
isFull = exWHFull[i];
else if (size > 0)
isFull = exWHFull[size - 1];

if (isFull)
WarheadTypeExt::DetonateAt(pWH, coords, pFirer, damage, pHouse, pThis->Target);
else
pWHExt->DamageAreaWithTarget(coords, damage, pFirer, pWH, true, pHouse, pTarget);
}
}

static inline int SetBuildingFireAnimZAdjust(BuildingClass* pBuilding, int animY)
{
if (pBuilding->GetOccupantCount() > 0)
Expand Down
1 change: 1 addition & 0 deletions src/Ext/Bullet/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class BulletExt
void InterceptBullet(TechnoClass* pSource, WeaponTypeClass* pWeapon);
void ApplyRadiationToCell(CellStruct cell, int spread, int radLevel);
void InitializeLaserTrails();
void ApplyExtraWarheads(const std::vector<WarheadTypeClass*>& exWH, const std::vector<int>& exWHOverrides, const std::vector<double>& exWHChances, const std::vector<bool>& exWHFull, const std::vector<bool>& exWHOwner, const CoordStruct& coords, HouseClass* pOwner, TechnoClass* pInvoker = nullptr);

private:
template <typename T>
Expand Down
72 changes: 22 additions & 50 deletions src/Ext/Bullet/Hooks.DetonateLogics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,56 +345,40 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5)
GET(BulletClass*, pThis, ESI);
GET_BASE(CoordStruct*, coords, 0x8);

auto const pBulletExt = BulletExt::ExtMap.Find(pThis);
auto const pTechno = pThis->Owner;
auto const pOwner = pTechno ? pTechno->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse;

// Extra warheads
if (pThis->WeaponType)
{
auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pThis->WeaponType);
int defaultDamage = pThis->WeaponType->Damage;

for (size_t i = 0; i < pWeaponExt->ExtraWarheads.size(); i++)
if (pWeaponExt->ExtraWarheads.size() > 0)
{
auto const pWH = pWeaponExt->ExtraWarheads[i];
int damage = defaultDamage;
size_t size = pWeaponExt->ExtraWarheads_DamageOverrides.size();
auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH);
std::vector<bool> vec;

if (auto const pTarget = abstract_cast<TechnoClass*>(pThis->Target))
{
if (!pWHExt->IsHealthInThreshold(pTarget))
continue;
}

if (size > i)
damage = pWeaponExt->ExtraWarheads_DamageOverrides[i];
else if (size > 0)
damage = pWeaponExt->ExtraWarheads_DamageOverrides[size - 1];

bool detonate = true;
size = pWeaponExt->ExtraWarheads_DetonationChances.size();

if (size > i)
detonate = pWeaponExt->ExtraWarheads_DetonationChances[i] >= ScenarioClass::Instance->Random.RandomDouble();
else if (size > 0)
detonate = pWeaponExt->ExtraWarheads_DetonationChances[size - 1] >= ScenarioClass::Instance->Random.RandomDouble();

bool isFull = true;
size = pWeaponExt->ExtraWarheads_FullDetonation.size();
pBulletExt->ApplyExtraWarheads(pWeaponExt->ExtraWarheads, pWeaponExt->ExtraWarheads_DamageOverrides,
pWeaponExt->ExtraWarheads_DetonationChances, pWeaponExt->ExtraWarheads_FullDetonation, vec, *coords, pOwner);
}
}

if (size > i)
isFull = pWeaponExt->ExtraWarheads_FullDetonation[i];
else if (size > 0)
isFull = pWeaponExt->ExtraWarheads_FullDetonation[size - 1];
if (pThis->Owner)
{
auto const pExt = TechnoExt::ExtMap.Find(pThis->Owner);

if (!detonate)
continue;
if (pExt->AE.HasExtraWarheads)
{
for (auto const& pAE : pExt->AttachedEffects)
{
auto const pType = pAE->GetType();

if (isFull)
WarheadTypeExt::DetonateAt(pWH, *coords, pTechno, damage, pOwner, pThis->Target);
else
WarheadTypeExt::ExtMap.Find(pWH)->DamageAreaWithTarget(*coords, damage, pTechno, pWH, true, pOwner, abstract_cast<TechnoClass*>(pThis->Target));
if (pType->ExtraWarheads.size() > 0)
{
pBulletExt->ApplyExtraWarheads(pType->ExtraWarheads, pType->ExtraWarheads_DamageOverrides, pType->ExtraWarheads_DetonationChances,
pType->ExtraWarheads_FullDetonation, pType->ExtraWarheads_UseInvokerAsOwner, *coords, pOwner, pAE->GetInvoker());
}
}
}
}

Expand All @@ -404,19 +388,7 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5)
auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type);

if (auto const pWeapon = pTypeExt->ReturnWeapon)
{
auto damage = pWeapon->Damage;

if (pTypeExt->ReturnWeapon_ApplyFirepowerMult)
damage = static_cast<int>(damage * pTechno->FirepowerMultiplier * TechnoExt::ExtMap.Find(pTechno)->AE.FirepowerMultiplier);

if (BulletClass* pBullet = pWeapon->Projectile->CreateBullet(pTechno, pTechno,
damage, pWeapon->Warhead, pWeapon->Speed, pWeapon->Bright))
{
BulletExt::SimulatedFiringUnlimbo(pBullet, pOwner, pWeapon, pThis->Location, false);
BulletExt::SimulatedFiringEffects(pBullet, pOwner, nullptr, false, true);
}
}
TechnoExt::RealLaunch(pWeapon, pTechno, pTechno, pTypeExt->ReturnWeapon_ApplyFirepowerMult);
}

WarheadTypeExt::ExtMap.Find(pThis->WH)->InDamageArea = true;
Expand Down
14 changes: 13 additions & 1 deletion src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void TechnoExt::ExtData::OnEarlyUpdate()
this->ApplyMindControlRangeLimit();
this->UpdateRecountBurst();
this->UpdateRearmInEMPState();

if (this->AttackMoveFollowerTempCount)
{
this->AttackMoveFollowerTempCount--;
Expand Down Expand Up @@ -1833,6 +1833,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
double armor = 1.0;
double speed = 1.0;
double ROF = 1.0;
double negativeDamage = 1.0;
bool cloak = false;
bool forceDecloak = false;
bool disableWeapons = false;
Expand All @@ -1842,6 +1843,9 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
bool reflectsDamage = false;
bool hasOnFireDiscardables = false;
bool hasRestrictedArmorMultipliers = false;
bool hasCritModifiers = false;
bool hasExtraWarheads = false;
bool hasFeedbackOrAuxWeapon = false;

for (const auto& attachEffect : this->AttachedEffects)
{
Expand All @@ -1853,6 +1857,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
speed *= type->SpeedMultiplier;
armor *= type->ArmorMultiplier;
ROF *= type->ROFMultiplier;
negativeDamage *= type->NegativeDamage_Multiplier;
cloak |= type->Cloakable;
forceDecloak |= type->ForceDecloak;
disableWeapons |= type->DisableWeapons;
Expand All @@ -1862,12 +1867,16 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
reflectsDamage |= type->ReflectDamage;
hasOnFireDiscardables |= (type->DiscardOn & DiscardCondition::Firing) != DiscardCondition::None;
hasRestrictedArmorMultipliers |= (type->ArmorMultiplier != 1.0 && (type->ArmorMultiplier_AllowWarheads.size() > 0 || type->ArmorMultiplier_DisallowWarheads.size() > 0));
hasCritModifiers |= (type->Crit_Multiplier != 1.0 || type->Crit_ExtraChance != 0.0);
hasExtraWarheads |= type->ExtraWarheads.size() > 0;
hasFeedbackOrAuxWeapon |= type->FeedbackWeapon != nullptr || type->AuxWeapon != nullptr;
}

pAE.FirepowerMultiplier = firepower;
pAE.ArmorMultiplier = armor;
pAE.SpeedMultiplier = speed;
pAE.ROFMultiplier = ROF;
pAE.NegativeDamageMultiplier = negativeDamage;
pAE.Cloakable = cloak;
pAE.ForceDecloak = forceDecloak;
pAE.DisableWeapons = disableWeapons;
Expand All @@ -1877,6 +1886,9 @@ void TechnoExt::ExtData::RecalculateStatMultipliers()
pAE.ReflectDamage = reflectsDamage;
pAE.HasOnFireDiscardables = hasOnFireDiscardables;
pAE.HasRestrictedArmorMultipliers = hasRestrictedArmorMultipliers;
pAE.HasCritModifiers = hasCritModifiers;
pAE.HasExtraWarheads = hasExtraWarheads;
pAE.HasFeedbackOrAuxWeapon = hasFeedbackOrAuxWeapon;

if (forceDecloak && pThis->CloakState == CloakState::Cloaked)
pThis->Uncloak(true);
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class TechnoExt
void ApplyMindControlRangeLimit();
int ApplyForceWeaponInRange(AbstractClass* pTarget);
void UpdateTintValues();
void ApplyAuxWeapon(WeaponTypeClass* pAuxWeapon, AbstractClass* pTarget, const CoordStruct& offset, int range, const double& accuracy, bool onTurret, bool retarget, bool aroundFirer, bool zeroDamage, bool firepowerMult, TechnoClass* pInvoker = nullptr);

UnitTypeClass* GetUnitTypeExtra() const;

Expand Down Expand Up @@ -264,4 +265,6 @@ class TechnoExt
static void ApplyKillWeapon(TechnoClass* pThis, TechnoClass* pSource, WarheadTypeClass* pWH);
static void ApplyRevengeWeapon(TechnoClass* pThis, TechnoClass* pSource, WarheadTypeClass* pWH);
static bool MultiWeaponCanFire(TechnoClass* const pThis, AbstractClass* const pTarget, WeaponTypeClass* const pWeaponType);
static bool IsAllowedSplitsTarget(TechnoClass* pSource, HouseClass* pOwner, WeaponTypeClass* pWeapon, TechnoClass* pTarget, bool useWeaponTargeting = true, bool allowZeroDamage = false);
static void RealLaunch(WeaponTypeClass* pWeapon, TechnoClass* pSource, TechnoClass* pTarget, bool applyFirepowerMult = true, TechnoClass* pFirer = nullptr);
};
43 changes: 40 additions & 3 deletions src/Ext/Techno/Hooks.Firing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -582,15 +582,52 @@ DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6)
{
GET(TechnoClass*, pThis, ESI);
GET(WeaponTypeClass*, pWeapon, EBX);
GET_BASE(AbstractClass*, pTarget, 0x8);

auto const pExt = TechnoExt::ExtMap.Find(pThis);

if (auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon))
{
if (auto const pWeaponFeedback = pWeaponExt->FeedbackWeapon)
{
if (pThis->InOpenToppedTransport && !pWeaponFeedback->FireInTransport)
return 0;
if (!pThis->InOpenToppedTransport || pWeaponFeedback->FireInTransport)
WeaponTypeExt::DetonateAt(pWeaponFeedback, pThis, pThis);
}

if (auto const pAuxWeapon = pWeaponExt->AuxWeapon)
{
pExt->ApplyAuxWeapon(pAuxWeapon, pTarget, pWeaponExt->AuxWeapon_Offset, pWeaponExt->AuxWeapon_Retarget_Range, pWeaponExt->AuxWeapon_Retarget_Accuracy, pWeaponExt->AuxWeapon_FireOnTurret,
pWeaponExt->AuxWeapon_Retarget, pWeaponExt->AuxWeapon_Retarget_AroundFirer, pWeaponExt->AuxWeapon_AllowZeroDamage, pWeaponExt->AuxWeapon_ApplyFirepowerMult);
}
}

if (pExt->AE.HasFeedbackOrAuxWeapon)
{
for (auto const& pAE : pExt->AttachedEffects)
{
auto const pAEType = pAE->GetType();

if (auto const pWeaponFeedback = pAEType->FeedbackWeapon)
{
if (pThis->InOpenToppedTransport && !pWeaponFeedback->FireInTransport)
continue;

if (pAEType->FeedbackWeapon_UseInvokerAsOwner)
WeaponTypeExt::DetonateAt(pWeaponFeedback, pThis, pAE->GetInvoker());
else
WeaponTypeExt::DetonateAt(pWeaponFeedback, pThis, pThis);
}

WeaponTypeExt::DetonateAt(pWeaponFeedback, pThis, pThis);
if (auto const pAuxWeapon = pAEType->AuxWeapon)
{
TechnoClass* pInvoker = nullptr;

if (pAEType->AuxWeapon_UseInvokerAsOwner)
pInvoker = pAE->GetInvoker();

pExt->ApplyAuxWeapon(pAuxWeapon, pTarget, pAEType->AuxWeapon_Offset, pAEType->AuxWeapon_Retarget_Range, pAEType->AuxWeapon_Retarget_Accuracy, pAEType->AuxWeapon_FireOnTurret,
pAEType->AuxWeapon_Retarget, pAEType->AuxWeapon_Retarget_AroundFirer, pAEType->AuxWeapon_AllowZeroDamage, pAEType->AuxWeapon_ApplyFirepowerMult, pInvoker);
}
}
}

Expand Down
25 changes: 16 additions & 9 deletions src/Ext/Techno/Hooks.ReceiveDamage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)

const auto pSourceHouse = args->SourceHouse;
const auto pTargetHouse = pThis->Owner;
int& damage = *args->Damage;

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

Expand All @@ -40,14 +41,14 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)

if (multiplier != 1.0)
{
const auto sgnDamage = *args->Damage > 0 ? 1 : -1;
const auto calculateDamage = static_cast<int>(*args->Damage * multiplier);
*args->Damage = calculateDamage ? calculateDamage : sgnDamage;
const auto sgnDamage = damage > 0 ? 1 : -1;
const auto calculateDamage = static_cast<int>(damage * multiplier);
damage = calculateDamage ? calculateDamage : sgnDamage;
}
}

// Raise Combat Alert
if (pRules->CombatAlert && *args->Damage > 1)
if (pRules->CombatAlert && damage > 1)
{
auto raiseCombatAlert = [&]()
{
Expand Down Expand Up @@ -104,7 +105,7 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
// Shield Receive Damage
if (!args->IgnoreDefenses)
{
int nDamageLeft = *args->Damage;
int nDamageLeft = damage;

if (const auto pShieldData = pExt->Shield.get())
{
Expand All @@ -114,9 +115,9 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)

if (nDamageLeft >= 0)
{
*args->Damage = nDamageLeft;
damage = nDamageLeft;

if (auto pTag = pThis->AttachedTag)
if (const auto pTag = pThis->AttachedTag)
pTag->RaiseEvent((TriggerEvent)PhobosTriggerEvent::ShieldBroken, pThis, CellStruct::Empty);
}

Expand All @@ -131,11 +132,17 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
&& MapClass::GetTotalDamage(nDamageLeft, args->WH, pThis->GetTechnoType()->Armor, args->DistanceToEpicenter) >= pThis->Health)
{
// Update remaining damage and check if the target will die and should be avoided
*args->Damage = 0;
damage = 0;
pThis->Health = 1;
pThis->EstimatedHealth = 1;
ReceiveDamageTemp::SkipLowDamageCheck = true;
}

if (pExt->AE.NegativeDamageMultiplier != 1.0 && nDamageLeft != 0 && pWHExt->CanTargetHouse(pSourceHouse, pThis)
&& MapClass::GetTotalDamage(nDamageLeft, args->WH, pThis->GetTechnoType()->Armor, args->DistanceToEpicenter) < 0)
{
damage = static_cast<int>(damage * pExt->AE.NegativeDamageMultiplier);
}
}

return 0;
Expand Down
Loading