forked from 0ad/0ad
Map flares
Add flaring on the map and resize the minimap buttons. Target marker and button by Stan. Reviewed/Commented by wraitii, elexis, vladislavbelov Refs: #3491 Refs: #57 Differential Revision: https://code.wildfiregames.com/D1751 This was SVN commit r25691.
This commit is contained in:
parent
3bb5f364c2
commit
8e63a0322c
@ -341,6 +341,8 @@ rotate.cw = RightBracket ; Rotate building placement preview clockwise
|
||||
rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise
|
||||
snaptoedges = Ctrl ; Modifier to align new structures with nearby existing structure
|
||||
toggledefaultformation = "" ; Switch between null default formation and the last default formation used (defaults to "box")
|
||||
flare = K ; Modifier to send a flare to your allies
|
||||
flareactivate = "" ; Modifier to activate the mode to send a flare to your allies
|
||||
; Overlays
|
||||
showstatusbars = Tab ; Toggle display of status bars
|
||||
devcommands.toggle = "Alt+D" ; Toggle developer commands panel
|
||||
|
@ -230,6 +230,31 @@
|
||||
<ref name="bool"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="flare_texture_count">
|
||||
<data type="nonNegativeInteger"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="flare_render_size">
|
||||
<data type="nonNegativeInteger"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="flare_animation_speed">
|
||||
<data type="nonNegativeInteger"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="flare_interleave">
|
||||
<ref name="bool"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="flare_lifetime_seconds">
|
||||
<data type="nonNegativeInteger"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="font"/>
|
||||
</optional>
|
||||
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<actor version="1">
|
||||
<castshadow/>
|
||||
<float/>
|
||||
<group>
|
||||
<variant frequency="100" name="Flare Target Marker">
|
||||
<animations>
|
||||
<animation file="other/flare_target_marker_idle.dae" name="Idle" speed="100"/>
|
||||
</animations>
|
||||
<mesh>skeletal/flare_target_marker.dae</mesh>
|
||||
<textures>
|
||||
<texture file="skeletal/flare_target_marker_diffuse.png" name="baseTex"/>
|
||||
<texture file="skeletal/flare_target_marker_spec.png" name="specTex"/>
|
||||
</textures>
|
||||
</variant>
|
||||
</group>
|
||||
<material>player_trans_spec.xml</material>
|
||||
</actor>
|
BIN
binaries/data/mods/public/art/animation/other/flare_target_marker_idle.dae
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/animation/other/flare_target_marker_idle.dae
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/meshes/skeletal/flare_target_marker.dae
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/meshes/skeletal/flare_target_marker.dae
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,27 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<skeletons>
|
||||
<standard_skeleton title="flare-marker-armature" id="flare-marker-armature">
|
||||
<bone name="base">
|
||||
<bone name="ping-marker-ball" />
|
||||
<bone name="ping-marker-body" />
|
||||
</bone>
|
||||
<bone name="target marker" />
|
||||
</standard_skeleton>
|
||||
<skeleton title="flare-marker-armature" target="flare-marker-armature">
|
||||
<identifier>
|
||||
<root>base</root>
|
||||
</identifier>
|
||||
<bone name="base">
|
||||
<target>base</target>
|
||||
<bone name="ping-marker-ball">
|
||||
<target>ping-marker-ball</target>
|
||||
</bone>
|
||||
<bone name="ping-marker-body">
|
||||
<target>ping-marker-body</target>
|
||||
</bone>
|
||||
</bone>
|
||||
<bone name="target marker">
|
||||
<target>target marker</target>
|
||||
</bone>
|
||||
</skeleton>
|
||||
</skeletons>
|
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame00.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame00.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame01.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame01.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame02.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame02.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame03.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame03.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame04.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame04.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame05.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame05.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame06.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame06.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame07.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame07.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame08.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame08.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame09.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame09.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame10.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame10.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame11.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame11.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame12.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame12.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame13.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame13.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame14.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame14.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame15.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/animated/minimap-flare/frame15.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/cursors/action-flare.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/cursors/action-flare.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
1 1
|
BIN
binaries/data/mods/public/art/textures/skins/skeletal/flare_target_marker_diffuse.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/skins/skeletal/flare_target_marker_diffuse.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/skins/skeletal/flare_target_marker_spec.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/skins/skeletal/flare_target_marker_spec.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/minimap-flare-highlight.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/minimap-flare-highlight.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/minimap-flare.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/minimap-flare.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -115,6 +115,7 @@ You may change hotkeys in [font="sans-bold-14"]Options > Hotkeys[font="sans-14"]
|
||||
hotkey.session.attackmove + Right Click with unit(s) selected – Attack move (by default all enemy units and structures along the way are targeted)
|
||||
hotkey.session.attackmoveUnit + Right Click with unit(s) selected – Attack move, only units along the way are targeted
|
||||
hotkey.session.snaptoedges + Mouse Move near structures – Align the new structure with an existing nearby structure
|
||||
hotkey.session.flare + Right Click – Send a flare to your allies
|
||||
|
||||
[font="sans-bold-14"]Overlays[font="sans-14"]
|
||||
hotkey.session.gui.toggle – Toggle the GUI
|
||||
|
@ -32,6 +32,7 @@ const INPUT_BUILDING_WALL_CLICK = 8;
|
||||
const INPUT_BUILDING_WALL_PATHING = 9;
|
||||
const INPUT_UNIT_POSITION_START = 10;
|
||||
const INPUT_UNIT_POSITION = 11;
|
||||
const INPUT_FLARE = 12;
|
||||
|
||||
var inputState = INPUT_NORMAL;
|
||||
|
||||
@ -79,6 +80,16 @@ var g_DragStart;
|
||||
*/
|
||||
var clickedEntity = INVALID_ENTITY;
|
||||
|
||||
/**
|
||||
* Store the last time the flare functionality was used to prevent overusage.
|
||||
*/
|
||||
var g_LastFlareTime;
|
||||
|
||||
/**
|
||||
* The duration in ms for which we disable flaring after each flare to prevent overusage.
|
||||
*/
|
||||
const g_FlareCooldown = 5000;
|
||||
|
||||
// Same double-click behaviour for hotkey presses.
|
||||
const doublePressTime = 500;
|
||||
var doublePressTimer = 0;
|
||||
@ -89,7 +100,12 @@ function updateCursorAndTooltip()
|
||||
let cursorSet = false;
|
||||
let tooltipSet = false;
|
||||
let informationTooltip = Engine.GetGUIObjectByName("informationTooltip");
|
||||
if (!mouseIsOverObject && (inputState == INPUT_NORMAL || inputState == INPUT_PRESELECTEDACTION) || g_MiniMapPanel.isMouseOverMiniMap())
|
||||
if (inputState == INPUT_FLARE || inputState == INPUT_NORMAL && Engine.HotkeyIsPressed("session.flare") && !g_IsObserver)
|
||||
{
|
||||
Engine.SetCursor("action-flare");
|
||||
cursorSet = true;
|
||||
}
|
||||
else if (!mouseIsOverObject && (inputState == INPUT_NORMAL || inputState == INPUT_PRESELECTEDACTION) || g_MiniMapPanel.isMouseOverMiniMap())
|
||||
{
|
||||
let action = determineAction(mouseX, mouseY, g_MiniMapPanel.isMouseOverMiniMap());
|
||||
if (action)
|
||||
@ -810,6 +826,11 @@ function handleInputAfterGui(ev)
|
||||
return false;
|
||||
|
||||
case "mousebuttondown":
|
||||
if (Engine.HotkeyIsPressed("session.flare") && controlsPlayer(g_ViewedPlayer))
|
||||
{
|
||||
triggerFlareAction(Engine.GetTerrainAtScreenPoint(ev.x, ev.y));
|
||||
return true;
|
||||
}
|
||||
if (ev.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
g_DragStart = new Vector2D(ev.x, ev.y);
|
||||
@ -1111,6 +1132,21 @@ function handleInputAfterGui(ev)
|
||||
|
||||
}
|
||||
break;
|
||||
case INPUT_FLARE:
|
||||
if (ev.type == "mousebuttondown")
|
||||
{
|
||||
if (ev.button == SDL_BUTTON_LEFT && controlsPlayer(g_ViewedPlayer))
|
||||
{
|
||||
triggerFlareAction(Engine.GetTerrainAtScreenPoint(ev.x, ev.y));
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
else if (ev.button == SDL_BUTTON_RIGHT)
|
||||
{
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1224,6 +1260,21 @@ function positionUnitsFreehandSelectionMouseUp(ev)
|
||||
return true;
|
||||
}
|
||||
|
||||
function triggerFlareAction(target)
|
||||
{
|
||||
let now = Date.now();
|
||||
if (g_LastFlareTime && now < g_LastFlareTime + g_FlareCooldown)
|
||||
return;
|
||||
|
||||
g_LastFlareTime = now;
|
||||
displayFlare(target, Engine.GetPlayerID());
|
||||
Engine.PlayUISound(g_FlareSound, false);
|
||||
Engine.PostNetworkCommand({
|
||||
"type": "map-flare",
|
||||
"target": target
|
||||
});
|
||||
}
|
||||
|
||||
function handleUnitAction(target, action)
|
||||
{
|
||||
if (!g_UnitActions[action.type] || !g_UnitActions[action.type].execute)
|
||||
|
@ -8,6 +8,22 @@ var g_TutorialMessages = [];
|
||||
*/
|
||||
var g_TutorialNewMessageTags = { "color": "255 226 149" };
|
||||
|
||||
/**
|
||||
* The number of seconds we observe for rate limiting flares.
|
||||
*/
|
||||
var g_FlareRateLimitScope = 10;
|
||||
|
||||
/**
|
||||
* The maximum allowed number of flares within g_FlareRateLimitScope seconds.
|
||||
* This should be a bit larger than the number of flares that can be sent in theory by using the GUI.
|
||||
*/
|
||||
var g_FlareRateLimitMaximumFlares = 16;
|
||||
|
||||
/**
|
||||
* Contains the arrival timestamps the flares of the last g_FlareRateLimitScope seconds.
|
||||
*/
|
||||
var g_FlareRateLimitLastTimes = [];
|
||||
|
||||
/**
|
||||
* These handlers are called everytime a client joins or disconnects.
|
||||
*/
|
||||
@ -267,6 +283,27 @@ var g_NotificationsTypes =
|
||||
}
|
||||
|
||||
global.music.setLocked(notification.lock);
|
||||
},
|
||||
"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);
|
||||
|
||||
displayFlare(notification.target, player);
|
||||
Engine.PlayUISound(g_FlareSound, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,9 +6,10 @@ class MiniMap
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
Engine.GetGUIObjectByName("minimap").onWorldClick = this.onWorldClick.bind(this);
|
||||
Engine.GetGUIObjectByName("minimap").onMouseEnter = this.onMouseEnter.bind(this);
|
||||
Engine.GetGUIObjectByName("minimap").onMouseLeave = this.onMouseLeave.bind(this);
|
||||
this.miniMap = Engine.GetGUIObjectByName("minimap");
|
||||
this.miniMap.onWorldClick = this.onWorldClick.bind(this);
|
||||
this.miniMap.onMouseEnter = this.onMouseEnter.bind(this);
|
||||
this.miniMap.onMouseLeave = this.onMouseLeave.bind(this);
|
||||
this.mouseIsOverMiniMap = false;
|
||||
}
|
||||
|
||||
@ -16,9 +17,15 @@ 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"))
|
||||
{
|
||||
triggerFlareAction(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
if (inputState != INPUT_PRESELECTEDACTION || preSelectedAction == ACTION_NONE)
|
||||
if (inputState != INPUT_FLARE && (inputState != INPUT_PRESELECTEDACTION || preSelectedAction == ACTION_NONE))
|
||||
return false;
|
||||
}
|
||||
else if (button == SDL_BUTTON_RIGHT)
|
||||
@ -29,6 +36,11 @@ class MiniMap
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
else if (inputState == INPUT_FLARE)
|
||||
{
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
else if (inputState != INPUT_NORMAL)
|
||||
return false;
|
||||
}
|
||||
@ -39,6 +51,13 @@ class MiniMap
|
||||
if (!controlsPlayer(g_ViewedPlayer))
|
||||
return false;
|
||||
|
||||
if (inputState == INPUT_FLARE && button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
triggerFlareAction(target);
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
let action = determineAction(undefined, undefined, true);
|
||||
if (!action)
|
||||
return false;
|
||||
@ -64,4 +83,9 @@ class MiniMap
|
||||
{
|
||||
return this.mouseIsOverMiniMap;
|
||||
}
|
||||
|
||||
flare(target, playerID)
|
||||
{
|
||||
return this.miniMap.flare({ "x": target.x, "y": target.z }, g_DiplomacyColors.getPlayerColor(playerID));
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<!-- Idle Worker Button -->
|
||||
<object name="idleWorkerButton"
|
||||
type="button"
|
||||
size="50%-5 50%-5 100%-5 100%-5"
|
||||
size="100%-119 100%-120 100%-4 100%-5"
|
||||
tooltip_style="sessionToolTip"
|
||||
hotkey="selection.idleworker"
|
||||
sprite="stretched:session/minimap-idle.png"
|
||||
@ -28,11 +28,33 @@
|
||||
<!-- Diplomacy Colors Button -->
|
||||
<object name="diplomacyColorsButton"
|
||||
type="button"
|
||||
size="5 50%-5 50%+5 100%-5"
|
||||
size="4 100%-120 119 100%-5"
|
||||
tooltip_style="sessionToolTip"
|
||||
hotkey="session.diplomacycolors"
|
||||
/>
|
||||
|
||||
<!-- Flare Button -->
|
||||
<object
|
||||
name="flareButton"
|
||||
type="button"
|
||||
size="3 3 118 118"
|
||||
tooltip_style="sessionToolTip"
|
||||
hotkey="session.flareactivate"
|
||||
sprite="stretched:session/minimap-flare.png"
|
||||
sprite_over="stretched:session/minimap-flare-highlight.png"
|
||||
mouse_event_mask="texture:session/minimap-flare.png"
|
||||
/>
|
||||
|
||||
<!-- MiniMap -->
|
||||
<object name="minimap" size="8 8 100%-8 100%-8" type="minimap" mask="true"/>
|
||||
<object
|
||||
name="minimap"
|
||||
size="8 8 100%-8 100%-8"
|
||||
type="minimap"
|
||||
mask="true"
|
||||
flare_texture_count="16"
|
||||
flare_render_size="32"
|
||||
flare_animation_speed="7"
|
||||
flare_interleave="true"
|
||||
flare_lifetime_seconds="6"
|
||||
/>
|
||||
</object>
|
||||
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* If the button that this class manages is pressed, an idle unit having one of the given classes is selected.
|
||||
*/
|
||||
class MiniMapFlareButton
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.flareButton = Engine.GetGUIObjectByName("flareButton");
|
||||
this.flareButton.onPress = this.onPress.bind(this);
|
||||
registerHotkeyChangeHandler(this.onHotkeyChange.bind(this));
|
||||
}
|
||||
|
||||
onHotkeyChange()
|
||||
{
|
||||
this.flareButton.tooltip =
|
||||
colorizeHotkey("%(hotkey)s" + " ", "session.flare") +
|
||||
translate(this.Tooltip);
|
||||
}
|
||||
|
||||
onPress()
|
||||
{
|
||||
if (g_IsObserver)
|
||||
return;
|
||||
if (inputState == INPUT_NORMAL)
|
||||
inputState = INPUT_FLARE;
|
||||
}
|
||||
}
|
||||
|
||||
MiniMapFlareButton.prototype.Tooltip = markForTranslation("Send a flare to your allies");
|
@ -7,9 +7,15 @@ class MiniMapPanel
|
||||
{
|
||||
this.diplomacyColorsButton = new MiniMapDiplomacyColorsButton(diplomacyColors);
|
||||
this.idleWorkerButton = new MiniMapIdleWorkerButton(playerViewControl, idleWorkerClasses);
|
||||
this.flareButton = new MiniMapFlareButton();
|
||||
this.miniMap = new MiniMap();
|
||||
}
|
||||
|
||||
flare(target, playerID)
|
||||
{
|
||||
return this.miniMap.flare(target, playerID);
|
||||
}
|
||||
|
||||
isMouseOverMiniMap()
|
||||
{
|
||||
return this.miniMap.isMouseOverMiniMap();
|
||||
|
@ -3,9 +3,15 @@
|
||||
* given a command type.
|
||||
*/
|
||||
var g_TargetMarker = {
|
||||
"move": "special/target_marker"
|
||||
"move": "special/target_marker",
|
||||
"map_flare": "special/flare_target_marker"
|
||||
};
|
||||
|
||||
/**
|
||||
* Sound we play when displaying a flare.
|
||||
*/
|
||||
var g_FlareSound = "audio/interface/alarm/alarmally_1.ogg";
|
||||
|
||||
/**
|
||||
* Which enemy entity types will be attacked on sight when patroling.
|
||||
*/
|
||||
@ -1828,6 +1834,17 @@ function DrawTargetMarker(target)
|
||||
});
|
||||
}
|
||||
|
||||
function displayFlare(target, playerID)
|
||||
{
|
||||
Engine.GuiInterfaceCall("AddTargetMarker", {
|
||||
"template": g_TargetMarker.map_flare,
|
||||
"x": target.x,
|
||||
"z": target.z,
|
||||
"owner": playerID
|
||||
});
|
||||
g_MiniMapPanel.flare(target, playerID);
|
||||
}
|
||||
|
||||
function getCommandInfo(command, entStates)
|
||||
{
|
||||
return entStates && g_EntityCommands[command] &&
|
||||
|
@ -1116,7 +1116,9 @@ GuiInterface.prototype.AddTargetMarker = function(player, cmd)
|
||||
let ent = Engine.AddLocalEntity(cmd.template);
|
||||
if (!ent)
|
||||
return;
|
||||
|
||||
let cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
||||
if (cmpOwnership)
|
||||
cmpOwnership.SetOwner(cmd.owner);
|
||||
let cmpPosition = Engine.QueryInterface(ent, IID_Position);
|
||||
cmpPosition.JumpTo(cmd.x, cmd.z);
|
||||
};
|
||||
|
@ -878,6 +878,16 @@ var g_Commands = {
|
||||
}
|
||||
},
|
||||
|
||||
"map-flare": function(player, cmd, data)
|
||||
{
|
||||
let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGuiInterface.PushNotification({
|
||||
"type": "map-flare",
|
||||
"players": [player],
|
||||
"target": cmd.target
|
||||
});
|
||||
},
|
||||
|
||||
"autoqueue-on": function(player, cmd, data)
|
||||
{
|
||||
for (let ent of data.entities)
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="special/actor">
|
||||
<Decay>
|
||||
<Active>true</Active>
|
||||
<SinkingAnim>false</SinkingAnim>
|
||||
<DelayTime>6</DelayTime>
|
||||
<SinkRate>10000</SinkRate>
|
||||
<SinkAccel>0</SinkAccel>
|
||||
</Decay>
|
||||
<Ownership/>
|
||||
<Position>
|
||||
<Floating>true</Floating>
|
||||
</Position>
|
||||
<VisualActor>
|
||||
<Actor>special/flare_target_marker.xml</Actor>
|
||||
</VisualActor>
|
||||
</Entity>
|
@ -40,6 +40,7 @@ void CGUI::AddObjectTypes()
|
||||
m_ProxyData.insert(JSI_GUIProxy<IGUIObject>::CreateData(*m_ScriptInterface));
|
||||
m_ProxyData.insert(JSI_GUIProxy<CText>::CreateData(*m_ScriptInterface));
|
||||
m_ProxyData.insert(JSI_GUIProxy<CList>::CreateData(*m_ScriptInterface));
|
||||
m_ProxyData.insert(JSI_GUIProxy<CMiniMap>::CreateData(*m_ScriptInterface));
|
||||
m_ProxyData.insert(JSI_GUIProxy<CButton>::CreateData(*m_ScriptInterface));
|
||||
|
||||
AddObjectType("button", &CButton::ConstructObject);
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "lib/external_libraries/libsdl.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/timer.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/ConfigDB.h"
|
||||
#include "ps/CStrInternStatic.h"
|
||||
#include "ps/Filesystem.h"
|
||||
@ -141,7 +142,10 @@ CMiniMap::CMiniMap(CGUI& pGUI) :
|
||||
IGUIObject(pGUI),
|
||||
m_MapSize(0), m_MapScale(1.f),
|
||||
m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW), m_Mask(this, "mask", false),
|
||||
m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false)
|
||||
m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false),
|
||||
m_FlareTextureCount(this, "flare_texture_count", 0), m_FlareRenderSize(this, "flare_render_size", 0),
|
||||
m_FlareAnimationSpeed(this, "flare_animation_speed", 0), m_FlareInterleave(this, "flare_interleave", false),
|
||||
m_FlareLifetimeSeconds(this, "flare_lifetime_seconds", 0)
|
||||
{
|
||||
m_Clicking = false;
|
||||
m_MouseHovering = false;
|
||||
@ -201,6 +205,13 @@ void CMiniMap::HandleMessage(SGUIMessage& Message)
|
||||
IGUIObject::HandleMessage(Message);
|
||||
switch (Message.type)
|
||||
{
|
||||
case GUIM_LOAD:
|
||||
RecreateFlareTextures();
|
||||
break;
|
||||
case GUIM_SETTINGS_UPDATED:
|
||||
if (Message.value == "flare_texture_count")
|
||||
RecreateFlareTextures();
|
||||
break;
|
||||
case GUIM_MOUSE_PRESS_LEFT:
|
||||
if (m_MouseHovering)
|
||||
{
|
||||
@ -248,6 +259,24 @@ void CMiniMap::HandleMessage(SGUIMessage& Message)
|
||||
}
|
||||
}
|
||||
|
||||
void CMiniMap::RecreateFlareTextures()
|
||||
{
|
||||
// Catch invalid values.
|
||||
if (m_FlareTextureCount > 99)
|
||||
{
|
||||
LOGERROR("Invalid value for flare texture count. Valid range is 0-99.");
|
||||
return;
|
||||
}
|
||||
const CStr numberingFormat = "%02u";
|
||||
m_FlareTextures.clear();
|
||||
m_FlareTextures.reserve(m_FlareTextureCount);
|
||||
for (u32 i = 0; i < m_FlareTextureCount; ++i)
|
||||
{
|
||||
CTextureProperties textureProps(L"art/textures/animated/minimap-flare/frame" + CStr(fmt::sprintf(numberingFormat, i)).FromUTF8() + L".png");
|
||||
m_FlareTextures.emplace_back(g_Renderer.GetTextureManager().CreateTexture(textureProps));
|
||||
}
|
||||
}
|
||||
|
||||
bool CMiniMap::IsMouseOver() const
|
||||
{
|
||||
// Get the mouse position.
|
||||
@ -369,6 +398,41 @@ void CMiniMap::DrawViewRect(const CMatrix3D& transform) const
|
||||
glLineWidth(1.0f);
|
||||
}
|
||||
|
||||
void CMiniMap::DrawFlare(CCanvas2D& canvas, const MapFlare& flare, double currentTime) const
|
||||
{
|
||||
if (!m_FlareTextures.size())
|
||||
return;
|
||||
|
||||
// Coordinates with 0,0 in the middle of the minimap and +-0.5 as max.
|
||||
const float invTileMapSize = 1.0f / static_cast<float>(TERRAIN_TILE_SIZE * m_MapSize);
|
||||
const float relativeX = (flare.pos.X * invTileMapSize - 0.5) / m_MapScale;
|
||||
const float relativeY = (flare.pos.Y * invTileMapSize - 0.5) / m_MapScale;
|
||||
|
||||
// Rotate coordinates.
|
||||
const float angle = GetAngle();
|
||||
const float rotatedX = cos(angle) * relativeX + sin(angle) * relativeY;
|
||||
const float rotatedY = -sin(angle) * relativeX + cos(angle) * relativeY;
|
||||
// Calculate coordinates in gui space.
|
||||
const float cx = m_CachedActualSize.left + (0.5f + rotatedX) * m_CachedActualSize.GetWidth();
|
||||
const float cy = m_CachedActualSize.bottom - (0.5f + rotatedY) * m_CachedActualSize.GetHeight();
|
||||
|
||||
const CRect destination(cx-m_FlareRenderSize, cy-m_FlareRenderSize, cx+m_FlareRenderSize, cy+m_FlareRenderSize);
|
||||
|
||||
const u32 flooredStep = floor((currentTime - flare.time) * m_FlareAnimationSpeed);
|
||||
|
||||
CTexturePtr texture = m_FlareTextures[flooredStep % m_FlareTextures.size()];
|
||||
// TODO: Only draw inside the minimap circle.
|
||||
canvas.DrawTexture(texture, destination, CRect(0, 0, texture->GetWidth(), texture->GetHeight()), flare.color, CColor(0.0f, 0.0f, 0.0f, 0.0f), 0.0f);
|
||||
|
||||
// Draw a second circle if the first has reached half of the animation
|
||||
if (m_FlareInterleave && flooredStep >= m_FlareTextures.size() / 2)
|
||||
{
|
||||
texture = m_FlareTextures[(flooredStep - m_FlareTextures.size() / 2) % m_FlareTextures.size()];
|
||||
// TODO: Only draw inside the minimap circle.
|
||||
canvas.DrawTexture(texture, destination, CRect(0, 0, texture->GetWidth(), texture->GetHeight()), flare.color, CColor(0.0f, 0.0f, 0.0f, 0.0f), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
struct MinimapUnitVertex
|
||||
{
|
||||
// This struct is copyable for convenience and because to move is to copy for primitives.
|
||||
@ -521,7 +585,7 @@ void CMiniMap::Draw(CCanvas2D& canvas)
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
PROFILE_START("minimap units");
|
||||
PROFILE_START("minimap units and flares");
|
||||
|
||||
CShaderDefines pointDefines;
|
||||
pointDefines.Add(str_MINIMAP_POINT, str_1);
|
||||
@ -641,5 +705,23 @@ void CMiniMap::Draw(CCanvas2D& canvas)
|
||||
|
||||
DrawViewRect(unitMatrix);
|
||||
|
||||
PROFILE_END("minimap units");
|
||||
while (!m_MapFlares.empty() && m_FlareLifetimeSeconds + m_MapFlares.front().time < cur_time)
|
||||
m_MapFlares.pop_front();
|
||||
|
||||
for (const MapFlare& flare : m_MapFlares)
|
||||
DrawFlare(canvas, flare, cur_time);
|
||||
|
||||
PROFILE_END("minimap units and flares");
|
||||
}
|
||||
|
||||
bool CMiniMap::Flare(const CVector2D& pos, const CStr& colorStr)
|
||||
{
|
||||
CColor color;
|
||||
if (!color.ParseString(colorStr))
|
||||
{
|
||||
LOGERROR("CMiniMap::Flare: Couldn't parse color string");
|
||||
return false;
|
||||
}
|
||||
m_MapFlares.push_back({ pos, color, timer_Time() });
|
||||
return true;
|
||||
}
|
||||
|
@ -18,9 +18,15 @@
|
||||
#ifndef INCLUDED_MINIMAP
|
||||
#define INCLUDED_MINIMAP
|
||||
|
||||
#include "graphics/Color.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "gui/ObjectBases/IGUIObject.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "renderer/VertexArray.h"
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
class CCamera;
|
||||
class CMatrix3D;
|
||||
class CTerrain;
|
||||
@ -28,13 +34,25 @@ class CTerrain;
|
||||
class CMiniMap : public IGUIObject
|
||||
{
|
||||
GUI_OBJECT(CMiniMap)
|
||||
|
||||
public:
|
||||
CMiniMap(CGUI& pGUI);
|
||||
virtual ~CMiniMap();
|
||||
|
||||
bool Flare(const CVector2D& pos, const CStr& colorStr);
|
||||
|
||||
protected:
|
||||
struct MapFlare
|
||||
{
|
||||
CVector2D pos;
|
||||
CColor color;
|
||||
double time;
|
||||
};
|
||||
|
||||
virtual void Draw(CCanvas2D& canvas);
|
||||
|
||||
virtual void CreateJSObject();
|
||||
|
||||
/**
|
||||
* @see IGUIObject#HandleMessage()
|
||||
*/
|
||||
@ -57,6 +75,16 @@ private:
|
||||
// Whether or not the mouse is currently down
|
||||
bool m_Clicking;
|
||||
|
||||
std::deque<MapFlare> m_MapFlares;
|
||||
|
||||
std::vector<CTexturePtr> m_FlareTextures;
|
||||
|
||||
CGUISimpleSetting<u32> m_FlareTextureCount;
|
||||
CGUISimpleSetting<u32> m_FlareRenderSize;
|
||||
CGUISimpleSetting<u32> m_FlareAnimationSpeed;
|
||||
CGUISimpleSetting<bool> m_FlareInterleave;
|
||||
CGUISimpleSetting<u32> m_FlareLifetimeSeconds;
|
||||
|
||||
// Whether to draw a black square around and under the minimap.
|
||||
CGUISimpleSetting<bool> m_Mask;
|
||||
|
||||
@ -66,8 +94,12 @@ private:
|
||||
// 1.f if map is circular or 1.414f if square (to shrink it inside the circle)
|
||||
float m_MapScale;
|
||||
|
||||
void RecreateFlareTextures();
|
||||
|
||||
void DrawViewRect(const CMatrix3D& transform) const;
|
||||
|
||||
void DrawFlare(CCanvas2D& canvas, const MapFlare& flare, double curentTime) const;
|
||||
|
||||
void GetMouseWorldCoordinates(float& x, float& z) const;
|
||||
|
||||
float GetAngle() const;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "gui/ObjectBases/IGUIObject.h"
|
||||
#include "gui/ObjectTypes/CButton.h"
|
||||
#include "gui/ObjectTypes/CList.h"
|
||||
#include "gui/ObjectTypes/CMiniMap.h"
|
||||
#include "gui/ObjectTypes/CText.h"
|
||||
|
||||
// Called for every specialization - adds the common interface.
|
||||
@ -58,3 +59,10 @@ template<> void JSI_GUIProxy<CList>::CreateFunctions(const ScriptRequest& rq, GU
|
||||
CreateFunction<static_cast<void(CList::*)(const CGUIString&)>(&CList::AddItem)>(rq, cache, "addItem");
|
||||
}
|
||||
DECLARE_GUIPROXY(CList);
|
||||
|
||||
// CMiniMap
|
||||
template<> void JSI_GUIProxy<CMiniMap>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
|
||||
{
|
||||
CreateFunction<&CMiniMap::Flare>(rq, cache, "flare");
|
||||
}
|
||||
DECLARE_GUIPROXY(CMiniMap);
|
||||
|
Loading…
Reference in New Issue
Block a user