Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/develop' into ae-enhancement
Browse files Browse the repository at this point in the history
# Conflicts:
#	docs/Whats-New.md
  • Loading branch information
Coronia committed Sep 18, 2024
2 parents f15085d + 0455a77 commit 273818b
Show file tree
Hide file tree
Showing 24 changed files with 205 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .github/actions/build-phobos/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ runs:

- name: Upload Artifact
if: ${{success()}}
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: compiled-dll-${{github.sha}}
path: |
Expand Down
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ This page lists all the individual contributions to the project by their author.
- Re-enable the Veinhole Monster and Weeds from TS
- Recreate the weed-charging of SWs like the TS Chemical Missile
- Allow to change the speed of gas particles
- **CrimRecya**
- Fix `LimboKill` not working reliably
- **Ollerus**
- Build limit group enhancement
- Customizable rocker amplitude
Expand Down
14 changes: 13 additions & 1 deletion docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- Building upgrades now consistently use building's `PowerUpN` animation settings corresponding to the upgrade's `PowersUpToLevel` where possible.
- Subterranean units are no longer allowed to perform deploy functions like firing weapons or `IsSimpleDeployer` while burrowed or burrowing, they will instead emerge first like they do for transport unloading.
- The otherwise unused setting `[AI]` -> `PowerSurplus` (defaults to 50) which determines how much surplus power AI players will strive to have can be restored by setting `[AI]` -> `EnablePowerSurplus` to true.
- Planning paths are now shown for all units under player control or when `[GlobalControls]->DebugPlanningPaths=yes` in singleplayer game modes.
- Fixed `Temporal=true` Warheads potentially crashing game if used to attack `Slaved=true` infantry.

## Fixes / interactions with other extensions

Expand Down Expand Up @@ -1253,7 +1255,7 @@ DecloakDamagedTargets=true ; boolean

- You can now make Warheads behave in nonprovocative fashion. Warheads with `Nonprovocative=true` exhibit following behaviours:
- They will not generate any EVA announcements upon hitting targets, be it for attacking ore miners, base buildings or ally base buildings.
- They will not spring 'attacked' / 'attacked by' events. Note that if the Warhead deals actual damage, events that check for that can still be sprung.
- They will not spring 'attacked' / 'attacked by' events. Note that if the Warhead deals actual damage, events that check for that can still be sprung.
- They will not evoke defense response from AI players when used to attack base buildings, `ToProtect=true` TechnoTypes or members of TeamTypes with `Whiner=true`.
- They will not evoke retaliation from TechnoTypes hit by the Warhead.

Expand Down Expand Up @@ -1333,6 +1335,16 @@ In `rulesmd.ini`
KickOutPassengers=true ; boolean
```

### Disable FireOnce resetting infantry sequence

- It is now possible to disable `FireOnce=true` weapon resetting infantry sequences after firing via `FireOnce.ResetSequence`. Target will be forgotten like before, the firing sequence will simply continue playing after firing if there are any frames left.

In `rulesmd.ini`
```ini
[SOMEWEAPON]
FireOnce.ResetSequence=true ; boolean
```

### Single-color lasers

![image](_static/images/issinglecolor.gif)
Expand Down
5 changes: 4 additions & 1 deletion docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ This page describes all the engine features that are either new and introduced b
- `AttachEffect.(Required|Disallowed)MinCounts & (Required|Disallowed)MaxCounts` can be used to set the minimum and maximum number of instances required / disallowed to be on the Techno for `Cumulative=true` types (ignored for other types) respectively.
- `AttachEffect.IgnoreFromSameSource` can be set to true to ignore effects that have been attached by the firer of the weapon and its Warhead.
- `AttachEffect.CheckOnFirer` is set to true makes it so that the required / disallowed attached effects are checked from the firer of the weapon instead of the target.

In `rulesmd.ini`:
```ini
[AttachEffectTypes]
Expand Down Expand Up @@ -348,6 +348,8 @@ Shield.AbsorbPercent= ; floating point value
Shield.PassPercent= ; floating point value
Shield.ReceivedDamage.Minimum= ; integer
Shield.ReceivedDamage.Maximum= ; integer
Shield.ReceivedDamage.MinMultiplier=1.0 ; floating point value
Shield.ReceivedDamage.MaxMultiplier=1.0 ; floating point value
Shield.Respawn.Duration=0 ; integer, game frames
Shield.Respawn.Amount=0.0 ; floating point value, percents or absolute
Shield.Respawn.Rate=-1.0 ; floating point value, ingame minutes
Expand Down Expand Up @@ -423,6 +425,7 @@ Shield.InheritStateOnReplace=false ; boolean
- `Shield.AbsorbPercent` overrides the `AbsorbPercent` value set in the ShieldType that is being damaged.
- `Shield.PassPercent` overrides the `PassPercent` value set in the ShieldType that is being damaged.
- `Shield.ReceivedDamage.Minimum` & `Shield.ReceivedDamage.Maximum` override the values set in in the ShieldType that is being damaged.
- `Shield.ReceivedDamage.MinMultiplier` and `Shield.ReceivedDamage.MinMultiplier` are multipliers to the effective `Shield.ReceivedDamage.Minimum` and `Shield.ReceivedDamage.Maximum` respectively that are applied when the Warhead deals damage to a shield.
- `Shield.Respawn.Rate` & `Shield.Respawn.Amount` override ShieldType `Respawn.Rate` and `Respawn.Amount` for duration of `Shield.Respawn.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.Respawn.RestartTimer` is set, currently running shield respawn timer is reset, otherwise the timer's duration is adjusted in proportion to the new `Shield.Respawn.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while respawn timer is running, remaining time is adjusted to proportionally match ShieldType `Respawn.Rate`. Re-applying the effect resets the duration to `Shield.Respawn.Duration`
- `Shield.SelfHealing.Rate` & `Shield.SelfHealing.Amount` override ShieldType `SelfHealing.Rate` and `SelfHealing.Amount` for duration of `Shield.SelfHealing.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.SelfHealing.RestartTimer` is set, currently running self-healing timer is restarted, otherwise timer's duration is adjusted in proportion to the new `Shield.SelfHealing.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while self-healing timer is running, remaining time is adjusted to proportionally match ShieldType `SelfHealing.Rate`. Re-applying the effect resets the duration to `Shield.SelfHealing.Duration`.
- Additionally `Shield.SelfHealing.RestartInCombat` & `Shield.SelfHealing.RestartInCombatDelay` can be used to override ShieldType settings.
Expand Down
6 changes: 4 additions & 2 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ New:
- Promotion animation (by Fryone)
- Allow different technos to share build limit in a group (by ststl & Ollerus)
- Map events `604-605` for checking if a specific Techno enters in a cell (by FS-21)
- Waypoint path is drawn for all units, even those not under player control if `DebugKeysEnabled=yes` (by Trsdy)
- Waypoint path is drawn for all units under player control or if `[GlobalControls]->DebugPlanningPaths=yes` (by Trsdy)
- `RemoveDisguise` now works on vehicle disguises (by Trsdy)
- Allow anchoring extended tooltips to the left side of the sidebar (by Trsdy)
- Toggle to allow spawned aircraft to attack immediately after being spawned (by Starkku)
Expand All @@ -450,6 +450,7 @@ New:
- Forbidding parallel AI queues for specific TechnoTypes (by Starkku)
- Nonprovocative Warheads (by Starkku)
- Option to restore `PowerSurplus` setting for AI (by Starkku)
- `FireOnce` infantry sequence reset toggle (by Starkku)
- Minimum & maximum values for received damage and speed (by Ollerus)
Vanilla fixes:
Expand Down Expand Up @@ -522,7 +523,7 @@ Vanilla fixes:
- Fixed disguised units not using the correct palette if target has custom palette (by NetsuNegi)
- Building upgrades now consistently use building's `PowerUpN` animation settings corresponding to the upgrade's `PowersUpToLevel` where possible (by Starkku)
- Subterranean units are no longer allowed to perform deploy functions like firing weapons or `IsSimpleDeployer` while burrowed or burrowing, they will instead emerge first like they do for transport unloading (by Starkku)
- Subterranean units no longer draw an incorrectly positioned shadow when burrowing etc. (by Starkku)
- Fixed `Temporal=true` Warheads potentially crashing game if used to attack `Slaved=true` infantry (by Starkku)
Phobos fixes:
- Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy)
Expand Down Expand Up @@ -564,6 +565,7 @@ Phobos fixes:
- Fixed frame by frame hotkey description to read `TXT_FRAME_BY_FRAME_DESC` instead of `TXT_DISPLAY_DAMAGE_DESC` (by DeathFishAtEase)
- Buildings considered vehicles (`ConsideredVehicle=true` or not set in conjunction with `UndeploysInto` & 1x1 foundation) are now considered units by affected target enum checks (by Starkku)
- Fixed Phobos Warhead effects not reliably being applied on damage area as opposed to full weapon-based Warhead detonation (by Starkku)
- Fix `LimboKill` not working reliably (by CrimRecya)
Fixes / interactions with other extensions:
- `IsSimpleDeployer` units with Hover locomotor and `DeployToLand` no longer get stuck after deploying or play their move sound indefinitely (by Starkku)
Expand Down
8 changes: 4 additions & 4 deletions src/Ext/Anim/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,10 @@ DEFINE_HOOK_AGAIN(0x422126, AnimClass_CTOR, 0x5)
DEFINE_HOOK_AGAIN(0x422707, AnimClass_CTOR, 0x5)
DEFINE_HOOK(0x4228D2, AnimClass_CTOR, 0x5)
{
GET(AnimClass*, pItem, ESI);

if (!Phobos::IsLoadingSaveGame)
{
GET(AnimClass*, pItem, ESI);

auto const callerAddress = CTORTemp::callerAddress;

// Do this here instead of using a duplicate hook in SyncLogger.cpp
Expand All @@ -296,10 +296,10 @@ DEFINE_HOOK(0x4228D2, AnimClass_CTOR, 0x5)
Debug::Log("Attempting to create animation with null Type (Caller: %08x)!\n", callerAddress);
return 0;
}

AnimExt::ExtMap.Allocate(pItem);
}

AnimExt::ExtMap.Allocate(pItem);

return 0;
}

Expand Down
30 changes: 14 additions & 16 deletions src/Ext/SWType/FireSuperWeapon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,6 @@ inline void LimboCreate(BuildingTypeClass* pType, HouseClass* pOwner, int ID)
}
}

inline void LimboDelete(BuildingClass* pBuilding, HouseClass* pTargetHouse)
{
auto pOwnerExt = HouseExt::ExtMap.Find(pTargetHouse);

// Remove building from list of owned limbo buildings
auto& vec = pOwnerExt->OwnedLimboDeliveredBuildings;
vec.erase(std::remove(vec.begin(), vec.end(), pBuilding), vec.end());

pBuilding->Stun();
pBuilding->Limbo();
pBuilding->RegisterDestruction(nullptr);
pBuilding->UnInit();
}

void SWTypeExt::ExtData::ApplyLimboDelivery(HouseClass* pHouse)
{
// random mode
Expand Down Expand Up @@ -174,13 +160,25 @@ void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse)
if (EnumFunctions::CanTargetHouse(this->LimboKill_Affected, pHouse, pTargetHouse))
{
auto const pHouseExt = HouseExt::ExtMap.Find(pTargetHouse);
auto& vec = pHouseExt->OwnedLimboDeliveredBuildings;

for (const auto& pBuilding : pHouseExt->OwnedLimboDeliveredBuildings)
for (auto it = vec.begin(); it != vec.end(); )
{
BuildingClass* const pBuilding = *it;
auto const pBuildingExt = BuildingExt::ExtMap.Find(pBuilding);

if (pBuildingExt->LimboID == limboKillID)
LimboDelete(pBuilding, pTargetHouse);
{
it = vec.erase(it);
pBuilding->Stun();
pBuilding->Limbo();
pBuilding->RegisterDestruction(nullptr);
pBuilding->UnInit();
}
else
{
++it;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo)
// death if listed technos exist
if (!pTypeExt->AutoDeath_TechnosExist.empty())
{
if (existTechnoTypes(pTypeExt->AutoDeath_TechnosExist, pTypeExt->AutoDeath_TechnosExist_Houses, pTypeExt->AutoDeath_TechnosExist_Any, pTypeExt->AutoDeath_TechnosDontExist_AllowLimboed))
if (existTechnoTypes(pTypeExt->AutoDeath_TechnosExist, pTypeExt->AutoDeath_TechnosExist_Houses, pTypeExt->AutoDeath_TechnosExist_Any, pTypeExt->AutoDeath_TechnosExist_AllowLimboed))
{
TechnoExt::KillSelf(pThis, howToDie, pVanishAnim, isInLimbo);

Expand Down
1 change: 1 addition & 0 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ void TechnoExt::ExtData::Serialize(T& Stm)
.Process(this->IsBurrowed)
.Process(this->HasBeenPlacedOnMap)
.Process(this->DeployFireTimer)
.Process(this->SkipTargetChangeResetSequence)
.Process(this->ForceFullRearmDelay)
.Process(this->CanCloakDuringRearm)
.Process(this->WHAnimRemainingCreationInterval)
Expand Down
2 changes: 2 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class TechnoExt
bool IsBurrowed;
bool HasBeenPlacedOnMap; // Set to true on first Unlimbo() call.
CDTimerClass DeployFireTimer;
bool SkipTargetChangeResetSequence;
bool ForceFullRearmDelay;
bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon.
int WHAnimRemainingCreationInterval;
Expand Down Expand Up @@ -75,6 +76,7 @@ class TechnoExt
, IsBurrowed { false }
, HasBeenPlacedOnMap { false }
, DeployFireTimer {}
, SkipTargetChangeResetSequence { false }
, ForceFullRearmDelay { false }
, CanCloakDuringRearm { false }
, WHAnimRemainingCreationInterval { 0 }
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/Techno/Hooks.AttachEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ DEFINE_HOOK(0x701966, TechnoClass_ArmorMultiplier, 0x6) // TechnoClass_Rec
damage = static_cast<int>(damage / pExt->AE.ArmorMultiplier) - pTypeExt->ArmorBonus - pExt->AE.ArmorBonus;

if (pShieldData && pShieldData->IsActive())
damage -= pShieldData->ArmorBonus;
damage -= pShieldData->GetType()->ArmorBonus;

R->EAX(damage);

Expand Down
16 changes: 16 additions & 0 deletions src/Ext/Techno/Hooks.Firing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,22 @@ DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6)
return 0;
}

DEFINE_HOOK(0x6FF905, TechnoClass_FireAt_FireOnce, 0x6)
{
GET(TechnoClass*, pThis, ESI);

if (auto const pInf = abstract_cast<InfantryClass*>(pThis))
{
GET(WeaponTypeClass*, pWeapon, EBX);
auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon);

if (!pWeaponExt->FireOnce_ResetSequence)
TechnoExt::ExtMap.Find(pInf)->SkipTargetChangeResetSequence = true;
}

return 0;
}

DEFINE_HOOK(0x6FF660, TechnoClass_FireAt_Interceptor, 0x6)
{
GET(TechnoClass* const, pSource, ESI);
Expand Down
21 changes: 18 additions & 3 deletions src/Ext/Techno/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,21 @@ DEFINE_HOOK(0x6F9FA9, TechnoClass_AI_PromoteAnim, 0x6)
return aresProcess();
}

// TunnelLocomotionClass_IsToHaveShadow, skip shadow on all but idle.
// TODO: Investigate if it is possible to fix the shadows not tilting on the burrowing etc. states.
DEFINE_JUMP(LJMP, 0x72A070, 0x72A07F);
DEFINE_HOOK(0x51B20E, InfantryClass_AssignTarget_FireOnce, 0x6)
{
enum { SkipGameCode = 0x51B255 };

GET(InfantryClass*, pThis, ESI);
GET(AbstractClass*, pTarget, EBX);

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

if (!pTarget && pExt->SkipTargetChangeResetSequence)
{
pThis->IsFiring = false;
pExt->SkipTargetChangeResetSequence = false;
return SkipGameCode;
}

return 0;
}
77 changes: 62 additions & 15 deletions src/Ext/TechnoType/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <JumpjetLocomotionClass.h>
#include <FlyLocomotionClass.h>
#include <RocketLocomotionClass.h>
#include <TunnelLocomotionClass.h>

DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5)
{
Expand Down Expand Up @@ -301,6 +302,60 @@ constexpr double Pade2_2(double in)
* (12. - 6 * s + s * s) / (12. + 6 * s + s * s);
}

// We need to handle Ares turrets/barrels/waterimage/nospawnalt
struct DummyExtHere // TODO: move it
{
char _[0xA4];
std::vector<VoxelStruct> ChargerTurrets;
std::vector<VoxelStruct> ChargerBarrels;
char __[0x120];
UnitTypeClass* WaterImage;
VoxelStruct NoSpawnAltVXL;
};

Matrix3D* __stdcall TunnelLocomotionClass_ShadowMatrix(ILocomotion* iloco, Matrix3D* ret,VoxelIndexKey* key)
{
__assume(iloco != nullptr);
auto tLoco = static_cast<TunnelLocomotionClass*>(iloco);
*ret = tLoco->LocomotionClass::Shadow_Matrix(key);
if (tLoco->State != TunnelLocomotionClass::State::Idle)
{
double theta = 0.;
switch (tLoco->State)
{
case TunnelLocomotionClass::State::DiggingIn:
if (key)key->Invalidate();
theta = Math::HalfPi;
if (auto total = tLoco->DigTimer.Rate)
theta *= 1.0 - double(tLoco->DigTimer.GetTimeLeft()) / double(total);
break;
case TunnelLocomotionClass::State::DugIn:
theta = Math::HalfPi;
break;
case TunnelLocomotionClass::State::PreDigOut:
theta = -Math::HalfPi;
break;
case TunnelLocomotionClass::State::DiggingOut:
if (key)key->Invalidate();
theta = -Math::HalfPi;
if (auto total = tLoco->DigTimer.Rate)
theta *= double(tLoco->DigTimer.GetTimeLeft()) / double(total);
break;
case TunnelLocomotionClass::State::DugOut:
if (key)key->Invalidate();
theta = Math::HalfPi;
if (auto total = tLoco->DigTimer.Rate)
theta *= double(tLoco->DigTimer.GetTimeLeft()) / double(total);
break;
default:break;
}
ret->ScaleX((float)Math::cos(theta));// I know it's ugly
}
return ret;
}

DEFINE_JUMP(VTABLE, 0x7F5A4C, GET_OFFSET(TunnelLocomotionClass_ShadowMatrix));

DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5)
{
GET(UnitClass*, pThis, EBP);
Expand Down Expand Up @@ -355,17 +410,6 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5)
shadow_matrix.Scale((float)Pade2_2(baseScale_log));
}

// We need to handle Ares turrets/barrels
struct DummyExtHere
{
char _[0xA4];
std::vector<VoxelStruct> ChargerTurrets;
std::vector<VoxelStruct> ChargerBarrels;
char __[0x120];
UnitTypeClass* WaterImage;
VoxelStruct NoSpawnAltVXL;
};

auto GetMainVoxel = [&]()
{
if (pType->NoSpawnAlt && pThis->SpawnManager && pThis->SpawnManager->CountDockedSpawns() == 0)
Expand Down Expand Up @@ -636,7 +680,7 @@ DEFINE_JUMP(CALL6, 0x4148AB, 0x5F4300);
DEFINE_JUMP(CALL6, 0x4147F3, 0x5F4300);
*/

DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7)
DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x6)
{
GET(FootClass*, pThis, EBX);//Maybe Techno later
GET(VoxelStruct*, pVXL, EBP);
Expand All @@ -660,9 +704,12 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7)
if (who_are_you[0] == UnitTypeClass::AbsVTable)
pType = reinterpret_cast<TechnoTypeClass*>(who_are_you);//you are someone else
else
return pThis->TurretAnimFrame % hva->FrameCount;
// you might also be SpawnAlt voxel, but I can't know
// otherwise what would you expect me to do, shift back to ares typeext base and check if ownerobject is technotype?
{
// guess what, someone actually has a multisection nospawnalt
if (!(AresHelper::CanUseAres && pVXL == &reinterpret_cast<DummyExtHere*>(pType->align_2FC)->NoSpawnAltVXL))
return pThis->TurretAnimFrame % hva->FrameCount;
}
// you might also be WaterImage or sth else, but I don't want to care anymore, go fuck yourself
}

// Main body sections
Expand Down
Loading

0 comments on commit 273818b

Please sign in to comment.