From 1aec6bc35d001c0e08d612e8e2833db51e45e4af Mon Sep 17 00:00:00 2001 From: obsidianus Date: Sun, 2 Aug 2026 16:31:10 +0800 Subject: [PATCH 1/3] Different theater, various unit image --- src/Ext/Techno/Hooks.cpp | 29 ++++++++++++++++++++---- src/Ext/Unit/Body.cpp | 46 +++++++++++++++++++++++++++++++++++++++ src/Ext/Unit/Body.h | 1 + src/Ext/UnitType/Body.cpp | 13 +++++++++++ src/Ext/UnitType/Body.h | 13 +++++++++++ 5 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 1f0a256ce3..6832d5bad0 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -795,10 +795,19 @@ DEFINE_HOOK(0x73B4DA, UnitClass_DrawVXL_WaterType_Extra, 0x6) const auto pTypeExt = UnitTypeExt::Fetch(pThis->Type); - if (pTypeExt->NeedDamagedImage && pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer) && !pThis->Deployed) + if (pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer) && !pThis->Deployed) { - if (const auto pCustomType = UnitExt::GetUnitTypeExtra(pThis, pTypeExt)) + if (const auto pCustomType = UnitExt::GetUnitType(pThis, pTypeExt)) + { R->EBX(pCustomType); + } + if (pTypeExt->NeedDamagedImage) + { + if (const auto pCustomType = UnitExt::GetUnitTypeExtra(pThis, pTypeExt)) + { + R->EBX(pCustomType); + } + } } return 0; @@ -813,12 +822,24 @@ DEFINE_HOOK(0x73C602, UnitClass_DrawSHP_WaterType_Extra, 0x6) const auto pType = pThis->Type; const auto pTypeExt = UnitTypeExt::Fetch(pType); - if (pTypeExt->NeedDamagedImage && pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer) && !pThis->Deployed) + if (pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer) && !pThis->Deployed) { - if (const auto pCustomType = UnitExt::GetUnitTypeExtra(pThis, pTypeExt)) + if (const auto pCustomType = UnitExt::GetUnitType(pThis, pTypeExt)) { if (const auto Image = pCustomType->GetImage()) + { R->EAX(Image); + } + } + if (pTypeExt->NeedDamagedImage) + { + if (const auto pCustomType = UnitExt::GetUnitTypeExtra(pThis, pTypeExt)) + { + if (const auto Image = pCustomType->GetImage()) + { + R->EAX(Image); + } + } } } diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index baac35745d..a3a9b1d08c 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -342,6 +342,52 @@ bool UnitExt::CanDeployIntoBuilding(UnitClass* pThis, bool noDeploysIntoDefaultV return canDeploy; } +UnitTypeClass* UnitExt::GetUnitType(UnitClass* pUnit, UnitTypeExt* pData) +{ + switch (ScenarioClass::Instance->Theater) + { + case TheaterType::Temperate: + if (const auto image = pData->Image_Temperate) + { + return image; + } + break; + case TheaterType::Snow: + if (const auto image = pData->Image_Snow) + { + return image; + } + break; + case TheaterType::Urban: + if (const auto image = pData->Image_Urban) + { + return image; + } + break; + case TheaterType::Desert: + if (const auto image = pData->Image_Desert) + { + return image; + } + break; + case TheaterType::NewUrban: + if (const auto image = pData->Image_NewUrban) + { + return image; + } + break; + case TheaterType::Lunar: + if (const auto image = pData->Image_Lunar) + { + return image; + } + break; + default: + break; + } + return nullptr; +} + UnitTypeClass* UnitExt::GetUnitTypeExtra(UnitClass* pUnit, UnitTypeExt* pData) { if (pUnit->IsGreenHP()) diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h index f314b6ef7b..b513a2af37 100644 --- a/src/Ext/Unit/Body.h +++ b/src/Ext/Unit/Body.h @@ -58,6 +58,7 @@ class UnitExt final : public FootExt static bool SimpleDeployerAllowedToDeploy(UnitClass* pThis, bool defaultValue, bool alwaysCheckLandTypes); static bool CanDeployIntoBuilding(UnitClass* pThis, bool noDeploysIntoDefaultValue = false); static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit, UnitTypeExt* pData); + static UnitTypeClass* GetUnitType(UnitClass* pUnit, UnitTypeExt* pData); UnitClass* OwnerObject() const { diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index 5f6cd33986..fb1f6d66d6 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -88,6 +88,13 @@ void UnitTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->Deploy_NoTiberium.Read(exINI, pSection, "Deploy.NoTiberium"); this->HoverDrownable.Read(exINI, pSection, "HoverDrownable"); + this->Image_Temperate.Read(exINI, pSection, "Image.Temperate"); + this->Image_Snow.Read(exINI, pSection, "Image.Snow"); + this->Image_Urban.Read(exINI, pSection, "Image.Urban"); + this->Image_Desert.Read(exINI, pSection, "Image.Desert"); + this->Image_NewUrban.Read(exINI, pSection, "Image.NewUrban"); + this->Image_Lunar.Read(exINI, pSection, "Image.Lunar"); + const auto pArtINI = &CCINIClass::INI_Art; INI_EX exArtINI(pArtINI); auto pArtSection = pThis->ImageFile; @@ -192,6 +199,12 @@ void UnitTypeExt::Serialize(T& Stm) .Process(this->Deploy_NoPassenger) .Process(this->Deploy_NoTiberium) .Process(this->HoverDrownable) + .Process(this->Image_Temperate) + .Process(this->Image_Snow) + .Process(this->Image_Urban) + .Process(this->Image_Desert) + .Process(this->Image_NewUrban) + .Process(this->Image_Lunar) .Process(this->TurretShape) .Process(this->BarrelOverTurret) .Process(this->BarrelOffset) diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h index f232759a20..b8f8cabe96 100644 --- a/src/Ext/UnitType/Body.h +++ b/src/Ext/UnitType/Body.h @@ -83,6 +83,13 @@ class UnitTypeExt final : public TechnoTypeExt std::vector ExtraTurretOffsets; Valueable BurstPerTurret; + Nullable Image_Temperate; + Nullable Image_Snow; + Nullable Image_Urban; + Nullable Image_Desert; + Nullable Image_NewUrban; + Nullable Image_Lunar; + explicit UnitTypeExt(UnitTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) , SinkSpeed {} , Sinkable {} @@ -143,6 +150,12 @@ class UnitTypeExt final : public TechnoTypeExt , ExtraTurretCount { 0 } , ExtraTurretOffsets { } , BurstPerTurret { 0 } + , Image_Temperate {} + , Image_Snow {} + , Image_Urban {} + , Image_Desert {} + , Image_NewUrban {} + , Image_Lunar {} { } UnitTypeClass* OwnerObject() const From b986e861767e242ab1a40d6d676c026fb9f311b7 Mon Sep 17 00:00:00 2001 From: obsidianus Date: Sun, 2 Aug 2026 17:19:50 +0800 Subject: [PATCH 2/3] Edited documents --- CREDITS.md | 2 ++ docs/New-or-Enhanced-Logics.md | 20 ++++++++++++++++++++ docs/Whats-New.md | 1 + 3 files changed, 23 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 61295f2ce5..d63694037f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -917,3 +917,5 @@ This page lists all the individual contributions to the project by their author. - **Chang_zhi**: - Interop export interface for accessing scenario local/global variables - Add `ClampToScreen` tag for `BannerType` to control whether banner position is clamped to the visible area +- **obsidianus** + - Unit image changes in various theater types diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 1cef4bcfe4..447605eacd 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2539,6 +2539,26 @@ WaterImage.ConditionRed= ; VehicleType entry Note that the VehicleTypes had to be defined under [VehicleTypes] and use same image type (SHP/VXL) for vanilla/damaged states. ``` +### Unit image changes in various theater types + +- Units on maps of different theater types can now change their models to the corresponding theater-specific models. +- The INI tags are shown below. + +In `rulesmd.ini`: +```ini +[SOMEVEHICLE] ; VehicleType +Image.Temperate= ; VehicleType entry +Image.Snow= ; VehicleType entry +Image.Urban= ; VehicleType entry +Image.Desert= ; VehicleType entry +Image.NewUrban= ; VehicleType entry +Image.Lunar= ; VehicleType entry +``` + +```{warning} +Note that the VehicleTypes had to be defined under [VehicleTypes] and use same image type (SHP/VXL) for vanilla/damaged states. +``` + ### Default mirage disguise for individual VehicleTypes - Vehicle can now have its `DefaultMirageDisguises` overridden per-type. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 5562162d1a..c8196f56b2 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -646,6 +646,7 @@ HideShakeEffects=false ; boolean - Separately define the global default values of TerrainTypes' `IsPassable` and `CanBeBuiltOn` based on `SpawnsTiberium` (by Noble_Fish) - Customize reveal radius of `RevealToAll` (by NetsuNegi) - [Customize whether aircraft is a cargo plane](Fixed-or-Improved-Logics.md#customize-whether-aircraft-is-a-cargo-plane) (by TaranDahl) +- [Unit image changes in various theater types](New-or-Enhanced-Logics.md#unit-image-changes-in-various-theater-types) (by obsidianus) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From 26dc56a8f56c11ff3a70b23aa88f4a7d9e45245d Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Tue, 4 Aug 2026 08:33:32 +0800 Subject: [PATCH 3/3] update CREDITS --- CREDITS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 9e8deca87c..817a722983 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -921,6 +921,6 @@ This page lists all the individual contributions to the project by their author. - **Chang_zhi**: - Interop export interface for accessing scenario local/global variables - Add `ClampToScreen` tag for `BannerType` to control whether banner position is clamped to the visible area -- **obsidianus** - Automatic conversion based on health -- **obsidianus** +- **obsidianus**: + - Automatic conversion based on health - Unit image changes in various theater types