Index: binaries/data/mods/public/gui/common/gamedescription.js
===================================================================
--- binaries/data/mods/public/gui/common/gamedescription.js
+++ binaries/data/mods/public/gui/common/gamedescription.js
@@ -199,6 +199,17 @@
),
{ "min": g_GameAttributes.settings.WonderDuration }
);
+ else if (g_VictoryConditions.Name[victoryIdx] == "capture_the_flag")
+ title = sprintf(
+ translatePluralWithContext(
+ "victory condition",
+ "Capture The Flag (%(min)s minute)",
+ "Capture The Flag (%(min)s minutes)",
+ g_GameAttributes.settings.CaptureTheFlagDuration
+ ),
+ { "min": g_GameAttributes.settings.CaptureTheFlagDuration }
+ );
+
titles.push({
"label": title,
"value": g_VictoryConditions.Description[victoryIdx]
Index: binaries/data/mods/public/gui/common/settings.js
===================================================================
--- binaries/data/mods/public/gui/common/settings.js
+++ binaries/data/mods/public/gui/common/settings.js
@@ -32,7 +32,8 @@
"AIDescriptions": loadAIDescriptions(),
"AIDifficulties": loadAIDifficulties(),
"Ceasefire": loadCeasefire(),
- "WonderDurations": loadWonderDuration(),
+ "WonderDurations": loadVictoryDuration("wonder"),
+ "CaptureTheFlagDurations": loadVictoryDuration("capture_the_flag"),
"GameSpeeds": loadSettingValuesFile("game_speeds.json"),
"MapTypes": loadMapTypes(),
"MapSizes": loadSettingValuesFile("map_sizes.json"),
@@ -132,11 +133,11 @@
}
/**
- * Loads available wonder-victory times
+ * Loads available victory times for victory conditions like wonder and capture the flag.
*/
-function loadWonderDuration()
+function loadVictoryDuration(gameType)
{
- var jsonFile = "wonder_times.json";
+ var jsonFile = "victory_times.json";
var json = Engine.ReadJSONFile(g_SettingsDirectory + jsonFile);
if (!json || json.Default === undefined || !json.Times || !Array.isArray(json.Times))
@@ -148,7 +149,8 @@
return json.Times.map(duration => ({
"Duration": duration,
"Default": duration == json.Default,
- "Title": sprintf(translatePluralWithContext("wonder victory", "%(min)s minute", "%(min)s minutes", duration), { "min": duration })
+ "Title": sprintf(translatePluralWithContext(gameType == "wonder" ? "wonder victory" : "capture the flag victory",
+ "%(min)s minute", "%(min)s minutes", duration), { "min": duration })
}));
}
Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- binaries/data/mods/public/gui/gamesetup/gamesetup.js
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.js
@@ -9,6 +9,7 @@
const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources);
const g_VictoryConditions = prepareForDropdown(g_Settings && g_Settings.VictoryConditions);
const g_WonderDurations = prepareForDropdown(g_Settings && g_Settings.WonderDurations);
+const g_CaptureTheFlagDurations = prepareForDropdown(g_Settings && g_Settings.CaptureTheFlagDurations);
/**
* All selectable playercolors except gaia.
@@ -307,6 +308,7 @@
initStartingResources();
initCeasefire();
initWonderDurations();
+ initCaptureTheFlagDurations();
initVictoryConditions();
initMapSizes();
initRadioButtons();
@@ -516,6 +518,21 @@
wonderConditions.selected = g_WonderDurations.Default;
}
+function initCaptureTheFlagDurations()
+{
+ let captureTheFlagConditions = Engine.GetGUIObjectByName("captureTheFlagDuration");
+ captureTheFlagConditions.list = g_CaptureTheFlagDurations.Title;
+ captureTheFlagConditions.list_data = g_CaptureTheFlagDurations.Duration;
+ captureTheFlagConditions.onSelectionChange = function()
+ {
+ if (this.selected != -1)
+ g_GameAttributes.settings.CaptureTheFlagDuration = g_CaptureTheFlagDurations.Duration[this.selected];
+
+ updateGameAttributes();
+ };
+ captureTheFlagConditions.selected = g_CaptureTheFlagDurations.Default;
+}
+
function initMapSizes()
{
let mapSize = Engine.GetGUIObjectByName("mapSize");
@@ -1262,6 +1279,7 @@
{
delete g_GameAttributes.settings.WonderDuration;
delete g_GameAttributes.settings.LastManStanding;
+ delete g_GameAttributes.settings.CaptureTheFlagDuration;
}
if (mapSettings.PlayerData)
@@ -1403,6 +1421,8 @@
let mapSizeIdx = mapSettings.Size !== undefined ? g_MapSizes.Tiles.indexOf(mapSettings.Size) : g_MapSizes.Default;
let victoryIdx = mapSettings.GameType !== undefined ? g_VictoryConditions.Name.indexOf(mapSettings.GameType) : g_VictoryConditions.Default;
let wonderDurationIdx = mapSettings.WonderDuration !== undefined ? g_WonderDurations.Duration.indexOf(mapSettings.WonderDuration) : g_WonderDurations.Default;
+ let captureTheFlagDurationIdx = mapSettings.CaptureTheFlagDuration !== undefined ?
+ g_CaptureTheFlagDurations.Duration.indexOf(mapSettings.CaptureTheFlagDuration) : g_CaptureTheFlagDurations.Default;
let popIdx = mapSettings.PopulationCap !== undefined ? g_PopulationCapacities.Population.indexOf(mapSettings.PopulationCap) : g_PopulationCapacities.Default;
let startingResIdx = mapSettings.StartingResources !== undefined ? g_StartingResources.Resources.indexOf(mapSettings.StartingResources) : g_StartingResources.Default;
let ceasefireIdx = mapSettings.Ceasefire !== undefined ? g_Ceasefire.Duration.indexOf(mapSettings.Ceasefire) : g_Ceasefire.Default;
@@ -1417,6 +1437,7 @@
Engine.GetGUIObjectByName("numPlayers").selected = numPlayers - 1;
Engine.GetGUIObjectByName("victoryCondition").selected = victoryIdx;
Engine.GetGUIObjectByName("wonderDuration").selected = wonderDurationIdx;
+ Engine.GetGUIObjectByName("captureTheFlagDuration").selected = captureTheFlagDurationIdx;
Engine.GetGUIObjectByName("populationCap").selected = popIdx;
Engine.GetGUIObjectByName("gameSpeed").selected = gameSpeedIdx;
Engine.GetGUIObjectByName("ceasefire").selected = ceasefireIdx;
@@ -1435,6 +1456,7 @@
Engine.GetGUIObjectByName("numPlayersText").caption = numPlayers;
Engine.GetGUIObjectByName("victoryConditionText").caption = g_VictoryConditions.Title[victoryIdx];
Engine.GetGUIObjectByName("wonderDurationText").caption = g_WonderDurations.Title[wonderDurationIdx];
+ Engine.GetGUIObjectByName("captureTheFlagDurationText").caption = g_CaptureTheFlagDurations.Title[captureTheFlagDurationIdx];
Engine.GetGUIObjectByName("populationCapText").caption = g_PopulationCapacities.Title[popIdx];
Engine.GetGUIObjectByName("startingResourcesText").caption = g_StartingResources.Title[startingResIdx];
Engine.GetGUIObjectByName("ceasefireText").caption = g_Ceasefire.Title[ceasefireIdx];
@@ -1452,6 +1474,10 @@
g_GameAttributes.settings.GameType &&
g_GameAttributes.settings.GameType != "wonder";
+ Engine.GetGUIObjectByName("optionCaptureTheFlagDuration").hidden =
+ g_GameAttributes.settings.GameType &&
+ g_GameAttributes.settings.GameType != "capture_the_flag";
+
Engine.GetGUIObjectByName("cheatWarningText").hidden = !g_IsNetworked || !mapSettings.CheatsEnabled;
Engine.GetGUIObjectByName("lastManStanding").enabled = !mapSettings.LockTeams;
@@ -1467,7 +1493,7 @@
let notScenario = g_GameAttributes.mapType != "scenario" && g_IsController ;
- for (let ctrl of ["victoryCondition", "wonderDuration", "populationCap",
+ for (let ctrl of ["victoryCondition", "wonderDuration", "captureTheFlagDuration", "populationCap",
"startingResources", "ceasefire", "revealMap",
"exploreMap", "disableTreasures", "lockTeams", "lastManStanding"])
hideControl(ctrl, ctrl + "Text", notScenario);
Index: binaries/data/mods/public/gui/gamesetup/gamesetup.xml
===================================================================
--- binaries/data/mods/public/gui/gamesetup/gamesetup.xml
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.xml
@@ -347,6 +347,16 @@
+
+
Population Cap:
Index: binaries/data/mods/public/maps/scripts/CaptureTheFlag.js
===================================================================
--- binaries/data/mods/public/maps/scripts/CaptureTheFlag.js
+++ binaries/data/mods/public/maps/scripts/CaptureTheFlag.js
@@ -0,0 +1,158 @@
+let g_FlagTemplate = "other/special_flag";
+
+Trigger.prototype.CheckCaptureTheFlagVictory = function(data)
+{
+ let cmpIdentity = Engine.QueryInterface(data.entity, IID_Identity);
+ if (!cmpIdentity || !cmpIdentity.HasClass("Flag") || data.from == -1)
+ return;
+
+ if (data.from != 0)
+ --this.playerFlagsCount[data.from];
+
+ ++this.playerFlagsCount[data.to];
+
+ this.CheckCaptureTheFlagCountdown();
+};
+
+/**
+ * Check if an individual player or team has acquired all the flags.
+ * Also check if the countdown needs to be stopped if a player/team no longer has all the flags.
+ */
+Trigger.prototype.CheckCaptureTheFlagCountdown = function()
+{
+ for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID)
+ {
+ let playerAndAllies = QueryPlayerIDInterface(playerID).GetMutualAllies();
+ let teamflagsOwned = 0;
+
+ for (let ally of playerAndAllies)
+ teamflagsOwned += this.playerFlagsCount[ally];
+
+ if (teamflagsOwned == this.flags.length - 1)
+ {
+ let data = { "entity": this.flags[playerID], "to": playerAndAllies };
+ this.StartCaptureTheFlagCountdown(data, playerAndAllies.length > 1);
+ return;
+ }
+ }
+
+ this.DeleteCaptureTheFlagVictoryMessages();
+};
+
+Trigger.prototype.DeleteCaptureTheFlagVictoryMessages = function()
+{
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+
+ for (let ent in this.flagsVictoryMessages)
+ {
+ cmpGuiInterface.DeleteTimeNotification(this.flagsVictoryMessages[ent].ownMessage);
+ cmpGuiInterface.DeleteTimeNotification(this.flagsVictoryMessages[ent].otherMessage);
+ cmpTimer.CancelTimer(this.flagsVictoryTimers[ent]);
+ }
+};
+
+Trigger.prototype.StartCaptureTheFlagCountdown = function(data, isTeam)
+{
+ let timer = this.flagsVictoryTimers[data.entity];
+ let messages = this.flagsVictoryMessages[data.entity] || {};
+
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+
+ if (timer)
+ {
+ cmpTimer.CancelTimer(timer);
+ cmpGuiInterface.DeleteTimeNotification(messages.ownMessage);
+ cmpGuiInterface.DeleteTimeNotification(messages.otherMessage);
+ }
+
+ let players = [-1];
+ for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID)
+ {
+ let cmpPlayer = QueryPlayerIDInterface(playerID);
+ if (cmpPlayer.GetState() == "won")
+ return;
+ if (data.to.indexOf(playerID) == -1)
+ players.push(playerID);
+ }
+
+ let cmpPlayer = QueryOwnerInterface(data.entity, IID_Player);
+ let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
+ let captureTheFlagDuration = cmpEndGameManager.GetGameTypeSettings().captureTheFlagDuration || 0;
+
+ messages.otherMessage = cmpGuiInterface.AddTimeNotification({
+ "message": isTeam ? markForTranslation("%(player)s's team has captured all of the flags. They will have won in %(time)s") :
+ markForTranslation("%(player)s has captured all of the flags. They will have won in %(time)s"),
+ "players": players,
+ "parameters": {
+ "player": cmpPlayer.GetName()
+ },
+ "translateMessage": true,
+ "translateParameters": [],
+ }, captureTheFlagDuration);
+
+ messages.ownMessage = cmpGuiInterface.AddTimeNotification({
+ "message": isTeam ? markForTranslation("Your team has captured all of the flags. You will have won in %(time)s") :
+ markForTranslation("You have captured all of the flags. You will have won in %(time)s"),
+ "players": data.to,
+ "translateMessage": true,
+ }, captureTheFlagDuration);
+
+ timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager,
+ "MarkPlayerAsWon", captureTheFlagDuration, data.to[0]);
+
+ this.flagsVictoryTimers[data.entity] = timer;
+ this.flagsVictoryMessages[data.entity] = messages;
+};
+
+Trigger.prototype.InitCaptureTheFlag = function()
+{
+ // Attempt to spawn one flag per player randomly in neutral territory
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);
+ let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
+
+ let validSpawnPoint = entity => {
+
+ let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
+ let cmpPosition = Engine.QueryInterface(entity, IID_Position);
+
+ if (!cmpIdentity || !cmpPosition || !cmpPosition.IsInWorld())
+ return false;
+
+ let pos = cmpPosition.GetPosition();
+ if (pos.y <= cmpWaterManager.GetWaterLevel(pos.x, pos.z) ||
+ cmpTerritoryManager.GetOwner(pos.x, pos.z) != 0)
+ return false;
+
+ return true;
+ };
+
+ let gaiaEntities = cmpRangeManager.GetEntitiesByPlayer(0).filter(entity => validSpawnPoint(entity));
+
+ if (!gaiaEntities.length)
+ {
+ error("No valid flag spawn points on map");
+ return;
+ }
+
+ for (let i = 1; i < TriggerHelper.GetNumberOfPlayers(); ++i)
+ {
+ this.flags[i] = TriggerHelper.SpawnUnits(pickRandom(gaiaEntities), g_FlagTemplate, 1, 0)[0];
+ this.playerFlagsCount[i] = 0;
+
+ let cmpDamageReceiver = Engine.QueryInterface(this.flags[i], IID_DamageReceiver);
+ cmpDamageReceiver.SetInvulnerability(true);
+ }
+};
+
+let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+cmpTrigger.flags = [];
+cmpTrigger.playerFlagsCount = [];
+cmpTrigger.flagsVictoryTimers = {};
+cmpTrigger.flagsVictoryMessages = {};
+
+cmpTrigger.DoAfterDelay(0, "InitCaptureTheFlag", {});
+cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckCaptureTheFlagVictory", { "enabled": true });
+cmpTrigger.RegisterTrigger("OnDiplomacyChanged", "CheckCaptureTheFlagCountdown", { "enabled": true });
Index: binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- binaries/data/mods/public/simulation/components/Identity.js
+++ binaries/data/mods/public/simulation/components/Identity.js
@@ -54,7 +54,7 @@
"" +
"" +
"" +
- "" +
+ "" +
"" +
"tokens" +
"" +
Index: binaries/data/mods/public/simulation/components/Trigger.js
===================================================================
--- binaries/data/mods/public/simulation/components/Trigger.js
+++ binaries/data/mods/public/simulation/components/Trigger.js
@@ -11,6 +11,7 @@
"CinemaPathEnded",
"CinemaQueueEnded",
"ConstructionStarted",
+ "DiplomacyChanged",
"InitGame",
"Interval",
"OwnershipChanged",
@@ -271,6 +272,11 @@
this.CallEvent("PlayerWon", msg);
};
+Trigger.prototype.OnGlobalDiplomacyChanged = function(msg)
+{
+ this.CallEvent("DiplomacyChanged", msg);
+};
+
/**
* Execute a function after a certain delay.
*
Index: binaries/data/mods/public/simulation/data/settings/victory_conditions/capture_the_flag.json
===================================================================
--- binaries/data/mods/public/simulation/data/settings/victory_conditions/capture_the_flag.json
+++ binaries/data/mods/public/simulation/data/settings/victory_conditions/capture_the_flag.json
@@ -0,0 +1,15 @@
+{
+ "TranslatedKeys": ["Title", "Description"],
+ "Data":
+ {
+ "Title": "Capture The Flag",
+ "Description": "Collect all of the flags spread across the map and keep them for a certain time to win the game.",
+ "Scripts":
+ [
+ "scripts/TriggerHelper.js",
+ "scripts/ConquestCommon.js",
+ "scripts/Conquest.js",
+ "scripts/CaptureTheFlag.js"
+ ]
+ }
+}
Index: binaries/data/mods/public/simulation/data/settings/victory_times.json
===================================================================
--- binaries/data/mods/public/simulation/data/settings/victory_times.json
+++ binaries/data/mods/public/simulation/data/settings/victory_times.json
@@ -0,0 +1,4 @@
+{
+ "Times": [0, 1, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 75, 90, 105, 120],
+ "Default": 20
+}
Index: binaries/data/mods/public/simulation/data/settings/wonder_times.json
===================================================================
--- binaries/data/mods/public/simulation/data/settings/wonder_times.json
+++ binaries/data/mods/public/simulation/data/settings/wonder_times.json
@@ -1,4 +0,0 @@
-{
- "Times": [0, 1, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 75, 90, 105, 120],
- "Default": 20
-}
Index: binaries/data/mods/public/simulation/helpers/Setup.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Setup.js
+++ binaries/data/mods/public/simulation/helpers/Setup.js
@@ -47,6 +47,8 @@
let gameTypeSettings = {};
if (settings.WonderDuration)
gameTypeSettings.wonderDuration = settings.WonderDuration * 60 * 1000;
+ if (settings.CaptureTheFlagDuration)
+ gameTypeSettings.captureTheFlagDuration = settings.CaptureTheFlagDuration * 60 * 1000;
if (settings.GameType)
cmpEndGameManager.SetGameType(settings.GameType, gameTypeSettings);
Index: binaries/data/mods/public/simulation/templates/other/special_flag.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/other/special_flag.xml
+++ binaries/data/mods/public/simulation/templates/other/special_flag.xml
@@ -0,0 +1,43 @@
+
+
+
+ 100
+ 1
+ 10
+
+
+ 0
+
+
+
+ 2.0
+
+
+ true
+
+
+ gaia
+ Flag
+ Flag
+ technologies/metal_pot.png
+ Special unit for Capture The Flag games.
+
+
+
+
+
+ star/256x256.png
+ star/256x256_mask.png
+
+
+
+
+ 6
+
+ 12.0
+
+
+
+ units/athenians/siege_spear_packed.xml
+
+
Index: source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp
===================================================================
--- source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp
+++ source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2016 Wildfire Games.
+/* Copyright (C) 2017 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -149,6 +149,7 @@
gameTypes.Add(_T("wonder"));
gameTypes.Add(_T("endless"));
gameTypes.Add(_T("regicide"));
+ gameTypes.Add(_T("capture_the_flag"));
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5);
gridSizer->AddGrowableCol(1);