Skip to content

Commit 5e17992

Browse files
committed
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
1 parent eb23fdb commit 5e17992

16 files changed

Lines changed: 281 additions & 1 deletion

CREDITS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,3 +821,4 @@ This page lists all the individual contributions to the project by their author.
821821
- Multiplayer gamespeed fix for RealTimeTimers
822822
- Revert Ares patch to allow OpenTopped transport customization
823823
- Fix for units with Fly, Jumpjet or Rocket locomotors crashing off-map not being cleaned up
824+
- **ABCZ (CZHczhABCZ)** - Unit passengers display

Phobos.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<ClCompile Include="src\Commands\SaveVariablesToFile.cpp" />
4646
<ClCompile Include="src\Commands\ToggleDesignatorRange.cpp" />
4747
<ClCompile Include="src\Commands\ToggleDigitalDisplay.cpp" />
48+
<ClCompile Include="src\Commands\ToggleUnitPassengers.cpp" />
4849
<ClCompile Include="src\Commands\ToggleMessageList.cpp" />
4950
<ClCompile Include="src\Commands\ToggleSWSidebar.cpp" />
5051
<!-- src\Ext -->
@@ -303,6 +304,7 @@
303304
<ClInclude Include="src\Commands\SaveVariablesToFile.h" />
304305
<ClInclude Include="src\Commands\ToggleDesignatorRange.h" />
305306
<ClInclude Include="src\Commands\ToggleDigitalDisplay.h" />
307+
<ClInclude Include="src\Commands\ToggleUnitPassengers.h" />
306308
<ClInclude Include="src\Commands\ToggleMessageList.h" />
307309
<ClInclude Include="src\Commands\ToggleSWSidebar.h" />
308310
<!-- src\Ext -->

docs/User-Interface.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,41 @@ In `RA2MD.INI`:
487487
ShowPowerPlantEnhancerRange=false ; boolean
488488
```
489489

490+
### Unit Passengers Display
491+
492+
- 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.
493+
- Two activation modes:
494+
- **Permanent mode**: Set `ShowUnitPassengers=true` (global) or `ShowPassengers=true` (per-vehicle) to always show the display, regardless of the hotkey state.
495+
- **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.
496+
- 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.
497+
- 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.
498+
499+
In `rulesmd.ini`:
500+
```ini
501+
[AudioVisual]
502+
ShowUnitPassengers=false ; boolean, permanently shows passengers for all vehicles (ignores hotkey). Defaults to false
503+
ShowUnitPassengers.Toggleable=false ; boolean, allows hotkey to toggle the display. Defaults to false
504+
ShowUnitPassengers.EnabledMessage= ; CSF entry key, message shown when the feature is enabled via hotkey
505+
ShowUnitPassengers.DisabledMessage= ; CSF entry key, message shown when the feature is disabled via hotkey
506+
507+
[SOMETECHNO] ; TechnoType
508+
ShowPassengers= ; boolean, permanently show/hide for this vehicle (overrides global). Not set = follow global
509+
ShowPassengers.Toggleable= ; boolean, override global toggleable setting for this vehicle. Not set = follow global
510+
Passengers.PerRow=5 ; integer, max icons per row before wrapping, defaults to 5
511+
Passengers.BottomOffset=0,0 ; X,Y, bottom boundary offset from selection bracket, defaults to 0,0
512+
```
513+
514+
In `RA2MD.CSF`:
515+
516+
; Hotkey UI labels
517+
TXT_UNIT_PASSENGERS Toggle Unit Passengers
518+
TXT_UNIT_PASSENGERS_DESC Show/hide unit passengers display.
519+
520+
; Optional: customize the enabled/disabled messages
521+
; (must match the labels set in ShowUnitPassengers.EnabledMessage / DisabledMessage)
522+
MSG:UnitPassengersEnabled Unit Passengers display: Enabled
523+
MSG:UnitPassengersDisabled Unit Passengers display: Disabled
524+
490525
## Hotkey Commands
491526

492527
### `[ ]` Display Damage Numbers
@@ -519,6 +554,11 @@ ShowPowerPlantEnhancerRange=false ; boolean
519554
- Switches on/off [digital display types](#digital-display).
520555
- For localization add `TXT_DIGITAL_DISPLAY` and `TXT_DIGITAL_DISPLAY_DESC` into your `.csf` file.
521556

557+
### `[ ]` Toggle Unit Passengers
558+
559+
- Switches on/off [unit passengers display](#unit-passengers-display).
560+
- For localization add `TXT_UNIT_PASSENGERS` and `TXT_UNIT_PASSENGERS_DESC` into your `.csf` file.
561+
522562
### `[ ]` Next Idle Harvester
523563

524564
- Selects and centers the camera on the next TechnoType that is counted via the [harvester counter](#harvester-counter) and is currently idle.

docs/Whats-New.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ This serves as a changelog for when you just need to drop the new version in wit
2929

3030
### Version TBD (develop branch nightly builds)
3131

32+
#### New:
33+
34+
- New unit passengers display feature - show passenger cameo icons and counts above vehicles when selected (see [User Interface](User-Interface.md#unit-passengers-display))
35+
- New hotkey `Toggle Unit Passengers` to enable/disable the passengers display, with customizable CSF messages
36+
- New INI tags: `[AudioVisual] -> ShowUnitPassengers`, `ShowUnitPassengers.Toggleable`, `ShowUnitPassengers.EnabledMessage`, `ShowUnitPassengers.DisabledMessage`, `[TechnoType] -> ShowPassengers`, `ShowPassengers.Toggleable`, `Passengers.PerRow`, `Passengers.BottomOffset`
3237
- `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.
3338
- 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]`.
3439
- `[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=`.

src/Commands/Commands.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "FrameByFrame.h"
88
#include "FrameStep.h"
99
#include "ToggleDigitalDisplay.h"
10+
#include "ToggleUnitPassengers.h"
1011
#include "ToggleDesignatorRange.h"
1112
#include "SaveVariablesToFile.h"
1213
#include "ToggleSWSidebar.h"
@@ -27,6 +28,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
2728
MakeCommand<NextIdleHarvesterCommandClass>();
2829
MakeCommand<QuickSaveCommandClass>();
2930
MakeCommand<ToggleDigitalDisplayCommandClass>();
31+
MakeCommand<ToggleUnitPassengersCommandClass>();
3032
MakeCommand<ToggleDesignatorRangeCommandClass>();
3133
MakeCommand<ToggleMessageListCommandClass>();
3234
MakeCommand<ToggleSWSidebar>();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "ToggleUnitPassengers.h"
2+
3+
#include <Utilities/GeneralUtils.h>
4+
#include <Ext/Rules/Body.h>
5+
#include <MessageListClass.h>
6+
#include <HouseClass.h>
7+
8+
const char* ToggleUnitPassengersCommandClass::GetName() const
9+
{
10+
return "Toggle Unit Passengers";
11+
}
12+
13+
const wchar_t* ToggleUnitPassengersCommandClass::GetUIName() const
14+
{
15+
return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS", L"Toggle Unit Passengers");
16+
}
17+
18+
const wchar_t* ToggleUnitPassengersCommandClass::GetUICategory() const
19+
{
20+
return CATEGORY_INTERFACE;
21+
}
22+
23+
const wchar_t* ToggleUnitPassengersCommandClass::GetUIDescription() const
24+
{
25+
return GeneralUtils::LoadStringUnlessMissing("TXT_UNIT_PASSENGERS_DESC", L"Show/hide unit passengers display.");
26+
}
27+
28+
void ToggleUnitPassengersCommandClass::Execute(WWKey eInput) const
29+
{
30+
Phobos::Config::UnitPassengers_Enable = !Phobos::Config::UnitPassengers_Enable;
31+
32+
auto PrintMessage = [](const wchar_t* pMessage)
33+
{
34+
MessageListClass::Instance.PrintMessage(
35+
pMessage,
36+
RulesClass::Instance->MessageDelay,
37+
HouseClass::CurrentPlayer->ColorSchemeIndex,
38+
true
39+
);
40+
};
41+
42+
const auto pRulesExt = RulesExt::Global();
43+
44+
if (Phobos::Config::UnitPassengers_Enable)
45+
{
46+
const auto& msg = pRulesExt->ShowUnitPassengers_EnabledMessage;
47+
if (msg.isset())
48+
PrintMessage(GeneralUtils::LoadStringUnlessMissing(msg.Get().Label, L"Unit Passengers display: Enabled"));
49+
}
50+
else
51+
{
52+
const auto& msg = pRulesExt->ShowUnitPassengers_DisabledMessage;
53+
if (msg.isset())
54+
PrintMessage(GeneralUtils::LoadStringUnlessMissing(msg.Get().Label, L"Unit Passengers display: Disabled"));
55+
}
56+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include "Commands.h"
4+
5+
class ToggleUnitPassengersCommandClass : public CommandClass
6+
{
7+
public:
8+
virtual const char* GetName() const override;
9+
virtual const wchar_t* GetUIName() const override;
10+
virtual const wchar_t* GetUICategory() const override;
11+
virtual const wchar_t* GetUIDescription() const override;
12+
virtual void Execute(WWKey eInput) const override;
13+
};

src/Ext/Rules/Body.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
366366
this->SortCameoByName.Read(exINI, GameStrings::General, "SortCameoByName");
367367

368368
this->MergeBuildingDamage.Read(exINI, GameStrings::CombatDamage, "MergeBuildingDamage");
369+
this->ShowUnitPassengers.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers");
370+
this->ShowUnitPassengers_Toggleable.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.Toggleable");
371+
this->ShowUnitPassengers_EnabledMessage.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.EnabledMessage");
372+
this->ShowUnitPassengers_DisabledMessage.Read(exINI, GameStrings::AudioVisual, "ShowUnitPassengers.DisabledMessage");
369373

370374
this->ApplyPerTargetEffectsOnDetonate.Read(exINI, GameStrings::CombatDamage, "ApplyPerTargetEffectsOnDetonate");
371375

@@ -769,6 +773,10 @@ void RulesExt::ExtData::Serialize(T& Stm)
769773
.Process(this->MindControl_Permanent_ReplaceSilent)
770774
.Process(this->TeamDelays_DynamicType)
771775
.Process(this->TeamDelays_Count)
776+
.Process(this->ShowUnitPassengers)
777+
.Process(this->ShowUnitPassengers_Toggleable)
778+
.Process(this->ShowUnitPassengers_EnabledMessage)
779+
.Process(this->ShowUnitPassengers_DisabledMessage)
772780
;
773781
}
774782

src/Ext/Rules/Body.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ class RulesExt
194194

195195
Valueable<bool> MergeBuildingDamage;
196196

197+
Valueable<bool> ShowUnitPassengers;
198+
Valueable<bool> ShowUnitPassengers_Toggleable;
199+
Nullable<CSFText> ShowUnitPassengers_EnabledMessage;
200+
Nullable<CSFText> ShowUnitPassengers_DisabledMessage;
201+
197202
Valueable<double> DamageOwnerMultiplier;
198203
Valueable<double> DamageAlliesMultiplier;
199204
Valueable<double> DamageEnemiesMultiplier;
@@ -664,9 +669,12 @@ class RulesExt
664669
, DiscardOn_MoveBasedOnDestination { false }
665670
, RemoveMindControl_Silent { false }
666671
, MindControl_Permanent_ReplaceSilent { false }
667-
668672
, TeamDelays_DynamicType { DynamicTeamDelayType::StartingPoint }
669673
, TeamDelays_Count {}
674+
, ShowUnitPassengers { false }
675+
, ShowUnitPassengers_Toggleable { false }
676+
, ShowUnitPassengers_EnabledMessage {}
677+
, ShowUnitPassengers_DisabledMessage {}
670678
{ }
671679

672680
virtual ~ExtData() = default;

src/Ext/Techno/Body.Visuals.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <Ext/Anim/Body.h>
44
#include <Ext/SWType/Body.h>
55
#include <Ext/House/Body.h>
6+
#include <ObjectClass.h>
67

78
void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds)
89
{
@@ -873,3 +874,122 @@ void TechnoExt::ShowPromoteAnim(TechnoClass* pThis)
873874
else if (!eliteAnims.empty())
874875
AnimExt::CreateRandomAnim(eliteAnims, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true);
875876
}
877+
878+
void TechnoExt::DrawUnitPassengers(TechnoClass* pThis)
879+
{
880+
if (ObjectClass::CurrentObjects.Count != 1)
881+
return;
882+
883+
if (ObjectClass::CurrentObjects.GetItem(0) != pThis)
884+
return;
885+
886+
const auto whatAmI = pThis->WhatAmI();
887+
if (whatAmI != AbstractType::Unit && whatAmI != AbstractType::Aircraft)
888+
return;
889+
890+
const auto pType = pThis->GetTechnoType();
891+
const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType);
892+
const auto pRulesExt = RulesExt::Global();
893+
894+
bool shouldShow = false;
895+
896+
if (pTypeExt->ShowPassengers.isset())
897+
{
898+
shouldShow = pTypeExt->ShowPassengers.Get();
899+
}
900+
else if (pRulesExt->ShowUnitPassengers)
901+
{
902+
shouldShow = true;
903+
}
904+
else
905+
{
906+
const bool toggleable = pTypeExt->ShowPassengers_Toggleable.Get(pRulesExt->ShowUnitPassengers_Toggleable);
907+
if (toggleable && Phobos::Config::UnitPassengers_Enable)
908+
shouldShow = true;
909+
}
910+
911+
if (!shouldShow)
912+
return;
913+
914+
if (pThis->Passengers.NumPassengers <= 0)
915+
return;
916+
917+
std::map<TechnoTypeClass*, int> passengerCounts;
918+
for (auto pPassenger = pThis->Passengers.GetFirstPassenger(); pPassenger; pPassenger = abstract_cast<FootClass*>(pPassenger->NextObject))
919+
{
920+
passengerCounts[pPassenger->GetTechnoType()]++;
921+
}
922+
923+
if (passengerCounts.empty())
924+
return;
925+
926+
const auto bracketPos = GetFootSelectBracketPosition(pThis, Anchor(HorizontalPosition::Center, VerticalPosition::Top));
927+
const auto& bottomOffset = pTypeExt->Passengers_BottomOffset;
928+
Point2D basePos = bracketPos;
929+
basePos.X += bottomOffset.Get().X;
930+
basePos.Y += bottomOffset.Get().Y + pType->PixelSelectionBracketDelta;
931+
932+
const double scale = 1.0;
933+
934+
const int perRow = pTypeExt->Passengers_PerRow;
935+
const int iconWidth = static_cast<int>(60 * scale);
936+
const int iconHeight = static_cast<int>(48 * scale);
937+
const int hGap = 2;
938+
const int vGap = 2;
939+
940+
const int count = static_cast<int>(passengerCounts.size());
941+
const int rows = (count + perRow - 1) / perRow;
942+
943+
const auto pSurface = DSurface::Composite;
944+
RectangleStruct bounds = DSurface::Composite->GetRect();
945+
946+
int idx = 0;
947+
for (const auto& item : passengerCounts)
948+
{
949+
const auto pPassengerType = item.first;
950+
const int passengerCount = item.second;
951+
952+
const int row = idx / perRow;
953+
const int col = idx % perRow;
954+
955+
const int itemsInThisRow = (row == rows - 1 && count % perRow != 0) ? (count % perRow) : perRow;
956+
const int rowWidth = itemsInThisRow * (iconWidth + hGap) - hGap;
957+
const int startX = basePos.X - rowWidth / 2;
958+
959+
const int y = basePos.Y - iconHeight - (rows - 1 - row) * (iconHeight + vGap);
960+
961+
Point2D iconPos = { startX + col * (iconWidth + hGap), y };
962+
963+
SHPStruct* pCameo = pPassengerType->GetCameo();
964+
if (pCameo)
965+
{
966+
pSurface->DrawSHP(
967+
FileSystem::CAMEO_PAL,
968+
pCameo,
969+
0,
970+
&iconPos,
971+
&bounds,
972+
BlitterFlags::bf_400 | BlitterFlags::Alpha,
973+
0, 0,
974+
ZGradient::Ground,
975+
1000, 0, nullptr, 0, 0, 0
976+
);
977+
978+
wchar_t countText[16];
979+
_snwprintf_s(countText, _countof(countText), _TRUNCATE, L"%d", passengerCount);
980+
981+
Point2D textPos = { iconPos.X + iconWidth - 4, iconPos.Y + 2 };
982+
983+
pSurface->DrawTextA(
984+
countText,
985+
&bounds,
986+
&textPos,
987+
Drawing::RGB_To_Int({ 0, 255, 0 }),
988+
0,
989+
TextPrintType::Center | TextPrintType::FullShadow
990+
);
991+
}
992+
993+
idx++;
994+
}
995+
}

0 commit comments

Comments
 (0)