Rewrite developer overlay to use class syntax, one class per checkbox, a class for the EntityState overlay and TimeWarp debug feature, refs #5387.

Using 22 classes instead of 1 class (refs 2bcf4f678b/D1928) leverages
more benefit of the paradigm.
In particular it means the checkboxes can own the EntityState and
TimeWarp helpers and the DeveloperOverlay itself can remain independent.
Improve performance by 200 microseconds per turn by unsubscribing from
onSimulationUpdate when the developer overlay is not opened, refs
e3f43f6352 / D2378.
Move TimeWarp from input.js from 8ddef2fee0 to independent class using
hotkey release event from 78bc56f33e, refs #3194.

Differential Revision: https://code.wildfiregames.com/D2383
This was SVN commit r23081.
This commit is contained in:
elexis 2019-10-19 00:26:34 +00:00
parent 6ec5855136
commit 96a6a136b6
15 changed files with 601 additions and 319 deletions

View File

@ -1,259 +0,0 @@
function DeveloperOverlay(playerViewControl)
{
this.commandHeight = 20;
this.displayState = false;
this.timeWarp = false;
this.controlAll = false;
this.playerViewControl = playerViewControl;
Engine.GetGUIObjectByName("devCommandsOverlay").onPress = this.toggle;
this.layout();
registerSimulationUpdateHandler(this.update.bind(this));
registerEntitySelectionChangeHandler(this.update.bind(this));
}
DeveloperOverlay.prototype.getCommands = function() {
return [
{
"label": translate("Control all units"),
"onPress": checked => {
Engine.PostNetworkCommand({
"type": "control-all",
"flag": checked
});
},
"checked": () => this.controlAll,
},
{
"label": translate("Change perspective"),
"onPress": checked => {
this.playerViewControl.setChangePerspective(checked);
},
},
{
"label": translate("Display selection state"),
"onPress": checked => {
this.displayState = checked;
this.update();
},
},
{
"label": translate("Pathfinder overlay"),
"onPress": checked => {
Engine.GuiInterfaceCall("SetPathfinderDebugOverlay", checked);
},
},
{
"label": translate("Obstruction overlay"),
"onPress": checked => {
Engine.GuiInterfaceCall("SetObstructionDebugOverlay", checked);
},
},
{
"label": translate("Unit motion overlay"),
"onPress": checked => {
g_Selection.SetMotionDebugOverlay(checked);
},
},
{
"label": translate("Range overlay"),
"onPress": checked => {
Engine.GuiInterfaceCall("SetRangeDebugOverlay", checked);
},
},
{
"label": translate("Bounding box overlay"),
"onPress": checked => {
Engine.SetBoundingBoxDebugOverlay(checked);
},
},
{
"label": translate("Restrict camera"),
"onPress": checked => {
Engine.GameView_SetConstrainCameraEnabled(checked);
},
"checked": () => Engine.GameView_GetConstrainCameraEnabled(),
},
{
"label": translate("Reveal map"),
"onPress": checked => {
Engine.PostNetworkCommand({
"type": "reveal-map",
"enable": checked
});
},
"checked": () => Engine.GuiInterfaceCall("IsMapRevealed"),
},
{
"label": translate("Enable time warp"),
"onPress": checked => {
this.timeWarp = checked;
if (checked)
messageBox(
500, 250,
translate("Note: time warp mode is a developer option, and not intended for use over long periods of time. Using it incorrectly may cause the game to run out of memory or crash."),
translate("Time warp mode"));
Engine.EnableTimeWarpRecording(checked ? 10 : 0);
},
},
{
"label": translate("Promote selected units"),
"onPress": checked => {
Engine.PostNetworkCommand({
"type": "promote",
"entities": g_Selection.toList()
});
},
},
{
"label": translate("Hierarchical pathfinder overlay"),
"onPress": checked => {
Engine.GuiInterfaceCall("SetPathfinderHierDebugOverlay", checked);
},
},
{
"label": translate("Enable culling"),
"onPress": checked => {
Engine.GameView_SetCullingEnabled(checked);
},
"checked": () => Engine.GameView_GetCullingEnabled(),
},
{
"label": translate("Lock cull camera"),
"onPress": checked => {
Engine.GameView_SetLockCullCameraEnabled(checked);
},
"checked": () => Engine.GameView_GetLockCullCameraEnabled(),
},
{
"label": translate("Display camera frustum"),
"onPress": checked => {
Engine.Renderer_SetDisplayFrustumEnabled(checked);
},
"checked": () => Engine.Renderer_GetDisplayFrustumEnabled(),
},
];
};
DeveloperOverlay.prototype.layout = function()
{
for (let body of Engine.GetGUIObjectByName("dev_commands").children)
body.hidden = true;
let overlayHeight = 0;
let commands = this.getCommands();
for (let i = 0; i < commands.length; ++i)
{
let command = commands[i];
let body = Engine.GetGUIObjectByName("dev_command[" + i + "]");
let bodySize = body.size;
bodySize.top = i * this.commandHeight;
bodySize.bottom = bodySize.top + this.commandHeight;
body.size = bodySize;
body.hidden = false;
let label = Engine.GetGUIObjectByName("dev_command_label[" + i + "]");
label.caption = command.label;
let checkbox = Engine.GetGUIObjectByName("dev_command_checkbox[" + i + "]");
checkbox.onPress = function() {
command.onPress(checkbox.checked);
if (command.checked)
checkbox.checked = command.checked();
};
overlayHeight += this.commandHeight;
}
let overlay = Engine.GetGUIObjectByName("devCommandsOverlay");
let overlaySize = overlay.size;
overlaySize.bottom = overlaySize.top + overlayHeight;
overlay.size = overlaySize;
this.updateValues();
};
DeveloperOverlay.prototype.updateValues = function()
{
this.getCommands().forEach((command, i) => {
let checkbox = Engine.GetGUIObjectByName("dev_command_checkbox[" + i + "]");
if (command.checked)
checkbox.checked = command.checked();
});
};
DeveloperOverlay.prototype.toggle = function()
{
if (!g_GameAttributes.settings.CheatsEnabled && !g_IsReplay)
return;
let overlay = Engine.GetGUIObjectByName("devCommandsOverlay");
overlay.hidden = !overlay.hidden;
let message = overlay.hidden ?
markForTranslation("The Developer Overlay was closed.") :
markForTranslation("The Developer Overlay was opened.");
// Only players can send the simulation chat command
if (Engine.GetPlayerID() == -1)
g_Chat.submitChat(message);
else
Engine.PostNetworkCommand({
"type": "aichat",
"message": message,
"translateMessage": true,
"translateParameters": [],
"parameters": {}
});
};
DeveloperOverlay.prototype.update = function()
{
let playerState = g_SimState.players[g_ViewedPlayer];
this.controlAll = playerState ? playerState.controlsAll : false;
this.updateValues();
this.updateEntityState();
}
DeveloperOverlay.prototype.updateEntityState = function()
{
let debug = Engine.GetGUIObjectByName("debugEntityState");
if (!this.displayState)
{
debug.hidden = true;
return;
}
debug.hidden = false;
let conciseSimState = clone(GetSimState());
conciseSimState.players = "<<<omitted>>>";
let text = "simulation: " + uneval(conciseSimState);
let selection = g_Selection.toList();
if (selection.length)
{
let entState = GetEntityState(selection[0]);
if (entState)
{
let template = GetTemplateData(entState.template);
text += "\n\nentity: {\n";
for (let k in entState)
text += " " + k + ":" + uneval(entState[k]) + "\n";
text += "}\n\ntemplate: " + uneval(template);
}
}
debug.caption = text.replace(/\[/g, "\\[");
};
DeveloperOverlay.prototype.isTimeWarpEnabled = function() {
return this.timeWarp;
};
DeveloperOverlay.prototype.isControlAll = function() {
return this.controlAll;
};

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<object
name="devCommandsOverlay"
type="image"
sprite="sessionOverlayBackground"
size="100%-250 50%-200 100%-8 50%"
z="40"
hidden="true"
hotkey="session.devcommands.toggle"
>
<object name="dev_commands">
<repeat count="16">
<object name="dev_command[n]" size="0 0 100% 0">
<object name="dev_command_label[n]" size="0 0 100%-26 100%" type="text" style="devCommandsText"/>
<object name="dev_command_checkbox[n]" size="100%-24 1 100% 100%" type="checkbox" style="ModernTickBox"/>
</object>
</repeat>
</object>
</object>

View File

@ -0,0 +1,68 @@
/**
* This class stores the checkboxes that are part of the developer overlay.
* These checkboxes may own their own helper class instances, such as the EntityState or TimeWarp feature.
*/
class DeveloperOverlay
{
constructor(playerViewControl, selection)
{
this.devCommandsOverlay = Engine.GetGUIObjectByName("devCommandsOverlay");
this.devCommandsOverlay.onPress = this.toggle.bind(this);
this.checkBoxes = this.getCheckboxNames().map((name, i) =>
new DeveloperOverlayCheckbox(
new DeveloperOverlayCheckboxes.prototype[name](playerViewControl, selection),
i));
this.resize();
}
/**
* Mods may overwrite this to change the order.
*/
getCheckboxNames()
{
return Object.keys(DeveloperOverlayCheckboxes.prototype);
}
toggle()
{
if (g_IsNetworked && !g_GameAttributes.settings.CheatsEnabled)
return;
this.devCommandsOverlay.hidden = !this.devCommandsOverlay.hidden;
this.sendNotification();
this.checkBoxes.forEach(checkbox => {
checkbox.setHidden(this.devCommandsOverlay.hidden);
});
}
sendNotification()
{
let message = this.devCommandsOverlay.hidden ? this.CloseNotification : this.OpenNotification;
// Only players can send the simulation chat command
if (Engine.GetPlayerID() == -1)
g_Chat.submitChat(message);
else
Engine.PostNetworkCommand({
"type": "aichat",
"message": message,
"translateMessage": true,
"translateParameters": [],
"parameters": {}
});
}
resize()
{
let size = this.devCommandsOverlay.size;
size.bottom =
size.top +
this.checkBoxes.reduce((height, checkbox) => height + checkbox.getHeight(), 0);
this.devCommandsOverlay.size = size;
}
}
DeveloperOverlay.prototype.OpenNotification = markForTranslation("The Developer Overlay was opened.");
DeveloperOverlay.prototype.CloseNotification = markForTranslation("The Developer Overlay was closed.");

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<object
name="devCommandsOverlay"
type="image"
sprite="sessionOverlayBackground"
size="100%-250 50%-200 100%-8 50%"
z="40"
hidden="true"
hotkey="session.devcommands.toggle"
>
<repeat count="20">
<object name="dev_command[n]" size="0 0 100% 20" hidden="true">
<object name="dev_command_label[n]" size="0 0 100%-26 100%" type="text" style="devCommandsText"/>
<object name="dev_command_checkbox[n]" size="100%-24 1 100% 100%" type="checkbox" style="ModernTickBox"/>
</object>
</repeat>
</object>

View File

@ -0,0 +1,63 @@
/**
* This class sets up a checkbox in the developer overlay and assigns its specific handler.
*/
class DeveloperOverlayCheckbox
{
constructor(handler, i)
{
this.handler = handler;
this.label = Engine.GetGUIObjectByName("dev_command_label[" + i + "]");
this.label.caption = this.handler.label();
this.checkbox = Engine.GetGUIObjectByName("dev_command_checkbox[" + i + "]");
this.checkbox.onPress = this.onPress.bind(this);
this.body = Engine.GetGUIObjectByName("dev_command[" + i + "]");
this.resize(i);
this.updater = this.update.bind(this);
if (this.handler.checked)
registerPlayersInitHandler(this.updater);
}
onPress()
{
this.handler.onPress(this.checkbox.checked);
if (this.handler.checked)
this.update();
}
update()
{
this.checkbox.checked = this.handler.checked();
}
setHidden(hidden)
{
if (!this.handler.checked)
return;
if (hidden)
unregisterSimulationUpdateHandler(this.updater);
else
registerSimulationUpdateHandler(this.updater);
}
getHeight()
{
return this.body.size.bottom - this.body.size.top;
}
resize(i)
{
let size = this.body.size;
let height = size.bottom;
size.top = height * i;
size.bottom = height * (i + 1);
this.body.size = size;
this.body.hidden = false;
}
}

View File

@ -0,0 +1,277 @@
/**
* This class stores the handlers for the individual checkboxes available in the developer overlay.
* Such a class must have label and onPress function.
* If the class has a checked property, then that will be called every simulation update to
* synchronize the state of the checkbox (only if the developer overaly is opened).
*/
class DeveloperOverlayCheckboxes
{
}
DeveloperOverlayCheckboxes.prototype.ControlAll = class
{
label()
{
return translate("Control all units");
}
onPress(checked)
{
Engine.PostNetworkCommand({
"type": "control-all",
"flag": checked
});
}
checked()
{
let playerState = g_SimState.players[g_ViewedPlayer];
return playerState ? playerState.controlsAll : false;
}
};
DeveloperOverlayCheckboxes.prototype.ChangePerspective = class
{
constructor(playerViewControl)
{
this.playerViewControl = playerViewControl;
}
label()
{
return translate("Change perspective");
}
onPress(checked)
{
this.playerViewControl.setChangePerspective(checked);
}
};
DeveloperOverlayCheckboxes.prototype.SelectionEntityState = class
{
constructor(playerViewControl, selection)
{
this.developerOverlayEntityState = new DeveloperOverlayEntityState(selection);
}
label()
{
return translate("Display selection state");
}
onPress(checked)
{
this.developerOverlayEntityState.setEnabled(checked);
}
};
DeveloperOverlayCheckboxes.prototype.PathfinderOverlay = class
{
label()
{
return translate("Pathfinder overlay");
}
onPress(checked)
{
Engine.GuiInterfaceCall("SetPathfinderDebugOverlay", checked);
}
};
DeveloperOverlayCheckboxes.prototype.HierarchicalPathfinderOverlay = class
{
label()
{
return translate("Hierarchical pathfinder overlay");
}
onPress(checked)
{
Engine.GuiInterfaceCall("SetPathfinderHierDebugOverlay", checked);
}
};
DeveloperOverlayCheckboxes.prototype.ObstructionOverlay = class
{
label()
{
return translate("Obstruction overlay");
}
onPress(checked)
{
Engine.GuiInterfaceCall("SetObstructionDebugOverlay", checked);
}
};
DeveloperOverlayCheckboxes.prototype.UnitMotionOverlay = class
{
label()
{
return translate("Unit motion overlay");
}
onPress(checked)
{
g_Selection.SetMotionDebugOverlay(checked);
}
};
DeveloperOverlayCheckboxes.prototype.RangeOverlay = class
{
label()
{
return translate("Range overlay");
}
onPress(checked)
{
Engine.GuiInterfaceCall("SetRangeDebugOverlay", checked);
}
};
DeveloperOverlayCheckboxes.prototype.BoundingBoxOverlay = class
{
label()
{
return translate("Bounding box overlay");
}
onPress(checked)
{
Engine.SetBoundingBoxDebugOverlay(checked);
}
};
DeveloperOverlayCheckboxes.prototype.RestrictCamera = class
{
label()
{
return translate("Restrict camera");
}
onPress(checked)
{
Engine.GameView_SetConstrainCameraEnabled(checked);
}
checked()
{
return Engine.GameView_GetConstrainCameraEnabled();
}
};
DeveloperOverlayCheckboxes.prototype.RevealMap = class
{
label()
{
return translate("Reveal map");
}
onPress(checked)
{
Engine.PostNetworkCommand({
"type": "reveal-map",
"enable": checked
});
}
checked()
{
return Engine.GuiInterfaceCall("IsMapRevealed");
}
};
DeveloperOverlayCheckboxes.prototype.EnableTimeWarp = class
{
constructor()
{
this.timeWarp = new TimeWarp();
}
label()
{
return translate("Enable time warp");
}
onPress(checked)
{
this.timeWarp.setEnabled(checked);
}
};
DeveloperOverlayCheckboxes.prototype.PromoteSelectedUnits = class
{
label()
{
return translate("Promote selected units");
}
onPress(checked)
{
Engine.PostNetworkCommand({
"type": "promote",
"entities": g_Selection.toList()
});
}
checked()
{
return false;
}
};
DeveloperOverlayCheckboxes.prototype.EnableCulling = class
{
label()
{
return translate("Enable culling");
}
onPress(checked)
{
Engine.GameView_SetCullingEnabled(checked);
}
checked()
{
return Engine.GameView_GetCullingEnabled();
}
};
DeveloperOverlayCheckboxes.prototype.LockCullCamera = class
{
label()
{
return translate("Lock cull camera");
}
onPress(checked)
{
Engine.GameView_SetLockCullCameraEnabled(checked);
}
checked()
{
return Engine.GameView_GetLockCullCameraEnabled();
}
};
DeveloperOverlayCheckboxes.prototype.DisplayCameraFrustum = class
{
label()
{
return translate("Display camera frustum");
}
onPress(checked)
{
Engine.Renderer_SetDisplayFrustumEnabled(checked);
}
checked()
{
return Engine.Renderer_GetDisplayFrustumEnabled();
}
};

View File

@ -0,0 +1,51 @@
/**
* This class manages the developer overlay which displays the state of the first selected entity.
*/
class DeveloperOverlayEntityState
{
constructor(selection)
{
this.developerOverlayEntityState = Engine.GetGUIObjectByName("developerOverlayEntityState");
this.selection = selection;
this.updater = this.update.bind(this);
}
setEnabled(enabled)
{
this.developerOverlayEntityState.hidden = !enabled;
if (enabled)
{
registerSimulationUpdateHandler(this.updater);
registerEntitySelectionChangeHandler(this.updater);
}
else
{
unregisterSimulationUpdateHandler(this.updater);
unregisterEntitySelectionChangeHandler(this.updater);
}
}
update()
{
let simState = clone(g_SimState);
simState.players = "<<<omitted>>>";
let text = "simulation: " + uneval(simState);
let selection = this.selection.toList();
if (selection.length)
{
let entState = GetEntityState(selection[0]);
if (entState)
{
let template = GetTemplateData(entState.template);
text += "\n\nentity: {\n";
for (let k in entState)
text += " " + k + ":" + uneval(entState[k]) + "\n";
text += "}\n\ntemplate: " + uneval(template);
}
}
this.developerOverlayEntityState.caption = text.replace(/\[/g, "\\[");
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<object name="developerOverlayEntityState"
type="text"
size="0 70 80% 100%"
ghost="true"
textcolor="yellow"
font="mono-stroke-10"
hidden="true"
/>

View File

@ -0,0 +1,66 @@
/**
* This class manages the timewarp hotkeys that allow jumping back in time and fast forwarding the game.
*/
class TimeWarp
{
constructor()
{
this.enabled = false;
this.timewarpRewind = Engine.GetGUIObjectByName("timewarpRewind");
this.timewarpRewind.onPress = this.rewind.bind(this);
this.timewarpFastForward = Engine.GetGUIObjectByName("timewarpFastForward");
this.timewarpFastForward.onPress = this.fastForward.bind(this);
this.timewarpFastForward.onRelease = this.resetSpeed.bind(this);
}
setEnabled(enabled)
{
if (g_IsNetworked)
return;
this.enabled = enabled;
if (enabled)
messageBox(
500, 250,
translate(this.Description),
translate(this.Title));
Engine.EnableTimeWarpRecording(enabled ? this.NumberTurns : 0);
}
rewind()
{
if (this.enabled)
Engine.RewindTimeWarp();
}
fastForward()
{
if (this.enabled)
Engine.SetSimRate(this.FastForwardSpeed);
}
resetSpeed()
{
if (this.enabled)
Engine.SetSimRate(1);
}
}
/**
* Number of turns between snapshots.
*/
TimeWarp.prototype.NumberTurns = 10;
/**
* Gamespeed used while pressing the fast forward hotkey.
*/
TimeWarp.prototype.FastForwardSpeed = 20;
TimeWarp.prototype.Title = markForTranslation("Time warp mode");
TimeWarp.prototype.Description = markForTranslation(
"Note: time warp mode is a developer option, and not intended for use over long periods of time. Using it incorrectly may cause the game to run out of memory or crash.");

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<object>
<object name="timewarpRewind" hotkey="session.timewarp.rewind"/>
<object name="timewarpFastForward" hotkey="session.timewarp.fastforward"/>
</object>

View File

@ -223,12 +223,7 @@ function determineAction(x, y, fromMinimap)
return undefined;
// If the selection isn't friendly units, no action
var allOwnedByPlayer = selection.every(ent => {
var entState = GetEntityState(ent);
return entState && entState.player == g_ViewedPlayer;
});
if (!g_DeveloperOverlay.isControlAll() && !allOwnedByPlayer)
if (!selection.every(canControlEntity))
return undefined;
var target = undefined;
@ -278,6 +273,19 @@ function determineAction(x, y, fromMinimap)
return { "type": "none", "cursor": "", "target": target };
}
function canControlEntity(ent)
{
let entState = GetEntityState(ent);
if (!entState)
return false;
if (entState.player == g_ViewedPlayer)
return true;
let playerState = g_SimState.players[entState.player];
return playerState && playerState.controlsAll;
}
function tryPlaceBuilding(queued)
{
if (placementSupport.mode !== "building")
@ -786,17 +794,6 @@ function handleInputAfterGui(ev)
if (ev.hotkey === undefined)
ev.hotkey = null;
// Handle the time-warp testing features, restricted to single-player
if (!g_IsNetworked && g_DeveloperOverlay.isTimeWarpEnabled())
{
if (ev.type == "hotkeydown" && ev.hotkey == "session.timewarp.fastforward")
Engine.SetSimRate(20.0);
else if (ev.type == "hotkeyup" && ev.hotkey == "session.timewarp.fastforward")
Engine.SetSimRate(1.0);
else if (ev.type == "hotkeyup" && ev.hotkey == "session.timewarp.rewind")
Engine.RewindTimeWarp();
}
if (ev.hotkey == "session.highlightguarding")
{
g_ShowGuarding = (ev.type == "hotkeydown");

View File

@ -214,8 +214,10 @@ EntitySelection.prototype.update = function()
{
this.checkRenamedEntities();
let controlsAll = g_SimState.players[g_ViewedPlayer] && g_SimState.players[g_ViewedPlayer].controlsAll;
let removeOwnerChanges = !g_IsObserver && !controlsAll && this.toList().length > 1;
let changed = false;
let removeOwnerChanges = !g_IsObserver && !g_DeveloperOverlay.isControlAll() && this.toList().length > 1;
for (let ent in this.selected)
{
@ -377,6 +379,9 @@ EntitySelection.prototype.getFirstSelected = function()
return undefined;
};
/**
* TODO: This array should not be recreated every call
*/
EntitySelection.prototype.toList = function()
{
let ents = [];

View File

@ -11,6 +11,7 @@ const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDu
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
var g_Chat;
var g_DeveloperOverlay;
var g_DiplomacyColors;
var g_DiplomacyDialog;
var g_GameSpeedControl;
@ -107,8 +108,6 @@ var g_ReplaySelectionData;
*/
var g_PlayerAssignments;
var g_DeveloperOverlay;
/**
* Whether the entire UI should be hidden (useful for promotional screenshots).
* Can be toggled with a hotkey.
@ -134,22 +133,22 @@ var g_ResourceData = new Resources();
* These handlers are called each time a new turn was simulated.
* Use this as sparely as possible.
*/
var g_SimulationUpdateHandlers = [];
var g_SimulationUpdateHandlers = new Set();
/**
* These handlers are called after the player states have been initialized.
*/
var g_PlayersInitHandlers = [];
var g_PlayersInitHandlers = new Set();
/**
* These handlers are called when a player has been defeated or won the game.
*/
var g_PlayerFinishedHandlers = [];
var g_PlayerFinishedHandlers = new Set();
/**
* These events are fired whenever the player added or removed entities from the selection.
*/
var g_EntitySelectionChangeHandlers = [];
var g_EntitySelectionChangeHandlers = new Set();
/**
* These events are fired when the user has performed a hotkey assignment change.
@ -281,7 +280,7 @@ function init(initData, hotloadData)
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates);
g_Chat = new Chat(g_PlayerViewControl);
g_DeveloperOverlay = new DeveloperOverlay(g_PlayerViewControl);
g_DeveloperOverlay = new DeveloperOverlay(g_PlayerViewControl, g_Selection);
g_DiplomacyDialog = new DiplomacyDialog(g_PlayerViewControl, g_DiplomacyColors);
g_GameSpeedControl = new GameSpeedControl(g_PlayerViewControl);
g_MiniMapPanel = new MiniMapPanel(g_PlayerViewControl, g_DiplomacyColors, g_WorkerTypes);
@ -323,22 +322,32 @@ function init(initData, hotloadData)
function registerPlayersInitHandler(handler)
{
g_PlayersInitHandlers.push(handler);
g_PlayersInitHandlers.add(handler);
}
function registerPlayersFinishedHandler(handler)
{
g_PlayerFinishedHandlers.push(handler);
g_PlayerFinishedHandlers.add(handler);
}
function registerSimulationUpdateHandler(handler)
{
g_SimulationUpdateHandlers.push(handler);
g_SimulationUpdateHandlers.add(handler);
}
function unregisterSimulationUpdateHandler(handler)
{
g_SimulationUpdateHandlers.delete(handler);
}
function registerEntitySelectionChangeHandler(handler)
{
g_EntitySelectionChangeHandlers.push(handler);
g_EntitySelectionChangeHandlers.add(handler);
}
function unregisterEntitySelectionChangeHandler(handler)
{
g_EntitySelectionChangeHandlers.delete(handler);
}
function registerHotkeyChangeHandler(handler)

View File

@ -5,6 +5,7 @@
<script directory="gui/common/"/>
<script directory="gui/session/"/>
<script directory="gui/session/chat/"/>
<script directory="gui/session/developer_overlay/"/>
<script directory="gui/session/diplomacy/"/>
<script directory="gui/session/diplomacy/playercontrols/"/>
<script directory="gui/session/minimap/"/>
@ -78,20 +79,11 @@
<object name="chatText" size="3 1 100%-1 100%-1" type="text" style="chatPanelOverlay" ghost="true"/>
</object>
<!-- Entity selection state text -->
<object name="debugEntityState"
type="text"
size="0 70 80% 100%"
ghost="true"
textcolor="yellow"
font="mono-stroke-10"
/>
<include directory="gui/session/chat/"/>
<include directory="gui/session/developer_overlay/"/>
<include directory="gui/session/dialogs/"/>
<include directory="gui/session/diplomacy/"/>
<include directory="gui/session/objectives/"/>
<include file="gui/session/developer_overlay.xml"/>
<include file="gui/session/GameSpeedControl.xml"/>
<include file="gui/session/TopPanel.xml"/>
<include file="gui/session/trade/TradeDialog.xml"/>

View File

@ -1465,7 +1465,8 @@ function someCanPatrol(entities)
*/
function isUndeletable(entState)
{
if (g_DeveloperOverlay.isControlAll())
let playerState = g_SimState.players[entState.player];
if (playerState && playerState.controlsAll)
return false;
if (entState.resourceSupply && entState.resourceSupply.killBeforeGather)