Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -1654,6 +1654,23 @@ lastIdleClasses = []; } +function updateColors() +{ + // Recolor units, selection, and the minimap. + Engine.GuiInterfaceCall("UpdateColors", g_DiplomacyColors); + + // Recolor panels, windows, and status bars. + onSimulationUpdate(); + updatePlayerData(); + recalculateStatusBarDisplay(); + + // Recolor the player selection dropdown. + let playerNames = [translate("Observer")]; + for (let player in g_Players) + playerNames.push(colorizePlayernameHelper("■", player) + " " + g_Players[player].name); + Engine.GetGUIObjectByName("viewPlayer").list = playerNames; +} + function findIdleUnit(classes) { var append = Engine.HotkeyIsPressed("selection.add"); 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 @@ -336,6 +336,9 @@ }, "diplomacy": function(notification, player) { + if (g_DiplomacyColors) + updateColors(); + addChatMessage({ "type": "diplomacy", "sourcePlayer": 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_DiplomacyColors = !g_DiplomacyColors; updateColors(); + 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 @@ -186,6 +186,12 @@ * Unit classes to be checked for the military-only-selection modifier and for the idle-warrior-hotkey. */ var g_MilitaryTypes = ["Melee", "Ranged"]; + +/** + * True if we should use diplomacy colors (ally/neutral/enemy as a player, teams as an observer). + */ +var g_DiplomacyColors = false; + /** * Cache the idle worker status. */ @@ -334,6 +340,7 @@ g_Selection.selected = hotloadData.selection; initChatWindow(); + updateColors(); sendLobbyPlayerlistUpdate(); onSimulationUpdate(); @@ -508,6 +515,9 @@ } Engine.SetViewedPlayer(g_ViewedPlayer); + if (g_DiplomacyColors) + updateColors(); + updateTopPanel(); updateChatAddressees(); updateHotkeyTooltips(); 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 @@ -3,6 +3,13 @@ GuiInterface.prototype.Schema = ""; +/** + * Store diplomacy colors (ally/neutral/enemy as a player, teams as an observer). + */ +var g_DiplomacyColors = Engine.ReadJSONFile("settings/diplomacy_colors.json"); +if (!g_DiplomacyColors) + throw("GuiInterface.js: Error reading diplomacy_colors.json"); + GuiInterface.prototype.Serialize = function() { // This component isn't network-synchronised for the biggest part @@ -1757,6 +1764,55 @@ return reduced; }; +GuiInterface.prototype.UpdateColors = function(player, diplomacyColors) +{ + let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + let numPlayers = cmpPlayerManager.GetNumPlayers(); + + let cmpPlayer = QueryPlayerIDInterface(player, IID_Player); + if (!cmpPlayer) + return; + + let teamColors = {}; + for (let i = 1; i < numPlayers; ++i) + { + let cmpPlayer2 = QueryPlayerIDInterface(i, IID_Player); + if (!cmpPlayer2) + continue; + + if (!diplomacyColors) + cmpPlayer2.ResetDiplomacyColor(); + else if (player == -1) + { + // Observers see team colors. + let team = cmpPlayer2.GetTeam(); + if (teamColors[team]) + cmpPlayer2.SetDiplomacyColor(...g_DiplomacyColors[teamColors[team]]); + else + cmpPlayer2.SetDiplomacyColor(...g_DiplomacyColors[i]); + if (team != -1 && !teamColors[team]) + teamColors[team] = i; + } + else if (player == i) + cmpPlayer2.SetDiplomacyColor(...g_DiplomacyColors.Self); + else if (cmpPlayer.IsAlly(i)) + cmpPlayer2.SetDiplomacyColor(...g_DiplomacyColors.Ally); + else if (cmpPlayer.IsEnemy(i)) + cmpPlayer2.SetDiplomacyColor(...g_DiplomacyColors.Enemy); + else + cmpPlayer2.SetDiplomacyColor(...g_DiplomacyColors.Neutral); + + let entities = cmpRangeManager.GetEntitiesByPlayer(i); + for (let entity of entities) + Engine.PostMessage(entity, MT_ColorChanged, { + "entity": entity, + "from": i, + "to": i + }); + } +} + /** * Discover if the player has idle units. * @@ -1996,6 +2052,7 @@ "GetFoundationSnapData": 1, "PlaySound": 1, "FindIdleUnits": 1, + "UpdateColors": 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,20 @@ ""; /** + * Don't serialise this.diplomacyColor since it's modified by the GUI + */ +Player.prototype.Serialize = function() +{ + var ret = {}; + for (var i in this) + if (this.hasOwnProperty(i)) + ret[i] = this[i]; + + delete ret.diplomacyColor; + return ret; +}; + +/** * Which units will be shown with special icons at the top. */ var panelEntityClasses = "Hero Relic"; @@ -32,6 +46,7 @@ this.name = undefined; // define defaults elsewhere (supporting other languages) this.civ = undefined; this.color = { "r": 0.0, "g": 0.0, "b": 0.0, "a": 1.0 }; + 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 @@ -123,9 +138,19 @@ this.color = { "r": r/255.0, "g": g/255.0, "b": b/255.0, "a": 1.0 }; }; +Player.prototype.SetDiplomacyColor = function(r, g, b) +{ + this.diplomacyColor = { "r": r/255.0, "g": g/255.0, "b": b/255.0, "a": 1.0 }; +}; + +Player.prototype.ResetDiplomacyColor = function() +{ + this.diplomacyColor = undefined; +}; + Player.prototype.GetColor = function() { - return this.color; + return this.diplomacyColor ? 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 @@ -101,6 +101,12 @@ this.RegenerateSprites(); }; +StatusBars.prototype.OnColorChanged = 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,15 @@ +{ + "0": [255, 255, 255], + "1": [0, 0, 255], + "2": [255, 0, 0], + "3": [0, 255, 0], + "4": [255, 255, 0], + "5": [0, 255, 255], + "6": [255, 0, 255], + "7": [255, 128, 0], + "8": [25, 25, 25], + "Self": [0, 0, 255], + "Ally": [0, 255, 0], + "Neutral": [255, 255, 0], + "Enemy": [255, 0, 0] +} Index: source/simulation2/MessageTypes.h =================================================================== --- source/simulation2/MessageTypes.h +++ source/simulation2/MessageTypes.h @@ -243,6 +243,21 @@ entity_id_t entity; }; +class CMessageColorChanged : public CMessage +{ +public: + DEFAULT_MESSAGE_IMPL(ColorChanged) + + CMessageColorChanged(entity_id_t entity, player_id_t from, player_id_t to) : + entity(entity), from(from), to(to) + { + } + + entity_id_t entity; + player_id_t from; + player_id_t to; +}; + class CMessageOwnershipChanged : public CMessage { public: Index: source/simulation2/TypeList.h =================================================================== --- source/simulation2/TypeList.h +++ source/simulation2/TypeList.h @@ -41,6 +41,7 @@ MESSAGE(Deserialized) // non-deterministic (use with caution) MESSAGE(Create) MESSAGE(Destroy) +MESSAGE(ColorChanged) MESSAGE(OwnershipChanged) MESSAGE(PositionChanged) MESSAGE(InterpolatedPositionChanged) Index: source/simulation2/components/CCmpMinimap.cpp =================================================================== --- source/simulation2/components/CCmpMinimap.cpp +++ source/simulation2/components/CCmpMinimap.cpp @@ -33,6 +33,7 @@ static void ClassInit(CComponentManager& componentManager) { componentManager.SubscribeToMessageType(MT_PositionChanged); + componentManager.SubscribeToMessageType(MT_ColorChanged); 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) @@ -166,6 +160,7 @@ break; } + case MT_ColorChanged: case MT_OwnershipChanged: { if (!m_UsePlayerColor) Index: source/simulation2/components/CCmpSelectable.cpp =================================================================== --- source/simulation2/components/CCmpSelectable.cpp +++ source/simulation2/components/CCmpSelectable.cpp @@ -54,6 +54,7 @@ public: static void ClassInit(CComponentManager& componentManager) { + componentManager.SubscribeToMessageType(MT_ColorChanged); componentManager.SubscribeToMessageType(MT_OwnershipChanged); componentManager.SubscribeToMessageType(MT_PositionChanged); componentManager.SubscribeToMessageType(MT_TerrainChanged); @@ -354,6 +355,7 @@ break; } + case MT_ColorChanged: case MT_OwnershipChanged: { const CMessageOwnershipChanged& msgData = static_cast (msg); Index: source/simulation2/components/CCmpTerritoryManager.cpp =================================================================== --- source/simulation2/components/CCmpTerritoryManager.cpp +++ source/simulation2/components/CCmpTerritoryManager.cpp @@ -59,6 +59,7 @@ public: static void ClassInit(CComponentManager& componentManager) { + componentManager.SubscribeGloballyToMessageType(MT_ColorChanged); componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged); componentManager.SubscribeGloballyToMessageType(MT_PositionChanged); componentManager.SubscribeGloballyToMessageType(MT_ValueModification); @@ -166,6 +167,19 @@ { switch (msg.GetType()) { + case MT_ColorChanged: + { + const CMessageOwnershipChanged& msgData = static_cast (msg); + CmpPtr cmpTerritoryInfluence(GetSimContext(), msgData.entity); + if (cmpTerritoryInfluence) + { + SAFE_DELETE(m_Territories); + ++m_DirtyID; + m_BoundaryLinesDirty = true; +// m_TriggerEvent = true; // Same as case MT_OwnershipChanged, except for this line + } + break; + } case MT_OwnershipChanged: { const CMessageOwnershipChanged& msgData = static_cast (msg); Index: source/simulation2/scripting/MessageTypeConversions.cpp =================================================================== --- source/simulation2/scripting/MessageTypeConversions.cpp +++ source/simulation2/scripting/MessageTypeConversions.cpp @@ -190,6 +190,26 @@ //////////////////////////////// +JS::Value CMessageColorChanged::ToJSVal(ScriptInterface& scriptInterface) const +{ + TOJSVAL_SETUP(); + SET_MSG_PROPERTY(entity); + SET_MSG_PROPERTY(from); + SET_MSG_PROPERTY(to); + return JS::ObjectValue(*obj); +} + +CMessage* CMessageColorChanged::FromJSVal(ScriptInterface& scriptInterface, JS::HandleValue val) +{ + FROMJSVAL_SETUP(); + GET_MSG_PROPERTY(entity_id_t, entity); + GET_MSG_PROPERTY(player_id_t, from); + GET_MSG_PROPERTY(player_id_t, to); + return new CMessageColorChanged(entity, from, to); +} + +//////////////////////////////// + JS::Value CMessageOwnershipChanged::ToJSVal(ScriptInterface& scriptInterface) const { TOJSVAL_SETUP();