From 5e17992c19c20b0a291ed8a2f784fe98c1f9e987 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 27 Jun 2026 19:26:28 +0800 Subject: [PATCH 01/30] feat: add Unit Passengers Display - Show passenger cameo icons and counts above vehicles when selected - Toggle via hotkey Toggle Unit Passengers with CSF-configurable messages - Two modes: Permanent (ShowUnitPassengers=true) and Toggleable (ShowUnitPassengers.Toggleable=true) - Icons use CAMEO_PAL, arranged bottom-to-top with fixed bottom boundary New INI tags: [AudioVisual] ShowUnitPassengers, ShowUnitPassengers.Toggleable, ShowUnitPassengers.EnabledMessage, ShowUnitPassengers.DisabledMessage [TechnoType] ShowPassengers, ShowPassengers.Toggleable, Passengers.PerRow, Passengers.BottomOffset --- CREDITS.md | 1 + Phobos.vcxproj | 2 + docs/User-Interface.md | 40 +++++++++ docs/Whats-New.md | 5 ++ src/Commands/Commands.cpp | 2 + src/Commands/ToggleUnitPassengers.cpp | 56 ++++++++++++ src/Commands/ToggleUnitPassengers.h | 13 +++ src/Ext/Rules/Body.cpp | 8 ++ src/Ext/Rules/Body.h | 10 ++- src/Ext/Techno/Body.Visuals.cpp | 120 ++++++++++++++++++++++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.Pips.cpp | 2 + src/Ext/TechnoType/Body.cpp | 10 +++ src/Ext/TechnoType/Body.h | 10 +++ src/Phobos.INI.cpp | 1 + src/Phobos.h | 1 + 16 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/Commands/ToggleUnitPassengers.cpp create mode 100644 src/Commands/ToggleUnitPassengers.h diff --git a/CREDITS.md b/CREDITS.md index b712b4adb7..77accc3e7e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -821,3 +821,4 @@ This page lists all the individual contributions to the project by their author. - Multiplayer gamespeed fix for RealTimeTimers - Revert Ares patch to allow OpenTopped transport customization - Fix for units with Fly, Jumpjet or Rocket locomotors crashing off-map not being cleaned up +- **ABCZ (CZHczhABCZ)** - Unit passengers display diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 5d2d15eca0..775b144295 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -45,6 +45,7 @@ + @@ -303,6 +304,7 @@ + diff --git a/docs/User-Interface.md b/docs/User-Interface.md index efdb80975b..a11eb2e320 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -487,6 +487,41 @@ In `RA2MD.INI`: ShowPowerPlantEnhancerRange=false ; boolean ``` +### Unit Passengers Display + +- You can now configure the game to display the passengers contained within a vehicle above it when selected. Each passenger type is shown as its Cameo icon with a count number in the upper-right corner. +- Two activation modes: + - **Permanent mode**: Set `ShowUnitPassengers=true` (global) or `ShowPassengers=true` (per-vehicle) to always show the display, regardless of the hotkey state. + - **Toggleable mode**: Set `ShowUnitPassengers.Toggleable=true` (global) and use the [Toggle Unit Passengers](#toggle-unit-passengers) hotkey to switch the display on/off. Per-vehicle `ShowPassengers.Toggleable` can override the global setting. +- When the feature is toggled via the hotkey, a message is shown in the top-left corner of the screen. The message text can be customized via CSF labels. +- Icons are arranged in horizontal rows from left to right, with the bottom row fixed at the bottom boundary. If the number of passenger types exceeds `Passengers.PerRow`, additional rows are drawn above the bottom row. + +In `rulesmd.ini`: +```ini +[AudioVisual] +ShowUnitPassengers=false ; boolean, permanently shows passengers for all vehicles (ignores hotkey). Defaults to false +ShowUnitPassengers.Toggleable=false ; boolean, allows hotkey to toggle the display. Defaults to false +ShowUnitPassengers.EnabledMessage= ; CSF entry key, message shown when the feature is enabled via hotkey +ShowUnitPassengers.DisabledMessage= ; CSF entry key, message shown when the feature is disabled via hotkey + +[SOMETECHNO] ; TechnoType +ShowPassengers= ; boolean, permanently show/hide for this vehicle (overrides global). Not set = follow global +ShowPassengers.Toggleable= ; boolean, override global toggleable setting for this vehicle. Not set = follow global +Passengers.PerRow=5 ; integer, max icons per row before wrapping, defaults to 5 +Passengers.BottomOffset=0,0 ; X,Y, bottom boundary offset from selection bracket, defaults to 0,0 +``` + +In `RA2MD.CSF`: + +; Hotkey UI labels +TXT_UNIT_PASSENGERS Toggle Unit Passengers +TXT_UNIT_PASSENGERS_DESC Show/hide unit passengers display. + +; Optional: customize the enabled/disabled messages +; (must match the labels set in ShowUnitPassengers.EnabledMessage / DisabledMessage) +MSG:UnitPassengersEnabled Unit Passengers display: Enabled +MSG:UnitPassengersDisabled Unit Passengers display: Disabled + ## Hotkey Commands ### `[ ]` Display Damage Numbers @@ -519,6 +554,11 @@ ShowPowerPlantEnhancerRange=false ; boolean - Switches on/off [digital display types](#digital-display). - For localization add `TXT_DIGITAL_DISPLAY` and `TXT_DIGITAL_DISPLAY_DESC` into your `.csf` file. +### `[ ]` Toggle Unit Passengers + +- Switches on/off [unit passengers display](#unit-passengers-display). +- For localization add `TXT_UNIT_PASSENGERS` and `TXT_UNIT_PASSENGERS_DESC` into your `.csf` file. + ### `[ ]` Next Idle Harvester - Selects and centers the camera on the next TechnoType that is counted via the [harvester counter](#harvester-counter) and is currently idle. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 84368953ce..34c75c3d7b 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -29,6 +29,11 @@ This serves as a changelog for when you just need to drop the new version in wit ### Version TBD (develop branch nightly builds) +#### New: + +- New unit passengers display feature - show passenger cameo icons and counts above vehicles when selected (see [User Interface](User-Interface.md#unit-passengers-display)) +- New hotkey `Toggle Unit Passengers` to enable/disable the passengers display, with customizable CSF messages +- New INI tags: `[AudioVisual] -> ShowUnitPassengers`, `ShowUnitPassengers.Toggleable`, `ShowUnitPassengers.EnabledMessage`, `ShowUnitPassengers.DisabledMessage`, `[TechnoType] -> ShowPassengers`, `ShowPassengers.Toggleable`, `Passengers.PerRow`, `Passengers.BottomOffset` - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 14b9044f56..e82368e5dc 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -7,6 +7,7 @@ #include "FrameByFrame.h" #include "FrameStep.h" #include "ToggleDigitalDisplay.h" +#include "ToggleUnitPassengers.h" #include "ToggleDesignatorRange.h" #include "SaveVariablesToFile.h" #include "ToggleSWSidebar.h" @@ -27,6 +28,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); + MakeCommand(); MakeCommand(); MakeCommand(); MakeCommand(); diff --git a/src/Commands/ToggleUnitPassengers.cpp b/src/Commands/ToggleUnitPassengers.cpp new file mode 100644 index 0000000000..52992d2426 --- /dev/null +++ b/src/Commands/ToggleUnitPassengers.cpp @@ -0,0 +1,56 @@ +#include "ToggleUnitPassengers.h" + +#include +#include +#include +#include + +const char* ToggleUnitPassengersCommandClass::GetName() const +{ + return "Toggle Unit Passengers"; +} + +const wchar_t* ToggleUnitPassengersCommandClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS", L"Toggle Unit Passengers"); +} + +const wchar_t* ToggleUnitPassengersCommandClass::GetUICategory() const +{ + return CATEGORY_INTERFACE; +} + +const wchar_t* ToggleUnitPassengersCommandClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS_DESC", L"Show/hide unit passengers display."); +} + +void ToggleUnitPassengersCommandClass::Execute(WWKey eInput) const +{ + Phobos::Config::UnitPassengers_Enable = !Phobos::Config::UnitPassengers_Enable; + + auto PrintMessage = [](const wchar_t* pMessage) + { + MessageListClass::Instance.PrintMessage( + pMessage, + RulesClass::Instance->MessageDelay, + HouseClass::CurrentPlayer->ColorSchemeIndex, + true + ); + }; + + const auto pRulesExt = RulesExt::Global(); + + if (Phobos::Config::UnitPassengers_Enable) + { + const auto& msg = pRulesExt->ShowUnitPassengers_EnabledMessage; + if (msg.isset()) + PrintMessage(GeneralUtils::LoadStringUnlessMissing(msg.Get().Label, L"Unit Passengers display: Enabled")); + } + else + { + const auto& msg = pRulesExt->ShowUnitPassengers_DisabledMessage; + if (msg.isset()) + PrintMessage(GeneralUtils::LoadStringUnlessMissing(msg.Get().Label, L"Unit Passengers display: Disabled")); + } +} \ No newline at end of file diff --git a/src/Commands/ToggleUnitPassengers.h b/src/Commands/ToggleUnitPassengers.h new file mode 100644 index 0000000000..c0888371d6 --- /dev/null +++ b/src/Commands/ToggleUnitPassengers.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Commands.h" + +class ToggleUnitPassengersCommandClass : public CommandClass +{ +public: + virtual const char* GetName() const override; + virtual const wchar_t* GetUIName() const override; + virtual const wchar_t* GetUICategory() const override; + virtual const wchar_t* GetUIDescription() const override; + virtual void Execute(WWKey eInput) const override; +}; \ No newline at end of file diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 898fe3acc5..8449f27cc1 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -366,6 +366,10 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->SortCameoByName.Read(exINI, GameStrings::General, "SortCameoByName"); this->MergeBuildingDamage.Read(exINI, GameStrings::CombatDamage, "MergeBuildingDamage"); + this->ShowUnitPassengers.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers"); + this->ShowUnitPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.Toggleable"); + this->ShowUnitPassengers_EnabledMessage.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.EnabledMessage"); + this->ShowUnitPassengers_DisabledMessage.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.DisabledMessage"); this->ApplyPerTargetEffectsOnDetonate.Read(exINI, GameStrings::CombatDamage, "ApplyPerTargetEffectsOnDetonate"); @@ -769,6 +773,10 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->MindControl_Permanent_ReplaceSilent) .Process(this->TeamDelays_DynamicType) .Process(this->TeamDelays_Count) + .Process(this->ShowUnitPassengers) + .Process(this->ShowUnitPassengers_Toggleable) + .Process(this->ShowUnitPassengers_EnabledMessage) + .Process(this->ShowUnitPassengers_DisabledMessage) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 2de853da1f..fe26e0213f 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -194,6 +194,11 @@ class RulesExt Valueable MergeBuildingDamage; + Valueable ShowUnitPassengers; + Valueable ShowUnitPassengers_Toggleable; + Nullable ShowUnitPassengers_EnabledMessage; + Nullable ShowUnitPassengers_DisabledMessage; + Valueable DamageOwnerMultiplier; Valueable DamageAlliesMultiplier; Valueable DamageEnemiesMultiplier; @@ -664,9 +669,12 @@ class RulesExt , DiscardOn_MoveBasedOnDestination { false } , RemoveMindControl_Silent { false } , MindControl_Permanent_ReplaceSilent { false } - , TeamDelays_DynamicType { DynamicTeamDelayType::StartingPoint } , TeamDelays_Count {} + , ShowUnitPassengers { false } + , ShowUnitPassengers_Toggleable { false } + , ShowUnitPassengers_EnabledMessage {} + , ShowUnitPassengers_DisabledMessage {} { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index ff47accb46..e98ca3b1c3 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -3,6 +3,7 @@ #include #include #include +#include void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds) { @@ -873,3 +874,122 @@ void TechnoExt::ShowPromoteAnim(TechnoClass* pThis) else if (!eliteAnims.empty()) AnimExt::CreateRandomAnim(eliteAnims, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true); } + +void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) +{ + if (ObjectClass::CurrentObjects.Count != 1) + return; + + if (ObjectClass::CurrentObjects.GetItem(0) != pThis) + return; + + const auto whatAmI = pThis->WhatAmI(); + if (whatAmI != AbstractType::Unit && whatAmI != AbstractType::Aircraft) + return; + + const auto pType = pThis->GetTechnoType(); + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pRulesExt = RulesExt::Global(); + + bool shouldShow = false; + + if (pTypeExt->ShowPassengers.isset()) + { + shouldShow = pTypeExt->ShowPassengers.Get(); + } + else if (pRulesExt->ShowUnitPassengers) + { + shouldShow = true; + } + else + { + const bool toggleable = pTypeExt->ShowPassengers_Toggleable.Get(pRulesExt->ShowUnitPassengers_Toggleable); + if (toggleable && Phobos::Config::UnitPassengers_Enable) + shouldShow = true; + } + + if (!shouldShow) + return; + + if (pThis->Passengers.NumPassengers <= 0) + return; + + std::map passengerCounts; + for (auto pPassenger = pThis->Passengers.GetFirstPassenger(); pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) + { + passengerCounts[pPassenger->GetTechnoType()]++; + } + + if (passengerCounts.empty()) + return; + + const auto bracketPos = GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); + const auto& bottomOffset = pTypeExt->Passengers_BottomOffset; + Point2D basePos = bracketPos; + basePos.X += bottomOffset.Get().X; + basePos.Y += bottomOffset.Get().Y + pType->PixelSelectionBracketDelta; + + const double scale = 1.0; + + const int perRow = pTypeExt->Passengers_PerRow; + const int iconWidth = static_cast(60 * scale); + const int iconHeight = static_cast(48 * scale); + const int hGap = 2; + const int vGap = 2; + + const int count = static_cast(passengerCounts.size()); + const int rows = (count + perRow - 1) / perRow; + + const auto pSurface = DSurface::Composite; + RectangleStruct bounds = DSurface::Composite->GetRect(); + + int idx = 0; + for (const auto& item : passengerCounts) + { + const auto pPassengerType = item.first; + const int passengerCount = item.second; + + const int row = idx / perRow; + const int col = idx % perRow; + + const int itemsInThisRow = (row == rows - 1 && count % perRow != 0) ? (count % perRow) : perRow; + const int rowWidth = itemsInThisRow * (iconWidth + hGap) - hGap; + const int startX = basePos.X - rowWidth / 2; + + const int y = basePos.Y - iconHeight - (rows - 1 - row) * (iconHeight + vGap); + + Point2D iconPos = { startX + col * (iconWidth + hGap), y }; + + SHPStruct* pCameo = pPassengerType->GetCameo(); + if (pCameo) + { + pSurface->DrawSHP( + FileSystem::CAMEO_PAL, + pCameo, + 0, + &iconPos, + &bounds, + BlitterFlags::bf_400 | BlitterFlags::Alpha, + 0, 0, + ZGradient::Ground, + 1000, 0, nullptr, 0, 0, 0 + ); + + wchar_t countText[16]; + _snwprintf_s(countText, _countof(countText), _TRUNCATE, L"%d", passengerCount); + + Point2D textPos = { iconPos.X + iconWidth - 4, iconPos.Y + 2 }; + + pSurface->DrawTextA( + countText, + &bounds, + &textPos, + Drawing::RGB_To_Int({ 0, 255, 0 }), + 0, + TextPrintType::Center | TextPrintType::FullShadow + ); + } + + idx++; + } +} diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 08a7061b96..fad93c7d06 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -296,6 +296,7 @@ class TechnoExt static Point2D GetBuildingSelectBracketPosition(TechnoClass* pThis, BuildingSelectBracketPosition bracketPosition); static void DrawSelectBox(TechnoClass* pThis, const Point2D* pLocation, const RectangleStruct* pBounds, bool drawBefore = false); static void ProcessDigitalDisplays(TechnoClass* pThis); + static void DrawUnitPassengers(TechnoClass* pThis); static void GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, DisplayInfoType infoType, int& value, int& maxValue, int infoIndex); static void GetDigitalDisplayFakeHealth(TechnoClass* pThis, int& value, int& maxValue); static void CreateDelayedFireAnim(TechnoClass* pThis, AnimTypeClass* pAnimType, int weaponIndex, bool attach, bool center, bool removeOnNoDelay, bool onTurret, CoordStruct firingCoords); diff --git a/src/Ext/Techno/Hooks.Pips.cpp b/src/Ext/Techno/Hooks.Pips.cpp index 075299f870..e96e906b3f 100644 --- a/src/Ext/Techno/Hooks.Pips.cpp +++ b/src/Ext/Techno/Hooks.Pips.cpp @@ -93,6 +93,7 @@ DEFINE_HOOK(0x6F65D1, TechnoClass_DrawHealthBar_Buildings, 0x6) } TechnoExt::ProcessDigitalDisplays(pThis); + TechnoExt::DrawUnitPassengers(pThis); return 0; } @@ -123,6 +124,7 @@ DEFINE_HOOK(0x6F683C, TechnoClass_DrawHealthBar_Units, 0x7) } TechnoExt::ProcessDigitalDisplays(pThis); + TechnoExt::DrawUnitPassengers(pThis); if (pExt->TypeExtData->HealthBar_HidePips) { diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 26ac38f475..b49cc30a3d 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -988,6 +988,11 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->DigitalDisplay_Disable.Read(exINI, pSection, "DigitalDisplay.Disable"); this->DigitalDisplayTypes.Read(exINI, pSection, "DigitalDisplayTypes"); + this->ShowPassengers.Read(exINI, pSection, "ShowPassengers"); + this->ShowPassengers_Toggleable.Read(exINI, pSection, "ShowPassengers.Toggleable"); + this->Passengers_PerRow.Read(exINI, pSection, "Passengers.PerRow"); + this->Passengers_BottomOffset.Read(exINI, pSection, "Passengers.BottomOffset"); + this->SelectBox.Read(exINI, pSection, "SelectBox"); this->HideSelectBox.Read(exINI, pSection, "HideSelectBox"); @@ -1713,6 +1718,11 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->DigitalDisplay_Disable) .Process(this->DigitalDisplayTypes) + .Process(this->ShowPassengers) + .Process(this->ShowPassengers_Toggleable) + .Process(this->Passengers_PerRow) + .Process(this->Passengers_BottomOffset) + .Process(this->SelectBox) .Process(this->HideSelectBox) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index f796500382..aaea54c759 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -267,6 +267,11 @@ class TechnoTypeExt Valueable DigitalDisplay_Disable; ValueableVector DigitalDisplayTypes; + Nullable ShowPassengers; + Nullable ShowPassengers_Toggleable; + Valueable Passengers_PerRow; + Valueable Passengers_BottomOffset; + Nullable SelectBox; Valueable HideSelectBox; @@ -749,6 +754,11 @@ class TechnoTypeExt , DigitalDisplay_Disable { false } , DigitalDisplayTypes {} + , ShowPassengers {} + , ShowPassengers_Toggleable {} + , Passengers_PerRow { 5 } + , Passengers_BottomOffset { { 0, 0 } } + , SelectBox {} , HideSelectBox { false } diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 8c450eb4d9..601dc594db 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -55,6 +55,7 @@ bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::EnableSelectBox = false; bool Phobos::Config::DigitalDisplay_Enable = false; +bool Phobos::Config::UnitPassengers_Enable = false; bool Phobos::Config::MessageApplyHoverState = false; bool Phobos::Config::MessageDisplayInCenter = false; int Phobos::Config::MessageDisplayInCenter_BoardOpacity = 40; diff --git a/src/Phobos.h b/src/Phobos.h index 1ec2645d83..75a29b439d 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -90,6 +90,7 @@ class Phobos static bool EnableBuildingPlacementPreview; static bool EnableSelectBox; static bool DigitalDisplay_Enable; + static bool UnitPassengers_Enable; static bool MessageApplyHoverState; static bool MessageDisplayInCenter; static int MessageDisplayInCenter_BoardOpacity; From acf66e8fc5abb9d27c0cd1f3f76cdd2fc16577f5 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 17:18:07 +0800 Subject: [PATCH 02/30] docs: refine documentation formatting per review --- docs/User-Interface.md | 11 +++++------ docs/Whats-New.md | 6 +++--- src/Commands/Commands.cpp | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 351aee713a..20b1c652f6 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -493,19 +493,18 @@ ShowPowerPlantEnhancerRange=false ; boolean - Two activation modes: - **Permanent mode**: Set `ShowUnitPassengers=true` (global) or `ShowPassengers=true` (per-vehicle) to always show the display, regardless of the hotkey state. - **Toggleable mode**: Set `ShowUnitPassengers.Toggleable=true` (global) and use the [Toggle Unit Passengers](#toggle-unit-passengers) hotkey to switch the display on/off. -- When the feature is toggled via the hotkey, the messages are read from CSF labels `MSG:UnitPassengersEnabled` and `MSG:UnitPassengersDisabled`. - Icons are arranged in horizontal rows from left to right, with the bottom row fixed at the bottom boundary. If the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are drawn above the bottom row. In `rulesmd.ini`: ```ini [AudioVisual] -ShowUnitPassengers=false ; boolean, permanently shows passengers for all vehicles (ignores hotkey). Defaults to false -ShowUnitPassengers.Toggleable=false ; boolean, allows hotkey to toggle the display. Defaults to false +ShowUnitPassengers=false ; boolean +ShowUnitPassengers.Toggleable=false ; boolean [SOMETECHNO] ; TechnoType -ShowPassengers= ; boolean, permanently show/hide for this vehicle (overrides global). Not set = follow global -ShowPassengers.PerRow=5 ; integer, max icons per row before wrapping, defaults to 5 -ShowPassengers.BottomOffset=0,0 ; X,Y, bottom boundary offset from selection bracket, defaults to 0,0 +ShowPassengers= ; boolean, defaults to [AudioVisual] -> ShowUnitPassengers +ShowPassengers.PerRow=5 ; integer +ShowPassengers.BottomOffset=0,0 ; X,Y ``` ## Hotkey Commands diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 271a19adbd..3b1838eada 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -31,9 +31,9 @@ This serves as a changelog for when you just need to drop the new version in wit #### New: -- New unit passengers display feature - show passenger cameo icons and counts above vehicles when selected (see [User Interface](User-Interface.md#unit-passengers-display)) -- New hotkey `Toggle Unit Passengers` to enable/disable the passengers display, with customizable CSF messages -- New INI tags: `[AudioVisual] -> ShowUnitPassengers`, `ShowUnitPassengers.Toggleable`, `[TechnoType] -> ShowPassengers`, `ShowPassengers.PerRow`, `ShowPassengers.BottomOffset` +- New unit passengers display feature (see [User Interface](User-Interface.md#unit-passengers-display)) +- Hotkey `Toggle Unit Passengers` to toggle the passengers display +- New INI tags: `[AudioVisual] -> ShowUnitPassengers`, `ShowUnitPassengers.Toggleable`; `[TechnoType] -> ShowPassengers`, `ShowPassengers.PerRow`, `ShowPassengers.BottomOffset` - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index e82368e5dc..eb40d2940d 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -4,10 +4,10 @@ #include "NextIdleHarvester.h" #include "QuickSave.h" #include "DamageDisplay.h" +#include "ToggleUnitPassengers.h" #include "FrameByFrame.h" #include "FrameStep.h" #include "ToggleDigitalDisplay.h" -#include "ToggleUnitPassengers.h" #include "ToggleDesignatorRange.h" #include "SaveVariablesToFile.h" #include "ToggleSWSidebar.h" @@ -27,8 +27,8 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); - MakeCommand(); MakeCommand(); + MakeCommand(); MakeCommand(); MakeCommand(); MakeCommand(); From f8694e2662c341e09cff6f600df772a7726a8fe0 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 17:27:56 +0800 Subject: [PATCH 03/30] fix: address review feedback - Move ToggleUnitPassengers include and MakeCommand to end of lists - Place CameoPCX.Read before AltCameoPCX.Read per Ares convention - Cache bottomOffset.Get() to avoid repeated calls - Remove redundant passengerCounts.empty() check (already guards on NumPassengers) --- src/Commands/Commands.cpp | 4 ++-- src/Ext/Techno/Body.Visuals.cpp | 8 +++----- src/Ext/TechnoType/Body.cpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index eb40d2940d..ac2d4aa25c 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -4,7 +4,6 @@ #include "NextIdleHarvester.h" #include "QuickSave.h" #include "DamageDisplay.h" -#include "ToggleUnitPassengers.h" #include "FrameByFrame.h" #include "FrameStep.h" #include "ToggleDigitalDisplay.h" @@ -15,6 +14,7 @@ #include "ToggleMessageList.h" #include "DeselectObject.h" #include "DeselectObject5.h" +#include "ToggleUnitPassengers.h" #include @@ -27,13 +27,13 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); - MakeCommand(); MakeCommand(); MakeCommand(); MakeCommand(); MakeCommand(); MakeCommand(); MakeCommand(); + MakeCommand(); if (Phobos::Config::SuperWeaponSidebarCommands) { diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 07dfb6c39c..ee8d5c11a2 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -914,14 +914,12 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) passengerCounts[pPassenger->GetTechnoType()]++; } - if (passengerCounts.empty()) - return; - const auto bracketPos = GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); const auto& bottomOffset = pTypeExt->ShowPassengers_BottomOffset; + auto& offset = bottomOffset.Get(); Point2D basePos = bracketPos; - basePos.X += bottomOffset.Get().X; - basePos.Y += bottomOffset.Get().Y + pType->PixelSelectionBracketDelta; + basePos.X += offset.X; + basePos.Y += offset.Y + pType->PixelSelectionBracketDelta; const int perRow = pTypeExt->ShowPassengers_PerRow; const int iconWidth = 60; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 27289c9d8f..9a123fe94b 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1332,8 +1332,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShadowIndex_Frame.Read(exArtINI, pArtSection, "ShadowIndex.Frame"); - this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); this->CameoPCX.Read(pArtINI, pArtSection, "CameoPCX"); + this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); this->LaserTrailData.clear(); for (size_t i = 0; ; ++i) From 929925e31902741a38548703eee06109de9b09f2 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 18:57:23 +0800 Subject: [PATCH 04/30] docs: update CSF fallback text and shorten Whats-New entry - Rename fallback strings to 'Unit Passenger Icons' per review feedback - Merge three Whats-New lines into one summary line with anchor link --- docs/User-Interface.md | 4 ++-- docs/Whats-New.md | 2 -- src/Commands/ToggleUnitPassengers.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 20b1c652f6..eca300b50b 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -542,8 +542,8 @@ ShowPassengers.BottomOffset=0,0 ; X,Y ### `[ ]` Toggle Unit Passengers - Switches on/off [unit passengers display](#unit-passengers-display). -- When toggled, messages are shown from CSF labels `MSG:UnitPassengersEnabled` and `MSG:UnitPassengersDisabled` (fallback: "Unit Passengers display: Enabled" / "Unit Passengers display: Disabled"). -- For localization add `TXT_UNIT_PASSENGERS` (fallback: "Toggle Unit Passengers") and `TXT_UNIT_PASSENGERS_DESC` (fallback: "Show/hide unit passengers display.") into your `.csf` file. +- When toggled, messages are shown from CSF labels `MSG:UnitPassengersEnabled` and `MSG:UnitPassengersDisabled` (fallback: "Unit Passenger Icons Display: Enabled" / "Unit Passenger Icons Display: Disabled"). +- For localization add `TXT_UNIT_PASSENGERS` (fallback: "Toggle Unit Passenger Icons") and `TXT_UNIT_PASSENGERS_DESC` (fallback: "Show/hide unit passenger icons display.") into your `.csf` file. ### `[ ]` Next Idle Harvester diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 3b1838eada..8384924262 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -32,8 +32,6 @@ This serves as a changelog for when you just need to drop the new version in wit #### New: - New unit passengers display feature (see [User Interface](User-Interface.md#unit-passengers-display)) -- Hotkey `Toggle Unit Passengers` to toggle the passengers display -- New INI tags: `[AudioVisual] -> ShowUnitPassengers`, `ShowUnitPassengers.Toggleable`; `[TechnoType] -> ShowPassengers`, `ShowPassengers.PerRow`, `ShowPassengers.BottomOffset` - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. diff --git a/src/Commands/ToggleUnitPassengers.cpp b/src/Commands/ToggleUnitPassengers.cpp index 50578176dd..0e26a2ee3d 100644 --- a/src/Commands/ToggleUnitPassengers.cpp +++ b/src/Commands/ToggleUnitPassengers.cpp @@ -11,7 +11,7 @@ const char* ToggleUnitPassengersCommandClass::GetName() const const wchar_t* ToggleUnitPassengersCommandClass::GetUIName() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS", L"Toggle Unit Passengers"); + return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS", L"Toggle Unit Passenger Icons"); } const wchar_t* ToggleUnitPassengersCommandClass::GetUICategory() const @@ -21,7 +21,7 @@ const wchar_t* ToggleUnitPassengersCommandClass::GetUICategory() const const wchar_t* ToggleUnitPassengersCommandClass::GetUIDescription() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS_DESC", L"Show/hide unit passengers display."); + return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS_DESC", L"Show/hide unit passenger icons display."); } void ToggleUnitPassengersCommandClass::Execute(WWKey eInput) const @@ -39,7 +39,7 @@ void ToggleUnitPassengersCommandClass::Execute(WWKey eInput) const }; if (Phobos::Config::UnitPassengers_Enable) - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:UnitPassengersEnabled", L"Unit Passengers display: Enabled")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:UnitPassengersEnabled", L"Unit Passenger Icons Display: Enabled")); else - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:UnitPassengersDisabled", L"Unit Passengers display: Disabled")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:UnitPassengersDisabled", L"Unit Passenger Icons Display: Disabled")); } \ No newline at end of file From 833bc8272c260e9873c3d2c092d5293ebfdac1f4 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Sun, 28 Jun 2026 19:00:54 +0800 Subject: [PATCH 05/30] [doc] Correct the writing of Changelog --- docs/Whats-New.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 8384924262..bccbf8fa61 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -29,9 +29,6 @@ This serves as a changelog for when you just need to drop the new version in wit ### Version TBD (develop branch nightly builds) -#### New: - -- New unit passengers display feature (see [User Interface](User-Interface.md#unit-passengers-display)) - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. @@ -600,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) +- [New unit passengers display feature](User-Interface.md#unit-passengers-display) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From 7e442ae6d07472d8aaa1f2ec20813b018b60773c Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 19:06:49 +0800 Subject: [PATCH 06/30] docs: move unit passengers display entry from Breaking changes to Changelog New features belong in Changelog > #### New:, not Breaking changes --- docs/Whats-New.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index bccbf8fa61..64775efd17 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -29,6 +29,8 @@ This serves as a changelog for when you just need to drop the new version in wit ### Version TBD (develop branch nightly builds) +#### New: + - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. @@ -409,6 +411,7 @@ HideShakeEffects=false ; boolean - Shield armor inheritance customization (by Ollerus) - [Damaged unit image changes](New-or-Enhanced-Logics.md#damaged-unit-image-changes) (by Fryone) - [Customizable spawns queue](New-or-Enhanced-Logics.md#customizable-spawns-queue) (by TwinkleStar) +- [Unit passengers display](User-Interface.md#unit-passengers-display) (by ABCZ) - [Initial spawns number](New-or-Enhanced-Logics.md#initial-spawns-number) (by TwinkleStar) - [Override target under EMP attack behavior](New-or-Enhanced-Logics.md#forcing-specific-weapon-against-certain-targets) (By FS-21) - [Aircraft spawner recycling improvements (custom offset, animation etc.)](New-or-Enhanced-Logics.md#aircraft-spawner-customizations) (by TaranDahl) @@ -597,7 +600,6 @@ 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) -- [New unit passengers display feature](User-Interface.md#unit-passengers-display) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From 4853568925595a8cf74de0e78d1d691e88714122 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 19:08:20 +0800 Subject: [PATCH 07/30] fix: add FootClass cast guard for buildings and RA2MD.INI config - Add abstract_cast guard to prevent crash on BuildingType passengers - Read UnitPassengers.Enable from [Phobos] section in RA2MD.INI - Use pFoot instead of pThis for GetFootSelectBracketPosition --- src/Ext/Techno/Body.Visuals.cpp | 6 +++++- src/Phobos.INI.cpp | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index ee8d5c11a2..79cd0ba9ec 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -883,6 +883,10 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) if (ObjectClass::CurrentObjects.GetItem(0) != pThis) return; + const auto pFoot = abstract_cast(pThis); + if (!pFoot) + return; + const auto pType = pThis->GetTechnoType(); const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); const auto pRulesExt = RulesExt::Global(); @@ -914,7 +918,7 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) passengerCounts[pPassenger->GetTechnoType()]++; } - const auto bracketPos = GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); + const auto bracketPos = GetFootSelectBracketPosition(pFoot, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); const auto& bottomOffset = pTypeExt->ShowPassengers_BottomOffset; auto& offset = bottomOffset.Get(); Point2D basePos = bracketPos; diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 601dc594db..2096d6e1d9 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -104,6 +104,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "RealTimeTimers.Adaptive", false); Phobos::Config::EnableSelectBox = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "EnableSelectBox", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "DigitalDisplay.Enable", false); + Phobos::Config::UnitPassengers_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "UnitPassengers.Enable", false); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "SaveGameOnScenarioStart", true); Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowBriefing", true); Phobos::Config::ShowPowerDelta = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPowerDelta", true); From 42d0d0b8669b2e8a1fc59aec054ae7060a60f7fc Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 19:17:24 +0800 Subject: [PATCH 08/30] Revert "docs: move unit passengers display entry from Breaking changes to Changelog" This reverts commit 7e442ae6d07472d8aaa1f2ec20813b018b60773c. --- docs/Whats-New.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 64775efd17..bccbf8fa61 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -29,8 +29,6 @@ This serves as a changelog for when you just need to drop the new version in wit ### Version TBD (develop branch nightly builds) -#### New: - - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. @@ -411,7 +409,6 @@ HideShakeEffects=false ; boolean - Shield armor inheritance customization (by Ollerus) - [Damaged unit image changes](New-or-Enhanced-Logics.md#damaged-unit-image-changes) (by Fryone) - [Customizable spawns queue](New-or-Enhanced-Logics.md#customizable-spawns-queue) (by TwinkleStar) -- [Unit passengers display](User-Interface.md#unit-passengers-display) (by ABCZ) - [Initial spawns number](New-or-Enhanced-Logics.md#initial-spawns-number) (by TwinkleStar) - [Override target under EMP attack behavior](New-or-Enhanced-Logics.md#forcing-specific-weapon-against-certain-targets) (By FS-21) - [Aircraft spawner recycling improvements (custom offset, animation etc.)](New-or-Enhanced-Logics.md#aircraft-spawner-customizations) (by TaranDahl) @@ -600,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) +- [New unit passengers display feature](User-Interface.md#unit-passengers-display) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From db52a4f7a1b9d62da962233f39962ae41e3c100b Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 20:20:37 +0800 Subject: [PATCH 09/30] feat: add CameoPalette support for unit passenger display --- src/Ext/Techno/Body.Visuals.cpp | 3 ++- src/Ext/TechnoType/Body.cpp | 2 ++ src/Ext/TechnoType/Body.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 79cd0ba9ec..9ec29905d5 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -969,8 +969,9 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) SHPStruct* pCameo = pPassengerType->GetCameo(); if (pCameo) { + const auto pConvert = pPassengerTypeExt->CameoPalette.GetOrDefaultConvert(FileSystem::CAMEO_PAL); pSurface->DrawSHP( - FileSystem::CAMEO_PAL, + pConvert, pCameo, 0, &iconPos, diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 9a123fe94b..210fbce724 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1333,6 +1333,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShadowIndex_Frame.Read(exArtINI, pArtSection, "ShadowIndex.Frame"); this->CameoPCX.Read(pArtINI, pArtSection, "CameoPCX"); + this->CameoPalette.LoadFromINI(pArtINI, pArtSection, "CameoPalette"); this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); this->LaserTrailData.clear(); @@ -1727,6 +1728,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->ShowPassengers_PerRow) .Process(this->ShowPassengers_BottomOffset) .Process(this->CameoPCX) + .Process(this->CameoPalette) .Process(this->SelectBox) .Process(this->HideSelectBox) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index df2ef94443..04618e7357 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -271,6 +271,7 @@ class TechnoTypeExt Valueable ShowPassengers_PerRow; Valueable ShowPassengers_BottomOffset; PhobosPCXFile CameoPCX; + CustomPalette CameoPalette; Nullable SelectBox; Valueable HideSelectBox; @@ -763,6 +764,7 @@ class TechnoTypeExt , ShowPassengers_PerRow { 5 } , ShowPassengers_BottomOffset { { 0, 0 } } , CameoPCX {} + , CameoPalette {} , SelectBox {} , HideSelectBox { false } From 92365a567450e789853dfd8692762122fa2c9d5b Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 21:16:29 +0800 Subject: [PATCH 10/30] style: fix code placement and formatting per review - Move ShowUnitPassengers reads to end of Rules/Body.cpp LoadFromINIFile - Add blank line between BunkerStateUpdateDelay and ShowUnitPassengers in Rules/Body.h - Move DrawUnitPassengers declaration after ApplyKillDriver in Techno/Body.h - Ensure proper blank line spacing in Hooks.Pips.cpp --- src/Ext/Rules/Body.cpp | 5 ++--- src/Ext/Rules/Body.h | 1 + src/Ext/Techno/Body.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index efaa146af8..673257ef86 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -366,9 +366,6 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->SortCameoByName.Read(exINI, GameStrings::General, "SortCameoByName"); this->MergeBuildingDamage.Read(exINI, GameStrings::CombatDamage, "MergeBuildingDamage"); - this->ShowUnitPassengers.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers"); - this->ShowUnitPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.Toggleable"); - this->ApplyPerTargetEffectsOnDetonate.Read(exINI, GameStrings::CombatDamage, "ApplyPerTargetEffectsOnDetonate"); this->AffectsInvokerOnly_IgnoreInvokerState.Read(exINI, GameStrings::CombatDamage, "AffectsInvokerOnly.IgnoreInvokerState"); @@ -446,6 +443,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) } this->BerzerkMission.Read(exINI, GameStrings::CombatDamage, "BerzerkMission"); + this->ShowUnitPassengers.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers"); + this->ShowUnitPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.Toggleable"); // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index dabe0083ca..86b2cb8caa 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -692,6 +692,7 @@ class RulesExt , BerzerkMission { Mission::Hunt } , BunkerStateUpdateDelay { 15 } + , ShowUnitPassengers { false } , ShowUnitPassengers_Toggleable { false } { } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 1ffc8b82af..8b6d94cde3 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -301,7 +301,6 @@ class TechnoExt static Point2D GetBuildingSelectBracketPosition(TechnoClass* pThis, BuildingSelectBracketPosition bracketPosition); static void DrawSelectBox(TechnoClass* pThis, const Point2D* pLocation, const RectangleStruct* pBounds, bool drawBefore = false); static void ProcessDigitalDisplays(TechnoClass* pThis); - static void DrawUnitPassengers(TechnoClass* pThis); static void GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, DisplayInfoType infoType, int& value, int& maxValue, int infoIndex); static void GetDigitalDisplayFakeHealth(TechnoClass* pThis, int& value, int& maxValue); static void CreateDelayedFireAnim(TechnoClass* pThis, AnimTypeClass* pAnimType, int weaponIndex, bool attach, bool center, bool removeOnNoDelay, bool onTurret, CoordStruct firingCoords); @@ -321,6 +320,7 @@ class TechnoExt static bool EjectRandomly(FootClass* pEjectee, const CoordStruct& coords, int distance, bool select); static bool EjectSurvivor(FootClass* pSurvivor, CoordStruct coords, bool select); static bool __fastcall ApplyKillDriver(TechnoClass** pData, void*, HouseClass* pToHouse, TechnoClass* pKiller, bool resetVeterancy); + static void DrawUnitPassengers(TechnoClass* pThis); // WeaponHelpers.cpp static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true); From ee05b3af3d39a7bc13368d436eb72dcc33c2ed4c Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 21:31:55 +0800 Subject: [PATCH 11/30] style: add blank lines between unrelated declarations and calls --- src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.Pips.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 8b6d94cde3..ba76ff36ca 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -320,6 +320,7 @@ class TechnoExt static bool EjectRandomly(FootClass* pEjectee, const CoordStruct& coords, int distance, bool select); static bool EjectSurvivor(FootClass* pSurvivor, CoordStruct coords, bool select); static bool __fastcall ApplyKillDriver(TechnoClass** pData, void*, HouseClass* pToHouse, TechnoClass* pKiller, bool resetVeterancy); + static void DrawUnitPassengers(TechnoClass* pThis); // WeaponHelpers.cpp diff --git a/src/Ext/Techno/Hooks.Pips.cpp b/src/Ext/Techno/Hooks.Pips.cpp index e96e906b3f..8e6f665386 100644 --- a/src/Ext/Techno/Hooks.Pips.cpp +++ b/src/Ext/Techno/Hooks.Pips.cpp @@ -93,6 +93,7 @@ DEFINE_HOOK(0x6F65D1, TechnoClass_DrawHealthBar_Buildings, 0x6) } TechnoExt::ProcessDigitalDisplays(pThis); + TechnoExt::DrawUnitPassengers(pThis); return 0; @@ -124,6 +125,7 @@ DEFINE_HOOK(0x6F683C, TechnoClass_DrawHealthBar_Units, 0x7) } TechnoExt::ProcessDigitalDisplays(pThis); + TechnoExt::DrawUnitPassengers(pThis); if (pExt->TypeExtData->HealthBar_HidePips) From 074849c1af031d68a2e535e2474b363bf0574e1f Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 28 Jun 2026 21:45:57 +0800 Subject: [PATCH 12/30] style: fix whitespace in Rules/Body.cpp --- src/Ext/Rules/Body.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 673257ef86..3e235fcc38 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -366,6 +366,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->SortCameoByName.Read(exINI, GameStrings::General, "SortCameoByName"); this->MergeBuildingDamage.Read(exINI, GameStrings::CombatDamage, "MergeBuildingDamage"); + this->ApplyPerTargetEffectsOnDetonate.Read(exINI, GameStrings::CombatDamage, "ApplyPerTargetEffectsOnDetonate"); this->AffectsInvokerOnly_IgnoreInvokerState.Read(exINI, GameStrings::CombatDamage, "AffectsInvokerOnly.IgnoreInvokerState"); @@ -389,7 +390,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->PenetratesTransport_Level.Read(exINI, GameStrings::CombatDamage, "PenetratesTransport.Level"); this->UnitsUnsellable.Read(exINI, GameStrings::General, "UnitsUnsellable"); - + this->DisableOveroptimizationInTargeting.Read(exINI, GameStrings::General, "DisableOveroptimizationInTargeting"); this->DriverKilled_KillPassengers.Read(exINI, GameStrings::CombatDamage, "DriverKilled.KillPassengers"); @@ -405,7 +406,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->DriveLocomotorMakesWake.Read(exINI, GameStrings::AudioVisual, "DriveLocomotorMakesWake"); this->HoverLocomotorMakesWake.Read(exINI, GameStrings::AudioVisual, "HoverLocomotionClassMakesWake"); this->ShipLocomotorMakesWake.Read(exINI, GameStrings::AudioVisual, "ShipLocomotionClassMakesWake"); - + this->FiringAnim_Update.Read(exINI, GameStrings::AudioVisual, "FiringAnim.Update"); this->ExtendedPlayerRepair.Read(exINI, GameStrings::General, "ExtendedPlayerRepair"); @@ -443,6 +444,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) } this->BerzerkMission.Read(exINI, GameStrings::CombatDamage, "BerzerkMission"); + this->ShowUnitPassengers.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers"); this->ShowUnitPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.Toggleable"); From 801330c64525908dbf24bcf369e5901d459c404a Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 13:18:23 +0800 Subject: [PATCH 13/30] refactor: rename UnitPassengers to Passengers, add global PerRow/BottomOffset, fix display order and screen bounds --- CREDITS.md | 2 +- Phobos.vcxproj | 4 +- YRpp | 2 +- docs/User-Interface.md | 30 ++++++------ docs/Whats-New.md | 2 +- src/Commands/Commands.cpp | 4 +- src/Commands/TogglePassengers.cpp | 45 +++++++++++++++++ ...gleUnitPassengers.h => TogglePassengers.h} | 2 +- src/Commands/ToggleUnitPassengers.cpp | 45 ----------------- src/Ext/Rules/Body.cpp | 16 +++--- src/Ext/Rules/Body.h | 14 ++++-- src/Ext/Techno/Body.Visuals.cpp | 49 +++++++++++++------ src/Ext/Techno/Body.h | 2 +- src/Ext/Techno/Hooks.Pips.cpp | 4 +- src/Ext/TechnoType/Body.cpp | 4 +- src/Ext/TechnoType/Body.h | 12 ++--- src/Phobos.INI.cpp | 4 +- src/Phobos.h | 2 +- 18 files changed, 136 insertions(+), 107 deletions(-) create mode 100644 src/Commands/TogglePassengers.cpp rename src/Commands/{ToggleUnitPassengers.h => TogglePassengers.h} (83%) delete mode 100644 src/Commands/ToggleUnitPassengers.cpp diff --git a/CREDITS.md b/CREDITS.md index a4ef71b7aa..5bc278c313 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -877,4 +877,4 @@ 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 -- **ABCZ (CZHczhABCZ)** - Unit passengers display +- **ABCZ (CZHczhABCZ)** - Passengers display diff --git a/Phobos.vcxproj b/Phobos.vcxproj index dfc16df97e..b555139ea8 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -45,7 +45,7 @@ - + @@ -309,7 +309,7 @@ - + diff --git a/YRpp b/YRpp index 85b5b5e185..f304f4fa13 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 85b5b5e1857a6e06e1341dc87d01c38b1104272e +Subproject commit f304f4fa13bdaab0495a6c847a91bf8af2b9d932 diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 80b9843b87..26ddd2cb48 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -526,24 +526,26 @@ In `RA2MD.INI`: ShowPowerPlantEnhancerRange=false ; boolean ``` -### Unit Passengers Display +### Passengers Display -- You can now configure the game to display the passengers contained within a vehicle above it when selected. Each passenger type is shown as its Cameo icon with a count number in the upper-right corner. +- You can now configure the game to display the passengers contained within a vehicle or building above it when selected. Each passenger type is shown as its Cameo icon with a count number in the upper-right corner. - Two activation modes: - - **Permanent mode**: Set `ShowUnitPassengers=true` (global) or `ShowPassengers=true` (per-vehicle) to always show the display, regardless of the hotkey state. - - **Toggleable mode**: Set `ShowUnitPassengers.Toggleable=true` (global) and use the [Toggle Unit Passengers](#toggle-unit-passengers) hotkey to switch the display on/off. + - **Permanent mode**: Set `ShowPassengers=true` (global) or `ShowPassengers=true` (per-unit) to always show the display, regardless of the hotkey state. + - **Toggleable mode**: Set `ShowPassengers.Toggleable=true` (global) and use the [Toggle Passengers](#toggle-passengers) hotkey to switch the display on/off. - Icons are arranged in horizontal rows from left to right, with the bottom row fixed at the bottom boundary. If the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are drawn above the bottom row. In `rulesmd.ini`: ```ini [AudioVisual] -ShowUnitPassengers=false ; boolean -ShowUnitPassengers.Toggleable=false ; boolean +ShowPassengers=false ; boolean +ShowPassengers.Toggleable=false ; boolean +ShowPassengers.PerRow=5 ; integer, defaults to 5 +ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to 0,0 -[SOMETECHNO] ; TechnoType -ShowPassengers= ; boolean, defaults to [AudioVisual] -> ShowUnitPassengers -ShowPassengers.PerRow=5 ; integer -ShowPassengers.BottomOffset=0,0 ; X,Y +[SOMETECHNO] ; TechnoType +ShowPassengers= ; boolean, defaults to [AudioVisual] -> ShowPassengers +ShowPassengers.PerRow=5 ; integer, defaults to [AudioVisual] -> ShowPassengers.PerRow +ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to [AudioVisual] -> ShowPassengers.BottomOffset ``` ## Hotkey Commands @@ -578,11 +580,11 @@ ShowPassengers.BottomOffset=0,0 ; X,Y - Switches on/off [digital display types](#digital-display). - For localization add `TXT_DIGITAL_DISPLAY` and `TXT_DIGITAL_DISPLAY_DESC` into your `.csf` file. -### `[ ]` Toggle Unit Passengers +### `[ ]` Toggle Passengers -- Switches on/off [unit passengers display](#unit-passengers-display). -- When toggled, messages are shown from CSF labels `MSG:UnitPassengersEnabled` and `MSG:UnitPassengersDisabled` (fallback: "Unit Passenger Icons Display: Enabled" / "Unit Passenger Icons Display: Disabled"). -- For localization add `TXT_UNIT_PASSENGERS` (fallback: "Toggle Unit Passenger Icons") and `TXT_UNIT_PASSENGERS_DESC` (fallback: "Show/hide unit passenger icons display.") into your `.csf` file. +- Switches on/off [passengers display](#passengers-display). +- When toggled, messages are shown from CSF labels `MSG:PassengersEnabled` and `MSG:PassengersDisabled` (fallback: "Passenger Icons Display: Enabled" / "Passenger Icons Display: Disabled"). +- For localization add `TXT_PASSENGERS` (fallback: "Toggle Passenger Icons") and `TXT_PASSENGERS_DESC` (fallback: "Show/hide passenger icons display.") into your `.csf` file. ### `[ ]` Next Idle Harvester diff --git a/docs/Whats-New.md b/docs/Whats-New.md index b5d12d033f..84be834abf 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -605,7 +605,7 @@ HideShakeEffects=false ; boolean - [Auto-remove earliest beacon](Fixed-or-Improved-Logics.md#auto-remove-earliest-beacon) (by TaranDahl) - [Customize the step limit of the credits indicator](User-Interface.md#customize-the-step-limit-of-the-credits-indicator) (by Noble_Fish) - [Disable the credits indicator smooth transition](User-Interface.md#disable-the-credits-indicator-smooth-transition) (by Noble_Fish) -- [New unit passengers display feature](User-Interface.md#unit-passengers-display) (by ABCZ) +- [New passengers display feature](User-Interface.md#passengers-display) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index ac2d4aa25c..31ee97c390 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -14,7 +14,7 @@ #include "ToggleMessageList.h" #include "DeselectObject.h" #include "DeselectObject5.h" -#include "ToggleUnitPassengers.h" +#include "TogglePassengers.h" #include @@ -33,7 +33,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); + MakeCommand(); if (Phobos::Config::SuperWeaponSidebarCommands) { diff --git a/src/Commands/TogglePassengers.cpp b/src/Commands/TogglePassengers.cpp new file mode 100644 index 0000000000..7a5375b7c7 --- /dev/null +++ b/src/Commands/TogglePassengers.cpp @@ -0,0 +1,45 @@ +#include "TogglePassengers.h" + +#include +#include +#include + +const char* TogglePassengersCommandClass::GetName() const +{ + return "Toggle Passengers"; +} + +const wchar_t* TogglePassengersCommandClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS", L"Toggle Passenger Icons"); +} + +const wchar_t* TogglePassengersCommandClass::GetUICategory() const +{ + return CATEGORY_INTERFACE; +} + +const wchar_t* TogglePassengersCommandClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS_DESC", L"Show/hide passenger icons display."); +} + +void TogglePassengersCommandClass::Execute(WWKey eInput) const +{ + Phobos::Config::Passengers_Enable = !Phobos::Config::Passengers_Enable; + + auto PrintMessage = [](const wchar_t* pMessage) + { + MessageListClass::Instance.PrintMessage( + pMessage, + RulesClass::Instance->MessageDelay, + HouseClass::CurrentPlayer->ColorSchemeIndex, + true + ); + }; + + if (Phobos::Config::Passengers_Enable) + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersEnabled", L"Passenger Icons Display: Enabled")); + else + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersDisabled", L"Passenger Icons Display: Disabled")); +} \ No newline at end of file diff --git a/src/Commands/ToggleUnitPassengers.h b/src/Commands/TogglePassengers.h similarity index 83% rename from src/Commands/ToggleUnitPassengers.h rename to src/Commands/TogglePassengers.h index c0888371d6..e1e84bb8eb 100644 --- a/src/Commands/ToggleUnitPassengers.h +++ b/src/Commands/TogglePassengers.h @@ -2,7 +2,7 @@ #include "Commands.h" -class ToggleUnitPassengersCommandClass : public CommandClass +class TogglePassengersCommandClass : public CommandClass { public: virtual const char* GetName() const override; diff --git a/src/Commands/ToggleUnitPassengers.cpp b/src/Commands/ToggleUnitPassengers.cpp deleted file mode 100644 index 0e26a2ee3d..0000000000 --- a/src/Commands/ToggleUnitPassengers.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "ToggleUnitPassengers.h" - -#include -#include -#include - -const char* ToggleUnitPassengersCommandClass::GetName() const -{ - return "Toggle Unit Passengers"; -} - -const wchar_t* ToggleUnitPassengersCommandClass::GetUIName() const -{ - return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS", L"Toggle Unit Passenger Icons"); -} - -const wchar_t* ToggleUnitPassengersCommandClass::GetUICategory() const -{ - return CATEGORY_INTERFACE; -} - -const wchar_t* ToggleUnitPassengersCommandClass::GetUIDescription() const -{ - return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS_DESC", L"Show/hide unit passenger icons display."); -} - -void ToggleUnitPassengersCommandClass::Execute(WWKey eInput) const -{ - Phobos::Config::UnitPassengers_Enable = !Phobos::Config::UnitPassengers_Enable; - - auto PrintMessage = [](const wchar_t* pMessage) - { - MessageListClass::Instance.PrintMessage( - pMessage, - RulesClass::Instance->MessageDelay, - HouseClass::CurrentPlayer->ColorSchemeIndex, - true - ); - }; - - if (Phobos::Config::UnitPassengers_Enable) - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:UnitPassengersEnabled", L"Unit Passenger Icons Display: Enabled")); - else - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:UnitPassengersDisabled", L"Unit Passenger Icons Display: Disabled")); -} \ No newline at end of file diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index cd9e1978ec..5a36edf738 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -390,7 +390,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->PenetratesTransport_Level.Read(exINI, GameStrings::CombatDamage, "PenetratesTransport.Level"); this->UnitsUnsellable.Read(exINI, GameStrings::General, "UnitsUnsellable"); - + this->DisableOveroptimizationInTargeting.Read(exINI, GameStrings::General, "DisableOveroptimizationInTargeting"); this->DriverKilled_KillPassengers.Read(exINI, GameStrings::CombatDamage, "DriverKilled.KillPassengers"); @@ -406,7 +406,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->DriveLocomotorMakesWake.Read(exINI, GameStrings::AudioVisual, "DriveLocomotorMakesWake"); this->HoverLocomotorMakesWake.Read(exINI, GameStrings::AudioVisual, "HoverLocomotionClassMakesWake"); this->ShipLocomotorMakesWake.Read(exINI, GameStrings::AudioVisual, "ShipLocomotionClassMakesWake"); - + this->FiringAnim_Update.Read(exINI, GameStrings::AudioVisual, "FiringAnim.Update"); this->ExtendedPlayerRepair.Read(exINI, GameStrings::General, "ExtendedPlayerRepair"); @@ -451,8 +451,10 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->AutoRemoveEarliestBeacon.Read(exINI, GameStrings::General, "AutoRemoveEarliestBeacon"); this->AllowBeaconHotKeyInSinglePlayer.Read(exINI, GameStrings::General, "AllowBeaconHotKeyInSinglePlayer"); - this->ShowUnitPassengers.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers"); - this->ShowUnitPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.Toggleable"); + this->ShowPassengers.Read(exINI, GameStrings::AudioVisual, "ShowPassengers"); + this->ShowPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowPassengers.Toggleable"); + this->ShowPassengers_PerRow.Read(exINI, GameStrings::AudioVisual, "ShowPassengers.PerRow"); + this->ShowPassengers_BottomOffset.Read(exINI, GameStrings::AudioVisual, "ShowPassengers.BottomOffset"); // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); @@ -803,8 +805,10 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->SecondaryFireSequenceLandOnly) .Process(this->AutoRemoveEarliestBeacon) .Process(this->AllowBeaconHotKeyInSinglePlayer) - .Process(this->ShowUnitPassengers) - .Process(this->ShowUnitPassengers_Toggleable) + .Process(this->ShowPassengers) + .Process(this->ShowPassengers_Toggleable) + .Process(this->ShowPassengers_PerRow) + .Process(this->ShowPassengers_BottomOffset) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 897e49c274..5fb23c66f9 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -194,8 +194,10 @@ class RulesExt Valueable MergeBuildingDamage; - Valueable ShowUnitPassengers; - Valueable ShowUnitPassengers_Toggleable; + Valueable ShowPassengers; + Valueable ShowPassengers_Toggleable; + Valueable ShowPassengers_PerRow; + Valueable ShowPassengers_BottomOffset; Valueable DamageOwnerMultiplier; Valueable DamageAlliesMultiplier; @@ -699,7 +701,7 @@ class RulesExt , BerzerkMission { Mission::Hunt } , BunkerStateUpdateDelay { 15 } - + , AllowChatBoxInSinglePlayer { false } , SecondaryFireSequenceLandOnly { true } @@ -708,8 +710,10 @@ class RulesExt , AllowBeaconHotKeyInSinglePlayer { false } - , ShowUnitPassengers { false } - , ShowUnitPassengers_Toggleable { false } + , ShowPassengers { false } + , ShowPassengers_Toggleable { false } + , ShowPassengers_PerRow { 5 } + , ShowPassengers_BottomOffset { { 0, 0 } } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 9ec29905d5..f37b116cc4 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -875,7 +875,7 @@ void TechnoExt::ShowPromoteAnim(TechnoClass* pThis) AnimExt::CreateRandomAnim(eliteAnims, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true); } -void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) +void TechnoExt::DrawPassengers(TechnoClass* pThis) { if (ObjectClass::CurrentObjects.Count != 1) return; @@ -883,9 +883,7 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) if (ObjectClass::CurrentObjects.GetItem(0) != pThis) return; - const auto pFoot = abstract_cast(pThis); - if (!pFoot) - return; + const auto whatAmI = pThis->WhatAmI(); const auto pType = pThis->GetTechnoType(); const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); @@ -893,17 +891,18 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) bool shouldShow = false; - if (pTypeExt->ShowPassengers.isset()) + if (pRulesExt->ShowPassengers) { - shouldShow = pTypeExt->ShowPassengers.Get(); + shouldShow = true; } - else if (pRulesExt->ShowUnitPassengers) + else if (pRulesExt->ShowPassengers_Toggleable && Phobos::Config::Passengers_Enable) { shouldShow = true; } - else if (pRulesExt->ShowUnitPassengers_Toggleable && Phobos::Config::UnitPassengers_Enable) + + if (pTypeExt->ShowPassengers.isset()) { - shouldShow = true; + shouldShow = pTypeExt->ShowPassengers.Get(); } if (!shouldShow) @@ -912,20 +911,31 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) if (pThis->Passengers.NumPassengers <= 0) return; - std::map passengerCounts; + std::vector> passengerCounts; for (auto pPassenger = pThis->Passengers.GetFirstPassenger(); pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) { - passengerCounts[pPassenger->GetTechnoType()]++; + const auto pPassengerType = pPassenger->GetTechnoType(); + auto it = std::find_if(passengerCounts.begin(), passengerCounts.end(), + [pPassengerType](const auto& pair) { return pair.first == pPassengerType; }); + if (it != passengerCounts.end()) + it->second++; + else + passengerCounts.emplace_back(pPassengerType, 1); } - const auto bracketPos = GetFootSelectBracketPosition(pFoot, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); - const auto& bottomOffset = pTypeExt->ShowPassengers_BottomOffset; - auto& offset = bottomOffset.Get(); + const auto bracketPos = whatAmI == AbstractType::Building + ? GetBuildingSelectBracketPosition(pThis, BuildingSelectBracketPosition::Top) + : GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); + Point2D offset = pTypeExt->ShowPassengers_BottomOffset.isset() + ? pTypeExt->ShowPassengers_BottomOffset.Get() + : pRulesExt->ShowPassengers_BottomOffset.Get(); Point2D basePos = bracketPos; basePos.X += offset.X; basePos.Y += offset.Y + pType->PixelSelectionBracketDelta; - const int perRow = pTypeExt->ShowPassengers_PerRow; + const int perRow = pTypeExt->ShowPassengers_PerRow.isset() + ? pTypeExt->ShowPassengers_PerRow.Get() + : pRulesExt->ShowPassengers_PerRow.Get(); const int iconWidth = 60; const int iconHeight = 48; const int hGap = 2; @@ -936,6 +946,7 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) const auto pSurface = DSurface::Composite; RectangleStruct bounds = DSurface::Composite->GetRect(); + RectangleStruct viewBounds = DSurface::ViewBounds; int idx = 0; for (const auto& item : passengerCounts) @@ -954,6 +965,14 @@ void TechnoExt::DrawUnitPassengers(TechnoClass* pThis) Point2D iconPos = { startX + col * (iconWidth + hGap), y }; + // Skip icons that are outside the screen bounds to prevent crash + RectangleStruct iconRect = { iconPos.X, iconPos.Y, iconWidth, iconHeight }; + if (iconRect.X < 0 || iconRect.Y < 0 || iconRect.X + iconRect.Width > viewBounds.Width || iconRect.Y + iconRect.Height > viewBounds.Height) + { + idx++; + continue; + } + bool drawn = false; const auto pPassengerTypeExt = TechnoTypeExt::ExtMap.Find(pPassengerType); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index ba76ff36ca..a69c02c43f 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -321,7 +321,7 @@ class TechnoExt static bool EjectSurvivor(FootClass* pSurvivor, CoordStruct coords, bool select); static bool __fastcall ApplyKillDriver(TechnoClass** pData, void*, HouseClass* pToHouse, TechnoClass* pKiller, bool resetVeterancy); - static void DrawUnitPassengers(TechnoClass* pThis); + static void DrawPassengers(TechnoClass* pThis); // WeaponHelpers.cpp static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true); diff --git a/src/Ext/Techno/Hooks.Pips.cpp b/src/Ext/Techno/Hooks.Pips.cpp index 8e6f665386..86c82a5dc9 100644 --- a/src/Ext/Techno/Hooks.Pips.cpp +++ b/src/Ext/Techno/Hooks.Pips.cpp @@ -94,7 +94,7 @@ DEFINE_HOOK(0x6F65D1, TechnoClass_DrawHealthBar_Buildings, 0x6) TechnoExt::ProcessDigitalDisplays(pThis); - TechnoExt::DrawUnitPassengers(pThis); + TechnoExt::DrawPassengers(pThis); return 0; } @@ -126,7 +126,7 @@ DEFINE_HOOK(0x6F683C, TechnoClass_DrawHealthBar_Units, 0x7) TechnoExt::ProcessDigitalDisplays(pThis); - TechnoExt::DrawUnitPassengers(pThis); + TechnoExt::DrawPassengers(pThis); if (pExt->TypeExtData->HealthBar_HidePips) { diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 911f9e0ce2..5d7798e22b 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1346,8 +1346,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShadowIndex_Frame.Read(exArtINI, pArtSection, "ShadowIndex.Frame"); - this->CameoPCX.Read(pArtINI, pArtSection, "CameoPCX"); this->CameoPalette.LoadFromINI(pArtINI, pArtSection, "CameoPalette"); + this->CameoPCX.Read(pArtINI, pArtSection, "CameoPCX"); this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); this->LaserTrailData.clear(); @@ -1537,6 +1537,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->ImmuneToCrit) .Process(this->MultiMindControl_ReleaseVictim) .Process(this->CameoPriority) + .Process(this->CameoPCX) .Process(this->AltCameoPCX) .Process(this->NoManualMove) .Process(this->InitialStrength) @@ -1743,7 +1744,6 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->ShowPassengers) .Process(this->ShowPassengers_PerRow) .Process(this->ShowPassengers_BottomOffset) - .Process(this->CameoPCX) .Process(this->CameoPalette) .Process(this->SelectBox) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 73cd025305..bfb6be84cb 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -66,6 +66,7 @@ class TechnoTypeExt Valueable ImmuneToCrit; Valueable MultiMindControl_ReleaseVictim; Valueable CameoPriority; + PhobosPCXFile CameoPCX; PhobosPCXFile AltCameoPCX; Valueable NoManualMove; Nullable InitialStrength; @@ -268,9 +269,8 @@ class TechnoTypeExt ValueableVector DigitalDisplayTypes; Nullable ShowPassengers; - Valueable ShowPassengers_PerRow; - Valueable ShowPassengers_BottomOffset; - PhobosPCXFile CameoPCX; + Nullable ShowPassengers_PerRow; + Nullable ShowPassengers_BottomOffset; CustomPalette CameoPalette; Nullable SelectBox; @@ -569,6 +569,7 @@ class TechnoTypeExt , ImmuneToCrit { false } , MultiMindControl_ReleaseVictim { false } , CameoPriority { 0 } + , CameoPCX {} , AltCameoPCX {} , NoManualMove { false } , InitialStrength {} @@ -767,9 +768,8 @@ class TechnoTypeExt , DigitalDisplayTypes {} , ShowPassengers {} - , ShowPassengers_PerRow { 5 } - , ShowPassengers_BottomOffset { { 0, 0 } } - , CameoPCX {} + , ShowPassengers_PerRow {} + , ShowPassengers_BottomOffset {} , CameoPalette {} , SelectBox {} diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 4227418357..02cea86239 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -57,7 +57,7 @@ bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::EnableSelectBox = false; bool Phobos::Config::DigitalDisplay_Enable = false; -bool Phobos::Config::UnitPassengers_Enable = false; +bool Phobos::Config::Passengers_Enable = false; bool Phobos::Config::MessageApplyHoverState = false; bool Phobos::Config::MessageDisplayInCenter = false; int Phobos::Config::MessageDisplayInCenter_BoardOpacity = 40; @@ -106,7 +106,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "RealTimeTimers.Adaptive", false); Phobos::Config::EnableSelectBox = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "EnableSelectBox", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "DigitalDisplay.Enable", false); - Phobos::Config::UnitPassengers_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "UnitPassengers.Enable", false); + Phobos::Config::Passengers_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "Passengers.Enable", false); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "SaveGameOnScenarioStart", true); Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowBriefing", true); Phobos::Config::ShowPowerDelta = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPowerDelta", true); diff --git a/src/Phobos.h b/src/Phobos.h index d7a6d56510..b35bbe46ae 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -92,7 +92,7 @@ class Phobos static bool EnableBuildingPlacementPreview; static bool EnableSelectBox; static bool DigitalDisplay_Enable; - static bool UnitPassengers_Enable; + static bool Passengers_Enable; static bool MessageApplyHoverState; static bool MessageDisplayInCenter; static int MessageDisplayInCenter_BoardOpacity; From 04e0649b3a0a12591e60e28fb52ed58f349092f9 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 14:25:45 +0800 Subject: [PATCH 14/30] docs: rename Icons to Cameos, merge CSF messages, fix dirty diff in Rules/Body.h --- CREDITS.md | 2 +- docs/User-Interface.md | 8 +++----- docs/Whats-New.md | 2 +- src/Commands/TogglePassengers.cpp | 8 ++++---- src/Ext/Rules/Body.h | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 5bc278c313..5d7bb3aa76 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -877,4 +877,4 @@ 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 -- **ABCZ (CZHczhABCZ)** - Passengers display +- **ABCZ (CZHczhABCZ)** - Passengers Cameo Display diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 26ddd2cb48..acfdab4040 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -528,11 +528,11 @@ ShowPowerPlantEnhancerRange=false ; boolean ### Passengers Display -- You can now configure the game to display the passengers contained within a vehicle or building above it when selected. Each passenger type is shown as its Cameo icon with a count number in the upper-right corner. +- You can now configure the game to display the passengers contained within a vehicle or building above it when selected. Each passenger type is shown as its cameo with a count number in the upper-right corner. - Two activation modes: - **Permanent mode**: Set `ShowPassengers=true` (global) or `ShowPassengers=true` (per-unit) to always show the display, regardless of the hotkey state. - **Toggleable mode**: Set `ShowPassengers.Toggleable=true` (global) and use the [Toggle Passengers](#toggle-passengers) hotkey to switch the display on/off. -- Icons are arranged in horizontal rows from left to right, with the bottom row fixed at the bottom boundary. If the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are drawn above the bottom row. +- Cameos are arranged in horizontal rows, centered from left to right below the selection bracket, with the bottom row fixed at the bottom boundary. When the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are stacked above the bottom row. In `rulesmd.ini`: ```ini @@ -582,9 +582,7 @@ ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to [AudioVisual] -> ShowPa ### `[ ]` Toggle Passengers -- Switches on/off [passengers display](#passengers-display). -- When toggled, messages are shown from CSF labels `MSG:PassengersEnabled` and `MSG:PassengersDisabled` (fallback: "Passenger Icons Display: Enabled" / "Passenger Icons Display: Disabled"). -- For localization add `TXT_PASSENGERS` (fallback: "Toggle Passenger Icons") and `TXT_PASSENGERS_DESC` (fallback: "Show/hide passenger icons display.") into your `.csf` file. +- Switches on/off [passengers display](#passengers-display). When toggled, messages are shown from CSF labels `MSG:PassengersEnabled` and `MSG:PassengersDisabled` (fallback: "Passenger Cameos Display: Enabled" / "Passenger Cameos Display: Disabled"). For localization add `TXT_PASSENGERS` (fallback: "Toggle Passenger Cameos") and `TXT_PASSENGERS_DESC` (fallback: "Show/hide passenger cameos display.") into your `.csf` file. ### `[ ]` Next Idle Harvester diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 84be834abf..2c4c947c73 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -605,7 +605,7 @@ HideShakeEffects=false ; boolean - [Auto-remove earliest beacon](Fixed-or-Improved-Logics.md#auto-remove-earliest-beacon) (by TaranDahl) - [Customize the step limit of the credits indicator](User-Interface.md#customize-the-step-limit-of-the-credits-indicator) (by Noble_Fish) - [Disable the credits indicator smooth transition](User-Interface.md#disable-the-credits-indicator-smooth-transition) (by Noble_Fish) -- [New passengers display feature](User-Interface.md#passengers-display) (by ABCZ) +- [New passengers cameo display feature](User-Interface.md#passengers-display) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Commands/TogglePassengers.cpp b/src/Commands/TogglePassengers.cpp index 7a5375b7c7..882316ce10 100644 --- a/src/Commands/TogglePassengers.cpp +++ b/src/Commands/TogglePassengers.cpp @@ -11,7 +11,7 @@ const char* TogglePassengersCommandClass::GetName() const const wchar_t* TogglePassengersCommandClass::GetUIName() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS", L"Toggle Passenger Icons"); + return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS", L"Toggle Passenger Cameos"); } const wchar_t* TogglePassengersCommandClass::GetUICategory() const @@ -21,7 +21,7 @@ const wchar_t* TogglePassengersCommandClass::GetUICategory() const const wchar_t* TogglePassengersCommandClass::GetUIDescription() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS_DESC", L"Show/hide passenger icons display."); + return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS_DESC", L"Show/hide passenger cameos display."); } void TogglePassengersCommandClass::Execute(WWKey eInput) const @@ -39,7 +39,7 @@ void TogglePassengersCommandClass::Execute(WWKey eInput) const }; if (Phobos::Config::Passengers_Enable) - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersEnabled", L"Passenger Icons Display: Enabled")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersEnabled", L"Passenger Cameos Display: Enabled")); else - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersDisabled", L"Passenger Icons Display: Disabled")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersDisabled", L"Passenger Cameos Display: Disabled")); } \ No newline at end of file diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 5fb23c66f9..d8dd01d514 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -701,7 +701,7 @@ class RulesExt , BerzerkMission { Mission::Hunt } , BunkerStateUpdateDelay { 15 } - + , AllowChatBoxInSinglePlayer { false } , SecondaryFireSequenceLandOnly { true } From 35a61cd8262ab25deb3c250474e9956985359c39 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 14:33:37 +0800 Subject: [PATCH 15/30] docs: split Toggle Passengers into standard format, add CSF message detail to Passengers Display section --- docs/User-Interface.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index acfdab4040..f677835256 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -548,6 +548,8 @@ ShowPassengers.PerRow=5 ; integer, defaults to [AudioVisual] -> Sh ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to [AudioVisual] -> ShowPassengers.BottomOffset ``` +- When toggled via the hotkey, messages are shown from CSF labels `MSG:PassengersEnabled` (fallback: "Passenger Cameos Display: Enabled") and `MSG:PassengersDisabled` (fallback: "Passenger Cameos Display: Disabled"). + ## Hotkey Commands ### `[ ]` Display Damage Numbers @@ -582,7 +584,8 @@ ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to [AudioVisual] -> ShowPa ### `[ ]` Toggle Passengers -- Switches on/off [passengers display](#passengers-display). When toggled, messages are shown from CSF labels `MSG:PassengersEnabled` and `MSG:PassengersDisabled` (fallback: "Passenger Cameos Display: Enabled" / "Passenger Cameos Display: Disabled"). For localization add `TXT_PASSENGERS` (fallback: "Toggle Passenger Cameos") and `TXT_PASSENGERS_DESC` (fallback: "Show/hide passenger cameos display.") into your `.csf` file. +- Switches on/off [passengers display](#passengers-display). +- For localization add `TXT_PASSENGERS` and `TXT_PASSENGERS_DESC` into your `.csf` file. ### `[ ]` Next Idle Harvester From 2b7b98092fa25382a2252fb1368e05b7b225c028 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 15:09:29 +0800 Subject: [PATCH 16/30] refactor: rename Passengers.Enable to ShowPassengers.Enable, move fields to end of blocks in TechnoType --- src/Commands/TogglePassengers.cpp | 4 ++-- src/Ext/Techno/Body.Visuals.cpp | 2 +- src/Ext/TechnoType/Body.cpp | 18 +++++++++--------- src/Ext/TechnoType/Body.h | 20 ++++++++++---------- src/Phobos.INI.cpp | 4 ++-- src/Phobos.h | 2 +- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Commands/TogglePassengers.cpp b/src/Commands/TogglePassengers.cpp index 882316ce10..33bcfad56c 100644 --- a/src/Commands/TogglePassengers.cpp +++ b/src/Commands/TogglePassengers.cpp @@ -26,7 +26,7 @@ const wchar_t* TogglePassengersCommandClass::GetUIDescription() const void TogglePassengersCommandClass::Execute(WWKey eInput) const { - Phobos::Config::Passengers_Enable = !Phobos::Config::Passengers_Enable; + Phobos::Config::ShowPassengers_Enable = !Phobos::Config::ShowPassengers_Enable; auto PrintMessage = [](const wchar_t* pMessage) { @@ -38,7 +38,7 @@ void TogglePassengersCommandClass::Execute(WWKey eInput) const ); }; - if (Phobos::Config::Passengers_Enable) + if (Phobos::Config::ShowPassengers_Enable) PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersEnabled", L"Passenger Cameos Display: Enabled")); else PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersDisabled", L"Passenger Cameos Display: Disabled")); diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index f37b116cc4..981c0c29a7 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -895,7 +895,7 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) { shouldShow = true; } - else if (pRulesExt->ShowPassengers_Toggleable && Phobos::Config::Passengers_Enable) + else if (pRulesExt->ShowPassengers_Toggleable && Phobos::Config::ShowPassengers_Enable) { shouldShow = true; } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 5d7798e22b..d85e793d9a 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -996,10 +996,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->DigitalDisplay_Disable.Read(exINI, pSection, "DigitalDisplay.Disable"); this->DigitalDisplayTypes.Read(exINI, pSection, "DigitalDisplayTypes"); - this->ShowPassengers.Read(exINI, pSection, "ShowPassengers"); - this->ShowPassengers_PerRow.Read(exINI, pSection, "ShowPassengers.PerRow"); - this->ShowPassengers_BottomOffset.Read(exINI, pSection, "ShowPassengers.BottomOffset"); - this->SelectBox.Read(exINI, pSection, "SelectBox"); this->HideSelectBox.Read(exINI, pSection, "HideSelectBox"); @@ -1468,6 +1464,10 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // VoiceIFVRepair from Ares 0.2 this->VoiceIFVRepair.Read(exINI, pSection, "VoiceIFVRepair"); this->ParseVoiceWeaponAttacks(exINI, pSection, this->VoiceWeaponAttacks, this->VoiceEliteWeaponAttacks); + + this->ShowPassengers.Read(exINI, pSection, "ShowPassengers"); + this->ShowPassengers_PerRow.Read(exINI, pSection, "ShowPassengers.PerRow"); + this->ShowPassengers_BottomOffset.Read(exINI, pSection, "ShowPassengers.BottomOffset"); } void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection) @@ -1741,11 +1741,6 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->DigitalDisplay_Disable) .Process(this->DigitalDisplayTypes) - .Process(this->ShowPassengers) - .Process(this->ShowPassengers_PerRow) - .Process(this->ShowPassengers_BottomOffset) - .Process(this->CameoPalette) - .Process(this->SelectBox) .Process(this->HideSelectBox) @@ -1983,6 +1978,11 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->Missile_Cruise) .Process(this->Missile_TakeOffAnim) .Process(this->Missile_TakeOffSeparation) + + .Process(this->ShowPassengers) + .Process(this->ShowPassengers_PerRow) + .Process(this->ShowPassengers_BottomOffset) + .Process(this->CameoPalette) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index bfb6be84cb..38b76ae79b 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -268,11 +268,6 @@ class TechnoTypeExt Valueable DigitalDisplay_Disable; ValueableVector DigitalDisplayTypes; - Nullable ShowPassengers; - Nullable ShowPassengers_PerRow; - Nullable ShowPassengers_BottomOffset; - CustomPalette CameoPalette; - Nullable SelectBox; Valueable HideSelectBox; @@ -528,6 +523,11 @@ class TechnoTypeExt Valueable Missile_TakeOffAnim; Valueable Missile_TakeOffSeparation; + Nullable ShowPassengers; + Nullable ShowPassengers_PerRow; + Nullable ShowPassengers_BottomOffset; + CustomPalette CameoPalette; + ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) , HealthBar_Hide { false } , HealthBar_HidePips { false } @@ -767,11 +767,6 @@ class TechnoTypeExt , DigitalDisplay_Disable { false } , DigitalDisplayTypes {} - , ShowPassengers {} - , ShowPassengers_PerRow {} - , ShowPassengers_BottomOffset {} - , CameoPalette {} - , SelectBox {} , HideSelectBox { false } @@ -1008,6 +1003,11 @@ class TechnoTypeExt , Missile_Cruise { false } , Missile_TakeOffAnim { nullptr } , Missile_TakeOffSeparation { 24 } + + , ShowPassengers {} + , ShowPassengers_PerRow {} + , ShowPassengers_BottomOffset {} + , CameoPalette {} { } virtual ~ExtData() = default; diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 02cea86239..5dfe63ace2 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -57,7 +57,7 @@ bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::EnableSelectBox = false; bool Phobos::Config::DigitalDisplay_Enable = false; -bool Phobos::Config::Passengers_Enable = false; +bool Phobos::Config::ShowPassengers_Enable = false; bool Phobos::Config::MessageApplyHoverState = false; bool Phobos::Config::MessageDisplayInCenter = false; int Phobos::Config::MessageDisplayInCenter_BoardOpacity = 40; @@ -106,7 +106,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "RealTimeTimers.Adaptive", false); Phobos::Config::EnableSelectBox = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "EnableSelectBox", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "DigitalDisplay.Enable", false); - Phobos::Config::Passengers_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "Passengers.Enable", false); + Phobos::Config::ShowPassengers_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPassengers.Enable", false); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "SaveGameOnScenarioStart", true); Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowBriefing", true); Phobos::Config::ShowPowerDelta = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPowerDelta", true); diff --git a/src/Phobos.h b/src/Phobos.h index b35bbe46ae..7ca5045552 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -92,7 +92,7 @@ class Phobos static bool EnableBuildingPlacementPreview; static bool EnableSelectBox; static bool DigitalDisplay_Enable; - static bool Passengers_Enable; + static bool ShowPassengers_Enable; static bool MessageApplyHoverState; static bool MessageDisplayInCenter; static int MessageDisplayInCenter_BoardOpacity; From 090019766c3c9065ed3097fee094dd77a049ff0e Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 16:19:45 +0800 Subject: [PATCH 17/30] Fix ShowPassengers logic priority and CameoPalette INI reading order Move ShowPassengers_Enable as master switch with early return; merge redundant shouldShow branches; move CameoPalette.LoadFromINI after ShowPassengers reads to match declaration order --- src/Ext/Techno/Body.Visuals.cpp | 10 +++++----- src/Ext/TechnoType/Body.cpp | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 981c0c29a7..53e1239663 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -889,13 +889,13 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); const auto pRulesExt = RulesExt::Global(); + // Master switch, highest priority + if (!Phobos::Config::ShowPassengers_Enable) + return; + bool shouldShow = false; - if (pRulesExt->ShowPassengers) - { - shouldShow = true; - } - else if (pRulesExt->ShowPassengers_Toggleable && Phobos::Config::ShowPassengers_Enable) + if (pRulesExt->ShowPassengers || pRulesExt->ShowPassengers_Toggleable) { shouldShow = true; } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index d85e793d9a..aae1a4ca05 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1342,7 +1342,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShadowIndex_Frame.Read(exArtINI, pArtSection, "ShadowIndex.Frame"); - this->CameoPalette.LoadFromINI(pArtINI, pArtSection, "CameoPalette"); this->CameoPCX.Read(pArtINI, pArtSection, "CameoPCX"); this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); @@ -1468,6 +1467,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShowPassengers.Read(exINI, pSection, "ShowPassengers"); this->ShowPassengers_PerRow.Read(exINI, pSection, "ShowPassengers.PerRow"); this->ShowPassengers_BottomOffset.Read(exINI, pSection, "ShowPassengers.BottomOffset"); + + this->CameoPalette.LoadFromINI(pArtINI, pArtSection, "CameoPalette"); } void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection) From a2bf97b648f6847547c04a164070f3a7411e890d Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 22:19:17 +0800 Subject: [PATCH 18/30] Rename CSF labels to PassengerCameoDisplay, fix icon layout order CSF: MSG:PassengerCameoDisplayEnabled/Disabled, TXT_PASSENGERCAMEO/_DESC. Fix passenger vector reversal for LIFO list. Docs: section title, description text, INI defaults. --- docs/User-Interface.md | 18 +++++++++--------- docs/Whats-New.md | 2 +- src/Commands/TogglePassengers.cpp | 8 ++++---- src/Ext/Techno/Body.Visuals.cpp | 5 ++++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index f677835256..7576c7cb6c 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -526,13 +526,13 @@ In `RA2MD.INI`: ShowPowerPlantEnhancerRange=false ; boolean ``` -### Passengers Display +### Passenger Cameo Display -- You can now configure the game to display the passengers contained within a vehicle or building above it when selected. Each passenger type is shown as its cameo with a count number in the upper-right corner. +- You can now configure the game to show passenger cameo icons and counts above a vehicle or building when it is selected. Each passenger type is shown as its cameo with a count number in the upper-right corner. - Two activation modes: - - **Permanent mode**: Set `ShowPassengers=true` (global) or `ShowPassengers=true` (per-unit) to always show the display, regardless of the hotkey state. + - **Permanent mode**: Set `ShowPassengers=true` (global) or `ShowPassengers=true` (per-unit) to always show the cameos, regardless of the hotkey state. - **Toggleable mode**: Set `ShowPassengers.Toggleable=true` (global) and use the [Toggle Passengers](#toggle-passengers) hotkey to switch the display on/off. -- Cameos are arranged in horizontal rows, centered from left to right below the selection bracket, with the bottom row fixed at the bottom boundary. When the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are stacked above the bottom row. +- Cameos are arranged in horizontal rows, centered from left to right above the selection bracket, with the bottom row fixed at the bottom boundary. When the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are stacked above the bottom row. In `rulesmd.ini`: ```ini @@ -544,11 +544,11 @@ ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to 0,0 [SOMETECHNO] ; TechnoType ShowPassengers= ; boolean, defaults to [AudioVisual] -> ShowPassengers -ShowPassengers.PerRow=5 ; integer, defaults to [AudioVisual] -> ShowPassengers.PerRow -ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to [AudioVisual] -> ShowPassengers.BottomOffset +ShowPassengers.PerRow= ; integer, defaults to [AudioVisual] -> ShowPassengers.PerRow +ShowPassengers.BottomOffset= ; X,Y, defaults to [AudioVisual] -> ShowPassengers.BottomOffset ``` -- When toggled via the hotkey, messages are shown from CSF labels `MSG:PassengersEnabled` (fallback: "Passenger Cameos Display: Enabled") and `MSG:PassengersDisabled` (fallback: "Passenger Cameos Display: Disabled"). +- When toggled via the hotkey, messages are shown from CSF labels `MSG:PassengerCameoDisplayEnabled` (fallback: "Passenger Cameos Display: Enabled") and `MSG:PassengerCameoDisplayDisabled` (fallback: "Passenger Cameos Display: Disabled"). ## Hotkey Commands @@ -584,8 +584,8 @@ ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to [AudioVisual] -> ShowPa ### `[ ]` Toggle Passengers -- Switches on/off [passengers display](#passengers-display). -- For localization add `TXT_PASSENGERS` and `TXT_PASSENGERS_DESC` into your `.csf` file. +- Switches on/off [passenger cameo display](#passenger-cameo-display). +- For localization add `TXT_PASSENGERCAMEO` and `TXT_PASSENGERCAMEO_DESC` into your `.csf` file. ### `[ ]` Next Idle Harvester diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 2c4c947c73..218cfdd897 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -605,7 +605,7 @@ HideShakeEffects=false ; boolean - [Auto-remove earliest beacon](Fixed-or-Improved-Logics.md#auto-remove-earliest-beacon) (by TaranDahl) - [Customize the step limit of the credits indicator](User-Interface.md#customize-the-step-limit-of-the-credits-indicator) (by Noble_Fish) - [Disable the credits indicator smooth transition](User-Interface.md#disable-the-credits-indicator-smooth-transition) (by Noble_Fish) -- [New passengers cameo display feature](User-Interface.md#passengers-display) (by ABCZ) +- [New passenger cameo display feature](User-Interface.md#passenger-cameo-display) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Commands/TogglePassengers.cpp b/src/Commands/TogglePassengers.cpp index 33bcfad56c..b6c2860d57 100644 --- a/src/Commands/TogglePassengers.cpp +++ b/src/Commands/TogglePassengers.cpp @@ -11,7 +11,7 @@ const char* TogglePassengersCommandClass::GetName() const const wchar_t* TogglePassengersCommandClass::GetUIName() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS", L"Toggle Passenger Cameos"); + return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERCAMEO", L"Toggle Passenger Cameos"); } const wchar_t* TogglePassengersCommandClass::GetUICategory() const @@ -21,7 +21,7 @@ const wchar_t* TogglePassengersCommandClass::GetUICategory() const const wchar_t* TogglePassengersCommandClass::GetUIDescription() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERS_DESC", L"Show/hide passenger cameos display."); + return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERCAMEO_DESC", L"Show/hide passenger cameos display."); } void TogglePassengersCommandClass::Execute(WWKey eInput) const @@ -39,7 +39,7 @@ void TogglePassengersCommandClass::Execute(WWKey eInput) const }; if (Phobos::Config::ShowPassengers_Enable) - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersEnabled", L"Passenger Cameos Display: Enabled")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengerCameoDisplayEnabled", L"Passenger Cameos Display: Enabled")); else - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengersDisabled", L"Passenger Cameos Display: Disabled")); + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengerCameoDisplayDisabled", L"Passenger Cameos Display: Disabled")); } \ No newline at end of file diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 53e1239663..a76b9b29f2 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -4,6 +4,7 @@ #include #include #include +#include void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds) { @@ -923,6 +924,8 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) passengerCounts.emplace_back(pPassengerType, 1); } + std::reverse(passengerCounts.begin(), passengerCounts.end()); + const auto bracketPos = whatAmI == AbstractType::Building ? GetBuildingSelectBracketPosition(pThis, BuildingSelectBracketPosition::Top) : GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); @@ -963,7 +966,7 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) const int y = basePos.Y - iconHeight - (rows - 1 - row) * (iconHeight + vGap); - Point2D iconPos = { startX + col * (iconWidth + hGap), y }; + Point2D iconPos = { startX + (itemsInThisRow - 1 - col) * (iconWidth + hGap), y }; // Skip icons that are outside the screen bounds to prevent crash RectangleStruct iconRect = { iconPos.X, iconPos.Y, iconWidth, iconHeight }; From 6cf1934d6720e82d1ff350e87b9376fd5664b4aa Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sat, 4 Jul 2026 22:46:10 +0800 Subject: [PATCH 19/30] Fix passenger cameo layout: correct X (left-to-right) and Y (top-to-bottom) ordering --- src/Ext/Techno/Body.Visuals.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index a76b9b29f2..0002f5db91 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -966,7 +966,7 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) const int y = basePos.Y - iconHeight - (rows - 1 - row) * (iconHeight + vGap); - Point2D iconPos = { startX + (itemsInThisRow - 1 - col) * (iconWidth + hGap), y }; + Point2D iconPos = { startX + col * (iconWidth + hGap), y }; // Skip icons that are outside the screen bounds to prevent crash RectangleStruct iconRect = { iconPos.X, iconPos.Y, iconWidth, iconHeight }; From 7820299dc829032b2f9f0e86abd8c221e476e454 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Sun, 5 Jul 2026 22:45:38 +0800 Subject: [PATCH 20/30] feat: add Show Cameo display feature with CustomPalette support - Rename TogglePassengers to ToggleCameos - Add ShowCameo.CustomPalette tag (artmd.ini) - Fix building passenger display order (LIFO reverse + Y flip) - Fix occupant display order (FIFO, no reverse) - Update docs with full CN/EN bilingual documentation --- Phobos.vcxproj | 4 +- docs/User-Interface.md | 117 +++++-- docs/Whats-New.md | 2 + src/Commands/Commands.cpp | 4 +- src/Commands/ToggleCameos.cpp | 54 ++++ .../{TogglePassengers.h => ToggleCameos.h} | 2 +- src/Commands/TogglePassengers.cpp | 45 --- src/Ext/Rules/Body.cpp | 24 +- src/Ext/Rules/Body.h | 24 +- src/Ext/Techno/Body.Visuals.cpp | 288 ++++++++++++++---- src/Ext/Techno/Body.h | 2 +- src/Ext/Techno/Hooks.Pips.cpp | 4 +- src/Ext/TechnoType/Body.cpp | 27 +- src/Ext/TechnoType/Body.h | 24 +- src/Phobos.INI.cpp | 4 +- src/Phobos.h | 2 +- 16 files changed, 468 insertions(+), 159 deletions(-) create mode 100644 src/Commands/ToggleCameos.cpp rename src/Commands/{TogglePassengers.h => ToggleCameos.h} (84%) delete mode 100644 src/Commands/TogglePassengers.cpp diff --git a/Phobos.vcxproj b/Phobos.vcxproj index b555139ea8..d89c9cecee 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -45,7 +45,7 @@ - + @@ -309,7 +309,7 @@ - + diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 7576c7cb6c..06c3e985ec 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -189,6 +189,7 @@ In `RA2MD.INI`: ```ini [Phobos] DigitalDisplay.Enable=false ; boolean +ShowCameo.Enable=true ; boolean ``` ```{note} @@ -526,29 +527,111 @@ In `RA2MD.INI`: ShowPowerPlantEnhancerRange=false ; boolean ``` -### Passenger Cameo Display +### Show Cameo / 显示载员图标 + +- You can now configure the game to show cameo icons and counts above a vehicle or building when it is selected. Both vehicle passengers (`Passengers=`) and garrisoned infantry / occupants (`Occupy=`) are displayed. Each passenger/occupant type is shown as its cameo with a count number in the upper-right corner. Only shown when a single unit is selected (not box-selection). +- 选中载具或建筑时,在其上方显示载员(`Passengers=`)和驻军(`Occupy=`)的图标与数量。图标为对应单位的Cameo,右上角显示该类型数量。仅单选时显示,框选不显示。 + +#### Activation modes / 激活模式 -- You can now configure the game to show passenger cameo icons and counts above a vehicle or building when it is selected. Each passenger type is shown as its cameo with a count number in the upper-right corner. - Two activation modes: - - **Permanent mode**: Set `ShowPassengers=true` (global) or `ShowPassengers=true` (per-unit) to always show the cameos, regardless of the hotkey state. - - **Toggleable mode**: Set `ShowPassengers.Toggleable=true` (global) and use the [Toggle Passengers](#toggle-passengers) hotkey to switch the display on/off. -- Cameos are arranged in horizontal rows, centered from left to right above the selection bracket, with the bottom row fixed at the bottom boundary. When the number of passenger types exceeds `ShowPassengers.PerRow`, additional rows are stacked above the bottom row. + - **Permanent mode / 永久模式**: Set `ShowCameo=true` (global) or `ShowCameo=true` (per-unit) to always show the cameos, regardless of the hotkey state. Per-unit setting overrides the global setting. If a per-unit `ShowCameo` is explicitly set to `false`, cameos are never shown for that unit. + - **Toggleable mode / 切换模式**: Set `ShowCameo=false` (global) and `ShowCameo.Toggleable=true` (global), then use the [Toggle Cameos](#toggle-cameos) hotkey to switch the display on/off. The master switch `ShowCameo.Enable` in `RA2MD.INI` controls the toggle state. +- 两种模式: + - **永久模式**:设置全局 `ShowCameo=true` 或单位 `ShowCameo=true` 后始终显示图标,不受快捷键影响。单位级设置覆盖全局设置。若单位级 `ShowCameo` 显式设为 `false`,则该单位永不显示图标。 + - **切换模式**:全局 `ShowCameo=false` 且 `ShowCameo.Toggleable=true`,使用快捷键切换显示/隐藏。总开关 `ShowCameo.Enable` 在 `RA2MD.INI` 中控制。 + +#### Display order / 显示顺序 + +- Cameos are displayed with the **first-entered unit at the top** and the **last-entered unit at the bottom**. For vehicles, the passenger linked list is LIFO (last-in-first-out), so the natural iteration order (last-entered first) already produces the correct layout. For buildings, the passenger list is also LIFO but the Y-axis is flipped to match the same top-to-bottom convention. Occupants (garrisoned infantry) are stored in FIFO order and are displayed directly without reversing. +- 图标按**先进先上、后进先下**的顺序排列。载具的乘客链表为LIFO(后进先出),自然迭代即产生正确布局。建筑的乘客链表同样为LIFO,但通过Y轴翻转后与载具保持一致的显示顺序。驻军(进驻步兵)按FIFO存储,直接显示,无需反转。 + +#### Row layout / 行布局 + +- Cameos are arranged in horizontal rows, centered from left to right above the selection bracket, with the bottom row fixed at the bottom boundary. When the number of cameo types exceeds `ShowCameo.PerRow`, additional rows are stacked above the bottom row. +- 图标按水平行排列,自左向右居中于选择框上方,底行固定在下边界。当图标类型数量超过 `ShowCameo.PerRow` 时,在底行上方堆叠更多行。 + +#### Custom cameo shape / 自定义图标 + +- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. The value is a SHP filename (e.g., `mycameo.shp`). Animated SHPs are supported (frames cycle automatically). The SHP's actual dimensions define its canvas size for overlap calculations. +- 可在 `artmd.ini` 中通过 `ShowCameo.CustomShape` 为每种载员/驻军类型指定自定义SHP图标(如 `mycameo.shp`)。支持动画SHP(帧自动循环)。SHP的实际尺寸决定其画布大小。 +- A custom palette can be specified via `ShowCameo.CustomPalette` in `artmd.ini`. If not set, the palette falls back to `CameoPalette` (from Ares) or the default `CAMEO_PAL`. +- 可通过 `artmd.ini` 中的 `ShowCameo.CustomPalette` 指定自定义色盘。未设置时回退到 `CameoPalette`(Ares标签)或默认的 `CAMEO_PAL`。 + +#### Translucency / 透明度 + +- Transparency can be controlled via `ShowCameo.Translucency` (values 0/25/50/75, default 0 = fully opaque). The translucency applies to SHP cameos; PCX cameos are drawn without transparency. +- 透明度通过 `ShowCameo.Translucency` 控制(取值 0/25/50/75,默认 0 = 完全不透明)。透明度仅作用于SHP图标,PCX图标无透明效果。 + +#### Icon overlap and positioning / 图标重叠与定位 + +Each cameo icon occupies a **canvas** (rectangle area). For standard cameos, the canvas is 60x48 pixels. For custom SHPs, the canvas is the SHP's actual dimensions (`Width` x `Height`). +每个图标占据一个**画布**(矩形区域)。标准图标的画布为60x48像素,自定义SHP的画布为其实际尺寸。 + +The positioning logic follows this order / 定位顺序: +1. **Row layout / 行布局**: Cameos are placed in rows according to `ShowCameo.PerRow`. +2. **OverlapXY / 重叠百分比**: Percentage-based overlap is applied. +3. **OffsetXY / 像素偏移**: Pixel-based offset is added. +4. **Centering / 居中**: Each row is horizontally centered. + +The overlap formula / 重叠公式: +``` +overlap = coveredCanvasSize * (OverlapXY% / 100) + OffsetXY +``` +- `ShowCameo.OverlapPrimary=true` (default): the primary (earlier) cameo covers the secondary (later) cameo. Overlap is based on the secondary cameo's canvas size. +- `ShowCameo.OverlapPrimary=false`: the secondary (later) cameo covers the primary (earlier) cameo. Overlap is based on the primary cameo's canvas size. +- `ShowCameo.OverlapPrimary=true`(默认):前面的图标覆盖后面的图标,重叠量基于后面图标的画布尺寸计算。 +- `ShowCameo.OverlapPrimary=false`:后面的图标覆盖前面的图标,重叠量基于前面图标的画布尺寸计算。 +- On the same row, only X overlap matters (Y has no effect). When wrapping to a new row, only Y overlap matters (X has no effect). +- 同行只有X重叠生效(Y无效果),换行只有Y重叠生效(X无效果)。 + +```{hint} +For example, with `ShowCameo.PerRow=1` (vertical stacking), `ShowCameo.OverlapPrimary=true` means the bottom cameo covers the one above it, preserving the bottom cameo's text label from being hidden. +例如,`ShowCameo.PerRow=1`(垂直排列)时,`ShowCameo.OverlapPrimary=true` 表示底部的图标覆盖上方图标,保证底部图标的数量文字不被遮挡。 +``` + +- `ShowCameo.OverlapXY` has no range restriction — values can be freely set (including negative values to increase spacing). +- `ShowCameo.OffsetXY` accepts any integer value (positive or negative) for fine-tuning. +- `ShowCameo.OverlapXY` 无范围限制,可自由设置(包括负值以增加间距)。 +- `ShowCameo.OffsetXY` 接受任意整数值(正负均可)用于微调。 + +In `RA2MD.INI`: +```ini +[Phobos] +ShowCameo.Enable=true ; boolean, master switch / 总开关 +``` In `rulesmd.ini`: ```ini [AudioVisual] -ShowPassengers=false ; boolean -ShowPassengers.Toggleable=false ; boolean -ShowPassengers.PerRow=5 ; integer, defaults to 5 -ShowPassengers.BottomOffset=0,0 ; X,Y, defaults to 0,0 +ShowCameo=false ; boolean, permanent mode / 永久模式, default false +ShowCameo.Toggleable=false ; boolean, allow hotkey toggle / 允许快捷键切换, default false +ShowCameo.PerRow=5 ; integer, max cameos per row / 每行最大图标数, default 5 +ShowCameo.BottomOffset=0,0 ; X,Y, bottom boundary offset / 下边界偏移, default 0,0 +ShowCameo.Translucency=0 ; translucency level (0/25/50/75) / 透明度, default 0 +ShowCameo.OverlapXY=0,0 ; X,Y overlap percentage / 重叠百分比, no limit, default 0,0 +ShowCameo.OffsetXY=0,0 ; X,Y pixel offset / 像素偏移, default 0,0 +ShowCameo.OverlapPrimary=true ; boolean, primary covers secondary / 前面覆盖后面, default true + +[SOMETECHNO] ; TechnoType (transport building/vehicle / 载具/建筑类型) +ShowCameo= ; boolean, default true (does not follow [AudioVisual] / 不跟随全局默认) +ShowCameo.PerRow= ; integer, defaults to [AudioVisual] -> ShowCameo.PerRow +ShowCameo.BottomOffset= ; X,Y, defaults to [AudioVisual] -> ShowCameo.BottomOffset +ShowCameo.Translucency= ; translucency level (0/25/50/75), defaults to [AudioVisual] +ShowCameo.OverlapXY= ; X,Y, defaults to [AudioVisual] -> ShowCameo.OverlapXY +ShowCameo.OffsetXY= ; X,Y, defaults to [AudioVisual] -> ShowCameo.OffsetXY +ShowCameo.OverlapPrimary= ; boolean, defaults to [AudioVisual] -> ShowCameo.OverlapPrimary +``` -[SOMETECHNO] ; TechnoType -ShowPassengers= ; boolean, defaults to [AudioVisual] -> ShowPassengers -ShowPassengers.PerRow= ; integer, defaults to [AudioVisual] -> ShowPassengers.PerRow -ShowPassengers.BottomOffset= ; X,Y, defaults to [AudioVisual] -> ShowPassengers.BottomOffset +In `artmd.ini`: +```ini +[SOMETECHNO] ; TechnoType (passenger/occupant type / 载员/驻军类型) +ShowCameo.CustomShape= ; SHP filename, custom cameo icon / 自定义图标SHP文件名 +ShowCameo.CustomPalette= ; PAL filename, custom palette / 自定义色盘文件名 ``` -- When toggled via the hotkey, messages are shown from CSF labels `MSG:PassengerCameoDisplayEnabled` (fallback: "Passenger Cameos Display: Enabled") and `MSG:PassengerCameoDisplayDisabled` (fallback: "Passenger Cameos Display: Disabled"). +- When toggled via the hotkey in toggleable mode, messages are shown from CSF labels `MSG:ShowCameoEnabled` (fallback: "Cameos Display: Enabled") and `MSG:ShowCameoDisabled` (fallback: "Cameos Display: Disabled"). +- 切换模式下,通过快捷键切换时显示CSF消息 `MSG:ShowCameoEnabled`(兜底:"Cameos Display: Enabled")和 `MSG:ShowCameoDisabled`(兜底:"Cameos Display: Disabled")。 ## Hotkey Commands @@ -582,10 +665,10 @@ ShowPassengers.BottomOffset= ; X,Y, defaults to [AudioVisual] -> ShowPa - Switches on/off [digital display types](#digital-display). - For localization add `TXT_DIGITAL_DISPLAY` and `TXT_DIGITAL_DISPLAY_DESC` into your `.csf` file. -### `[ ]` Toggle Passengers +### `[ ]` Toggle Cameos -- Switches on/off [passenger cameo display](#passenger-cameo-display). -- For localization add `TXT_PASSENGERCAMEO` and `TXT_PASSENGERCAMEO_DESC` into your `.csf` file. +- Switches on/off [cameo display](#show-cameo) in toggleable mode. Has no effect in permanent mode. +- For localization add `TXT_SHOWCAMEO` and `TXT_SHOWCAMEO_DESC` into your `.csf` file. ### `[ ]` Next Idle Harvester diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 218cfdd897..d90b357d95 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -35,6 +35,7 @@ This serves as a changelog for when you just need to drop the new version in wit - `UseCenterCoordsIfAttached` has been replaced by enumeration key `AttachedAnimPosition`. Set `AttachedAnimPosition=center` to replicate effects of `UseCenterCoordsIfAttached=true`. - Units' `LaserTrails` will no longer lag behind by one frame, so it needs to be repositioned if the position was corrected to account for the bug. - `DeployingAnim.AllowAnyDirection` has been superceded by `DeployDir`. Use value of -1 to re-enable the no facing restriction. +- Show Cameo feature: All `ShowPassengers` tags have been renamed to `ShowCameo` for consistency and to avoid ambiguity with building occupants. The master switch `ShowPassengers.Enable` in `[Phobos]` is now `ShowCameo.Enable` and defaults to `true`. See [Show Cameo](User-Interface.md#show-cameo) for details. - The following tags were renamed for consistency: - `[AttachEffectType] -> AffectTargets` -> `[AttachEffectType] -> AffectsTarget` - `[AttachEffectType] -> ReflectDamage.AffectsHouses` -> `[AttachEffectType] -> ReflectDamage.AffectsHouse` @@ -137,6 +138,7 @@ This serves as a changelog for when you just need to drop the new version in wit CampaignDefaultGameSpeed=4 ; integer ShowBriefing=true ; boolean DigitalDisplay.Enable=false ; boolean +ShowCameo.Enable=true ; boolean ShowDesignatorRange=false ; boolean PrioritySelectionFiltering=true ; boolean PriorityDeployFiltering=true ; boolean diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 31ee97c390..ef90486e81 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -14,7 +14,7 @@ #include "ToggleMessageList.h" #include "DeselectObject.h" #include "DeselectObject5.h" -#include "TogglePassengers.h" +#include "ToggleCameos.h" #include @@ -33,7 +33,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); + MakeCommand(); if (Phobos::Config::SuperWeaponSidebarCommands) { diff --git a/src/Commands/ToggleCameos.cpp b/src/Commands/ToggleCameos.cpp new file mode 100644 index 0000000000..5c9ad1355c --- /dev/null +++ b/src/Commands/ToggleCameos.cpp @@ -0,0 +1,54 @@ +#include "ToggleCameos.h" + +#include +#include +#include +#include + +const char* ToggleCameosCommandClass::GetName() const +{ + return "Toggle Cameos"; +} + +const wchar_t* ToggleCameosCommandClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_SHOWCAMEO", L"Toggle Cameos"); +} + +const wchar_t* ToggleCameosCommandClass::GetUICategory() const +{ + return CATEGORY_INTERFACE; +} + +const wchar_t* ToggleCameosCommandClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_SHOWCAMEO_DESC", L"Show/hide cameo display."); +} + +void ToggleCameosCommandClass::Execute(WWKey eInput) const +{ + // Ignore hotkey in permanent mode + if (RulesExt::Global()->ShowCameo) + return; + + // Ignore hotkey if toggleable is off + if (!RulesExt::Global()->ShowCameo_Toggleable) + return; + + Phobos::Config::ShowCameo_Enable = !Phobos::Config::ShowCameo_Enable; + + auto PrintMessage = [](const wchar_t* pMessage) + { + MessageListClass::Instance.PrintMessage( + pMessage, + RulesClass::Instance->MessageDelay, + HouseClass::CurrentPlayer->ColorSchemeIndex, + true + ); + }; + + if (Phobos::Config::ShowCameo_Enable) + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:ShowCameoEnabled", L"Cameos Display: Enabled")); + else + PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:ShowCameoDisabled", L"Cameos Display: Disabled")); +} \ No newline at end of file diff --git a/src/Commands/TogglePassengers.h b/src/Commands/ToggleCameos.h similarity index 84% rename from src/Commands/TogglePassengers.h rename to src/Commands/ToggleCameos.h index e1e84bb8eb..d77beb37f7 100644 --- a/src/Commands/TogglePassengers.h +++ b/src/Commands/ToggleCameos.h @@ -2,7 +2,7 @@ #include "Commands.h" -class TogglePassengersCommandClass : public CommandClass +class ToggleCameosCommandClass : public CommandClass { public: virtual const char* GetName() const override; diff --git a/src/Commands/TogglePassengers.cpp b/src/Commands/TogglePassengers.cpp deleted file mode 100644 index b6c2860d57..0000000000 --- a/src/Commands/TogglePassengers.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "TogglePassengers.h" - -#include -#include -#include - -const char* TogglePassengersCommandClass::GetName() const -{ - return "Toggle Passengers"; -} - -const wchar_t* TogglePassengersCommandClass::GetUIName() const -{ - return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERCAMEO", L"Toggle Passenger Cameos"); -} - -const wchar_t* TogglePassengersCommandClass::GetUICategory() const -{ - return CATEGORY_INTERFACE; -} - -const wchar_t* TogglePassengersCommandClass::GetUIDescription() const -{ - return GeneralUtils::LoadStringUnlessMissing("TXT_PASSENGERCAMEO_DESC", L"Show/hide passenger cameos display."); -} - -void TogglePassengersCommandClass::Execute(WWKey eInput) const -{ - Phobos::Config::ShowPassengers_Enable = !Phobos::Config::ShowPassengers_Enable; - - auto PrintMessage = [](const wchar_t* pMessage) - { - MessageListClass::Instance.PrintMessage( - pMessage, - RulesClass::Instance->MessageDelay, - HouseClass::CurrentPlayer->ColorSchemeIndex, - true - ); - }; - - if (Phobos::Config::ShowPassengers_Enable) - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengerCameoDisplayEnabled", L"Passenger Cameos Display: Enabled")); - else - PrintMessage(GeneralUtils::LoadStringUnlessMissing("MSG:PassengerCameoDisplayDisabled", L"Passenger Cameos Display: Disabled")); -} \ No newline at end of file diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 5a36edf738..0c213c07a5 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -451,10 +451,14 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->AutoRemoveEarliestBeacon.Read(exINI, GameStrings::General, "AutoRemoveEarliestBeacon"); this->AllowBeaconHotKeyInSinglePlayer.Read(exINI, GameStrings::General, "AllowBeaconHotKeyInSinglePlayer"); - this->ShowPassengers.Read(exINI, GameStrings::AudioVisual, "ShowPassengers"); - this->ShowPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowPassengers.Toggleable"); - this->ShowPassengers_PerRow.Read(exINI, GameStrings::AudioVisual, "ShowPassengers.PerRow"); - this->ShowPassengers_BottomOffset.Read(exINI, GameStrings::AudioVisual, "ShowPassengers.BottomOffset"); + this->ShowCameo.Read(exINI, GameStrings::AudioVisual, "ShowCameo"); + this->ShowCameo_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowCameo.Toggleable"); + this->ShowCameo_PerRow.Read(exINI, GameStrings::AudioVisual, "ShowCameo.PerRow"); + this->ShowCameo_BottomOffset.Read(exINI, GameStrings::AudioVisual, "ShowCameo.BottomOffset"); + this->ShowCameo_Translucency.Read(exINI, GameStrings::AudioVisual, "ShowCameo.Translucency"); + this->ShowCameo_OverlapXY.Read(exINI, GameStrings::AudioVisual, "ShowCameo.OverlapXY"); + this->ShowCameo_OffsetXY.Read(exINI, GameStrings::AudioVisual, "ShowCameo.OffsetXY"); + this->ShowCameo_OverlapPrimary.Read(exINI, GameStrings::AudioVisual, "ShowCameo.OverlapPrimary"); // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); @@ -805,10 +809,14 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->SecondaryFireSequenceLandOnly) .Process(this->AutoRemoveEarliestBeacon) .Process(this->AllowBeaconHotKeyInSinglePlayer) - .Process(this->ShowPassengers) - .Process(this->ShowPassengers_Toggleable) - .Process(this->ShowPassengers_PerRow) - .Process(this->ShowPassengers_BottomOffset) + .Process(this->ShowCameo) + .Process(this->ShowCameo_Toggleable) + .Process(this->ShowCameo_PerRow) + .Process(this->ShowCameo_BottomOffset) + .Process(this->ShowCameo_Translucency) + .Process(this->ShowCameo_OverlapXY) + .Process(this->ShowCameo_OffsetXY) + .Process(this->ShowCameo_OverlapPrimary) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index d8dd01d514..7020a07e6f 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -194,10 +194,14 @@ class RulesExt Valueable MergeBuildingDamage; - Valueable ShowPassengers; - Valueable ShowPassengers_Toggleable; - Valueable ShowPassengers_PerRow; - Valueable ShowPassengers_BottomOffset; + Valueable ShowCameo; + Valueable ShowCameo_Toggleable; + Valueable ShowCameo_PerRow; + Valueable ShowCameo_BottomOffset; + TranslucencyLevel ShowCameo_Translucency; + Valueable ShowCameo_OverlapXY; + Valueable ShowCameo_OffsetXY; + Valueable ShowCameo_OverlapPrimary; Valueable DamageOwnerMultiplier; Valueable DamageAlliesMultiplier; @@ -710,10 +714,14 @@ class RulesExt , AllowBeaconHotKeyInSinglePlayer { false } - , ShowPassengers { false } - , ShowPassengers_Toggleable { false } - , ShowPassengers_PerRow { 5 } - , ShowPassengers_BottomOffset { { 0, 0 } } + , ShowCameo { false } + , ShowCameo_Toggleable { false } + , ShowCameo_PerRow { 5 } + , ShowCameo_BottomOffset { { 0, 0 } } + , ShowCameo_Translucency { 0 } + , ShowCameo_OverlapXY { { 0, 0 } } + , ShowCameo_OffsetXY { { 0, 0 } } + , ShowCameo_OverlapPrimary { true } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 0002f5db91..d7bad777f7 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -876,7 +876,7 @@ void TechnoExt::ShowPromoteAnim(TechnoClass* pThis) AnimExt::CreateRandomAnim(eliteAnims, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true); } -void TechnoExt::DrawPassengers(TechnoClass* pThis) +void TechnoExt::DrawCameos(TechnoClass* pThis) { if (ObjectClass::CurrentObjects.Count != 1) return; @@ -890,87 +890,236 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); const auto pRulesExt = RulesExt::Global(); - // Master switch, highest priority - if (!Phobos::Config::ShowPassengers_Enable) + // Per-unit explicitly disabled - skip entirely + if (pTypeExt->ShowCameo.isset() && !pTypeExt->ShowCameo.Get()) return; - bool shouldShow = false; + // Determine if permanent mode is on (permanent mode bypasses master switch and toggleable) + const bool permanentMode = pTypeExt->ShowCameo.isset() + ? pTypeExt->ShowCameo.Get() + : pRulesExt->ShowCameo; - if (pRulesExt->ShowPassengers || pRulesExt->ShowPassengers_Toggleable) - { - shouldShow = true; - } - - if (pTypeExt->ShowPassengers.isset()) - { - shouldShow = pTypeExt->ShowPassengers.Get(); - } - - if (!shouldShow) + // Master switch check (skipped for permanent mode) + if (!permanentMode && !Phobos::Config::ShowCameo_Enable) return; - if (pThis->Passengers.NumPassengers <= 0) + // Toggleable check (skipped for permanent mode) + if (!permanentMode && !pRulesExt->ShowCameo_Toggleable) return; + // Collect cameos: passengers + occupants + // Passengers are stored in LIFO linked list; for buildings we reverse to FIFO + // so that Y flip produces correct top-to-bottom layout. + // Occupants are stored in FIFO DynamicVectorClass and do NOT need reversing. + std::vector> cameoCounts; + + // Step 1: Collect passengers into a temp vector (LIFO order) std::vector> passengerCounts; - for (auto pPassenger = pThis->Passengers.GetFirstPassenger(); pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) + for (auto pUnit = pThis->Passengers.GetFirstPassenger(); pUnit; pUnit = abstract_cast(pUnit->NextObject)) { - const auto pPassengerType = pPassenger->GetTechnoType(); + const auto pUnitType = pUnit->GetTechnoType(); auto it = std::find_if(passengerCounts.begin(), passengerCounts.end(), - [pPassengerType](const auto& pair) { return pair.first == pPassengerType; }); + [pUnitType](const auto& pair) { return pair.first == pUnitType; }); if (it != passengerCounts.end()) it->second++; else - passengerCounts.emplace_back(pPassengerType, 1); + passengerCounts.emplace_back(pUnitType, 1); + } + + // For buildings, reverse passenger order: LIFO -> FIFO + if (whatAmI == AbstractType::Building) + std::reverse(passengerCounts.begin(), passengerCounts.end()); + + // Merge passengers into cameoCounts + for (const auto& item : passengerCounts) + cameoCounts.emplace_back(item.first, item.second); + + // Step 2: Collect occupants (FIFO order, no reverse needed) + if (whatAmI == AbstractType::Building) + { + const auto pBuilding = static_cast(pThis); + for (int i = 0; i < pBuilding->Occupants.Count; i++) + { + const auto pOccupant = pBuilding->Occupants.GetItem(i); + const auto pOccupantType = pOccupant->GetTechnoType(); + auto it = std::find_if(cameoCounts.begin(), cameoCounts.end(), + [pOccupantType](const auto& pair) { return pair.first == pOccupantType; }); + if (it != cameoCounts.end()) + it->second++; + else + cameoCounts.emplace_back(pOccupantType, 1); + } } - std::reverse(passengerCounts.begin(), passengerCounts.end()); + if (cameoCounts.empty()) + return; const auto bracketPos = whatAmI == AbstractType::Building ? GetBuildingSelectBracketPosition(pThis, BuildingSelectBracketPosition::Top) : GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top)); - Point2D offset = pTypeExt->ShowPassengers_BottomOffset.isset() - ? pTypeExt->ShowPassengers_BottomOffset.Get() - : pRulesExt->ShowPassengers_BottomOffset.Get(); + Point2D offset = pTypeExt->ShowCameo_BottomOffset.isset() + ? pTypeExt->ShowCameo_BottomOffset.Get() + : pRulesExt->ShowCameo_BottomOffset.Get(); Point2D basePos = bracketPos; basePos.X += offset.X; basePos.Y += offset.Y + pType->PixelSelectionBracketDelta; - const int perRow = pTypeExt->ShowPassengers_PerRow.isset() - ? pTypeExt->ShowPassengers_PerRow.Get() - : pRulesExt->ShowPassengers_PerRow.Get(); - const int iconWidth = 60; - const int iconHeight = 48; + const int perRow = pTypeExt->ShowCameo_PerRow.isset() + ? pTypeExt->ShowCameo_PerRow.Get() + : pRulesExt->ShowCameo_PerRow.Get(); + const int count = static_cast(cameoCounts.size()); + const int rows = (count + perRow - 1) / perRow; + + // Get overlap/offset/overlapPrimary settings + const Point2D overlapXY = pTypeExt->ShowCameo_OverlapXY.isset() + ? pTypeExt->ShowCameo_OverlapXY.Get() + : pRulesExt->ShowCameo_OverlapXY.Get(); + const Point2D cameoOffsetXY = pTypeExt->ShowCameo_OffsetXY.isset() + ? pTypeExt->ShowCameo_OffsetXY.Get() + : pRulesExt->ShowCameo_OffsetXY.Get(); + const bool overlapPrimary = pTypeExt->ShowCameo_OverlapPrimary.isset() + ? pTypeExt->ShowCameo_OverlapPrimary.Get() + : pRulesExt->ShowCameo_OverlapPrimary.Get(); + // overlapPrimary=true: primary (earlier) covers secondary (later) (前面覆盖后面) + // overlapPrimary=false: secondary (later) covers primary (earlier) (后面覆盖前面) + + // Pre-compute canvas sizes for each cameo (standard 60x48, or custom SHP dimensions) + struct CameoCanvas { int width; int height; }; + std::vector canvases; + canvases.reserve(count); + for (const auto& item : cameoCounts) + { + const auto pCameoType = item.first; + const auto pCameoTypeExt = TechnoTypeExt::ExtMap.Find(pCameoType); + int w = 60, h = 48; + if (SHPStruct* pCustomCameo = pCameoTypeExt->ShowCameo_CustomShape) + { + w = pCustomCameo->Width; + h = pCustomCameo->Height; + } + canvases.push_back({ w, h }); + } + + // Step 1-2-3: Calculate positions sequentially (row layout + OverlapXY + OffsetXY) + // Overlap formula: overlap = coveredCanvasSize * (OverlapXY% / 100) + OffsetXY + // OverlapPrimary=true: overlap based on secondary cameo's size (secondary is covered) + // OverlapPrimary=false: overlap based on primary cameo's size (primary is covered) + // Base gap between adjacent icons: 2 pixels (hGap / vGap) const int hGap = 2; const int vGap = 2; + // Iterate from bottom row to top row, so that each row can reference the row below it + std::vector cameoPositions(count); + for (int row = 0; row < rows; row++) + { + const int firstIdx = row * perRow; + const int lastIdx = Math::min(firstIdx + perRow - 1, count - 1); + for (int idx = firstIdx; idx <= lastIdx; idx++) + { + const int col = idx - firstIdx; + const int curW = canvases[idx].width; + const int curH = canvases[idx].height; - const int count = static_cast(passengerCounts.size()); - const int rows = (count + perRow - 1) / perRow; + if (row == 0) + { + // Bottom row: anchor at base position (bottom edge fixed at basePos.Y) + if (col == 0) + { + cameoPositions[idx].X = basePos.X; + cameoPositions[idx].Y = basePos.Y - curH; + } + else + { + // Same row: position to the right of the cameo directly to the left + const int prevIdx = idx - 1; + const int prevX = cameoPositions[prevIdx].X; + const int prevW = canvases[prevIdx].width; - const auto pSurface = DSurface::Composite; - RectangleStruct bounds = DSurface::Composite->GetRect(); - RectangleStruct viewBounds = DSurface::ViewBounds; + const int overlapX = overlapPrimary + ? (curW * overlapXY.X / 100 + cameoOffsetXY.X) + : (prevW * overlapXY.X / 100 + cameoOffsetXY.X); - int idx = 0; - for (const auto& item : passengerCounts) + cameoPositions[idx].X = prevX + prevW + hGap - overlapX; + cameoPositions[idx].Y = cameoPositions[prevIdx].Y; + } + } + else + { + if (col == 0) + { + // First in row: position above the cameo directly below + const int belowIdx = (row - 1) * perRow; + const int belowY = cameoPositions[belowIdx].Y; + const int belowH = canvases[belowIdx].height; + + const int overlapY = overlapPrimary + ? (curH * overlapXY.Y / 100 + cameoOffsetXY.Y) + : (belowH * overlapXY.Y / 100 + cameoOffsetXY.Y); + + cameoPositions[idx].X = basePos.X; + cameoPositions[idx].Y = belowY - curH - vGap + overlapY; + } + else + { + // Same row: position to the right of the cameo directly to the left + const int prevIdx = idx - 1; + const int prevX = cameoPositions[prevIdx].X; + const int prevW = canvases[prevIdx].width; + + const int overlapX = overlapPrimary + ? (curW * overlapXY.X / 100 + cameoOffsetXY.X) + : (prevW * overlapXY.X / 100 + cameoOffsetXY.X); + + cameoPositions[idx].X = prevX + prevW + hGap - overlapX; + cameoPositions[idx].Y = cameoPositions[prevIdx].Y; + } + } + } + } + + // Step 4: Center each row horizontally + for (int row = 0; row < rows; row++) + { + const int firstIdx = row * perRow; + const int lastIdx = Math::min(firstIdx + perRow - 1, count - 1); + const int rowWidth = cameoPositions[lastIdx].X + canvases[lastIdx].width - cameoPositions[firstIdx].X; + const int centerOffset = basePos.X - rowWidth / 2 - cameoPositions[firstIdx].X; + for (int i = firstIdx; i <= lastIdx; i++) + cameoPositions[i].X += centerOffset; + } + + // For buildings, flip Y axis: first-entered goes to top, last-entered to bottom + if (whatAmI == AbstractType::Building) { - const auto pPassengerType = item.first; - const int passengerCount = item.second; + int topY = INT_MAX; + for (int i = 0; i < count; i++) + topY = Math::min(topY, cameoPositions[i].Y); - const int row = idx / perRow; - const int col = idx % perRow; + for (int i = 0; i < count; i++) + cameoPositions[i].Y = basePos.Y - canvases[i].height - (cameoPositions[i].Y - topY); + } - const int itemsInThisRow = (row == rows - 1 && count % perRow != 0) ? (count % perRow) : perRow; - const int rowWidth = itemsInThisRow * (iconWidth + hGap) - hGap; - const int startX = basePos.X - rowWidth / 2; + const auto pSurface = DSurface::Composite; + RectangleStruct bounds = DSurface::Composite->GetRect(); - const int y = basePos.Y - iconHeight - (rows - 1 - row) * (iconHeight + vGap); + // Determine BlitterFlags for translucency + const TranslucencyLevel translucency = pTypeExt->ShowCameo_Translucency.isset() + ? pTypeExt->ShowCameo_Translucency.Get() + : pRulesExt->ShowCameo_Translucency; + const BlitterFlags translucencyFlag = translucency.GetBlitterFlags(); - Point2D iconPos = { startX + col * (iconWidth + hGap), y }; + int idx = 0; + for (const auto& item : cameoCounts) + { + const auto pCameoType = item.first; + const int cameoCount = item.second; + const int curW = canvases[idx].width; + const int curH = canvases[idx].height; - // Skip icons that are outside the screen bounds to prevent crash - RectangleStruct iconRect = { iconPos.X, iconPos.Y, iconWidth, iconHeight }; - if (iconRect.X < 0 || iconRect.Y < 0 || iconRect.X + iconRect.Width > viewBounds.Width || iconRect.Y + iconRect.Height > viewBounds.Height) + Point2D iconPos = cameoPositions[idx]; + + // Skip icons whose any part exceeds the screen top boundary (Y < 0) to prevent crash + // Only the top boundary matters because cameos are always above the selection bracket + if (iconPos.Y < 0) { idx++; continue; @@ -978,27 +1127,52 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) bool drawn = false; - const auto pPassengerTypeExt = TechnoTypeExt::ExtMap.Find(pPassengerType); - if (const auto pCameoPCX = pPassengerTypeExt->CameoPCX.GetSurface()) + const auto pCameoTypeExt = TechnoTypeExt::ExtMap.Find(pCameoType); + + // Use custom ShowCameo SHP if specified + if (SHPStruct* pCustomCameo = pCameoTypeExt->ShowCameo_CustomShape) { - RectangleStruct pcxBounds = { iconPos.X, iconPos.Y, iconWidth, iconHeight }; - PCX::Instance.BlitToSurface(&pcxBounds, pSurface, pCameoPCX); + const auto pCameoConvert = pCameoTypeExt->CameoPalette.GetOrDefaultConvert(FileSystem::CAMEO_PAL); + const auto pConvert = pCameoTypeExt->ShowCameo_CustomPalette.GetOrDefaultConvert(pCameoConvert); + const int frameCount = pCustomCameo->Frames > 0 ? pCustomCameo->Frames : 1; + const int frame = (Unsorted::CurrentFrame % frameCount); + pSurface->DrawSHP( + pConvert, + pCustomCameo, + frame, + &iconPos, + &bounds, + BlitterFlags::bf_400 | BlitterFlags::Alpha | translucencyFlag, + 0, 0, + ZGradient::Ground, + 1000, 0, nullptr, 0, 0, 0 + ); drawn = true; } if (!drawn) { - SHPStruct* pCameo = pPassengerType->GetCameo(); + if (const auto pCameoPCX = pCameoTypeExt->CameoPCX.GetSurface()) + { + RectangleStruct pcxBounds = { iconPos.X, iconPos.Y, curW, curH }; + PCX::Instance.BlitToSurface(&pcxBounds, pSurface, pCameoPCX); + drawn = true; + } + } + + if (!drawn) + { + SHPStruct* pCameo = pCameoType->GetCameo(); if (pCameo) { - const auto pConvert = pPassengerTypeExt->CameoPalette.GetOrDefaultConvert(FileSystem::CAMEO_PAL); + const auto pConvert = pCameoTypeExt->CameoPalette.GetOrDefaultConvert(FileSystem::CAMEO_PAL); pSurface->DrawSHP( pConvert, pCameo, 0, &iconPos, &bounds, - BlitterFlags::bf_400 | BlitterFlags::Alpha, + BlitterFlags::bf_400 | BlitterFlags::Alpha | translucencyFlag, 0, 0, ZGradient::Ground, 1000, 0, nullptr, 0, 0, 0 @@ -1010,9 +1184,9 @@ void TechnoExt::DrawPassengers(TechnoClass* pThis) if (drawn) { wchar_t countText[16]; - _snwprintf_s(countText, _countof(countText), _TRUNCATE, L"%d", passengerCount); + _snwprintf_s(countText, _countof(countText), _TRUNCATE, L"%d", cameoCount); - Point2D textPos = { iconPos.X + iconWidth - 4, iconPos.Y + 2 }; + Point2D textPos = { iconPos.X + curW - 4, iconPos.Y + 2 }; pSurface->DrawTextA( countText, diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a69c02c43f..9651713f25 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -321,7 +321,7 @@ class TechnoExt static bool EjectSurvivor(FootClass* pSurvivor, CoordStruct coords, bool select); static bool __fastcall ApplyKillDriver(TechnoClass** pData, void*, HouseClass* pToHouse, TechnoClass* pKiller, bool resetVeterancy); - static void DrawPassengers(TechnoClass* pThis); + static void DrawCameos(TechnoClass* pThis); // WeaponHelpers.cpp static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true); diff --git a/src/Ext/Techno/Hooks.Pips.cpp b/src/Ext/Techno/Hooks.Pips.cpp index 86c82a5dc9..7d8b842aa4 100644 --- a/src/Ext/Techno/Hooks.Pips.cpp +++ b/src/Ext/Techno/Hooks.Pips.cpp @@ -94,7 +94,7 @@ DEFINE_HOOK(0x6F65D1, TechnoClass_DrawHealthBar_Buildings, 0x6) TechnoExt::ProcessDigitalDisplays(pThis); - TechnoExt::DrawPassengers(pThis); + TechnoExt::DrawCameos(pThis); return 0; } @@ -126,7 +126,7 @@ DEFINE_HOOK(0x6F683C, TechnoClass_DrawHealthBar_Units, 0x7) TechnoExt::ProcessDigitalDisplays(pThis); - TechnoExt::DrawPassengers(pThis); + TechnoExt::DrawCameos(pThis); if (pExt->TypeExtData->HealthBar_HidePips) { diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index aae1a4ca05..8191152b5d 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1344,6 +1344,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->CameoPCX.Read(pArtINI, pArtSection, "CameoPCX"); this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); + this->ShowCameo_CustomPalette.LoadFromINI(pArtINI, pArtSection, "ShowCameo.CustomPalette"); + this->ShowCameo_CustomShape.Read(exArtINI, pArtSection, "ShowCameo.CustomShape"); this->LaserTrailData.clear(); for (size_t i = 0; ; ++i) @@ -1464,9 +1466,13 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->VoiceIFVRepair.Read(exINI, pSection, "VoiceIFVRepair"); this->ParseVoiceWeaponAttacks(exINI, pSection, this->VoiceWeaponAttacks, this->VoiceEliteWeaponAttacks); - this->ShowPassengers.Read(exINI, pSection, "ShowPassengers"); - this->ShowPassengers_PerRow.Read(exINI, pSection, "ShowPassengers.PerRow"); - this->ShowPassengers_BottomOffset.Read(exINI, pSection, "ShowPassengers.BottomOffset"); + this->ShowCameo.Read(exINI, pSection, "ShowCameo"); + this->ShowCameo_PerRow.Read(exINI, pSection, "ShowCameo.PerRow"); + this->ShowCameo_BottomOffset.Read(exINI, pSection, "ShowCameo.BottomOffset"); + this->ShowCameo_Translucency.Read(exINI, pSection, "ShowCameo.Translucency"); + this->ShowCameo_OverlapXY.Read(exINI, pSection, "ShowCameo.OverlapXY"); + this->ShowCameo_OffsetXY.Read(exINI, pSection, "ShowCameo.OffsetXY"); + this->ShowCameo_OverlapPrimary.Read(exINI, pSection, "ShowCameo.OverlapPrimary"); this->CameoPalette.LoadFromINI(pArtINI, pArtSection, "CameoPalette"); } @@ -1540,6 +1546,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->CameoPriority) .Process(this->CameoPCX) .Process(this->AltCameoPCX) + .Process(this->ShowCameo_CustomPalette) + .Process(this->ShowCameo_CustomShape) .Process(this->NoManualMove) .Process(this->InitialStrength) .Process(this->ReloadInTransport) @@ -1980,11 +1988,16 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->Missile_TakeOffAnim) .Process(this->Missile_TakeOffSeparation) - .Process(this->ShowPassengers) - .Process(this->ShowPassengers_PerRow) - .Process(this->ShowPassengers_BottomOffset) + .Process(this->ShowCameo) + .Process(this->ShowCameo_PerRow) + .Process(this->ShowCameo_BottomOffset) + .Process(this->ShowCameo_Translucency) + .Process(this->ShowCameo_OverlapXY) + .Process(this->ShowCameo_OffsetXY) + .Process(this->ShowCameo_OverlapPrimary) .Process(this->CameoPalette) - ; + .Process(this->ShowCameo_CustomPalette) + .Process(this->ShowCameo_CustomShape) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 38b76ae79b..1a28645bd7 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -523,10 +523,16 @@ class TechnoTypeExt Valueable Missile_TakeOffAnim; Valueable Missile_TakeOffSeparation; - Nullable ShowPassengers; - Nullable ShowPassengers_PerRow; - Nullable ShowPassengers_BottomOffset; + Nullable ShowCameo; + Nullable ShowCameo_PerRow; + Nullable ShowCameo_BottomOffset; + Nullable ShowCameo_Translucency; + Nullable ShowCameo_OverlapXY; + Nullable ShowCameo_OffsetXY; + Nullable ShowCameo_OverlapPrimary; CustomPalette CameoPalette; + CustomPalette ShowCameo_CustomPalette; + TheaterSpecificSHP ShowCameo_CustomShape; ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) , HealthBar_Hide { false } @@ -1004,10 +1010,16 @@ class TechnoTypeExt , Missile_TakeOffAnim { nullptr } , Missile_TakeOffSeparation { 24 } - , ShowPassengers {} - , ShowPassengers_PerRow {} - , ShowPassengers_BottomOffset {} + , ShowCameo {} + , ShowCameo_PerRow {} + , ShowCameo_BottomOffset {} + , ShowCameo_Translucency {} + , ShowCameo_OverlapXY {} + , ShowCameo_OffsetXY {} + , ShowCameo_OverlapPrimary {} , CameoPalette {} + , ShowCameo_CustomPalette {} + , ShowCameo_CustomShape {} { } virtual ~ExtData() = default; diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 5dfe63ace2..6463cd7b7f 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -57,7 +57,7 @@ bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::EnableSelectBox = false; bool Phobos::Config::DigitalDisplay_Enable = false; -bool Phobos::Config::ShowPassengers_Enable = false; +bool Phobos::Config::ShowCameo_Enable = true; bool Phobos::Config::MessageApplyHoverState = false; bool Phobos::Config::MessageDisplayInCenter = false; int Phobos::Config::MessageDisplayInCenter_BoardOpacity = 40; @@ -106,7 +106,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "RealTimeTimers.Adaptive", false); Phobos::Config::EnableSelectBox = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "EnableSelectBox", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "DigitalDisplay.Enable", false); - Phobos::Config::ShowPassengers_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPassengers.Enable", false); + Phobos::Config::ShowCameo_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowCameo.Enable", true); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "SaveGameOnScenarioStart", true); Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowBriefing", true); Phobos::Config::ShowPowerDelta = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPowerDelta", true); diff --git a/src/Phobos.h b/src/Phobos.h index 7ca5045552..2b72d8cf0a 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -92,7 +92,7 @@ class Phobos static bool EnableBuildingPlacementPreview; static bool EnableSelectBox; static bool DigitalDisplay_Enable; - static bool ShowPassengers_Enable; + static bool ShowCameo_Enable; static bool MessageApplyHoverState; static bool MessageDisplayInCenter; static int MessageDisplayInCenter_BoardOpacity; From a01c29ad2e17679366f2d08a6c474a347b4debe5 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Mon, 6 Jul 2026 19:25:49 +0800 Subject: [PATCH 21/30] fix: address review feedback - simplify docs, fix English comments, fix formatting - Move ShowCameo.Enable to its own [Phobos] block in Show Cameo section - Rewrite Show Cameo docs to be concise, English-only, following Phobos style - Remove Chinese from comments in Body.Visuals.cpp - Remove blank line before CameoPalette in TechnoType/Body.cpp - Fix ; placement in serialization chain --- docs/User-Interface.md | 115 +++++++++----------------------- src/Ext/Techno/Body.Visuals.cpp | 4 +- src/Ext/TechnoType/Body.cpp | 4 +- 3 files changed, 34 insertions(+), 89 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 06c3e985ec..204e64b62d 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -189,7 +189,6 @@ In `RA2MD.INI`: ```ini [Phobos] DigitalDisplay.Enable=false ; boolean -ShowCameo.Enable=true ; boolean ``` ```{note} @@ -527,112 +526,58 @@ In `RA2MD.INI`: ShowPowerPlantEnhancerRange=false ; boolean ``` -### Show Cameo / 显示载员图标 +### Show Cameo -- You can now configure the game to show cameo icons and counts above a vehicle or building when it is selected. Both vehicle passengers (`Passengers=`) and garrisoned infantry / occupants (`Occupy=`) are displayed. Each passenger/occupant type is shown as its cameo with a count number in the upper-right corner. Only shown when a single unit is selected (not box-selection). -- 选中载具或建筑时,在其上方显示载员(`Passengers=`)和驻军(`Occupy=`)的图标与数量。图标为对应单位的Cameo,右上角显示该类型数量。仅单选时显示,框选不显示。 - -#### Activation modes / 激活模式 - -- Two activation modes: - - **Permanent mode / 永久模式**: Set `ShowCameo=true` (global) or `ShowCameo=true` (per-unit) to always show the cameos, regardless of the hotkey state. Per-unit setting overrides the global setting. If a per-unit `ShowCameo` is explicitly set to `false`, cameos are never shown for that unit. - - **Toggleable mode / 切换模式**: Set `ShowCameo=false` (global) and `ShowCameo.Toggleable=true` (global), then use the [Toggle Cameos](#toggle-cameos) hotkey to switch the display on/off. The master switch `ShowCameo.Enable` in `RA2MD.INI` controls the toggle state. -- 两种模式: - - **永久模式**:设置全局 `ShowCameo=true` 或单位 `ShowCameo=true` 后始终显示图标,不受快捷键影响。单位级设置覆盖全局设置。若单位级 `ShowCameo` 显式设为 `false`,则该单位永不显示图标。 - - **切换模式**:全局 `ShowCameo=false` 且 `ShowCameo.Toggleable=true`,使用快捷键切换显示/隐藏。总开关 `ShowCameo.Enable` 在 `RA2MD.INI` 中控制。 - -#### Display order / 显示顺序 - -- Cameos are displayed with the **first-entered unit at the top** and the **last-entered unit at the bottom**. For vehicles, the passenger linked list is LIFO (last-in-first-out), so the natural iteration order (last-entered first) already produces the correct layout. For buildings, the passenger list is also LIFO but the Y-axis is flipped to match the same top-to-bottom convention. Occupants (garrisoned infantry) are stored in FIFO order and are displayed directly without reversing. -- 图标按**先进先上、后进先下**的顺序排列。载具的乘客链表为LIFO(后进先出),自然迭代即产生正确布局。建筑的乘客链表同样为LIFO,但通过Y轴翻转后与载具保持一致的显示顺序。驻军(进驻步兵)按FIFO存储,直接显示,无需反转。 - -#### Row layout / 行布局 - -- Cameos are arranged in horizontal rows, centered from left to right above the selection bracket, with the bottom row fixed at the bottom boundary. When the number of cameo types exceeds `ShowCameo.PerRow`, additional rows are stacked above the bottom row. -- 图标按水平行排列,自左向右居中于选择框上方,底行固定在下边界。当图标类型数量超过 `ShowCameo.PerRow` 时,在底行上方堆叠更多行。 - -#### Custom cameo shape / 自定义图标 - -- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. The value is a SHP filename (e.g., `mycameo.shp`). Animated SHPs are supported (frames cycle automatically). The SHP's actual dimensions define its canvas size for overlap calculations. -- 可在 `artmd.ini` 中通过 `ShowCameo.CustomShape` 为每种载员/驻军类型指定自定义SHP图标(如 `mycameo.shp`)。支持动画SHP(帧自动循环)。SHP的实际尺寸决定其画布大小。 -- A custom palette can be specified via `ShowCameo.CustomPalette` in `artmd.ini`. If not set, the palette falls back to `CameoPalette` (from Ares) or the default `CAMEO_PAL`. -- 可通过 `artmd.ini` 中的 `ShowCameo.CustomPalette` 指定自定义色盘。未设置时回退到 `CameoPalette`(Ares标签)或默认的 `CAMEO_PAL`。 - -#### Translucency / 透明度 - -- Transparency can be controlled via `ShowCameo.Translucency` (values 0/25/50/75, default 0 = fully opaque). The translucency applies to SHP cameos; PCX cameos are drawn without transparency. -- 透明度通过 `ShowCameo.Translucency` 控制(取值 0/25/50/75,默认 0 = 完全不透明)。透明度仅作用于SHP图标,PCX图标无透明效果。 - -#### Icon overlap and positioning / 图标重叠与定位 - -Each cameo icon occupies a **canvas** (rectangle area). For standard cameos, the canvas is 60x48 pixels. For custom SHPs, the canvas is the SHP's actual dimensions (`Width` x `Height`). -每个图标占据一个**画布**(矩形区域)。标准图标的画布为60x48像素,自定义SHP的画布为其实际尺寸。 - -The positioning logic follows this order / 定位顺序: -1. **Row layout / 行布局**: Cameos are placed in rows according to `ShowCameo.PerRow`. -2. **OverlapXY / 重叠百分比**: Percentage-based overlap is applied. -3. **OffsetXY / 像素偏移**: Pixel-based offset is added. -4. **Centering / 居中**: Each row is horizontally centered. - -The overlap formula / 重叠公式: -``` -overlap = coveredCanvasSize * (OverlapXY% / 100) + OffsetXY -``` -- `ShowCameo.OverlapPrimary=true` (default): the primary (earlier) cameo covers the secondary (later) cameo. Overlap is based on the secondary cameo's canvas size. -- `ShowCameo.OverlapPrimary=false`: the secondary (later) cameo covers the primary (earlier) cameo. Overlap is based on the primary cameo's canvas size. -- `ShowCameo.OverlapPrimary=true`(默认):前面的图标覆盖后面的图标,重叠量基于后面图标的画布尺寸计算。 -- `ShowCameo.OverlapPrimary=false`:后面的图标覆盖前面的图标,重叠量基于前面图标的画布尺寸计算。 -- On the same row, only X overlap matters (Y has no effect). When wrapping to a new row, only Y overlap matters (X has no effect). -- 同行只有X重叠生效(Y无效果),换行只有Y重叠生效(X无效果)。 +- You can now configure the game to display cameo icons and their counts above a selected vehicle or building, showing both passengers (`Passengers=`) and garrisoned occupants (`Occupy=`). Only shown when a single unit is selected (not box-selection). +- Two activation modes are available: + - **Permanent mode**: Set `ShowCameo=true` (globally or per-unit) to always display cameos. Per-unit setting overrides the global setting. If a per-unit `ShowCameo` is explicitly set to `false`, cameos are never shown for that unit. + - **Toggleable mode**: Set `ShowCameo=false` (global) and `ShowCameo.Toggleable=true` (global), then use the [Toggle Cameos](#toggle-cameos) hotkey to switch the display on/off. The master switch `ShowCameo.Enable` in `RA2MD.INI` controls the toggle state. +- Cameos are arranged in horizontal rows, centered above the selection bracket. When the number of cameo types exceeds `ShowCameo.PerRow`, additional rows are stacked above the bottom row. The first-entered unit is displayed at the top. +- Translucency can be set via `ShowCameo.Translucency` (0/25/50/75, default 0). SHP cameos are affected; PCX cameos are not. +- `ShowCameo.OverlapXY` and `ShowCameo.OffsetXY` control overlap between adjacent cameos. `ShowCameo.OverlapPrimary` (default true) determines whether the earlier cameo covers the later one. On the same row, only X overlap applies; when wrapping to a new row, only Y overlap applies. +- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. Animated SHPs are supported. A custom palette can be specified via `ShowCameo.CustomPalette`; if not set, the palette falls back to `CameoPalette` or the default `CAMEO_PAL`. ```{hint} For example, with `ShowCameo.PerRow=1` (vertical stacking), `ShowCameo.OverlapPrimary=true` means the bottom cameo covers the one above it, preserving the bottom cameo's text label from being hidden. -例如,`ShowCameo.PerRow=1`(垂直排列)时,`ShowCameo.OverlapPrimary=true` 表示底部的图标覆盖上方图标,保证底部图标的数量文字不被遮挡。 ``` -- `ShowCameo.OverlapXY` has no range restriction — values can be freely set (including negative values to increase spacing). -- `ShowCameo.OffsetXY` accepts any integer value (positive or negative) for fine-tuning. -- `ShowCameo.OverlapXY` 无范围限制,可自由设置(包括负值以增加间距)。 -- `ShowCameo.OffsetXY` 接受任意整数值(正负均可)用于微调。 +- When toggled via the hotkey, messages are shown from CSF labels `MSG:ShowCameoEnabled` (fallback: "Cameos Display: Enabled") and `MSG:ShowCameoDisabled` (fallback: "Cameos Display: Disabled"). In `RA2MD.INI`: ```ini [Phobos] -ShowCameo.Enable=true ; boolean, master switch / 总开关 +ShowCameo.Enable=true ; boolean ``` In `rulesmd.ini`: ```ini [AudioVisual] -ShowCameo=false ; boolean, permanent mode / 永久模式, default false -ShowCameo.Toggleable=false ; boolean, allow hotkey toggle / 允许快捷键切换, default false -ShowCameo.PerRow=5 ; integer, max cameos per row / 每行最大图标数, default 5 -ShowCameo.BottomOffset=0,0 ; X,Y, bottom boundary offset / 下边界偏移, default 0,0 -ShowCameo.Translucency=0 ; translucency level (0/25/50/75) / 透明度, default 0 -ShowCameo.OverlapXY=0,0 ; X,Y overlap percentage / 重叠百分比, no limit, default 0,0 -ShowCameo.OffsetXY=0,0 ; X,Y pixel offset / 像素偏移, default 0,0 -ShowCameo.OverlapPrimary=true ; boolean, primary covers secondary / 前面覆盖后面, default true - -[SOMETECHNO] ; TechnoType (transport building/vehicle / 载具/建筑类型) -ShowCameo= ; boolean, default true (does not follow [AudioVisual] / 不跟随全局默认) -ShowCameo.PerRow= ; integer, defaults to [AudioVisual] -> ShowCameo.PerRow -ShowCameo.BottomOffset= ; X,Y, defaults to [AudioVisual] -> ShowCameo.BottomOffset +ShowCameo=false ; boolean +ShowCameo.Toggleable=false ; boolean +ShowCameo.PerRow=5 ; integer +ShowCameo.BottomOffset=0,0 ; X,Y +ShowCameo.Translucency=0 ; translucency level (0/25/50/75) +ShowCameo.OverlapXY=0,0 ; X,Y overlap percentage +ShowCameo.OffsetXY=0,0 ; X,Y pixel offset +ShowCameo.OverlapPrimary=true ; boolean + +[SOMETECHNO] ; TechnoType +ShowCameo= ; boolean, default true +ShowCameo.PerRow= ; integer, defaults to [AudioVisual] +ShowCameo.BottomOffset= ; X,Y, defaults to [AudioVisual] ShowCameo.Translucency= ; translucency level (0/25/50/75), defaults to [AudioVisual] -ShowCameo.OverlapXY= ; X,Y, defaults to [AudioVisual] -> ShowCameo.OverlapXY -ShowCameo.OffsetXY= ; X,Y, defaults to [AudioVisual] -> ShowCameo.OffsetXY -ShowCameo.OverlapPrimary= ; boolean, defaults to [AudioVisual] -> ShowCameo.OverlapPrimary +ShowCameo.OverlapXY= ; X,Y, defaults to [AudioVisual] +ShowCameo.OffsetXY= ; X,Y, defaults to [AudioVisual] +ShowCameo.OverlapPrimary= ; boolean, defaults to [AudioVisual] ``` In `artmd.ini`: ```ini -[SOMETECHNO] ; TechnoType (passenger/occupant type / 载员/驻军类型) -ShowCameo.CustomShape= ; SHP filename, custom cameo icon / 自定义图标SHP文件名 -ShowCameo.CustomPalette= ; PAL filename, custom palette / 自定义色盘文件名 +[SOMETECHNO] ; TechnoType +ShowCameo.CustomShape= ; SHP filename +ShowCameo.CustomPalette= ; PAL filename ``` -- When toggled via the hotkey in toggleable mode, messages are shown from CSF labels `MSG:ShowCameoEnabled` (fallback: "Cameos Display: Enabled") and `MSG:ShowCameoDisabled` (fallback: "Cameos Display: Disabled"). -- 切换模式下,通过快捷键切换时显示CSF消息 `MSG:ShowCameoEnabled`(兜底:"Cameos Display: Enabled")和 `MSG:ShowCameoDisabled`(兜底:"Cameos Display: Disabled")。 - ## Hotkey Commands ### `[ ]` Display Damage Numbers diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index d7bad777f7..c1ac49d725 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -980,8 +980,8 @@ void TechnoExt::DrawCameos(TechnoClass* pThis) const bool overlapPrimary = pTypeExt->ShowCameo_OverlapPrimary.isset() ? pTypeExt->ShowCameo_OverlapPrimary.Get() : pRulesExt->ShowCameo_OverlapPrimary.Get(); - // overlapPrimary=true: primary (earlier) covers secondary (later) (前面覆盖后面) - // overlapPrimary=false: secondary (later) covers primary (earlier) (后面覆盖前面) + // overlapPrimary=true: primary (earlier) covers secondary (later) + // overlapPrimary=false: secondary (later) covers primary (earlier) // Pre-compute canvas sizes for each cameo (standard 60x48, or custom SHP dimensions) struct CameoCanvas { int width; int height; }; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 8191152b5d..a309dca7c1 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1473,7 +1473,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShowCameo_OverlapXY.Read(exINI, pSection, "ShowCameo.OverlapXY"); this->ShowCameo_OffsetXY.Read(exINI, pSection, "ShowCameo.OffsetXY"); this->ShowCameo_OverlapPrimary.Read(exINI, pSection, "ShowCameo.OverlapPrimary"); - this->CameoPalette.LoadFromINI(pArtINI, pArtSection, "CameoPalette"); } @@ -1997,7 +1996,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->ShowCameo_OverlapPrimary) .Process(this->CameoPalette) .Process(this->ShowCameo_CustomPalette) - .Process(this->ShowCameo_CustomShape) ; + .Process(this->ShowCameo_CustomShape) + ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { From 424ce7391797bc92c909c245bf52bfaea7d2cda5 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Mon, 6 Jul 2026 20:08:06 +0800 Subject: [PATCH 22/30] fix: reverse draw order for buildings so that OverlapPrimary works correctly with Y flip - When OverlapPrimary=true, earlier (visual top) cameos should cover later (visual bottom) ones - For buildings, the Y flip reverses visual order but draw order still followed cameoCounts - Fix: draw buildings in reverse cameoCounts order so visual top draws last --- src/Ext/Techno/Body.Visuals.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index c1ac49d725..23d1b5ee6b 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -1107,9 +1107,15 @@ void TechnoExt::DrawCameos(TechnoClass* pThis) : pRulesExt->ShowCameo_Translucency; const BlitterFlags translucencyFlag = translucency.GetBlitterFlags(); - int idx = 0; - for (const auto& item : cameoCounts) + // For buildings, draw in reverse so that visual top (earlier-entered) covers visual bottom. + // This is needed because Y flip reverses the visual order but overlap calculation + // still follows the original cameoCounts (bottom-to-top) order. + int idx = (whatAmI == AbstractType::Building) ? count - 1 : 0; + const int idxEnd = (whatAmI == AbstractType::Building) ? -1 : count; + const int idxStep = (whatAmI == AbstractType::Building) ? -1 : 1; + for (; idx != idxEnd; idx += idxStep) { + const auto& item = cameoCounts[idx]; const auto pCameoType = item.first; const int cameoCount = item.second; const int curW = canvases[idx].width; @@ -1120,10 +1126,7 @@ void TechnoExt::DrawCameos(TechnoClass* pThis) // Skip icons whose any part exceeds the screen top boundary (Y < 0) to prevent crash // Only the top boundary matters because cameos are always above the selection bracket if (iconPos.Y < 0) - { - idx++; continue; - } bool drawn = false; @@ -1197,7 +1200,5 @@ void TechnoExt::DrawCameos(TechnoClass* pThis) TextPrintType::Center | TextPrintType::FullShadow ); } - - idx++; } } From 2a35501562189702818ad2a8dc34e800e44cb322 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Mon, 6 Jul 2026 21:28:05 +0800 Subject: [PATCH 23/30] fix: address second round review - placement and formatting - Move Toggle Cameos to end of Hotkey Commands section - Move ShowCameo changelog entry from Breaking changes to New features - Move ShowCameo declarations to end of public block in Rules/Body.h - Restore blank line before BuildingSelectBracketPosition in Enum.h - Move ShowCameo_Enable to end of Config struct in Phobos.h - Move ShowCameo_Enable fields to end of Config blocks in Phobos.INI.cpp - Move ToggleCameos files to end of Commands block in Phobos.vcxproj --- Phobos.vcxproj | 4 ++-- docs/User-Interface.md | 10 +++++----- docs/Whats-New.md | 2 +- src/Ext/Rules/Body.h | 18 +++++++++--------- src/Phobos.INI.cpp | 4 ++-- src/Phobos.h | 2 +- src/Utilities/Enum.h | 1 + 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index d89c9cecee..df818f4921 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -45,9 +45,9 @@ - + @@ -309,9 +309,9 @@ - + diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 204e64b62d..f442cb9070 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -610,11 +610,6 @@ ShowCameo.CustomPalette= ; PAL filename - Switches on/off [digital display types](#digital-display). - For localization add `TXT_DIGITAL_DISPLAY` and `TXT_DIGITAL_DISPLAY_DESC` into your `.csf` file. -### `[ ]` Toggle Cameos - -- Switches on/off [cameo display](#show-cameo) in toggleable mode. Has no effect in permanent mode. -- For localization add `TXT_SHOWCAMEO` and `TXT_SHOWCAMEO_DESC` into your `.csf` file. - ### `[ ]` Next Idle Harvester - Selects and centers the camera on the next TechnoType that is counted via the [harvester counter](#harvester-counter) and is currently idle. @@ -637,6 +632,11 @@ For this command to work in multiplayer - you need to use a version of [YRpp spa - Switches on/off [Task subtitles' label in the middle of the screen](#task-subtitles-display-in-the-middle-of-the-screen). - For localization add `TXT_TOGGLE_MESSAGE` and `TXT_TOGGLE_MESSAGE_DESC` into your `.csf` file. +### `[ ]` Toggle Cameos + +- Switches on/off [cameo display](#show-cameo) in toggleable mode. Has no effect in permanent mode. +- For localization add `TXT_SHOWCAMEO` and `TXT_SHOWCAMEO_DESC` into your `.csf` file. + ### `[ ]` Deselect Object(s) - Deselect 1 or 5 object(s) from current selected objects. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index d90b357d95..be8f676e02 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -35,7 +35,6 @@ This serves as a changelog for when you just need to drop the new version in wit - `UseCenterCoordsIfAttached` has been replaced by enumeration key `AttachedAnimPosition`. Set `AttachedAnimPosition=center` to replicate effects of `UseCenterCoordsIfAttached=true`. - Units' `LaserTrails` will no longer lag behind by one frame, so it needs to be repositioned if the position was corrected to account for the bug. - `DeployingAnim.AllowAnyDirection` has been superceded by `DeployDir`. Use value of -1 to re-enable the no facing restriction. -- Show Cameo feature: All `ShowPassengers` tags have been renamed to `ShowCameo` for consistency and to avoid ambiguity with building occupants. The master switch `ShowPassengers.Enable` in `[Phobos]` is now `ShowCameo.Enable` and defaults to `true`. See [Show Cameo](User-Interface.md#show-cameo) for details. - The following tags were renamed for consistency: - `[AttachEffectType] -> AffectTargets` -> `[AttachEffectType] -> AffectsTarget` - `[AttachEffectType] -> ReflectDamage.AffectsHouses` -> `[AttachEffectType] -> ReflectDamage.AffectsHouse` @@ -694,6 +693,7 @@ HideShakeEffects=false ; boolean - Fixed the bug that low-air taking off / landing objects will receive twice damage (by NetsuNegi) - Fixed voxel projectile and animation lighting issues (by TaranDahl) - Fixed the bug that techno will get stuck if change owner in tunnel (by NetsuNegi) +- Show Cameo feature to display passenger/occupant cameos above selected vehicles/buildings (see [User Interface](User-Interface.md#show-cameo)) #### Phobos fixes: - Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 7020a07e6f..baa8abb772 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -194,15 +194,6 @@ class RulesExt Valueable MergeBuildingDamage; - Valueable ShowCameo; - Valueable ShowCameo_Toggleable; - Valueable ShowCameo_PerRow; - Valueable ShowCameo_BottomOffset; - TranslucencyLevel ShowCameo_Translucency; - Valueable ShowCameo_OverlapXY; - Valueable ShowCameo_OffsetXY; - Valueable ShowCameo_OverlapPrimary; - Valueable DamageOwnerMultiplier; Valueable DamageAlliesMultiplier; Valueable DamageEnemiesMultiplier; @@ -391,6 +382,15 @@ class RulesExt Valueable AutoRemoveEarliestBeacon; Valueable AllowBeaconHotKeyInSinglePlayer; + Valueable ShowCameo; + Valueable ShowCameo_Toggleable; + Valueable ShowCameo_PerRow; + Valueable ShowCameo_BottomOffset; + TranslucencyLevel ShowCameo_Translucency; + Valueable ShowCameo_OverlapXY; + Valueable ShowCameo_OffsetXY; + Valueable ShowCameo_OverlapPrimary; + ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } , HarvesterDumpAmount { 0.0f } diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 6463cd7b7f..2fbea55fb6 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -57,7 +57,6 @@ bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::EnableSelectBox = false; bool Phobos::Config::DigitalDisplay_Enable = false; -bool Phobos::Config::ShowCameo_Enable = true; bool Phobos::Config::MessageApplyHoverState = false; bool Phobos::Config::MessageDisplayInCenter = false; int Phobos::Config::MessageDisplayInCenter_BoardOpacity = 40; @@ -81,6 +80,7 @@ bool Phobos::Config::HideShakeEffects = true; bool Phobos::Config::ShowFlashOnSelecting = false; bool Phobos::Config::UnitPowerDrain = false; int Phobos::Config::SuperWeaponSidebar_RequiredSignificance = 0; +bool Phobos::Config::ShowCameo_Enable = true; bool Phobos::Misc::CustomGS = false; int Phobos::Misc::CustomGS_ChangeInterval[7] = { -1, -1, -1, -1, -1, -1, -1 }; @@ -106,7 +106,6 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "RealTimeTimers.Adaptive", false); Phobos::Config::EnableSelectBox = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "EnableSelectBox", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "DigitalDisplay.Enable", false); - Phobos::Config::ShowCameo_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowCameo.Enable", true); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "SaveGameOnScenarioStart", true); Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowBriefing", true); Phobos::Config::ShowPowerDelta = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowPowerDelta", true); @@ -117,6 +116,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::HideShakeEffects = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "HideShakeEffects", false); Phobos::Config::ShowFlashOnSelecting = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowFlashOnSelecting", false); Phobos::Config::SuperWeaponSidebar_RequiredSignificance = CCINIClass::INI_RA2MD.ReadInteger(phobosSection, "SuperWeaponSidebar.RequiredSignificance", 0); + Phobos::Config::ShowCameo_Enable = CCINIClass::INI_RA2MD.ReadBool(phobosSection, "ShowCameo.Enable", true); // Custom game speeds, 6 - i so that GS6 is index 0, just like in the engine Phobos::Config::CampaignDefaultGameSpeed = 6 - CCINIClass::INI_RA2MD.ReadInteger(phobosSection, "CampaignDefaultGameSpeed", 4); diff --git a/src/Phobos.h b/src/Phobos.h index 2b72d8cf0a..fc76be3878 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -92,7 +92,6 @@ class Phobos static bool EnableBuildingPlacementPreview; static bool EnableSelectBox; static bool DigitalDisplay_Enable; - static bool ShowCameo_Enable; static bool MessageApplyHoverState; static bool MessageDisplayInCenter; static int MessageDisplayInCenter_BoardOpacity; @@ -117,6 +116,7 @@ class Phobos static bool ShowFlashOnSelecting; static bool UnitPowerDrain; static int SuperWeaponSidebar_RequiredSignificance; + static bool ShowCameo_Enable; }; class Misc diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 2e9d9645f4..922b95c87f 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -275,6 +275,7 @@ enum class VerticalPosition : BYTE Center = 1, Bottom = 2 }; + //hexagon enum class BuildingSelectBracketPosition :BYTE { From a9560501301a47f03f49eacb8eeaea7a6482094c Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Mon, 6 Jul 2026 21:56:15 +0800 Subject: [PATCH 24/30] fix: micro-review - move Toggle Cameos after Deselect, clean up Whats-New - Move Toggle Cameos to after Deselect Object(s), before Loading screen - Remove ShowCameo.Enable from New user settings (0.2 version log) - Remove duplicate ShowCameo entry from Vanilla fixes (already in New section) --- docs/User-Interface.md | 10 +++++----- docs/Whats-New.md | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index f442cb9070..7a3bff7822 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -632,16 +632,16 @@ For this command to work in multiplayer - you need to use a version of [YRpp spa - Switches on/off [Task subtitles' label in the middle of the screen](#task-subtitles-display-in-the-middle-of-the-screen). - For localization add `TXT_TOGGLE_MESSAGE` and `TXT_TOGGLE_MESSAGE_DESC` into your `.csf` file. -### `[ ]` Toggle Cameos - -- Switches on/off [cameo display](#show-cameo) in toggleable mode. Has no effect in permanent mode. -- For localization add `TXT_SHOWCAMEO` and `TXT_SHOWCAMEO_DESC` into your `.csf` file. - ### `[ ]` Deselect Object(s) - Deselect 1 or 5 object(s) from current selected objects. - For localization add `TXT_DESELECT`, `TXT_DESELECT_DESC`, `TXT_DESELECT5` and `TXT_DESELECT5_DESC` into your `.csf` file. +### `[ ]` Toggle Cameos + +- Switches on/off [cameo display](#show-cameo) in toggleable mode. Has no effect in permanent mode. +- For localization add `TXT_SHOWCAMEO` and `TXT_SHOWCAMEO_DESC` into your `.csf` file. + ## Loading screen - PCX files can now be used as loadscreen images. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index be8f676e02..218cfdd897 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -137,7 +137,6 @@ This serves as a changelog for when you just need to drop the new version in wit CampaignDefaultGameSpeed=4 ; integer ShowBriefing=true ; boolean DigitalDisplay.Enable=false ; boolean -ShowCameo.Enable=true ; boolean ShowDesignatorRange=false ; boolean PrioritySelectionFiltering=true ; boolean PriorityDeployFiltering=true ; boolean @@ -693,7 +692,6 @@ HideShakeEffects=false ; boolean - Fixed the bug that low-air taking off / landing objects will receive twice damage (by NetsuNegi) - Fixed voxel projectile and animation lighting issues (by TaranDahl) - Fixed the bug that techno will get stuck if change owner in tunnel (by NetsuNegi) -- Show Cameo feature to display passenger/occupant cameos above selected vehicles/buildings (see [User Interface](User-Interface.md#show-cameo)) #### Phobos fixes: - Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi) From 54f9776e09db302d1eed3ea613f6a13b3dff2d59 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Mon, 6 Jul 2026 21:59:52 +0800 Subject: [PATCH 25/30] fix: update changelog entry name and anchor to Show Cameo --- docs/Whats-New.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 218cfdd897..de50f8b0e5 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -605,7 +605,7 @@ HideShakeEffects=false ; boolean - [Auto-remove earliest beacon](Fixed-or-Improved-Logics.md#auto-remove-earliest-beacon) (by TaranDahl) - [Customize the step limit of the credits indicator](User-Interface.md#customize-the-step-limit-of-the-credits-indicator) (by Noble_Fish) - [Disable the credits indicator smooth transition](User-Interface.md#disable-the-credits-indicator-smooth-transition) (by Noble_Fish) -- [New passenger cameo display feature](User-Interface.md#passenger-cameo-display) (by ABCZ) +- [Show Cameo feature to display passenger/occupant cameos above selected vehicles/buildings](User-Interface.md#show-cameo) (by ABCZ) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From d00b4f5220fcb863e75ca84605d6b088d340ff5d Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Wed, 8 Jul 2026 14:41:04 +0800 Subject: [PATCH 26/30] docs: fix Occupier INI tag reference in Show Cameo section --- docs/User-Interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 7a3bff7822..136de6bcd4 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -528,7 +528,7 @@ ShowPowerPlantEnhancerRange=false ; boolean ### Show Cameo -- You can now configure the game to display cameo icons and their counts above a selected vehicle or building, showing both passengers (`Passengers=`) and garrisoned occupants (`Occupy=`). Only shown when a single unit is selected (not box-selection). +- You can now configure the game to display cameo icons and their counts above a selected vehicle or building, showing both passengers (`Passengers=`) and garrisoned occupants (`Occupier=`). Only shown when a single unit is selected (not box-selection). - Two activation modes are available: - **Permanent mode**: Set `ShowCameo=true` (globally or per-unit) to always display cameos. Per-unit setting overrides the global setting. If a per-unit `ShowCameo` is explicitly set to `false`, cameos are never shown for that unit. - **Toggleable mode**: Set `ShowCameo=false` (global) and `ShowCameo.Toggleable=true` (global), then use the [Toggle Cameos](#toggle-cameos) hotkey to switch the display on/off. The master switch `ShowCameo.Enable` in `RA2MD.INI` controls the toggle state. From e19bb920652904b34bcd018239f7a04f65923eef Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Wed, 8 Jul 2026 14:55:56 +0800 Subject: [PATCH 27/30] fix: reorder ShowCameo entry after upstream DiscardOn entry in Whats-New.md --- docs/Whats-New.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index de50f8b0e5..ecb0f39e57 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -605,6 +605,7 @@ HideShakeEffects=false ; boolean - [Auto-remove earliest beacon](Fixed-or-Improved-Logics.md#auto-remove-earliest-beacon) (by TaranDahl) - [Customize the step limit of the credits indicator](User-Interface.md#customize-the-step-limit-of-the-credits-indicator) (by Noble_Fish) - [Disable the credits indicator smooth transition](User-Interface.md#disable-the-credits-indicator-smooth-transition) (by Noble_Fish) +- Add `selling`, `undeploying` and `harvesting` conditions to `DiscardOn` (by Noble_Fish) - [Show Cameo feature to display passenger/occupant cameos above selected vehicles/buildings](User-Interface.md#show-cameo) (by ABCZ) #### Vanilla fixes: From 5fad5fc57c10d7cf83356077f886fb74e2ce44d2 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Wed, 8 Jul 2026 15:00:56 +0800 Subject: [PATCH 28/30] fix: remove duplicate DiscardOn entry, place ShowCameo at end --- docs/Whats-New.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index b0bcfc356e..ec386108c0 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -608,7 +608,6 @@ HideShakeEffects=false ; boolean - [Disable the credits indicator smooth transition](User-Interface.md#disable-the-credits-indicator-smooth-transition) (by Noble_Fish) - Add `selling`, `undeploying` and `harvesting` conditions to `DiscardOn` (by Noble_Fish) - [Show Cameo feature to display passenger/occupant cameos above selected vehicles/buildings](User-Interface.md#show-cameo) (by ABCZ) -- Add `selling`, `undeploying` and `harvesting` conditions to `DiscardOn` (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) From 3e4230a17acd32e21099704075fbe6ce3236e037 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Wed, 8 Jul 2026 15:13:24 +0800 Subject: [PATCH 29/30] docs: use cameo.pal instead of CAMEO_PAL for readability --- docs/User-Interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 136de6bcd4..78eb695ccf 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -535,7 +535,7 @@ ShowPowerPlantEnhancerRange=false ; boolean - Cameos are arranged in horizontal rows, centered above the selection bracket. When the number of cameo types exceeds `ShowCameo.PerRow`, additional rows are stacked above the bottom row. The first-entered unit is displayed at the top. - Translucency can be set via `ShowCameo.Translucency` (0/25/50/75, default 0). SHP cameos are affected; PCX cameos are not. - `ShowCameo.OverlapXY` and `ShowCameo.OffsetXY` control overlap between adjacent cameos. `ShowCameo.OverlapPrimary` (default true) determines whether the earlier cameo covers the later one. On the same row, only X overlap applies; when wrapping to a new row, only Y overlap applies. -- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. Animated SHPs are supported. A custom palette can be specified via `ShowCameo.CustomPalette`; if not set, the palette falls back to `CameoPalette` or the default `CAMEO_PAL`. +- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. Animated SHPs are supported. A custom palette can be specified via `ShowCameo.CustomPalette`; if not set, the palette falls back to `CameoPalette` or the default `cameo.pal`. ```{hint} For example, with `ShowCameo.PerRow=1` (vertical stacking), `ShowCameo.OverlapPrimary=true` means the bottom cameo covers the one above it, preserving the bottom cameo's text label from being hidden. From 6c9ed243befe295ad1e2fb81a99af912badf3017 Mon Sep 17 00:00:00 2001 From: CZHczhABCZ <3772522570@qq.com> Date: Wed, 8 Jul 2026 18:52:38 +0800 Subject: [PATCH 30/30] feat: add ShowCameo.CustomPCX support for custom PCX cameo icons --- docs/User-Interface.md | 3 ++- src/Ext/Techno/Body.Visuals.cpp | 15 +++++++++++++++ src/Ext/TechnoType/Body.cpp | 3 +++ src/Ext/TechnoType/Body.h | 2 ++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 78eb695ccf..f37ad94ab3 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -535,7 +535,7 @@ ShowPowerPlantEnhancerRange=false ; boolean - Cameos are arranged in horizontal rows, centered above the selection bracket. When the number of cameo types exceeds `ShowCameo.PerRow`, additional rows are stacked above the bottom row. The first-entered unit is displayed at the top. - Translucency can be set via `ShowCameo.Translucency` (0/25/50/75, default 0). SHP cameos are affected; PCX cameos are not. - `ShowCameo.OverlapXY` and `ShowCameo.OffsetXY` control overlap between adjacent cameos. `ShowCameo.OverlapPrimary` (default true) determines whether the earlier cameo covers the later one. On the same row, only X overlap applies; when wrapping to a new row, only Y overlap applies. -- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. Animated SHPs are supported. A custom palette can be specified via `ShowCameo.CustomPalette`; if not set, the palette falls back to `CameoPalette` or the default `cameo.pal`. +- A custom cameo shape (SHP) can be specified per passenger/occupant type via `ShowCameo.CustomShape` in `artmd.ini`. Animated SHPs are supported. A custom PCX can be specified via `ShowCameo.CustomPCX`. A custom palette can be specified via `ShowCameo.CustomPalette`; if not set, the palette falls back to `CameoPalette` or the default `cameo.pal`. ```{hint} For example, with `ShowCameo.PerRow=1` (vertical stacking), `ShowCameo.OverlapPrimary=true` means the bottom cameo covers the one above it, preserving the bottom cameo's text label from being hidden. @@ -575,6 +575,7 @@ In `artmd.ini`: ```ini [SOMETECHNO] ; TechnoType ShowCameo.CustomShape= ; SHP filename +ShowCameo.CustomPCX= ; PCX filename ShowCameo.CustomPalette= ; PAL filename ``` diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 23d1b5ee6b..838d08f58e 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -997,6 +997,11 @@ void TechnoExt::DrawCameos(TechnoClass* pThis) w = pCustomCameo->Width; h = pCustomCameo->Height; } + else if (auto pCustomPCX = pCameoTypeExt->ShowCameo_CustomPCX.GetSurface()) + { + w = pCustomPCX->GetWidth(); + h = pCustomPCX->GetHeight(); + } canvases.push_back({ w, h }); } @@ -1153,6 +1158,16 @@ void TechnoExt::DrawCameos(TechnoClass* pThis) drawn = true; } + if (!drawn) + { + if (const auto pCustomPCX = pCameoTypeExt->ShowCameo_CustomPCX.GetSurface()) + { + RectangleStruct pcxBounds = { iconPos.X, iconPos.Y, curW, curH }; + PCX::Instance.BlitToSurface(&pcxBounds, pSurface, pCustomPCX); + drawn = true; + } + } + if (!drawn) { if (const auto pCameoPCX = pCameoTypeExt->CameoPCX.GetSurface()) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index f2a2904300..3ebe2eb2b6 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1347,6 +1347,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AltCameoPCX.Read(pArtINI, pArtSection, "AltCameoPCX"); this->ShowCameo_CustomPalette.LoadFromINI(pArtINI, pArtSection, "ShowCameo.CustomPalette"); this->ShowCameo_CustomShape.Read(exArtINI, pArtSection, "ShowCameo.CustomShape"); + this->ShowCameo_CustomPCX.Read(pArtINI, pArtSection, "ShowCameo.CustomPCX"); this->LaserTrailData.clear(); for (size_t i = 0; ; ++i) @@ -1548,6 +1549,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->AltCameoPCX) .Process(this->ShowCameo_CustomPalette) .Process(this->ShowCameo_CustomShape) + .Process(this->ShowCameo_CustomPCX) .Process(this->NoManualMove) .Process(this->InitialStrength) .Process(this->ReloadInTransport) @@ -1999,6 +2001,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->CameoPalette) .Process(this->ShowCameo_CustomPalette) .Process(this->ShowCameo_CustomShape) + .Process(this->ShowCameo_CustomPCX) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index d0b727d2b5..c0616dec48 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -534,6 +534,7 @@ class TechnoTypeExt CustomPalette CameoPalette; CustomPalette ShowCameo_CustomPalette; TheaterSpecificSHP ShowCameo_CustomShape; + PhobosPCXFile ShowCameo_CustomPCX; ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) , HealthBar_Hide { false } @@ -1022,6 +1023,7 @@ class TechnoTypeExt , CameoPalette {} , ShowCameo_CustomPalette {} , ShowCameo_CustomShape {} + , ShowCameo_CustomPCX {} { } virtual ~ExtData() = default;