diff --git a/binaries/data/mods/public/art/actors/props/special/common/athen_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/athen_waypoint_flag.xml
new file mode 100644
index 0000000000..82c4b25369
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/athen_waypoint_flag.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/brit_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/brit_waypoint_flag.xml
new file mode 100644
index 0000000000..d6ac063cd5
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/brit_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/cart_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/cart_waypoint_flag.xml
new file mode 100644
index 0000000000..3cdb724e8a
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/cart_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/gaul_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/gaul_waypoint_flag.xml
new file mode 100644
index 0000000000..1e9fb0ba2f
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/gaul_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/han_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/han_waypoint_flag.xml
new file mode 100644
index 0000000000..3da7ef0667
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/han_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/iber_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/iber_waypoint_flag.xml
new file mode 100644
index 0000000000..26b0e663c9
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/iber_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/kush_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/kush_waypoint_flag.xml
new file mode 100644
index 0000000000..ed51d37b48
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/kush_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/mace_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/mace_waypoint_flag.xml
new file mode 100644
index 0000000000..9310ed13d9
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/mace_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/maur_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/maur_waypoint_flag.xml
new file mode 100644
index 0000000000..1e29dcec9a
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/maur_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/pers_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/pers_waypoint_flag.xml
new file mode 100644
index 0000000000..875fefb04b
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/pers_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/ptol_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/ptol_waypoint_flag.xml
new file mode 100644
index 0000000000..fc51a1ec1f
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/ptol_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/rome_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/rome_waypoint_flag.xml
new file mode 100644
index 0000000000..40f967c782
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/rome_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/sele_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/sele_waypoint_flag.xml
new file mode 100644
index 0000000000..481ad69b98
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/sele_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/spart_waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/spart_waypoint_flag.xml
new file mode 100644
index 0000000000..3b9bfa0755
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/special/common/spart_waypoint_flag.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic_trans_norm_spec.xml
+
diff --git a/binaries/data/mods/public/art/actors/props/special/common/waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/waypoint_flag.xml
deleted file mode 100644
index 8fd86dcfbb..0000000000
--- a/binaries/data/mods/public/art/actors/props/special/common/waypoint_flag.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
- props/waypoint_flag.dae
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- basic_trans_norm_spec.xml
-
diff --git a/binaries/data/mods/public/art/variants/others/waypoints/base.xml b/binaries/data/mods/public/art/variants/others/waypoints/base.xml
new file mode 100644
index 0000000000..ccdd40c197
--- /dev/null
+++ b/binaries/data/mods/public/art/variants/others/waypoints/base.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ props/waypoint_flag.dae
+
+
+
+
+
diff --git a/binaries/data/mods/public/maps/random/caledonian_meadows.js b/binaries/data/mods/public/maps/random/caledonian_meadows.js
index 564e86cdaa..f1463f9b73 100644
--- a/binaries/data/mods/public/maps/random/caledonian_meadows.js
+++ b/binaries/data/mods/public/maps/random/caledonian_meadows.js
@@ -161,7 +161,7 @@ export function* generateMap()
"units/gaul/infantry_slinger_b",
"units/gaul/infantry_javelineer_b",
"units/gaul/champion_fanatic",
- "actor|props/special/common/waypoint_flag.xml",
+ "actor|props/special/common/waypoint_flag_factions.xml",
"actor|props/special/eyecandy/barrel_a.xml",
"actor|props/special/eyecandy/basket_celt_a.xml",
"actor|props/special/eyecandy/crate_a.xml",
diff --git a/binaries/data/mods/public/maps/random/survivalofthefittest.js b/binaries/data/mods/public/maps/random/survivalofthefittest.js
index 6b68e109e7..bd77c1c9ae 100644
--- a/binaries/data/mods/public/maps/random/survivalofthefittest.js
+++ b/binaries/data/mods/public/maps/random/survivalofthefittest.js
@@ -28,7 +28,7 @@ export function* generateMap(mapSettings)
const aRockMedium = g_Decoratives.rockMedium;
const aBushMedium = g_Decoratives.bushMedium;
const aBushSmall = g_Decoratives.bushSmall;
- const aWaypointFlag = "actor|props/special/common/waypoint_flag.xml";
+ const aWaypointFlag = "actor|props/special/common/waypoint_flag_factions.xml";
const pForest1 = [
tForestFloor2 + TERRAIN_SEPARATOR + oTree1,
diff --git a/binaries/data/mods/public/maps/random/wild_lake_biomes.json b/binaries/data/mods/public/maps/random/wild_lake_biomes.json
index 167c179759..b85aa73f8b 100644
--- a/binaries/data/mods/public/maps/random/wild_lake_biomes.json
+++ b/binaries/data/mods/public/maps/random/wild_lake_biomes.json
@@ -272,7 +272,7 @@
"campEntities": [
"gaia/treasure/metal",
"gaia/treasure/standing_stone",
- "actor|props/special/common/waypoint_flag.xml",
+ "actor|props/special/common/waypoint_flag_factions.xml",
"actor|props/special/eyecandy/barrel_a.xml",
"actor|props/special/eyecandy/basket_celt_a.xml",
"actor|props/special/eyecandy/crate_a.xml",
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoint.xml b/binaries/data/mods/public/simulation/templates/special/rallypoint.xml
index 4418e5abcf..0349079696 100644
--- a/binaries/data/mods/public/simulation/templates/special/rallypoint.xml
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoint.xml
@@ -1,9 +1,15 @@
+
+ gaia
+ Rally point flag
+ true
+
+
true
- props/special/common/waypoint_flag.xml
+ props/special/common/{civ}_waypoint_flag.xml
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/athen.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/athen.xml
new file mode 100644
index 0000000000..2ed54cee58
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/athen.xml
@@ -0,0 +1,6 @@
+
+
+
+ athen
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/brit.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/brit.xml
new file mode 100644
index 0000000000..d9c2707893
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/brit.xml
@@ -0,0 +1,6 @@
+
+
+
+ brit
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/cart.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/cart.xml
new file mode 100644
index 0000000000..7c9423a8fe
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/cart.xml
@@ -0,0 +1,6 @@
+
+
+
+ cart
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/gaul.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/gaul.xml
new file mode 100644
index 0000000000..10e06626f9
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/gaul.xml
@@ -0,0 +1,6 @@
+
+
+
+ gaul
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/han.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/han.xml
new file mode 100644
index 0000000000..105af98119
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/han.xml
@@ -0,0 +1,6 @@
+
+
+
+ han
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/iber.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/iber.xml
new file mode 100644
index 0000000000..3215440faa
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/iber.xml
@@ -0,0 +1,6 @@
+
+
+
+ iber
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/kush.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/kush.xml
new file mode 100644
index 0000000000..35d81439e4
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/kush.xml
@@ -0,0 +1,6 @@
+
+
+
+ kush
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/mace.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/mace.xml
new file mode 100644
index 0000000000..671e597050
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/mace.xml
@@ -0,0 +1,6 @@
+
+
+
+ mace
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/maur.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/maur.xml
new file mode 100644
index 0000000000..1c7d2d1eb5
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/maur.xml
@@ -0,0 +1,6 @@
+
+
+
+ maur
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/pers.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/pers.xml
new file mode 100644
index 0000000000..93c05b0375
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/pers.xml
@@ -0,0 +1,6 @@
+
+
+
+ pers
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/ptol.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/ptol.xml
new file mode 100644
index 0000000000..f7bb9d1319
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/ptol.xml
@@ -0,0 +1,6 @@
+
+
+
+ ptol
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/rome.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/rome.xml
new file mode 100644
index 0000000000..e8d59bff10
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/rome.xml
@@ -0,0 +1,6 @@
+
+
+
+ rome
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/sele.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/sele.xml
new file mode 100644
index 0000000000..4bb14c90f7
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/sele.xml
@@ -0,0 +1,6 @@
+
+
+
+ sele
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoints/spart.xml b/binaries/data/mods/public/simulation/templates/special/rallypoints/spart.xml
new file mode 100644
index 0000000000..8464e8f58f
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special/rallypoints/spart.xml
@@ -0,0 +1,6 @@
+
+
+
+ spart
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
index 478cfc8f61..94b1633189 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -79,7 +79,7 @@
- special/rallypoint
+ special/rallypoints/{civ}
art/textures/misc/rallypoint_line.png
art/textures/misc/rallypoint_line_mask.png
0.25
diff --git a/binaries/data/mods/public/simulation/templates/template_territory_pull.xml b/binaries/data/mods/public/simulation/templates/template_territory_pull.xml
index 63de91aeb3..2ede38278d 100644
--- a/binaries/data/mods/public/simulation/templates/template_territory_pull.xml
+++ b/binaries/data/mods/public/simulation/templates/template_territory_pull.xml
@@ -35,7 +35,7 @@
false
true
- props/special/common/waypoint_flag.xml
+ props/special/common/waypoint_flag_factions.xml
true
diff --git a/source/ps/algorithm.h b/source/ps/algorithm.h
index 54e2be2319..2e4e28fdbd 100644
--- a/source/ps/algorithm.h
+++ b/source/ps/algorithm.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2024 Wildfire Games.
+/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -18,6 +18,10 @@
#ifndef ALGORITHM_H
#define ALGORITHM_H
+#include
+#include
+#include
+
namespace PS
{
/**
@@ -34,6 +38,27 @@ bool contains(Range&& range, const T& value)
return elem == value;
});
}
+
+/**
+ * @brief Replaces all occurrences of a substring within a string with a given value.
+ *
+ * This function searches the input string `base` for all instances of the specified
+ * `tag` and replaces them with the provided `value`. The replacement is performed
+ * in-place and modifies the original string.
+ *
+ * @param base The string in which to perform replacements. Modified in-place.
+ * @param tag The substring to search for (e.g., a placeholder like L"{civ}").
+ * @param value The string to replace each occurrence of `tag` with.
+ */
+inline void ReplaceSubrange(std::wstring& base, std::wstring_view tag, std::wstring_view value)
+{
+ size_t pos = 0;
+ while ((pos = base.find(tag, pos)) != std::wstring::npos)
+ {
+ base.replace(pos, tag.length(), value);
+ pos += value.length();
+ }
}
+} // namespace PS
#endif // ALGORITHM_H
diff --git a/source/simulation2/components/CCmpRallyPointRenderer.cpp b/source/simulation2/components/CCmpRallyPointRenderer.cpp
index b126772120..60958fd481 100644
--- a/source/simulation2/components/CCmpRallyPointRenderer.cpp
+++ b/source/simulation2/components/CCmpRallyPointRenderer.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 Wildfire Games.
+/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -17,7 +17,9 @@
#include "precompiled.h"
#include "CCmpRallyPointRenderer.h"
+#include "ICmpIdentity.h"
+#include "ps/algorithm.h"
#include "ps/Profile.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/helpers/Los.h"
@@ -218,69 +220,71 @@ void CCmpRallyPointRenderer::UpdateMessageSubscriptions()
GetSimContext().GetComponentManager().DynamicSubscriptionNonsync(MT_RenderSubmit, this, m_Displayed && IsSet());
}
+void CCmpRallyPointRenderer::CreateMarkerEntity(size_t index, player_id_t ownerId)
+{
+ if (m_MarkerTemplate.empty() || ownerId == INVALID_PLAYER)
+ return;
+
+ CmpPtr cmpPlayerManager(GetSystemEntity());
+ if (!cmpPlayerManager)
+ return;
+
+ CmpPtr cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(ownerId));
+ if (!cmpPlayer)
+ return;
+
+ CmpPtr cmpIdentity(GetSimContext(), cmpPlayer->GetEntityId());
+ if (!cmpIdentity)
+ return;
+
+ // Create a copy we do not want to alter the marker template.
+ std::wstring markerTemplate = m_MarkerTemplate;
+ PS::ReplaceSubrange(markerTemplate, L"{civ}", cmpIdentity->GetCiv());
+
+ CComponentManager& componentMgr = GetSimContext().GetComponentManager();
+ entity_id_t newMarker = componentMgr.AllocateNewLocalEntity();
+
+ if (newMarker != INVALID_ENTITY)
+ newMarker = componentMgr.AddEntity(markerTemplate, newMarker);
+
+ m_MarkerEntityIds[index] = newMarker;
+}
+
void CCmpRallyPointRenderer::UpdateMarkers()
{
player_id_t previousOwner = m_LastOwner;
+ CmpPtr cmpOwnership(GetEntityHandle());
+ const player_id_t ownerId{cmpOwnership ? cmpOwnership->GetOwner() : 0};
+
for (size_t i = 0; i < m_RallyPoints.size(); ++i)
{
if (i >= m_MarkerEntityIds.size())
m_MarkerEntityIds.push_back(INVALID_ENTITY);
if (m_MarkerEntityIds[i] == INVALID_ENTITY)
- {
- // No marker exists yet, create one first
- CComponentManager& componentMgr = GetSimContext().GetComponentManager();
+ CreateMarkerEntity(i, ownerId);
- // Allocate a new entity for the marker
- if (!m_MarkerTemplate.empty())
- {
- m_MarkerEntityIds[i] = componentMgr.AllocateNewLocalEntity();
- if (m_MarkerEntityIds[i] != INVALID_ENTITY)
- m_MarkerEntityIds[i] = componentMgr.AddEntity(m_MarkerTemplate, m_MarkerEntityIds[i]);
- }
- }
-
- // The marker entity should be valid at this point, otherwise something went wrong trying to allocate it
if (m_MarkerEntityIds[i] == INVALID_ENTITY)
+ {
LOGERROR("Failed to create rally point marker entity");
+ continue;
+ }
CmpPtr markerCmpPosition(GetSimContext(), m_MarkerEntityIds[i]);
if (markerCmpPosition)
{
if (m_Displayed && IsSet())
- {
markerCmpPosition->MoveTo(m_RallyPoints[i].X, m_RallyPoints[i].Y);
- }
else
- {
markerCmpPosition->MoveOutOfWorld();
- }
}
- // Set rally point flag selection based on player civilization
- CmpPtr cmpOwnership(GetEntityHandle());
- if (!cmpOwnership)
- continue;
-
- player_id_t ownerId = cmpOwnership->GetOwner();
if (ownerId == INVALID_PLAYER || (ownerId == previousOwner && m_LastMarkerCount >= i))
continue;
m_LastOwner = ownerId;
- CmpPtr cmpPlayerManager(GetSystemEntity());
- // cmpPlayerManager should not be null as long as this method is called on-demand instead of at Init() time
- // (we can't rely on component initialization order in Init())
- if (!cmpPlayerManager)
- continue;
-
- CmpPtr cmpIdentity(GetSimContext(), cmpPlayerManager->GetPlayerByID(ownerId));
- if (!cmpIdentity)
- continue;
-
- CmpPtr cmpVisualActor(GetSimContext(), m_MarkerEntityIds[i]);
- if (cmpVisualActor)
- cmpVisualActor->SetVariant("civ", CStrW(cmpIdentity->GetCiv()).ToUTF8());
}
+
m_LastMarkerCount = m_RallyPoints.size() - 1;
}
diff --git a/source/simulation2/components/CCmpRallyPointRenderer.h b/source/simulation2/components/CCmpRallyPointRenderer.h
index 1e73d5ea54..e283b03fed 100644
--- a/source/simulation2/components/CCmpRallyPointRenderer.h
+++ b/source/simulation2/components/CCmpRallyPointRenderer.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 Wildfire Games.
+/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -194,6 +194,14 @@ private:
*/
void UpdateMarkers();
+ /**
+ * @brief Creates a rally point marker entity for the specified index and owner.
+ *
+ * @param index The index in the rally point list for which to create a marker.
+ * @param ownerId The ID of the player who currently owns the entity.
+ */
+ void CreateMarkerEntity(size_t index, player_id_t ownerId);
+
/**
* Recomputes all the full paths from this entity to the rally point and from the rally point to the next, and does all the necessary
* post-processing to make them prettier.
diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp
index 8cb19c3206..9113919e25 100644
--- a/source/simulation2/components/CCmpVisualActor.cpp
+++ b/source/simulation2/components/CCmpVisualActor.cpp
@@ -27,6 +27,8 @@
#include "ICmpIdentity.h"
#include "ICmpMirage.h"
#include "ICmpOwnership.h"
+#include "ICmpPlayer.h"
+#include "ICmpPlayerManager.h"
#include "ICmpPosition.h"
#include "ICmpTemplateManager.h"
#include "ICmpTerrain.h"
@@ -47,6 +49,7 @@
#include "maths/Frustum.h"
#include "maths/Matrix3D.h"
#include "maths/Vector3D.h"
+#include "ps/algorithm.h"
#include "ps/CLogger.h"
#include "ps/GameSetup/Config.h"
#include "renderer/Scene.h"
@@ -560,7 +563,7 @@ public:
}
private:
- // Replace {phenotype} with the correct value in m_ActorName
+ // Replace {phenotype} and {civ} with the correct value in m_ActorName
void ParseActorName(std::wstring base);
/// Helper function shared by component init and actor reloading
@@ -583,18 +586,13 @@ REGISTER_COMPONENT_TYPE(VisualActor)
void CCmpVisualActor::ParseActorName(std::wstring base)
{
CmpPtr cmpIdentity(GetEntityHandle());
- const std::wstring pattern = L"{phenotype}";
if (cmpIdentity)
{
- size_t pos = base.find(pattern);
- while (pos != std::string::npos)
- {
- base.replace(pos, pattern.size(), cmpIdentity->GetPhenotype());
- pos = base.find(pattern, pos + pattern.size());
- }
+ PS::ReplaceSubrange(base, L"{phenotype}", cmpIdentity->GetPhenotype());
+ PS::ReplaceSubrange(base, L"{civ}", cmpIdentity->GetCiv());
}
- m_ActorName = base;
+ m_ActorName = std::move(base);
}
void CCmpVisualActor::InitModel()
diff --git a/source/tools/entity/checkrefs.py b/source/tools/entity/checkrefs.py
index 5d3e56e79d..31a1901820 100755
--- a/source/tools/entity/checkrefs.py
+++ b/source/tools/entity/checkrefs.py
@@ -340,22 +340,32 @@ class CheckRefs:
and entity.find("VisualActor").find("Actor") is not None
and entity.find("Identity") is not None
):
- phenotype_tag = entity.find("Identity").find("Phenotype")
- phenotypes = (
- phenotype_tag.text.split()
- if (phenotype_tag is not None and phenotype_tag.text)
- else ["default"]
- )
+ cmp_identity = entity.find("Identity")
+
actor = entity.find("VisualActor").find("Actor")
- if "{phenotype}" in actor.text:
- for phenotype in phenotypes:
- # See simulation2/components/CCmpVisualActor.cpp and Identity.js
- # for explanation.
- actor_path = actor.text.replace("{phenotype}", phenotype)
- self.deps.append((fp, Path(f"art/actors/{actor_path}")))
+ if cmp_identity is not None:
+ actor_path = actor.text
+ if "{civ}" in actor_path:
+ civ_tag = cmp_identity.find("Civ")
+ civ = civ_tag.text if civ_tag is not None else "gaia"
+ actor_path = actor_path.replace("{civ}", civ)
+
+ if "{phenotype}" in actor_path:
+ phenotype_tag = cmp_identity.find("Phenotype")
+ phenotypes = (
+ phenotype_tag.text.split()
+ if (phenotype_tag is not None and phenotype_tag.text)
+ else ["default"]
+ )
+ for phenotype in phenotypes:
+ # See simulation2/components/CCmpVisualActor.cpp and Identity.js
+ # for explanation.
+ phenotype_path = actor_path.replace("{phenotype}", phenotype)
+ self.deps.append((fp, Path(f"art/actors/{phenotype_path}")))
else:
actor_path = actor.text
self.deps.append((fp, Path(f"art/actors/{actor_path}")))
+
foundation_actor = entity.find("VisualActor").find("FoundationActor")
if foundation_actor is not None:
self.deps.append((fp, Path(f"art/actors/{foundation_actor.text}")))