Skip to content
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
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -966,5 +966,7 @@ This page lists all the individual contributions to the project by their author.
- **obsidianus** - Automatic conversion based on health
- **Nuke** - Reload speed adjustment on promotion
- **frg2089 (舰队的偶像-岛风酱!)** - Fix `Slaved.OwnerWhenMasterKilled` not being respected when the master is sold or self-destructed
- **dh381-1(dh381)**
- Add country initial elite unit lists
- **weiyongxuan** - Extended `CanTargetHouses` to allow targeting neutral houses
- **Sovietianqi** - Customizable crew type per country
1 change: 1 addition & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
<ClCompile Include="src\Ext\House\Hooks.UnitFromFactory.cpp" />
<ClCompile Include="src\Ext\HouseType\Body.cpp" />
<ClCompile Include="src\Ext\HouseType\Hooks.cpp" />
<ClCompile Include="src\Ext\HouseType\Hooks.InitialElite.cpp" />
<!-- src\Ext\Infantry -->
<ClCompile Include="src\Ext\Infantry\Hooks.cpp" />
<ClCompile Include="src\Ext\Infantry\Hooks.Firing.cpp" />
Expand Down
23 changes: 23 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,29 @@ SpyEffect.VictimSuperWeapon= ; SuperWeaponType
SpyEffect.InfiltratorSuperWeapon= ; SuperWeaponType
```

## Countries

### Set country initial elite

- Now you can add `TechnoTypes` in the three lists. They will appear at an elite initial level when produced, and cloned infantry will also be affected by this effect.

In `rulesmd.ini`:
```ini
[SOMECOUNTRY] ; Country
EliteInfantry= ; List of InfantryTypes
EliteUnits= ; List of UnitTypes
EliteAircraft= ; List of AircraftTypes
```

- For each `TechnoType`, you can define `SpareCameo` and `SpareCameoPCX` to specify the elite‑level icon. When a `TechnoType` is added to the initial elite list, it will preferentially use the SpareCameo as its icon.

In `artmd.ini`:
```ini
[SOMETECHNO] ; TechnoType
SpareCameo= ; filename
SpareCameoPCX= ; filename - include ".pcx" extension
```

## Infantry

### Allow infantry to perform type conversion when deploying and undeploying
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ HideShakeEffects=false ; boolean
- Country-based attached effects (by Ollerus)
- [Customizable crew type per country](Fixed-or-Improved-Logics.md#customizable-crew-type-per-country) (by Sovietianqi, FlyStar, Noble_Fish)
- [Country-specific veteran buildings](Fixed-or-Improved-Logics.md#country-specific-veteran-buildings) (by Noble_Fish)
- [Set country initial elite](New-or-Enhanced-Logics.md#set-country-initial-elite) (by dh381)

#### Vanilla fixes:
- Fixed the bug where a building with `Factory=BuildingType` owned by the AI did not play `ProductionAnim` when placing a produced building (by Noble_Fish)
Expand Down
1 change: 1 addition & 0 deletions src/Ext/House/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ void HouseExt::Serialize(T& Stm)
.Process(this->ForceRadar)
.Process(this->PlayerAutoRepair)
//.Process(this->BeaconsPlacedOrder) beacon is not saved, so this follows it.
.Process(this->TechnoPCX_IsLoad)
;
}

Expand Down
3 changes: 3 additions & 0 deletions src/Ext/House/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class HouseExt final : public AbstractExt, public Detach::Listener<BuildingClass

std::array<int, 3> BeaconsPlacedOrder;

ValueableVector<TechnoTypeClass*> TechnoPCX_IsLoad;

HouseExt(HouseClass* OwnerObject) : AbstractExt(OwnerObject)
, PowerPlantEnhancers {}
, OwnedLimboDeliveredBuildings {}
Expand Down Expand Up @@ -116,6 +118,7 @@ class HouseExt final : public AbstractExt, public Detach::Listener<BuildingClass
, ForceRadar(false)
, PlayerAutoRepair(true)
, BeaconsPlacedOrder { 0, 0, 0 }
, TechnoPCX_IsLoad {}
{ }

bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const;
Expand Down
10 changes: 10 additions & 0 deletions src/Ext/HouseType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ void HouseTypeExt::LoadFromINIFile(CCINIClass* pINI)
this->AttachEffects.LoadFromINI(pINI, pSection);
this->AttachEffects_AttachOnOwnerChange.Read(exINI, pSection, "AttachEffect.AttachOnOwnerChange");

this->Crew.Read(exINI, pSection, "Crew");

this->EliteInfantry.Read(exINI, pSection, "EliteInfantry");
this->EliteUnits.Read(exINI, pSection, "EliteUnits");
this->EliteAircraft.Read(exINI, pSection, "EliteAircraft");
this->Crew.Read<true>(exINI, pSection, "Crew");

this->VeteranBuildings.Read(exINI, pSection, "VeteranBuildings");
Expand All @@ -33,9 +38,14 @@ void HouseTypeExt::Serialize(T& Stm)
{
Stm
.Process(this->EVATag)

.Process(this->AttachEffects)
.Process(this->AttachEffects_AttachOnOwnerChange)
.Process(this->Crew)

.Process(this->EliteInfantry)
.Process(this->EliteUnits)
.Process(this->EliteAircraft)
.Process(this->VeteranBuildings)
.Process(this->VeteranDefenses)
;
Expand Down
6 changes: 6 additions & 0 deletions src/Ext/HouseType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class HouseTypeExt final : public AbstractTypeExt
Nullable<bool> AttachEffects_AttachOnOwnerChange;
Nullable<InfantryTypeClass*> Crew;

ValueableVector<TechnoTypeClass*> EliteInfantry;
ValueableVector<TechnoTypeClass*> EliteUnits;
ValueableVector<TechnoTypeClass*> EliteAircraft;
ValueableVector<BuildingTypeClass*> VeteranBuildings;
ValueableVector<BuildingTypeClass*> VeteranDefenses;

Expand All @@ -40,6 +43,9 @@ class HouseTypeExt final : public AbstractTypeExt
, AttachEffects {}
, AttachEffects_AttachOnOwnerChange {}
, Crew {}
, EliteInfantry {}
, EliteUnits {}
, EliteAircraft {}
, VeteranBuildings {}
, VeteranDefenses {}
{ }
Expand Down
152 changes: 152 additions & 0 deletions src/Ext/HouseType/Hooks.InitialElite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#include "Body.h"

#include <Ext/House/Body.h>
#include <Ext/TechnoType/Body.h>

static SHPStruct* LoadSHPCameo(TechnoTypeClass* pType)
{
auto pTypeExt = TechnoTypeExt::Fetch(pType);

if(pTypeExt->SHPCameo_IsLoad)
return pTypeExt->SpareCameo;

char pFileName[0x20];

strcpy_s(pFileName, pTypeExt->SpareCameoFile.data());
_strlwr_s(pFileName);

if(!_stricmp(pFileName, pTypeExt->SpareCameoFile.data())
&& !strstr(pFileName, ".pcx"))
{
std::string Filename(pFileName);
Filename += ".shp";
SHPStruct* pSHP = FileSystem::LoadSHPFile(Filename.c_str());
pTypeExt->SHPCameo_IsLoad = true;
pTypeExt->SpareCameo = pSHP;
return pSHP;
}
return nullptr;
}

static bool DrawPCXCameo(TechnoTypeClass* pType, const int destX, const int destY)
{
auto pTypeExt = TechnoTypeExt::Fetch(pType);
char pFileName[0x20];

strcpy_s(pFileName, pTypeExt->SpareCameoPCX.GetFilename());
_strlwr_s(pFileName);

if(!_stricmp(pFileName, pTypeExt->SpareCameoPCX.GetFilename())
&& strstr(pFileName, ".pcx"))
{
PCX::Instance.LoadFile(pFileName);
if (const auto pCameoPCX = PCX::Instance.GetSurface(pFileName))
{
RectangleStruct bounds = { destX, destY, 60, 48 };
return PCX::Instance.BlitToSurface(&bounds, DSurface::Sidebar, pCameoPCX);
}
}
return false;
}

DEFINE_HOOK(0x6A980A, StripClass_Draw_DrawSHPCameo, 0x8)
{
GET(TechnoTypeClass*, pType, EBX);

auto pHouseExt = HouseExt::Fetch(HouseClass::CurrentPlayer);
auto pHouseTypeExt = HouseTypeExt::Fetch(HouseClass::CurrentPlayer->Type);

SHPStruct* pSHP = nullptr;

switch(pType->WhatAmI())
{
case AbstractType::InfantryType:
{
if(pHouseTypeExt->EliteInfantry.Contains(pType))
{
pHouseExt->TechnoPCX_IsLoad.push_back(pType);
pSHP = LoadSHPCameo(pType);
}
break;
}
case AbstractType::UnitType:
{
if(pHouseTypeExt->EliteUnits.Contains(pType))
{
pHouseExt->TechnoPCX_IsLoad.push_back(pType);
pSHP = LoadSHPCameo(pType);
}
break;
}
case AbstractType::AircraftType:
{
if(pHouseTypeExt->EliteAircraft.Contains(pType))
{
pHouseExt->TechnoPCX_IsLoad.push_back(pType);
pSHP = LoadSHPCameo(pType);
}
break;
}
default:
break;
}

if(pSHP)
R->EAX(pSHP);

return 0;
}

DEFINE_HOOK(0x6A99E7, StripClass_Draw_DrawPCXCameo, 0x6)
{
enum { SkipDrawSHP = 0x6A9A43 };

GET(const int, destX, ESI);
GET(const int, destY, EBP);
GET_STACK(SHPStruct*, pSHP, STACK_OFFSET(0x48C, -0x444));

auto pHouseExt = HouseExt::Fetch(HouseClass::CurrentPlayer);

for(auto pType : pHouseExt->TechnoPCX_IsLoad)
{
if(pSHP == pType->Cameo || pSHP == pType->AltCameo)
{
if(DrawPCXCameo(pType, destX, destY))
return SkipDrawSHP;
}
}
return 0;
}

DEFINE_HOOK(0x443C71, BuildingClass_ExitObject_CountryInitialElite, 0x6)
{
GET(TechnoClass*, pTechno, EDI);

const auto pHouseTypeExt = HouseTypeExt::Fetch(pTechno->Owner->Type);

switch(pTechno->WhatAmI())
{
case AbstractType::Infantry:
{
if(pHouseTypeExt->EliteInfantry.Contains(pTechno->GetTechnoType()))
pTechno->Veterancy.SetElite();
break;
}
case AbstractType::Unit:
{
if(pHouseTypeExt->EliteUnits.Contains(pTechno->GetTechnoType()))
pTechno->Veterancy.SetElite();
break;
}
case AbstractType::Aircraft:
{
if(pHouseTypeExt->EliteAircraft.Contains(pTechno->GetTechnoType()))
pTechno->Veterancy.SetElite();
break;
}
default:
break;
}

return 0;
}
8 changes: 8 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,9 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI)
// VoiceIFVRepair from Ares 0.2
this->VoiceIFVRepair.Read(exINI, pSection, "VoiceIFVRepair");
this->ParseVoiceWeaponAttacks(exINI, pSection, this->VoiceWeaponAttacks, this->VoiceEliteWeaponAttacks);

this->SpareCameoFile.Read(pArtINI, pArtSection, "SpareCameo");
this->SpareCameoPCX.Read(pArtINI, pArtSection, "SpareCameoPCX");
}

template <typename T>
Expand Down Expand Up @@ -1838,6 +1841,11 @@ void TechnoTypeExt::Serialize(T& Stm)
.Process(this->DecloakAnims)
.Process(this->Cloak_KickOutParasite)

.Process(this->SpareCameoFile)
.Process(this->SpareCameo)
.Process(this->SHPCameo_IsLoad)
.Process(this->SpareCameoPCX)

// Ares 0.2
.Process(this->RadarJamRadius)

Expand Down
11 changes: 11 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,12 @@ class TechnoTypeExt : public ObjectTypeExt
Nullable<bool> Unsellable;
Nullable<bool> KeepAlive;

PhobosFixedString<0x20> SpareCameoFile;
SHPStruct* SpareCameo;
bool SHPCameo_IsLoad;
PhobosPCXFile SpareCameoPCX;


TechnoTypeExt(TechnoTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject)
, HealthBar_Hide { false }
, HealthBar_HidePips { false }
Expand Down Expand Up @@ -855,6 +861,11 @@ class TechnoTypeExt : public ObjectTypeExt
, DecloakAnims {}
, Cloak_KickOutParasite {}

, SpareCameoFile { NONE_STR }
, SpareCameo { nullptr }
, SHPCameo_IsLoad { false }
, SpareCameoPCX {}

// Ares 0.2
, RadarJamRadius { 0 }

Expand Down