Index: binaries/data/mods/mod/autostart/entrypoint.js
===================================================================
--- /dev/null
+++ binaries/data/mods/mod/autostart/entrypoint.js
@@ -0,0 +1,23 @@
+/**
+ * This file is called from the visual & non-visual paths when autostarting.
+ * To avoid relying on the GUI, this script has access to a special 'LoadScript' function.
+ * See implementation in the public mod for more details.
+ */
+
+function autostartClient(initData)
+{
+ error("Autostart is not implemented in the 'mod' mod");
+}
+
+function autostartHost(initData, networked = false)
+{
+ error("Autostart is not implemented in the 'mod' mod");
+}
+
+/**
+ * @returns false if the loop should carry on.
+ */
+function onTick()
+{
+ return true;
+}
Index: binaries/data/mods/public/autostart/autostart.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/autostart/autostart.js
@@ -0,0 +1,29 @@
+class AutoStart
+{
+ constructor(initData)
+ {
+ this.settings = new GameSettings().init();
+ this.settings.fromInitAttributes(initData.attribs);
+
+ this.playerAssignments = initData.playerAssignments;
+
+ this.settings.launchGame(this.playerAssignments, initData.storeReplay);
+
+ this.onLaunch();
+ }
+
+ onTick()
+ {
+ }
+
+ /**
+ * In the visual autostart path, we need to show the loading screen.
+ */
+ onLaunch()
+ {
+ Engine.SwitchGuiPage("page_loading.xml", {
+ "attribs": this.settings.finalizedAttributes,
+ "playerAssignments": this.playerAssignments
+ });
+ }
+}
Index: binaries/data/mods/public/autostart/autostart_client.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/autostart/autostart_client.js
@@ -0,0 +1,54 @@
+class AutoStartClient
+{
+ constructor(initData)
+ {
+ this.playerAssignments = {};
+
+ try
+ {
+ Engine.StartNetworkJoin(initData.playerName, initData.ip, initData.port, initData.storeReplay);
+ }
+ catch (e)
+ {
+ const message = sprintf(translate("Cannot join game: %(message)s."), { "message": e.message });
+ messageBox(400, 200, message, translate("Error"));
+ }
+ }
+
+ onTick()
+ {
+ while (true)
+ {
+ const message = Engine.PollNetworkClient();
+ if (!message)
+ break;
+
+ switch (message.type)
+ {
+ case "players":
+ this.playerAssignments = message.newAssignments;
+ Engine.SendNetworkReady(2);
+ break;
+ case "start":
+ this.onLaunch(message);
+ // Process further pending netmessages in the session page.
+ return true;
+ default:
+ }
+ }
+ return false;
+ }
+
+ /**
+ * In the visual autostart path, we need to show the loading screen.
+ * Overload this as appropriate, the default implementation works for the public mod.
+ */
+ onLaunch(message)
+ {
+ Engine.SwitchGuiPage("page_loading.xml", {
+ "attribs": message.initAttributes,
+ "isRejoining": true,
+ "playerAssignments": this.playerAssignments
+ });
+ }
+}
Index: binaries/data/mods/public/autostart/autostart_host.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/autostart/autostart_host.js
@@ -0,0 +1,85 @@
+class AutoStartHost
+{
+ constructor(initData)
+ {
+ this.launched = false;
+ this.maxPlayers = initData.maxPlayers;
+ this.storeReplay = initData.storeReplay;
+ this.playerAssignments = {};
+
+ this.initAttribs = initData.attribs;
+
+ try
+ {
+ // Stun and password not implemented for autostart.
+ Engine.StartNetworkHost(initData.playerName, initData.port, false, "", initData.storeReplay);
+ }
+ catch (e)
+ {
+ const message = sprintf(translate("Cannot host game: %(message)s."), { "message": e.message });
+ messageBox(400, 200, message, translate("Error"));
+ }
+ }
+
+ /**
+ * Handles a simple implementation of player assignments.
+ * Should not need be overloaded in mods unless you want to change that logic.
+ */
+ onTick()
+ {
+ while (true)
+ {
+ const message = Engine.PollNetworkClient();
+ if (!message)
+ break;
+
+ switch (message.type)
+ {
+ case "players":
+ this.playerAssignments = message.newAssignments;
+ Engine.SendNetworkReady(2);
+ let max = 0;
+ for (const uid in this.playerAssignments)
+ {
+ max = Math.max(this.playerAssignments[uid].player, max);
+ if (this.playerAssignments[uid].player == -1)
+ Engine.AssignNetworkPlayer(++max, uid);
+ }
+ break;
+ case "ready":
+ this.playerAssignments[message.guid].status = message.status;
+ break;
+ case "start":
+ return true;
+ default:
+ }
+ }
+
+ if (!this.launched && Object.keys(this.playerAssignments).length == this.maxPlayers)
+ {
+ for (const uid in this.playerAssignments)
+ if (this.playerAssignments[uid].player == -1 || this.playerAssignments[uid].status == 0)
+ return false;
+ this.onLaunch();
+ }
+ return false;
+ }
+
+ /**
+ * In the visual autostart path, we need to show the loading screen.
+ * Overload this as appropriate.
+ */
+ onLaunch()
+ {
+ this.launched = true;
+
+ this.settings = new GameSettings().init();
+ this.settings.fromInitAttributes(this.initAttribs);
+ this.settings.playerCount.setNb(Object.keys(this.playerAssignments).length);
+ this.settings.launchGame(this.playerAssignments, this.storeReplay);
+ Engine.SwitchGuiPage("page_loading.xml", {
+ "attribs": this.initAttribs,
+ "playerAssignments": this.playerAssignments
+ });
+ }
+}
Index: binaries/data/mods/public/autostart/entrypoint.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/autostart/entrypoint.js
@@ -0,0 +1,60 @@
+/**
+ * Implement autostart for the public mod.
+ * We want to avoid relying on too many specific files, so we'll mock a few engine functions.
+ * Depending on the path, these may get overwritten with the real function.
+ */
+
+Engine.HasNetClient = () => false;
+Engine.HasXmppClient = () => false;
+Engine.GetMatchID = () => 0;
+Engine.SetRankedGame = () => {};
+Engine.TextureExists = () => false;
+Engine.PushGuiPage = () => {};
+Engine.SwitchGuiPage = () => {};
+
+var translateObjectKeys = () => {}
+var translate = x => x;
+var translateWithContext = x => x;
+
+// MsgBox is used in the failure path.
+// TODO: clean this up and show errors better in the non-visual path.
+Engine.LoadScript("gui/common/functions_msgbox.js");
+
+var autostartInstance;
+
+function autostartClient(initData)
+{
+ autostartInstance = new AutoStartClient(initData);
+}
+
+/**
+ * This path depends on files currently stored under gui/, which should be moved.
+ * The best place would probably be a new 'engine' mod, independent from the 'mod' mod and the public mod.
+ */
+function autostartHost(initData, networked = false)
+{
+ Engine.LoadScript("globalscripts/");
+
+ Engine.LoadScript("gui/common/color.js");
+ Engine.LoadScript("gui/common/functions_utility.js");
+ Engine.LoadScript("gui/common/Observable.js");
+ Engine.LoadScript("gui/common/settings.js");
+
+ Engine.LoadScript("gui/maps/MapCache.js")
+
+ Engine.LoadScript("gamesettings/");
+ Engine.LoadScript("gamesettings/attributes/");
+
+ if (networked)
+ autostartInstance = new AutoStartHost(initData);
+ else
+ autostartInstance = new AutoStart(initData);
+}
+
+/**
+ * @returns false if the loop should carry on.
+ */
+function onTick()
+{
+ return autostartInstance.onTick();
+}
Index: binaries/data/mods/public/gamesettings/GameSettings.js
===================================================================
--- binaries/data/mods/public/gamesettings/GameSettings.js
+++ binaries/data/mods/public/gamesettings/GameSettings.js
@@ -11,7 +11,7 @@
*/
class GameSettings
{
- init(mapCache)
+ init(mapCache, civData)
{
if (!mapCache)
mapCache = new MapCache();
@@ -19,9 +19,11 @@
"value": mapCache,
});
- // Load all possible civ data - don't presume that some will be available.
+ if (!civData)
+ // Load all possible civ data - don't presume that some will be available.
+ civData = loadCivData(false, false);
Object.defineProperty(this, "civData", {
- "value": loadCivData(false, false),
+ "value": civData
});
Object.defineProperty(this, "isNetworked", {
Index: binaries/data/mods/public/globalscripts/Civilisations.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/globalscripts/Civilisations.js
@@ -0,0 +1,49 @@
+/**
+ * Loads history and gameplay data of all civs.
+ *
+ * @param selectableOnly {boolean} - Only load civs that can be selected
+ * in the gamesetup. Scenario maps might set non-selectable civs.
+ */
+function loadCivFiles(selectableOnly)
+{
+ let propertyNames = [
+ "Code", "Culture", "Music", "CivBonuses", "StartEntities",
+ "AINames", "SkirmishReplacements", "SelectableInGameSetup"];
+
+ let civData = {};
+
+ for (let filename of Engine.ListDirectoryFiles("simulation/data/civs/", "*.json", false))
+ {
+ let data = Engine.ReadJSONFile(filename);
+
+ for (let prop of propertyNames)
+ if (data[prop] === undefined)
+ throw new Error(filename + " doesn't contain " + prop);
+
+ if (selectableOnly && !data.SelectableInGameSetup)
+ continue;
+
+ const template = Engine.GetTemplate("special/players/" + data.Code);
+ data.Name = template.Identity.GenericName;
+ data.Emblem = "session/portraits/" + template.Identity.Icon;
+ data.History = template.Identity.History;
+
+ civData[data.Code] = data;
+ }
+
+ return civData;
+}
+
+function getCivCodes(selectableOnly)
+{
+ let codes = [];
+
+ for (let filename of Engine.ListDirectoryFiles("simulation/data/civs/", "*.json", false))
+ {
+ let data = Engine.ReadJSONFile(filename);
+
+ if (selectableOnly && !data.SelectableInGameSetup)
+ continue;
+
+ }
+}
Index: binaries/data/mods/public/gui/autostart/autostart.js
===================================================================
--- binaries/data/mods/public/gui/autostart/autostart.js
+++ binaries/data/mods/public/gui/autostart/autostart.js
@@ -1,12 +1,4 @@
function init(initData)
{
- let settings = new GameSettings().init();
- settings.fromInitAttributes(initData.attribs);
-
- settings.launchGame(initData.playerAssignments, initData.storeReplay);
-
- Engine.SwitchGuiPage("page_loading.xml", {
- "attribs": settings.finalizedAttributes,
- "playerAssignments": initData.playerAssignments
- });
+ return new AutoStart(initData);
}
Index: binaries/data/mods/public/gui/autostart/autostart.xml
===================================================================
--- binaries/data/mods/public/gui/autostart/autostart.xml
+++ binaries/data/mods/public/gui/autostart/autostart.xml
@@ -2,7 +2,8 @@
-
-
+
+
+
Index: binaries/data/mods/public/gui/autostart/autostart_client.js
===================================================================
--- binaries/data/mods/public/gui/autostart/autostart_client.js
+++ /dev/null
@@ -1,54 +0,0 @@
-class AutoStartClient
-{
- constructor(initData)
- {
- Engine.GetGUIObjectByName("ticker").onTick = this.onTick.bind(this);
-
- this.playerAssignments = {};
-
- try
- {
- Engine.StartNetworkJoin(initData.playerName, initData.ip, initData.port, initData.storeReplay);
- }
- catch (e)
- {
- messageBox(
- 400, 200,
- sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }),
- translate("Error")
- );
- }
- }
-
- onTick()
- {
- while (true)
- {
- const message = Engine.PollNetworkClient();
- if (!message)
- break;
-
- switch (message.type)
- {
- case "players":
- this.playerAssignments = message.newAssignments;
- break;
- case "start":
- Engine.SwitchGuiPage("page_loading.xml", {
- "attribs": message.initAttributes,
- "isRejoining": true,
- "playerAssignments": this.playerAssignments
- });
-
- // Process further pending netmessages in the session page.
- return;
- default:
- }
- }
- }
-}
-
-function init(initData)
-{
- new AutoStartClient(initData);
-}
Index: binaries/data/mods/public/gui/autostart/autostart_client.xml
===================================================================
--- binaries/data/mods/public/gui/autostart/autostart_client.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
Index: binaries/data/mods/public/gui/autostart/autostart_host.js
===================================================================
--- binaries/data/mods/public/gui/autostart/autostart_host.js
+++ /dev/null
@@ -1,61 +0,0 @@
-class AutoStartHost
-{
- constructor(initData)
- {
- this.maxPlayers = initData.maxPlayers;
- this.storeReplay = initData.storeReplay;
- this.playerAssignments = {};
-
- Engine.GetGUIObjectByName("ticker").onTick = this.onTick.bind(this);
-
- try
- {
- // Stun and password not implemented for autostart.
- Engine.StartNetworkHost(initData.playerName, initData.port, false, "", initData.storeReplay);
- }
- catch (e)
- {
- messageBox(
- 400, 200,
- sprintf(translate("Cannot host game: %(message)s."), { "message": e.message }),
- translate("Error")
- );
- }
-
- this.settings = new GameSettings().init();
- this.settings.fromInitAttributes(initData.attribs);
- }
-
- onTick()
- {
- while (true)
- {
- const message = Engine.PollNetworkClient();
- if (!message)
- break;
-
- switch (message.type)
- {
- case "players":
- this.playerAssignments = message.newAssignments;
- break;
- default:
- }
- }
-
- if (Object.keys(this.playerAssignments).length == this.maxPlayers)
- {
- this.settings.launchGame(this.playerAssignments, this.storeReplay);
-
- Engine.SwitchGuiPage("page_loading.xml", {
- "attribs": this.settings.finalizedAttributes,
- "playerAssignments": this.playerAssignments
- });
- }
- }
-}
-
-function init(initData)
-{
- new AutoStartHost(initData);
-}
Index: binaries/data/mods/public/gui/autostart/autostart_host.xml
===================================================================
--- binaries/data/mods/public/gui/autostart/autostart_host.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
Index: binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml
===================================================================
--- binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml
+++ binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml
@@ -3,8 +3,8 @@
-
-
+
+