Enable observers to see and send flares

This patch gives observers the possibility to see and send flares.
Players keep their color after resigning and "true" observers flare in white.
Observers render all flares; the ones sent by any player or observer.
Player continue to only see their allies' flares.
The flare notification option is converted into a dropdown.
-> adding the possibility to only receive them as observer.
This commit is contained in:
Vantha 2024-11-14 10:26:09 +01:00 committed by Nicolas Auvray
parent 9e5e2708f4
commit a1796ed71f
19 changed files with 97 additions and 43 deletions

View file

@ -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]

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11022a8511dfb6779248ae55590973942e6469681f78f78a8e27a9a01c6aed5d
size 1653

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:41432cec030750dc8ba0fd3a1ff496c50a5731e78f6c12164d97fd991b37ae63
size 13267

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8f2680500fd122178db80b378e19821eb30baf639edc46eacc3d23c97b19481c
size 6214

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:85d985e5b208f4947fe2bf580a5f181990892620c50f1dd356655d879a94aea4
size 5356

View file

@ -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",

View file

@ -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\"]",

View file

@ -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;

View file

@ -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);
}
};

View file

@ -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)
);
}
}

View file

@ -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"
/>

View file

@ -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.");

View file

@ -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({

View file

@ -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.
*/