diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index 8106beb054..36ebb04e66 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -477,7 +477,7 @@ pingduration = 50.0 ; The duration for which an entity will be pin attack = true ; Show a chat notification if you are attacked by another player tribute = true ; Show a chat notification if an ally tributes resources to another team member if teams are locked, and all tributes in observer mode barter = true ; Show a chat notification to observers when a player bartered resources -flare = true ; Show a chat notification when a you or an ally sends a flare +flare = always ; Show a chat notification when a player sends a flare. Possible values: never, observer, always. phase = completed ; Show a chat notification if you or an ally have started, aborted or completed a new phase, and phases of all players in observer mode. Possible values: none, completed, all. [gui.splashscreen] diff --git a/binaries/data/mods/public/art/textures/cursors/action-observer-flare.png b/binaries/data/mods/public/art/textures/cursors/action-observer-flare.png new file mode 100644 index 0000000000..95f725dae6 --- /dev/null +++ b/binaries/data/mods/public/art/textures/cursors/action-observer-flare.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11022a8511dfb6779248ae55590973942e6469681f78f78a8e27a9a01c6aed5d +size 1653 diff --git a/binaries/data/mods/public/art/textures/cursors/action-flare.txt b/binaries/data/mods/public/art/textures/cursors/action-observer-flare.txt similarity index 100% rename from binaries/data/mods/public/art/textures/cursors/action-flare.txt rename to binaries/data/mods/public/art/textures/cursors/action-observer-flare.txt diff --git a/binaries/data/mods/public/art/textures/cursors/action-flare.png b/binaries/data/mods/public/art/textures/cursors/action-player-flare.png similarity index 100% rename from binaries/data/mods/public/art/textures/cursors/action-flare.png rename to binaries/data/mods/public/art/textures/cursors/action-player-flare.png diff --git a/binaries/data/mods/public/art/textures/cursors/action-player-flare.txt b/binaries/data/mods/public/art/textures/cursors/action-player-flare.txt new file mode 100644 index 0000000000..2fb73a07ec --- /dev/null +++ b/binaries/data/mods/public/art/textures/cursors/action-player-flare.txt @@ -0,0 +1 @@ +1 1 diff --git a/binaries/data/mods/public/art/textures/ui/session/minimap-flare-disabled.png b/binaries/data/mods/public/art/textures/ui/session/minimap-flare-disabled.png deleted file mode 100644 index 2762b4d945..0000000000 --- a/binaries/data/mods/public/art/textures/ui/session/minimap-flare-disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:41432cec030750dc8ba0fd3a1ff496c50a5731e78f6c12164d97fd991b37ae63 -size 13267 diff --git a/binaries/data/mods/public/art/textures/ui/session/minimap-observer-flare-highlight.png b/binaries/data/mods/public/art/textures/ui/session/minimap-observer-flare-highlight.png new file mode 100644 index 0000000000..ec71983ccf --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/minimap-observer-flare-highlight.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f2680500fd122178db80b378e19821eb30baf639edc46eacc3d23c97b19481c +size 6214 diff --git a/binaries/data/mods/public/art/textures/ui/session/minimap-observer-flare.png b/binaries/data/mods/public/art/textures/ui/session/minimap-observer-flare.png new file mode 100644 index 0000000000..753652d3da --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/minimap-observer-flare.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85d985e5b208f4947fe2bf580a5f181990892620c50f1dd356655d879a94aea4 +size 5356 diff --git a/binaries/data/mods/public/art/textures/ui/session/minimap-flare-highlight.png b/binaries/data/mods/public/art/textures/ui/session/minimap-player-flare-highlight.png similarity index 100% rename from binaries/data/mods/public/art/textures/ui/session/minimap-flare-highlight.png rename to binaries/data/mods/public/art/textures/ui/session/minimap-player-flare-highlight.png diff --git a/binaries/data/mods/public/art/textures/ui/session/minimap-flare.png b/binaries/data/mods/public/art/textures/ui/session/minimap-player-flare.png similarity index 100% rename from binaries/data/mods/public/art/textures/ui/session/minimap-flare.png rename to binaries/data/mods/public/art/textures/ui/session/minimap-player-flare.png diff --git a/binaries/data/mods/public/gui/options/options.json b/binaries/data/mods/public/gui/options/options.json index 70a0299281..5b4af2326c 100644 --- a/binaries/data/mods/public/gui/options/options.json +++ b/binaries/data/mods/public/gui/options/options.json @@ -595,10 +595,15 @@ "config": "gui.session.notifications.barter" }, { - "type": "boolean", + "type": "dropdown", "label": "Chat notification flare", - "tooltip": "Show a chat notification when you or an ally sends a flare.", - "config": "gui.session.notifications.flare" + "tooltip": "Show a chat notification when a player sends a flare.", + "config": "gui.session.notifications.flare", + "list": [ + { "value": "never", "label": "Never", "tooltip": "Disable flare chat notifications."}, + { "value": "observer", "label": "Only as observer", "tooltip": "Only show flare chat notifications when you are observer."}, + { "value": "always", "label": "Always", "tooltip": "Always show flare chat notifications – as player and as observer."} + ] }, { "type": "dropdown", diff --git a/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js b/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js index 7cd3a15731..a801a329a7 100644 --- a/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js +++ b/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js @@ -177,12 +177,22 @@ ChatMessageFormatSimulation.tribute = class ChatMessageFormatSimulation.flare = class { - // We don't need to check whether the player is supposed to see the flare as this function is only ever called in that case + // We don't need to check whether the player is supposed to see the flare as this function is only ever called in that case. parse(msg) { - if (Engine.ConfigDB_GetValue("user", "gui.session.notifications.flare") != "true") + switch (Engine.ConfigDB_GetValue("user", "gui.session.notifications.flare")) + { + case "never": return ""; + case "observer": + if (!g_IsObserver) + return ""; + + default: + break; + } + return { "text": sprintf(translate("%(icon)s%(player)s has sent a flare."), { "icon": "[icon=\"icon_focusflare\" displace=\"0 1\"]", diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js index 2710979f3e..ef50a0c667 100644 --- a/binaries/data/mods/public/gui/session/input.js +++ b/binaries/data/mods/public/gui/session/input.js @@ -101,9 +101,13 @@ function updateCursorAndTooltip() let cursorSet = false; let tooltipSet = false; let informationTooltip = Engine.GetGUIObjectByName("informationTooltip"); - if (inputState == INPUT_FLARE || inputState == INPUT_NORMAL && Engine.HotkeyIsPressed("session.flare") && !g_IsObserver) + if (inputState == INPUT_FLARE || inputState == INPUT_NORMAL && Engine.HotkeyIsPressed("session.flare")) { - Engine.SetCursor("action-flare"); + Engine.SetCursor( + g_IsObserver ? + "action-observer-flare" : + "action-player-flare" + ); cursorSet = true; } else if (!mouseIsOverObject && (inputState == INPUT_NORMAL || inputState == INPUT_PRESELECTEDACTION) || g_MiniMapPanel.isMouseOverMiniMap()) @@ -825,7 +829,7 @@ function handleInputAfterGui(ev) return false; case "mousebuttondown": - if (Engine.HotkeyIsPressed("session.flare") && controlsPlayer(g_ViewedPlayer)) + if (Engine.HotkeyIsPressed("session.flare")) { triggerFlareAction(Engine.GetTerrainAtScreenPoint(ev.x, ev.y)); return true; @@ -1134,7 +1138,7 @@ function handleInputAfterGui(ev) case INPUT_FLARE: if (ev.type == "mousebuttondown") { - if (ev.button == SDL_BUTTON_LEFT && controlsPlayer(g_ViewedPlayer)) + if (ev.button == SDL_BUTTON_LEFT) { triggerFlareAction(Engine.GetTerrainAtScreenPoint(ev.x, ev.y)); inputState = INPUT_NORMAL; diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index 63100b58f4..7f688c5347 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -290,24 +290,26 @@ var g_NotificationsTypes = }, "map-flare": function(notification, player) { - // Don't display for the player that did the flare because they will see it immediately - if (player != Engine.GetPlayerID() && g_Players[player].isMutualAlly[Engine.GetPlayerID()]) - { - let now = Date.now(); - if (g_FlareRateLimitLastTimes.length) - { - g_FlareRateLimitLastTimes = g_FlareRateLimitLastTimes.filter(t => now - t < g_FlareRateLimitScope * 1000); - if (g_FlareRateLimitLastTimes.length >= g_FlareRateLimitMaximumFlares) - { - warn("Received too many flares. Dropping a flare request by '" + g_Players[player].name + "'."); - return; - } - } - g_FlareRateLimitLastTimes.push(now); + const shouldSeeFlare = g_IsObserver || g_Players[player]?.isMutualAlly[Engine.GetPlayerID()]; - displayFlare(notification.position, notification.guid); - Engine.PlayUISound(g_FlareSound, false); + // Don't display for the player that did the flare because they will see it immediately. + if (!shouldSeeFlare || notification.guid == Engine.GetPlayerGUID()) + return; + + let now = Date.now(); + if (g_FlareRateLimitLastTimes.length) + { + g_FlareRateLimitLastTimes = g_FlareRateLimitLastTimes.filter(t => now - t < g_FlareRateLimitScope * 1000); + if (g_FlareRateLimitLastTimes.length >= g_FlareRateLimitMaximumFlares) + { + warn("Received too many flares. Dropping a flare request by '" + g_Players[player].name + "'."); + return; + } } + g_FlareRateLimitLastTimes.push(now); + + displayFlare(notification.position, notification.guid); + Engine.PlayUISound(g_FlareSound, false); } }; diff --git a/binaries/data/mods/public/gui/session/minimap/MiniMap.js b/binaries/data/mods/public/gui/session/minimap/MiniMap.js index 3dc47c84d0..4b88643a85 100644 --- a/binaries/data/mods/public/gui/session/minimap/MiniMap.js +++ b/binaries/data/mods/public/gui/session/minimap/MiniMap.js @@ -28,7 +28,7 @@ class MiniMap { // Partly duplicated from handleInputAfterGui(), but with the input being // world coordinates instead of screen coordinates. - if (inputState == INPUT_NORMAL && controlsPlayer(g_ViewedPlayer) && Engine.HotkeyIsPressed("session.flare")) + if (inputState == INPUT_NORMAL && Engine.HotkeyIsPressed("session.flare")) { triggerFlareAction(target); return true; @@ -97,6 +97,10 @@ class MiniMap flare(target, playerID) { - return this.miniMap.flare({ "x": target.x, "y": target.z }, g_DiplomacyColors.getPlayerColor(playerID)); + return this.miniMap.flare( + { "x": target.x, "y": target.z }, + // Observers flare in white. + g_DiplomacyColors.getPlayerColor(playerID == -1 ? 0 : playerID) + ); } } diff --git a/binaries/data/mods/public/gui/session/minimap/MiniMap.xml b/binaries/data/mods/public/gui/session/minimap/MiniMap.xml index 74cf99fbf4..2b6cefa89e 100644 --- a/binaries/data/mods/public/gui/session/minimap/MiniMap.xml +++ b/binaries/data/mods/public/gui/session/minimap/MiniMap.xml @@ -57,9 +57,6 @@ size="2 4 117 119" tooltip_style="sessionToolTip" hotkey="session.flareactivate" - sprite="stretched:session/minimap-flare.png" - sprite_over="stretched:session/minimap-flare-highlight.png" - sprite_disabled="stretched:session/minimap-flare-disabled.png" mouse_event_mask="texture:session/minimap-flare.png" /> diff --git a/binaries/data/mods/public/gui/session/minimap/MiniMapFlareButton.js b/binaries/data/mods/public/gui/session/minimap/MiniMapFlareButton.js index 1130a8a1c6..e91f45bae8 100644 --- a/binaries/data/mods/public/gui/session/minimap/MiniMapFlareButton.js +++ b/binaries/data/mods/public/gui/session/minimap/MiniMapFlareButton.js @@ -1,5 +1,5 @@ /** - * If the button that this class manages is pressed, an idle unit having one of the given classes is selected. + * If the button that this class manages is pressed, the input state is switched to 'flare', letting the player send a flare by left-clicking on the map. */ class MiniMapFlareButton { @@ -13,23 +13,36 @@ class MiniMapFlareButton rebuild() { - this.flareButton.enabled = !g_IsObserver; + if (g_IsObserver) + { + this.flareButton.sprite = "stretched:session/minimap-observer-flare.png"; + this.flareButton.sprite_over = "stretched:session/minimap-observer-flare-highlight.png"; + } + else + { + this.flareButton.sprite = "stretched:session/minimap-player-flare.png"; + this.flareButton.sprite_over = "stretched:session/minimap-player-flare-highlight.png"; + } + this.updateTooltip(); } onHotkeyChange() { - this.flareButton.tooltip = - colorizeHotkey("%(hotkey)s" + " ", "session.flare") + - translate(this.Tooltip); + this.colorizedHotkey = colorizeHotkey("%(hotkey)s" + " ", "session.flare"); + this.updateTooltip(); + } + + updateTooltip() + { + this.flareButton.tooltip = this.colorizedHotkey + (g_IsObserver ? this.ObserverTooltip : this.PlayerTooltip); } onPress() { - if (g_IsObserver) - return; if (inputState == INPUT_NORMAL) inputState = INPUT_FLARE; } } -MiniMapFlareButton.prototype.Tooltip = markForTranslation("Send a flare to your allies"); +MiniMapFlareButton.prototype.PlayerTooltip = markForTranslation("Send a flare to your allies."); +MiniMapFlareButton.prototype.ObserverTooltip = markForTranslation("Send a flare to other observers."); diff --git a/binaries/data/mods/public/gui/session/unit_actions.js b/binaries/data/mods/public/gui/session/unit_actions.js index 316b7115ea..62673e1198 100644 --- a/binaries/data/mods/public/gui/session/unit_actions.js +++ b/binaries/data/mods/public/gui/session/unit_actions.js @@ -1944,7 +1944,8 @@ function displayFlare(position, playerGUID) "template": g_TargetMarker.map_flare, "x": position.x, "z": position.z, - "owner": playerID + // Set the owner to gaia if the flare was sent by an observer (to make the target marker white). + "owner": playerID != -1 ? playerID : 0 }); g_MiniMapPanel.flare(position, playerID); addChatMessage({ diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js index 8498388f0d..8e301bb145 100644 --- a/binaries/data/mods/public/simulation/helpers/Commands.js +++ b/binaries/data/mods/public/simulation/helpers/Commands.js @@ -4,6 +4,13 @@ var g_DebugCommands = false; function ProcessCommand(player, cmd) { + if (player == -1) + { + if (g_ObserverCommands[cmd.type]) + g_ObserverCommands[cmd.type](player, cmd, {}); + return; + } + let cmpPlayer = QueryPlayerIDInterface(player); if (!cmpPlayer) return; @@ -908,6 +915,10 @@ var g_Commands = { }; +var g_ObserverCommands = { + "map-flare": g_Commands["map-flare"] +}; + /** * Sends a GUI notification about unit(s) that failed to ungarrison. */