Skip to content
Merged
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
1 change: 1 addition & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- Fixed an issue that technos head to building's dock even they are not going to dock.
- Fixed an issue that the jumpjet vehicles cannot stop correctly after going berserk.
- Fixed the issue where Ares' `Flash.Duration` cannot override the weapon's repair flash effect.
- Fixed an issue where the game would only use `Weapon1` and `Weapon2` for auto-targeting even when `MultiWeapon=yes` was set.

```{note}
The described behavior is a replica of and is compliant with XNA CnCNet Client's multiplayer save game support.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ Phobos fixes:
- Fixed an issue where the 77 trigger event in Ares was not functioning properly (by NetsuNegi)
- Fixed an interaction error between the engineer and the Ares rubble (by FlyStar)
- Fixed the projection location of selectbox when over elevated bridge (by NetsuNegi)
- Fixed an issue where the game would only use `Weapon1` and `Weapon2` for auto-targeting even when `MultiWeapon=yes` was set (by FlyStar)

Fixes / interactions with other extensions:
- Allowed `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades (by Ollerus)
Expand Down
2 changes: 2 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ void RulesExt::LoadAfterTypeData(RulesClass* pThis, CCINIClass* pINI)
// Spawner range
if (pTechnoTypeExt->Spawner_LimitRange)
pTechnoTypeExt->CalculateSpawnerRange();

pTechnoTypeExt->UpdateAdditionalAttributes();
}
}

Expand Down
55 changes: 51 additions & 4 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,26 +139,26 @@ bool TechnoTypeExt::ExtData::IsSecondary(int nWeaponIndex)

int TechnoTypeExt::ExtData::SelectMultiWeapon(TechnoClass* const pThis, AbstractClass* const pTarget)
{
if (!pTarget || !this->MultiWeapon.Get())
if (!pTarget || !this->MultiWeapon)
return -1;

const auto pType = this->OwnerObject();

if (pType->IsGattling || (pType->HasMultipleTurrets() && pType->Gunner))
return -1;

const int weaponCount = Math::min(pType->WeaponCount, this->MultiWeapon_SelectCount.Get());
const int weaponCount = Math::min(pType->WeaponCount, this->MultiWeapon_SelectCount);
const bool noSecondary = this->NoSecondaryWeaponFallback;

if (weaponCount < 2)
return 0;
else if (weaponCount == 2)
else if (weaponCount == 2 && !noSecondary)
return -1;

std::vector<bool> secondaryCanTargets {};
secondaryCanTargets.resize(weaponCount, false);

const bool isElite = pThis->Veterancy.IsElite();
const bool noSecondary = this->NoSecondaryWeaponFallback.Get();

if (const auto pTargetTechno = abstract_cast<TechnoClass*, true>(pTarget))
{
Expand Down Expand Up @@ -362,6 +362,53 @@ void TechnoTypeExt::ExtData::ParseVoiceWeaponAttacks(INI_EX& exINI, const char*
}
}

void TechnoTypeExt::ExtData::UpdateAdditionalAttributes()
{
int num = 0;
int eliteNum = 0;

this->ThreatTypes = { ThreatType::Normal,ThreatType::Normal };
this->CombatDamages = { 0,0 };

const auto pThis = this->OwnerObject();
int count = 2;

if (this->MultiWeapon
&& (!pThis->IsGattling && (!pThis->HasMultipleTurrets() || !pThis->Gunner)))
{
count = pThis->WeaponCount;
}

for (int index = 0; index < count; index++)
{
const auto pWeapon = pThis->GetWeapon(index)->WeaponType;
auto pEliteWeapon = pThis->GetEliteWeapon(index)->WeaponType;

if (!pEliteWeapon)
pEliteWeapon = pWeapon;

if (pWeapon)
{
this->ThreatTypes.X |= pWeapon->AllowedThreats();
this->CombatDamages.X += (pWeapon->Damage + pWeapon->AmbientDamage);
num++;
}

if (pEliteWeapon)
{
this->ThreatTypes.Y |= pEliteWeapon->AllowedThreats();
this->CombatDamages.Y += (pEliteWeapon->Damage + pEliteWeapon->AmbientDamage);
eliteNum++;
}
}

if (num > 0)
this->CombatDamages.X /= num;

if (eliteNum > 0)
this->CombatDamages.Y /= eliteNum;
}

void TechnoTypeExt::ExtData::CalculateSpawnerRange()
{
const auto pThis = this->OwnerObject();
Expand Down
6 changes: 6 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,8 @@ class TechnoTypeExt
ValueableVector<bool> MultiWeapon_IsSecondary;
Valueable<int> MultiWeapon_SelectCount;
bool ReadMultiWeapon;
Vector2D<ThreatType> ThreatTypes;
Vector2D<int> CombatDamages;

ValueableIdx<VocClass> VoiceIFVRepair;
ValueableVector<int> VoiceWeaponAttacks;
Expand Down Expand Up @@ -804,6 +806,8 @@ class TechnoTypeExt
, MultiWeapon_IsSecondary {}
, MultiWeapon_SelectCount { 2 }
, ReadMultiWeapon { false }
, ThreatTypes { ThreatType::Normal,ThreatType::Normal }
, CombatDamages { 0,0 }

, VoiceIFVRepair { -1 }
, VoiceWeaponAttacks {}
Expand Down Expand Up @@ -832,6 +836,8 @@ class TechnoTypeExt
int SelectForceWeapon(TechnoClass* pThis, AbstractClass* pTarget);
int SelectMultiWeapon(TechnoClass* const pThis, AbstractClass* const pTarget);

void UpdateAdditionalAttributes();

// Ares 0.A
const char* GetSelectionGroupID() const;

Expand Down
117 changes: 117 additions & 0 deletions src/Ext/TechnoType/Hooks.MultiWeapon.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "Body.h"
#include <Randomizer.h>

#include <Ext/Techno/Body.h>

DEFINE_HOOK(0x7128B2, TechnoTypeClass_ReadINI_MultiWeapon, 0x6)
{
GET(TechnoTypeClass*, pThis, EBP);
Expand Down Expand Up @@ -141,3 +143,118 @@ DEFINE_HOOK(0x7090A0, TechnoClass_VoiceAttack, 0x7)

return 0x7091C7;
}

ThreatType __forceinline GetThreatType(TechnoClass* pThis, TechnoTypeExt::ExtData* pTypeExt, ThreatType result)
{
const ThreatType flags = pThis->Veterancy.IsElite() ? pTypeExt->ThreatTypes.Y : pTypeExt->ThreatTypes.X;
return result | flags;
}

DEFINE_HOOK_AGAIN(0x51E2CF, FootClass_SelectAutoTarget_MultiWeapon, 0x6) // InfantryClass_SelectAutoTarget
DEFINE_HOOK(0x7431C9, FootClass_SelectAutoTarget_MultiWeapon, 0x7) // UnitClass_SelectAutoTarget
{
enum { InfantryReturn = 0x51E31B, UnitReturn = 0x74324F, UnitGunner = 0x7431E4 };

GET(FootClass*, pThis, ESI);
GET(const ThreatType, result, EDI);

const bool isUnit = R->Origin() == 0x7431C9;
const auto pType = pThis->GetTechnoType();
const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType);

if (isUnit
&& !pType->IsGattling && pType->TurretCount > 0
&& (pType->Gunner || !pTypeExt->MultiWeapon))
{
return UnitGunner;
}

R->EDI(GetThreatType(pThis, pTypeExt, result));
return isUnit ? UnitReturn : InfantryReturn;
}

DEFINE_HOOK(0x445F04, BuildingClass_SelectAutoTarget_MultiWeapon, 0xA)
{
enum { ReturnThreatType = 0x445F58, Continue = 0x445F0E };

GET(BuildingClass*, pThis, ESI);
GET_STACK(const ThreatType, result, STACK_OFFSET(0x8, 0x4));

if (pThis->UpgradeLevel > 0 || pThis->CanOccupyFire())
{
R->EAX(pThis->GetWeapon(0));
return Continue;
}

R->EDI(GetThreatType(pThis, TechnoTypeExt::ExtMap.Find(pThis->Type), result));
return ReturnThreatType;
}

DEFINE_HOOK(0x6F398E, TechnoClass_CombatDamage_MultiWeapon, 0x7)
{
enum { ReturnDamage = 0x6F3ABB, GunnerDamage = 0x6F39AD, Continue = 0x6F39F4 };

GET(TechnoClass*, pThis, ESI);

const AbstractType rtti = pThis->WhatAmI();

if (rtti == AbstractType::Building)
{
const auto pBuilding = static_cast<BuildingClass*>(pThis);

if (pBuilding->UpgradeLevel > 0 || pBuilding->CanOccupyFire())
return Continue;
}

const auto pType = pThis->GetTechnoType();
const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType);

if (rtti == AbstractType::Unit
&& !pType->IsGattling && pType->TurretCount > 0
&& (pType->Gunner || !pTypeExt->MultiWeapon))
{
return GunnerDamage;
}

R->EAX(pThis->Veterancy.IsElite() ? pTypeExt->CombatDamages.Y : pTypeExt->CombatDamages.X);
return ReturnDamage;
}

DEFINE_HOOK(0x707ED0, TechnoClass_GetGuardRange_MultiWeapon, 0x6)
{
enum { ReturnRange = 0x707F08 };

GET(TechnoClass*, pThis, ESI);

const auto pType = pThis->GetTechnoType();
const bool specialWeapon = !pType->IsGattling && (!pType->HasMultipleTurrets() || !pType->Gunner);

if (!pType->IsGattling && pType->TurretCount > 0
&& (pType->Gunner || !specialWeapon)
&& pThis->WhatAmI() == AbstractType::Unit)
{
R->EAX(pThis->GetWeaponRange(pThis->CurrentWeaponNumber));
return ReturnRange;
}

const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType);

if (pTypeExt->MultiWeapon && specialWeapon)
{
const int selectCount = Math::min(pType->WeaponCount, pTypeExt->MultiWeapon_SelectCount);
int range = 0;

for (int index = selectCount - 1; index >= 0; --index)
{
const auto weaponRange = pThis->GetWeaponRange(index);

if (weaponRange > range)
range = weaponRange;
}

R->EAX(range);
return ReturnRange;
}

return 0;
}