Skip to content
Draft
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 CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ This page lists all the individual contributions to the project by their author.
- Customize whether technos with `Locomotor=Fly` wobble
- Customize the landing animation of technos that have `Locomotor=Fly`
- Allow infantry to use `Convert.Deploy` without requiring `IsSimpleDeployer=true`
- Customize the images for building sell and undeploy
- **Ollerus**:
- Build limit group enhancement
- Customizable rocker amplitude
Expand Down
17 changes: 17 additions & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,23 @@ Overpower.ChargeWeapon=1 ; integer, negative values mean that weapons can never
Ares' [Battery Super Weapon](https://ares-developers.github.io/Ares-docs/new/superweapons/types/battery.html) won't be affected by this.
```

### Customize the images for building sell and undeploy

- In vanilla, when a building is sold or undeployed, it plays the reversed `Buildup` image. Now you can customize the images for these two cases and set whether to reverse them separately.

In `artmd.ini`:
```ini
[SOMEBUILDING] ; BuildingType
Sell= ; filename - excluding the .shp extension
Sell.Reverse=true ; boolean
Undeploy= ; filename - excluding the .shp extension
Undeploy.Reverse=true ; boolean
```

```{note}
When the replacement image is not explicitly set, the `Buildup` image will be used, and reversal can still work independently.
```

### Disable `DamageSound`

- Now you can disable `DamageSound` of a building.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ HideShakeEffects=false ; boolean
- Add `ClampToScreen` tag for `BannerType` (defaults to `true`) to control whether banner position is clamped to the visible area (by Chang_zhi)
- [Customizable Berzerk mission](New-or-Enhanced-Logics.md#enhanced-berzerk-behavior) (by TaranDahl)
- [Tank Bunker foundation and state update delay improvements](Fixed-or-Improved-Logics.md#tank-bunker-improvements) (by Starkku)
- [Customize the images for building sell and undeploy](Fixed-or-Improved-Logics.md#customize-the-images-for-building-sell-and-undeploy) (by Noble_Fish)

#### Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down
2 changes: 2 additions & 0 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@ void BuildingExt::ExtData::Serialize(T& Stm)
.Process(this->TurretAnimIdleFrame)
.Process(this->TurretAnimFiringFrame)
.Process(this->TurretAnimRateTick)
.Process(this->UseCustomSellFrames)
.Process(this->CustomSellFrameCount)
//.Process(this->IsFiringNow) It is set and reset within a same function.
;
}
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/Building/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class BuildingExt
int TurretAnimIdleFrame;
int TurretAnimFiringFrame;
int TurretAnimRateTick;
bool UseCustomSellFrames;
int CustomSellFrameCount;

ExtData(BuildingClass* OwnerObject) : Extension<BuildingClass>(OwnerObject)
, TypeExtData { nullptr }
Expand All @@ -48,6 +50,8 @@ class BuildingExt
, TurretAnimIdleFrame { 0 }
, TurretAnimFiringFrame { -1 }
, TurretAnimRateTick { 0 }
, UseCustomSellFrames { false }
, CustomSellFrameCount { 0 }
{ }

void DisplayIncomeString();
Expand Down
151 changes: 151 additions & 0 deletions src/Ext/Building/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1300,3 +1300,154 @@ DEFINE_HOOK(0x44C976, BuildingClass_Mission_Repair_TankBunker, 0x5)
}

#pragma endregion

static SHPStruct* LoadTheaterSHP(const char* baseName)
{
char filename[MAX_PATH];
_snprintf_s(filename, _TRUNCATE, "%s", baseName);

if (isalpha(static_cast<unsigned char>(filename[0])))
{
auto const c1 = static_cast<unsigned char>(filename[1]) & ~0x20;
if (c1 == 'A' || c1 == 'T')
{
auto const theater = ScenarioClass::Instance->Theater;
filename[1] = Theater::GetTheater(theater).Letter[0];
}
}

char fullPath[MAX_PATH];
_snprintf_s(fullPath, _TRUNCATE, "%s.SHP", filename);
SHPStruct* pSHP = static_cast<SHPStruct*>(FileSystem::LoadFile(fullPath, false));

if (!pSHP)
{
filename[1] = 'G';
_snprintf_s(fullPath, _TRUNCATE, "%s.SHP", filename);
pSHP = static_cast<SHPStruct*>(FileSystem::LoadFile(fullPath, false));
}

if (!pSHP)
{
_snprintf_s(fullPath, _TRUNCATE, "%s.SHP", baseName);
pSHP = static_cast<SHPStruct*>(FileSystem::LoadFile(fullPath, false));
}

return pSHP;
}

DEFINE_HOOK(0x43D2B5, BuildingClass_Draw_Sell, 0x6)
{
enum { Continue = 0x43D2C5 };

GET(BuildingClass*, pThis, ESI);
auto const pBldExt = BuildingExt::ExtMap.Find(pThis);

if (pThis->CurrentMission != Mission::Selling || pThis->BState != 0)
{
pBldExt->UseCustomSellFrames = false;
return 0;
}

auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type);
SHPStruct* pSHP = nullptr;

if (pThis->ArchiveTarget)
{
if (pTypeExt->UndeployFileName[0])
{
if (!pTypeExt->Undeploy)
pTypeExt->Undeploy = LoadTheaterSHP(pTypeExt->UndeployFileName);
pSHP = pTypeExt->Undeploy;
}
}
else
{
if (pTypeExt->SellFileName[0])
{
if (!pTypeExt->Sell)
pTypeExt->Sell = LoadTheaterSHP(pTypeExt->SellFileName);
pSHP = pTypeExt->Sell;
}
}

if (pSHP)
{
if (!pBldExt->UseCustomSellFrames)
{
pBldExt->UseCustomSellFrames = true;
int effectiveFrames = pSHP->Frames / 2;
pBldExt->CustomSellFrameCount = effectiveFrames;

double sellTimeMinutes = pTypeExt->SellTime.isset()
? pTypeExt->SellTime.Get()
: (pTypeExt->BuildupTime.isset()
? pTypeExt->BuildupTime.Get()
: RulesClass::Instance->BuildupTime);
int totalDuration = static_cast<int>(sellTimeMinutes * 900);
int newRate = Math::max(1, totalDuration / effectiveFrames);

pThis->Animation.Start(newRate);
pThis->Animation.Value = 1;
pThis->Animation.Timer.StartTime = Unsorted::CurrentFrame - newRate;
}

R->EAX(pSHP);
R->Stack(0x14, pSHP);
return Continue;
}

pBldExt->UseCustomSellFrames = false;
return 0;
}

DEFINE_HOOK(0x43F000, BuildingClass_GetCurrentFrame_Sell, 0x6)
{
enum { Continue = 0x43F029 };

GET(BuildingClass*, pThis, ESI);
auto const pBldExt = BuildingExt::ExtMap.Find(pThis);

if (!pBldExt->UseCustomSellFrames)
return 0;

auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type);
bool reverse = pThis->ArchiveTarget ? pTypeExt->Undeploy_Reverse : pTypeExt->Sell_Reverse;

int effectiveFrames = pBldExt->CustomSellFrameCount;
int value = pThis->Animation.Value;
int frameIndex = reverse ? (effectiveFrames - value - 1) : value;
if (frameIndex < 0) frameIndex = 0;
if (frameIndex >= effectiveFrames) frameIndex = effectiveFrames - 1;

R->EAX(frameIndex);
return Continue;
}

DEFINE_HOOK(0x4511F1, BuildingClass_UpdateAnimations_CustomFrameCount_End, 0x6)
{
GET(BuildingClass*, pThis, ESI);
auto const pBldExt = BuildingExt::ExtMap.Find(pThis);

int total = pThis->Type->BuildingAnimFrame[pThis->BState].dwUnknown
+ pThis->Type->BuildingAnimFrame[pThis->BState].FrameCount;

if (pBldExt->UseCustomSellFrames)
total = pBldExt->CustomSellFrameCount;

return (pThis->Animation.Value < total) ? 0x4511FC : 0x4511F7;
}

DEFINE_HOOK(0x4511A5, BuildingClass_UpdateAnimations_CustomFrameCount_Ready, 0x6)
{
GET(BuildingClass*, pThis, ESI);
auto const pBldExt = BuildingExt::ExtMap.Find(pThis);

int totalMinusOne = pThis->Type->BuildingAnimFrame[pThis->BState].dwUnknown
+ pThis->Type->BuildingAnimFrame[pThis->BState].FrameCount - 1;

if (pBldExt->UseCustomSellFrames)
totalMinusOne = pBldExt->CustomSellFrameCount - 1;

return (pThis->Animation.Value == totalMinusOne) ? 0x4511DF : 0x4511B3;
}
29 changes: 29 additions & 0 deletions src/Ext/BuildingType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,29 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->TurretAnim_LowPowerFiringFrames.Read(exINI, pSection, "TurretAnim.LowPowerFiringFrames");
this->TurretAnim_IdleRate.Read(exINI, pSection, "TurretAnim.IdleRate");
this->TurretAnim_FiringRate.Read(exINI, pSection, "TurretAnim.FiringRate");
this->BuildupTime.Read(exINI, pSection, "BuildupTime");
this->SellTime.Read(exINI, pSection, "SellTime");

if (exArtINI.ReadString(pArtSection, "Sell") > 0)
{
strcpy_s(this->SellFileName, exArtINI.value());
this->Sell = nullptr;
}
else
{
this->SellFileName[0] = '\0';
}
this->Sell_Reverse.Read(exArtINI, pArtSection, "Sell.Reverse");
if (exArtINI.ReadString(pArtSection, "Undeploy") > 0)
{
strcpy_s(this->UndeployFileName, exArtINI.value());
this->Undeploy = nullptr;
}
else
{
this->UndeployFileName[0] = '\0';
}
this->Undeploy_Reverse.Read(exArtINI, pArtSection, "Undeploy.Reverse");

if (pThis->PowersUpBuilding[0] == NULL && this->PowersUp_Buildings.size() > 0)
{
Expand Down Expand Up @@ -418,6 +441,12 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm)
.Process(this->TurretAnim_LowPowerFiringFrames)
.Process(this->TurretAnim_IdleRate)
.Process(this->TurretAnim_FiringFrames)
.Process(this->Sell)
.Process(this->Sell_Reverse)
.Process(this->Undeploy)
.Process(this->Undeploy_Reverse)
.Process(this->BuildupTime)
.Process(this->SellTime)

// Ares 0.2
.Process(this->CloningFacility)
Expand Down
20 changes: 19 additions & 1 deletion src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ class BuildingTypeExt
Valueable<int> TurretAnim_IdleRate;
Valueable<int> TurretAnim_FiringRate;

Valueable<SHPStruct*> Sell;
Valueable<bool> Sell_Reverse;
Valueable<SHPStruct*> Undeploy;
Valueable<bool> Undeploy_Reverse;
Nullable<double> BuildupTime;
Nullable<double> SellTime;
char SellFileName[0x20];
char UndeployFileName[0x20];

// Ares 0.2
Valueable<bool> CloningFacility;

Expand Down Expand Up @@ -203,6 +212,12 @@ class BuildingTypeExt
, TurretAnim_LowPowerFiringFrames { 0 }
, TurretAnim_IdleRate { 1 }
, TurretAnim_FiringRate { 1 }
, Sell { nullptr }
, Sell_Reverse { true }
, Undeploy { nullptr }
, Undeploy_Reverse { true }
, BuildupTime {}
, SellTime {}

// Ares 0.2
, CloningFacility { false }
Expand All @@ -213,7 +228,10 @@ class BuildingTypeExt

// Ares 3.0
, UnitSell {}
{ }
{
SellFileName[0] = '\0';
UndeployFileName[0] = '\0';
}

// Ares 0.A functions
int GetSuperWeaponCount() const;
Expand Down
Loading