Index: binaries/data/mods/public/gui/session/messages.js =================================================================== --- binaries/data/mods/public/gui/session/messages.js +++ binaries/data/mods/public/gui/session/messages.js @@ -328,6 +328,7 @@ "status": notification.status }); updatePlayerData(); + setDiplomacyColors(); }, "ceasefire-ended": function(notification, player) { Index: binaries/data/mods/public/gui/session/minimap_panel.xml =================================================================== --- binaries/data/mods/public/gui/session/minimap_panel.xml +++ binaries/data/mods/public/gui/session/minimap_panel.xml @@ -19,6 +19,20 @@ if (g_HasIdleWorker) Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle-highlight.png"; + + + + Toggle Diplomacy colors + g_DiplomacyColorsToggle = !g_DiplomacyColorsToggle; updatePlayerColors(); + Engine.GetGUIObjectByName("diploOverlay").sprite = "stretched:session/minimap-diplo-highlight.png"; + Engine.GetGUIObjectByName("diploOverlay").sprite = "stretched:session/minimap-diplo.png"; + Engine.GetGUIObjectByName("diploOverlay").sprite = "stretched:session/minimap-diplo.png"; + Engine.GetGUIObjectByName("diploOverlay").sprite = "stretched:session/minimap-diplo-highlight.png"; + + - + + Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -12,6 +12,15 @@ const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations); /** + * Diplomacy colors (ally/neutral/enemy as a player, teams as an observer). + */ +var g_OriginalColors, g_DiplomacyColors; +var g_DiplomacyColorsToggle = false; +var g_DiplomacyColorsList = Engine.ReadJSONFile("simulation/data/settings/diplomacy_colors.json"); +if (!g_DiplomacyColorsList) + throw("session.js: Error reading diplomacy_colors.json"); + +/** * Colors to flash when pop limit reached. */ var g_DefaultPopulationColor = "white"; @@ -280,6 +289,8 @@ g_PlayerAssignments.local.player = -1; updatePlayerData(); + g_OriginalColors = Array.from(g_Players, x => x.color); + updatePlayerColors(); g_BarterSell = g_ResourceData.GetCodes()[0]; @@ -291,19 +302,12 @@ initPanelEntities(slot); // Populate player selection dropdown - let playerNames = [translate("Observer")]; - let playerIDs = [-1]; - for (let player in g_Players) - { - playerIDs.push(player); - playerNames.push(colorizePlayernameHelper("■", player) + " " + g_Players[player].name); - } + let playerIDs = Array.from({ length: g_Players.length + 1 }, (v, i) => i - 1); + let viewPlayerDropdown = Engine.GetGUIObjectByName("viewPlayer"); + viewPlayerDropdown.list_data = playerIDs; // Select "observer" item when rejoining as a defeated player let viewedPlayer = g_Players[Engine.GetPlayerID()]; - let viewPlayerDropdown = Engine.GetGUIObjectByName("viewPlayer"); - viewPlayerDropdown.list = playerNames; - viewPlayerDropdown.list_data = playerIDs; viewPlayerDropdown.selected = viewedPlayer && viewedPlayer.state == "defeated" ? 0 : Engine.GetPlayerID() + 1; // If in Atlas editor, disable the exit button @@ -330,6 +334,53 @@ // setTimeout(function() { reportPerformance(60); }, 60000); } +function setDiplomacyColors() +{ + g_DiplomacyColors = [g_Players[0].color]; + let teamColors = {}; + for (let i = 1; i < g_Players.length; ++i) + { + if (g_ViewedPlayer <= 0) + { + // Observers (and gaia) see team colors + let team = g_Players[i].team; + g_DiplomacyColors.push(g_OriginalColors[teamColors[team] || i]); + if (team != -1 && !teamColors[team]) + teamColors[team] = i; + } + else if (g_ViewedPlayer == i) + g_DiplomacyColors.push(g_DiplomacyColorsList.Self); + else if (g_Players[g_ViewedPlayer].isAlly[i]) + g_DiplomacyColors.push(g_DiplomacyColorsList.Ally); + else if (g_Players[g_ViewedPlayer].isNeutral[i]) + g_DiplomacyColors.push(g_DiplomacyColorsList.Neutral); + else + g_DiplomacyColors.push(g_DiplomacyColorsList.Enemy); + } + + if (g_DiplomacyColorsToggle) + updatePlayerColors(); +} + +function updatePlayerColors() +{ + let playerColors = g_DiplomacyColorsToggle ? g_DiplomacyColors : g_OriginalColors; + for (let i = 1; i < g_Players.length; ++i) + g_Players[i].color = playerColors[i]; + + Engine.GuiInterfaceCall("UpdatePlayerColors", playerColors); + updateViewPlayerNames(); +} + +function updateViewPlayerNames() +{ + let playerNames = [translate("Observer")]; + for (let player in g_Players) + playerNames.push(colorizePlayernameHelper("■", player) + " " + g_Players[player].name); + + Engine.GetGUIObjectByName("viewPlayer").list = playerNames; +} + function updatePlayerData() { let simState = GetSimState(); @@ -488,6 +539,7 @@ } Engine.SetViewedPlayer(g_ViewedPlayer); + setDiplomacyColors(); updateTopPanel(); updateChatAddressees(); updateHotkeyTooltips(); @@ -797,6 +849,7 @@ else if (idleWorkerButton.sprite != prefix + "minimap-idle-highlight.png") idleWorkerButton.sprite = prefix + "minimap-idle.png"; } + function onSimulationUpdate() { // Templates change depending on technologies and auras, so they have to be reloaded every turn. Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -874,6 +874,18 @@ return buildableEnts; }; +GuiInterface.prototype.UpdatePlayerColors = function(player, data) +{ + for (let i = 0; i < data.length; ++i) + { + let cmpPlayer = QueryPlayerIDInterface(i, IID_Player); + if (!cmpPlayer) + continue; + + cmpPlayer.SetDiplomacyColor(data[i].r, data[i].g, data[i].b); + } +}; + GuiInterface.prototype.SetSelectionHighlight = function(player, cmd) { let playerColors = {}; // cache of owner -> color map @@ -2015,6 +2027,7 @@ "GetFoundationSnapData": 1, "PlaySound": 1, "FindIdleUnits": 1, + "UpdatePlayerColors": 1, "HasIdleUnits": 1, "GetTradingRouteGain": 1, "GetTradingDetails": 1, Index: binaries/data/mods/public/simulation/components/Player.js =================================================================== --- binaries/data/mods/public/simulation/components/Player.js +++ binaries/data/mods/public/simulation/components/Player.js @@ -22,6 +22,19 @@ ""; /** + * Don't serialise diplomacyColor since it's modified by the GUI + */ +Player.prototype.Serialize = function() +{ + let data = {}; + for (let key in this) + if (this.hasOwnProperty(key) && key != "diplomacyColor") + data[key] = this[key]; + + return data; +}; + +/** * Which units will be shown with special icons at the top. */ var panelEntityClasses = "Hero Relic"; @@ -32,6 +45,7 @@ this.name = undefined; // define defaults elsewhere (supporting other languages) this.civ = undefined; this.color = undefined; + this.diplomacyColor = undefined; this.popUsed = 0; // population of units owned or trained by this player this.popBonuses = 0; // sum of population bonuses of player's entities this.maxPop = 300; // maximum population @@ -131,9 +145,18 @@ }); }; +Player.prototype.SetDiplomacyColor = function(r, g, b) +{ + this.diplomacyColor = { "r": r/255.0, "g": g/255.0, "b": b/255.0, "a": 1.0 }; + + Engine.BroadcastMessage(MT_PlayerColorChanged, { + "player": this.playerID + }); +}; + Player.prototype.GetColor = function() { - return this.color; + return this.diplomacyColor || this.color; }; // Try reserving num population slots. Returns 0 on success or number of missing slots otherwise. Index: binaries/data/mods/public/simulation/components/StatusBars.js =================================================================== --- binaries/data/mods/public/simulation/components/StatusBars.js +++ binaries/data/mods/public/simulation/components/StatusBars.js @@ -100,6 +100,12 @@ this.RegenerateSprites(); }; +StatusBars.prototype.OnPlayerColorChanged = function(msg) +{ + if (this.enabled) + this.RegenerateSprites(); +}; + StatusBars.prototype.RegenerateSprites = function() { let cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer); Index: binaries/data/mods/public/simulation/data/settings/diplomacy_colors.json =================================================================== --- binaries/data/mods/public/simulation/data/settings/diplomacy_colors.json +++ binaries/data/mods/public/simulation/data/settings/diplomacy_colors.json @@ -0,0 +1,6 @@ +{ + "Self": { "r": 0, "g": 0, "b": 255 }, + "Ally": { "r": 0, "g": 255, "b": 0 }, + "Neutral": { "r": 255, "g": 255, "b": 0 }, + "Enemy": { "r": 255, "g": 0, "b": 0 } +} Index: source/simulation2/components/CCmpMinimap.cpp =================================================================== --- source/simulation2/components/CCmpMinimap.cpp +++ source/simulation2/components/CCmpMinimap.cpp @@ -32,6 +32,7 @@ public: static void ClassInit(CComponentManager& componentManager) { + componentManager.SubscribeToMessageType(MT_PlayerColorChanged); componentManager.SubscribeToMessageType(MT_PositionChanged); componentManager.SubscribeToMessageType(MT_OwnershipChanged); componentManager.SubscribeToMessageType(MT_MinimapPing); @@ -117,13 +118,6 @@ template void SerializeCommon(S& serialize) { - if (m_UsePlayerColor) - { - serialize.NumberU8_Unbounded("r", m_R); - serialize.NumberU8_Unbounded("g", m_G); - serialize.NumberU8_Unbounded("b", m_B); - } - serialize.Bool("active", m_Active); if (m_Active) @@ -177,18 +171,23 @@ if (msgData.to == INVALID_PLAYER) break; - // Find the new player's color - CmpPtr cmpPlayerManager(GetSystemEntity()); - if (!cmpPlayerManager) + UpdatePlayerColor(msgData.to); + + break; + } + case MT_PlayerColorChanged: + { + if (!m_UsePlayerColor) break; - CmpPtr cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(msgData.to)); - if (!cmpPlayer) + + const CMessagePlayerColorChanged& msgData = static_cast (msg); + + CmpPtr cmpOwnership(GetEntityHandle()); + + if (!cmpOwnership || msgData.player != cmpOwnership->GetOwner()) break; - CColor color = cmpPlayer->GetColor(); - m_R = (u8)(color.r*255.0); - m_G = (u8)(color.g*255.0); - m_B = (u8)(color.b*255.0); - // TODO: probably should avoid using floating-point here + + UpdatePlayerColor(msgData.player); break; } @@ -236,6 +235,24 @@ return m_IsPinging; } + + void UpdatePlayerColor(player_id_t player) + { + CmpPtr cmpPlayerManager(GetSystemEntity()); + if (!cmpPlayerManager) + return; + + CmpPtr cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(player)); + if (!cmpPlayer) + return; + + CColor color = cmpPlayer->GetColor(); + + m_R = (u8)(color.r*255.0); + m_G = (u8)(color.g*255.0); + m_B = (u8)(color.b*255.0); + // TODO: probably should avoid using floating-point here + } }; REGISTER_COMPONENT_TYPE(Minimap) Index: source/simulation2/components/CCmpTerritoryManager.cpp =================================================================== --- source/simulation2/components/CCmpTerritoryManager.cpp +++ source/simulation2/components/CCmpTerritoryManager.cpp @@ -177,7 +177,10 @@ } case MT_PlayerColorChanged: { - MakeDirty(); + // Color change is not a trigger event + SAFE_DELETE(m_Territories); + ++m_DirtyID; + m_BoundaryLinesDirty = true; break; } case MT_PositionChanged: