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,153 @@ +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.CheckCountdown(); +}; + +/** + * Check if an individual player or a 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.CheckCountdown = 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.StartCountdown(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.StartCountdown = 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); + 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": [], + }, 10 * 60 * 1000); // 10 min + + 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, + }, 10 * 60 * 1000); + + timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager, + "MarkPlayerAsWon", 10 * 60 * 1000, data.to[0]); + + this.flagsVictoryTimers[data.entity] = timer; + this.flagsVictoryMessages[data.entity] = messages; +}; + +Trigger.prototype.InitflagsGame = 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), "special_units/special_flag", 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, "InitflagsGame", {}); +cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckCaptureTheFlagVictory", { "enabled": true }); +cmpTrigger.RegisterTrigger("OnDiplomacyChanged", "CheckCountdown", { "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/templates/special_units/special_flag.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special_units/special_flag.xml +++ binaries/data/mods/public/simulation/templates/special_units/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 @@ -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);