Index: ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js
+++ ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js
@@ -260,6 +260,18 @@
"label": translate("Relic Count"),
"value": g_GameAttributes.settings.RelicCount
});
+
+ if (g_VictoryConditions.Name[victoryIdx] == "regicide")
+ if (g_GameAttributes.settings.RegicideGarrison)
+ titles.push({
+ "label": translate("Hero Garrison"),
+ "value": translate("Heroes can be garrisoned.")
+ });
+ else
+ titles.push({
+ "label": translate("Exposed Heroes"),
+ "value": translate("Heroes cannot be garrisoned, and they are vulnerable to raids.")
+ });
}
if (g_GameAttributes.settings.RatingEnabled &&
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
@@ -340,6 +340,7 @@
"ceasefire",
],
"Checkbox": [
+ "regicideGarrison",
"exploreMap",
"revealMap",
"disableTreasures",
@@ -680,6 +681,18 @@
* Contains the logic of all boolean gamesettings.
*/
var g_Checkboxes = {
+ "regicideGarrison": {
+ "title": () => translate("Hero Garrison"),
+ "tooltip": () => translate("Toggle whether heroes can be garrisoned."),
+ "default": () => false,
+ "defined": () => g_GameAttributes.settings.RegicideGarrison !== undefined,
+ "get": () => g_GameAttributes.settings.RegicideGarrison,
+ "set": checked => {
+ g_GameAttributes.settings.RegicideGarrison = checked;
+ },
+ "hidden": () => g_GameAttributes.settings.GameType != "regicide",
+ "enabled": () => g_GameAttributes.mapType != "scenario",
+ },
"revealMap": {
"title": () =>
// Translation: Make sure to differentiate between the revealed map and explored map options!
@@ -1568,6 +1581,7 @@
{
delete g_GameAttributes.settings.VictoryDuration;
delete g_GameAttributes.settings.LastManStanding;
+ delete g_GameAttributes.settings.RegicideGarrison;
}
if (mapSettings.PlayerData)
Index: ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js
+++ ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js
@@ -647,8 +647,7 @@
},
"getActionInfo": function(entState, targetState)
{
- if (!hasClass(entState, "Unit") ||
- !targetState.garrisonHolder ||
+ if (!entState.canGarrison || !targetState.garrisonHolder ||
!playerCheck(entState, targetState, ["Player", "MutualAlly"]))
return false;
Index: ps/trunk/binaries/data/mods/public/maps/scripts/CaptureTheRelic.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scripts/CaptureTheRelic.js
+++ ps/trunk/binaries/data/mods/public/maps/scripts/CaptureTheRelic.js
@@ -4,44 +4,7 @@
let catafalqueTemplates = shuffleArray(cmpTemplateManager.FindAllTemplates(false).filter(
name => name.startsWith("other/catafalque/")));
- // Attempt to spawn relics using gaia entities in neutral territory
- // If there are none, try to spawn using gaia entities in non-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 potentialGaiaSpawnPoints = [];
-
- let potentialSpawnPoints = cmpRangeManager.GetEntitiesByPlayer(0).filter(entity => {
- let cmpPosition = Engine.QueryInterface(entity, IID_Position);
- if (!cmpPosition || !cmpPosition.IsInWorld())
- return false;
-
- let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
- if (!cmpIdentity)
- return false;
-
- let templateName = cmpTemplateManager.GetCurrentTemplateName(entity);
- if (!templateName)
- return false;
-
- let template = cmpTemplateManager.GetTemplate(templateName);
- if (!template || template.UnitMotionFlying)
- return false;
-
- let pos = cmpPosition.GetPosition();
- if (pos.y <= cmpWaterManager.GetWaterLevel(pos.x, pos.z))
- return false;
-
- if (cmpTerritoryManager.GetOwner(pos.x, pos.z) == 0)
- potentialGaiaSpawnPoints.push(entity);
-
- return true;
- });
-
- if (potentialGaiaSpawnPoints.length)
- potentialSpawnPoints = potentialGaiaSpawnPoints;
-
+ let potentialSpawnPoints = TriggerHelper.GetLandSpawnPoints();
if (!potentialSpawnPoints.length)
{
error("No gaia entities found on this map that could be used as spawn points!");
Index: ps/trunk/binaries/data/mods/public/maps/scripts/Regicide.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scripts/Regicide.js
+++ ps/trunk/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);
+ let 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": regicideGarrison ? templateName : "ungarrisonable|" + templateName,
"classes": classes
});
}
@@ -58,6 +61,16 @@
let spawnPoints = cmpRangeManager.GetEntitiesByPlayer(playerID).sort((entity1, entity2) =>
getSpawnPreference(entity2) - getSpawnPreference(entity1));
+ // Spawn the hero on land as close as possible
+ if (!regicideGarrison && TriggerHelper.EntityHasClass(spawnPoints[0], "Ship"))
+ {
+ let shipPosition = Engine.QueryInterface(spawnPoints[0], IID_Position).GetPosition2D();
+ let distanceToShip = entity =>
+ Engine.QueryInterface(entity, IID_Position).GetPosition2D().distanceTo(shipPosition);
+ spawnPoints = TriggerHelper.GetLandSpawnPoints().sort((entity1, entity2) =>
+ distanceToShip(entity1) - distanceToShip(entity2));
+ }
+
this.regicideHeroes[playerID] = this.SpawnRegicideHero(playerID, heroTemplates[playersCivs[playerID]], spawnPoints);
}
};
Index: ps/trunk/binaries/data/mods/public/maps/scripts/TriggerHelper.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scripts/TriggerHelper.js
+++ ps/trunk/binaries/data/mods/public/maps/scripts/TriggerHelper.js
@@ -158,4 +158,46 @@
return classes && classes.indexOf(classname) != -1;
};
+/**
+ * Return valid gaia-owned spawn points on land in neutral territory.
+ * If there are none, use those available in player-owned territory.
+ */
+TriggerHelper.GetLandSpawnPoints = function()
+{
+ let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);
+ let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+
+ let neutralSpawnPoints = [];
+ let nonNeutralSpawnPoints = [];
+
+ for (let ent of cmpRangeManager.GetEntitiesByPlayer(0))
+ {
+ let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+ let cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ if (!cmpIdentity || !cmpPosition || !cmpPosition.IsInWorld())
+ continue;
+
+ let templateName = cmpTemplateManager.GetCurrentTemplateName(ent);
+ if (!templateName)
+ continue;
+
+ let template = cmpTemplateManager.GetTemplate(templateName);
+ if (!template || template.UnitMotionFlying)
+ continue;
+
+ let pos = cmpPosition.GetPosition();
+ if (pos.y <= cmpWaterManager.GetWaterLevel(pos.x, pos.z))
+ continue;
+
+ if (cmpTerritoryManager.GetOwner(pos.x, pos.z) == 0)
+ neutralSpawnPoints.push(ent);
+ else
+ nonNeutralSpawnPoints.push(ent);
+ }
+
+ return neutralSpawnPoints.length ? neutralSpawnPoints : nonNeutralSpawnPoints;
+};
+
Engine.RegisterGlobal("TriggerHelper", TriggerHelper);
Index: ps/trunk/binaries/data/mods/public/simulation/ai/common-api/entity.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/ai/common-api/entity.js
+++ ps/trunk/binaries/data/mods/public/simulation/ai/common-api/entity.js
@@ -769,6 +769,8 @@
"canGuard": function() { return this.get("UnitAI/CanGuard") === "true"; },
+ "canGarrison": function() { return this.get("Garrisonable") !== "false"; },
+
move: function(x, z, queued = false) {
Engine.PostCommand(PlayerID,{"type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued });
return this;
Index: ps/trunk/binaries/data/mods/public/simulation/ai/common-api/shared.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/ai/common-api/shared.js
+++ ps/trunk/binaries/data/mods/public/simulation/ai/common-api/shared.js
@@ -114,6 +114,20 @@
this._derivedTemplates[name] = resource;
return resource;
}
+ else if (name.indexOf("ungarrisonable|") !== -1)
+ {
+ let base = this.GetTemplate(name.substr(15));
+
+ let ent = {};
+ for (let key in base)
+ if (key !== "Garrisonable")
+ ent[key] = base[key];
+ else
+ ent[key] = "false";
+
+ this._derivedTemplates[name] = ent;
+ return ent;
+ }
error("Tried to retrieve invalid template '"+name+"'");
return null;
Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
+++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
@@ -198,7 +198,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: ps/trunk/binaries/data/mods/public/simulation/ai/petra/navalManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/ai/petra/navalManager.js
+++ ps/trunk/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: ps/trunk/binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/GarrisonHolder.js
+++ ps/trunk/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) && !!Engine.QueryInterface(entity, IID_Garrisonable);
};
/**
Index: ps/trunk/binaries/data/mods/public/simulation/components/Garrisonable.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Garrisonable.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/Garrisonable.js
@@ -0,0 +1,11 @@
+function Garrisonable() {}
+
+Garrisonable.prototype.Schema = "";
+
+Garrisonable.prototype.Init = function()
+{
+};
+
+Garrisonable.prototype.Serialize = null;
+
+Engine.RegisterComponentType(IID_Garrisonable, "Garrisonable", Garrisonable);
Index: ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -240,6 +240,7 @@
"alertRaiser": null,
"builder": null,
+ "canGarrison": null,
"identity": null,
"fogging": null,
"foundation": null,
@@ -374,6 +375,8 @@
"garrisonedEntitiesCount": cmpGarrisonHolder.GetGarrisonedEntitiesCount()
};
+ ret.canGarrison = !!Engine.QueryInterface(ent, IID_Garrisonable);
+
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (cmpUnitAI)
ret.unitAI = {
Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Garrisonable.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Garrisonable.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Garrisonable.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("Garrisonable");
Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
@@ -10,6 +10,7 @@
Engine.LoadComponentScript("interfaces/EndGameManager.js");
Engine.LoadComponentScript("interfaces/EntityLimits.js");
Engine.LoadComponentScript("interfaces/Foundation.js");
+Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/Gate.js");
Engine.LoadComponentScript("interfaces/Guard.js");
@@ -607,6 +608,7 @@
template: "example",
alertRaiser: null,
builder: true,
+ canGarrison: false,
identity: {
rank: "foo",
classes: ["class1", "class2"],
Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js
@@ -45,10 +45,12 @@
let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
let gameTypeSettings = {};
- if (settings.RelicCount)
+ if (settings.GameType && settings.GameType == "capture_the_relic")
gameTypeSettings.relicCount = settings.RelicCount;
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: ps/trunk/binaries/data/mods/public/simulation/templates/special_filter/ungarrisonable.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/special_filter/ungarrisonable.xml
+++ ps/trunk/binaries/data/mods/public/simulation/templates/special_filter/ungarrisonable.xml
@@ -0,0 +1,4 @@
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
@@ -27,6 +27,7 @@
2.5
+
corpse