petra adaptation to multiple victory conditions, in preparation of D1240

This was SVN commit r21027.
This commit is contained in:
mimo
2018-01-27 10:10:55 +00:00
parent 8585040f27
commit fe3668aebe
8 changed files with 157 additions and 106 deletions
@@ -20,6 +20,7 @@ m.GameState.prototype.init = function(SharedScript, state, player)
this.player = player;
this.playerData = SharedScript.playersData[this.player];
this.gameType = SharedScript.gameType;
this.victoryConditions = new Set([this.gameType]);
this.alliedVictory = SharedScript.alliedVictory;
this.ceasefireActive = SharedScript.ceasefireActive;
this.ceasefireTimeRemaining = SharedScript.ceasefireTimeRemaining;
@@ -123,6 +124,11 @@ m.GameState.prototype.getBarterPrices = function()
return this.playerData.barterPrices;
};
m.GameState.prototype.getVictoryConditions = function()
{
return this.victoryConditions;
};
m.GameState.prototype.getGameType = function()
{
return this.gameType;
@@ -439,7 +439,7 @@ m.AttackManager.prototype.getAttackInPreparation = function(type)
};
/**
* determine which player should be attacked: when called when starting the attack,
* Determine which player should be attacked: when called when starting the attack,
* attack.targetPlayer is undefined and in that case, we keep track of the chosen target
* for future attacks.
*/
@@ -447,67 +447,24 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
{
let enemyPlayer;
// first check if there is a preferred enemy based on our victory conditions
if (gameState.getGameType() === "wonder")
{
let moreAdvanced;
let enemyWonder;
let wonders = gameState.getEnemyStructures().filter(API3.Filters.byClass("Wonder"));
for (let wonder of wonders.values())
{
if (wonder.owner() === 0)
continue;
let progress = wonder.foundationProgress();
if (progress === undefined)
{
enemyWonder = wonder;
break;
}
if (enemyWonder && moreAdvanced > progress)
continue;
enemyWonder = wonder;
moreAdvanced = progress;
}
if (enemyWonder)
{
enemyPlayer = enemyWonder.owner();
if (attack.targetPlayer === undefined)
this.currentEnemyPlayer = enemyPlayer;
return enemyPlayer;
}
}
else if (gameState.getGameType() === "capture_the_relic")
{
// Target the player with the most relics (including gaia)
let allRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic"));
let maxRelicsOwned = 0;
for (let i = 0; i < gameState.sharedScript.playersData.length; ++i)
{
if (!gameState.isPlayerEnemy(i) || this.defeated[i] ||
i === 0 && !gameState.ai.HQ.gameTypeManager.tryCaptureGaiaRelic)
continue;
// First check if there is a preferred enemy based on our victory conditions.
// If both wonder and relic, choose randomly between them TODO should combine decisions
let relicsCount = allRelics.filter(relic => relic.owner() === i).length;
if (relicsCount <= maxRelicsOwned)
continue;
maxRelicsOwned = relicsCount;
enemyPlayer = i;
}
if (enemyPlayer !== undefined)
{
if (attack.targetPlayer === undefined)
this.currentEnemyPlayer = enemyPlayer;
if (enemyPlayer === 0)
gameState.ai.HQ.gameTypeManager.resetCaptureGaiaRelic(gameState);
return enemyPlayer;
}
}
if (gameState.getVictoryConditions().has("wonder"))
enemyPlayer = this.getWonderEnemyPlayer(gameState);
if (gameState.getVictoryConditions().has("capture_the_relic"))
if (!enemyPlayer || randBool())
enemyPlayer = this.getWonderEnemyPlayer(gameState) || enemyPlayer;
if (enemyPlayer)
return enemyPlayer;
let veto = {};
for (let i in this.defeated)
veto[i] = true;
// No rush if enemy too well defended (i.e. iberians)
if (attack.type === "Rush")
if (attack.type == "Rush")
{
for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
{
@@ -526,7 +483,7 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
// then if not a huge attack, continue attacking our previous target as long as it has some entities,
// otherwise target the most accessible one
if (attack.type !== "HugeAttack")
if (attack.type != "HugeAttack")
{
if (attack.targetPlayer === undefined && this.currentEnemyPlayer !== undefined &&
!this.defeated[this.currentEnemyPlayer] &&
@@ -539,7 +496,7 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre"));
for (let ourcc of ccEnts.values())
{
if (ourcc.owner() !== PlayerID)
if (ourcc.owner() != PlayerID)
continue;
let ourPos = ourcc.position();
let access = gameState.ai.accessibility.getAccessValue(ourPos);
@@ -550,7 +507,7 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
if (!gameState.isPlayerEnemy(enemycc.owner()))
continue;
let enemyPos = enemycc.position();
if (access !== gameState.ai.accessibility.getAccessValue(enemyPos))
if (access != gameState.ai.accessibility.getAccessValue(enemyPos))
continue;
let dist = API3.SquareVectorDistance(ourPos, enemyPos);
if (distmin && dist > distmin)
@@ -597,6 +554,69 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
return enemyPlayer;
};
/**
* Target the player with the most advanced wonder.
* TODO currently the first built wonder is kept, should chek on the minimum wonderDuration left instead.
*/
m.AttackManager.prototype.getWonderEnemyPlayer = function(gameState)
{
let enemyPlayer;
let enemyWonder;
let moreAdvanced;
for (let wonder of gameState.getEnemyStructures().filter(API3.Filters.byClass("Wonder")))
{
if (wonder.owner() == 0)
continue;
let progress = wonder.foundationProgress();
if (progress === undefined)
{
enemyWonder = wonder;
break;
}
if (enemyWonder && moreAdvanced > progress)
continue;
enemyWonder = wonder;
moreAdvanced = progress;
}
if (enemyWonder)
{
enemyPlayer = enemyWonder.owner();
if (attack.targetPlayer === undefined)
this.currentEnemyPlayer = enemyPlayer;
}
return enemyPlayer;
};
/**
* Target the player with the most relics (including gaia).
*/
m.AttackManager.prototype.getRelicEnemyPlayer = function(gameState)
{
let enemyPlayer;
let allRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic"));
let maxRelicsOwned = 0;
for (let i = 0; i < gameState.sharedScript.playersData.length; ++i)
{
if (!gameState.isPlayerEnemy(i) || this.defeated[i] ||
i == 0 && !gameState.ai.HQ.gameTypeManager.tryCaptureGaiaRelic)
continue;
let relicsCount = allRelics.filter(relic => relic.owner() == i).length;
if (relicsCount <= maxRelicsOwned)
continue;
maxRelicsOwned = relicsCount;
enemyPlayer = i;
}
if (enemyPlayer !== undefined)
{
if (attack.targetPlayer === undefined)
this.currentEnemyPlayer = enemyPlayer;
if (enemyPlayer == 0)
gameState.ai.HQ.gameTypeManager.resetCaptureGaiaRelic(gameState);
}
return enemyPlayer;
};
/** f.e. if we have changed diplomacy with another player. */
m.AttackManager.prototype.cancelAttacksAgainstPlayer = function(gameState, player)
{
@@ -853,7 +853,7 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand
let minDist = Math.min();
for (let ent of targets.values())
{
if (this.targetPlayer == 0 && gameState.getGameType() == "capture_the_relic" &&
if (this.targetPlayer == 0 && gameState.getVictoryConditions().has("capture_the_relic") &&
(!ent.hasClass("Relic") || gameState.ai.HQ.gameTypeManager.targetedGaiaRelics.has(ent.id())))
continue;
// Do not bother with some pointless targets
@@ -877,7 +877,7 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand
if (!target)
return undefined;
if (this.targetPlayer == 0 && gameState.getGameType() == "capture_the_relic" && target.hasClass("Relic"))
if (this.targetPlayer == 0 && gameState.getVictoryConditions().has("capture_the_relic") && target.hasClass("Relic"))
gameState.ai.HQ.gameTypeManager.targetedGaiaRelics.add(target.id());
// Rushes can change their enemy target if nothing found with the preferred enemy
// Obstruction also can change the enemy target
@@ -887,23 +887,24 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand
/**
* Default target finder aims for conquest critical targets
* We must apply the same selection (isValidTarget) as done in getNearestTarget
* We must apply the *same* selection (isValidTarget) as done in getNearestTarget
*/
m.AttackPlan.prototype.defaultTargetFinder = function(gameState, playerEnemy)
{
let targets;
if (gameState.getGameType() == "wonder")
targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("Wonder"));
else if (gameState.getGameType() == "regicide")
targets = gameState.getEnemyUnits(playerEnemy).filter(API3.Filters.byClass("Hero"));
else if (gameState.getGameType() == "capture_the_relic")
targets = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")).filter(relic => relic.owner() == playerEnemy);
if (targets)
targets = targets.filter(this.isValidTarget, this);
if (targets && targets.hasEntities())
let targets = new API3.EntityCollection(gameState.sharedScript);
if (gameState.getVictoryConditions().has("wonder"))
for (let ent of gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("Wonder")).values())
targets.addEnt(ent);
if (gameState.getVictoryConditions().has("regicide"))
for (let ent of gameState.getEnemyUnits(playerEnemy).filter(API3.Filters.byClass("Hero")).values())
targets.addEnt(ent);
if (gameState.getVictoryConditions().has("capture_the_relic"))
for (let ent of gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")).filter(relic => relic.owner() == playerEnemy).values())
targets.addEnt(ent);
targets = targets.filter(this.isValidTarget, this);
if (targets.hasEntities())
return targets;
// We must apply the *same* selection as done in getNearestTarget
let validTargets = gameState.getEnemyStructures(playerEnemy).filter(this.isValidTarget, this);
targets = validTargets.filter(API3.Filters.byClass("CivCentre"));
if (!targets.hasEntities())
@@ -749,7 +749,7 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
if (gameState.ai.HQ.isNearInvadingArmy(target.position()))
if (!target.hasClass("CivCentre") && !target.hasClass("StoneWall") &&
(!target.hasClass("Wonder") || gameState.getGameType() != "wonder"))
(!target.hasClass("Wonder") || !gameState.getVictoryConditions().has("wonder")))
continue;
// if our territory has shrinked since this foundation was positioned, do not build it
@@ -774,7 +774,7 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
targetNB = 3;
if (target.getMetadata(PlayerID, "baseAnchor") == true ||
target.hasClass("Wonder") && gameState.getGameType() == "wonder")
target.hasClass("Wonder") && gameState.getVictoryConditions().has("wonder"))
{
targetNB = 15;
maxTotalBuilders = Math.max(maxTotalBuilders, 15);
@@ -848,7 +848,7 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
if (gameState.ai.HQ.isNearInvadingArmy(target.position()))
if (target.healthLevel() > 0.5 ||
!target.hasClass("CivCentre") && !target.hasClass("StoneWall") &&
(!target.hasClass("Wonder") || gameState.getGameType() != "wonder"))
(!target.hasClass("Wonder") || !gameState.getVictoryConditions().has("wonder")))
continue;
else if (noRepair && !target.hasClass("CivCentre"))
continue;
@@ -862,7 +862,7 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
if (target.hasClass("Fortress") || target.hasClass("Wonder"))
targetNB = 3;
if (target.getMetadata(PlayerID, "baseAnchor") == true ||
target.hasClass("Wonder") && gameState.getGameType() == "wonder")
target.hasClass("Wonder") && gameState.getVictoryConditions().has("wonder"))
{
maxTotalBuilders = Math.ceil(workers.length * Math.max(0.3, builderRatio));
targetNB = 5;
@@ -209,7 +209,7 @@ m.Config.prototype.setConfig = function(gameState)
this.Economy.targetNumTraders = 2 + this.difficulty;
if (gameState.getGameType() === "wonder")
if (gameState.getVictoryConditions().has("wonder"))
{
this.Economy.workPhase3 = Math.floor(0.9 * this.Economy.workPhase3);
this.Economy.workPhase4 = Math.floor(0.9 * this.Economy.workPhase4);
@@ -315,7 +315,7 @@ m.DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
if (gameState.isPlayerNeutral(i)) // be more inclined to turn against neutral players
turnFactor += this.betrayWeighting;
if (gameState.getGameType() === "wonder")
if (gameState.getVictoryConditions().has("wonder"))
{
let wonder = gameState.getEnemyStructures(i).filter(API3.Filters.byClass("Wonder"))[0];
if (wonder)
@@ -330,7 +330,7 @@ m.DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
}
}
if (gameState.getGameType() === "capture_the_relic")
if (gameState.getVictoryConditions().has("capture_the_relic"))
{
let relicsCount = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic"))
.filter(relic => relic.owner() === i).length;
@@ -27,13 +27,13 @@ m.GameTypeManager = function(Config)
*/
m.GameTypeManager.prototype.init = function(gameState)
{
if (gameState.getGameType() === "wonder")
if (gameState.getVictoryConditions().has("wonder"))
{
for (let wonder of gameState.getOwnEntitiesByClass("Wonder", true).values())
this.criticalEnts.set(wonder.id(), { "guardsAssigned": 0, "guards": new Map() });
}
if (gameState.getGameType() === "regicide")
if (gameState.getVictoryConditions().has("regicide"))
{
for (let hero of gameState.getOwnEntitiesByClass("Hero", true).values())
{
@@ -57,7 +57,7 @@ m.GameTypeManager.prototype.init = function(gameState)
*/
m.GameTypeManager.prototype.checkEvents = function(gameState, events)
{
if (gameState.getGameType() === "wonder")
if (gameState.getVictoryConditions().has("wonder"))
{
for (let evt of events.Create)
{
@@ -89,7 +89,7 @@ m.GameTypeManager.prototype.checkEvents = function(gameState, events)
}
}
if (gameState.getGameType() === "regicide")
if (gameState.getVictoryConditions().has("regicide"))
{
for (let evt of events.Attacked)
{
@@ -288,8 +288,8 @@ m.GameTypeManager.prototype.checkEvents = function(gameState, events)
continue;
let ent = gameState.getEntityById(evt.entity);
if (ent && (gameState.getGameType() === "wonder" && ent.hasClass("Wonder") ||
gameState.getGameType() === "capture_the_relic" && ent.hasClass("Relic")))
if (ent && (gameState.getVictoryConditions().has("wonder") && ent.hasClass("Wonder") ||
gameState.getVictoryConditions().has("capture_the_relic") && ent.hasClass("Relic")))
{
this.criticalEnts.set(ent.id(), { "guardsAssigned": 0, "guards": new Map() });
// Move captured relics to the closest base
@@ -328,6 +328,24 @@ m.GameTypeManager.prototype.removeGuardsFromCriticalEnt = function(gameState, cr
this.criticalEnts.delete(criticalEntId);
};
/**
* Train more healers and affect them to critical entities if needed
*/
m.GameTypeManager.prototype.manageCriticalEntHealers = function(gameState, queues)
{
if (this.guardEnts.size > Math.min(gameState.getPopulationMax() / 10, gameState.getPopulation() / 4))
return;
for (let [id, data] of this.criticalEnts)
{
if (data.healersAssigned !== undefined && data.healersAssigned < this.healersPerCriticalEnt)
{
this.trainCriticalEntHealer(gameState, queues, id);
return;
}
}
};
/**
* Try to keep some military units guarding any criticalEnts, if we can afford it.
* If we have too low a population and require units for other needs, remove guards so they can be reassigned.
@@ -556,36 +574,42 @@ m.GameTypeManager.prototype.resetCaptureGaiaRelic = function(gameState)
m.GameTypeManager.prototype.update = function(gameState, events, queues)
{
// Wait a turn for trigger scripts to spawn any critical ents (i.e. in regicide)
if (gameState.ai.playedTurn === 1)
if (gameState.ai.playedTurn == 1)
this.init(gameState);
/* for (let [id, data] of this.criticalEnts)
{
API3.warn(" critical " + id + " data " + uneval(data));
for (let [k, v] of data.guards)
API3.warn(" --- guard " + k + " >>> " + uneval(v));
} */
this.checkEvents(gameState, events);
if (gameState.getGameType() === "wonder" && gameState.ai.playedTurn % 10 === 0)
{
gameState.ai.HQ.buildWonder(gameState, queues, true);
this.manageCriticalEntGuards(gameState);
}
if (gameState.ai.playedTurn % 10 != 0 ||
!gameState.getVictoryConditions().has("wonder") && !gameState.getVictoryConditions().has("regicide") &&
!gameState.getVictoryConditions().has("capture_the_relic"))
return;
if (gameState.getGameType() === "regicide" && gameState.ai.playedTurn % 10 === 0)
this.manageCriticalEntGuards(gameState);
if (gameState.getVictoryConditions().has("wonder"))
gameState.ai.HQ.buildWonder(gameState, queues, true);
if (gameState.getVictoryConditions().has("regicide"))
{
for (let [id, data] of this.criticalEnts)
for (let id of this.criticalEnts.keys())
{
let ent = gameState.getEntityById(id);
if (ent && ent.healthLevel() > this.Config.garrisonHealthLevel.high && ent.hasClass("Soldier") &&
ent.getStance() !== "aggressive")
ent.getStance() != "aggressive")
ent.setStance("aggressive");
if (data.healersAssigned < this.healersPerCriticalEnt &&
this.guardEnts.size < Math.min(gameState.getPopulationMax() / 10, gameState.getPopulation() / 4))
this.trainCriticalEntHealer(gameState, queues, id);
}
this.manageCriticalEntGuards(gameState);
this.manageCriticalEntHealers(gameState, queues);
}
if (gameState.getGameType() === "capture_the_relic" && gameState.ai.playedTurn % 10 === 0)
if (gameState.getVictoryConditions().has("capture_the_relic"))
{
this.manageCriticalEntGuards(gameState);
if (!this.tryCaptureGaiaRelic && gameState.ai.elapsedTime > this.tryCaptureGaiaRelicLapseTime)
this.tryCaptureGaiaRelic = true;
@@ -596,8 +620,8 @@ m.GameTypeManager.prototype.update = function(gameState, events, queues)
for (let relic of allRelics.values())
{
let relicPosition = relic.position();
if (this.targetedGaiaRelics.has(relic.id()) || relic.owner() !== 0 ||
!relicPosition || gameState.ai.HQ.territoryMap.getOwner(relicPosition) !== PlayerID)
if (this.targetedGaiaRelics.has(relic.id()) || relic.owner() != 0 ||
!relicPosition || gameState.ai.HQ.territoryMap.getOwner(relicPosition) != PlayerID)
continue;
gameState.ai.HQ.attackManager.raidTargetEntity(gameState, relic);
@@ -1310,7 +1310,7 @@ m.HQ.prototype.findDefensiveLocation = function(gameState, template)
}
enemyStructures = enemyStructures.toEntityArray();
let wonderMode = gameState.getGameType() === "wonder";
let wonderMode = gameState.getVictoryConditions().has("wonder");
let wonderDistmin;
let wonders;
if (wonderMode)
@@ -1431,7 +1431,7 @@ m.HQ.prototype.buildTemple = function(gameState, queues)
!gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities())
return;
// Try to build a temple earlier if in regicide to recruit healer guards
if (this.currentPhase < 3 && gameState.getGameType() !== "regicide")
if (this.currentPhase < 3 && !gameState.getVictoryConditions().has("regicide"))
return;
if (!this.canBuild(gameState, "structures/{civ}_temple"))
return;