Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5e17992
feat: add Unit Passengers Display
CZHczhABCZ Jun 27, 2026
1f1f0db
feat: add Unit Passengers Display
CZHczhABCZ Jun 27, 2026
acf66e8
docs: refine documentation formatting per review
CZHczhABCZ Jun 28, 2026
f8694e2
fix: address review feedback
CZHczhABCZ Jun 28, 2026
929925e
docs: update CSF fallback text and shorten Whats-New entry
CZHczhABCZ Jun 28, 2026
833bc82
[doc] Correct the writing of Changelog
DeathFishAtEase Jun 28, 2026
7e442ae
docs: move unit passengers display entry from Breaking changes to Cha…
CZHczhABCZ Jun 28, 2026
4853568
fix: add FootClass cast guard for buildings and RA2MD.INI config
CZHczhABCZ Jun 28, 2026
42d0d0b
Revert "docs: move unit passengers display entry from Breaking change…
CZHczhABCZ Jun 28, 2026
db52a4f
feat: add CameoPalette support for unit passenger display
CZHczhABCZ Jun 28, 2026
92365a5
style: fix code placement and formatting per review
CZHczhABCZ Jun 28, 2026
ee05b3a
style: add blank lines between unrelated declarations and calls
CZHczhABCZ Jun 28, 2026
074849c
style: fix whitespace in Rules/Body.cpp
CZHczhABCZ Jun 28, 2026
c2314d0
Merge upstream/develop into feat-unit-passengers-display
CZHczhABCZ Jul 4, 2026
801330c
refactor: rename UnitPassengers to Passengers, add global PerRow/Bott…
CZHczhABCZ Jul 4, 2026
04e0649
docs: rename Icons to Cameos, merge CSF messages, fix dirty diff in R…
CZHczhABCZ Jul 4, 2026
35a61cd
docs: split Toggle Passengers into standard format, add CSF message d…
CZHczhABCZ Jul 4, 2026
2b7b980
refactor: rename Passengers.Enable to ShowPassengers.Enable, move fie…
CZHczhABCZ Jul 4, 2026
0900197
Fix ShowPassengers logic priority and CameoPalette INI reading order
CZHczhABCZ Jul 4, 2026
a2bf97b
Rename CSF labels to PassengerCameoDisplay, fix icon layout order
CZHczhABCZ Jul 4, 2026
6cf1934
Fix passenger cameo layout: correct X (left-to-right) and Y (top-to-b…
CZHczhABCZ Jul 4, 2026
7820299
feat: add Show Cameo display feature with CustomPalette support
CZHczhABCZ Jul 5, 2026
a01c29a
fix: address review feedback - simplify docs, fix English comments, f…
CZHczhABCZ Jul 6, 2026
424ce73
fix: reverse draw order for buildings so that OverlapPrimary works co…
CZHczhABCZ Jul 6, 2026
2a35501
fix: address second round review - placement and formatting
CZHczhABCZ Jul 6, 2026
a956050
fix: micro-review - move Toggle Cameos after Deselect, clean up Whats…
CZHczhABCZ Jul 6, 2026
54f9776
fix: update changelog entry name and anchor to Show Cameo
CZHczhABCZ Jul 6, 2026
d00b4f5
docs: fix Occupier INI tag reference in Show Cameo section
CZHczhABCZ Jul 8, 2026
e8f1a88
Merge upstream/develop: resolve conflict in Whats-New.md
CZHczhABCZ Jul 8, 2026
e19bb92
fix: reorder ShowCameo entry after upstream DiscardOn entry in Whats-…
CZHczhABCZ Jul 8, 2026
2518c80
Merge branch 'feat-passengers-cameo-display' into feat-unit-passenger…
CZHczhABCZ Jul 8, 2026
5fad5fc
fix: remove duplicate DiscardOn entry, place ShowCameo at end
CZHczhABCZ Jul 8, 2026
3e4230a
docs: use cameo.pal instead of CAMEO_PAL for readability
CZHczhABCZ Jul 8, 2026
fa96886
Merge branch 'feat-passengers-cameo-display' into feat-unit-passenger…
CZHczhABCZ Jul 8, 2026
6c9ed24
feat: add ShowCameo.CustomPCX support for custom PCX cameo icons
CZHczhABCZ Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -877,3 +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 Cameo Display
2 changes: 2 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<ClCompile Include="src\Commands\SaveVariablesToFile.cpp" />
<ClCompile Include="src\Commands\ToggleDesignatorRange.cpp" />
<ClCompile Include="src\Commands\ToggleDigitalDisplay.cpp" />
<ClCompile Include="src\Commands\TogglePassengers.cpp" />
<ClCompile Include="src\Commands\ToggleMessageList.cpp" />
<ClCompile Include="src\Commands\ToggleSWSidebar.cpp" />
<!-- src\Ext -->
Expand Down Expand Up @@ -308,6 +309,7 @@
<ClInclude Include="src\Commands\SaveVariablesToFile.h" />
<ClInclude Include="src\Commands\ToggleDesignatorRange.h" />
<ClInclude Include="src\Commands\ToggleDigitalDisplay.h" />
<ClInclude Include="src\Commands\TogglePassengers.h" />
<ClInclude Include="src\Commands\ToggleMessageList.h" />
<ClInclude Include="src\Commands\ToggleSWSidebar.h" />
<!-- src\Ext -->
Expand Down
2 changes: 1 addition & 1 deletion YRpp
29 changes: 29 additions & 0 deletions docs/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,30 @@ In `RA2MD.INI`:
ShowPowerPlantEnhancerRange=false ; boolean
```

### Passenger Cameo Display

- 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.

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

[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
```
Comment on lines +574 to +580

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No CustomPCX?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow, that makes sense... I forgot again. Something like a banner, hmm — yes, we do need support for custom PCX.

But WAIT…
The purpose of custom shapes is simply to expand the ways mods can display passengers or occupants. For example, an SHK could show a lightning bolt icon or animation, and a YURI could show a small mind-control icon or animation... So just custom SHP support might already be sufficient for these needs. As for custom PCX, I haven't yet figured out how it could be used.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for custom PCX, I haven't yet figured out how it could be used.

Any difference with the CameoPCX?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SOMETECHNO]                        ; TechnoType
ShowCameo.CustomShape=              ; SHP filename
ShowCameo.CustomPCX=                ; PCX filename
ShowCameo.CustomPalette=            ; PAL filename

It has been added…But I haven't tested whether it works properly (I think it should).


- 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").
Comment thread
DeathFishAtEase marked this conversation as resolved.
Outdated

## Hotkey Commands

### `[ ]` Display Damage Numbers
Expand Down Expand Up @@ -558,6 +582,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 Passengers

- 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

- Selects and centers the camera on the next TechnoType that is counted via the [harvester counter](#harvester-counter) and is currently idle.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- [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)
Expand Down
2 changes: 2 additions & 0 deletions src/Commands/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ToggleMessageList.h"
#include "DeselectObject.h"
#include "DeselectObject5.h"
#include "TogglePassengers.h"

#include <CCINIClass.h>

Expand All @@ -32,6 +33,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
MakeCommand<ToggleSWSidebar>();
MakeCommand<DeselectObjectCommandClass>();
MakeCommand<DeselectObject5CommandClass>();
MakeCommand<TogglePassengersCommandClass>();

if (Phobos::Config::SuperWeaponSidebarCommands)
{
Expand Down
45 changes: 45 additions & 0 deletions src/Commands/TogglePassengers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "TogglePassengers.h"

#include <Utilities/GeneralUtils.h>
#include <MessageListClass.h>
#include <HouseClass.h>

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"));
}
13 changes: 13 additions & 0 deletions src/Commands/TogglePassengers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "Commands.h"

class TogglePassengersCommandClass : 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;
};
9 changes: 9 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,11 @@ 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");

// Section AITargetTypes
int itemsCount = pINI->GetKeyCount("AITargetTypes");
for (int i = 0; i < itemsCount; ++i)
Expand Down Expand Up @@ -800,6 +805,10 @@ 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)
;
}

Expand Down
10 changes: 10 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ class RulesExt

Valueable<bool> MergeBuildingDamage;

Valueable<bool> ShowPassengers;
Valueable<bool> ShowPassengers_Toggleable;
Valueable<int> ShowPassengers_PerRow;
Valueable<Point2D> ShowPassengers_BottomOffset;

Valueable<double> DamageOwnerMultiplier;
Valueable<double> DamageAlliesMultiplier;
Valueable<double> DamageEnemiesMultiplier;
Expand Down Expand Up @@ -704,6 +709,11 @@ class RulesExt
, AutoRemoveEarliestBeacon { false }

, AllowBeaconHotKeyInSinglePlayer { false }

, ShowPassengers { false }
, ShowPassengers_Toggleable { false }
, ShowPassengers_PerRow { 5 }
, ShowPassengers_BottomOffset { { 0, 0 } }
{ }

virtual ~ExtData() = default;
Expand Down
154 changes: 154 additions & 0 deletions src/Ext/Techno/Body.Visuals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <Ext/Anim/Body.h>
#include <Ext/SWType/Body.h>
#include <Ext/House/Body.h>
#include <ObjectClass.h>
#include <algorithm>

void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds)
{
Expand Down Expand Up @@ -873,3 +875,155 @@ void TechnoExt::ShowPromoteAnim(TechnoClass* pThis)
else if (!eliteAnims.empty())
AnimExt::CreateRandomAnim(eliteAnims, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true);
}

void TechnoExt::DrawPassengers(TechnoClass* pThis)
{
if (ObjectClass::CurrentObjects.Count != 1)
return;

if (ObjectClass::CurrentObjects.GetItem(0) != pThis)
return;

const auto whatAmI = pThis->WhatAmI();

const auto pType = pThis->GetTechnoType();
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 || pRulesExt->ShowPassengers_Toggleable)
{
shouldShow = true;
}

if (pTypeExt->ShowPassengers.isset())
{
shouldShow = pTypeExt->ShowPassengers.Get();
}

if (!shouldShow)
return;

if (pThis->Passengers.NumPassengers <= 0)
return;

std::vector<std::pair<TechnoTypeClass*, int>> passengerCounts;
for (auto pPassenger = pThis->Passengers.GetFirstPassenger(); pPassenger; pPassenger = abstract_cast<FootClass*>(pPassenger->NextObject))
{
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);
}

std::reverse(passengerCounts.begin(), passengerCounts.end());

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.isset()
? pTypeExt->ShowPassengers_PerRow.Get()
: pRulesExt->ShowPassengers_PerRow.Get();
const int iconWidth = 60;
const int iconHeight = 48;
const int hGap = 2;
const int vGap = 2;

const int count = static_cast<int>(passengerCounts.size());
const int rows = (count + perRow - 1) / perRow;

const auto pSurface = DSurface::Composite;
RectangleStruct bounds = DSurface::Composite->GetRect();
RectangleStruct viewBounds = DSurface::ViewBounds;

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 };

// 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);
if (const auto pCameoPCX = pPassengerTypeExt->CameoPCX.GetSurface())
{
RectangleStruct pcxBounds = { iconPos.X, iconPos.Y, iconWidth, iconHeight };
PCX::Instance.BlitToSurface(&pcxBounds, pSurface, pCameoPCX);
drawn = true;
}

if (!drawn)
{
SHPStruct* pCameo = pPassengerType->GetCameo();
if (pCameo)
{
const auto pConvert = pPassengerTypeExt->CameoPalette.GetOrDefaultConvert(FileSystem::CAMEO_PAL);
pSurface->DrawSHP(
pConvert,
pCameo,
0,
&iconPos,
&bounds,
BlitterFlags::bf_400 | BlitterFlags::Alpha,
0, 0,
ZGradient::Ground,
1000, 0, nullptr, 0, 0, 0
);
drawn = true;
}
}

if (drawn)
{
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++;
}
}
2 changes: 2 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ 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);

// WeaponHelpers.cpp
static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true);
static void FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponType);
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/Techno/Hooks.Pips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ DEFINE_HOOK(0x6F65D1, TechnoClass_DrawHealthBar_Buildings, 0x6)

TechnoExt::ProcessDigitalDisplays(pThis);

TechnoExt::DrawPassengers(pThis);

return 0;
}

Expand Down Expand Up @@ -124,6 +126,8 @@ DEFINE_HOOK(0x6F683C, TechnoClass_DrawHealthBar_Units, 0x7)

TechnoExt::ProcessDigitalDisplays(pThis);

TechnoExt::DrawPassengers(pThis);

if (pExt->TypeExtData->HealthBar_HidePips)
{
R->EDI(pLocation);
Expand Down
Loading
Loading