diff --git a/CREDITS.md b/CREDITS.md index a07fb6e15f..1db04aa072 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -373,6 +373,7 @@ This page lists all the individual contributions to the project by their author. - Fix an issue that teleport units board transport vehicles on the bridge will create an impassable invisible barrier, which may cause the game to freeze or even crash - Fix wrong shadow when a vehicle has hover locomotor and is being lifted by `IsLocomotor=yes` warhead - Fix the bug that a unit can overlap with `Teleport` units after it's been damaged by a fallen unit lifted by `IsLocomotor=yes` warheads + - Directional armor - **Apollo** - Translucent SHP drawing patches - **ststl**: - Customizable `ShowTimer` priority of superweapons diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 29c0d89f85..90183d0b32 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1774,6 +1774,38 @@ JumpjetTilt.SidewaysRotationFactor=1.0 ; floating point value JumpjetTilt.SidewaysSpeedFactor=1.0 ; floating point value ``` +### Directional armor + +- The damage suffered by the vehicle can now be affected by the hit direction. +- The front and rear judgment ranges are always symmetrical. A front angle of 0.5 indicates that the front direction is the axis, and the 45 degree angle range on both sides belongs to the front judgment range. +A front angle of 1.0 indicates that the 90 degree angle range on both sides belongs to the front judgment range; +- The lateral range refers to the remaining angle range after excluding the front and back sides. +- The warhead needs to have `Directional=true` to enable this effect. +- `Directional.Multiplier` is an additional multiplier used to control the intensity of the effect. + +In `rulesmd.ini` +```ini +[CombatDamage] +DirectionalArmor=false ; boolean +DirectionalArmor.FrontMultiplier=1.0 ; float +DirectionalArmor.SideMultiplier=1.0 ; float +DirectionalArmor.BackMultiplier=1.0 ; float +DirectionalArmor.FrontField=0.5 ; float +DirectionalArmor.BackField=0.5 ; float + +[SOMEVEHICLE] ; VehicleType +DirectionalArmor= ; boolean +DirectionalArmor.FrontMultiplier= ; float +DirectionalArmor.SideMultiplier= ; float +DirectionalArmor.BackMultiplier= ; float +DirectionalArmor.FrontField= ; float +DirectionalArmor.BackField= ; float + +[SOMEWARHEAD] +Directional=false ; boolean +Directional.Multiplier=1.0 ; float +``` + ## Warheads ```{hint} diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 1b520e62ce..b492ce38c3 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -358,6 +358,7 @@ New: - [Jumpjet Tilts While Moving](New-or-Enhanced-Logics.md#jumpjet-tilts-while-moving) (by CrimRecya) - [Spawned aircraft facing to match turret toggle](New-or-Enhanced-Logics.md#aircraft-spawner-customizations) (by Starkku) - [Removed dependency on `blowfish.dll`](Miscellanous.md#blowfish-dependency) (by ZivDero) +- Directional armor (by NetsuNegi) Vanilla fixes: - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index cd405e5825..7dc16a7ee9 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 3178c0ab06..c0d12cd1aa 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -204,6 +204,13 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->DamageAlliesMultiplier.Read(exINI, GameStrings::CombatDamage, "DamageAlliesMultiplier"); this->DamageEnemiesMultiplier.Read(exINI, GameStrings::CombatDamage, "DamageEnemiesMultiplier"); + this->DirectionalArmor.Read(exINI, GameStrings::CombatDamage, "DirectionalArmor"); + this->DirectionalArmor_FrontMultiplier.Read(exINI, GameStrings::CombatDamage, "DirectionalArmor.FrontMultiplier"); + this->DirectionalArmor_SideMultiplier.Read(exINI, GameStrings::CombatDamage, "DirectionalArmor.SideMultiplier"); + this->DirectionalArmor_BackMultiplier.Read(exINI, GameStrings::CombatDamage, "DirectionalArmor.BackMultiplier"); + this->DirectionalArmor_FrontField.Read(exINI, GameStrings::CombatDamage, "DirectionalArmor.FrontField"); + this->DirectionalArmor_BackField.Read(exINI, GameStrings::CombatDamage, "DirectionalArmor.BackField"); + this->AircraftLevelLightMultiplier.Read(exINI, GameStrings::AudioVisual, "AircraftLevelLightMultiplier"); this->JumpjetLevelLightMultiplier.Read(exINI, GameStrings::AudioVisual, "JumpjetLevelLightMultiplier"); @@ -429,6 +436,12 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->DamageOwnerMultiplier) .Process(this->DamageAlliesMultiplier) .Process(this->DamageEnemiesMultiplier) + .Process(this->DirectionalArmor) + .Process(this->DirectionalArmor_FrontMultiplier) + .Process(this->DirectionalArmor_SideMultiplier) + .Process(this->DirectionalArmor_BackMultiplier) + .Process(this->DirectionalArmor_FrontField) + .Process(this->DirectionalArmor_BackField) .Process(this->AircraftLevelLightMultiplier) .Process(this->JumpjetLevelLightMultiplier) .Process(this->VoxelLightSource) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b4a5d764c2..9c1dd9bc99 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -162,6 +162,13 @@ class RulesExt Valueable DamageAlliesMultiplier; Valueable DamageEnemiesMultiplier; + Valueable DirectionalArmor; + Valueable DirectionalArmor_FrontMultiplier; + Valueable DirectionalArmor_SideMultiplier; + Valueable DirectionalArmor_BackMultiplier; + Valueable DirectionalArmor_FrontField; + Valueable DirectionalArmor_BackField; + Valueable AircraftLevelLightMultiplier; Valueable JumpjetLevelLightMultiplier; @@ -325,6 +332,14 @@ class RulesExt , DamageOwnerMultiplier { 1.0 } , DamageAlliesMultiplier { 1.0 } , DamageEnemiesMultiplier { 1.0 } + + , DirectionalArmor { false } + , DirectionalArmor_FrontMultiplier { 1.0 } + , DirectionalArmor_SideMultiplier { 1.0 } + , DirectionalArmor_BackMultiplier { 1.0 } + , DirectionalArmor_FrontField { 0.5 } + , DirectionalArmor_BackField { 0.5 } + , AircraftLevelLightMultiplier { 1.0 } , JumpjetLevelLightMultiplier { 0.0 } , VoxelLightSource { } diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index 36e37c366d..a661191dbf 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -40,6 +40,21 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) else multiplier = pWHExt->DamageOwnerMultiplier.Get(pRules->DamageOwnerMultiplier); + if (pTypeExt->DirectionalArmor.Get(RulesExt::Global()->DirectionalArmor) && pThis->WhatAmI() == AbstractType::Unit && WarheadTypeExt::HitDirection >= 0 && args->DistanceToEpicenter <= 64) + { + const int tarFacing = pThis->PrimaryFacing.Current().GetValue<16>(); + const int angle = abs(WarheadTypeExt::HitDirection - tarFacing); + const int frontField = static_cast(16384 * pTypeExt->DirectionalArmor_FrontField.Get(RulesExt::Global()->DirectionalArmor_FrontField)); + const int backField = static_cast(16384 * pTypeExt->DirectionalArmor_BackField.Get(RulesExt::Global()->DirectionalArmor_BackField)); + + if (angle >= 32768 - frontField && angle <= 32768 + frontField) + multiplier *= pTypeExt->DirectionalArmor_FrontMultiplier.Get(RulesExt::Global()->DirectionalArmor_FrontMultiplier) * pWHExt->Directional_Multiplier; + else if ((angle < backField && angle >= 0) || (angle > 49152 + backField && angle <= 65536)) + multiplier *= pTypeExt->DirectionalArmor_BackMultiplier.Get(RulesExt::Global()->DirectionalArmor_BackMultiplier) * pWHExt->Directional_Multiplier; + else + multiplier *= pTypeExt->DirectionalArmor_SideMultiplier.Get(RulesExt::Global()->DirectionalArmor_SideMultiplier) * pWHExt->Directional_Multiplier; + } + if (multiplier != 1.0) { const auto sgnDamage = *args->Damage > 0 ? 1 : -1; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index bcc74bf4b5..b9dba07722 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -498,6 +498,13 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->KeepTargetOnMove.Read(exINI, pSection, "KeepTargetOnMove"); this->KeepTargetOnMove_ExtraDistance.Read(exINI, pSection, "KeepTargetOnMove.ExtraDistance"); + this->DirectionalArmor.Read(exINI, pSection, "DirectionalArmor"); + this->DirectionalArmor_FrontMultiplier.Read(exINI, pSection, "DirectionalArmor.FrontMultiplier"); + this->DirectionalArmor_SideMultiplier.Read(exINI, pSection, "DirectionalArmor.SideMultiplier"); + this->DirectionalArmor_BackMultiplier.Read(exINI, pSection, "DirectionalArmor.BackMultiplier"); + this->DirectionalArmor_FrontField.Read(exINI, pSection, "DirectionalArmor.FrontField"); + this->DirectionalArmor_BackField.Read(exINI, pSection, "DirectionalArmor.BackField"); + this->Power.Read(exINI, pSection, "Power"); this->Image_ConditionYellow.Read(exINI, pSection, "Image.ConditionYellow"); @@ -943,6 +950,13 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->KeepTargetOnMove) .Process(this->KeepTargetOnMove_ExtraDistance) + .Process(this->DirectionalArmor) + .Process(this->DirectionalArmor_FrontMultiplier) + .Process(this->DirectionalArmor_SideMultiplier) + .Process(this->DirectionalArmor_BackMultiplier) + .Process(this->DirectionalArmor_FrontField) + .Process(this->DirectionalArmor_BackField) + .Process(this->Power) .Process(this->Image_ConditionYellow) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 974c9ba36c..7edd04a46a 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -273,6 +273,13 @@ class TechnoTypeExt Valueable KeepTargetOnMove; Valueable KeepTargetOnMove_ExtraDistance; + Nullable DirectionalArmor; + Nullable DirectionalArmor_FrontMultiplier; + Nullable DirectionalArmor_SideMultiplier; + Nullable DirectionalArmor_BackMultiplier; + Nullable DirectionalArmor_FrontField; + Nullable DirectionalArmor_BackField; + Valueable Power; Nullable Image_ConditionYellow; @@ -568,6 +575,13 @@ class TechnoTypeExt , KeepTargetOnMove { false } , KeepTargetOnMove_ExtraDistance { Leptons(0) } + , DirectionalArmor {} + , DirectionalArmor_FrontMultiplier {} + , DirectionalArmor_SideMultiplier {} + , DirectionalArmor_BackMultiplier {} + , DirectionalArmor_FrontField {} + , DirectionalArmor_BackField {} + , Power { } , Image_ConditionYellow { } diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 39687fb2cd..4469dd9076 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -235,6 +235,9 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Shield_Respawn_Types.Read(exINI, pSection, "Shield.Respawn.Types"); this->Shield_SelfHealing_Types.Read(exINI, pSection, "Shield.SelfHealing.Types"); + this->Directional.Read(exINI, pSection, "Directional"); + this->Directional_Multiplier.Read(exINI, pSection, "Directional.Multiplier"); + this->NotHuman_DeathSequence.Read(exINI, pSection, "NotHuman.DeathSequence"); this->LaunchSW.Read(exINI, pSection, "LaunchSW"); this->LaunchSW_RealLaunch.Read(exINI, pSection, "LaunchSW.RealLaunch"); @@ -475,6 +478,9 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->Shield_Respawn_Types) .Process(this->Shield_SelfHealing_Types) + .Process(this->Directional) + .Process(this->Directional_Multiplier) + .Process(this->SpawnsCrate_Types) .Process(this->SpawnsCrate_Weights) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index a0fbd42c03..97acb72535 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -113,6 +113,9 @@ class WarheadTypeExt NullableVector Shield_Respawn_Types; NullableVector Shield_SelfHealing_Types; + Valueable Directional; + Valueable Directional_Multiplier; + Valueable NotHuman_DeathSequence; ValueableIdxVector LaunchSW; Valueable LaunchSW_RealLaunch; @@ -286,6 +289,9 @@ class WarheadTypeExt , Shield_Respawn_Types {} , Shield_SelfHealing_Types {} + , Directional { false} + , Directional_Multiplier { 1.0 } + , SpawnsCrate_Types {} , SpawnsCrate_Weights {} @@ -409,6 +415,8 @@ class WarheadTypeExt }; static ExtContainer ExtMap; + static int HitDirection; + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/WarheadType/Hooks.cpp b/src/Ext/WarheadType/Hooks.cpp index 6f0bd4241f..a6c9e178e0 100644 --- a/src/Ext/WarheadType/Hooks.cpp +++ b/src/Ext/WarheadType/Hooks.cpp @@ -10,6 +10,7 @@ #include #pragma region Detonation +int WarheadTypeExt::HitDirection = -1; DEFINE_HOOK(0x46920B, BulletClass_Detonate, 0x6) { @@ -21,12 +22,58 @@ DEFINE_HOOK(0x46920B, BulletClass_Detonate, 0x6) auto const pOwner = pBullet->Owner; auto const pHouse = pOwner ? pOwner->Owner : nullptr; auto const pDecidedHouse = pHouse ? pHouse : pBulletExt->FirerHouse; + pWHExt->Detonate(pOwner, pDecidedHouse, pBulletExt, *pCoords); pWHExt->InDamageArea = false; return 0; } +DEFINE_HOOK(0x469A69, BulletClass_Detonate_DamageArea, 0x6) +{ + enum { SkipGameCode = 0x469A88 }; + + GET(BulletClass*, pBullet, ESI); + GET(TechnoClass*, pSourceTechno, EAX); + GET(int, damage, EDX); + GET_BASE(CoordStruct*, coords, 0x8); + const auto pBulletExt = BulletExt::ExtMap.Find(pBullet); + const auto pSourceHouse = pSourceTechno ? pSourceTechno->Owner : pBulletExt->FirerHouse; + const auto pWH = pBullet->WH; + const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + + do + { + if (pWHExt->Directional) + { + if (pBullet->Type->Inviso) + { + if (pBullet->SourceCoords.X != pBullet->TargetCoords.X || pBullet->SourceCoords.Y != pBullet->TargetCoords.Y) + { + WarheadTypeExt::HitDirection = DirStruct(Math::atan2(static_cast(pBullet->SourceCoords.Y - pBullet->TargetCoords.Y), static_cast(pBullet->TargetCoords.X - pBullet->SourceCoords.X))).GetValue<16>(); + break; + } + } + else + { + if (pBullet->Velocity.X != 0.0 || pBullet->Velocity.Y != 0.0) + { + WarheadTypeExt::HitDirection = DirStruct((-1) * Math::atan2(pBullet->Velocity.Y, pBullet->Velocity.X)).GetValue<16>(); + break; + } + } + } + + WarheadTypeExt::HitDirection = -1; + } + while (false); + + R->EAX(MapClass::Instance.DamageArea(*coords, damage, pSourceTechno, pWH, true, pSourceHouse)); + WarheadTypeExt::HitDirection = -1; + + return SkipGameCode; +} + DEFINE_HOOK(0x489286, MapClass_DamageArea, 0x6) { GET_BASE(const WarheadTypeClass*, pWH, 0x0C);