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 @@ -211,6 +211,18 @@ { "min": g_GameAttributes.settings.VictoryDuration } ); + else if (g_VictoryConditions.Name[victoryIdx] == "regicide") + if (g_GameAttributes.settings.RegicideGarrison) + titles.push({ + "label": translate("Hero Garrison"), + "value": translate("Heroes can be garrisoned in structures.") + }); + else + titles.push({ + "label": translate("Exposed Heroes"), + "value": translate("Heroes cannot be garrisoned, and so they are vulnerable to raids.") + }); + titles.push({ "label": title, "value": g_VictoryConditions.Description[victoryIdx] 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 @@ -540,7 +540,8 @@ "DisableTreasures": "disableTreasures", "DisableSpies": "disableSpies", "LockTeams": "lockTeams", - "LastManStanding" : "lastManStanding", + "LastManStanding": "lastManStanding", + "RegicideGarrison": "regicideGarrison", "CheatsEnabled": "enableCheats" }; @@ -1260,6 +1261,7 @@ { delete g_GameAttributes.settings.VictoryDuration; delete g_GameAttributes.settings.LastManStanding; + delete g_GameAttributes.settings.RegicideGarrison; } if (mapSettings.PlayerData) @@ -1443,11 +1445,15 @@ setGUIBoolean("revealMap", "revealMapText", !!mapSettings.RevealMap); setGUIBoolean("lockTeams", "lockTeamsText", !!mapSettings.LockTeams); setGUIBoolean("lastManStanding", "lastManStandingText", !!mapSettings.LastManStanding); + setGUIBoolean("regicideGarrison", "regicideGarrisonText", !!mapSettings.RegicideGarrison); setGUIBoolean("enableRating", "enableRatingText", !!mapSettings.RatingEnabled); Engine.GetGUIObjectByName("optionVictoryDuration").hidden = g_GameAttributes.settings.GameType && g_GameAttributes.settings.GameType != "wonder" && g_GameAttributes.settings.GameType != "capture_the_relic"; + Engine.GetGUIObjectByName("optionRegicideGarrison").hidden = + g_GameAttributes.settings.GameType && + g_GameAttributes.settings.GameType != "regicide"; Engine.GetGUIObjectByName("cheatWarningText").hidden = !g_IsNetworked || !mapSettings.CheatsEnabled; @@ -1466,7 +1472,8 @@ for (let ctrl of ["victoryCondition", "victoryDuration", "populationCap", "startingResources", "ceasefire", "revealMap", - "exploreMap", "disableTreasures", "disableSpies", "lockTeams", "lastManStanding"]) + "exploreMap", "disableTreasures", "disableSpies", + "lockTeams", "lastManStanding", "regicideGarrison"]) hideControl(ctrl, ctrl + "Text", notScenario); Engine.GetGUIObjectByName("civResetButton").hidden = !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 @@ + + + Hero Garrison: + + + + + Population Cap: Index: binaries/data/mods/public/gui/session/unit_actions.js =================================================================== --- binaries/data/mods/public/gui/session/unit_actions.js +++ binaries/data/mods/public/gui/session/unit_actions.js @@ -642,7 +642,7 @@ }, "getActionInfo": function(entState, targetState) { - if (!hasClass(entState, "Unit") || + if (!hasClass(entState, "Unit") || !entState.identity.canGarrison || !targetState.garrisonHolder || !playerCheck(entState, targetState, ["Player", "MutualAlly"])) return false; Index: binaries/data/mods/public/maps/scripts/Regicide.js =================================================================== --- binaries/data/mods/public/maps/scripts/Regicide.js +++ binaries/data/mods/public/maps/scripts/Regicide.js @@ -6,6 +6,9 @@ Trigger.prototype.InitRegicideGame = function(msg) { + let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); + this.regicideGarrison = cmpEndGameManager.GetGameTypeSettings().regicideGarrison; + let playersCivs = []; for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID) playersCivs[playerID] = QueryPlayerIDInterface(playerID).GetCiv(); @@ -30,7 +33,7 @@ if (heroTemplates[identity.Civ].indexOf(templateName) == -1) heroTemplates[identity.Civ].push({ - "templateName": templateName, + "templateName": this.regicideGarrison ? templateName : "ungarrisonable|" + templateName, "classes": classes }); } @@ -51,6 +54,28 @@ return 0; }; + let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); + let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager); + + let gaiaSpawnPreferences = (shipEnt, entity) => { + + let cmpIdentity = Engine.QueryInterface(entity, IID_Identity); + let cmpPosition = Engine.QueryInterface(entity, IID_Position); + let cmpShipPosition = Engine.QueryInterface(shipEnt, IID_Position); + + if (!cmpIdentity || !cmpPosition || !cmpPosition.IsInWorld()) + return Infinity; + + let pos = cmpPosition.GetPosition(); + + // Do not spawn heroes underwater + if (pos.y <= cmpWaterManager.GetWaterLevel(pos.x, pos.z) || + cmpTerritoryManager.GetOwner(pos.x, pos.z) != 0) + return Infinity; + + return Engine.QueryInterface(shipEnt, IID_Position).GetPosition2D().distanceTo(Vector2D.from3D(pos)); + } + // Attempt to spawn one hero per player let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID) @@ -58,6 +83,11 @@ let spawnPoints = cmpRangeManager.GetEntitiesByPlayer(playerID).sort((entity1, entity2) => getSpawnPreference(entity2) - getSpawnPreference(entity1)); + // Spawn the hero on land as close as possible + if (!this.regicideGarrison && TriggerHelper.EntityHasClass(spawnPoints[0], "Ship")) + spawnPoints = cmpRangeManager.GetEntitiesByPlayer(0).sort((entity1, entity2) => + gaiaSpawnPreferences(spawnPoints[0], entity1) - gaiaSpawnPreferences(spawnPoints[0], entity2)); + this.regicideHeroes[playerID] = this.SpawnRegicideHero(playerID, heroTemplates[playersCivs[playerID]], spawnPoints); } }; @@ -108,5 +138,6 @@ let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); cmpTrigger.regicideHeroes = []; +cmpTrigger.regicideGarrison = false; cmpTrigger.DoAfterDelay(0, "InitRegicideGame", {}); cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckRegicideDefeat", { "enabled": true }); Index: binaries/data/mods/public/simulation/ai/common-api/entity.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/entity.js +++ binaries/data/mods/public/simulation/ai/common-api/entity.js @@ -750,6 +750,8 @@ "canGuard": function() { return this.get("UnitAI/CanGuard") === "true"; }, + "canGarrison": function() { return this.get("Identity/CanGarrison") !== "false"; }, + move: function(x, z, queued = false) { Engine.PostCommand(PlayerID,{"type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued }); return this; Index: binaries/data/mods/public/simulation/ai/common-api/shared.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/shared.js +++ binaries/data/mods/public/simulation/ai/common-api/shared.js @@ -114,6 +114,16 @@ this._derivedTemplates[name] = resource; return resource; } + else if (name.indexOf("ungarrisonable|") !== -1) + { + let base = this.GetTemplate(name.substr(15)); + + let ent = clone(base); + ent.Identity.CanGarrison = "false"; + + this._derivedTemplates[name] = ent; + return ent; + } error("Tried to retrieve invalid template '"+name+"'"); return null; Index: binaries/data/mods/public/simulation/ai/petra/garrisonManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/garrisonManager.js +++ binaries/data/mods/public/simulation/ai/petra/garrisonManager.js @@ -171,7 +171,7 @@ /** This is just a pre-garrison state, while the entity walk to the garrison holder */ m.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type) { - if (this.numberOfGarrisonedUnits(holder) >= holder.garrisonMax()) + if (this.numberOfGarrisonedUnits(holder) >= holder.garrisonMax() || !ent.canGarrison()) return; this.registerHolder(gameState, holder); Index: binaries/data/mods/public/simulation/ai/petra/navalManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/navalManager.js +++ binaries/data/mods/public/simulation/ai/petra/navalManager.js @@ -379,6 +379,9 @@ */ m.NavalManager.prototype.requireTransport = function(gameState, entity, startIndex, endIndex, endPos) { + if (!entity.canGarrison()) + return false; + if (entity.getMetadata(PlayerID, "transport") !== undefined) { if (this.Config.debug > 0) Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js =================================================================== --- binaries/data/mods/public/simulation/components/GarrisonHolder.js +++ binaries/data/mods/public/simulation/components/GarrisonHolder.js @@ -200,7 +200,7 @@ if (!cmpIdentity) return false; var entityClasses = cmpIdentity.GetClassesList(); - return MatchesClassList(entityClasses, this.template.List._string); + return MatchesClassList(entityClasses, this.template.List._string) && cmpIdentity.GetCanGarrison(); }; /** Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -271,7 +271,8 @@ "rank": cmpIdentity.GetRank(), "classes": cmpIdentity.GetClassesList(), "visibleClasses": cmpIdentity.GetVisibleClassesList(), - "selectionGroupName": cmpIdentity.GetSelectionGroupName() + "selectionGroupName": cmpIdentity.GetSelectionGroupName(), + "canGarrison": cmpIdentity.GetCanGarrison() }; let cmpPosition = Engine.QueryInterface(ent, IID_Position); 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 @@ -86,6 +86,11 @@ "" + "" + "" + + "" + + "" + + "" + + "" + + "" + ""; @@ -163,4 +168,9 @@ return this.template.GenericName; }; +Identity.prototype.GetCanGarrison = function() +{ + return this.template.CanGarrison ? this.template.CanGarrison == "true" : true; +}; + Engine.RegisterComponentType(IID_Identity, "Identity", Identity); Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -546,6 +546,7 @@ GetRank: function() { return "foo"; }, GetSelectionGroupName: function() { return "Selection Group Name"; }, HasClass: function() { return true; }, + GetCanGarrison: function() { return true; }, }); AddMock(10, IID_Position, { @@ -584,6 +585,7 @@ classes: ["class1", "class2"], visibleClasses: ["class3", "class4"], selectionGroupName: "Selection Group Name", + canGarrison: true, }, fogging: null, foundation: null, 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.VictoryDuration) gameTypeSettings.victoryDuration = settings.VictoryDuration * 60 * 1000; + if (settings.GameType && settings.GameType == "regicide") + gameTypeSettings.regicideGarrison = settings.RegicideGarrison; if (settings.GameType) cmpEndGameManager.SetGameType(settings.GameType, gameTypeSettings); Index: binaries/data/mods/public/simulation/templates/special_filter/ungarrisonable.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special_filter/ungarrisonable.xml +++ binaries/data/mods/public/simulation/templates/special_filter/ungarrisonable.xml @@ -0,0 +1,6 @@ + + + + false + +