diff --git a/README.md b/README.md index 4f85f581d0..aa18d06935 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Credits - **ChrisLv_CN** - interceptor logic, LaserTrails, laser fixes, general assistance (work relicensed under [following permission](images/ChrisLv-relicense.png)) - **Xkein** - general assistance, YRpp edits - **thomassneddon** - general assistance -- **Starkku** - Warhead shield penetration & breaking, strafing aircraft weapon customization, vehicle DeployFire fixes/improvements, stationary VehicleTypes, Burst logic improvements, TechnoType auto-firing weapons, Secondary weapon fallback customization, weapon target type filtering, AreaFire targeting customization, CreateUnit improvements, Attached animation & jumpjet unit layer customization, IsSimpleDeployer improvements, Shield modification warheads, Warhead decloaking toggle, Warp(In/Out)Weapon, Grinder improvements / additions, Attached animation position customization, Critical hit logic additions, Aircraft & jumpjet speed modifiers fix, Local warhead screen shaking, Vehicle custom palette fix +- **Starkku** - Warhead shield penetration & breaking, strafing aircraft weapon customization, vehicle DeployFire fixes/improvements, stationary VehicleTypes, Burst logic improvements, TechnoType auto-firing weapons, Secondary weapon fallback customization, weapon target type filtering, AreaFire targeting customization, CreateUnit improvements, Attached animation & jumpjet unit layer customization, IsSimpleDeployer improvements, Shield modification warheads, Warhead decloaking toggle, Warp(In/Out)Weapon, Grinder improvements / additions, Attached animation position customization, Critical hit logic additions, Aircraft & jumpjet speed modifiers fix, Local warhead screen shaking, Vehicle custom palette fix, Weapon owner detachment, Feedback weapon - **SukaHati (Erzoid)** - Minimum interceptor guard range - **Morton (MortonPL)** - XDrawOffset, Shield passthrough & absorption, building LimboDelivery, fix for Image in art rules, power delta counter - **mevitar** - honorary shield tester *triple* award diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 2862a79a59..52d95db6ac 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -52,6 +52,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed Engineers being able to enter `Grinding` buildings even when they shouldn't (such as ally building at full HP). - Aircraft & jumpjet units are now affected by speed modifiers such as `SpeedAircraft/Infantry/UnitsMult` on `Countries`, `VeteranSpeed` and Crates / AttachEffect (Ares feature). - Both voxel and SHP vehicle units should now correctly respect custom palette set through `Palette`. +- Weapons fired by EMPulse superweapons without `EMPulse.TargetSelf=true` *(Ares feature)* can now create radiation. ## Animations @@ -285,6 +286,18 @@ Bolt.Disable2=false ; boolean Bolt.Disable3=false ; boolean ``` +### Detaching weapon from owner TechnoType + +- You can now control if weapon is detached from the TechnoType that fired it. This results in the weapon / warhead being able to damage the TechnoType itself even if it does not have `DamageSelf=true` set, but also treats it as if owned by no house or object, meaning any ownership-based checks like `AffectsAllies` do not function as expected and no experience is awarded. + - The effect of this is inherited through `AirburstWeapon` and `ShrapnelWeapon`. + - This does not affect projectile image or functionality or `FirersPalette` on initially fired weapon, but `FirersPalette` will not function for any weapons inheriting the effect. + +In `rulesmd.ini`: +```ini +[SOMEWEAPONTYPE] ; WeaponType +DetachedFromOwner=false ; boolean +``` + ## Projectiles ### Customizable projectile gravity diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index a714ebb655..186526f3fb 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -453,25 +453,11 @@ In `rulesmd.ini`: ForceWeapon.Naval.Decloaked=-1 ; Integer. 0 for primary weapon, 1 for secondary weapon ``` -## Terrains - -### Destroy animation & sound - -- You can now specify a destroy animation and sound for a TerrainType that are played when it is destroyed. - -In `rulesmd.ini`: -```ini -[SOMETERRAINTYPE] ; TerrainType -DestroyAnim= ; Animation -DestroySound= ; Sound -``` - ### Weapons fired on warping in / out - It is now possible to add weapons that are fired on a teleporting TechnoType when it warps in or out. They are at the same time as the appropriate animations (`WarpIn` / `WarpOut`) are displayed. - `WarpInMinRangeWeapon` is used instead of `WarpInWeapon` if the distance traveled (in leptons) was less than `ChronoRangeMinimum`. This works regardless of if `ChronoTrigger` is set or not. If `WarpInMinRangeWeapon` is not set, it defaults to `WarpInWeapon`. - If `WarpInWeapon.UseDistanceAsDamage` is set, `Damage` of `WarpIn(MinRange)Weapon` is overriden by the number of whole cells teleported across. - - `WarpInWeapon.FireAsSelf` & `WarpOutWeapon.FireAsSelf` can be used to disable firing the weapon with the teleporting TechnoType as an owner. This allows damaging itself, but also makes certain Weapon or Warhead features reliant on owner not available. In `rulesmd.ini`: ```ini @@ -479,9 +465,20 @@ In `rulesmd.ini`: WarpInWeapon= ; WeaponType WarpInMinRangeWeapon= ; WeaponType WarpInWeapon.UseDistanceAsDamage=false ; boolean -WarpInWeapon.FireAsSelf=true ; boolean WarpOutWeapon= ; WeaponType -WarpOutWeapon.FireAsSelf=true ; boolean +``` + +## Terrains + +### Destroy animation & sound + +- You can now specify a destroy animation and sound for a TerrainType that are played when it is destroyed. + +In `rulesmd.ini`: +```ini +[SOMETERRAINTYPE] ; TerrainType +DestroyAnim= ; Animation +DestroySound= ; Sound ``` ## Weapons @@ -551,6 +548,17 @@ In `rulesmd.ini`: AreaFire.Target=base ; AreaFire Target Enumeration (base|self|random) ``` +### Feedback weapon + +- You can now specify an auxiliary weapon to be fired on the firer itself when a weapon is fired. + - `FireInTransport` setting of the feedback weapon is respected to determine if it can be fired when the original weapon is fired from inside `OpenTopped=true` transport. If feedback weapon is fired, it is fired on the transport. `OpenToppedDamageMultiplier` is not applied on feedback weapons. + +In `rulesmd.ini`: +```ini +[SOMEWEAPON] ; WeaponType +FeedbackWeapon= ; WeaponType +``` + ## Warheads ```{hint} diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 55f422c492..67a3335290 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -279,6 +279,8 @@ New: - Additional critical hit logic customizations (by Starkku) - Laser trails for VoxelAnims (by Otamaa) - Local warhead screen shaking (by Starkku) +- Weapon owner detachment (by Starkku) +- Feedback weapon (by Starkku) Vanilla fixes: - Fixed laser drawing code to allow for thicker lasers in house color draw mode (by Kerbiter, ChrisLv_CN) diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index e75b1b2056..48e8ab4fbc 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -1,5 +1,6 @@ #include "Body.h" +#include #include DEFINE_HOOK(0x7396D2, UnitClass_TryToDeploy_Transfer, 0x5) @@ -69,3 +70,14 @@ DEFINE_HOOK(0x4401BB, Factory_AI_PickWithFreeDocks, 0x6) return 0; } + + +DEFINE_HOOK(0x44D455, BuildingClass_Mission_Missile_EMPPulseBulletWeapon, 0x8) +{ + GET(WeaponTypeClass*, pWeapon, EBP); + GET_STACK(BulletClass*, pBullet, STACK_OFFS(0xF0, 0xA4)); + + pBullet->SetWeaponType(pWeapon); + + return 0; +} \ No newline at end of file diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index 9f575971f6..079f58ff97 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include #include #include #include @@ -38,7 +39,8 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI, 0x6) pThis->WeaponType && pThis->WeaponType->LimboLaunch; - if (isLimbo) { + if (isLimbo) + { pThis->SetTarget(nullptr); auto damage = pTechno->Health * 2; pTechno->SetLocation(pThis->GetCoords()); @@ -65,7 +67,7 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI, 0x6) (int)(location.Z + velocity.Z) }; - for (auto const& trail: pBulletExt->LaserTrails) + for (auto const& trail : pBulletExt->LaserTrails) { // We insert initial position so the first frame of trail doesn't get skipped - Kerbiter // TODO move hack to BulletClass creation @@ -183,9 +185,9 @@ DEFINE_HOOK(0x46A3D6, BulletClass_Shrapnel_Forced, 0xA) enum { Shrapnel = 0x46A40C, Skip = 0x46ADCD }; GET(BulletClass*, pBullet, EDI); - + auto const pData = BulletTypeExt::ExtMap.Find(pBullet->Type); - + if (auto const pObject = pBullet->GetCell()->FirstObject) { if (pObject->WhatAmI() != AbstractType::Building || pData->Shrapnel_AffectsBuildings) @@ -212,5 +214,20 @@ DEFINE_HOOK(0x4690D4, BulletClass_Logics_ScreenShake, 0x6) return SkipShaking; } + return 0; +} + +DEFINE_HOOK(0x4690C1, BulletClass_Logics_DetachFromOwner, 0x8) +{ + GET(BulletClass*, pThis, ESI); + + if (pThis->Owner && pThis->WeaponType) + { + auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pThis->WeaponType); + + if (pWeaponExt->DetachedFromOwner) + pThis->Owner = nullptr; + } + return 0; } \ No newline at end of file diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 872f098c9c..c77498c97c 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -167,12 +167,12 @@ DEFINE_HOOK(0x518505, InfantryClass_TakeDamage_NotHuman, 0x4) DEFINE_HOOK(0x5218F3, InfantryClass_WhatWeaponShouldIUse_DeployFireWeapon, 0x6) { - GET(TechnoTypeClass*, pType, ECX); + GET(TechnoTypeClass*, pType, ECX); - if (pType->DeployFireWeapon == -1) - return 0x52194E; + if (pType->DeployFireWeapon == -1) + return 0x52194E; - return 0; + return 0; } // Customizable OpenTopped Properties @@ -355,7 +355,7 @@ DEFINE_HOOK(0x71067B, TechnoClass_EnterTransport_LaserTrails, 0x7) if (pTechnoExt && pTechnoTypeExt) { - for (auto &pLaserTrail : pTechnoExt->LaserTrails) + for (auto& pLaserTrail : pTechnoExt->LaserTrails) { pLaserTrail->Visible = false; pLaserTrail->LastLocation = { }; @@ -372,7 +372,7 @@ DEFINE_HOOK(0x5F4F4E, ObjectClass_Unlimbo_LaserTrails, 0x7) auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); if (pTechnoExt) { - for (auto &pLaserTrail : pTechnoExt->LaserTrails) + for (auto& pLaserTrail : pTechnoExt->LaserTrails) { pLaserTrail->LastLocation = { }; pLaserTrail->Visible = true; @@ -402,8 +402,8 @@ DEFINE_HOOK(0x6F3428, TechnoClass_GetWeapon_ForceWeapon, 0x6) if (auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType)) { - if (pTechnoTypeExt->ForceWeapon_Naval_Decloaked >= 0 - && pTargetType->Cloakable && pTargetType->Naval + if (pTechnoTypeExt->ForceWeapon_Naval_Decloaked >= 0 + && pTargetType->Cloakable && pTargetType->Naval && pTarget->CloakState == CloakState::Uncloaked) { R->EAX(pTechnoTypeExt->ForceWeapon_Naval_Decloaked); @@ -424,3 +424,24 @@ DEFINE_HOOK(0x6FB086, TechnoClass_Reload_ReloadAmount, 0x8) return 0; } + +DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6) +{ + GET(TechnoClass*, pThis, ESI); + GET(WeaponTypeClass*, pWeapon, EBX); + + if (auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon)) + { + if (pWeaponExt->FeedbackWeapon.isset()) + { + auto fbWeapon = pWeaponExt->FeedbackWeapon.Get(); + + if (pThis->InOpenToppedTransport && !fbWeapon->FireInTransport) + return 0; + + WeaponTypeExt::DetonateAt(fbWeapon, pThis, pThis); + } + } + + return 0; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index ff13041dad..c6ba27225f 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -116,8 +116,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->WarpInMinRangeWeapon.Read(exINI, pSection, "WarpInMinRangeWeapon", true); this->WarpOutWeapon.Read(exINI, pSection, "WarpOutWeapon", true); this->WarpInWeapon_UseDistanceAsDamage.Read(exINI, pSection, "WarpInWeapon.UseDistanceAsDamage"); - this->WarpInWeapon_FireAsSelf.Read(exINI, pSection, "WarpInWeapon.FireAsSelf"); - this->WarpOutWeapon_FireAsSelf.Read(exINI, pSection, "WarpOutWeapon.FireAsSelf"); this->OreGathering_Anims.Read(exINI, pSection, "OreGathering.Anims"); this->OreGathering_Tiberiums.Read(exINI, pSection, "OreGathering.Tiberiums"); @@ -271,8 +269,6 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->WarpInMinRangeWeapon) .Process(this->WarpOutWeapon) .Process(this->WarpInWeapon_UseDistanceAsDamage) - .Process(this->WarpInWeapon_FireAsSelf) - .Process(this->WarpOutWeapon_FireAsSelf) .Process(this->OreGathering_Anims) .Process(this->OreGathering_Tiberiums) .Process(this->OreGathering_FramesPerDir) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index ddadc73584..a4b113e062 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -65,8 +65,6 @@ class TechnoTypeExt Nullable WarpInMinRangeWeapon; Nullable WarpOutWeapon; Valueable WarpInWeapon_UseDistanceAsDamage; - Valueable WarpInWeapon_FireAsSelf; - Valueable WarpOutWeapon_FireAsSelf; ValueableVector OreGathering_Anims; ValueableVector OreGathering_Tiberiums; @@ -158,8 +156,6 @@ class TechnoTypeExt , WarpInMinRangeWeapon {} , WarpOutWeapon {} , WarpInWeapon_UseDistanceAsDamage { false } - , WarpInWeapon_FireAsSelf { true } - , WarpOutWeapon_FireAsSelf { true } , OreGathering_Anims {} , OreGathering_Tiberiums {} , OreGathering_FramesPerDir {} diff --git a/src/Ext/TechnoType/Hooks.Teleport.cpp b/src/Ext/TechnoType/Hooks.Teleport.cpp index f0f9464684..abcd447f99 100644 --- a/src/Ext/TechnoType/Hooks.Teleport.cpp +++ b/src/Ext/TechnoType/Hooks.Teleport.cpp @@ -18,7 +18,7 @@ DEFINE_HOOK(0x719439, TeleportLocomotionClass_ILocomotion_Process_WarpoutAnim, 0 R->EDX(pExt->WarpOut.Get(RulesClass::Instance->WarpOut)); if (pExt->WarpOutWeapon.isset()) - WeaponTypeExt::DetonateAt(pExt->WarpOutWeapon.Get(), pLocomotor->LinkedTo, pExt->WarpOutWeapon_FireAsSelf ? pLocomotor->LinkedTo : nullptr); + WeaponTypeExt::DetonateAt(pExt->WarpOutWeapon.Get(), pLocomotor->LinkedTo, pLocomotor->LinkedTo); return 0x71943F; } @@ -39,7 +39,7 @@ DEFINE_HOOK(0x719788, TeleportLocomotionClass_ILocomotion_Process_WarpInAnim, 0x if (weaponType) { int damage = pExt->WarpInWeapon_UseDistanceAsDamage ? pTechnoExt->LastWarpDistance / 256 : weaponType->Damage; - WeaponTypeExt::DetonateAt(weaponType, pLocomotor->LinkedTo, pExt->WarpInWeapon_FireAsSelf ? pLocomotor->LinkedTo : nullptr, damage); + WeaponTypeExt::DetonateAt(weaponType, pLocomotor->LinkedTo, pLocomotor->LinkedTo, damage); } return 0x71978E; diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index cdb416ac05..16fc6badcb 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -44,6 +44,8 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->CanTargetHouses.Read(exINI, pSection, "CanTargetHouses"); this->Burst_Delays.Read(exINI, pSection, "Burst.Delays"); this->AreaFire_Target.Read(exINI, pSection, "AreaFire.Target"); + this->DetachedFromOwner.Read(exINI, pSection, "DetachedFromOwner"); + this->FeedbackWeapon.Read(exINI, pSection, "FeedbackWeapon", true); } template @@ -63,6 +65,8 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->RadType) .Process(this->Burst_Delays) .Process(this->AreaFire_Target) + .Process(this->DetachedFromOwner) + .Process(this->FeedbackWeapon) ; }; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 575ad47216..6fc683f0d2 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -30,6 +30,8 @@ class WeaponTypeExt Valueable CanTargetHouses; ValueableVector Burst_Delays; Valueable AreaFire_Target; + Valueable DetachedFromOwner; + Nullable FeedbackWeapon; ExtData(WeaponTypeClass* OwnerObject) : Extension(OwnerObject) , DiskLaser_Radius { 38.2 } @@ -45,6 +47,8 @@ class WeaponTypeExt , CanTargetHouses { AffectedHouse::All } , Burst_Delays {} , AreaFire_Target { AreaFireTarget::Base } + , DetachedFromOwner { false } + , FeedbackWeapon {} { } virtual ~ExtData() = default;