forked from mirrors/0ad
Use proper js-modules for petra
Petra already used a coding pattern which was called modules. That is replaced with native js-modules. Refs: #8081 Fixes: #1024
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { Config } from "simulation/ai/petra/config.js";
|
||||
import { Headquather } from "simulation/ai/petra/headquarters.js";
|
||||
import { Queue } from "simulation/ai/petra/queue.js";
|
||||
import { QueueManager } from "simulation/ai/petra/queueManager.js";
|
||||
|
||||
Engine.IncludeModule("common-api");
|
||||
|
||||
var PETRA = {};
|
||||
|
||||
PETRA.PetraBot = function(settings)
|
||||
export function PetraBot(settings)
|
||||
{
|
||||
API3.BaseAI.call(this, settings);
|
||||
|
||||
@@ -16,14 +19,14 @@ PETRA.PetraBot = function(settings)
|
||||
"transports": 1 // transport plans start at 1 because 0 might be used as none
|
||||
};
|
||||
|
||||
this.Config = new PETRA.Config(settings.difficulty, settings.behavior);
|
||||
this.Config = new Config(settings.difficulty, settings.behavior);
|
||||
|
||||
this.savedEvents = {};
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.PetraBot.prototype = Object.create(API3.BaseAI.prototype);
|
||||
PetraBot.prototype = Object.create(API3.BaseAI.prototype);
|
||||
|
||||
PETRA.PetraBot.prototype.CustomInit = function(gameState)
|
||||
PetraBot.prototype.CustomInit = function(gameState)
|
||||
{
|
||||
if (this.isDeserialized)
|
||||
{
|
||||
@@ -51,11 +54,11 @@ PETRA.PetraBot.prototype.CustomInit = function(gameState)
|
||||
|
||||
this.Config.Deserialize(this.data.config);
|
||||
|
||||
this.queueManager = new PETRA.QueueManager(this.Config, {});
|
||||
this.queueManager = new QueueManager(this.Config, {});
|
||||
this.queueManager.Deserialize(gameState, this.data.queueManager);
|
||||
this.queues = this.queueManager.queues;
|
||||
|
||||
this.HQ = new PETRA.HQ(this.Config);
|
||||
this.HQ = new Headquarther(this.Config);
|
||||
this.HQ.init(gameState, this.queues);
|
||||
this.HQ.Deserialize(gameState, this.data.HQ);
|
||||
|
||||
@@ -73,11 +76,11 @@ PETRA.PetraBot.prototype.CustomInit = function(gameState)
|
||||
// this.queues can only be modified by the queue manager or things will go awry.
|
||||
this.queues = {};
|
||||
for (const i in this.Config.priorities)
|
||||
this.queues[i] = new PETRA.Queue();
|
||||
this.queues[i] = new Queue();
|
||||
|
||||
this.queueManager = new PETRA.QueueManager(this.Config, this.queues);
|
||||
this.queueManager = new QueueManager(this.Config, this.queues);
|
||||
|
||||
this.HQ = new PETRA.HQ(this.Config);
|
||||
this.HQ = new Headquather(this.Config);
|
||||
|
||||
this.HQ.init(gameState, this.queues);
|
||||
|
||||
@@ -86,7 +89,7 @@ PETRA.PetraBot.prototype.CustomInit = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.PetraBot.prototype.OnUpdate = function(sharedScript)
|
||||
PetraBot.prototype.OnUpdate = function(sharedScript)
|
||||
{
|
||||
if (this.gameFinished || this.gameState.playerData.state == "defeated")
|
||||
return;
|
||||
@@ -128,7 +131,7 @@ PETRA.PetraBot.prototype.OnUpdate = function(sharedScript)
|
||||
this.turn++;
|
||||
};
|
||||
|
||||
PETRA.PetraBot.prototype.Serialize = function()
|
||||
PetraBot.prototype.Serialize = function()
|
||||
{
|
||||
const savedEvents = {};
|
||||
for (const key in this.savedEvents)
|
||||
@@ -159,7 +162,7 @@ PETRA.PetraBot.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.PetraBot.prototype.Deserialize = function(data, sharedScript)
|
||||
PetraBot.prototype.Deserialize = function(data, sharedScript)
|
||||
{
|
||||
this.isDeserialized = true;
|
||||
this.data = data;
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
/**
|
||||
* Attack Manager
|
||||
*/
|
||||
PETRA.AttackManager = function(Config)
|
||||
import { AttackPlan } from "simulation/ai/petra/attackPlan.js";
|
||||
import * as chat from "simulation/ai/petra/chatHelper.js";
|
||||
import { Config, DIFFICULTY_VERY_EASY } from "simulation/ai/petra/config.js";
|
||||
import { allowCapture, getLandAccess } from "simulation/ai/petra/entityExtend.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
export function AttackManager(config)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.Config = config;
|
||||
|
||||
this.totalNumber = 0;
|
||||
this.attackNumber = 0;
|
||||
this.rushNumber = 0;
|
||||
this.raidNumber = 0;
|
||||
this.upcomingAttacks = {
|
||||
[PETRA.AttackPlan.TYPE_RUSH]: [],
|
||||
[PETRA.AttackPlan.TYPE_RAID]: [],
|
||||
[PETRA.AttackPlan.TYPE_DEFAULT]: [],
|
||||
[PETRA.AttackPlan.TYPE_HUGE_ATTACK]: []
|
||||
[AttackPlan.TYPE_RUSH]: [],
|
||||
[AttackPlan.TYPE_RAID]: [],
|
||||
[AttackPlan.TYPE_DEFAULT]: [],
|
||||
[AttackPlan.TYPE_HUGE_ATTACK]: []
|
||||
};
|
||||
this.startedAttacks = {
|
||||
[PETRA.AttackPlan.TYPE_RUSH]: [],
|
||||
[PETRA.AttackPlan.TYPE_RAID]: [],
|
||||
[PETRA.AttackPlan.TYPE_DEFAULT]: [],
|
||||
[PETRA.AttackPlan.TYPE_HUGE_ATTACK]: []
|
||||
[AttackPlan.TYPE_RUSH]: [],
|
||||
[AttackPlan.TYPE_RAID]: [],
|
||||
[AttackPlan.TYPE_DEFAULT]: [],
|
||||
[AttackPlan.TYPE_HUGE_ATTACK]: []
|
||||
};
|
||||
this.bombingAttacks = new Map();// Temporary attacks for siege units while waiting their current attack to start
|
||||
this.debugTime = 0;
|
||||
@@ -27,16 +30,16 @@ PETRA.AttackManager = function(Config)
|
||||
this.rushSize = [];
|
||||
this.currentEnemyPlayer = undefined; // enemy player we are currently targeting
|
||||
this.defeated = {};
|
||||
};
|
||||
}
|
||||
|
||||
/** More initialisation for stuff that needs the gameState */
|
||||
PETRA.AttackManager.prototype.init = function(gameState)
|
||||
AttackManager.prototype.init = function(gameState)
|
||||
{
|
||||
this.outOfPlan = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", -1));
|
||||
this.outOfPlan.registerUpdates();
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.setRushes = function(allowed)
|
||||
AttackManager.prototype.setRushes = function(allowed)
|
||||
{
|
||||
if (this.Config.personality.aggressive > this.Config.personalityCut.strong && allowed > 2)
|
||||
{
|
||||
@@ -55,7 +58,7 @@ PETRA.AttackManager.prototype.setRushes = function(allowed)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
for (const evt of events.PlayerDefeated)
|
||||
this.defeated[evt.playerId] = true;
|
||||
@@ -73,7 +76,7 @@ PETRA.AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
for (const attack of this.upcomingAttacks[attackType])
|
||||
{
|
||||
if (attack.state === PETRA.AttackPlan.STATE_COMPLETING)
|
||||
if (attack.state === AttackPlan.STATE_COMPLETING)
|
||||
{
|
||||
if (attack.targetPlayer === targetPlayer)
|
||||
available += attack.unitCollection.length;
|
||||
@@ -95,7 +98,7 @@ PETRA.AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
for (const attack of this.upcomingAttacks[attackType])
|
||||
{
|
||||
if (attack.state === PETRA.AttackPlan.STATE_COMPLETING ||
|
||||
if (attack.state === AttackPlan.STATE_COMPLETING ||
|
||||
attack.targetPlayer !== targetPlayer ||
|
||||
attack.unitCollection.length < 3)
|
||||
continue;
|
||||
@@ -110,7 +113,7 @@ PETRA.AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
break; // take only the first attack request into account
|
||||
}
|
||||
if (targetPlayer !== undefined)
|
||||
PETRA.chatAnswerRequestAttack(gameState, targetPlayer, answer, other);
|
||||
chat.answerRequestAttack(gameState, targetPlayer, answer, other);
|
||||
|
||||
for (const evt of events.EntityRenamed) // take care of packing units in bombing attacks
|
||||
{
|
||||
@@ -133,7 +136,7 @@ PETRA.AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
/**
|
||||
* Check for any structure in range from within our territory, and bomb it
|
||||
*/
|
||||
PETRA.AttackManager.prototype.assignBombers = function(gameState)
|
||||
AttackManager.prototype.assignBombers = function(gameState)
|
||||
{
|
||||
// First some cleaning of current bombing attacks
|
||||
for (const [targetId, unitIds] of this.bombingAttacks)
|
||||
@@ -171,7 +174,8 @@ PETRA.AttackManager.prototype.assignBombers = function(gameState)
|
||||
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") != -1)
|
||||
{
|
||||
const subrole = ent.getMetadata(PlayerID, "subrole");
|
||||
if (subrole && (subrole === PETRA.Worker.SUBROLE_COMPLETING || subrole === PETRA.Worker.SUBROLE_WALKING || subrole === PETRA.Worker.SUBROLE_ATTACKING))
|
||||
if (subrole && (subrole === Worker.SUBROLE_COMPLETING ||
|
||||
subrole === Worker.SUBROLE_WALKING || subrole === Worker.SUBROLE_ATTACKING))
|
||||
continue;
|
||||
}
|
||||
let alreadyBombing = false;
|
||||
@@ -187,10 +191,10 @@ PETRA.AttackManager.prototype.assignBombers = function(gameState)
|
||||
|
||||
const range = ent.attackRange("Ranged").max;
|
||||
const entPos = ent.position();
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
for (const struct of gameState.getEnemyStructures().values())
|
||||
{
|
||||
if (!ent.canAttackTarget(struct, PETRA.allowCapture(gameState, ent, struct)))
|
||||
if (!ent.canAttackTarget(struct, allowCapture(gameState, ent, struct)))
|
||||
continue;
|
||||
|
||||
const structPos = struct.position();
|
||||
@@ -245,7 +249,7 @@ PETRA.AttackManager.prototype.assignBombers = function(gameState)
|
||||
* Some functions are run every turn
|
||||
* Others once in a while
|
||||
*/
|
||||
PETRA.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
AttackManager.prototype.update = function(gameState, queues, events)
|
||||
{
|
||||
if (this.Config.debug > 2 && gameState.ai.elapsedTime > this.debugTime + 60)
|
||||
{
|
||||
@@ -263,10 +267,10 @@ PETRA.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
|
||||
this.checkEvents(gameState, events);
|
||||
const unexecutedAttacks = {
|
||||
[PETRA.AttackPlan.TYPE_RUSH]: 0,
|
||||
[PETRA.AttackPlan.TYPE_RAID]: 0,
|
||||
[PETRA.AttackPlan.TYPE_DEFAULT]: 0,
|
||||
[PETRA.AttackPlan.TYPE_HUGE_ATTACK]: 0
|
||||
[AttackPlan.TYPE_RUSH]: 0,
|
||||
[AttackPlan.TYPE_RAID]: 0,
|
||||
[AttackPlan.TYPE_DEFAULT]: 0,
|
||||
[AttackPlan.TYPE_HUGE_ATTACK]: 0
|
||||
};
|
||||
for (const attackType in this.upcomingAttacks)
|
||||
{
|
||||
@@ -280,27 +284,27 @@ PETRA.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
|
||||
const updateStep = attack.updatePreparation(gameState);
|
||||
// now we're gonna check if the preparation time is over
|
||||
if (updateStep === PETRA.AttackPlan.PREPARATION_KEEP_GOING || attack.isPaused())
|
||||
if (updateStep === AttackPlan.PREPARATION_KEEP_GOING || attack.isPaused())
|
||||
{
|
||||
// just chillin'
|
||||
if (attack.state === PETRA.AttackPlan.STATE_UNEXECUTED)
|
||||
if (attack.state === AttackPlan.STATE_UNEXECUTED)
|
||||
++unexecutedAttacks[attackType];
|
||||
}
|
||||
else if (updateStep === PETRA.AttackPlan.PREPARATION_FAILED)
|
||||
else if (updateStep === AttackPlan.PREPARATION_FAILED)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("Attack Manager: " + attack.getType() + " plan " + attack.getName() + " aborted.");
|
||||
attack.Abort(gameState);
|
||||
this.upcomingAttacks[attackType].splice(i--, 1);
|
||||
}
|
||||
else if (updateStep === PETRA.AttackPlan.PREPARATION_START)
|
||||
else if (updateStep === AttackPlan.PREPARATION_START)
|
||||
{
|
||||
if (attack.StartAttack(gameState))
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("Attack Manager: Starting " + attack.getType() + " plan " + attack.getName());
|
||||
if (this.Config.chat)
|
||||
PETRA.chatLaunchAttack(gameState, attack.targetPlayer, attack.getType());
|
||||
chat.launchAttack(gameState, attack.targetPlayer, attack.getType());
|
||||
this.startedAttacks[attackType].push(attack);
|
||||
}
|
||||
else
|
||||
@@ -335,33 +339,39 @@ PETRA.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
const barracksNb = gameState.getOwnEntitiesByClass("Barracks", true).filter(API3.Filters.isBuilt()).length;
|
||||
if (this.rushNumber < this.maxRushes && barracksNb >= 1)
|
||||
{
|
||||
if (unexecutedAttacks[PETRA.AttackPlan.TYPE_RUSH] === 0)
|
||||
if (unexecutedAttacks[AttackPlan.TYPE_RUSH] === 0)
|
||||
{
|
||||
// we have a barracks and we want to rush, rush.
|
||||
const data = { "targetSize": this.rushSize[this.rushNumber] };
|
||||
const attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, PETRA.AttackPlan.TYPE_RUSH, data);
|
||||
const attackPlan = new AttackPlan(gameState, this.Config, this.totalNumber,
|
||||
AttackPlan.TYPE_RUSH, data);
|
||||
if (!attackPlan.failed)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("Military Manager: Rushing plan " + this.totalNumber + " with maxRushes " + this.maxRushes);
|
||||
this.totalNumber++;
|
||||
attackPlan.init(gameState);
|
||||
this.upcomingAttacks[PETRA.AttackPlan.TYPE_RUSH].push(attackPlan);
|
||||
this.upcomingAttacks[AttackPlan.TYPE_RUSH].push(attackPlan);
|
||||
}
|
||||
this.rushNumber++;
|
||||
}
|
||||
}
|
||||
else if (unexecutedAttacks[PETRA.AttackPlan.TYPE_DEFAULT] == 0 && unexecutedAttacks[PETRA.AttackPlan.TYPE_HUGE_ATTACK] == 0 &&
|
||||
this.startedAttacks[PETRA.AttackPlan.TYPE_DEFAULT].length + this.startedAttacks[PETRA.AttackPlan.TYPE_HUGE_ATTACK].length <
|
||||
else if (unexecutedAttacks[AttackPlan.TYPE_DEFAULT] == 0 &&
|
||||
unexecutedAttacks[AttackPlan.TYPE_HUGE_ATTACK] == 0 &&
|
||||
this.startedAttacks[AttackPlan.TYPE_DEFAULT].length +
|
||||
this.startedAttacks[AttackPlan.TYPE_HUGE_ATTACK].length <
|
||||
Math.min(2, 1 + Math.round(gameState.getPopulationMax() / 100)) &&
|
||||
(this.startedAttacks[PETRA.AttackPlan.TYPE_DEFAULT].length + this.startedAttacks[PETRA.AttackPlan.TYPE_HUGE_ATTACK].length == 0 ||
|
||||
(this.startedAttacks[AttackPlan.TYPE_DEFAULT].length +
|
||||
this.startedAttacks[AttackPlan.TYPE_HUGE_ATTACK].length == 0 ||
|
||||
gameState.getPopulationMax() - gameState.getPopulation() > 12))
|
||||
{
|
||||
if (barracksNb >= 1 && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.getPhaseName(2))) ||
|
||||
!gameState.ai.HQ.hasPotentialBase()) // if we have no base ... nothing else to do than attack
|
||||
{
|
||||
const type = this.attackNumber < 2 || this.startedAttacks[PETRA.AttackPlan.TYPE_HUGE_ATTACK].length > 0 ? PETRA.AttackPlan.TYPE_DEFAULT : PETRA.AttackPlan.TYPE_HUGE_ATTACK;
|
||||
const attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, type);
|
||||
const type = this.attackNumber < 2 ||
|
||||
this.startedAttacks[AttackPlan.TYPE_HUGE_ATTACK].length > 0 ?
|
||||
AttackPlan.TYPE_DEFAULT : AttackPlan.TYPE_HUGE_ATTACK;
|
||||
const attackPlan = new AttackPlan(gameState, this.Config, this.totalNumber, type);
|
||||
if (attackPlan.failed)
|
||||
this.attackPlansEncounteredWater = true; // hack
|
||||
else
|
||||
@@ -376,7 +386,8 @@ PETRA.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
}
|
||||
}
|
||||
|
||||
if (unexecutedAttacks[PETRA.AttackPlan.TYPE_RAID] === 0 && gameState.ai.HQ.defenseManager.targetList.length)
|
||||
if (unexecutedAttacks[AttackPlan.TYPE_RAID] === 0 &&
|
||||
gameState.ai.HQ.defenseManager.targetList.length)
|
||||
{
|
||||
let target;
|
||||
for (const targetId of gameState.ai.HQ.defenseManager.targetList)
|
||||
@@ -393,11 +404,11 @@ PETRA.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
}
|
||||
|
||||
// Check if we have some unused ranged siege unit which could do something useful while waiting
|
||||
if (this.Config.difficulty > PETRA.DIFFICULTY_VERY_EASY && gameState.ai.playedTurn % 5 == 0)
|
||||
if (this.Config.difficulty > DIFFICULTY_VERY_EASY && gameState.ai.playedTurn % 5 == 0)
|
||||
this.assignBombers(gameState);
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.getPlan = function(planName)
|
||||
AttackManager.prototype.getPlan = function(planName)
|
||||
{
|
||||
for (const attackType in this.upcomingAttacks)
|
||||
{
|
||||
@@ -414,21 +425,21 @@ PETRA.AttackManager.prototype.getPlan = function(planName)
|
||||
return undefined;
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.pausePlan = function(planName)
|
||||
AttackManager.prototype.pausePlan = function(planName)
|
||||
{
|
||||
const attack = this.getPlan(planName);
|
||||
if (attack)
|
||||
attack.setPaused(true);
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.unpausePlan = function(planName)
|
||||
AttackManager.prototype.unpausePlan = function(planName)
|
||||
{
|
||||
const attack = this.getPlan(planName);
|
||||
if (attack)
|
||||
attack.setPaused(false);
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.pauseAllPlans = function()
|
||||
AttackManager.prototype.pauseAllPlans = function()
|
||||
{
|
||||
for (const attackType in this.upcomingAttacks)
|
||||
for (const attack of this.upcomingAttacks[attackType])
|
||||
@@ -439,7 +450,7 @@ PETRA.AttackManager.prototype.pauseAllPlans = function()
|
||||
attack.setPaused(true);
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.unpauseAllPlans = function()
|
||||
AttackManager.prototype.unpauseAllPlans = function()
|
||||
{
|
||||
for (const attackType in this.upcomingAttacks)
|
||||
for (const attack of this.upcomingAttacks[attackType])
|
||||
@@ -450,7 +461,7 @@ PETRA.AttackManager.prototype.unpauseAllPlans = function()
|
||||
attack.setPaused(false);
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.getAttackInPreparation = function(type)
|
||||
AttackManager.prototype.getAttackInPreparation = function(type)
|
||||
{
|
||||
return this.upcomingAttacks[type].length ? this.upcomingAttacks[type][0] : undefined;
|
||||
};
|
||||
@@ -460,7 +471,7 @@ PETRA.AttackManager.prototype.getAttackInPreparation = function(type)
|
||||
* attack.targetPlayer is undefined and in that case, we keep track of the chosen target
|
||||
* for future attacks.
|
||||
*/
|
||||
PETRA.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
|
||||
AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
|
||||
{
|
||||
let enemyPlayer;
|
||||
|
||||
@@ -481,7 +492,7 @@ PETRA.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
|
||||
for (const i in this.defeated)
|
||||
veto[i] = true;
|
||||
// No rush if enemy too well defended (i.e. iberians)
|
||||
if (attack.type === PETRA.AttackPlan.TYPE_RUSH)
|
||||
if (attack.type === AttackPlan.TYPE_RUSH)
|
||||
{
|
||||
for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
|
||||
{
|
||||
@@ -500,7 +511,7 @@ PETRA.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 !== PETRA.AttackPlan.TYPE_HUGE_ATTACK)
|
||||
if (attack.type !== AttackPlan.TYPE_HUGE_ATTACK)
|
||||
{
|
||||
if (attack.targetPlayer === undefined && this.currentEnemyPlayer !== undefined &&
|
||||
!this.defeated[this.currentEnemyPlayer] &&
|
||||
@@ -516,14 +527,14 @@ PETRA.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
|
||||
if (ourcc.owner() != PlayerID)
|
||||
continue;
|
||||
const ourPos = ourcc.position();
|
||||
const access = PETRA.getLandAccess(gameState, ourcc);
|
||||
const access = getLandAccess(gameState, ourcc);
|
||||
for (const enemycc of ccEnts.values())
|
||||
{
|
||||
if (veto[enemycc.owner()])
|
||||
continue;
|
||||
if (!gameState.isPlayerEnemy(enemycc.owner()))
|
||||
continue;
|
||||
if (access !== PETRA.getLandAccess(gameState, enemycc))
|
||||
if (access !== getLandAccess(gameState, enemycc))
|
||||
continue;
|
||||
const dist = API3.SquareVectorDistance(ourPos, enemycc.position());
|
||||
if (distmin && dist > distmin)
|
||||
@@ -574,7 +585,7 @@ PETRA.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
|
||||
* Target the player with the most advanced wonder.
|
||||
* TODO currently the first built wonder is kept, should chek on the minimum wonderDuration left instead.
|
||||
*/
|
||||
PETRA.AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack)
|
||||
AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack)
|
||||
{
|
||||
let enemyPlayer;
|
||||
let enemyWonder;
|
||||
@@ -606,7 +617,7 @@ PETRA.AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack)
|
||||
/**
|
||||
* Target the player with the most relics (including gaia).
|
||||
*/
|
||||
PETRA.AttackManager.prototype.getRelicEnemyPlayer = function(gameState, attack)
|
||||
AttackManager.prototype.getRelicEnemyPlayer = function(gameState, attack)
|
||||
{
|
||||
let enemyPlayer;
|
||||
const allRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic"));
|
||||
@@ -634,7 +645,7 @@ PETRA.AttackManager.prototype.getRelicEnemyPlayer = function(gameState, attack)
|
||||
};
|
||||
|
||||
/** f.e. if we have changed diplomacy with another player. */
|
||||
PETRA.AttackManager.prototype.cancelAttacksAgainstPlayer = function(gameState, player)
|
||||
AttackManager.prototype.cancelAttacksAgainstPlayer = function(gameState, player)
|
||||
{
|
||||
for (const attackType in this.upcomingAttacks)
|
||||
for (const attack of this.upcomingAttacks[attackType])
|
||||
@@ -653,10 +664,11 @@ PETRA.AttackManager.prototype.cancelAttacksAgainstPlayer = function(gameState, p
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.raidTargetEntity = function(gameState, ent)
|
||||
AttackManager.prototype.raidTargetEntity = function(gameState, ent)
|
||||
{
|
||||
const data = { "target": ent };
|
||||
const attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, PETRA.AttackPlan.TYPE_RAID, data);
|
||||
const attackPlan = new AttackPlan(gameState, this.Config, this.totalNumber,
|
||||
AttackPlan.TYPE_RAID, data);
|
||||
if (attackPlan.failed)
|
||||
return null;
|
||||
if (this.Config.debug > 1)
|
||||
@@ -664,14 +676,14 @@ PETRA.AttackManager.prototype.raidTargetEntity = function(gameState, ent)
|
||||
this.raidNumber++;
|
||||
this.totalNumber++;
|
||||
attackPlan.init(gameState);
|
||||
this.upcomingAttacks[PETRA.AttackPlan.TYPE_RAID].push(attackPlan);
|
||||
this.upcomingAttacks[AttackPlan.TYPE_RAID].push(attackPlan);
|
||||
return attackPlan;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the number of units from any of our attacking armies around this position
|
||||
*/
|
||||
PETRA.AttackManager.prototype.numAttackingUnitsAround = function(pos, dist)
|
||||
AttackManager.prototype.numAttackingUnitsAround = function(pos, dist)
|
||||
{
|
||||
let num = 0;
|
||||
for (const attackType in this.startedAttacks)
|
||||
@@ -691,7 +703,7 @@ PETRA.AttackManager.prototype.numAttackingUnitsAround = function(pos, dist)
|
||||
* data.armyID: transform only the defense army ID into a new attack
|
||||
* data.uniqueTarget: the attack will stop when the target is destroyed or captured
|
||||
*/
|
||||
PETRA.AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data)
|
||||
AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data)
|
||||
{
|
||||
if (!target || !target.position())
|
||||
return false;
|
||||
@@ -702,15 +714,15 @@ PETRA.AttackManager.prototype.switchDefenseToAttack = function(gameState, target
|
||||
}
|
||||
const attackData = data.uniqueTarget ? { "uniqueTargetId": target.id() } : undefined;
|
||||
const pos = target.position();
|
||||
const attackType = PETRA.AttackPlan.TYPE_DEFAULT;
|
||||
const attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, attackType, attackData);
|
||||
const attackType = AttackPlan.TYPE_DEFAULT;
|
||||
const attackPlan = new AttackPlan(gameState, this.Config, this.totalNumber, attackType, attackData);
|
||||
if (attackPlan.failed)
|
||||
return false;
|
||||
this.totalNumber++;
|
||||
attackPlan.init(gameState);
|
||||
this.startedAttacks[attackType].push(attackPlan);
|
||||
|
||||
const targetAccess = PETRA.getLandAccess(gameState, target);
|
||||
const targetAccess = getLandAccess(gameState, target);
|
||||
for (const army of gameState.ai.HQ.defenseManager.armies)
|
||||
{
|
||||
if (data.range)
|
||||
@@ -730,11 +742,11 @@ PETRA.AttackManager.prototype.switchDefenseToAttack = function(gameState, target
|
||||
army.removeOwn(gameState, unitId);
|
||||
const unit = gameState.getEntityById(unitId);
|
||||
const accessOk = unit.getMetadata(PlayerID, "transport") !== undefined ||
|
||||
unit.position() && PETRA.getLandAccess(gameState, unit) == targetAccess;
|
||||
unit.position() && getLandAccess(gameState, unit) == targetAccess;
|
||||
if (unit && accessOk && attackPlan.isAvailableUnit(gameState, unit))
|
||||
{
|
||||
unit.setMetadata(PlayerID, "plan", attackPlan.name);
|
||||
unit.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_ATTACK);
|
||||
unit.setMetadata(PlayerID, "role", Worker.ROLE_ATTACK);
|
||||
attackPlan.unitCollection.updateEnt(unit);
|
||||
}
|
||||
}
|
||||
@@ -745,15 +757,15 @@ PETRA.AttackManager.prototype.switchDefenseToAttack = function(gameState, target
|
||||
return false;
|
||||
}
|
||||
for (const unit of attackPlan.unitCollection.values())
|
||||
unit.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_ATTACK);
|
||||
unit.setMetadata(PlayerID, "role", Worker.ROLE_ATTACK);
|
||||
attackPlan.targetPlayer = target.owner();
|
||||
attackPlan.targetPos = pos;
|
||||
attackPlan.target = target;
|
||||
attackPlan.state = PETRA.AttackPlan.STATE_ARRIVED;
|
||||
attackPlan.state = AttackPlan.STATE_ARRIVED;
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.Serialize = function()
|
||||
AttackManager.prototype.Serialize = function()
|
||||
{
|
||||
const properties = {
|
||||
"totalNumber": this.totalNumber,
|
||||
@@ -786,7 +798,7 @@ PETRA.AttackManager.prototype.Serialize = function()
|
||||
return { "properties": properties, "upcomingAttacks": upcomingAttacks, "startedAttacks": startedAttacks };
|
||||
};
|
||||
|
||||
PETRA.AttackManager.prototype.Deserialize = function(gameState, data)
|
||||
AttackManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data.properties)
|
||||
this[key] = data.properties[key];
|
||||
@@ -797,7 +809,7 @@ PETRA.AttackManager.prototype.Deserialize = function(gameState, data)
|
||||
this.upcomingAttacks[key] = [];
|
||||
for (const dataAttack of data.upcomingAttacks[key])
|
||||
{
|
||||
const attack = new PETRA.AttackPlan(gameState, this.Config, dataAttack.properties.name);
|
||||
const attack = new AttackPlan(gameState, this.Config, dataAttack.properties.name);
|
||||
attack.Deserialize(gameState, dataAttack);
|
||||
attack.init(gameState);
|
||||
this.upcomingAttacks[key].push(attack);
|
||||
@@ -810,7 +822,7 @@ PETRA.AttackManager.prototype.Deserialize = function(gameState, data)
|
||||
this.startedAttacks[key] = [];
|
||||
for (const dataAttack of data.startedAttacks[key])
|
||||
{
|
||||
const attack = new PETRA.AttackPlan(gameState, this.Config, dataAttack.properties.name);
|
||||
const attack = new AttackPlan(gameState, this.Config, dataAttack.properties.name);
|
||||
attack.Deserialize(gameState, dataAttack);
|
||||
attack.init(gameState);
|
||||
this.startedAttacks[key].push(attack);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,10 @@
|
||||
import { Config, DIFFICULTY_EASY, DIFFICULTY_MEDIUM } from "simulation/ai/petra/config.js";
|
||||
import { getBestBase, getBuiltEntity, getLandAccess, isFastMoving, isNotWorthBuilding } from
|
||||
"simulation/ai/petra/entityExtend.js";
|
||||
import { createObstructionMap } from "simulation/ai/petra/mapModule.js";
|
||||
import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Base Manager
|
||||
* Handles lower level economic stuffs.
|
||||
@@ -10,7 +17,7 @@
|
||||
* -updating whatever needs updating, keeping track of stuffs (rebuilding needs…)
|
||||
*/
|
||||
|
||||
PETRA.BaseManager = function(gameState, basesManager)
|
||||
export function BaseManager(gameState, basesManager)
|
||||
{
|
||||
this.Config = basesManager.Config;
|
||||
this.ID = gameState.ai.uniqueIDs.bases++;
|
||||
@@ -27,42 +34,42 @@ PETRA.BaseManager = function(gameState, basesManager)
|
||||
|
||||
this.constructing = false;
|
||||
// Defenders to train in this cc when its construction is finished
|
||||
this.neededDefenders = this.Config.difficulty > PETRA.DIFFICULTY_EASY ? 3 + 2*(this.Config.difficulty - 3) : 0;
|
||||
this.neededDefenders = this.Config.difficulty > DIFFICULTY_EASY ? 3 + 2*(this.Config.difficulty - 3) : 0;
|
||||
|
||||
// vector for iterating, to check one use the HQ map.
|
||||
this.territoryIndices = [];
|
||||
|
||||
this.timeNextIdleCheck = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
PETRA.BaseManager.STATE_WITH_ANCHOR = "anchored";
|
||||
BaseManager.STATE_WITH_ANCHOR = "anchored";
|
||||
|
||||
/**
|
||||
* New base with a foundation anchor.
|
||||
*/
|
||||
PETRA.BaseManager.STATE_UNCONSTRUCTED = "unconstructed";
|
||||
BaseManager.STATE_UNCONSTRUCTED = "unconstructed";
|
||||
|
||||
/**
|
||||
* Captured base with an anchor.
|
||||
*/
|
||||
PETRA.BaseManager.STATE_CAPTURED = "captured";
|
||||
BaseManager.STATE_CAPTURED = "captured";
|
||||
|
||||
/**
|
||||
* Anchorless base, currently with dock.
|
||||
*/
|
||||
PETRA.BaseManager.STATE_ANCHORLESS = "anchorless";
|
||||
BaseManager.STATE_ANCHORLESS = "anchorless";
|
||||
|
||||
PETRA.BaseManager.prototype.init = function(gameState, state)
|
||||
BaseManager.prototype.init = function(gameState, state)
|
||||
{
|
||||
if (state === PETRA.BaseManager.STATE_UNCONSTRUCTED)
|
||||
if (state === BaseManager.STATE_UNCONSTRUCTED)
|
||||
this.constructing = true;
|
||||
else if (state !== PETRA.BaseManager.STATE_CAPTURED)
|
||||
else if (state !== BaseManager.STATE_CAPTURED)
|
||||
this.neededDefenders = 0;
|
||||
this.workerObject = new PETRA.Worker(this);
|
||||
this.workerObject = new Worker(this);
|
||||
// entitycollections
|
||||
this.units = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID));
|
||||
this.workers = this.units.filter(API3.Filters.byMetadata(PlayerID, "role", PETRA.Worker.ROLE_WORKER));
|
||||
this.workers = this.units.filter(API3.Filters.byMetadata(PlayerID, "role", Worker.ROLE_WORKER));
|
||||
this.buildings = gameState.getOwnStructures().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID));
|
||||
this.mobileDropsites = this.units.filter(API3.Filters.isDropsite());
|
||||
|
||||
@@ -82,19 +89,19 @@ PETRA.BaseManager.prototype.init = function(gameState, state)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.reset = function(gameState, state)
|
||||
BaseManager.prototype.reset = function(gameState, state)
|
||||
{
|
||||
if (state === PETRA.BaseManager.STATE_UNCONSTRUCTED)
|
||||
if (state === BaseManager.STATE_UNCONSTRUCTED)
|
||||
this.constructing = true;
|
||||
else
|
||||
this.constructing = false;
|
||||
if (state !== PETRA.BaseManager.STATE_CAPTURED || this.Config.difficulty < PETRA.DIFFICULTY_MEDIUM)
|
||||
if (state !== BaseManager.STATE_CAPTURED || this.Config.difficulty < DIFFICULTY_MEDIUM)
|
||||
this.neededDefenders = 0;
|
||||
else
|
||||
this.neededDefenders = 3 + 2 * (this.Config.difficulty - 3);
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.assignEntity = function(gameState, ent)
|
||||
BaseManager.prototype.assignEntity = function(gameState, ent)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "base", this.ID);
|
||||
this.units.updateEnt(ent);
|
||||
@@ -104,7 +111,7 @@ PETRA.BaseManager.prototype.assignEntity = function(gameState, ent)
|
||||
this.assignResourceToDropsite(gameState, ent);
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.setAnchor = function(gameState, anchorEntity)
|
||||
BaseManager.prototype.setAnchor = function(gameState, anchorEntity)
|
||||
{
|
||||
if (!anchorEntity.hasClass("CivCentre"))
|
||||
API3.warn("Error: Petra base " + this.ID + " has been assigned " + ent.templateName() + " as anchor.");
|
||||
@@ -117,12 +124,12 @@ PETRA.BaseManager.prototype.setAnchor = function(gameState, anchorEntity)
|
||||
}
|
||||
anchorEntity.setMetadata(PlayerID, "base", this.ID);
|
||||
this.buildings.updateEnt(anchorEntity);
|
||||
this.accessIndex = PETRA.getLandAccess(gameState, anchorEntity);
|
||||
this.accessIndex = getLandAccess(gameState, anchorEntity);
|
||||
return true;
|
||||
};
|
||||
|
||||
/* we lost our anchor. Let's reassign our units and buildings */
|
||||
PETRA.BaseManager.prototype.anchorLost = function(gameState, ent)
|
||||
BaseManager.prototype.anchorLost = function(gameState, ent)
|
||||
{
|
||||
this.anchor = undefined;
|
||||
this.anchorId = undefined;
|
||||
@@ -131,17 +138,18 @@ PETRA.BaseManager.prototype.anchorLost = function(gameState, ent)
|
||||
};
|
||||
|
||||
/** Set a building of an anchorless base */
|
||||
PETRA.BaseManager.prototype.setAnchorlessEntity = function(gameState, ent)
|
||||
BaseManager.prototype.setAnchorlessEntity = function(gameState, ent)
|
||||
{
|
||||
if (!this.buildings.hasEntities())
|
||||
{
|
||||
if (!PETRA.getBuiltEntity(gameState, ent).resourceDropsiteTypes())
|
||||
if (!getBuiltEntity(gameState, ent).resourceDropsiteTypes())
|
||||
API3.warn("Error: Petra base " + this.ID + " has been assigned " + ent.templateName() + " as origin.");
|
||||
this.accessIndex = PETRA.getLandAccess(gameState, ent);
|
||||
this.accessIndex = getLandAccess(gameState, ent);
|
||||
}
|
||||
else if (this.accessIndex !== PETRA.getLandAccess(gameState, ent))
|
||||
else if (this.accessIndex !== getLandAccess(gameState, ent))
|
||||
API3.warn(" Error: Petra base " + this.ID + " with access " + this.accessIndex +
|
||||
" has been assigned " + ent.templateName() + " with access" + PETRA.getLandAccess(gameState, ent));
|
||||
" has been assigned " + ent.templateName() + " with access" +
|
||||
getLandAccess(gameState, ent));
|
||||
|
||||
ent.setMetadata(PlayerID, "base", this.ID);
|
||||
this.buildings.updateEnt(ent);
|
||||
@@ -152,7 +160,7 @@ PETRA.BaseManager.prototype.setAnchorlessEntity = function(gameState, ent)
|
||||
* Assign the resources around the dropsites of this basis in three areas according to distance, and sort them in each area.
|
||||
* Moving resources (animals) and buildable resources (fields) are treated elsewhere.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.assignResourceToDropsite = function(gameState, dropsite)
|
||||
BaseManager.prototype.assignResourceToDropsite = function(gameState, dropsite)
|
||||
{
|
||||
if (this.dropsites[dropsite.id()])
|
||||
{
|
||||
@@ -167,7 +175,7 @@ PETRA.BaseManager.prototype.assignResourceToDropsite = function(gameState, drops
|
||||
this.dropsites[dropsiteId] = true;
|
||||
|
||||
if (this.ID == this.basesManager.baselessBase().ID)
|
||||
accessIndex = PETRA.getLandAccess(gameState, dropsite);
|
||||
accessIndex = getLandAccess(gameState, dropsite);
|
||||
|
||||
const maxDistResourceSquare = this.maxDistResourceSquare;
|
||||
for (const type of dropsite.resourceDropsiteTypes())
|
||||
@@ -188,7 +196,7 @@ PETRA.BaseManager.prototype.assignResourceToDropsite = function(gameState, drops
|
||||
if (supply.hasClasses(["Animal", "Field"]))
|
||||
return;
|
||||
// quick accessibility check
|
||||
if (PETRA.getLandAccess(gameState, supply) != accessIndex)
|
||||
if (getLandAccess(gameState, supply) != accessIndex)
|
||||
return;
|
||||
|
||||
const dist = API3.SquareVectorDistance(supply.position(), dropsitePos);
|
||||
@@ -233,7 +241,7 @@ PETRA.BaseManager.prototype.assignResourceToDropsite = function(gameState, drops
|
||||
});
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.removeFromAssignedDropsite = function(ent)
|
||||
BaseManager.prototype.removeFromAssignedDropsite = function(ent)
|
||||
{
|
||||
for (const type in this.dropsiteSupplies)
|
||||
for (const proxim in this.dropsiteSupplies[type])
|
||||
@@ -246,7 +254,7 @@ PETRA.BaseManager.prototype.removeFromAssignedDropsite = function(ent)
|
||||
};
|
||||
|
||||
// completely remove the dropsite resources from our list.
|
||||
PETRA.BaseManager.prototype.removeDropsite = function(gameState, ent)
|
||||
BaseManager.prototype.removeDropsite = function(gameState, ent)
|
||||
{
|
||||
if (!ent.id())
|
||||
return;
|
||||
@@ -277,7 +285,7 @@ PETRA.BaseManager.prototype.removeDropsite = function(gameState, ent)
|
||||
* @return {Object} - The position of the best place to build a new dropsite for the specified resource,
|
||||
* its quality and its template name.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.findBestDropsiteAndLocation = function(gameState, resource)
|
||||
BaseManager.prototype.findBestDropsiteAndLocation = function(gameState, resource)
|
||||
{
|
||||
let bestResult = {
|
||||
"quality": 0,
|
||||
@@ -297,7 +305,7 @@ PETRA.BaseManager.prototype.findBestDropsiteAndLocation = function(gameState, re
|
||||
/**
|
||||
* Returns the position of the best place to build a new dropsite for the specified resource and dropsite template.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource, templateName)
|
||||
BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource, templateName)
|
||||
{
|
||||
const template = gameState.getTemplate(gameState.applyCiv(templateName));
|
||||
|
||||
@@ -316,13 +324,13 @@ PETRA.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resou
|
||||
// Then checks for a good spot in the territory. If none, and town/city phase, checks outside
|
||||
// The AI will currently not build a CC if it wouldn't connect with an existing CC.
|
||||
|
||||
const obstructions = PETRA.createObstructionMap(gameState, this.accessIndex, template);
|
||||
const obstructions = createObstructionMap(gameState, this.accessIndex, template);
|
||||
|
||||
const dpEnts = gameState.getOwnStructures().filter(API3.Filters.isDropsite(resource)).toEntityArray();
|
||||
|
||||
// Foundations don't have the dropsite properties yet, so treat them separately.
|
||||
for (const foundation of gameState.getOwnFoundations().toEntityArray())
|
||||
if (PETRA.getBuiltEntity(gameState, foundation).isResourceDropsite(resource))
|
||||
if (getBuiltEntity(gameState, foundation).isResourceDropsite(resource))
|
||||
dpEnts.push(foundation);
|
||||
|
||||
let bestIdx;
|
||||
@@ -387,7 +395,7 @@ PETRA.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resou
|
||||
return { "quality": bestVal, "pos": [x, z] };
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.getResourceLevel = function(gameState, type, distances = ["nearby", "medium", "faraway"])
|
||||
BaseManager.prototype.getResourceLevel = function(gameState, type, distances = ["nearby", "medium", "faraway"])
|
||||
{
|
||||
let count = 0;
|
||||
const check = {};
|
||||
@@ -403,7 +411,7 @@ PETRA.BaseManager.prototype.getResourceLevel = function(gameState, type, distanc
|
||||
};
|
||||
|
||||
/** check our resource levels and react accordingly */
|
||||
PETRA.BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
{
|
||||
for (const type of Resources.GetCodes())
|
||||
{
|
||||
@@ -423,7 +431,8 @@ PETRA.BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
{
|
||||
if (count < 600)
|
||||
{
|
||||
queues.field.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/field", { "favoredBase": this.ID }));
|
||||
queues.field.addPlan(new ConstructionPlan(gameState,
|
||||
"structures/{civ}/field", { "favoredBase": this.ID }));
|
||||
gameState.ai.HQ.needFarm = true;
|
||||
}
|
||||
}
|
||||
@@ -434,11 +443,19 @@ PETRA.BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
if (gameState.ai.HQ.saveResources || gameState.ai.HQ.saveSpace || count > 300 || numFarms > 5)
|
||||
goal = Math.max(goal-1, 1);
|
||||
if (numFound + numQueue < goal)
|
||||
queues.field.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/field", { "favoredBase": this.ID }));
|
||||
{
|
||||
queues.field.addPlan(new ConstructionPlan(gameState,
|
||||
"structures/{civ}/field", { "favoredBase": this.ID }));
|
||||
}
|
||||
}
|
||||
else if (gameState.ai.HQ.needCorral &&
|
||||
!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() &&
|
||||
!queues.corral.hasQueuedUnits() &&
|
||||
gameState.ai.HQ.canBuild(gameState, "structures/{civ}/corral"))
|
||||
{
|
||||
queues.corral.addPlan(new ConstructionPlan(gameState,
|
||||
"structures/{civ}/corral", { "favoredBase": this.ID }));
|
||||
}
|
||||
else if (gameState.ai.HQ.needCorral && !gameState.getOwnEntitiesByClass("Corral", true).hasEntities() &&
|
||||
!queues.corral.hasQueuedUnits() && gameState.ai.HQ.canBuild(gameState, "structures/{civ}/corral"))
|
||||
queues.corral.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/corral", { "favoredBase": this.ID }));
|
||||
continue;
|
||||
}
|
||||
if (!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() &&
|
||||
@@ -447,7 +464,8 @@ PETRA.BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
const count = this.getResourceLevel(gameState, type, prox); // animals are not accounted
|
||||
if (count < 900)
|
||||
{
|
||||
queues.corral.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/corral", { "favoredBase": this.ID }));
|
||||
queues.corral.addPlan(new ConstructionPlan(gameState,
|
||||
"structures/{civ}/corral", { "favoredBase": this.ID }));
|
||||
gameState.ai.HQ.needCorral = true;
|
||||
}
|
||||
}
|
||||
@@ -480,15 +498,22 @@ PETRA.BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
{
|
||||
const newDP = this.findBestDropsiteAndLocation(gameState, type);
|
||||
if (newDP.quality > 50 && gameState.ai.HQ.canBuild(gameState, newDP.templateName))
|
||||
queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.ID, "type": type }, newDP.pos));
|
||||
{
|
||||
queues.dropsites.addPlan(new ConstructionPlan(gameState, newDP.templateName,
|
||||
{ "base": this.ID, "type": type }, newDP.pos));
|
||||
}
|
||||
else if (!gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre")).hasEntities() && !queues.civilCentre.hasQueuedUnits())
|
||||
{
|
||||
// No good dropsite, try to build a new base if no base already planned,
|
||||
// and if not possible, be less strict on dropsite quality.
|
||||
if ((!gameState.ai.HQ.canExpand || !gameState.ai.HQ.buildNewBase(gameState, queues, type)) &&
|
||||
newDP.quality > Math.min(25, 50*0.15/ratio) &&
|
||||
gameState.ai.HQ.canBuild(gameState, newDP.templateName))
|
||||
queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.ID, "type": type }, newDP.pos));
|
||||
newDP.quality > Math.min(25, 50*0.15/ratio) &&
|
||||
gameState.ai.HQ.canBuild(gameState, newDP.templateName))
|
||||
{
|
||||
queues.dropsites.addPlan(new ConstructionPlan(gameState,
|
||||
newDP.templateName, { "base": this.ID, "type": type },
|
||||
newDP.pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.gatherers[type].nextCheck = gameState.ai.playedTurn + 20;
|
||||
@@ -502,7 +527,7 @@ PETRA.BaseManager.prototype.checkResourceLevels = function(gameState, queues)
|
||||
};
|
||||
|
||||
/** Adds the estimated gather rates from this base to the currentRates */
|
||||
PETRA.BaseManager.prototype.addGatherRates = function(gameState, currentRates)
|
||||
BaseManager.prototype.addGatherRates = function(gameState, currentRates)
|
||||
{
|
||||
for (const res in currentRates)
|
||||
{
|
||||
@@ -521,14 +546,14 @@ PETRA.BaseManager.prototype.addGatherRates = function(gameState, currentRates)
|
||||
});
|
||||
if (res == "food")
|
||||
{
|
||||
this.workersBySubrole(gameState, PETRA.Worker.SUBROLE_HUNTER).forEach(ent => {
|
||||
this.workersBySubrole(gameState, Worker.SUBROLE_HUNTER).forEach(ent => {
|
||||
if (ent.isIdle() || !ent.position())
|
||||
return;
|
||||
const gRate = ent.currentGatherRate();
|
||||
if (gRate)
|
||||
currentRates[res] += Math.log(1+gRate)/1.1;
|
||||
});
|
||||
this.workersBySubrole(gameState, PETRA.Worker.SUBROLE_FISHER).forEach(ent => {
|
||||
this.workersBySubrole(gameState, Worker.SUBROLE_FISHER).forEach(ent => {
|
||||
if (ent.isIdle() || !ent.position())
|
||||
return;
|
||||
const gRate = ent.currentGatherRate();
|
||||
@@ -539,7 +564,7 @@ PETRA.BaseManager.prototype.addGatherRates = function(gameState, currentRates)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.assignRolelessUnits = function(gameState, roleless)
|
||||
BaseManager.prototype.assignRolelessUnits = function(gameState, roleless)
|
||||
{
|
||||
if (!roleless)
|
||||
roleless = this.units.filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "role"))).values();
|
||||
@@ -547,9 +572,9 @@ PETRA.BaseManager.prototype.assignRolelessUnits = function(gameState, roleless)
|
||||
for (const ent of roleless)
|
||||
{
|
||||
if (ent.hasClasses(["Worker", "CitizenSoldier", "FishingBoat"]))
|
||||
ent.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_WORKER);
|
||||
ent.setMetadata(PlayerID, "role", Worker.ROLE_WORKER);
|
||||
else if (ent.hasClass("Support") && ent.hasClass("Elephant"))
|
||||
ent.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_WORKER);
|
||||
ent.setMetadata(PlayerID, "role", Worker.ROLE_WORKER);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -558,7 +583,7 @@ PETRA.BaseManager.prototype.assignRolelessUnits = function(gameState, roleless)
|
||||
* they can be reassigned by reassignIdleWorkers.
|
||||
* TODO: actually this probably should be in the HQ.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.setWorkersIdleByPriority = function(gameState)
|
||||
BaseManager.prototype.setWorkersIdleByPriority = function(gameState)
|
||||
{
|
||||
this.timeNextIdleCheck = gameState.ai.elapsedTime + 8;
|
||||
// change resource only towards one which is more needed, and if changing will not change this order
|
||||
@@ -614,7 +639,7 @@ PETRA.BaseManager.prototype.setWorkersIdleByPriority = function(gameState)
|
||||
* and return remaining number of possible switches.
|
||||
* Prefer FemaleCitizen for food and CitizenSoldier for other resources.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.switchGatherer = function(gameState, from, to, number)
|
||||
BaseManager.prototype.switchGatherer = function(gameState, from, to, number)
|
||||
{
|
||||
let num = number;
|
||||
let only;
|
||||
@@ -640,12 +665,12 @@ PETRA.BaseManager.prototype.switchGatherer = function(gameState, from, to, numbe
|
||||
return num;
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorkers)
|
||||
BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorkers)
|
||||
{
|
||||
// Search for idle workers, and tell them to gather resources based on demand
|
||||
if (!idleWorkers)
|
||||
{
|
||||
const filter = API3.Filters.byMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
const filter = API3.Filters.byMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
idleWorkers = gameState.updatingCollection("idle-workers-base-" + this.ID, filter, this.workers).values();
|
||||
}
|
||||
|
||||
@@ -657,7 +682,7 @@ PETRA.BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorker
|
||||
// Support elephant can only be builders
|
||||
if (ent.hasClass("Support") && ent.hasClass("Elephant"))
|
||||
{
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -679,35 +704,37 @@ PETRA.BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorker
|
||||
continue;
|
||||
if (needed.type != "food" && this.basesManager.isResourceExhausted(needed.type))
|
||||
continue;
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_GATHERER);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_GATHERER);
|
||||
ent.setMetadata(PlayerID, "gather-type", needed.type);
|
||||
this.basesManager.AddTCResGatherer(needed.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PETRA.isFastMoving(ent) && ent.canGather("food") && ent.canAttackClass("Animal"))
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_HUNTER);
|
||||
else if (isFastMoving(ent) && ent.canGather("food") && ent.canAttackClass("Animal"))
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_HUNTER);
|
||||
else if (ent.hasClass("FishingBoat"))
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_FISHER);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_FISHER);
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.workersBySubrole = function(gameState, subrole)
|
||||
BaseManager.prototype.workersBySubrole = function(gameState, subrole)
|
||||
{
|
||||
return gameState.updatingCollection("subrole-" + subrole +"-base-" + this.ID, API3.Filters.byMetadata(PlayerID, "subrole", subrole), this.workers);
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.gatherersByType = function(gameState, type)
|
||||
BaseManager.prototype.gatherersByType = function(gameState, type)
|
||||
{
|
||||
return gameState.updatingCollection("workers-gathering-" + type +"-base-" + this.ID, API3.Filters.byMetadata(PlayerID, "gather-type", type), this.workersBySubrole(gameState, PETRA.Worker.SUBROLE_GATHERER));
|
||||
return gameState.updatingCollection("workers-gathering-" + type +"-base-" +
|
||||
this.ID, API3.Filters.byMetadata(PlayerID, "gather-type", type),
|
||||
this.workersBySubrole(gameState, Worker.SUBROLE_GATHERER));
|
||||
};
|
||||
|
||||
/**
|
||||
* returns an entity collection of workers.
|
||||
* They are idled immediatly and their subrole set to idle.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
|
||||
BaseManager.prototype.pickBuilders = function(gameState, workers, number)
|
||||
{
|
||||
const availableWorkers = this.workers.filter(ent => {
|
||||
if (!ent.position() || !ent.isBuilder())
|
||||
@@ -721,13 +748,13 @@ PETRA.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
|
||||
availableWorkers.sort((a, b) => {
|
||||
let vala = 0;
|
||||
let valb = 0;
|
||||
if (a.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_BUILDER)
|
||||
if (a.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_BUILDER)
|
||||
vala = 100;
|
||||
if (b.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_BUILDER)
|
||||
if (b.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_BUILDER)
|
||||
valb = 100;
|
||||
if (a.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_IDLE)
|
||||
if (a.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_IDLE)
|
||||
vala = -50;
|
||||
if (b.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_IDLE)
|
||||
if (b.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_IDLE)
|
||||
valb = -50;
|
||||
if (a.getMetadata(PlayerID, "plan") === undefined)
|
||||
vala = -20;
|
||||
@@ -739,7 +766,7 @@ PETRA.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
|
||||
for (let i = 0; i < needed; ++i)
|
||||
{
|
||||
availableWorkers[i].stopMoving();
|
||||
availableWorkers[i].setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
availableWorkers[i].setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
workers.addEnt(availableWorkers[i]);
|
||||
}
|
||||
return;
|
||||
@@ -750,7 +777,7 @@ PETRA.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
|
||||
* try reassigning some other workers who are nearby
|
||||
* AI tries to use builders sensibly, not completely stopping its econ.
|
||||
*/
|
||||
PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
{
|
||||
let foundations = this.buildings.filter(API3.Filters.and(API3.Filters.isFoundation(), API3.Filters.not(API3.Filters.byClass("Field"))));
|
||||
|
||||
@@ -761,7 +788,7 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
return;
|
||||
|
||||
const workers = this.workers.filter(ent => ent.isBuilder());
|
||||
const builderWorkers = this.workersBySubrole(gameState, PETRA.Worker.SUBROLE_BUILDER);
|
||||
const builderWorkers = this.workersBySubrole(gameState, Worker.SUBROLE_BUILDER);
|
||||
const idleBuilderWorkers = builderWorkers.filter(API3.Filters.isIdle());
|
||||
|
||||
// if we're constructing and we have the foundations to our base anchor, only try building that.
|
||||
@@ -787,7 +814,7 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
const baseID = this.ID;
|
||||
fromOtherBase.forEach(worker => {
|
||||
worker.setMetadata(PlayerID, "base", baseID);
|
||||
worker.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
workers.updateEnt(worker);
|
||||
builderWorkers.updateEnt(worker);
|
||||
idleBuilderWorkers.updateEnt(worker);
|
||||
@@ -822,7 +849,7 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
continue;
|
||||
|
||||
// if our territory has shrinked since this foundation was positioned, do not build it
|
||||
if (PETRA.isNotWorthBuilding(gameState, target))
|
||||
if (isNotWorthBuilding(gameState, target))
|
||||
continue;
|
||||
|
||||
let assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target.id()).length;
|
||||
@@ -869,7 +896,7 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
if (assigned >= targetNB || builderTot >= maxTotalBuilders)
|
||||
continue;
|
||||
const nonBuilderWorkers = workers.filter(function(ent) {
|
||||
if (ent.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_BUILDER)
|
||||
if (ent.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_BUILDER)
|
||||
return false;
|
||||
if (!ent.position())
|
||||
return false;
|
||||
@@ -902,7 +929,7 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
++builderTot;
|
||||
const ent = nonBuilderWorkers[current++];
|
||||
ent.stopMoving();
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
ent.setMetadata(PlayerID, "target-foundation", target.id());
|
||||
}
|
||||
}
|
||||
@@ -957,7 +984,7 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
if (assigned >= targetNB || builderTot >= maxTotalBuilders)
|
||||
continue;
|
||||
const nonBuilderWorkers = workers.filter(function(ent) {
|
||||
if (ent.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_BUILDER)
|
||||
if (ent.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_BUILDER)
|
||||
return false;
|
||||
if (!ent.position())
|
||||
return false;
|
||||
@@ -974,14 +1001,14 @@ PETRA.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
|
||||
++assigned;
|
||||
++builderTot;
|
||||
ent.stopMoving();
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
ent.setMetadata(PlayerID, "target-foundation", target.id());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** Return false when the base is not active (no workers on it) */
|
||||
PETRA.BaseManager.prototype.update = function(gameState, queues, events)
|
||||
BaseManager.prototype.update = function(gameState, queues, events)
|
||||
{
|
||||
if (this.ID == this.basesManager.baselessBase().ID)
|
||||
{
|
||||
@@ -991,13 +1018,13 @@ PETRA.BaseManager.prototype.update = function(gameState, queues, events)
|
||||
{
|
||||
for (const ent of this.units.values())
|
||||
{
|
||||
const bestBase = PETRA.getBestBase(gameState, ent);
|
||||
const bestBase = getBestBase(gameState, ent);
|
||||
if (bestBase.ID != this.ID)
|
||||
bestBase.assignEntity(gameState, ent);
|
||||
}
|
||||
for (const ent of this.buildings.values())
|
||||
{
|
||||
const bestBase = PETRA.getBestBase(gameState, ent);
|
||||
const bestBase = getBestBase(gameState, ent);
|
||||
if (!bestBase)
|
||||
{
|
||||
if (ent.hasClass("Dock"))
|
||||
@@ -1031,7 +1058,7 @@ PETRA.BaseManager.prototype.update = function(gameState, queues, events)
|
||||
// Reassign all remaining entities to its nearest base
|
||||
for (const ent of this.units.values())
|
||||
{
|
||||
const base = PETRA.getBestBase(gameState, ent, false, this.ID);
|
||||
const base = getBestBase(gameState, ent, false, this.ID);
|
||||
base.assignEntity(gameState, ent);
|
||||
}
|
||||
return false;
|
||||
@@ -1042,7 +1069,7 @@ PETRA.BaseManager.prototype.update = function(gameState, queues, events)
|
||||
{
|
||||
if (!ent.position())
|
||||
continue;
|
||||
const base = PETRA.getBestBase(gameState, ent);
|
||||
const base = getBestBase(gameState, ent);
|
||||
if (base.anchor)
|
||||
reassignedBase = base;
|
||||
break;
|
||||
@@ -1116,22 +1143,22 @@ PETRA.BaseManager.prototype.update = function(gameState, queues, events)
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.AddTCGatherer = function(supplyID)
|
||||
BaseManager.prototype.AddTCGatherer = function(supplyID)
|
||||
{
|
||||
return this.basesManager.AddTCGatherer(supplyID);
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.RemoveTCGatherer = function(supplyID)
|
||||
BaseManager.prototype.RemoveTCGatherer = function(supplyID)
|
||||
{
|
||||
this.basesManager.RemoveTCGatherer(supplyID);
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.GetTCGatherer = function(supplyID)
|
||||
BaseManager.prototype.GetTCGatherer = function(supplyID)
|
||||
{
|
||||
return this.basesManager.GetTCGatherer(supplyID);
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.Serialize = function()
|
||||
BaseManager.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"ID": this.ID,
|
||||
@@ -1146,7 +1173,7 @@ PETRA.BaseManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.BaseManager.prototype.Deserialize = function(gameState, data)
|
||||
BaseManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { BaseManager } from "simulation/ai/petra/baseManager.js";
|
||||
import { getBestBase, getLandAccess } from "simulation/ai/petra/entityExtend.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Bases Manager
|
||||
* Manages the list of available bases and queries information from those (e.g. resource levels).
|
||||
* Only one base is run every turn.
|
||||
*/
|
||||
|
||||
PETRA.BasesManager = function(Config)
|
||||
export function BasesManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
|
||||
@@ -17,28 +21,28 @@ PETRA.BasesManager = function(Config)
|
||||
this.noBase = undefined;
|
||||
|
||||
this.baseManagers = [];
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.BasesManager.prototype.init = function(gameState)
|
||||
BasesManager.prototype.init = function(gameState)
|
||||
{
|
||||
// Initialize base map. Each pixel is a base ID, or 0 if not or not accessible.
|
||||
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
|
||||
|
||||
this.noBase = new PETRA.BaseManager(gameState, this);
|
||||
this.noBase.init(gameState, PETRA.BaseManager.STATE_WITH_ANCHOR);
|
||||
this.noBase = new BaseManager(gameState, this);
|
||||
this.noBase.init(gameState, BaseManager.STATE_WITH_ANCHOR);
|
||||
this.noBase.accessIndex = 0;
|
||||
|
||||
for (const cc of gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).values())
|
||||
if (cc.foundationProgress() === undefined)
|
||||
this.createBase(gameState, cc, PETRA.BaseManager.STATE_WITH_ANCHOR);
|
||||
this.createBase(gameState, cc, BaseManager.STATE_WITH_ANCHOR);
|
||||
else
|
||||
this.createBase(gameState, cc, PETRA.BaseManager.STATE_UNCONSTRUCTED);
|
||||
this.createBase(gameState, cc, BaseManager.STATE_UNCONSTRUCTED);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialization needed after deserialization (only called when deserialising).
|
||||
*/
|
||||
PETRA.BasesManager.prototype.postinit = function(gameState)
|
||||
BasesManager.prototype.postinit = function(gameState)
|
||||
{
|
||||
// Rebuild the base maps from the territory indices of each base.
|
||||
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
|
||||
@@ -66,17 +70,17 @@ PETRA.BasesManager.prototype.postinit = function(gameState)
|
||||
* Otherwise create a new one.
|
||||
* TODO when buildings, criteria should depend on distance
|
||||
*/
|
||||
PETRA.BasesManager.prototype.createBase = function(gameState, ent, type = PETRA.BaseManager.STATE_WITH_ANCHOR)
|
||||
BasesManager.prototype.createBase = function(gameState, ent, type = BaseManager.STATE_WITH_ANCHOR)
|
||||
{
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
let newbase;
|
||||
for (const base of this.baseManagers)
|
||||
{
|
||||
if (base.accessIndex != access)
|
||||
continue;
|
||||
if (type !== PETRA.BaseManager.STATE_ANCHORLESS && base.anchor)
|
||||
if (type !== BaseManager.STATE_ANCHORLESS && base.anchor)
|
||||
continue;
|
||||
if (type !== PETRA.BaseManager.STATE_ANCHORLESS)
|
||||
if (type !== BaseManager.STATE_ANCHORLESS)
|
||||
{
|
||||
// TODO we keep the first one, we should rather use the nearest if buildings
|
||||
// and possibly also cut on distance
|
||||
@@ -105,14 +109,14 @@ PETRA.BasesManager.prototype.createBase = function(gameState, ent, type = PETRA.
|
||||
|
||||
if (!newbase)
|
||||
{
|
||||
newbase = new PETRA.BaseManager(gameState, this);
|
||||
newbase = new BaseManager(gameState, this);
|
||||
newbase.init(gameState, type);
|
||||
this.baseManagers.push(newbase);
|
||||
}
|
||||
else
|
||||
newbase.reset(type);
|
||||
|
||||
if (type !== PETRA.BaseManager.STATE_ANCHORLESS)
|
||||
if (type !== BaseManager.STATE_ANCHORLESS)
|
||||
newbase.setAnchor(gameState, ent);
|
||||
else
|
||||
newbase.setAnchorlessEntity(gameState, ent);
|
||||
@@ -121,7 +125,7 @@ PETRA.BasesManager.prototype.createBase = function(gameState, ent, type = PETRA.
|
||||
};
|
||||
|
||||
/** TODO check if the new anchorless bases should be added to addBase */
|
||||
PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
let addBase = false;
|
||||
|
||||
@@ -157,9 +161,9 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
const base = this.getBaseByID(baseID);
|
||||
|
||||
// Promoted workers should be reset - their new gathering rates/capabilities might differ meaningfully.
|
||||
if (ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_WORKER)
|
||||
if (ent.getMetadata(PlayerID, "role") === Worker.ROLE_WORKER)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
if (!ent.isGatherer())
|
||||
{
|
||||
// TODO: Find a way to properly reset the metadata, as this is currently done selectively
|
||||
@@ -189,28 +193,28 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
if (ent.getMetadata(PlayerID, "base") == -1) // Standard base around a cc
|
||||
{
|
||||
// Okay so let's try to create a new base around this.
|
||||
const newbase = this.createBase(gameState, ent, PETRA.BaseManager.STATE_UNCONSTRUCTED);
|
||||
const newbase = this.createBase(gameState, ent, BaseManager.STATE_UNCONSTRUCTED);
|
||||
// Let's get a few units from other bases there to build this.
|
||||
const builders = this.bulkPickWorkers(gameState, newbase, 10);
|
||||
if (builders !== false)
|
||||
{
|
||||
builders.forEach(worker => {
|
||||
worker.setMetadata(PlayerID, "base", newbase.ID);
|
||||
worker.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "target-foundation", ent.id());
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (ent.getMetadata(PlayerID, "base") == -2) // anchorless base around a dock
|
||||
{
|
||||
const newbase = this.createBase(gameState, ent, PETRA.BaseManager.STATE_ANCHORLESS);
|
||||
const newbase = this.createBase(gameState, ent, BaseManager.STATE_ANCHORLESS);
|
||||
// Let's get a few units from other bases there to build this.
|
||||
const builders = this.bulkPickWorkers(gameState, newbase, 4);
|
||||
if (builders != false)
|
||||
{
|
||||
builders.forEach(worker => {
|
||||
worker.setMetadata(PlayerID, "base", newbase.ID);
|
||||
worker.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "target-foundation", ent.id());
|
||||
});
|
||||
}
|
||||
@@ -260,17 +264,17 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
continue;
|
||||
if (ent.hasClass("Unit"))
|
||||
{
|
||||
PETRA.getBestBase(gameState, ent).assignEntity(gameState, ent);
|
||||
getBestBase(gameState, ent).assignEntity(gameState, ent);
|
||||
continue;
|
||||
}
|
||||
if (ent.hasClass("CivCentre")) // build a new base around it
|
||||
{
|
||||
let newbase;
|
||||
if (ent.foundationProgress() !== undefined)
|
||||
newbase = this.createBase(gameState, ent, PETRA.BaseManager.STATE_UNCONSTRUCTED);
|
||||
newbase = this.createBase(gameState, ent, BaseManager.STATE_UNCONSTRUCTED);
|
||||
else
|
||||
{
|
||||
newbase = this.createBase(gameState, ent, PETRA.BaseManager.STATE_CAPTURED);
|
||||
newbase = this.createBase(gameState, ent, BaseManager.STATE_CAPTURED);
|
||||
addBase = true;
|
||||
}
|
||||
newbase.assignEntity(gameState, ent);
|
||||
@@ -280,9 +284,9 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
let base;
|
||||
// If dropsite on new island, create a base around it
|
||||
if (!ent.decaying() && ent.resourceDropsiteTypes())
|
||||
base = this.createBase(gameState, ent, PETRA.BaseManager.STATE_ANCHORLESS);
|
||||
base = this.createBase(gameState, ent, BaseManager.STATE_ANCHORLESS);
|
||||
else
|
||||
base = PETRA.getBestBase(gameState, ent) || this.noBase;
|
||||
base = getBestBase(gameState, ent) || this.noBase;
|
||||
base.assignEntity(gameState, ent);
|
||||
}
|
||||
}
|
||||
@@ -296,12 +300,12 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
continue;
|
||||
|
||||
// Assign it immediately to something useful to do.
|
||||
if (ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_WORKER)
|
||||
if (ent.getMetadata(PlayerID, "role") === Worker.ROLE_WORKER)
|
||||
{
|
||||
let base;
|
||||
if (ent.getMetadata(PlayerID, "base") === undefined)
|
||||
{
|
||||
base = PETRA.getBestBase(gameState, ent);
|
||||
base = getBestBase(gameState, ent);
|
||||
base.assignEntity(gameState, ent);
|
||||
}
|
||||
else
|
||||
@@ -316,12 +320,12 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
continue;
|
||||
const dropsites = gameState.getOwnDropsites(type.generic);
|
||||
const pos = ent.position();
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
let distmin = Math.min();
|
||||
let goal;
|
||||
for (const dropsite of dropsites.values())
|
||||
{
|
||||
if (!dropsite.position() || PETRA.getLandAccess(gameState, dropsite) != access)
|
||||
if (!dropsite.position() || getLandAccess(gameState, dropsite) != access)
|
||||
continue;
|
||||
const dist = API3.SquareVectorDistance(pos, dropsite.position());
|
||||
if (dist > distmin)
|
||||
@@ -343,7 +347,7 @@ PETRA.BasesManager.prototype.checkEvents = function(gameState, events)
|
||||
* returns an entity collection of workers through BaseManager.pickBuilders
|
||||
* TODO: when same accessIndex, sort by distance
|
||||
*/
|
||||
PETRA.BasesManager.prototype.bulkPickWorkers = function(gameState, baseRef, number)
|
||||
BasesManager.prototype.bulkPickWorkers = function(gameState, baseRef, number)
|
||||
{
|
||||
const accessIndex = baseRef.accessIndex;
|
||||
if (!accessIndex)
|
||||
@@ -378,7 +382,7 @@ PETRA.BasesManager.prototype.bulkPickWorkers = function(gameState, baseRef, numb
|
||||
/**
|
||||
* @return {Object} - Resources (estimation) still gatherable in our territory.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.getTotalResourceLevel = function(gameState, resources = Resources.GetCodes(), proximity = ["nearby", "medium"])
|
||||
BasesManager.prototype.getTotalResourceLevel = function(gameState, resources = Resources.GetCodes(), proximity = ["nearby", "medium"])
|
||||
{
|
||||
const total = {};
|
||||
for (const res of resources)
|
||||
@@ -394,7 +398,7 @@ PETRA.BasesManager.prototype.getTotalResourceLevel = function(gameState, resourc
|
||||
* Returns the current gather rate
|
||||
* This is not per-se exact, it performs a few adjustments ad-hoc to account for travel distance, stuffs like that.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.GetCurrentGatherRates = function(gameState)
|
||||
BasesManager.prototype.GetCurrentGatherRates = function(gameState)
|
||||
{
|
||||
if (!this.turnCache.currentRates)
|
||||
{
|
||||
@@ -416,7 +420,7 @@ PETRA.BasesManager.prototype.GetCurrentGatherRates = function(gameState)
|
||||
/** Some functions that register that we assigned a gatherer to a resource this turn */
|
||||
|
||||
/** Add a gatherer to the turn cache for this supply. */
|
||||
PETRA.BasesManager.prototype.AddTCGatherer = function(supplyID)
|
||||
BasesManager.prototype.AddTCGatherer = function(supplyID)
|
||||
{
|
||||
if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID] !== undefined)
|
||||
++this.turnCache.resourceGatherer[supplyID];
|
||||
@@ -429,7 +433,7 @@ PETRA.BasesManager.prototype.AddTCGatherer = function(supplyID)
|
||||
};
|
||||
|
||||
/** Remove a gatherer from the turn cache for this supply. */
|
||||
PETRA.BasesManager.prototype.RemoveTCGatherer = function(supplyID)
|
||||
BasesManager.prototype.RemoveTCGatherer = function(supplyID)
|
||||
{
|
||||
if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID])
|
||||
--this.turnCache.resourceGatherer[supplyID];
|
||||
@@ -441,7 +445,7 @@ PETRA.BasesManager.prototype.RemoveTCGatherer = function(supplyID)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.GetTCGatherer = function(supplyID)
|
||||
BasesManager.prototype.GetTCGatherer = function(supplyID)
|
||||
{
|
||||
if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID])
|
||||
return this.turnCache.resourceGatherer[supplyID];
|
||||
@@ -450,7 +454,7 @@ PETRA.BasesManager.prototype.GetTCGatherer = function(supplyID)
|
||||
};
|
||||
|
||||
/** The next two are to register that we assigned a gatherer to a resource this turn. */
|
||||
PETRA.BasesManager.prototype.AddTCResGatherer = function(resource)
|
||||
BasesManager.prototype.AddTCResGatherer = function(resource)
|
||||
{
|
||||
const check = "resourceGatherer-" + resource;
|
||||
if (this.turnCache[check])
|
||||
@@ -462,7 +466,7 @@ PETRA.BasesManager.prototype.AddTCResGatherer = function(resource)
|
||||
this.turnCache.currentRates[resource] += 0.5;
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.GetTCResGatherer = function(resource)
|
||||
BasesManager.prototype.GetTCResGatherer = function(resource)
|
||||
{
|
||||
const check = "resourceGatherer-" + resource;
|
||||
if (this.turnCache[check])
|
||||
@@ -474,7 +478,7 @@ PETRA.BasesManager.prototype.GetTCResGatherer = function(resource)
|
||||
/**
|
||||
* flag a resource as exhausted
|
||||
*/
|
||||
PETRA.BasesManager.prototype.isResourceExhausted = function(resource)
|
||||
BasesManager.prototype.isResourceExhausted = function(resource)
|
||||
{
|
||||
const check = "exhausted-" + resource;
|
||||
if (this.turnCache[check] == undefined)
|
||||
@@ -488,26 +492,26 @@ PETRA.BasesManager.prototype.isResourceExhausted = function(resource)
|
||||
* ActiveBases includes only those with a built cc
|
||||
* PotentialBases includes also those with a cc in construction
|
||||
*/
|
||||
PETRA.BasesManager.prototype.numActiveBases = function()
|
||||
BasesManager.prototype.numActiveBases = function()
|
||||
{
|
||||
if (!this.turnCache.base)
|
||||
this.updateBaseCache();
|
||||
return this.turnCache.base.active;
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.hasActiveBase = function()
|
||||
BasesManager.prototype.hasActiveBase = function()
|
||||
{
|
||||
return !!this.numActiveBases();
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.numPotentialBases = function()
|
||||
BasesManager.prototype.numPotentialBases = function()
|
||||
{
|
||||
if (!this.turnCache.base)
|
||||
this.updateBaseCache();
|
||||
return this.turnCache.base.potential;
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.hasPotentialBase = function()
|
||||
BasesManager.prototype.hasPotentialBase = function()
|
||||
{
|
||||
return !!this.numPotentialBases();
|
||||
};
|
||||
@@ -517,7 +521,7 @@ PETRA.BasesManager.prototype.hasPotentialBase = function()
|
||||
* .potential {number} - Bases that may or may not still be a foundation.
|
||||
* .active {number} - Usable bases.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.updateBaseCache = function()
|
||||
BasesManager.prototype.updateBaseCache = function()
|
||||
{
|
||||
this.turnCache.base = { "active": 0, "potential": 0 };
|
||||
for (const base of this.baseManagers)
|
||||
@@ -530,12 +534,12 @@ PETRA.BasesManager.prototype.updateBaseCache = function()
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.resetBaseCache = function()
|
||||
BasesManager.prototype.resetBaseCache = function()
|
||||
{
|
||||
this.turnCache.base = undefined;
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.baselessBase = function()
|
||||
BasesManager.prototype.baselessBase = function()
|
||||
{
|
||||
return this.noBase;
|
||||
};
|
||||
@@ -544,7 +548,7 @@ PETRA.BasesManager.prototype.baselessBase = function()
|
||||
* @param {number} baseID
|
||||
* @return {Object} - The base corresponding to baseID.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.getBaseByID = function(baseID)
|
||||
BasesManager.prototype.getBaseByID = function(baseID)
|
||||
{
|
||||
if (this.noBase.ID === baseID)
|
||||
return this.noBase;
|
||||
@@ -554,7 +558,7 @@ PETRA.BasesManager.prototype.getBaseByID = function(baseID)
|
||||
/**
|
||||
* flag a resource as exhausted
|
||||
*/
|
||||
PETRA.BasesManager.prototype.isResourceExhausted = function(resource)
|
||||
BasesManager.prototype.isResourceExhausted = function(resource)
|
||||
{
|
||||
return this.baseManagers.every(base =>
|
||||
!base.dropsiteSupplies[resource].nearby.length &&
|
||||
@@ -566,7 +570,7 @@ PETRA.BasesManager.prototype.isResourceExhausted = function(resource)
|
||||
* Count gatherers returning resources in the number of gatherers of resourceSupplies
|
||||
* to prevent the AI always reassigning idle workers to these resourceSupplies (specially in naval maps).
|
||||
*/
|
||||
PETRA.BasesManager.prototype.assignGatherers = function()
|
||||
BasesManager.prototype.assignGatherers = function()
|
||||
{
|
||||
for (const base of this.baseManagers)
|
||||
for (const worker of base.workers.values())
|
||||
@@ -584,7 +588,7 @@ PETRA.BasesManager.prototype.assignGatherers = function()
|
||||
* Assign an entity to the closest base.
|
||||
* Used by the starting strategy.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.assignEntity = function(gameState, ent, territoryIndex)
|
||||
BasesManager.prototype.assignEntity = function(gameState, ent, territoryIndex)
|
||||
{
|
||||
let bestbase;
|
||||
for (const base of this.baseManagers)
|
||||
@@ -599,9 +603,9 @@ PETRA.BasesManager.prototype.assignEntity = function(gameState, ent, territoryIn
|
||||
if (!bestbase) // entity outside our territory
|
||||
{
|
||||
if (ent.hasClass("Structure") && !ent.decaying() && ent.resourceDropsiteTypes())
|
||||
bestbase = this.createBase(gameState, ent, PETRA.BaseManager.STATE_ANCHORLESS);
|
||||
bestbase = this.createBase(gameState, ent, BaseManager.STATE_ANCHORLESS);
|
||||
else
|
||||
bestbase = PETRA.getBestBase(gameState, ent) || this.noBase;
|
||||
bestbase = getBestBase(gameState, ent) || this.noBase;
|
||||
bestbase.assignEntity(gameState, ent);
|
||||
}
|
||||
// now assign entities garrisoned inside this entity
|
||||
@@ -612,7 +616,7 @@ PETRA.BasesManager.prototype.assignEntity = function(gameState, ent, territoryIn
|
||||
if (ent.position() && bestbase.ID !== this.noBase.ID)
|
||||
{
|
||||
bestbase.assignRolelessUnits(gameState, [ent]);
|
||||
if (ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_WORKER)
|
||||
if (ent.getMetadata(PlayerID, "role") === Worker.ROLE_WORKER)
|
||||
{
|
||||
bestbase.reassignIdleWorkers(gameState, [ent]);
|
||||
bestbase.workerObject.update(gameState, ent);
|
||||
@@ -625,7 +629,7 @@ PETRA.BasesManager.prototype.assignEntity = function(gameState, ent, territoryIn
|
||||
* @param {Object} gameState
|
||||
* @param {Object} rates - The rates to add the gather rates to.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.addGatherRates = function(gameState, rates)
|
||||
BasesManager.prototype.addGatherRates = function(gameState, rates)
|
||||
{
|
||||
for (const base of this.baseManagers)
|
||||
base.addGatherRates(gameState, rates);
|
||||
@@ -635,7 +639,7 @@ PETRA.BasesManager.prototype.addGatherRates = function(gameState, rates)
|
||||
* @param {number} territoryIndex
|
||||
* @return {number} - The ID of the base at the given territory index.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.baseAtIndex = function(territoryIndex)
|
||||
BasesManager.prototype.baseAtIndex = function(territoryIndex)
|
||||
{
|
||||
return this.basesMap.map[territoryIndex];
|
||||
};
|
||||
@@ -643,7 +647,7 @@ PETRA.BasesManager.prototype.baseAtIndex = function(territoryIndex)
|
||||
/**
|
||||
* @param {number} territoryIndex
|
||||
*/
|
||||
PETRA.BasesManager.prototype.removeBaseFromTerritoryIndex = function(territoryIndex)
|
||||
BasesManager.prototype.removeBaseFromTerritoryIndex = function(territoryIndex)
|
||||
{
|
||||
const baseID = this.basesMap.map[territoryIndex];
|
||||
if (baseID == 0)
|
||||
@@ -665,7 +669,7 @@ PETRA.BasesManager.prototype.removeBaseFromTerritoryIndex = function(territoryIn
|
||||
/**
|
||||
* @return {boolean} - Whether the index was added to a base.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.addTerritoryIndexToBase = function(gameState, territoryIndex, passabilityMap)
|
||||
BasesManager.prototype.addTerritoryIndexToBase = function(gameState, territoryIndex, passabilityMap)
|
||||
{
|
||||
if (this.baseAtIndex(territoryIndex) != 0)
|
||||
return false;
|
||||
@@ -705,7 +709,7 @@ PETRA.BasesManager.prototype.addTerritoryIndexToBase = function(gameState, terri
|
||||
};
|
||||
|
||||
/** Reassign territories when a base is going to be deleted */
|
||||
PETRA.BasesManager.prototype.reassignTerritories = function(deletedBase, territoryMap)
|
||||
BasesManager.prototype.reassignTerritories = function(deletedBase, territoryMap)
|
||||
{
|
||||
const cellSize = territoryMap.cellSize;
|
||||
const width = territoryMap.width;
|
||||
@@ -748,7 +752,7 @@ PETRA.BasesManager.prototype.reassignTerritories = function(deletedBase, territo
|
||||
/**
|
||||
* We will loop only on one active base per turn.
|
||||
*/
|
||||
PETRA.BasesManager.prototype.update = function(gameState, queues, events)
|
||||
BasesManager.prototype.update = function(gameState, queues, events)
|
||||
{
|
||||
Engine.ProfileStart("BasesManager update");
|
||||
|
||||
@@ -768,7 +772,7 @@ PETRA.BasesManager.prototype.update = function(gameState, queues, events)
|
||||
Engine.ProfileStop();
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.Serialize = function()
|
||||
BasesManager.prototype.Serialize = function()
|
||||
{
|
||||
const properties = {
|
||||
"currentBase": this.currentBase
|
||||
@@ -785,23 +789,23 @@ PETRA.BasesManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.BasesManager.prototype.Deserialize = function(gameState, data)
|
||||
BasesManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data.properties)
|
||||
this[key] = data.properties[key];
|
||||
|
||||
this.noBase = new PETRA.BaseManager(gameState, this);
|
||||
this.noBase = new BaseManager(gameState, this);
|
||||
this.noBase.Deserialize(gameState, data.noBase);
|
||||
this.noBase.init(gameState, PETRA.BaseManager.STATE_WITH_ANCHOR);
|
||||
this.noBase.init(gameState, BaseManager.STATE_WITH_ANCHOR);
|
||||
this.noBase.Deserialize(gameState, data.noBase);
|
||||
|
||||
this.baseManagers = [];
|
||||
for (const basedata of data.baseManagers)
|
||||
{
|
||||
// The first call to deserialize set the ID base needed by entitycollections.
|
||||
const newbase = new PETRA.BaseManager(gameState, this);
|
||||
const newbase = new BaseManager(gameState, this);
|
||||
newbase.Deserialize(gameState, basedata);
|
||||
newbase.init(gameState, PETRA.BaseManager.STATE_WITH_ANCHOR);
|
||||
newbase.init(gameState, BaseManager.STATE_WITH_ANCHOR);
|
||||
newbase.Deserialize(gameState, basedata);
|
||||
this.baseManagers.push(newbase);
|
||||
}
|
||||
|
||||
@@ -4,24 +4,24 @@
|
||||
* It also takes care of the structures we can't currently build and should not try to build endlessly.
|
||||
*/
|
||||
|
||||
PETRA.BuildManager = function()
|
||||
export function BuildManager()
|
||||
{
|
||||
// List of buildings we have builders for, with number of possible builders.
|
||||
this.builderCounters = new Map();
|
||||
// List of buildings we can't currently build (because no room, no builder or whatever),
|
||||
// with time we should wait before trying again to build it.
|
||||
this.unbuildables = new Map();
|
||||
};
|
||||
}
|
||||
|
||||
/** Initialization at start of game */
|
||||
PETRA.BuildManager.prototype.init = function(gameState)
|
||||
BuildManager.prototype.init = function(gameState)
|
||||
{
|
||||
const civ = gameState.getPlayerCiv();
|
||||
for (const ent of gameState.getOwnUnits().values())
|
||||
this.incrementBuilderCounters(civ, ent, 1);
|
||||
};
|
||||
|
||||
PETRA.BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment)
|
||||
BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment)
|
||||
{
|
||||
for (const buildable of ent.buildableEntities(civ))
|
||||
{
|
||||
@@ -43,7 +43,7 @@ PETRA.BuildManager.prototype.incrementBuilderCounters = function(civ, ent, incre
|
||||
};
|
||||
|
||||
/** Update the builders counters */
|
||||
PETRA.BuildManager.prototype.checkEvents = function(gameState, events)
|
||||
BuildManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
this.elapsedTime = gameState.ai.elapsedTime;
|
||||
const civ = gameState.getPlayerCiv();
|
||||
@@ -99,7 +99,7 @@ PETRA.BuildManager.prototype.checkEvents = function(gameState, events)
|
||||
/**
|
||||
* Get the buildable structures passing a filter.
|
||||
*/
|
||||
PETRA.BuildManager.prototype.findStructuresByFilter = function(gameState, filter)
|
||||
BuildManager.prototype.findStructuresByFilter = function(gameState, filter)
|
||||
{
|
||||
const result = [];
|
||||
for (const [templateName, count] of this.builderCounters)
|
||||
@@ -119,30 +119,30 @@ PETRA.BuildManager.prototype.findStructuresByFilter = function(gameState, filter
|
||||
* Get the first buildable structure with a given class
|
||||
* TODO when several available, choose the best one
|
||||
*/
|
||||
PETRA.BuildManager.prototype.findStructureWithClass = function(gameState, classes)
|
||||
BuildManager.prototype.findStructureWithClass = function(gameState, classes)
|
||||
{
|
||||
return this.findStructuresByFilter(gameState, API3.Filters.byClasses(classes))[0];
|
||||
};
|
||||
|
||||
PETRA.BuildManager.prototype.hasBuilder = function(template)
|
||||
BuildManager.prototype.hasBuilder = function(template)
|
||||
{
|
||||
const numBuilders = this.builderCounters.get(template);
|
||||
return numBuilders && numBuilders > 0;
|
||||
};
|
||||
|
||||
PETRA.BuildManager.prototype.isUnbuildable = function(gameState, template)
|
||||
BuildManager.prototype.isUnbuildable = function(gameState, template)
|
||||
{
|
||||
return this.unbuildables.has(template) && this.unbuildables.get(template).time > gameState.ai.elapsedTime;
|
||||
};
|
||||
|
||||
PETRA.BuildManager.prototype.setBuildable = function(template)
|
||||
BuildManager.prototype.setBuildable = function(template)
|
||||
{
|
||||
if (this.unbuildables.has(template))
|
||||
this.unbuildables.delete(template);
|
||||
};
|
||||
|
||||
/** Time is the duration in second that we will wait before checking again if it is buildable */
|
||||
PETRA.BuildManager.prototype.setUnbuildable = function(gameState, template, time = 90, reason = "room")
|
||||
BuildManager.prototype.setUnbuildable = function(gameState, template, time = 90, reason = "room")
|
||||
{
|
||||
if (!this.unbuildables.has(template))
|
||||
this.unbuildables.set(template, { "reason": reason, "time": gameState.ai.elapsedTime + time });
|
||||
@@ -158,7 +158,7 @@ PETRA.BuildManager.prototype.setUnbuildable = function(gameState, template, time
|
||||
};
|
||||
|
||||
/** Return the number of unbuildables due to missing room */
|
||||
PETRA.BuildManager.prototype.numberMissingRoom = function(gameState)
|
||||
BuildManager.prototype.numberMissingRoom = function(gameState)
|
||||
{
|
||||
let num = 0;
|
||||
for (const unbuildable of this.unbuildables.values())
|
||||
@@ -168,14 +168,14 @@ PETRA.BuildManager.prototype.numberMissingRoom = function(gameState)
|
||||
};
|
||||
|
||||
/** Reset the unbuildables due to missing room */
|
||||
PETRA.BuildManager.prototype.resetMissingRoom = function(gameState)
|
||||
BuildManager.prototype.resetMissingRoom = function(gameState)
|
||||
{
|
||||
for (const [key, unbuildable] of this.unbuildables)
|
||||
if (unbuildable.reason == "room")
|
||||
this.unbuildables.delete(key);
|
||||
};
|
||||
|
||||
PETRA.BuildManager.prototype.Serialize = function()
|
||||
BuildManager.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"builderCounters": this.builderCounters,
|
||||
@@ -183,7 +183,7 @@ PETRA.BuildManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.BuildManager.prototype.Deserialize = function(data)
|
||||
BuildManager.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
PETRA.launchAttackMessages = {
|
||||
import { AttackPlan } from "simulation/ai/petra/attackPlan.js";
|
||||
|
||||
const launchAttackMessages = {
|
||||
"hugeAttack": [
|
||||
markForTranslation("I am starting a massive military campaign against %(_player_)s, come and join me."),
|
||||
markForTranslation("I have set up a huge army to crush %(_player_)s. Join me and you will have your share of the loot."),
|
||||
@@ -11,7 +13,7 @@ PETRA.launchAttackMessages = {
|
||||
]
|
||||
};
|
||||
|
||||
PETRA.answerRequestAttackMessages = {
|
||||
const answerRequestAttackMessages = {
|
||||
"join": [
|
||||
markForTranslation("Let me regroup my army and I will then join you against %(_player_)s."),
|
||||
markForTranslation("I am finishing preparations to attack %(_player_)s.")
|
||||
@@ -25,24 +27,24 @@ PETRA.answerRequestAttackMessages = {
|
||||
]
|
||||
};
|
||||
|
||||
PETRA.sentTributeMessages = [
|
||||
const sentTributeMessages = [
|
||||
markForTranslation("Here is a gift for you, %(_player_)s. Make good use of it."),
|
||||
markForTranslation("I see you are in a bad situation, %(_player_)s. I hope this helps."),
|
||||
markForTranslation("I can help you this time, %(_player_)s, but you should manage your resources more carefully in the future.")
|
||||
];
|
||||
|
||||
PETRA.requestTributeMessages = [
|
||||
const requestTributeMessages = [
|
||||
markForTranslation("I am in need of %(resource)s, can you help? I will make it up to you."),
|
||||
markForTranslation("I would participate more efficiently in our common war effort if you could provide me some %(resource)s."),
|
||||
markForTranslation("If you can spare me some %(resource)s, I will be able to strengthen my army.")
|
||||
];
|
||||
|
||||
PETRA.newTradeRouteMessages = [
|
||||
const newTradeRouteMessages = [
|
||||
markForTranslation("I have set up a new route with %(_player_)s. Trading will be profitable for all of us."),
|
||||
markForTranslation("A new trade route is set up with %(_player_)s. Take your share of the profits.")
|
||||
];
|
||||
|
||||
PETRA.newDiplomacyMessages = {
|
||||
const newDiplomacyMessages = {
|
||||
"ally": [
|
||||
markForTranslation("%(_player_)s and I are now allies.")
|
||||
],
|
||||
@@ -54,7 +56,7 @@ PETRA.newDiplomacyMessages = {
|
||||
]
|
||||
};
|
||||
|
||||
PETRA.answerDiplomacyRequestMessages = {
|
||||
const answerDiplomacyRequestMessages = {
|
||||
"ally": {
|
||||
"decline": [
|
||||
markForTranslation("I cannot accept your offer to become allies, %(_player_)s."),
|
||||
@@ -114,7 +116,7 @@ PETRA.answerDiplomacyRequestMessages = {
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.sendDiplomacyRequestMessages = {
|
||||
const sendDiplomacyRequestMessages = {
|
||||
"ally": {
|
||||
"sendRequest": [
|
||||
markForTranslation("%(_player_)s, it would help both of our civilizations if we formed an alliance. If you become allies with me, I will respond in kind.")
|
||||
@@ -136,7 +138,7 @@ PETRA.sendDiplomacyRequestMessages = {
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.emergencyMessages = {
|
||||
const emergencyMessages = {
|
||||
"enter": [
|
||||
markForTranslation("My armies failed while defending my empire. Please honor our alliance and send help!"),
|
||||
markForTranslation("My humble armies feel weak and tired. My civilization depends on our alliance, please send help!"),
|
||||
@@ -148,114 +150,114 @@ PETRA.emergencyMessages = {
|
||||
]
|
||||
};
|
||||
|
||||
PETRA.chatLaunchAttack = function(gameState, player, type)
|
||||
export function launchAttack(gameState, player, type)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.launchAttackMessages[type === PETRA.AttackPlan.TYPE_HUGE_ATTACK ? "hugeAttack" : "other"]),
|
||||
"message": "/allies " + pickRandom(launchAttackMessages[type === AttackPlan.TYPE_HUGE_ATTACK ? "hugeAttack" : "other"]),
|
||||
"translateMessage": true,
|
||||
"translateParameters": ["_player_"],
|
||||
"parameters": { "_player_": player }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatAnswerRequestAttack = function(gameState, player, answer, other)
|
||||
export function answerRequestAttack(gameState, player, answer, other)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.answerRequestAttackMessages[answer]),
|
||||
"message": "/allies " + pickRandom(answerRequestAttackMessages[answer]),
|
||||
"translateMessage": true,
|
||||
"translateParameters": answer != "other" ? ["_player_"] : ["_player_", "_player_2"],
|
||||
"parameters": answer != "other" ? { "_player_": player } : { "_player_": player, "_player_2": other }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatSentTribute = function(gameState, player)
|
||||
export function sentTribute(gameState, player)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.sentTributeMessages),
|
||||
"message": "/allies " + pickRandom(sentTributeMessages),
|
||||
"translateMessage": true,
|
||||
"translateParameters": ["_player_"],
|
||||
"parameters": { "_player_": player }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatRequestTribute = function(gameState, resource)
|
||||
export function requestTribute(gameState, resource)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.requestTributeMessages),
|
||||
"message": "/allies " + pickRandom(requestTributeMessages),
|
||||
"translateMessage": true,
|
||||
"translateParameters": { "resource": "withinSentence" },
|
||||
"parameters": { "resource": Resources.GetNames()[resource] }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatNewTradeRoute = function(gameState, player)
|
||||
export function newTradeRoute(gameState, player)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.newTradeRouteMessages),
|
||||
"message": "/allies " + pickRandom(newTradeRouteMessages),
|
||||
"translateMessage": true,
|
||||
"translateParameters": ["_player_"],
|
||||
"parameters": { "_player_": player }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatNewPhase = function(gameState, phase, status)
|
||||
function newPhase(gameState, phase, status)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.newPhaseMessages[status]),
|
||||
"message": "/allies " + pickRandom(newPhaseMessages[status]),
|
||||
"translateMessage": true,
|
||||
"translateParameters": ["phase"],
|
||||
"parameters": { "phase": phase }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatNewDiplomacy = function(gameState, player, newDiplomaticStance)
|
||||
export function newDiplomacy(gameState, player, newDiplomaticStance)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": pickRandom(this.newDiplomacyMessages[newDiplomaticStance]),
|
||||
"message": pickRandom(newDiplomacyMessages[newDiplomaticStance]),
|
||||
"translateMessage": true,
|
||||
"translateParameters": ["_player_"],
|
||||
"parameters": { "_player_": player }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatAnswerRequestDiplomacy = function(gameState, player, requestType, response, requiredTribute)
|
||||
export function answerRequestDiplomacy(gameState, player, requestType, response, requiredTribute)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/msg " + gameState.sharedScript.playersData[player].name + " " +
|
||||
pickRandom(this.answerDiplomacyRequestMessages[requestType][response]),
|
||||
pickRandom(answerDiplomacyRequestMessages[requestType][response]),
|
||||
"translateMessage": true,
|
||||
"translateParameters": requiredTribute ? { "_amount_": null, "_resource_": "withinSentence", "_player_": null } : ["_player_"],
|
||||
"parameters": requiredTribute ?
|
||||
{ "_amount_": requiredTribute.wanted, "_resource_": Resources.GetNames()[requiredTribute.type], "_player_": player } :
|
||||
{ "_player_": player }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatNewRequestDiplomacy = function(gameState, player, requestType, status)
|
||||
export function newRequestDiplomacy(gameState, player, requestType, status)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/msg " + gameState.sharedScript.playersData[player].name + " " +
|
||||
pickRandom(this.sendDiplomacyRequestMessages[requestType][status]),
|
||||
pickRandom(sendDiplomacyRequestMessages[requestType][status]),
|
||||
"translateMessage": true,
|
||||
"translateParameters": ["_player_"],
|
||||
"parameters": { "_player_": player }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.chatEmergency = function(gameState, enable)
|
||||
export function emergency(gameState, enable)
|
||||
{
|
||||
Engine.PostCommand(PlayerID, {
|
||||
"type": "aichat",
|
||||
"message": "/allies " + pickRandom(this.emergencyMessages[enable ? "enter" : "exit"]),
|
||||
"message": "/allies " + pickRandom(emergencyMessages[enable ? "enter" : "exit"]),
|
||||
"translateMessage": true
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// These integers must be sequential
|
||||
PETRA.DIFFICULTY_SANDBOX = 0;
|
||||
PETRA.DIFFICULTY_VERY_EASY = 1;
|
||||
PETRA.DIFFICULTY_EASY = 2;
|
||||
PETRA.DIFFICULTY_MEDIUM = 3;
|
||||
PETRA.DIFFICULTY_HARD = 4;
|
||||
PETRA.DIFFICULTY_VERY_HARD = 5;
|
||||
/* eslint-disable prefer-const -- Mods should be able to change them */
|
||||
export let DIFFICULTY_SANDBOX = 0;
|
||||
export let DIFFICULTY_VERY_EASY = 1;
|
||||
export let DIFFICULTY_EASY = 2;
|
||||
export let DIFFICULTY_MEDIUM = 3;
|
||||
export let DIFFICULTY_HARD = 4;
|
||||
export let DIFFICULTY_VERY_HARD = 5;
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
PETRA.Config = function(difficulty = PETRA.DIFFICULTY_MEDIUM, behavior)
|
||||
export function Config(difficulty = DIFFICULTY_MEDIUM, behavior)
|
||||
{
|
||||
this.difficulty = difficulty;
|
||||
|
||||
@@ -147,7 +149,7 @@ PETRA.Config = function(difficulty = PETRA.DIFFICULTY_MEDIUM, behavior)
|
||||
"defensive": 0.5
|
||||
};
|
||||
|
||||
// See PETRA.QueueManager.prototype.wantedGatherRates()
|
||||
// See QueueManager.prototype.wantedGatherRates()
|
||||
this.queues =
|
||||
{
|
||||
"firstTurn": {
|
||||
@@ -202,11 +204,11 @@ PETRA.Config = function(difficulty = PETRA.DIFFICULTY_MEDIUM, behavior)
|
||||
0.35,
|
||||
0.2
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.Config.prototype.setConfig = function(gameState)
|
||||
Config.prototype.setConfig = function(gameState)
|
||||
{
|
||||
if (this.difficulty > PETRA.DIFFICULTY_SANDBOX)
|
||||
if (this.difficulty > DIFFICULTY_SANDBOX)
|
||||
{
|
||||
// Setup personality traits according to the user choice:
|
||||
// The parameter used to define the personality is basically the aggressivity or (1-defensiveness)
|
||||
@@ -249,14 +251,14 @@ PETRA.Config.prototype.setConfig = function(gameState)
|
||||
this.Military.fortressLapseTime = Math.round(this.Military.fortressLapseTime * (1.1 - 0.2 * this.personality.defensive));
|
||||
this.priorities.defenseBuilding = Math.round(this.priorities.defenseBuilding * (0.9 + 0.2 * this.personality.defensive));
|
||||
|
||||
if (this.difficulty < PETRA.DIFFICULTY_EASY)
|
||||
if (this.difficulty < DIFFICULTY_EASY)
|
||||
{
|
||||
this.popScaling = 0.5;
|
||||
this.Economy.supportRatio = 0.5;
|
||||
this.Economy.provisionFields = 1;
|
||||
this.Military.numSentryTowers = this.personality.defensive > this.personalityCut.strong ? 1 : 0;
|
||||
}
|
||||
else if (this.difficulty < PETRA.DIFFICULTY_MEDIUM)
|
||||
else if (this.difficulty < DIFFICULTY_MEDIUM)
|
||||
{
|
||||
this.popScaling = 0.7;
|
||||
this.Economy.supportRatio = 0.4;
|
||||
@@ -265,7 +267,7 @@ PETRA.Config.prototype.setConfig = function(gameState)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.difficulty == PETRA.DIFFICULTY_MEDIUM)
|
||||
if (this.difficulty == DIFFICULTY_MEDIUM)
|
||||
this.Military.numSentryTowers = 1;
|
||||
else
|
||||
this.Military.numSentryTowers = 2;
|
||||
@@ -283,9 +285,9 @@ PETRA.Config.prototype.setConfig = function(gameState)
|
||||
}
|
||||
|
||||
const maxPop = gameState.getPopulationMax();
|
||||
if (this.difficulty < PETRA.DIFFICULTY_EASY)
|
||||
if (this.difficulty < DIFFICULTY_EASY)
|
||||
this.Economy.targetNumWorkers = Math.max(1, Math.min(40, maxPop));
|
||||
else if (this.difficulty < PETRA.DIFFICULTY_MEDIUM)
|
||||
else if (this.difficulty < DIFFICULTY_MEDIUM)
|
||||
this.Economy.targetNumWorkers = Math.max(1, Math.min(60, Math.floor(maxPop/2)));
|
||||
else
|
||||
this.Economy.targetNumWorkers = Math.max(1, Math.min(120, Math.floor(maxPop/3)));
|
||||
@@ -311,7 +313,7 @@ PETRA.Config.prototype.setConfig = function(gameState)
|
||||
this.Economy.targetNumWorkers = Math.max(this.Economy.targetNumWorkers, this.Economy.popPhase2);
|
||||
this.Economy.workPhase3 = Math.min(this.Economy.workPhase3, this.Economy.targetNumWorkers);
|
||||
this.Economy.workPhase4 = Math.min(this.Economy.workPhase4, this.Economy.targetNumWorkers);
|
||||
if (this.difficulty < PETRA.DIFFICULTY_EASY)
|
||||
if (this.difficulty < DIFFICULTY_EASY)
|
||||
this.Economy.workPhase3 = Infinity; // prevent the phasing to city phase
|
||||
|
||||
this.emergencyValues = {
|
||||
@@ -327,7 +329,7 @@ PETRA.Config.prototype.setConfig = function(gameState)
|
||||
API3.warn(" >>> Petra bot: personality = " + uneval(this.personality));
|
||||
};
|
||||
|
||||
PETRA.Config.prototype.Cheat = function(gameState)
|
||||
Config.prototype.Cheat = function(gameState)
|
||||
{
|
||||
// Sandbox, Very Easy, Easy, Medium, Hard, Very Hard
|
||||
// rate apply on resource stockpiling as gathering and trading
|
||||
@@ -342,7 +344,7 @@ PETRA.Config.prototype.Cheat = function(gameState)
|
||||
}, gameState.playerData.entity);
|
||||
};
|
||||
|
||||
PETRA.Config.prototype.Serialize = function()
|
||||
Config.prototype.Serialize = function()
|
||||
{
|
||||
var data = {};
|
||||
for (const key in this)
|
||||
@@ -351,7 +353,7 @@ PETRA.Config.prototype.Serialize = function()
|
||||
return data;
|
||||
};
|
||||
|
||||
PETRA.Config.prototype.Deserialize = function(data)
|
||||
Config.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Petra Bot",
|
||||
"description": "Petra is the default 0 A.D. AI bot. Please report issues to Wildfire Games (see the link in the main menu).\n\nThe AI's resource gathering rate and trade gain depend on the difficulty level (Sandbox −58%, Very Easy −44%, Easy −25%, Medium ±0%, Hard +25%, Very Hard +56%). The easiest levels also have a slower research, training, and building rate. In addition, the Sandbox level does not expand nor attack.",
|
||||
"moduleName" : "PETRA",
|
||||
"constructor": "PetraBot",
|
||||
"filename": "_petrabot.js",
|
||||
"useShared": true
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import { allowCapture, getLandAccess, getMaxStrength, isSiegeUnit, returnResources } from
|
||||
"simulation/ai/petra/entityExtend.js";
|
||||
import { TransportPlan } from "simulation/ai/petra/transportPlan.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Armies used by the defense manager.
|
||||
* An army is a collection of own entities and enemy entities.
|
||||
@@ -7,7 +12,7 @@
|
||||
* "capturing": army set to capture a gaia building or recover capture points to one of its own structures
|
||||
* It must contain only one foe (the building to capture) and never be merged
|
||||
*/
|
||||
PETRA.DefenseArmy = function(gameState, foeEntities, type)
|
||||
export function DefenseArmy(gameState, foeEntities, type)
|
||||
{
|
||||
this.ID = gameState.ai.uniqueIDs.armies++;
|
||||
this.type = type || "default";
|
||||
@@ -40,7 +45,7 @@ PETRA.DefenseArmy = function(gameState, foeEntities, type)
|
||||
this.recalculatePosition(gameState, true);
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* add an entity to the enemy army
|
||||
@@ -48,7 +53,7 @@ PETRA.DefenseArmy = function(gameState, foeEntities, type)
|
||||
* won't recalculate our position but will dirty it.
|
||||
* force is true at army creation or when merging armies, so in this case we should add it even if far
|
||||
*/
|
||||
PETRA.DefenseArmy.prototype.addFoe = function(gameState, enemyId, force)
|
||||
DefenseArmy.prototype.addFoe = function(gameState, enemyId, force)
|
||||
{
|
||||
if (this.foeEntities.indexOf(enemyId) !== -1)
|
||||
return false;
|
||||
@@ -73,7 +78,7 @@ PETRA.DefenseArmy.prototype.addFoe = function(gameState, enemyId, force)
|
||||
* returns true if the entity was removed and false otherwise.
|
||||
* TODO: when there is a technology update, we should probably recompute the strengths, or weird stuffs will happen.
|
||||
*/
|
||||
PETRA.DefenseArmy.prototype.removeFoe = function(gameState, enemyId, enemyEntity)
|
||||
DefenseArmy.prototype.removeFoe = function(gameState, enemyId, enemyEntity)
|
||||
{
|
||||
const idx = this.foeEntities.indexOf(enemyId);
|
||||
if (idx === -1)
|
||||
@@ -100,7 +105,7 @@ PETRA.DefenseArmy.prototype.removeFoe = function(gameState, enemyId, enemyEntity
|
||||
* adds a defender but doesn't assign him yet.
|
||||
* force is true when merging armies, so in this case we should add it even if no position as it can be in a ship
|
||||
*/
|
||||
PETRA.DefenseArmy.prototype.addOwn = function(gameState, id, force)
|
||||
DefenseArmy.prototype.addOwn = function(gameState, id, force)
|
||||
{
|
||||
if (this.ownEntities.indexOf(id) !== -1)
|
||||
return false;
|
||||
@@ -119,13 +124,13 @@ PETRA.DefenseArmy.prototype.addOwn = function(gameState, id, force)
|
||||
else
|
||||
ent.setMetadata(PlayerID, "plan", -3);
|
||||
const subrole = ent.getMetadata(PlayerID, "subrole");
|
||||
if (subrole === undefined || subrole !== PETRA.Worker.SUBROLE_DEFENDER)
|
||||
if (subrole === undefined || subrole !== Worker.SUBROLE_DEFENDER)
|
||||
ent.setMetadata(PlayerID, "formerSubrole", subrole);
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_DEFENDER);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_DEFENDER);
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.removeOwn = function(gameState, id, Entity)
|
||||
DefenseArmy.prototype.removeOwn = function(gameState, id, Entity)
|
||||
{
|
||||
const idx = this.ownEntities.indexOf(id);
|
||||
if (idx === -1)
|
||||
@@ -163,7 +168,7 @@ PETRA.DefenseArmy.prototype.removeOwn = function(gameState, id, Entity)
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
{
|
||||
const plan = gameState.ai.HQ.navalManager.getPlan(ent.getMetadata(PlayerID, "transport"));
|
||||
if (plan && plan.state === PETRA.TransportPlan.BOARDING && ent.position())
|
||||
if (plan && plan.state === TransportPlan.BOARDING && ent.position())
|
||||
plan.removeUnit(gameState, ent);
|
||||
}
|
||||
|
||||
@@ -191,7 +196,7 @@ PETRA.DefenseArmy.prototype.removeOwn = function(gameState, id, Entity)
|
||||
* resets the army properly.
|
||||
* assumes we already cleared dead units.
|
||||
*/
|
||||
PETRA.DefenseArmy.prototype.clear = function(gameState)
|
||||
DefenseArmy.prototype.clear = function(gameState)
|
||||
{
|
||||
while (this.foeEntities.length > 0)
|
||||
this.removeFoe(gameState, this.foeEntities[0]);
|
||||
@@ -247,7 +252,7 @@ PETRA.DefenseArmy.prototype.clear = function(gameState)
|
||||
const pos = struct.position();
|
||||
if (!pos || !gameState.isPlayerMutualAlly(gameState.ai.HQ.territoryMap.getOwner(pos)))
|
||||
continue;
|
||||
if (PETRA.getLandAccess(gameState, struct) !== armyAccess)
|
||||
if (getLandAccess(gameState, struct) !== armyAccess)
|
||||
continue;
|
||||
const defensiveStruct = struct.hasDefensiveFire();
|
||||
if (defensiveFound && !defensiveStruct)
|
||||
@@ -290,7 +295,7 @@ PETRA.DefenseArmy.prototype.clear = function(gameState)
|
||||
this.recalculatePosition(gameState);
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
{
|
||||
// we'll assume this defender is ours already.
|
||||
// we'll also override any previous assignment
|
||||
@@ -300,7 +305,7 @@ PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
return false;
|
||||
|
||||
// try to return its resources, and if any, the attack order will be queued
|
||||
const queued = PETRA.returnResources(gameState, ent);
|
||||
const queued = returnResources(gameState, ent);
|
||||
|
||||
let idMin;
|
||||
let distMin;
|
||||
@@ -312,7 +317,7 @@ PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
if (!eEnt || !eEnt.position()) // probably can't happen.
|
||||
continue;
|
||||
|
||||
if (!ent.canAttackTarget(eEnt, PETRA.allowCapture(gameState, ent, eEnt)))
|
||||
if (!ent.canAttackTarget(eEnt, allowCapture(gameState, ent, eEnt)))
|
||||
continue;
|
||||
|
||||
if (eEnt.hasClass("Unit") && eEnt.unitAIOrderData() && eEnt.unitAIOrderData().length &&
|
||||
@@ -324,7 +329,7 @@ PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
|
||||
// already enough units against it
|
||||
if (this.assignedAgainst[id].length > 8 ||
|
||||
this.assignedAgainst[id].length > 5 && !eEnt.hasClass("Hero") && !PETRA.isSiegeUnit(eEnt))
|
||||
this.assignedAgainst[id].length > 5 && !eEnt.hasClass("Hero") && !isSiegeUnit(eEnt))
|
||||
continue;
|
||||
|
||||
const dist = API3.SquareVectorDistance(ent.position(), eEnt.position());
|
||||
@@ -350,7 +355,7 @@ PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
else
|
||||
return false;
|
||||
|
||||
const ownIndex = PETRA.getLandAccess(gameState, ent);
|
||||
const ownIndex = getLandAccess(gameState, ent);
|
||||
const foeEnt = gameState.getEntityById(idFoe);
|
||||
const foePosition = foeEnt.position();
|
||||
const foeIndex = gameState.ai.accessibility.getAccessValue(foePosition);
|
||||
@@ -358,19 +363,19 @@ PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
{
|
||||
this.assignedTo[entID] = idFoe;
|
||||
this.assignedAgainst[idFoe].push(entID);
|
||||
ent.attack(idFoe, PETRA.allowCapture(gameState, ent, foeEnt), queued);
|
||||
ent.attack(idFoe, allowCapture(gameState, ent, foeEnt), queued);
|
||||
}
|
||||
else
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, ent, ownIndex, foeIndex, foePosition);
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.getType = function()
|
||||
DefenseArmy.prototype.getType = function()
|
||||
{
|
||||
return this.type;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.getState = function()
|
||||
DefenseArmy.prototype.getState = function()
|
||||
{
|
||||
if (!this.foeEntities.length)
|
||||
return 0;
|
||||
@@ -382,7 +387,7 @@ PETRA.DefenseArmy.prototype.getState = function()
|
||||
* assumes units are in only one army.
|
||||
* also assumes that all have been properly cleaned up (no dead units).
|
||||
*/
|
||||
PETRA.DefenseArmy.prototype.merge = function(gameState, otherArmy)
|
||||
DefenseArmy.prototype.merge = function(gameState, otherArmy)
|
||||
{
|
||||
// copy over all parameters.
|
||||
for (const i in otherArmy.assignedAgainst)
|
||||
@@ -407,7 +412,7 @@ PETRA.DefenseArmy.prototype.merge = function(gameState, otherArmy)
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.needsDefenders = function(gameState)
|
||||
DefenseArmy.prototype.needsDefenders = function(gameState)
|
||||
{
|
||||
let defenseRatio;
|
||||
const territoryOwner = gameState.ai.HQ.territoryMap.getOwner(this.foePosition);
|
||||
@@ -436,7 +441,7 @@ PETRA.DefenseArmy.prototype.needsDefenders = function(gameState)
|
||||
|
||||
|
||||
/** if not forced, will only recalculate if on a different turn. */
|
||||
PETRA.DefenseArmy.prototype.recalculatePosition = function(gameState, force)
|
||||
DefenseArmy.prototype.recalculatePosition = function(gameState, force)
|
||||
{
|
||||
if (!force && this.positionLastUpdate === gameState.ai.elapsedTime)
|
||||
return;
|
||||
@@ -463,7 +468,7 @@ PETRA.DefenseArmy.prototype.recalculatePosition = function(gameState, force)
|
||||
this.positionLastUpdate = gameState.ai.elapsedTime;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.recalculateStrengths = function(gameState)
|
||||
DefenseArmy.prototype.recalculateStrengths = function(gameState)
|
||||
{
|
||||
this.ownStrength = 0;
|
||||
this.foeStrength = 0;
|
||||
@@ -475,7 +480,7 @@ PETRA.DefenseArmy.prototype.recalculateStrengths = function(gameState)
|
||||
};
|
||||
|
||||
/** adds or remove the strength of the entity either to the enemy or to our units. */
|
||||
PETRA.DefenseArmy.prototype.evaluateStrength = function(ent, isOwn, remove)
|
||||
DefenseArmy.prototype.evaluateStrength = function(ent, isOwn, remove)
|
||||
{
|
||||
if (!ent)
|
||||
return;
|
||||
@@ -489,7 +494,7 @@ PETRA.DefenseArmy.prototype.evaluateStrength = function(ent, isOwn, remove)
|
||||
entStrength = 2;
|
||||
}
|
||||
else
|
||||
entStrength = PETRA.getMaxStrength(ent, this.Config.debug, this.Config.DamageTypeImportance);
|
||||
entStrength = getMaxStrength(ent, this.Config.debug, this.Config.DamageTypeImportance);
|
||||
|
||||
// TODO adapt the getMaxStrength function for animals.
|
||||
// For the time being, just increase it for elephants as the returned value is too small.
|
||||
@@ -505,7 +510,7 @@ PETRA.DefenseArmy.prototype.evaluateStrength = function(ent, isOwn, remove)
|
||||
this.foeStrength += entStrength;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.checkEvents = function(gameState, events)
|
||||
DefenseArmy.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
// Warning the metadata is already cloned in shared.js. Futhermore, changes should be done before destroyEvents
|
||||
// otherwise it would remove the old entity from this army list
|
||||
@@ -564,7 +569,7 @@ PETRA.DefenseArmy.prototype.checkEvents = function(gameState, events)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.update = function(gameState)
|
||||
DefenseArmy.prototype.update = function(gameState)
|
||||
{
|
||||
for (const entId of this.ownEntities)
|
||||
{
|
||||
@@ -577,7 +582,7 @@ PETRA.DefenseArmy.prototype.update = function(gameState)
|
||||
else if (orderData.length && orderData[0].target && orderData[0].attackType && orderData[0].attackType === "Capture")
|
||||
{
|
||||
const target = gameState.getEntityById(orderData[0].target);
|
||||
if (target && !PETRA.allowCapture(gameState, ent, target))
|
||||
if (target && !allowCapture(gameState, ent, target))
|
||||
ent.attack(orderData[0].target, false);
|
||||
}
|
||||
}
|
||||
@@ -629,7 +634,7 @@ PETRA.DefenseArmy.prototype.update = function(gameState)
|
||||
return breakaways;
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.Serialize = function()
|
||||
DefenseArmy.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"ID": this.ID,
|
||||
@@ -645,7 +650,7 @@ PETRA.DefenseArmy.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.DefenseArmy.prototype.Deserialize = function(data)
|
||||
DefenseArmy.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
PETRA.DefenseManager = function(Config)
|
||||
import { AttackPlan } from "simulation/ai/petra/attackPlan.js";
|
||||
import { DefenseArmy } from "simulation/ai/petra/defenseArmy.js";
|
||||
import { allowCapture, getLandAccess, getMaxStrength, isSiegeUnit } from
|
||||
"simulation/ai/petra/entityExtend.js";
|
||||
import { GarrisonManager } from "simulation/ai/petra/garrisonManager.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
export function DefenseManager(Config)
|
||||
{
|
||||
// Array of "army" Objects.
|
||||
this.armies = [];
|
||||
@@ -12,9 +19,9 @@ PETRA.DefenseManager = function(Config)
|
||||
this.attackingArmies = {};
|
||||
this.attackingUnits = {};
|
||||
this.attackedAllies = {};
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.DefenseManager.prototype.update = function(gameState, events)
|
||||
DefenseManager.prototype.update = function(gameState, events)
|
||||
{
|
||||
Engine.ProfileStart("Defense Manager");
|
||||
|
||||
@@ -65,7 +72,7 @@ PETRA.DefenseManager.prototype.update = function(gameState, events)
|
||||
Engine.ProfileStop();
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.makeIntoArmy = function(gameState, entityID, type = "default")
|
||||
DefenseManager.prototype.makeIntoArmy = function(gameState, entityID, type = "default")
|
||||
{
|
||||
if (type == "default")
|
||||
{
|
||||
@@ -74,15 +81,15 @@ PETRA.DefenseManager.prototype.makeIntoArmy = function(gameState, entityID, type
|
||||
return;
|
||||
}
|
||||
|
||||
this.armies.push(new PETRA.DefenseArmy(gameState, [entityID], type));
|
||||
this.armies.push(new DefenseArmy(gameState, [entityID], type));
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.getArmy = function(partOfArmy)
|
||||
DefenseManager.prototype.getArmy = function(partOfArmy)
|
||||
{
|
||||
return this.armies.find(army => army.ID == partOfArmy);
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.isDangerous = function(gameState, entity)
|
||||
DefenseManager.prototype.isDangerous = function(gameState, entity)
|
||||
{
|
||||
if (!entity.position())
|
||||
return false;
|
||||
@@ -190,7 +197,7 @@ PETRA.DefenseManager.prototype.isDangerous = function(gameState, entity)
|
||||
return false;
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.checkEnemyUnits = function(gameState)
|
||||
DefenseManager.prototype.checkEnemyUnits = function(gameState)
|
||||
{
|
||||
const nbPlayers = gameState.sharedScript.playersData.length;
|
||||
const i = gameState.ai.playedTurn % nbPlayers;
|
||||
@@ -266,7 +273,7 @@ PETRA.DefenseManager.prototype.checkEnemyUnits = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.checkEnemyArmies = function(gameState)
|
||||
DefenseManager.prototype.checkEnemyArmies = function(gameState)
|
||||
{
|
||||
for (let i = 0; i < this.armies.length; ++i)
|
||||
{
|
||||
@@ -379,7 +386,7 @@ PETRA.DefenseManager.prototype.checkEnemyArmies = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
{
|
||||
if (!this.armies.length)
|
||||
return;
|
||||
@@ -398,7 +405,7 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
const ent = gameState.getEntityById(entId);
|
||||
if (!ent || !ent.position())
|
||||
continue;
|
||||
armyAccess = PETRA.getLandAccess(gameState, ent);
|
||||
armyAccess = getLandAccess(gameState, ent);
|
||||
break;
|
||||
}
|
||||
if (!armyAccess)
|
||||
@@ -430,8 +437,11 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
{
|
||||
const subrole = ent.getMetadata(PlayerID, "subrole");
|
||||
if (subrole &&
|
||||
(subrole === PETRA.Worker.SUBROLE_COMPLETING || subrole === PETRA.Worker.SUBROLE_WALKING || subrole === PETRA.Worker.SUBROLE_ATTACKING))
|
||||
(subrole === Worker.SUBROLE_COMPLETING || subrole === Worker.SUBROLE_WALKING ||
|
||||
subrole === Worker.SUBROLE_ATTACKING))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
potentialDefenders.push(ent.id());
|
||||
});
|
||||
@@ -449,7 +459,7 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
continue;
|
||||
let aMin;
|
||||
let distMin;
|
||||
const access = ipass == 0 ? PETRA.getLandAccess(gameState, ent) : undefined;
|
||||
const access = ipass == 0 ? getLandAccess(gameState, ent) : undefined;
|
||||
for (let a = 0; a < armiesNeeding.length; ++a)
|
||||
{
|
||||
if (access && armiesNeeding[a].access != access)
|
||||
@@ -458,7 +468,7 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
// Do not assign defender if it cannot attack at least part of the attacking army.
|
||||
if (!armiesNeeding[a].army.foeEntities.some(eEnt => {
|
||||
const eEntID = gameState.getEntityById(eEnt);
|
||||
return ent.canAttackTarget(eEntID, PETRA.allowCapture(gameState, ent, eEntID));
|
||||
return ent.canAttackTarget(eEntID, allowCapture(gameState, ent, eEntID));
|
||||
}))
|
||||
continue;
|
||||
|
||||
@@ -481,7 +491,8 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
else if (aMin === undefined)
|
||||
continue;
|
||||
|
||||
armiesNeeding[aMin].need -= PETRA.getMaxStrength(ent, this.Config.debug, this.Config.DamageTypeImportance);
|
||||
armiesNeeding[aMin].need -=
|
||||
getMaxStrength(ent, this.Config.debug, this.Config.DamageTypeImportance);
|
||||
armiesNeeding[aMin].army.addOwn(gameState, potentialDefenders[i]);
|
||||
armiesNeeding[aMin].army.assignUnit(gameState, potentialDefenders[i]);
|
||||
potentialDefenders[i] = undefined;
|
||||
@@ -500,7 +511,7 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
gameState.ai.HQ.trainEmergencyUnits(gameState, armiesPos);
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.abortArmy = function(gameState, army)
|
||||
DefenseManager.prototype.abortArmy = function(gameState, army)
|
||||
{
|
||||
army.clear(gameState);
|
||||
for (let i = 0; i < this.armies.length; ++i)
|
||||
@@ -518,7 +529,7 @@ PETRA.DefenseManager.prototype.abortArmy = function(gameState, army)
|
||||
* and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress.
|
||||
* If our hero is attacked with regicide victory condition, the victoryManager will handle it.
|
||||
*/
|
||||
PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
// Must be called every turn for all armies.
|
||||
for (const army of this.armies)
|
||||
@@ -607,7 +618,7 @@ PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
if (plan !== undefined && plan >= 0)
|
||||
{
|
||||
const attack = gameState.ai.HQ.attackManager.getPlan(plan);
|
||||
if (attack && attack.state != PETRA.AttackPlan.STATE_UNEXECUTED)
|
||||
if (attack && attack.state != AttackPlan.STATE_UNEXECUTED)
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -689,7 +700,7 @@ PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
continue;
|
||||
if (unitAIStateOrder === "REPAIR" && currentTarget.hasDefensiveFire())
|
||||
continue;
|
||||
if (unitAIStateOrder === "COMBAT" && !PETRA.isSiegeUnit(currentTarget) &&
|
||||
if (unitAIStateOrder === "COMBAT" && !isSiegeUnit(currentTarget) &&
|
||||
gameState.ai.HQ.capturableTargets.has(orderData[0].target))
|
||||
{
|
||||
// Take the nearest unit also attacking this structure to help us.
|
||||
@@ -703,8 +714,11 @@ PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
if (allAttacked[entId])
|
||||
continue;
|
||||
const ent = gameState.getEntityById(entId);
|
||||
if (!ent || !ent.position() || !ent.canAttackTarget(attacker, PETRA.allowCapture(gameState, ent, attacker)))
|
||||
if (!ent || !ent.position() || !ent.canAttackTarget(attacker,
|
||||
allowCapture(gameState, ent, attacker)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Check that the unit is still attacking the structure (since the last played turn).
|
||||
const state = ent.unitAIState();
|
||||
if (!state || !state.split(".")[1] || state.split(".")[1] != "COMBAT")
|
||||
@@ -722,18 +736,18 @@ PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
if (minEnt)
|
||||
{
|
||||
capturableTarget.ents.delete(minEnt.id());
|
||||
minEnt.attack(attacker.id(), PETRA.allowCapture(gameState, minEnt, attacker));
|
||||
minEnt.attack(attacker.id(), allowCapture(gameState, minEnt, attacker));
|
||||
}
|
||||
}
|
||||
}
|
||||
const allowCapture = PETRA.allowCapture(gameState, target, attacker);
|
||||
if (target.canAttackTarget(attacker, allowCapture))
|
||||
target.attack(attacker.id(), allowCapture);
|
||||
const shouldCapture = allowCapture(gameState, target, attacker);
|
||||
if (target.canAttackTarget(attacker, shouldCapture))
|
||||
target.attack(attacker.id(), shouldCapture);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.garrisonUnitsInside = function(gameState, target, data)
|
||||
DefenseManager.prototype.garrisonUnitsInside = function(gameState, target, data)
|
||||
{
|
||||
if (target.hitpoints() < target.garrisonEjectHealth() * target.maxHitpoints())
|
||||
return false;
|
||||
@@ -750,16 +764,19 @@ PETRA.DefenseManager.prototype.garrisonUnitsInside = function(gameState, target,
|
||||
if (dist >= range*range)
|
||||
return false;
|
||||
}
|
||||
const access = PETRA.getLandAccess(gameState, target);
|
||||
const access = getLandAccess(gameState, target);
|
||||
const garrisonManager = gameState.ai.HQ.garrisonManager;
|
||||
const garrisonArrowClasses = target.getGarrisonArrowClasses();
|
||||
const typeGarrison = data.type || PETRA.GarrisonManager.TYPE_PROTECTION;
|
||||
const typeGarrison = data.type || GarrisonManager.TYPE_PROTECTION;
|
||||
let allowMelee = gameState.ai.HQ.garrisonManager.allowMelee(target);
|
||||
if (allowMelee === undefined)
|
||||
{
|
||||
// Should be kept in sync with garrisonManager to avoid garrisoning-ungarrisoning some units.
|
||||
if (data.attacker)
|
||||
allowMelee = data.attacker.hasClass("Structure") ? data.attacker.attackRange("Ranged") : !PETRA.isSiegeUnit(data.attacker);
|
||||
{
|
||||
allowMelee = data.attacker.hasClass("Structure") ? data.attacker.attackRange("Ranged") :
|
||||
!isSiegeUnit(data.attacker);
|
||||
}
|
||||
else
|
||||
allowMelee = true;
|
||||
}
|
||||
@@ -768,7 +785,7 @@ PETRA.DefenseManager.prototype.garrisonUnitsInside = function(gameState, target,
|
||||
return false;
|
||||
if (!ent.hasClasses(garrisonArrowClasses))
|
||||
return false;
|
||||
if (typeGarrison !== PETRA.GarrisonManager.TYPE_DECAY && !allowMelee && ent.attackTypes().indexOf("Melee") != -1)
|
||||
if (typeGarrison !== GarrisonManager.TYPE_DECAY && !allowMelee && ent.attackTypes().indexOf("Melee") != -1)
|
||||
return false;
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
return false;
|
||||
@@ -779,11 +796,14 @@ PETRA.DefenseManager.prototype.garrisonUnitsInside = function(gameState, target,
|
||||
{
|
||||
const subrole = ent.getMetadata(PlayerID, "subrole");
|
||||
// When structure decaying (usually because we've just captured it in enemy territory), also allow units from an attack plan.
|
||||
if (typeGarrison !== PETRA.GarrisonManager.TYPE_DECAY && subrole &&
|
||||
(subrole === PETRA.Worker.SUBROLE_COMPLETING || subrole === PETRA.Worker.SUBROLE_WALKING || subrole === PETRA.Worker.SUBROLE_ATTACKING))
|
||||
if (typeGarrison !== GarrisonManager.TYPE_DECAY && subrole &&
|
||||
(subrole === Worker.SUBROLE_COMPLETING || subrole === Worker.SUBROLE_WALKING ||
|
||||
subrole === Worker.SUBROLE_ATTACKING))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (PETRA.getLandAccess(gameState, ent) != access)
|
||||
if (getLandAccess(gameState, ent) != access)
|
||||
return false;
|
||||
return true;
|
||||
}).filterNearest(target.position());
|
||||
@@ -809,11 +829,11 @@ PETRA.DefenseManager.prototype.garrisonUnitsInside = function(gameState, target,
|
||||
};
|
||||
|
||||
/** Garrison a attacked siege ranged unit inside the nearest fortress. */
|
||||
PETRA.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
|
||||
DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
|
||||
{
|
||||
let distmin = Math.min();
|
||||
let nearest;
|
||||
const unitAccess = PETRA.getLandAccess(gameState, unit);
|
||||
const unitAccess = getLandAccess(gameState, unit);
|
||||
const garrisonManager = gameState.ai.HQ.garrisonManager;
|
||||
for (const ent of gameState.getAllyStructures().values())
|
||||
{
|
||||
@@ -825,7 +845,7 @@ PETRA.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
|
||||
continue;
|
||||
if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints())
|
||||
continue;
|
||||
if (PETRA.getLandAccess(gameState, ent) != unitAccess)
|
||||
if (getLandAccess(gameState, ent) != unitAccess)
|
||||
continue;
|
||||
const dist = API3.SquareVectorDistance(ent.position(), unit.position());
|
||||
if (dist > distmin)
|
||||
@@ -834,7 +854,7 @@ PETRA.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
|
||||
nearest = ent;
|
||||
}
|
||||
if (nearest)
|
||||
garrisonManager.garrison(gameState, unit, nearest, PETRA.GarrisonManager.TYPE_PROTECTION);
|
||||
garrisonManager.garrison(gameState, unit, nearest, GarrisonManager.TYPE_PROTECTION);
|
||||
return nearest !== undefined;
|
||||
};
|
||||
|
||||
@@ -843,11 +863,11 @@ PETRA.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
|
||||
* If emergency is true, the unit will be garrisoned in the closest possible structure.
|
||||
* Otherwise, it will garrison in the closest healing structure.
|
||||
*/
|
||||
PETRA.DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit, emergency = false)
|
||||
DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit, emergency = false)
|
||||
{
|
||||
let distmin = Math.min();
|
||||
let nearest;
|
||||
const unitAccess = PETRA.getLandAccess(gameState, unit);
|
||||
const unitAccess = getLandAccess(gameState, unit);
|
||||
const garrisonManager = gameState.ai.HQ.garrisonManager;
|
||||
for (const ent of gameState.getAllyStructures().values())
|
||||
{
|
||||
@@ -862,7 +882,7 @@ PETRA.DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit,
|
||||
continue;
|
||||
if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints())
|
||||
continue;
|
||||
if (PETRA.getLandAccess(gameState, ent) != unitAccess)
|
||||
if (getLandAccess(gameState, ent) != unitAccess)
|
||||
continue;
|
||||
const dist = API3.SquareVectorDistance(ent.position(), unit.position());
|
||||
if (dist > distmin)
|
||||
@@ -875,20 +895,21 @@ PETRA.DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit,
|
||||
|
||||
if (!emergency)
|
||||
{
|
||||
garrisonManager.garrison(gameState, unit, nearest, PETRA.GarrisonManager.TYPE_PROTECTION);
|
||||
garrisonManager.garrison(gameState, unit, nearest, GarrisonManager.TYPE_PROTECTION);
|
||||
return true;
|
||||
}
|
||||
if (garrisonManager.numberOfGarrisonedSlots(nearest) >= nearest.garrisonMax()) // make room for this ent
|
||||
nearest.unload(nearest.garrisoned()[0]);
|
||||
|
||||
garrisonManager.garrison(gameState, unit, nearest, nearest.buffHeal() ? PETRA.GarrisonManager.TYPE_PROTECTION : PETRA.GarrisonManager.TYPE_EMERGENCY);
|
||||
garrisonManager.garrison(gameState, unit, nearest,
|
||||
nearest.buffHeal() ? GarrisonManager.TYPE_PROTECTION : GarrisonManager.TYPE_EMERGENCY);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Be more inclined to help an ally attacked by several enemies.
|
||||
*/
|
||||
PETRA.DefenseManager.prototype.GetCooperationLevel = function(ally)
|
||||
DefenseManager.prototype.GetCooperationLevel = function(ally)
|
||||
{
|
||||
let cooperation = this.Config.personality.cooperative;
|
||||
if (this.attackedAllies[ally] && this.attackedAllies[ally] > 1)
|
||||
@@ -899,7 +920,7 @@ PETRA.DefenseManager.prototype.GetCooperationLevel = function(ally)
|
||||
/**
|
||||
* Switch a defense army into an attack if needed.
|
||||
*/
|
||||
PETRA.DefenseManager.prototype.switchToAttack = function(gameState, army)
|
||||
DefenseManager.prototype.switchToAttack = function(gameState, army)
|
||||
{
|
||||
if (!army)
|
||||
return;
|
||||
@@ -908,12 +929,12 @@ PETRA.DefenseManager.prototype.switchToAttack = function(gameState, army)
|
||||
const target = gameState.getEntityById(targetId);
|
||||
if (!target || !target.position() || !gameState.isPlayerEnemy(target.owner()))
|
||||
continue;
|
||||
const targetAccess = PETRA.getLandAccess(gameState, target);
|
||||
const targetAccess = getLandAccess(gameState, target);
|
||||
const targetPos = target.position();
|
||||
for (const entId of army.ownEntities)
|
||||
{
|
||||
const ent = gameState.getEntityById(entId);
|
||||
if (!ent || !ent.position() || PETRA.getLandAccess(gameState, ent) != targetAccess)
|
||||
if (!ent || !ent.position() || getLandAccess(gameState, ent) != targetAccess)
|
||||
continue;
|
||||
if (API3.SquareVectorDistance(targetPos, ent.position()) > 14400)
|
||||
continue;
|
||||
@@ -923,7 +944,7 @@ PETRA.DefenseManager.prototype.switchToAttack = function(gameState, army)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.Serialize = function()
|
||||
DefenseManager.prototype.Serialize = function()
|
||||
{
|
||||
const properties = {
|
||||
"targetList": this.targetList,
|
||||
@@ -940,7 +961,7 @@ PETRA.DefenseManager.prototype.Serialize = function()
|
||||
return { "properties": properties, "armies": armies };
|
||||
};
|
||||
|
||||
PETRA.DefenseManager.prototype.Deserialize = function(gameState, data)
|
||||
DefenseManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data.properties)
|
||||
this[key] = data.properties[key];
|
||||
@@ -948,7 +969,7 @@ PETRA.DefenseManager.prototype.Deserialize = function(gameState, data)
|
||||
this.armies = [];
|
||||
for (const dataArmy of data.armies)
|
||||
{
|
||||
const army = new PETRA.DefenseArmy(gameState, []);
|
||||
const army = new DefenseArmy(gameState, []);
|
||||
army.Deserialize(dataArmy);
|
||||
this.armies.push(army);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import * as chat from "simulation/ai/petra/chatHelper.js";
|
||||
|
||||
/**
|
||||
* Manage the diplomacy:
|
||||
* update our cooperative trait
|
||||
@@ -20,7 +22,7 @@
|
||||
* sent through AIInterface. It is expected that the other player will change their diplomacy stance to the stance
|
||||
* that we suggested within a period of time, or else the request will be deleted from this.sentDiplomacyRequests.
|
||||
*/
|
||||
PETRA.DiplomacyManager = function(Config)
|
||||
export function DiplomacyManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.nextTributeUpdate = 90;
|
||||
@@ -32,13 +34,13 @@ PETRA.DiplomacyManager = function(Config)
|
||||
this.receivedDiplomacyRequests = new Map();
|
||||
this.sentDiplomacyRequests = new Map();
|
||||
this.sentDiplomacyRequestLapseTime = 120 + randFloat(10, 100);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any players that are allied/neutral with us but we are not allied/neutral with them,
|
||||
* treat this situation like an ally/neutral request.
|
||||
*/
|
||||
PETRA.DiplomacyManager.prototype.init = function(gameState)
|
||||
DiplomacyManager.prototype.init = function(gameState)
|
||||
{
|
||||
this.lastManStandingCheck(gameState);
|
||||
|
||||
@@ -60,7 +62,7 @@ PETRA.DiplomacyManager.prototype.init = function(gameState)
|
||||
* Check if any allied needs help (tribute) and sent it if we have enough resource
|
||||
* or ask for a tribute if we are in need and one ally can help
|
||||
*/
|
||||
PETRA.DiplomacyManager.prototype.tributes = function(gameState)
|
||||
DiplomacyManager.prototype.tributes = function(gameState)
|
||||
{
|
||||
this.nextTributeUpdate = gameState.ai.elapsedTime + 30;
|
||||
const resTribCodes = Resources.GetTributableCodes();
|
||||
@@ -104,7 +106,7 @@ PETRA.DiplomacyManager.prototype.tributes = function(gameState)
|
||||
{
|
||||
this.nextTributeRequest.set("all", gameState.ai.elapsedTime + 90);
|
||||
this.nextTributeRequest.set(res, gameState.ai.elapsedTime + 240);
|
||||
PETRA.chatRequestTribute(gameState, res);
|
||||
chat.requestTribute(gameState, res);
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("Tribute on " + res + " requested to player " + i);
|
||||
break;
|
||||
@@ -117,12 +119,12 @@ PETRA.DiplomacyManager.prototype.tributes = function(gameState)
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("Tribute " + uneval(tribute) + " sent to player " + i);
|
||||
if (this.Config.chat)
|
||||
PETRA.chatSentTribute(gameState, i);
|
||||
chat.sentTribute(gameState, i);
|
||||
Engine.PostCommand(PlayerID, { "type": "tribute", "player": i, "amounts": tribute });
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.checkEvents = function(gameState, events)
|
||||
DiplomacyManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
// Increase slowly the cooperative personality trait either when we receive tribute from our allies
|
||||
// or if our allies attack enemies inside our territory
|
||||
@@ -220,7 +222,7 @@ PETRA.DiplomacyManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
const response = request !== undefined && (request.status === "declinedRequest" || request.status === "allianceBroken") ?
|
||||
"decline" : "declineSuggestNeutral";
|
||||
PETRA.chatAnswerRequestDiplomacy(gameState, evt.player, "ally", response);
|
||||
chat.answerRequestDiplomacy(gameState, evt.player, "ally", response);
|
||||
}
|
||||
else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerNeutral(evt.player))
|
||||
this.handleDiplomacyRequest(gameState, evt.player, "ally");
|
||||
@@ -276,7 +278,7 @@ PETRA.DiplomacyManager.prototype.checkEvents = function(gameState, events)
|
||||
* If the "Last Man Standing" option is enabled, check if the only remaining players are allies or neutral.
|
||||
* If so, turn against the strongest first, but be more likely to first turn against neutral players, if there are any.
|
||||
*/
|
||||
PETRA.DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
|
||||
DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
|
||||
{
|
||||
if (gameState.sharedScript.playersData[PlayerID].teamsLocked || gameState.isCeasefireActive() ||
|
||||
gameState.getAlliedVictory() && gameState.hasAllies())
|
||||
@@ -356,7 +358,7 @@ PETRA.DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
|
||||
if (request && request.status !== "allianceBroken")
|
||||
{
|
||||
if (request.status === "waitingForTribute")
|
||||
PETRA.chatAnswerRequestDiplomacy(gameState, playerToTurnAgainst, request.requestType, "decline");
|
||||
chat.answerRequestDiplomacy(gameState, playerToTurnAgainst, request.requestType, "decline");
|
||||
request.status = request.status === "accepted" ? "allianceBroken" : "declinedRequest";
|
||||
}
|
||||
// If we had sent this player a diplomacy request, just rescind it
|
||||
@@ -370,7 +372,7 @@ PETRA.DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
|
||||
* Do not become allies with a player if the game would be over.
|
||||
* Overall, be reluctant to become allies with any one player, but be more likely to accept neutral requests.
|
||||
*/
|
||||
PETRA.DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, player, requestType)
|
||||
DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, player, requestType)
|
||||
{
|
||||
if (gameState.sharedScript.playersData[PlayerID].teamsLocked)
|
||||
return;
|
||||
@@ -431,10 +433,10 @@ PETRA.DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, pl
|
||||
response = "decline";
|
||||
}
|
||||
}
|
||||
PETRA.chatAnswerRequestDiplomacy(gameState, player, requestType, response, requiredTribute);
|
||||
chat.answerRequestDiplomacy(gameState, player, requestType, response, requiredTribute);
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, newDiplomaticStance)
|
||||
DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, newDiplomaticStance)
|
||||
{
|
||||
if (gameState.isPlayerEnemy(player) && (newDiplomaticStance === "ally" || newDiplomaticStance === "neutral"))
|
||||
gameState.ai.HQ.attackManager.cancelAttacksAgainstPlayer(gameState, player);
|
||||
@@ -442,10 +444,10 @@ PETRA.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, pla
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("diplomacy stance with player " + player + " is now " + newDiplomaticStance);
|
||||
if (this.Config.chat)
|
||||
PETRA.chatNewDiplomacy(gameState, player, newDiplomaticStance);
|
||||
chat.newDiplomacy(gameState, player, newDiplomaticStance);
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.checkRequestedTributes = function(gameState)
|
||||
DiplomacyManager.prototype.checkRequestedTributes = function(gameState)
|
||||
{
|
||||
for (const [player, data] of this.receivedDiplomacyRequests)
|
||||
if (data.status === "waitingForTribute" && gameState.ai.elapsedTime > data.warnTime)
|
||||
@@ -453,13 +455,13 @@ PETRA.DiplomacyManager.prototype.checkRequestedTributes = function(gameState)
|
||||
if (data.sentWarning)
|
||||
{
|
||||
this.receivedDiplomacyRequests.delete(player);
|
||||
PETRA.chatAnswerRequestDiplomacy(gameState, player, data.requestType, "decline");
|
||||
chat.answerRequestDiplomacy(gameState, player, data.requestType, "decline");
|
||||
}
|
||||
else
|
||||
{
|
||||
data.sentWarning = true;
|
||||
data.warnTime = gameState.ai.elapsedTime + 60;
|
||||
PETRA.chatAnswerRequestDiplomacy(gameState, player, data.requestType, "waitingForTribute", {
|
||||
chat.answerRequestDiplomacy(gameState, player, data.requestType, "waitingForTribute", {
|
||||
"wanted": data.wanted,
|
||||
"type": data.type
|
||||
});
|
||||
@@ -471,7 +473,7 @@ PETRA.DiplomacyManager.prototype.checkRequestedTributes = function(gameState)
|
||||
* Try to become allies with a player who has a lot of mutual enemies in common with us.
|
||||
* TODO: Possibly let human players demand tributes from AIs who send diplomacy requests.
|
||||
*/
|
||||
PETRA.DiplomacyManager.prototype.sendDiplomacyRequest = function(gameState)
|
||||
DiplomacyManager.prototype.sendDiplomacyRequest = function(gameState)
|
||||
{
|
||||
let player;
|
||||
let max = 0;
|
||||
@@ -510,21 +512,21 @@ PETRA.DiplomacyManager.prototype.sendDiplomacyRequest = function(gameState)
|
||||
if (this.Config.debug > 0)
|
||||
API3.warn("Sending diplomacy request to player " + player + " with " + requestType);
|
||||
Engine.PostCommand(PlayerID, { "type": "diplomacy-request", "source": PlayerID, "player": player, "to": requestType });
|
||||
PETRA.chatNewRequestDiplomacy(gameState, player, requestType, "sendRequest");
|
||||
chat.newRequestDiplomacy(gameState, player, requestType, "sendRequest");
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.checkSentDiplomacyRequests = function(gameState)
|
||||
DiplomacyManager.prototype.checkSentDiplomacyRequests = function(gameState)
|
||||
{
|
||||
for (const [player, data] of this.sentDiplomacyRequests)
|
||||
if (gameState.ai.elapsedTime > data.timeSent + 60 && !gameState.ai.HQ.saveResources &&
|
||||
gameState.getPopulation() > 70)
|
||||
{
|
||||
PETRA.chatNewRequestDiplomacy(gameState, player, data.requestType, "requestExpired");
|
||||
chat.newRequestDiplomacy(gameState, player, data.requestType, "requestExpired");
|
||||
this.sentDiplomacyRequests.delete(player);
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.update = function(gameState, events)
|
||||
DiplomacyManager.prototype.update = function(gameState, events)
|
||||
{
|
||||
this.checkEvents(gameState, events);
|
||||
|
||||
@@ -552,7 +554,7 @@ PETRA.DiplomacyManager.prototype.update = function(gameState, events)
|
||||
this.checkSentDiplomacyRequests(gameState);
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.Serialize = function()
|
||||
DiplomacyManager.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"nextTributeUpdate": this.nextTributeUpdate,
|
||||
@@ -566,7 +568,7 @@ PETRA.DiplomacyManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.DiplomacyManager.prototype.Deserialize = function(data)
|
||||
DiplomacyManager.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import { emergency as chatEmergency } from "simulation/ai/petra/chatHelper.js";
|
||||
|
||||
/**
|
||||
* Checks for emergencies and acts accordingly
|
||||
*/
|
||||
PETRA.EmergencyManager = function(Config)
|
||||
export function EmergencyManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.referencePopulation = 0;
|
||||
this.referenceStructureCount = 0;
|
||||
this.numRoots = 0;
|
||||
this.hasEmergency = false;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.EmergencyManager.prototype.init = function(gameState)
|
||||
EmergencyManager.prototype.init = function(gameState)
|
||||
{
|
||||
this.referencePopulation = gameState.getPopulation();
|
||||
this.referenceStructureCount = gameState.getOwnStructures().length;
|
||||
this.numRoots = this.rootCount(gameState);
|
||||
};
|
||||
|
||||
PETRA.EmergencyManager.prototype.update = function(gameState)
|
||||
EmergencyManager.prototype.update = function(gameState)
|
||||
{
|
||||
if (this.hasEmergency)
|
||||
{
|
||||
@@ -42,7 +44,7 @@ PETRA.EmergencyManager.prototype.update = function(gameState)
|
||||
this.numRoots = nRoots;
|
||||
};
|
||||
|
||||
PETRA.EmergencyManager.prototype.emergencyUpdate = function(gameState)
|
||||
EmergencyManager.prototype.emergencyUpdate = function(gameState)
|
||||
{
|
||||
const pop = gameState.getPopulation();
|
||||
const nStructures = gameState.getOwnStructures().length;
|
||||
@@ -60,7 +62,7 @@ PETRA.EmergencyManager.prototype.emergencyUpdate = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.EmergencyManager.prototype.rootCount = function(gameState)
|
||||
EmergencyManager.prototype.rootCount = function(gameState)
|
||||
{
|
||||
let roots = 0;
|
||||
gameState.getOwnStructures().toEntityArray().forEach(ent => {
|
||||
@@ -70,13 +72,13 @@ PETRA.EmergencyManager.prototype.rootCount = function(gameState)
|
||||
return roots;
|
||||
};
|
||||
|
||||
PETRA.EmergencyManager.prototype.setEmergency = function(gameState, enable)
|
||||
EmergencyManager.prototype.setEmergency = function(gameState, enable)
|
||||
{
|
||||
this.hasEmergency = enable;
|
||||
PETRA.chatEmergency(gameState, enable);
|
||||
chatEmergency(gameState, enable);
|
||||
};
|
||||
|
||||
PETRA.EmergencyManager.prototype.Serialize = function()
|
||||
EmergencyManager.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"referencePopulation": this.referencePopulation,
|
||||
@@ -86,7 +88,7 @@ PETRA.EmergencyManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.EmergencyManager.prototype.Deserialize = function(data)
|
||||
EmergencyManager.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/** returns true if this unit should be considered as a siege unit */
|
||||
PETRA.isSiegeUnit = function(ent)
|
||||
export function isSiegeUnit(ent)
|
||||
{
|
||||
return ent.hasClasses(["Siege", "Elephant+Melee"]);
|
||||
};
|
||||
}
|
||||
|
||||
/** returns true if this unit should be considered as "fast". */
|
||||
PETRA.isFastMoving = function(ent)
|
||||
export function isFastMoving(ent)
|
||||
{
|
||||
// TODO: use clever logic based on walkspeed comparisons.
|
||||
return ent.hasClass("FastMoving");
|
||||
};
|
||||
}
|
||||
|
||||
/** returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. */
|
||||
PETRA.getMaxStrength = function(ent, debugLevel, DamageTypeImportance, againstClass)
|
||||
export function getMaxStrength(ent, debugLevel, DamageTypeImportance, againstClass)
|
||||
{
|
||||
let strength = 0;
|
||||
const attackTypes = ent.attackTypes();
|
||||
@@ -74,22 +74,22 @@ PETRA.getMaxStrength = function(ent, debugLevel, DamageTypeImportance, againstCl
|
||||
// ToDo: Add support for StatusEffects and Capture.
|
||||
|
||||
return strength * ent.maxHitpoints() / 100.0;
|
||||
};
|
||||
}
|
||||
|
||||
/** Get access and cache it (except for units as it can change) in metadata if not already done */
|
||||
PETRA.getLandAccess = function(gameState, ent)
|
||||
export function getLandAccess(gameState, ent)
|
||||
{
|
||||
if (ent.hasClass("Unit"))
|
||||
{
|
||||
const pos = ent.position();
|
||||
if (!pos)
|
||||
{
|
||||
const holder = PETRA.getHolder(gameState, ent);
|
||||
const holder = getHolder(gameState, ent);
|
||||
if (holder)
|
||||
return PETRA.getLandAccess(gameState, holder);
|
||||
return getLandAccess(gameState, holder);
|
||||
|
||||
API3.warn("Petra error: entity without position, but not garrisoned");
|
||||
PETRA.dumpEntity(ent);
|
||||
dumpEntity(ent);
|
||||
return undefined;
|
||||
}
|
||||
return gameState.ai.accessibility.getAccessValue(pos);
|
||||
@@ -122,10 +122,10 @@ PETRA.getLandAccess = function(gameState, ent)
|
||||
ent.setMetadata(PlayerID, "access", access);
|
||||
}
|
||||
return access;
|
||||
};
|
||||
}
|
||||
|
||||
/** Sea access always cached as it never changes */
|
||||
PETRA.getSeaAccess = function(gameState, ent)
|
||||
export function getSeaAccess(gameState, ent)
|
||||
{
|
||||
let sea = ent.getMetadata(PlayerID, "sea");
|
||||
if (!sea)
|
||||
@@ -149,15 +149,15 @@ PETRA.getSeaAccess = function(gameState, ent)
|
||||
ent.setMetadata(PlayerID, "sea", sea);
|
||||
}
|
||||
return sea;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.setSeaAccess = function(gameState, ent)
|
||||
export function setSeaAccess(gameState, ent)
|
||||
{
|
||||
PETRA.getSeaAccess(gameState, ent);
|
||||
};
|
||||
getSeaAccess(gameState, ent);
|
||||
}
|
||||
|
||||
/** Decide if we should try to capture (returns true) or destroy (return false) */
|
||||
PETRA.allowCapture = function(gameState, ent, target)
|
||||
export function allowCapture(gameState, ent, target)
|
||||
{
|
||||
if (!target.isCapturable() || !ent.canCapture(target))
|
||||
return false;
|
||||
@@ -182,7 +182,7 @@ PETRA.allowCapture = function(gameState, ent, target)
|
||||
const capturableTargets = gameState.ai.HQ.capturableTargets;
|
||||
if (!capturableTargets.has(target.id()))
|
||||
{
|
||||
capture = ent.captureStrength() * PETRA.getAttackBonus(ent, target, "Capture");
|
||||
capture = ent.captureStrength() * getAttackBonus(ent, target, "Capture");
|
||||
capturableTargets.set(target.id(), { "strength": capture, "ents": new Set([ent.id()]) });
|
||||
}
|
||||
else
|
||||
@@ -190,7 +190,7 @@ PETRA.allowCapture = function(gameState, ent, target)
|
||||
const capturable = capturableTargets.get(target.id());
|
||||
if (!capturable.ents.has(ent.id()))
|
||||
{
|
||||
capturable.strength += ent.captureStrength() * PETRA.getAttackBonus(ent, target, "Capture");
|
||||
capturable.strength += ent.captureStrength() * getAttackBonus(ent, target, "Capture");
|
||||
capturable.ents.add(ent.id());
|
||||
}
|
||||
capture = capturable.strength;
|
||||
@@ -200,9 +200,9 @@ PETRA.allowCapture = function(gameState, ent, target)
|
||||
if (target.hasDefensiveFire() && target.isGarrisonHolder() && target.garrisoned())
|
||||
return capture > antiCapture + sumCapturePoints/50;
|
||||
return capture > antiCapture + sumCapturePoints/80;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.getAttackBonus = function(ent, target, type)
|
||||
export function getAttackBonus(ent, target, type)
|
||||
{
|
||||
let attackBonus = 1;
|
||||
if (!ent.get("Attack/" + type) || !ent.get("Attack/" + type + "/Bonuses"))
|
||||
@@ -217,10 +217,10 @@ PETRA.getAttackBonus = function(ent, target, type)
|
||||
attackBonus *= bonus.Multiplier;
|
||||
}
|
||||
return attackBonus;
|
||||
};
|
||||
}
|
||||
|
||||
/** Makes the worker deposit the currently carried resources at the closest accessible dropsite */
|
||||
PETRA.returnResources = function(gameState, ent)
|
||||
export function returnResources(gameState, ent)
|
||||
{
|
||||
if (!ent.resourceCarrying() || !ent.resourceCarrying().length || !ent.position())
|
||||
return false;
|
||||
@@ -229,7 +229,7 @@ PETRA.returnResources = function(gameState, ent)
|
||||
|
||||
let closestDropsite;
|
||||
let distmin = Math.min();
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
const dropsiteCollection = gameState.playerData.hasSharedDropsites ?
|
||||
gameState.getAnyDropsites(resource) : gameState.getOwnDropsites(resource);
|
||||
for (const dropsite of dropsiteCollection.values())
|
||||
@@ -240,7 +240,7 @@ PETRA.returnResources = function(gameState, ent)
|
||||
// owner !== PlayerID can only happen when hasSharedDropsites === true, so no need to test it again
|
||||
if (owner !== PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
|
||||
continue;
|
||||
if (PETRA.getLandAccess(gameState, dropsite) != access)
|
||||
if (getLandAccess(gameState, dropsite) != access)
|
||||
continue;
|
||||
const dist = API3.SquareVectorDistance(ent.position(), dropsite.position());
|
||||
if (dist > distmin)
|
||||
@@ -253,14 +253,14 @@ PETRA.returnResources = function(gameState, ent)
|
||||
return false;
|
||||
ent.returnResources(closestDropsite);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/** is supply full taking into account gatherers affected during this turn */
|
||||
PETRA.IsSupplyFull = function(gameState, ent)
|
||||
export function isSupplyFull(gameState, ent)
|
||||
{
|
||||
return ent.isFull() === true ||
|
||||
ent.resourceSupplyNumGatherers() + gameState.ai.HQ.basesManager.GetTCGatherer(ent.id()) >= ent.maxGatherers();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best base (in terms of distance and accessIndex) for an entity.
|
||||
@@ -268,24 +268,24 @@ PETRA.IsSupplyFull = function(gameState, ent)
|
||||
* If nothing found, return the noBase for units and undefined for structures.
|
||||
* If exclude is given, we exclude the base with ID = exclude.
|
||||
*/
|
||||
PETRA.getBestBase = function(gameState, ent, onlyConstructedBase = false, exclude = false)
|
||||
export function getBestBase(gameState, ent, onlyConstructedBase = false, exclude = false)
|
||||
{
|
||||
let pos = ent.position();
|
||||
let accessIndex;
|
||||
if (!pos)
|
||||
{
|
||||
const holder = PETRA.getHolder(gameState, ent);
|
||||
const holder = getHolder(gameState, ent);
|
||||
if (!holder || !holder.position())
|
||||
{
|
||||
API3.warn("Petra error: entity without position, but not garrisoned");
|
||||
PETRA.dumpEntity(ent);
|
||||
dumpEntity(ent);
|
||||
return gameState.ai.HQ.basesManager.baselessBase();
|
||||
}
|
||||
pos = holder.position();
|
||||
accessIndex = PETRA.getLandAccess(gameState, holder);
|
||||
accessIndex = getLandAccess(gameState, holder);
|
||||
}
|
||||
else
|
||||
accessIndex = PETRA.getLandAccess(gameState, ent);
|
||||
accessIndex = getLandAccess(gameState, ent);
|
||||
|
||||
let distmin = Math.min();
|
||||
let dist;
|
||||
@@ -326,9 +326,9 @@ PETRA.getBestBase = function(gameState, ent, onlyConstructedBase = false, exclud
|
||||
if (!bestbase && !ent.hasClass("Structure"))
|
||||
bestbase = gameState.ai.HQ.basesManager.baselessBase();
|
||||
return bestbase;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.getHolder = function(gameState, ent)
|
||||
export function getHolder(gameState, ent)
|
||||
{
|
||||
for (const holder of gameState.getEntities().values())
|
||||
{
|
||||
@@ -336,22 +336,22 @@ PETRA.getHolder = function(gameState, ent)
|
||||
return holder;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
/** return the template of the built foundation if a foundation, otherwise return the entity itself */
|
||||
PETRA.getBuiltEntity = function(gameState, ent)
|
||||
export function getBuiltEntity(gameState, ent)
|
||||
{
|
||||
if (ent.foundationProgress() !== undefined)
|
||||
return gameState.getBuiltTemplate(ent.templateName());
|
||||
|
||||
return ent;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if it is not worth finishing this building (it would surely decay)
|
||||
* TODO implement the other conditions
|
||||
*/
|
||||
PETRA.isNotWorthBuilding = function(gameState, ent)
|
||||
export function isNotWorthBuilding(gameState, ent)
|
||||
{
|
||||
if (gameState.ai.HQ.territoryMap.getOwner(ent.position()) !== PlayerID)
|
||||
{
|
||||
@@ -360,12 +360,12 @@ PETRA.isNotWorthBuilding = function(gameState, ent)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the straight line between the two positions crosses an enemy territory
|
||||
*/
|
||||
PETRA.isLineInsideEnemyTerritory = function(gameState, pos1, pos2, step=70)
|
||||
export function isLineInsideEnemyTerritory(gameState, pos1, pos2, step=70)
|
||||
{
|
||||
const n = Math.floor(Math.sqrt(API3.SquareVectorDistance(pos1, pos2))/step) + 1;
|
||||
const stepx = (pos2[0] - pos1[0]) / n;
|
||||
@@ -378,9 +378,9 @@ PETRA.isLineInsideEnemyTerritory = function(gameState, pos1, pos2, step=70)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.gatherTreasure = function(gameState, ent, water = false)
|
||||
export function gatherTreasure(gameState, ent, water = false)
|
||||
{
|
||||
if (!gameState.ai.HQ.treasures.hasEntities())
|
||||
return false;
|
||||
@@ -390,16 +390,16 @@ PETRA.gatherTreasure = function(gameState, ent, water = false)
|
||||
return false;
|
||||
let treasureFound;
|
||||
let distmin = Math.min();
|
||||
const access = water ? PETRA.getSeaAccess(gameState, ent) : PETRA.getLandAccess(gameState, ent);
|
||||
const access = water ? getSeaAccess(gameState, ent) : getLandAccess(gameState, ent);
|
||||
for (const treasure of gameState.ai.HQ.treasures.values())
|
||||
{
|
||||
// let some time for the previous gatherer to reach the treasure before trying again
|
||||
const lastGathered = treasure.getMetadata(PlayerID, "lastGathered");
|
||||
if (lastGathered && gameState.ai.elapsedTime - lastGathered < 20)
|
||||
continue;
|
||||
if (!water && access != PETRA.getLandAccess(gameState, treasure))
|
||||
if (!water && access != getLandAccess(gameState, treasure))
|
||||
continue;
|
||||
if (water && access != PETRA.getSeaAccess(gameState, treasure))
|
||||
if (water && access != getSeaAccess(gameState, treasure))
|
||||
continue;
|
||||
const territoryOwner = gameState.ai.HQ.territoryMap.getOwner(treasure.position());
|
||||
if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner))
|
||||
@@ -418,9 +418,9 @@ PETRA.gatherTreasure = function(gameState, ent, water = false)
|
||||
ent.collectTreasure(treasureFound);
|
||||
ent.setMetadata(PlayerID, "treasure", treasureFound.id());
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.dumpEntity = function(ent)
|
||||
export function dumpEntity(ent)
|
||||
{
|
||||
if (!ent)
|
||||
return;
|
||||
@@ -437,4 +437,4 @@ PETRA.dumpEntity = function(ent)
|
||||
" gather-type " + ent.getMetadata(PlayerID, "gather-type") +
|
||||
" target-foundation " + ent.getMetadata(PlayerID, "target-foundation") +
|
||||
" PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy"));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { dumpEntity, isSiegeUnit } from "simulation/ai/petra/entityExtend.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Manage the garrisonHolders
|
||||
* When a unit is ordered to garrison, it must be done through this.garrison() function so that
|
||||
@@ -6,20 +9,20 @@
|
||||
* Futhermore garrison units have a metadata garrisonType describing its reason (protection, transport, ...)
|
||||
*/
|
||||
|
||||
PETRA.GarrisonManager = function(Config)
|
||||
export function GarrisonManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.holders = new Map();
|
||||
this.decayingStructures = new Map();
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.GarrisonManager.TYPE_FORCE = "force";
|
||||
PETRA.GarrisonManager.TYPE_TRADE = "trade";
|
||||
PETRA.GarrisonManager.TYPE_PROTECTION = "protection";
|
||||
PETRA.GarrisonManager.TYPE_DECAY = "decay";
|
||||
PETRA.GarrisonManager.TYPE_EMERGENCY = "emergency";
|
||||
GarrisonManager.TYPE_FORCE = "force";
|
||||
GarrisonManager.TYPE_TRADE = "trade";
|
||||
GarrisonManager.TYPE_PROTECTION = "protection";
|
||||
GarrisonManager.TYPE_DECAY = "decay";
|
||||
GarrisonManager.TYPE_EMERGENCY = "emergency";
|
||||
|
||||
PETRA.GarrisonManager.prototype.update = function(gameState, events)
|
||||
GarrisonManager.prototype.update = function(gameState, events)
|
||||
{
|
||||
// First check for possible upgrade of a structure
|
||||
for (const evt of events.EntityRenamed)
|
||||
@@ -115,7 +118,7 @@ PETRA.GarrisonManager.prototype.update = function(gameState, events)
|
||||
API3.warn("Petra garrison error: unit " + ent.id() + " (" + ent.genericName() +
|
||||
") is expected to garrison in " + id + " (" + holder.genericName() +
|
||||
"), but has no such garrison order " + uneval(ent.unitAIOrderData()));
|
||||
PETRA.dumpEntity(ent);
|
||||
dumpEntity(ent);
|
||||
}
|
||||
list.splice(j--, 1);
|
||||
}
|
||||
@@ -151,7 +154,7 @@ PETRA.GarrisonManager.prototype.update = function(gameState, events)
|
||||
continue;
|
||||
if (ent.hasClass("Structure"))
|
||||
around.defenseStructure = true;
|
||||
else if (PETRA.isSiegeUnit(ent))
|
||||
else if (isSiegeUnit(ent))
|
||||
{
|
||||
if (ent.attackTypes().indexOf("Melee") !== -1)
|
||||
around.meleeSiege = true;
|
||||
@@ -200,12 +203,12 @@ PETRA.GarrisonManager.prototype.update = function(gameState, events)
|
||||
if (!ent || ent.owner() !== PlayerID)
|
||||
this.decayingStructures.delete(id);
|
||||
else if (this.numberOfGarrisonedSlots(ent) < gmin)
|
||||
gameState.ai.HQ.defenseManager.garrisonUnitsInside(gameState, ent, { "min": gmin, "type": PETRA.GarrisonManager.TYPE_DECAY });
|
||||
gameState.ai.HQ.defenseManager.garrisonUnitsInside(gameState, ent, { "min": gmin, "type": GarrisonManager.TYPE_DECAY });
|
||||
}
|
||||
};
|
||||
|
||||
/** TODO should add the units garrisoned inside garrisoned units */
|
||||
PETRA.GarrisonManager.prototype.numberOfGarrisonedUnits = function(holder)
|
||||
GarrisonManager.prototype.numberOfGarrisonedUnits = function(holder)
|
||||
{
|
||||
if (!this.holders.has(holder.id()))
|
||||
return holder.garrisoned().length;
|
||||
@@ -214,7 +217,7 @@ PETRA.GarrisonManager.prototype.numberOfGarrisonedUnits = function(holder)
|
||||
};
|
||||
|
||||
/** TODO should add the units garrisoned inside garrisoned units */
|
||||
PETRA.GarrisonManager.prototype.numberOfGarrisonedSlots = function(holder)
|
||||
GarrisonManager.prototype.numberOfGarrisonedSlots = function(holder)
|
||||
{
|
||||
if (!this.holders.has(holder.id()))
|
||||
return holder.garrisonedSlots();
|
||||
@@ -222,7 +225,7 @@ PETRA.GarrisonManager.prototype.numberOfGarrisonedSlots = function(holder)
|
||||
return holder.garrisonedSlots() + this.holders.get(holder.id()).list.length;
|
||||
};
|
||||
|
||||
PETRA.GarrisonManager.prototype.allowMelee = function(holder)
|
||||
GarrisonManager.prototype.allowMelee = function(holder)
|
||||
{
|
||||
if (!this.holders.has(holder.id()))
|
||||
return undefined;
|
||||
@@ -231,7 +234,7 @@ PETRA.GarrisonManager.prototype.allowMelee = function(holder)
|
||||
};
|
||||
|
||||
/** This is just a pre-garrison state, while the entity walk to the garrison holder */
|
||||
PETRA.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type)
|
||||
GarrisonManager.prototype.garrison = function(gameState, ent, holder, type)
|
||||
{
|
||||
if (this.numberOfGarrisonedSlots(holder) >= holder.garrisonMax() || !ent.canGarrison())
|
||||
return;
|
||||
@@ -250,7 +253,7 @@ PETRA.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type
|
||||
ent.setMetadata(PlayerID, "plan", -2);
|
||||
else
|
||||
ent.setMetadata(PlayerID, "plan", -3);
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_GARRISONING);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_GARRISONING);
|
||||
ent.setMetadata(PlayerID, "garrisonHolder", holder.id());
|
||||
ent.setMetadata(PlayerID, "garrisonType", type);
|
||||
ent.garrison(holder);
|
||||
@@ -262,7 +265,7 @@ PETRA.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type
|
||||
This function is for internal use inside garrisonManager. From outside, you should also update
|
||||
the holder and then using cancelGarrison should be the preferred solution
|
||||
*/
|
||||
PETRA.GarrisonManager.prototype.leaveGarrison = function(ent)
|
||||
GarrisonManager.prototype.leaveGarrison = function(ent)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "subrole", undefined);
|
||||
if (ent.getMetadata(PlayerID, "plan") === -2)
|
||||
@@ -273,7 +276,7 @@ PETRA.GarrisonManager.prototype.leaveGarrison = function(ent)
|
||||
};
|
||||
|
||||
/** Cancel a pre-garrison state */
|
||||
PETRA.GarrisonManager.prototype.cancelGarrison = function(ent)
|
||||
GarrisonManager.prototype.cancelGarrison = function(ent)
|
||||
{
|
||||
ent.stopMoving();
|
||||
this.leaveGarrison(ent);
|
||||
@@ -286,15 +289,15 @@ PETRA.GarrisonManager.prototype.cancelGarrison = function(ent)
|
||||
list.splice(index, 1);
|
||||
};
|
||||
|
||||
PETRA.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around)
|
||||
GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around)
|
||||
{
|
||||
switch (ent.getMetadata(PlayerID, "garrisonType"))
|
||||
{
|
||||
case PETRA.GarrisonManager.TYPE_FORCE: // force the ungarrisoning
|
||||
case GarrisonManager.TYPE_FORCE: // force the ungarrisoning
|
||||
return false;
|
||||
case PETRA.GarrisonManager.TYPE_TRADE: // trader garrisoned in ship
|
||||
case GarrisonManager.TYPE_TRADE: // trader garrisoned in ship
|
||||
return true;
|
||||
case PETRA.GarrisonManager.TYPE_PROTECTION: // hurt unit for healing or infantry for defense
|
||||
case GarrisonManager.TYPE_PROTECTION: // hurt unit for healing or infantry for defense
|
||||
{
|
||||
if (holder.buffHeal() && ent.isHealable() && ent.healthLevel() < this.Config.garrisonHealthLevel.high)
|
||||
return true;
|
||||
@@ -312,14 +315,14 @@ PETRA.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around)
|
||||
if (ent.attackTypes() && ent.attackTypes().indexOf("Melee") !== -1)
|
||||
return false;
|
||||
if (around.unit)
|
||||
return ent.hasClass("Support") || PETRA.isSiegeUnit(ent); // only ranged siege here and below as melee siege already released above
|
||||
if (PETRA.isSiegeUnit(ent))
|
||||
return ent.hasClass("Support") || isSiegeUnit(ent); // only ranged siege here and below as melee siege already released above
|
||||
if (isSiegeUnit(ent))
|
||||
return around.meleeSiege;
|
||||
return holder.buffHeal() && ent.needsHeal();
|
||||
}
|
||||
case PETRA.GarrisonManager.TYPE_DECAY:
|
||||
case GarrisonManager.TYPE_DECAY:
|
||||
return ent.captureStrength() && this.decayingStructures.has(holder.id());
|
||||
case PETRA.GarrisonManager.TYPE_EMERGENCY: // f.e. hero in regicide mode
|
||||
case GarrisonManager.TYPE_EMERGENCY: // f.e. hero in regicide mode
|
||||
if (holder.buffHeal() && ent.isHealable() && ent.healthLevel() < this.Config.garrisonHealthLevel.high)
|
||||
return true;
|
||||
if (around.unit || around.defenseStructure || around.meleeSiege ||
|
||||
@@ -332,13 +335,13 @@ PETRA.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around)
|
||||
API3.warn("unknown type in garrisonManager " + ent.getMetadata(PlayerID, "garrisonType") +
|
||||
" for " + ent.genericName() + " id " + ent.id() +
|
||||
" inside " + holder.genericName() + " id " + holder.id());
|
||||
ent.setMetadata(PlayerID, "garrisonType", PETRA.GarrisonManager.TYPE_PROTECTION);
|
||||
ent.setMetadata(PlayerID, "garrisonType", GarrisonManager.TYPE_PROTECTION);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** Add this holder in the list managed by the garrisonManager */
|
||||
PETRA.GarrisonManager.prototype.registerHolder = function(gameState, holder)
|
||||
GarrisonManager.prototype.registerHolder = function(gameState, holder)
|
||||
{
|
||||
if (this.holders.has(holder.id())) // already registered
|
||||
return;
|
||||
@@ -351,7 +354,7 @@ PETRA.GarrisonManager.prototype.registerHolder = function(gameState, holder)
|
||||
* do it only for structures useful for defense, except if we are expanding (justCaptured=true)
|
||||
* in which case we also do it for structures useful for unit trainings (TODO only Barracks are done)
|
||||
*/
|
||||
PETRA.GarrisonManager.prototype.addDecayingStructure = function(gameState, entId, justCaptured)
|
||||
GarrisonManager.prototype.addDecayingStructure = function(gameState, entId, justCaptured)
|
||||
{
|
||||
if (this.decayingStructures.has(entId))
|
||||
return true;
|
||||
@@ -365,19 +368,19 @@ PETRA.GarrisonManager.prototype.addDecayingStructure = function(gameState, entId
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.GarrisonManager.prototype.removeDecayingStructure = function(entId)
|
||||
GarrisonManager.prototype.removeDecayingStructure = function(entId)
|
||||
{
|
||||
if (!this.decayingStructures.has(entId))
|
||||
return;
|
||||
this.decayingStructures.delete(entId);
|
||||
};
|
||||
|
||||
PETRA.GarrisonManager.prototype.Serialize = function()
|
||||
GarrisonManager.prototype.Serialize = function()
|
||||
{
|
||||
return { "holders": this.holders, "decayingStructures": this.decayingStructures };
|
||||
};
|
||||
|
||||
PETRA.GarrisonManager.prototype.Deserialize = function(data)
|
||||
GarrisonManager.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
||||
/** map functions */
|
||||
|
||||
PETRA.TERRITORY_PLAYER_MASK = 0x1F;
|
||||
PETRA.TERRITORY_BLINKING_MASK = 0x40;
|
||||
/* eslint-disable prefer-const -- Mods should be able to change them */
|
||||
let TERRITORY_PLAYER_MASK = 0x1F;
|
||||
let TERRITORY_BLINKING_MASK = 0x40;
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
PETRA.createObstructionMap = function(gameState, accessIndex, template)
|
||||
export function createObstructionMap(gameState, accessIndex, template)
|
||||
{
|
||||
const passabilityMap = gameState.getPassabilityMap();
|
||||
const territoryMap = gameState.ai.territoryMap;
|
||||
@@ -41,8 +43,8 @@ PETRA.createObstructionMap = function(gameState, accessIndex, template)
|
||||
|
||||
for (let k = 0; k < territoryMap.data.length; ++k)
|
||||
{
|
||||
const tilePlayer = territoryMap.data[k] & PETRA.TERRITORY_PLAYER_MASK;
|
||||
const isConnected = (territoryMap.data[k] & PETRA.TERRITORY_BLINKING_MASK) == 0;
|
||||
const tilePlayer = territoryMap.data[k] & TERRITORY_PLAYER_MASK;
|
||||
const isConnected = (territoryMap.data[k] & TERRITORY_BLINKING_MASK) == 0;
|
||||
if (tilePlayer === PlayerID)
|
||||
{
|
||||
if (!buildOwn || !buildNeutral && !isConnected)
|
||||
@@ -108,19 +110,19 @@ PETRA.createObstructionMap = function(gameState, accessIndex, template)
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
PETRA.createTerritoryMap = function(gameState)
|
||||
export function createTerritoryMap(gameState)
|
||||
{
|
||||
const map = gameState.ai.territoryMap;
|
||||
|
||||
const ret = new API3.Map(gameState.sharedScript, "territory", map.data);
|
||||
ret.getOwner = function(p) { return this.point(p) & PETRA.TERRITORY_PLAYER_MASK; };
|
||||
ret.getOwnerIndex = function(p) { return this.map[p] & PETRA.TERRITORY_PLAYER_MASK; };
|
||||
ret.isBlinking = function(p) { return (this.point(p) & PETRA.TERRITORY_BLINKING_MASK) != 0; };
|
||||
ret.getOwner = function(p) { return this.point(p) & TERRITORY_PLAYER_MASK; };
|
||||
ret.getOwnerIndex = function(p) { return this.map[p] & TERRITORY_PLAYER_MASK; };
|
||||
ret.isBlinking = function(p) { return (this.point(p) & TERRITORY_BLINKING_MASK) != 0; };
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The borderMap contains some border and frontier information:
|
||||
@@ -132,14 +134,16 @@ PETRA.createTerritoryMap = function(gameState)
|
||||
* - large border (inside our territory, exclusive of narrow) => bit 3
|
||||
*/
|
||||
|
||||
PETRA.outside_Mask = 1;
|
||||
PETRA.border_Mask = 2;
|
||||
PETRA.fullBorder_Mask = PETRA.outside_Mask | PETRA.border_Mask;
|
||||
PETRA.narrowFrontier_Mask = 4;
|
||||
PETRA.largeFrontier_Mask = 8;
|
||||
PETRA.fullFrontier_Mask = PETRA.narrowFrontier_Mask | PETRA.largeFrontier_Mask;
|
||||
/* eslint-disable prefer-const -- Mods should be able to change them */
|
||||
export let outside_Mask = 1;
|
||||
export let border_Mask = 2;
|
||||
export let fullBorder_Mask = outside_Mask | border_Mask;
|
||||
export let narrowFrontier_Mask = 4;
|
||||
export let largeFrontier_Mask = 8;
|
||||
export let fullFrontier_Mask = narrowFrontier_Mask | largeFrontier_Mask;
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
PETRA.createBorderMap = function(gameState)
|
||||
export function createBorderMap(gameState)
|
||||
{
|
||||
const map = new API3.Map(gameState.sharedScript, "territory");
|
||||
const width = map.width;
|
||||
@@ -157,13 +161,13 @@ PETRA.createBorderMap = function(gameState)
|
||||
const radius = dx*dx + dy*dy;
|
||||
if (radius < radcut)
|
||||
continue;
|
||||
map.map[j] = PETRA.outside_Mask;
|
||||
map.map[j] = outside_Mask;
|
||||
const ind = API3.getMapIndices(j, map, passabilityMap);
|
||||
for (const k of ind)
|
||||
{
|
||||
if (passabilityMap.data[k] & obstructionMask)
|
||||
continue;
|
||||
map.map[j] = PETRA.border_Mask;
|
||||
map.map[j] = border_Mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -177,13 +181,13 @@ PETRA.createBorderMap = function(gameState)
|
||||
const iy = Math.floor(j/width);
|
||||
if (ix < border || ix >= borderCut || iy < border || iy >= borderCut)
|
||||
{
|
||||
map.map[j] = PETRA.outside_Mask;
|
||||
map.map[j] = outside_Mask;
|
||||
const ind = API3.getMapIndices(j, map, passabilityMap);
|
||||
for (const k of ind)
|
||||
{
|
||||
if (passabilityMap.data[k] & obstructionMask)
|
||||
continue;
|
||||
map.map[j] = PETRA.border_Mask;
|
||||
map.map[j] = border_Mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -192,9 +196,9 @@ PETRA.createBorderMap = function(gameState)
|
||||
|
||||
// map.dumpIm("border.png", 5);
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.debugMap = function(gameState, map)
|
||||
function debugMap(gameState, map)
|
||||
{
|
||||
const width = map.width;
|
||||
const cell = map.cellSize;
|
||||
@@ -212,4 +216,4 @@ PETRA.debugMap = function(gameState, map)
|
||||
else if (map.map[id] == 3)
|
||||
Engine.PostCommand(PlayerID, { "type": "set-shading-color", "entities": [ent.id()], "rgb": [0, 0, 2] });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { gatherTreasure, getLandAccess, getSeaAccess, isSiegeUnit, setSeaAccess } from
|
||||
"simulation/ai/petra/entityExtend.js";
|
||||
import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js";
|
||||
import { TrainingPlan } from "simulation/ai/petra/queueplanTraining.js";
|
||||
import { TransportPlan } from "simulation/ai/petra/transportPlan.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Naval Manager
|
||||
* Will deal with anything ships.
|
||||
@@ -7,7 +14,7 @@
|
||||
* -Scouting, ultimately.
|
||||
* Also deals with handling docks, making sure we have access and stuffs like that.
|
||||
*/
|
||||
PETRA.NavalManager = function(Config)
|
||||
export function NavalManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
|
||||
@@ -29,16 +36,17 @@ PETRA.NavalManager = function(Config)
|
||||
|
||||
// shore-line regions where we can load and unload units
|
||||
this.landingZones = {};
|
||||
};
|
||||
}
|
||||
|
||||
/** More initialisation for stuff that needs the gameState */
|
||||
PETRA.NavalManager.prototype.init = function(gameState, deserializing)
|
||||
NavalManager.prototype.init = function(gameState, deserializing)
|
||||
{
|
||||
// docks
|
||||
this.docks = gameState.getOwnStructures().filter(API3.Filters.byClasses(["Dock", "Shipyard"]));
|
||||
this.docks.registerUpdates();
|
||||
|
||||
this.ships = gameState.getOwnUnits().filter(API3.Filters.and(API3.Filters.byClass("Ship"), API3.Filters.not(API3.Filters.byMetadata(PlayerID, "role", PETRA.Worker.ROLE_TRADER))));
|
||||
this.ships = gameState.getOwnUnits().filter(API3.Filters.and(API3.Filters.byClass("Ship"),
|
||||
API3.Filters.not(API3.Filters.byMetadata(PlayerID, "role", Worker.ROLE_TRADER))));
|
||||
// note: those two can overlap (some transport ships are warships too and vice-versa).
|
||||
this.transportShips = this.ships.filter(API3.Filters.and(API3.Filters.byCanGarrison(), API3.Filters.not(API3.Filters.byClass("FishingBoat"))));
|
||||
this.warShips = this.ships.filter(API3.Filters.byClass("Warship"));
|
||||
@@ -152,18 +160,18 @@ PETRA.NavalManager.prototype.init = function(gameState, deserializing)
|
||||
|
||||
// Assign our initial docks and ships
|
||||
for (const ship of this.ships.values())
|
||||
PETRA.setSeaAccess(gameState, ship);
|
||||
setSeaAccess(gameState, ship);
|
||||
for (const dock of this.docks.values())
|
||||
PETRA.setSeaAccess(gameState, dock);
|
||||
setSeaAccess(gameState, dock);
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.updateFishingBoats = function(sea, num)
|
||||
NavalManager.prototype.updateFishingBoats = function(sea, num)
|
||||
{
|
||||
if (this.wantedFishShips[sea])
|
||||
this.wantedFishShips[sea] = num;
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.resetFishingBoats = function(gameState, sea)
|
||||
NavalManager.prototype.resetFishingBoats = function(gameState, sea)
|
||||
{
|
||||
if (sea !== undefined)
|
||||
this.wantedFishShips[sea] = 0;
|
||||
@@ -172,7 +180,7 @@ PETRA.NavalManager.prototype.resetFishingBoats = function(gameState, sea)
|
||||
};
|
||||
|
||||
/** Get the sea, cache it if not yet done and check if in opensea */
|
||||
PETRA.NavalManager.prototype.getFishSea = function(gameState, fish)
|
||||
NavalManager.prototype.getFishSea = function(gameState, fish)
|
||||
{
|
||||
let sea = fish.getMetadata(PlayerID, "sea");
|
||||
if (sea)
|
||||
@@ -209,7 +217,7 @@ PETRA.NavalManager.prototype.getFishSea = function(gameState, fish)
|
||||
};
|
||||
|
||||
/** check if we can safely fish at the fish position */
|
||||
PETRA.NavalManager.prototype.canFishSafely = function(gameState, fish)
|
||||
NavalManager.prototype.canFishSafely = function(gameState, fish)
|
||||
{
|
||||
if (fish.getMetadata(PlayerID, "opensea"))
|
||||
return true;
|
||||
@@ -235,20 +243,20 @@ PETRA.NavalManager.prototype.canFishSafely = function(gameState, fish)
|
||||
};
|
||||
|
||||
/** get the list of seas (or lands) around this region not connected by a dock */
|
||||
PETRA.NavalManager.prototype.getUnconnectedSeas = function(gameState, region)
|
||||
NavalManager.prototype.getUnconnectedSeas = function(gameState, region)
|
||||
{
|
||||
const seas = gameState.ai.accessibility.regionLinks[region].slice();
|
||||
this.docks.forEach(dock => {
|
||||
if (!dock.hasClass("Dock") || PETRA.getLandAccess(gameState, dock) != region)
|
||||
if (!dock.hasClass("Dock") || getLandAccess(gameState, dock) != region)
|
||||
return;
|
||||
const i = seas.indexOf(PETRA.getSeaAccess(gameState, dock));
|
||||
const i = seas.indexOf(getSeaAccess(gameState, dock));
|
||||
if (i != -1)
|
||||
seas.splice(i, 1);
|
||||
});
|
||||
return seas;
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
{
|
||||
for (const evt of events.Create)
|
||||
{
|
||||
@@ -256,7 +264,7 @@ PETRA.NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
continue;
|
||||
const ent = gameState.getEntityById(evt.entity);
|
||||
if (ent && ent.isOwn(PlayerID) && ent.foundationProgress() !== undefined && ent.hasClasses(["Dock", "Shipyard"]))
|
||||
PETRA.setSeaAccess(gameState, ent);
|
||||
setSeaAccess(gameState, ent);
|
||||
}
|
||||
|
||||
for (const evt of events.TrainingFinished)
|
||||
@@ -268,7 +276,7 @@ PETRA.NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
const ent = gameState.getEntityById(entId);
|
||||
if (!ent || !ent.hasClass("Ship") || !ent.isOwn(PlayerID))
|
||||
continue;
|
||||
PETRA.setSeaAccess(gameState, ent);
|
||||
setSeaAccess(gameState, ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +293,7 @@ PETRA.NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
const shipId = evt.entityObj.id();
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("one ship " + shipId + " from plan " + plan.ID + " destroyed during " + plan.state);
|
||||
if (plan.state === PETRA.TransportPlan.BOARDING)
|
||||
if (plan.state === TransportPlan.BOARDING)
|
||||
{
|
||||
// just reset the units onBoard metadata and wait for a new ship to be assigned to this plan
|
||||
plan.units.forEach(ent => {
|
||||
@@ -295,14 +303,14 @@ PETRA.NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
});
|
||||
plan.needTransportShips = !plan.transportShips.hasEntities();
|
||||
}
|
||||
else if (plan.state === PETRA.TransportPlan.SAILING)
|
||||
else if (plan.state === TransportPlan.SAILING)
|
||||
{
|
||||
const endIndex = plan.endIndex;
|
||||
for (const ent of plan.units.values())
|
||||
{
|
||||
if (!ent.position()) // unit from another ship of this plan ... do nothing
|
||||
continue;
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
const endPos = ent.getMetadata(PlayerID, "endPos");
|
||||
ent.setMetadata(PlayerID, "transport", undefined);
|
||||
ent.setMetadata(PlayerID, "onBoard", undefined);
|
||||
@@ -323,12 +331,12 @@ PETRA.NavalManager.prototype.checkEvents = function(gameState, queues, events)
|
||||
continue;
|
||||
const ent = gameState.getEntityById(evt.entity);
|
||||
if (ent && ent.hasClasses(["Dock", "Shipyard"]))
|
||||
PETRA.setSeaAccess(gameState, ent);
|
||||
setSeaAccess(gameState, ent);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
PETRA.NavalManager.prototype.getPlan = function(ID)
|
||||
NavalManager.prototype.getPlan = function(ID)
|
||||
{
|
||||
for (const plan of this.transportPlans)
|
||||
if (plan.ID === ID)
|
||||
@@ -336,7 +344,7 @@ PETRA.NavalManager.prototype.getPlan = function(ID)
|
||||
return undefined;
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.addPlan = function(plan)
|
||||
NavalManager.prototype.addPlan = function(plan)
|
||||
{
|
||||
this.transportPlans.push(plan);
|
||||
};
|
||||
@@ -346,7 +354,7 @@ PETRA.NavalManager.prototype.addPlan = function(plan)
|
||||
* (many units can then call this separately and end up in the same plan)
|
||||
* TODO check garrison classes
|
||||
*/
|
||||
PETRA.NavalManager.prototype.requireTransport = function(gameState, ent, startIndex, endIndex, endPos)
|
||||
NavalManager.prototype.requireTransport = function(gameState, ent, startIndex, endIndex, endPos)
|
||||
{
|
||||
if (!ent.canGarrison())
|
||||
return false;
|
||||
@@ -361,10 +369,13 @@ PETRA.NavalManager.prototype.requireTransport = function(gameState, ent, startIn
|
||||
const plans = [];
|
||||
for (const plan of this.transportPlans)
|
||||
{
|
||||
if (plan.startIndex != startIndex || plan.endIndex != endIndex || plan.state !== PETRA.TransportPlan.BOARDING)
|
||||
if (plan.startIndex != startIndex || plan.endIndex != endIndex ||
|
||||
plan.state !== TransportPlan.BOARDING)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Limit the number of siege units per transport to avoid problems when ungarrisoning
|
||||
if (PETRA.isSiegeUnit(ent) && plan.units.filter(unit => PETRA.isSiegeUnit(unit)).length > 3)
|
||||
if (isSiegeUnit(ent) && plan.units.filter(unit => isSiegeUnit(unit)).length > 3)
|
||||
continue;
|
||||
plans.push(plan);
|
||||
}
|
||||
@@ -376,7 +387,7 @@ PETRA.NavalManager.prototype.requireTransport = function(gameState, ent, startIn
|
||||
return true;
|
||||
}
|
||||
|
||||
const plan = new PETRA.TransportPlan(gameState, [ent], startIndex, endIndex, endPos);
|
||||
const plan = new TransportPlan(gameState, [ent], startIndex, endIndex, endPos);
|
||||
if (plan.failed)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
@@ -389,11 +400,11 @@ PETRA.NavalManager.prototype.requireTransport = function(gameState, ent, startIn
|
||||
};
|
||||
|
||||
/** split a transport plan in two, moving all entities not yet affected to a ship in the new plan */
|
||||
PETRA.NavalManager.prototype.splitTransport = function(gameState, plan)
|
||||
NavalManager.prototype.splitTransport = function(gameState, plan)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn(">>>> split of transport plan started <<<<");
|
||||
const newplan = new PETRA.TransportPlan(gameState, [], plan.startIndex, plan.endIndex, plan.endPos);
|
||||
const newplan = new TransportPlan(gameState, [], plan.startIndex, plan.endIndex, plan.endPos);
|
||||
if (newplan.failed)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
@@ -419,7 +430,7 @@ PETRA.NavalManager.prototype.splitTransport = function(gameState, plan)
|
||||
* create a transport from a garrisoned ship to a land location
|
||||
* needed at start game when starting with a garrisoned ship
|
||||
*/
|
||||
PETRA.NavalManager.prototype.createTransportIfNeeded = function(gameState, fromPos, toPos, toAccess)
|
||||
NavalManager.prototype.createTransportIfNeeded = function(gameState, fromPos, toPos, toAccess)
|
||||
{
|
||||
const fromAccess = gameState.ai.accessibility.getAccessValue(fromPos);
|
||||
if (fromAccess !== 1)
|
||||
@@ -437,7 +448,7 @@ PETRA.NavalManager.prototype.createTransportIfNeeded = function(gameState, fromP
|
||||
for (const entId of ship.garrisoned())
|
||||
units.push(gameState.getEntityById(entId));
|
||||
// TODO check that the garrisoned units have not another purpose
|
||||
const plan = new PETRA.TransportPlan(gameState, units, fromAccess, toAccess, toPos, ship);
|
||||
const plan = new TransportPlan(gameState, units, fromAccess, toAccess, toPos, ship);
|
||||
if (plan.failed)
|
||||
continue;
|
||||
plan.init(gameState);
|
||||
@@ -446,7 +457,7 @@ PETRA.NavalManager.prototype.createTransportIfNeeded = function(gameState, fromP
|
||||
};
|
||||
|
||||
// set minimal number of needed ships when a new event (new base or new attack plan)
|
||||
PETRA.NavalManager.prototype.setMinimalTransportShips = function(gameState, sea, number)
|
||||
NavalManager.prototype.setMinimalTransportShips = function(gameState, sea, number)
|
||||
{
|
||||
if (!sea)
|
||||
return;
|
||||
@@ -455,7 +466,7 @@ PETRA.NavalManager.prototype.setMinimalTransportShips = function(gameState, sea,
|
||||
};
|
||||
|
||||
// bumps up the number of ships we want if we need more.
|
||||
PETRA.NavalManager.prototype.checkLevels = function(gameState, queues)
|
||||
NavalManager.prototype.checkLevels = function(gameState, queues)
|
||||
{
|
||||
if (queues.ships.hasQueuedUnits())
|
||||
return;
|
||||
@@ -484,7 +495,7 @@ PETRA.NavalManager.prototype.checkLevels = function(gameState, queues)
|
||||
++this.wantedTransportShips[sea];
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.maintainFleet = function(gameState, queues)
|
||||
NavalManager.prototype.maintainFleet = function(gameState, queues)
|
||||
{
|
||||
if (queues.ships.hasQueuedUnits())
|
||||
return;
|
||||
@@ -503,7 +514,7 @@ PETRA.NavalManager.prototype.maintainFleet = function(gameState, queues)
|
||||
const template = this.getBestShip(gameState, sea, "transport");
|
||||
if (template)
|
||||
{
|
||||
queues.ships.addPlan(new PETRA.TrainingPlan(gameState, template, { "sea": sea }, 1, 1));
|
||||
queues.ships.addPlan(new TrainingPlan(gameState, template, { "sea": sea }, 1, 1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -514,7 +525,8 @@ PETRA.NavalManager.prototype.maintainFleet = function(gameState, queues)
|
||||
const template = this.getBestShip(gameState, sea, "fishing");
|
||||
if (template)
|
||||
{
|
||||
queues.ships.addPlan(new PETRA.TrainingPlan(gameState, template, { "base": 0, "role": PETRA.Worker.ROLE_WORKER, "sea": sea }, 1, 1));
|
||||
queues.ships.addPlan(new TrainingPlan(gameState, template,
|
||||
{ "base": 0, "role": Worker.ROLE_WORKER, "sea": sea }, 1, 1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -522,7 +534,7 @@ PETRA.NavalManager.prototype.maintainFleet = function(gameState, queues)
|
||||
};
|
||||
|
||||
/** assigns free ships to plans that need some */
|
||||
PETRA.NavalManager.prototype.assignShipsToPlans = function(gameState)
|
||||
NavalManager.prototype.assignShipsToPlans = function(gameState)
|
||||
{
|
||||
for (const plan of this.transportPlans)
|
||||
if (plan.needTransportShips)
|
||||
@@ -530,7 +542,7 @@ PETRA.NavalManager.prototype.assignShipsToPlans = function(gameState)
|
||||
};
|
||||
|
||||
/** Return true if this ship is likeky (un)garrisoning units */
|
||||
PETRA.NavalManager.prototype.isShipBoarding = function(ship)
|
||||
NavalManager.prototype.isShipBoarding = function(ship)
|
||||
{
|
||||
if (!ship.position())
|
||||
return false;
|
||||
@@ -544,7 +556,7 @@ PETRA.NavalManager.prototype.isShipBoarding = function(ship)
|
||||
* TODO Ships entity collections are currently in two parts as the trader ships are dealt with
|
||||
* in the tradeManager. That should be modified to avoid dupplicating all the code here.
|
||||
*/
|
||||
PETRA.NavalManager.prototype.moveApart = function(gameState)
|
||||
NavalManager.prototype.moveApart = function(gameState)
|
||||
{
|
||||
const blockedShips = [];
|
||||
const blockedIds = [];
|
||||
@@ -591,13 +603,13 @@ PETRA.NavalManager.prototype.moveApart = function(gameState)
|
||||
continue;
|
||||
ship.setMetadata(PlayerID, "stationnary", true);
|
||||
// Check if there are some treasure around
|
||||
if (PETRA.gatherTreasure(gameState, ship, true))
|
||||
if (gatherTreasure(gameState, ship, true))
|
||||
continue;
|
||||
// Do not stay idle near a dock to not disturb other ships
|
||||
const sea = ship.getMetadata(PlayerID, "sea");
|
||||
for (const dock of gameState.getAllyStructures().filter(API3.Filters.byClass("Dock")).values())
|
||||
{
|
||||
if (PETRA.getSeaAccess(gameState, dock) != sea)
|
||||
if (getSeaAccess(gameState, dock) != sea)
|
||||
continue;
|
||||
if (API3.SquareVectorDistance(shipPosition, dock.position()) > 4900)
|
||||
continue;
|
||||
@@ -613,7 +625,7 @@ PETRA.NavalManager.prototype.moveApart = function(gameState)
|
||||
if (!shipPosition)
|
||||
continue;
|
||||
const role = ship.getMetadata(PlayerID, "role");
|
||||
if (role === undefined || role !== PETRA.Worker.ROLE_TRADER) // already accounted before
|
||||
if (role === undefined || role !== Worker.ROLE_TRADER) // already accounted before
|
||||
continue;
|
||||
|
||||
const unitAIState = ship.unitAIState();
|
||||
@@ -648,13 +660,13 @@ PETRA.NavalManager.prototype.moveApart = function(gameState)
|
||||
continue;
|
||||
ship.setMetadata(PlayerID, "stationnary", true);
|
||||
// Check if there are some treasure around
|
||||
if (PETRA.gatherTreasure(gameState, ship, true))
|
||||
if (gatherTreasure(gameState, ship, true))
|
||||
continue;
|
||||
// Do not stay idle near a dock to not disturb other ships
|
||||
const sea = ship.getMetadata(PlayerID, "sea");
|
||||
for (const dock of gameState.getAllyStructures().filter(API3.Filters.byClass("Dock")).values())
|
||||
{
|
||||
if (PETRA.getSeaAccess(gameState, dock) != sea)
|
||||
if (getSeaAccess(gameState, dock) != sea)
|
||||
continue;
|
||||
if (API3.SquareVectorDistance(shipPosition, dock.position()) > 4900)
|
||||
continue;
|
||||
@@ -691,7 +703,7 @@ PETRA.NavalManager.prototype.moveApart = function(gameState)
|
||||
if (blockedIds.indexOf(blockingShip.id()) != -1 || !blockingShip.position())
|
||||
continue;
|
||||
const role = blockingShip.getMetadata(PlayerID, "role");
|
||||
if (role === undefined || role !== PETRA.Worker.ROLE_TRADER) // already accounted before
|
||||
if (role === undefined || role !== Worker.ROLE_TRADER) // already accounted before
|
||||
continue;
|
||||
const distSquare = API3.SquareVectorDistance(shipPosition, blockingShip.position());
|
||||
const unitAIState = blockingShip.unitAIState();
|
||||
@@ -706,7 +718,7 @@ PETRA.NavalManager.prototype.moveApart = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.buildNavalStructures = function(gameState, queues)
|
||||
NavalManager.prototype.buildNavalStructures = function(gameState, queues)
|
||||
{
|
||||
if (!gameState.ai.HQ.navalMap || !gameState.ai.HQ.hasPotentialBase())
|
||||
return;
|
||||
@@ -731,7 +743,8 @@ PETRA.NavalManager.prototype.buildNavalStructures = function(gameState, queues)
|
||||
continue;
|
||||
const wantedLand = {};
|
||||
wantedLand[base.accessIndex] = true;
|
||||
queues.dock.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/dock", { "land": wantedLand, "sea": sea }));
|
||||
queues.dock.addPlan(new ConstructionPlan(gameState, "structures/{civ}/dock",
|
||||
{ "land": wantedLand, "sea": sea }));
|
||||
dockStarted = true;
|
||||
break;
|
||||
}
|
||||
@@ -761,11 +774,12 @@ PETRA.NavalManager.prototype.buildNavalStructures = function(gameState, queues)
|
||||
if (base.anchor)
|
||||
wantedLand[base.accessIndex] = true;
|
||||
const sea = this.docks.toEntityArray()[0].getMetadata(PlayerID, "sea");
|
||||
queues.militaryBuilding.addPlan(new PETRA.ConstructionPlan(gameState, template, { "land": wantedLand, "sea": sea }));
|
||||
queues.militaryBuilding.addPlan(
|
||||
new ConstructionPlan(gameState, template, { "land": wantedLand, "sea": sea }));
|
||||
};
|
||||
|
||||
/** goal can be either attack (choose ship with best arrowCount) or transport (choose ship with best capacity) */
|
||||
PETRA.NavalManager.prototype.getBestShip = function(gameState, sea, goal)
|
||||
NavalManager.prototype.getBestShip = function(gameState, sea, goal)
|
||||
{
|
||||
const civ = gameState.getPlayerCiv();
|
||||
const trainableShips = [];
|
||||
@@ -822,7 +836,7 @@ PETRA.NavalManager.prototype.getBestShip = function(gameState, sea, goal)
|
||||
return bestShip;
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.update = function(gameState, queues, events)
|
||||
NavalManager.prototype.update = function(gameState, queues, events)
|
||||
{
|
||||
Engine.ProfileStart("Naval Manager update");
|
||||
|
||||
@@ -853,7 +867,7 @@ PETRA.NavalManager.prototype.update = function(gameState, queues, events)
|
||||
Engine.ProfileStop();
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.Serialize = function()
|
||||
NavalManager.prototype.Serialize = function()
|
||||
{
|
||||
const properties = {
|
||||
"wantedTransportShips": this.wantedTransportShips,
|
||||
@@ -871,7 +885,7 @@ PETRA.NavalManager.prototype.Serialize = function()
|
||||
return { "properties": properties, "transports": transports };
|
||||
};
|
||||
|
||||
PETRA.NavalManager.prototype.Deserialize = function(gameState, data)
|
||||
NavalManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data.properties)
|
||||
this[key] = data.properties[key];
|
||||
@@ -880,7 +894,7 @@ PETRA.NavalManager.prototype.Deserialize = function(gameState, data)
|
||||
for (const i in data.transports)
|
||||
{
|
||||
const dataPlan = data.transports[i];
|
||||
const plan = new PETRA.TransportPlan(gameState, [], dataPlan.startIndex, dataPlan.endIndex, dataPlan.endPos);
|
||||
const plan = new TransportPlan(gameState, [], dataPlan.startIndex, dataPlan.endIndex, dataPlan.endPos);
|
||||
plan.Deserialize(dataPlan);
|
||||
plan.init(gameState);
|
||||
this.transportPlans.push(plan);
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js";
|
||||
import { ResearchPlan } from "simulation/ai/petra/queueplanResearch.js";
|
||||
import { TrainingPlan } from "simulation/ai/petra/queueplanTraining.js";
|
||||
|
||||
/**
|
||||
* Holds a list of wanted plans to train or construct
|
||||
*/
|
||||
PETRA.Queue = function()
|
||||
export function Queue()
|
||||
{
|
||||
this.plans = [];
|
||||
this.paused = false;
|
||||
this.switched = 0;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.Queue.prototype.empty = function()
|
||||
Queue.prototype.empty = function()
|
||||
{
|
||||
this.plans = [];
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.addPlan = function(newPlan)
|
||||
Queue.prototype.addPlan = function(newPlan)
|
||||
{
|
||||
if (!newPlan)
|
||||
return;
|
||||
@@ -30,7 +34,7 @@ PETRA.Queue.prototype.addPlan = function(newPlan)
|
||||
this.plans.push(newPlan);
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.check= function(gameState)
|
||||
Queue.prototype.check= function(gameState)
|
||||
{
|
||||
while (this.plans.length > 0)
|
||||
{
|
||||
@@ -42,14 +46,14 @@ PETRA.Queue.prototype.check= function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.getNext = function()
|
||||
Queue.prototype.getNext = function()
|
||||
{
|
||||
if (this.plans.length > 0)
|
||||
return this.plans[0];
|
||||
return null;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.startNext = function(gameState)
|
||||
Queue.prototype.startNext = function(gameState)
|
||||
{
|
||||
if (this.plans.length > 0)
|
||||
{
|
||||
@@ -63,7 +67,7 @@ PETRA.Queue.prototype.startNext = function(gameState)
|
||||
* returns the maximal account we'll accept for this queue.
|
||||
* Currently all the cost of the first element and fraction of that of the second
|
||||
*/
|
||||
PETRA.Queue.prototype.maxAccountWanted = function(gameState, fraction)
|
||||
Queue.prototype.maxAccountWanted = function(gameState, fraction)
|
||||
{
|
||||
const cost = new API3.Resources();
|
||||
if (this.plans.length > 0 && this.plans[0].isGo(gameState))
|
||||
@@ -77,7 +81,7 @@ PETRA.Queue.prototype.maxAccountWanted = function(gameState, fraction)
|
||||
return cost;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.queueCost = function()
|
||||
Queue.prototype.queueCost = function()
|
||||
{
|
||||
const cost = new API3.Resources();
|
||||
for (const plan of this.plans)
|
||||
@@ -85,17 +89,17 @@ PETRA.Queue.prototype.queueCost = function()
|
||||
return cost;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.length = function()
|
||||
Queue.prototype.length = function()
|
||||
{
|
||||
return this.plans.length;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.hasQueuedUnits = function()
|
||||
Queue.prototype.hasQueuedUnits = function()
|
||||
{
|
||||
return this.plans.length > 0;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.countQueuedUnits = function()
|
||||
Queue.prototype.countQueuedUnits = function()
|
||||
{
|
||||
let count = 0;
|
||||
for (const plan of this.plans)
|
||||
@@ -103,12 +107,12 @@ PETRA.Queue.prototype.countQueuedUnits = function()
|
||||
return count;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.hasQueuedUnitsWithClass = function(classe)
|
||||
Queue.prototype.hasQueuedUnitsWithClass = function(classe)
|
||||
{
|
||||
return this.plans.some(plan => plan.template && plan.template.hasClass(classe));
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.countQueuedUnitsWithClass = function(classe)
|
||||
Queue.prototype.countQueuedUnitsWithClass = function(classe)
|
||||
{
|
||||
let count = 0;
|
||||
for (const plan of this.plans)
|
||||
@@ -117,7 +121,7 @@ PETRA.Queue.prototype.countQueuedUnitsWithClass = function(classe)
|
||||
return count;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.countQueuedUnitsWithMetadata = function(data, value)
|
||||
Queue.prototype.countQueuedUnitsWithMetadata = function(data, value)
|
||||
{
|
||||
let count = 0;
|
||||
for (const plan of this.plans)
|
||||
@@ -126,7 +130,7 @@ PETRA.Queue.prototype.countQueuedUnitsWithMetadata = function(data, value)
|
||||
return count;
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.Serialize = function()
|
||||
Queue.prototype.Serialize = function()
|
||||
{
|
||||
const plans = [];
|
||||
for (const plan of this.plans)
|
||||
@@ -135,7 +139,7 @@ PETRA.Queue.prototype.Serialize = function()
|
||||
return { "plans": plans, "paused": this.paused, "switched": this.switched };
|
||||
};
|
||||
|
||||
PETRA.Queue.prototype.Deserialize = function(gameState, data)
|
||||
Queue.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
this.paused = data.paused;
|
||||
this.switched = data.switched;
|
||||
@@ -144,11 +148,11 @@ PETRA.Queue.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
let plan;
|
||||
if (dataPlan.category == "unit")
|
||||
plan = new PETRA.TrainingPlan(gameState, dataPlan.type);
|
||||
plan = new TrainingPlan(gameState, dataPlan.type);
|
||||
else if (dataPlan.category == "building")
|
||||
plan = new PETRA.ConstructionPlan(gameState, dataPlan.type);
|
||||
plan = new ConstructionPlan(gameState, dataPlan.type);
|
||||
else if (dataPlan.category == "technology")
|
||||
plan = new PETRA.ResearchPlan(gameState, dataPlan.type);
|
||||
plan = new ResearchPlan(gameState, dataPlan.type);
|
||||
else
|
||||
{
|
||||
API3.warn("Petra deserialization error: plan unknown " + uneval(dataPlan));
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Queue } from "simulation/ai/petra/queue.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* This takes the input queues and picks which items to fund with resources until no more resources are left to distribute.
|
||||
*
|
||||
@@ -19,7 +22,7 @@
|
||||
* This system should be improved. It's probably not flexible enough.
|
||||
*/
|
||||
|
||||
PETRA.QueueManager = function(Config, queues)
|
||||
export function QueueManager(Config, queues)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.queues = queues;
|
||||
@@ -37,9 +40,9 @@ PETRA.QueueManager = function(Config, queues)
|
||||
}
|
||||
const priorities = this.priorities;
|
||||
this.queueArrays.sort((a, b) => priorities[b[0]] - priorities[a[0]]);
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.QueueManager.prototype.getAvailableResources = function(gameState)
|
||||
QueueManager.prototype.getAvailableResources = function(gameState)
|
||||
{
|
||||
const resources = gameState.getResources();
|
||||
for (const key in this.queues)
|
||||
@@ -47,7 +50,7 @@ PETRA.QueueManager.prototype.getAvailableResources = function(gameState)
|
||||
return resources;
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.getTotalAccountedResources = function()
|
||||
QueueManager.prototype.getTotalAccountedResources = function()
|
||||
{
|
||||
const resources = new API3.Resources();
|
||||
for (const key in this.queues)
|
||||
@@ -55,7 +58,7 @@ PETRA.QueueManager.prototype.getTotalAccountedResources = function()
|
||||
return resources;
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.currentNeeds = function(gameState)
|
||||
QueueManager.prototype.currentNeeds = function(gameState)
|
||||
{
|
||||
const needed = new API3.Resources();
|
||||
// queueArrays because it's faster.
|
||||
@@ -77,7 +80,7 @@ PETRA.QueueManager.prototype.currentNeeds = function(gameState)
|
||||
|
||||
// calculate the gather rates we'd want to be able to start all elements in our queues
|
||||
// TODO: many things.
|
||||
PETRA.QueueManager.prototype.wantedGatherRates = function(gameState)
|
||||
QueueManager.prototype.wantedGatherRates = function(gameState)
|
||||
{
|
||||
// default values for first turn when we have not yet set our queues.
|
||||
if (gameState.ai.playedTurn === 0)
|
||||
@@ -154,12 +157,15 @@ PETRA.QueueManager.prototype.wantedGatherRates = function(gameState)
|
||||
return rates;
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.printQueues = function(gameState)
|
||||
QueueManager.prototype.printQueues = function(gameState)
|
||||
{
|
||||
let numWorkers = 0;
|
||||
gameState.getOwnUnits().forEach(ent => {
|
||||
if (ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_WORKER && ent.getMetadata(PlayerID, "plan") === undefined)
|
||||
if (ent.getMetadata(PlayerID, "role") === Worker.ROLE_WORKER &&
|
||||
ent.getMetadata(PlayerID, "plan") === undefined)
|
||||
{
|
||||
numWorkers++;
|
||||
}
|
||||
});
|
||||
API3.warn("---------- QUEUES ------------ with pop " + gameState.getPopulation() + " and workers " + numWorkers);
|
||||
for (const i in this.queues)
|
||||
@@ -190,7 +196,7 @@ PETRA.QueueManager.prototype.printQueues = function(gameState)
|
||||
API3.warn("------------------------------------");
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.clear = function()
|
||||
QueueManager.prototype.clear = function()
|
||||
{
|
||||
for (const i in this.queues)
|
||||
this.queues[i].empty();
|
||||
@@ -199,7 +205,7 @@ PETRA.QueueManager.prototype.clear = function()
|
||||
/**
|
||||
* set accounts of queue i from the unaccounted resources
|
||||
*/
|
||||
PETRA.QueueManager.prototype.setAccounts = function(gameState, cost, i)
|
||||
QueueManager.prototype.setAccounts = function(gameState, cost, i)
|
||||
{
|
||||
const available = this.getAvailableResources(gameState);
|
||||
for (const res of Resources.GetCodes())
|
||||
@@ -213,7 +219,7 @@ PETRA.QueueManager.prototype.setAccounts = function(gameState, cost, i)
|
||||
/**
|
||||
* transfer accounts from queue i to queue j
|
||||
*/
|
||||
PETRA.QueueManager.prototype.transferAccounts = function(cost, i, j)
|
||||
QueueManager.prototype.transferAccounts = function(cost, i, j)
|
||||
{
|
||||
for (const res of Resources.GetCodes())
|
||||
{
|
||||
@@ -228,7 +234,7 @@ PETRA.QueueManager.prototype.transferAccounts = function(cost, i, j)
|
||||
/**
|
||||
* distribute the resources between the different queues according to their priorities
|
||||
*/
|
||||
PETRA.QueueManager.prototype.distributeResources = function(gameState)
|
||||
QueueManager.prototype.distributeResources = function(gameState)
|
||||
{
|
||||
const availableRes = this.getAvailableResources(gameState);
|
||||
for (const res of Resources.GetCodes())
|
||||
@@ -317,7 +323,7 @@ PETRA.QueueManager.prototype.distributeResources = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.switchResource = function(gameState, res)
|
||||
QueueManager.prototype.switchResource = function(gameState, res)
|
||||
{
|
||||
// We have no available resources, see if we can't "compact" them in one queue.
|
||||
// compare queues 2 by 2, and if one with a higher priority could be completed by our amount, give it.
|
||||
@@ -354,7 +360,7 @@ PETRA.QueueManager.prototype.switchResource = function(gameState, res)
|
||||
};
|
||||
|
||||
// Start the next item in the queue if we can afford it.
|
||||
PETRA.QueueManager.prototype.startNextItems = function(gameState)
|
||||
QueueManager.prototype.startNextItems = function(gameState)
|
||||
{
|
||||
for (const q of this.queueArrays)
|
||||
{
|
||||
@@ -383,7 +389,7 @@ PETRA.QueueManager.prototype.startNextItems = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.update = function(gameState)
|
||||
QueueManager.prototype.update = function(gameState)
|
||||
{
|
||||
Engine.ProfileStart("Queue Manager");
|
||||
|
||||
@@ -412,9 +418,9 @@ PETRA.QueueManager.prototype.update = function(gameState)
|
||||
};
|
||||
|
||||
// Recovery system: if short of workers after an attack, pause (and reset) some queues to favor worker training
|
||||
PETRA.QueueManager.prototype.checkPausedQueues = function(gameState)
|
||||
QueueManager.prototype.checkPausedQueues = function(gameState)
|
||||
{
|
||||
const numWorkers = gameState.countOwnEntitiesAndQueuedWithRole(PETRA.Worker.ROLE_WORKER);
|
||||
const numWorkers = gameState.countOwnEntitiesAndQueuedWithRole(Worker.ROLE_WORKER);
|
||||
const workersMin = Math.min(Math.max(12, 24 * this.Config.popScaling), this.Config.Economy.popPhase2);
|
||||
for (const q in this.queues)
|
||||
{
|
||||
@@ -466,14 +472,14 @@ PETRA.QueueManager.prototype.checkPausedQueues = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.canAfford = function(queue, cost)
|
||||
QueueManager.prototype.canAfford = function(queue, cost)
|
||||
{
|
||||
if (!this.accounts[queue])
|
||||
return false;
|
||||
return this.accounts[queue].canAfford(cost);
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.pauseQueue = function(queue, scrapAccounts)
|
||||
QueueManager.prototype.pauseQueue = function(queue, scrapAccounts)
|
||||
{
|
||||
if (!this.queues[queue])
|
||||
return;
|
||||
@@ -482,13 +488,13 @@ PETRA.QueueManager.prototype.pauseQueue = function(queue, scrapAccounts)
|
||||
this.accounts[queue].reset();
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.unpauseQueue = function(queue)
|
||||
QueueManager.prototype.unpauseQueue = function(queue)
|
||||
{
|
||||
if (this.queues[queue])
|
||||
this.queues[queue].paused = false;
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.pauseAll = function(scrapAccounts, but)
|
||||
QueueManager.prototype.pauseAll = function(scrapAccounts, but)
|
||||
{
|
||||
for (const q in this.queues)
|
||||
{
|
||||
@@ -500,7 +506,7 @@ PETRA.QueueManager.prototype.pauseAll = function(scrapAccounts, but)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.unpauseAll = function(but)
|
||||
QueueManager.prototype.unpauseAll = function(but)
|
||||
{
|
||||
for (const q in this.queues)
|
||||
if (q != but)
|
||||
@@ -508,12 +514,12 @@ PETRA.QueueManager.prototype.unpauseAll = function(but)
|
||||
};
|
||||
|
||||
|
||||
PETRA.QueueManager.prototype.addQueue = function(queueName, priority)
|
||||
QueueManager.prototype.addQueue = function(queueName, priority)
|
||||
{
|
||||
if (this.queues[queueName] !== undefined)
|
||||
return;
|
||||
|
||||
this.queues[queueName] = new PETRA.Queue();
|
||||
this.queues[queueName] = new Queue();
|
||||
this.priorities[queueName] = priority;
|
||||
this.accounts[queueName] = new API3.Resources();
|
||||
|
||||
@@ -524,7 +530,7 @@ PETRA.QueueManager.prototype.addQueue = function(queueName, priority)
|
||||
this.queueArrays.sort((a, b) => priorities[b[0]] - priorities[a[0]]);
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.removeQueue = function(queueName)
|
||||
QueueManager.prototype.removeQueue = function(queueName)
|
||||
{
|
||||
if (this.queues[queueName] === undefined)
|
||||
return;
|
||||
@@ -540,12 +546,12 @@ PETRA.QueueManager.prototype.removeQueue = function(queueName)
|
||||
this.queueArrays.sort((a, b) => priorities[b[0]] - priorities[a[0]]);
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.getPriority = function(queueName)
|
||||
QueueManager.prototype.getPriority = function(queueName)
|
||||
{
|
||||
return this.priorities[queueName];
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.changePriority = function(queueName, newPriority)
|
||||
QueueManager.prototype.changePriority = function(queueName, newPriority)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn(">>> Priority of queue " + queueName + " changed from " + this.priorities[queueName] + " to " + newPriority);
|
||||
@@ -555,7 +561,7 @@ PETRA.QueueManager.prototype.changePriority = function(queueName, newPriority)
|
||||
this.queueArrays.sort((a, b) => priorities[b[0]] - priorities[a[0]]);
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.Serialize = function()
|
||||
QueueManager.prototype.Serialize = function()
|
||||
{
|
||||
const accounts = {};
|
||||
const queues = {};
|
||||
@@ -575,7 +581,7 @@ PETRA.QueueManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.QueueManager.prototype.Deserialize = function(gameState, data)
|
||||
QueueManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
this.priorities = data.priorities;
|
||||
this.queues = {};
|
||||
@@ -585,7 +591,7 @@ PETRA.QueueManager.prototype.Deserialize = function(gameState, data)
|
||||
this.queueArrays = [];
|
||||
for (const q in data.queues)
|
||||
{
|
||||
this.queues[q] = new PETRA.Queue();
|
||||
this.queues[q] = new Queue();
|
||||
this.queues[q].Deserialize(gameState, data.queues[q]);
|
||||
this.accounts[q] = new API3.Resources();
|
||||
this.accounts[q].Deserialize(data.accounts[q]);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Common functions and variables to all queue plans.
|
||||
*/
|
||||
|
||||
PETRA.QueuePlan = function(gameState, type, metadata)
|
||||
export function QueuePlan(gameState, type, metadata)
|
||||
{
|
||||
this.type = gameState.applyCiv(type);
|
||||
this.metadata = metadata;
|
||||
@@ -19,33 +19,33 @@ PETRA.QueuePlan = function(gameState, type, metadata)
|
||||
this.category = "";
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/** Check the content of this queue */
|
||||
PETRA.QueuePlan.prototype.isInvalid = function(gameState)
|
||||
QueuePlan.prototype.isInvalid = function(gameState)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
/** if true, the queue manager will begin increasing this plan's account. */
|
||||
PETRA.QueuePlan.prototype.isGo = function(gameState)
|
||||
QueuePlan.prototype.isGo = function(gameState)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/** can we start this plan immediately? */
|
||||
PETRA.QueuePlan.prototype.canStart = function(gameState)
|
||||
QueuePlan.prototype.canStart = function(gameState)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
/** process the plan. */
|
||||
PETRA.QueuePlan.prototype.start = function(gameState)
|
||||
QueuePlan.prototype.start = function(gameState)
|
||||
{
|
||||
// should call onStart.
|
||||
};
|
||||
|
||||
PETRA.QueuePlan.prototype.getCost = function()
|
||||
QueuePlan.prototype.getCost = function()
|
||||
{
|
||||
const costs = new API3.Resources();
|
||||
costs.add(this.cost);
|
||||
@@ -60,6 +60,6 @@ PETRA.QueuePlan.prototype.getCost = function()
|
||||
* Need to be updated to actually do something if you want them to.
|
||||
* this is called by "Start" if it succeeds.
|
||||
*/
|
||||
PETRA.QueuePlan.prototype.onStart = function(gameState)
|
||||
QueuePlan.prototype.onStart = function(gameState)
|
||||
{
|
||||
};
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { getBuiltEntity, getLandAccess, getSeaAccess } from "simulation/ai/petra/entityExtend.js";
|
||||
import { border_Mask, createObstructionMap, fullBorder_Mask, outside_Mask } from
|
||||
"simulation/ai/petra/mapModule.js";
|
||||
import { QueuePlan } from "simulation/ai/petra/queueplan.js";
|
||||
|
||||
/**
|
||||
* Defines a construction plan, ie a building.
|
||||
* We'll try to fing a good position if non has been provided
|
||||
*/
|
||||
|
||||
PETRA.ConstructionPlan = function(gameState, type, metadata, position)
|
||||
export function ConstructionPlan(gameState, type, metadata, position)
|
||||
{
|
||||
if (!PETRA.QueuePlan.call(this, gameState, type, metadata))
|
||||
if (!QueuePlan.call(this, gameState, type, metadata))
|
||||
return false;
|
||||
|
||||
this.position = position ? position : 0;
|
||||
@@ -13,11 +18,11 @@ PETRA.ConstructionPlan = function(gameState, type, metadata, position)
|
||||
this.category = "building";
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.ConstructionPlan.prototype = Object.create(PETRA.QueuePlan.prototype);
|
||||
ConstructionPlan.prototype = Object.create(QueuePlan.prototype);
|
||||
|
||||
PETRA.ConstructionPlan.prototype.canStart = function(gameState)
|
||||
ConstructionPlan.prototype.canStart = function(gameState)
|
||||
{
|
||||
if (gameState.ai.HQ.turnCache.buildingBuilt) // do not start another building if already one this turn
|
||||
return false;
|
||||
@@ -31,7 +36,7 @@ PETRA.ConstructionPlan.prototype.canStart = function(gameState)
|
||||
return gameState.ai.HQ.buildManager.hasBuilder(this.type);
|
||||
};
|
||||
|
||||
PETRA.ConstructionPlan.prototype.start = function(gameState)
|
||||
ConstructionPlan.prototype.start = function(gameState)
|
||||
{
|
||||
Engine.ProfileStart("Building construction start");
|
||||
|
||||
@@ -106,7 +111,7 @@ PETRA.ConstructionPlan.prototype.start = function(gameState)
|
||||
gameState.ai.HQ.navalManager.createTransportIfNeeded(gameState, this.metadata.proximity, [pos.x, pos.z], this.metadata.access);
|
||||
};
|
||||
|
||||
PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
{
|
||||
let template = this.template;
|
||||
|
||||
@@ -214,7 +219,7 @@ PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
const x = Math.round(pos[0] / cellSize);
|
||||
const z = Math.round(pos[1] / cellSize);
|
||||
|
||||
const struct = PETRA.getBuiltEntity(gameState, ent);
|
||||
const struct = getBuiltEntity(gameState, ent);
|
||||
if (struct.resourceDropsiteTypes() && struct.resourceDropsiteTypes().indexOf("food") != -1)
|
||||
{
|
||||
if (template.hasClasses(["Field", "Corral"]))
|
||||
@@ -248,7 +253,7 @@ PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
for (let j = 0; j < placement.map.length; ++j)
|
||||
{
|
||||
let value = placement.map[j] - gameState.sharedScript.resourceMaps.wood.map[j]/3;
|
||||
if (HQ.borderMap.map[j] & PETRA.fullBorder_Mask)
|
||||
if (HQ.borderMap.map[j] & fullBorder_Mask)
|
||||
value /= 2; // we need space around farmstead, so disfavor map border
|
||||
placement.set(j, value);
|
||||
}
|
||||
@@ -270,9 +275,9 @@ PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
placement.map[j] = 0;
|
||||
else if (placement.map[j] > 0)
|
||||
{
|
||||
if (favorBorder && HQ.borderMap.map[j] & PETRA.border_Mask)
|
||||
if (favorBorder && HQ.borderMap.map[j] & border_Mask)
|
||||
placement.set(j, placement.map[j] + 50);
|
||||
else if (disfavorBorder && !(HQ.borderMap.map[j] & PETRA.fullBorder_Mask))
|
||||
else if (disfavorBorder && !(HQ.borderMap.map[j] & fullBorder_Mask))
|
||||
placement.set(j, placement.map[j] + 10);
|
||||
|
||||
const x = (j % placement.width + 0.5) * cellSize;
|
||||
@@ -290,9 +295,9 @@ PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
placement.map[j] = 0;
|
||||
else if (placement.map[j] > 0)
|
||||
{
|
||||
if (favorBorder && HQ.borderMap.map[j] & PETRA.border_Mask)
|
||||
if (favorBorder && HQ.borderMap.map[j] & border_Mask)
|
||||
placement.set(j, placement.map[j] + 50);
|
||||
else if (disfavorBorder && !(HQ.borderMap.map[j] & PETRA.fullBorder_Mask))
|
||||
else if (disfavorBorder && !(HQ.borderMap.map[j] & fullBorder_Mask))
|
||||
placement.set(j, placement.map[j] + 10);
|
||||
|
||||
const x = (j % placement.width + 0.5) * cellSize;
|
||||
@@ -311,7 +316,7 @@ PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
// note: not for houses and dropsites who ought to be closer to either each other or a resource.
|
||||
// also not for fields who can be stacked quite a bit
|
||||
|
||||
const obstructions = PETRA.createObstructionMap(gameState, 0, template);
|
||||
const obstructions = createObstructionMap(gameState, 0, template);
|
||||
// obstructions.dumpIm(template.buildPlacementType() + "_obstructions.png");
|
||||
|
||||
let radius;
|
||||
@@ -359,12 +364,12 @@ PETRA.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
* => we try not to be too far from our territory
|
||||
* In all cases, we add a bonus for nearby resources, and when a large extend of water in front ot it.
|
||||
*/
|
||||
PETRA.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
{
|
||||
const template = this.template;
|
||||
const territoryMap = gameState.ai.HQ.territoryMap;
|
||||
|
||||
const obstructions = PETRA.createObstructionMap(gameState, 0, template);
|
||||
const obstructions = createObstructionMap(gameState, 0, template);
|
||||
// obstructions.dumpIm(template.buildPlacementType() + "_obstructions.png");
|
||||
|
||||
let bestIdx;
|
||||
@@ -467,7 +472,7 @@ PETRA.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
let dockDist = 0;
|
||||
for (const dock of docks.values())
|
||||
{
|
||||
if (PETRA.getSeaAccess(gameState, dock) != navalPassMap[i])
|
||||
if (getSeaAccess(gameState, dock) != navalPassMap[i])
|
||||
continue;
|
||||
const dist = API3.SquareVectorDistance(pos, dock.position());
|
||||
if (dist > dockDist)
|
||||
@@ -483,7 +488,7 @@ PETRA.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
}
|
||||
|
||||
// Add a penalty if on the map border as ship movement will be difficult
|
||||
if (gameState.ai.HQ.borderMap.map[j] & PETRA.fullBorder_Mask)
|
||||
if (gameState.ai.HQ.borderMap.map[j] & fullBorder_Mask)
|
||||
score += 20;
|
||||
|
||||
// Do a pre-selection, supposing we will have the best possible water
|
||||
@@ -534,7 +539,7 @@ PETRA.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
/**
|
||||
* Find a good island to build a dock.
|
||||
*/
|
||||
PETRA.ConstructionPlan.prototype.buildOverseaDock = function(gameState, template)
|
||||
ConstructionPlan.prototype.buildOverseaDock = function(gameState, template)
|
||||
{
|
||||
const docks = gameState.getOwnStructures().filter(API3.Filters.byClass("Dock"));
|
||||
if (!docks.hasEntities())
|
||||
@@ -554,7 +559,7 @@ PETRA.ConstructionPlan.prototype.buildOverseaDock = function(gameState, template
|
||||
let keep = true;
|
||||
for (const dock of docks.values())
|
||||
{
|
||||
if (PETRA.getLandAccess(gameState, dock) != i)
|
||||
if (getLandAccess(gameState, dock) != i)
|
||||
continue;
|
||||
keep = false;
|
||||
break;
|
||||
@@ -564,7 +569,7 @@ PETRA.ConstructionPlan.prototype.buildOverseaDock = function(gameState, template
|
||||
let sea;
|
||||
for (const cc of ccEnts.values())
|
||||
{
|
||||
const ccAccess = PETRA.getLandAccess(gameState, cc);
|
||||
const ccAccess = getLandAccess(gameState, cc);
|
||||
if (ccAccess != i)
|
||||
{
|
||||
if (cc.owner() == PlayerID && !sea)
|
||||
@@ -614,7 +619,7 @@ PETRA.ConstructionPlan.prototype.buildOverseaDock = function(gameState, template
|
||||
};
|
||||
|
||||
/** Algorithm taken from the function GetDockAngle in simulation/helpers/Commands.js */
|
||||
PETRA.ConstructionPlan.prototype.getDockAngle = function(gameState, x, z, size)
|
||||
ConstructionPlan.prototype.getDockAngle = function(gameState, x, z, size)
|
||||
{
|
||||
let pos = gameState.ai.accessibility.gamePosToMapPos([x, z]);
|
||||
const k = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
@@ -676,7 +681,7 @@ PETRA.ConstructionPlan.prototype.getDockAngle = function(gameState, x, z, size)
|
||||
* to determine the special dock requirements
|
||||
* returns {"land": land index for this dock, "water": amount of water around this spot}
|
||||
*/
|
||||
PETRA.ConstructionPlan.prototype.checkDockPlacement = function(gameState, x, z, halfDepth, halfWidth, angle)
|
||||
ConstructionPlan.prototype.checkDockPlacement = function(gameState, x, z, halfDepth, halfWidth, angle)
|
||||
{
|
||||
const sz = halfDepth * Math.sin(angle);
|
||||
const cz = halfDepth * Math.cos(angle);
|
||||
@@ -738,10 +743,10 @@ PETRA.ConstructionPlan.prototype.checkDockPlacement = function(gameState, x, z,
|
||||
* if the (object) wantedLand is given, this nearest land should have one of these accessibility
|
||||
* if wantedSea is given, this tile should be inside this sea
|
||||
*/
|
||||
PETRA.ConstructionPlan.prototype.around = [[ 1.0, 0.0], [ 0.87, 0.50], [ 0.50, 0.87], [ 0.0, 1.0], [-0.50, 0.87], [-0.87, 0.50],
|
||||
ConstructionPlan.prototype.around = [[ 1.0, 0.0], [ 0.87, 0.50], [ 0.50, 0.87], [ 0.0, 1.0], [-0.50, 0.87], [-0.87, 0.50],
|
||||
[-1.0, 0.0], [-0.87, -0.50], [-0.50, -0.87], [ 0.0, -1.0], [ 0.50, -0.87], [ 0.87, -0.50]];
|
||||
|
||||
PETRA.ConstructionPlan.prototype.isDockLocation = function(gameState, j, dimension, wantedLand, wantedSea)
|
||||
ConstructionPlan.prototype.isDockLocation = function(gameState, j, dimension, wantedLand, wantedSea)
|
||||
{
|
||||
const width = gameState.ai.HQ.territoryMap.width;
|
||||
const cellSize = gameState.ai.HQ.territoryMap.cellSize;
|
||||
@@ -788,7 +793,7 @@ PETRA.ConstructionPlan.prototype.isDockLocation = function(gameState, j, dimensi
|
||||
* return a measure of the proximity to our frontier (including our allies)
|
||||
* 0=inside, 1=less than 24m, 2= less than 48m, 3= less than 72m, 4=less than 96m, 5=above 96m
|
||||
*/
|
||||
PETRA.ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
|
||||
ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
|
||||
{
|
||||
const alliedVictory = gameState.getAlliedVictory();
|
||||
const territoryMap = gameState.ai.HQ.territoryMap;
|
||||
@@ -812,7 +817,7 @@ PETRA.ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
|
||||
const jz = iz + Math.round(i*step*a[1]);
|
||||
if (jz < 0 || jz >= width)
|
||||
continue;
|
||||
if (borderMap.map[jx+width*jz] & PETRA.outside_Mask)
|
||||
if (borderMap.map[jx+width*jz] & outside_Mask)
|
||||
continue;
|
||||
territoryOwner = territoryMap.getOwnerIndex(jx+width*jz);
|
||||
if (alliedVictory && gameState.isPlayerAlly(territoryOwner) || territoryOwner == PlayerID)
|
||||
@@ -832,7 +837,7 @@ PETRA.ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
|
||||
* get the sum of the resources (except food) around, inside a given radius
|
||||
* resources have a weight (1 if dist=0 and 0 if dist=size) doubled for wood
|
||||
*/
|
||||
PETRA.ConstructionPlan.prototype.getResourcesAround = function(gameState, types, i, radius)
|
||||
ConstructionPlan.prototype.getResourcesAround = function(gameState, types, i, radius)
|
||||
{
|
||||
const resourceMaps = gameState.sharedScript.resourceMaps;
|
||||
const w = resourceMaps.wood.width;
|
||||
@@ -885,7 +890,7 @@ PETRA.ConstructionPlan.prototype.getResourcesAround = function(gameState, types,
|
||||
return nbcell ? total / nbcell : 0;
|
||||
};
|
||||
|
||||
PETRA.ConstructionPlan.prototype.isGo = function(gameState)
|
||||
ConstructionPlan.prototype.isGo = function(gameState)
|
||||
{
|
||||
if (this.goRequirement && this.goRequirement == "houseNeeded")
|
||||
{
|
||||
@@ -913,13 +918,13 @@ PETRA.ConstructionPlan.prototype.isGo = function(gameState)
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.ConstructionPlan.prototype.onStart = function(gameState)
|
||||
ConstructionPlan.prototype.onStart = function(gameState)
|
||||
{
|
||||
if (this.queueToReset)
|
||||
gameState.ai.queueManager.changePriority(this.queueToReset, gameState.ai.Config.priorities[this.queueToReset]);
|
||||
};
|
||||
|
||||
PETRA.ConstructionPlan.prototype.Serialize = function()
|
||||
ConstructionPlan.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"category": this.category,
|
||||
@@ -934,7 +939,7 @@ PETRA.ConstructionPlan.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.ConstructionPlan.prototype.Deserialize = function(gameState, data)
|
||||
ConstructionPlan.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
PETRA.ResearchPlan = function(gameState, type, rush = false)
|
||||
import { QueuePlan } from "simulation/ai/petra/queueplan.js";
|
||||
|
||||
export function ResearchPlan(gameState, type, rush = false)
|
||||
{
|
||||
if (!PETRA.QueuePlan.call(this, gameState, type, {}))
|
||||
if (!QueuePlan.call(this, gameState, type, {}))
|
||||
return false;
|
||||
|
||||
if (this.template.researchTime === undefined)
|
||||
@@ -15,11 +17,11 @@ PETRA.ResearchPlan = function(gameState, type, rush = false)
|
||||
this.rush = rush;
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.ResearchPlan.prototype = Object.create(PETRA.QueuePlan.prototype);
|
||||
ResearchPlan.prototype = Object.create(QueuePlan.prototype);
|
||||
|
||||
PETRA.ResearchPlan.prototype.canStart = function(gameState)
|
||||
ResearchPlan.prototype.canStart = function(gameState)
|
||||
{
|
||||
this.researchers = this.getBestResearchers(gameState);
|
||||
if (!this.researchers)
|
||||
@@ -28,7 +30,7 @@ PETRA.ResearchPlan.prototype.canStart = function(gameState)
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.ResearchPlan.prototype.getBestResearchers = function(gameState, noRequirementCheck = false)
|
||||
ResearchPlan.prototype.getBestResearchers = function(gameState, noRequirementCheck = false)
|
||||
{
|
||||
const allResearchers = gameState.findResearchers(this.type, noRequirementCheck);
|
||||
if (!allResearchers || !allResearchers.hasEntities())
|
||||
@@ -51,12 +53,12 @@ PETRA.ResearchPlan.prototype.getBestResearchers = function(gameState, noRequirem
|
||||
return researchers;
|
||||
};
|
||||
|
||||
PETRA.ResearchPlan.prototype.isInvalid = function(gameState)
|
||||
ResearchPlan.prototype.isInvalid = function(gameState)
|
||||
{
|
||||
return gameState.isResearched(this.type) || gameState.isResearching(this.type);
|
||||
};
|
||||
|
||||
PETRA.ResearchPlan.prototype.start = function(gameState)
|
||||
ResearchPlan.prototype.start = function(gameState)
|
||||
{
|
||||
// Prefer researcher with shortest queues (no need to serialize this.researchers
|
||||
// as the functions canStart and start are always called on the same turn)
|
||||
@@ -68,7 +70,7 @@ PETRA.ResearchPlan.prototype.start = function(gameState)
|
||||
this.onStart(gameState);
|
||||
};
|
||||
|
||||
PETRA.ResearchPlan.prototype.onStart = function(gameState)
|
||||
ResearchPlan.prototype.onStart = function(gameState)
|
||||
{
|
||||
if (this.queueToReset)
|
||||
gameState.ai.queueManager.changePriority(this.queueToReset, gameState.ai.Config.priorities[this.queueToReset]);
|
||||
@@ -83,7 +85,7 @@ PETRA.ResearchPlan.prototype.onStart = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.ResearchPlan.prototype.Serialize = function()
|
||||
ResearchPlan.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"category": this.category,
|
||||
@@ -97,7 +99,7 @@ PETRA.ResearchPlan.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.ResearchPlan.prototype.Deserialize = function(gameState, data)
|
||||
ResearchPlan.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
PETRA.TrainingPlan = function(gameState, type, metadata, number = 1, maxMerge = 5)
|
||||
import { QueuePlan } from "simulation/ai/petra/queueplan.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
export function TrainingPlan(gameState, type, metadata, number = 1, maxMerge = 5)
|
||||
{
|
||||
if (!PETRA.QueuePlan.call(this, gameState, type, metadata))
|
||||
if (!QueuePlan.call(this, gameState, type, metadata))
|
||||
{
|
||||
API3.warn(" Plan training " + type + " canceled");
|
||||
return false;
|
||||
@@ -16,11 +19,11 @@ PETRA.TrainingPlan = function(gameState, type, metadata, number = 1, maxMerge =
|
||||
this.maxMerge = maxMerge;
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.TrainingPlan.prototype = Object.create(PETRA.QueuePlan.prototype);
|
||||
TrainingPlan.prototype = Object.create(QueuePlan.prototype);
|
||||
|
||||
PETRA.TrainingPlan.prototype.canStart = function(gameState)
|
||||
TrainingPlan.prototype.canStart = function(gameState)
|
||||
{
|
||||
this.trainers = this.getBestTrainers(gameState);
|
||||
if (!this.trainers)
|
||||
@@ -29,7 +32,7 @@ PETRA.TrainingPlan.prototype.canStart = function(gameState)
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.TrainingPlan.prototype.getBestTrainers = function(gameState)
|
||||
TrainingPlan.prototype.getBestTrainers = function(gameState)
|
||||
{
|
||||
if (this.metadata && this.metadata.trainer)
|
||||
{
|
||||
@@ -63,7 +66,7 @@ PETRA.TrainingPlan.prototype.getBestTrainers = function(gameState)
|
||||
return trainers;
|
||||
};
|
||||
|
||||
PETRA.TrainingPlan.prototype.start = function(gameState)
|
||||
TrainingPlan.prototype.start = function(gameState)
|
||||
{
|
||||
if (this.metadata && this.metadata.trainer)
|
||||
{
|
||||
@@ -79,7 +82,8 @@ PETRA.TrainingPlan.prototype.start = function(gameState)
|
||||
let wantedIndex;
|
||||
if (this.metadata && this.metadata.index)
|
||||
wantedIndex = this.metadata.index;
|
||||
const workerUnit = this.metadata && this.metadata.role && this.metadata.role === PETRA.Worker.ROLE_WORKER;
|
||||
const workerUnit = this.metadata && this.metadata.role &&
|
||||
this.metadata.role === Worker.ROLE_WORKER;
|
||||
const supportUnit = this.template.hasClass("Support");
|
||||
this.trainers.sort(function(a, b) {
|
||||
// Prefer training buildings with short queues
|
||||
@@ -129,12 +133,12 @@ PETRA.TrainingPlan.prototype.start = function(gameState)
|
||||
this.onStart(gameState);
|
||||
};
|
||||
|
||||
PETRA.TrainingPlan.prototype.addItem = function(amount = 1)
|
||||
TrainingPlan.prototype.addItem = function(amount = 1)
|
||||
{
|
||||
this.number += amount;
|
||||
};
|
||||
|
||||
PETRA.TrainingPlan.prototype.Serialize = function()
|
||||
TrainingPlan.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"category": this.category,
|
||||
@@ -147,7 +151,7 @@ PETRA.TrainingPlan.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.TrainingPlan.prototype.Deserialize = function(gameState, data)
|
||||
TrainingPlan.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { ResearchPlan } from "simulation/ai/petra/queueplanResearch.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Manage the research
|
||||
*/
|
||||
PETRA.ResearchManager = function(Config)
|
||||
export function ResearchManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can go to the next phase
|
||||
*/
|
||||
PETRA.ResearchManager.prototype.checkPhase = function(gameState, queues)
|
||||
ResearchManager.prototype.checkPhase = function(gameState, queues)
|
||||
{
|
||||
if (queues.majorTech.hasQueuedUnits())
|
||||
return;
|
||||
@@ -31,11 +34,11 @@ PETRA.ResearchManager.prototype.checkPhase = function(gameState, queues)
|
||||
gameState.ai.HQ.phasing = currentPhaseIndex + 1;
|
||||
// Reset the queue priority in case it was changed during a previous phase update
|
||||
gameState.ai.queueManager.changePriority("majorTech", gameState.ai.Config.priorities.majorTech);
|
||||
queues.majorTech.addPlan(new PETRA.ResearchPlan(gameState, nextPhaseName, true));
|
||||
queues.majorTech.addPlan(new ResearchPlan(gameState, nextPhaseName, true));
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.ResearchManager.prototype.researchPopulationBonus = function(gameState, queues)
|
||||
ResearchManager.prototype.researchPopulationBonus = function(gameState, queues)
|
||||
{
|
||||
if (queues.minorTech.hasQueuedUnits())
|
||||
return;
|
||||
@@ -48,12 +51,12 @@ PETRA.ResearchManager.prototype.researchPopulationBonus = function(gameState, qu
|
||||
// TODO may-be loop on all modifs and check if the effect if positive ?
|
||||
if (tech[1]._template.modifications[0].value !== "Population/Bonus")
|
||||
continue;
|
||||
queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, tech[0]));
|
||||
queues.minorTech.addPlan(new ResearchPlan(gameState, tech[0]));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.ResearchManager.prototype.researchTradeBonus = function(gameState, queues)
|
||||
ResearchManager.prototype.researchTradeBonus = function(gameState, queues)
|
||||
{
|
||||
if (queues.minorTech.hasQueuedUnits())
|
||||
return;
|
||||
@@ -69,17 +72,17 @@ PETRA.ResearchManager.prototype.researchTradeBonus = function(gameState, queues)
|
||||
if (tech[1]._template.modifications[0].value !== "UnitMotion/WalkSpeed" &&
|
||||
tech[1]._template.modifications[0].value !== "Trader/GainMultiplier")
|
||||
continue;
|
||||
queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, tech[0]));
|
||||
queues.minorTech.addPlan(new ResearchPlan(gameState, tech[0]));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/** Techs to be searched for as soon as they are available */
|
||||
PETRA.ResearchManager.prototype.researchWantedTechs = function(gameState, techs)
|
||||
ResearchManager.prototype.researchWantedTechs = function(gameState, techs)
|
||||
{
|
||||
const phase1 = gameState.currentPhase() === 1;
|
||||
const available = phase1 ? gameState.ai.queueManager.getAvailableResources(gameState) : null;
|
||||
const numWorkers = phase1 ? gameState.getOwnEntitiesByRole(PETRA.Worker.ROLE_WORKER, true).length : 0;
|
||||
const numWorkers = phase1 ? gameState.getOwnEntitiesByRole(Worker.ROLE_WORKER, true).length : 0;
|
||||
for (const tech of techs)
|
||||
{
|
||||
if (tech[0].indexOf("unlock_champion") == 0)
|
||||
@@ -119,11 +122,11 @@ PETRA.ResearchManager.prototype.researchWantedTechs = function(gameState, techs)
|
||||
};
|
||||
|
||||
/** Techs to be searched for as soon as they are available, but only after phase 2 */
|
||||
PETRA.ResearchManager.prototype.researchPreferredTechs = function(gameState, techs)
|
||||
ResearchManager.prototype.researchPreferredTechs = function(gameState, techs)
|
||||
{
|
||||
const phase2 = gameState.currentPhase() === 2;
|
||||
const available = phase2 ? gameState.ai.queueManager.getAvailableResources(gameState) : null;
|
||||
const numWorkers = phase2 ? gameState.getOwnEntitiesByRole(PETRA.Worker.ROLE_WORKER, true).length : 0;
|
||||
const numWorkers = phase2 ? gameState.getOwnEntitiesByRole(Worker.ROLE_WORKER, true).length : 0;
|
||||
for (const tech of techs)
|
||||
{
|
||||
if (!tech[1]._template.modifications)
|
||||
@@ -155,7 +158,7 @@ PETRA.ResearchManager.prototype.researchPreferredTechs = function(gameState, tec
|
||||
return null;
|
||||
};
|
||||
|
||||
PETRA.ResearchManager.prototype.update = function(gameState, queues)
|
||||
ResearchManager.prototype.update = function(gameState, queues)
|
||||
{
|
||||
if (queues.minorTech.hasQueuedUnits() || queues.majorTech.hasQueuedUnits())
|
||||
return;
|
||||
@@ -168,12 +171,12 @@ PETRA.ResearchManager.prototype.update = function(gameState, queues)
|
||||
if (techName.increasePriority)
|
||||
{
|
||||
gameState.ai.queueManager.changePriority("minorTech", 2*this.Config.priorities.minorTech);
|
||||
const plan = new PETRA.ResearchPlan(gameState, techName.name);
|
||||
const plan = new ResearchPlan(gameState, techName.name);
|
||||
plan.queueToReset = "minorTech";
|
||||
queues.minorTech.addPlan(plan);
|
||||
}
|
||||
else
|
||||
queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, techName.name));
|
||||
queues.minorTech.addPlan(new ResearchPlan(gameState, techName.name));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,12 +189,12 @@ PETRA.ResearchManager.prototype.update = function(gameState, queues)
|
||||
if (techName.increasePriority)
|
||||
{
|
||||
gameState.ai.queueManager.changePriority("minorTech", 2*this.Config.priorities.minorTech);
|
||||
const plan = new PETRA.ResearchPlan(gameState, techName.name);
|
||||
const plan = new ResearchPlan(gameState, techName.name);
|
||||
plan.queueToReset = "minorTech";
|
||||
queues.minorTech.addPlan(plan);
|
||||
}
|
||||
else
|
||||
queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, techName.name));
|
||||
queues.minorTech.addPlan(new ResearchPlan(gameState, techName.name));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -221,10 +224,10 @@ PETRA.ResearchManager.prototype.update = function(gameState, queues)
|
||||
return;
|
||||
|
||||
// randomly pick one. No worries about pairs in that case.
|
||||
queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, pickRandom(techs)[0]));
|
||||
queues.minorTech.addPlan(new ResearchPlan(gameState, pickRandom(techs)[0]));
|
||||
};
|
||||
|
||||
PETRA.ResearchManager.prototype.CostSum = function(cost)
|
||||
ResearchManager.prototype.CostSum = function(cost)
|
||||
{
|
||||
let costSum = 0;
|
||||
for (const res in cost)
|
||||
@@ -232,11 +235,11 @@ PETRA.ResearchManager.prototype.CostSum = function(cost)
|
||||
return costSum;
|
||||
};
|
||||
|
||||
PETRA.ResearchManager.prototype.Serialize = function()
|
||||
ResearchManager.prototype.Serialize = function()
|
||||
{
|
||||
return {};
|
||||
};
|
||||
|
||||
PETRA.ResearchManager.prototype.Deserialize = function(data)
|
||||
ResearchManager.prototype.Deserialize = function(data)
|
||||
{
|
||||
};
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { Config, DIFFICULTY_SANDBOX } from "simulation/ai/petra/config.js";
|
||||
import { gatherTreasure, getLandAccess, isFastMoving } from "simulation/ai/petra/entityExtend.js";
|
||||
import { Headquather } from "simulation/ai/petra/headquarters.js";
|
||||
import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js";
|
||||
|
||||
/**
|
||||
* Determines the strategy to adopt when starting a new game,
|
||||
* depending on the initial conditions
|
||||
*/
|
||||
|
||||
PETRA.HQ.prototype.gameAnalysis = function(gameState)
|
||||
Headquather.prototype.gameAnalysis = function(gameState)
|
||||
{
|
||||
// Analysis of the terrain and the different access regions
|
||||
if (!this.regionAnalysis(gameState))
|
||||
@@ -27,7 +32,7 @@ PETRA.HQ.prototype.gameAnalysis = function(gameState)
|
||||
|
||||
|
||||
// Sandbox difficulty should not try to expand
|
||||
this.canExpand = this.Config.difficulty != PETRA.DIFFICULTY_SANDBOX;
|
||||
this.canExpand = this.Config.difficulty != DIFFICULTY_SANDBOX;
|
||||
// If no base yet, check if we can construct one. If not, dispatch our units to possible tasks/attacks
|
||||
this.canBuildUnits = true;
|
||||
if (!gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).hasEntities())
|
||||
@@ -52,7 +57,7 @@ PETRA.HQ.prototype.gameAnalysis = function(gameState)
|
||||
/**
|
||||
* Assign the starting entities to the different bases
|
||||
*/
|
||||
PETRA.HQ.prototype.assignStartingEntities = function(gameState)
|
||||
Headquather.prototype.assignStartingEntities = function(gameState)
|
||||
{
|
||||
for (const ent of gameState.getOwnEntities().values())
|
||||
{
|
||||
@@ -96,7 +101,7 @@ PETRA.HQ.prototype.assignStartingEntities = function(gameState)
|
||||
* determine the main land Index (or water index if none)
|
||||
* as well as the list of allowed (land andf water) regions
|
||||
*/
|
||||
PETRA.HQ.prototype.regionAnalysis = function(gameState)
|
||||
Headquather.prototype.regionAnalysis = function(gameState)
|
||||
{
|
||||
const accessibility = gameState.ai.accessibility;
|
||||
let landIndex;
|
||||
@@ -190,7 +195,7 @@ PETRA.HQ.prototype.regionAnalysis = function(gameState)
|
||||
* load units and buildings from the config files
|
||||
* TODO: change that to something dynamic
|
||||
*/
|
||||
PETRA.HQ.prototype.structureAnalysis = function(gameState)
|
||||
Headquather.prototype.structureAnalysis = function(gameState)
|
||||
{
|
||||
const civref = gameState.playerData.civ;
|
||||
const civ = civref in this.Config.buildings ? civref : 'default';
|
||||
@@ -204,7 +209,7 @@ PETRA.HQ.prototype.structureAnalysis = function(gameState)
|
||||
* build our first base
|
||||
* if not enough resource, try first to do a dock
|
||||
*/
|
||||
PETRA.HQ.prototype.buildFirstBase = function(gameState)
|
||||
Headquather.prototype.buildFirstBase = function(gameState)
|
||||
{
|
||||
if (gameState.ai.queues.civilCentre.hasQueuedUnits())
|
||||
return;
|
||||
@@ -226,7 +231,7 @@ PETRA.HQ.prototype.buildFirstBase = function(gameState)
|
||||
continue;
|
||||
// If we can get a treasure around, just do it
|
||||
if (ent.isIdle())
|
||||
PETRA.gatherTreasure(gameState, ent);
|
||||
gatherTreasure(gameState, ent);
|
||||
// Then count the resources from the treasures being collected
|
||||
const treasureId = ent.getMetadata(PlayerID, "treasure");
|
||||
if (!treasureId)
|
||||
@@ -266,12 +271,12 @@ PETRA.HQ.prototype.buildFirstBase = function(gameState)
|
||||
{
|
||||
if (!ent.hasClass("Worker"))
|
||||
continue;
|
||||
if (PETRA.isFastMoving(ent))
|
||||
if (isFastMoving(ent))
|
||||
continue;
|
||||
let pos = ent.position();
|
||||
if (!pos)
|
||||
{
|
||||
const holder = PETRA.getHolder(gameState, ent);
|
||||
const holder = getHolder(gameState, ent);
|
||||
if (!holder || !holder.position())
|
||||
continue;
|
||||
pos = holder.position();
|
||||
@@ -305,10 +310,15 @@ PETRA.HQ.prototype.buildFirstBase = function(gameState)
|
||||
if (goal == "dock")
|
||||
{
|
||||
const sea = startingPoint[imax].sea > 1 ? startingPoint[imax].sea : undefined;
|
||||
gameState.ai.queues.dock.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/dock", { "sea": sea, "proximity": startingPoint[imax].pos }));
|
||||
gameState.ai.queues.dock.addPlan(new ConstructionPlan(gameState, "structures/{civ}/dock",
|
||||
{ "sea": sea, "proximity": startingPoint[imax].pos }));
|
||||
}
|
||||
else
|
||||
gameState.ai.queues.civilCentre.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/civil_centre", { "base": -1, "resource": "wood", "proximity": startingPoint[imax].pos }));
|
||||
{
|
||||
gameState.ai.queues.civilCentre.addPlan(new ConstructionPlan(gameState,
|
||||
"structures/{civ}/civil_centre",
|
||||
{ "base": -1, "resource": "wood", "proximity": startingPoint[imax].pos }));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -316,7 +326,7 @@ PETRA.HQ.prototype.buildFirstBase = function(gameState)
|
||||
* - if one of our allies has a cc, affect a small fraction of our army for his defense, the rest will attack
|
||||
* - otherwise all units will attack
|
||||
*/
|
||||
PETRA.HQ.prototype.dispatchUnits = function(gameState)
|
||||
Headquather.prototype.dispatchUnits = function(gameState)
|
||||
{
|
||||
const allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
|
||||
if (allycc.length)
|
||||
@@ -333,10 +343,10 @@ PETRA.HQ.prototype.dispatchUnits = function(gameState)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
for (const cc of allycc)
|
||||
{
|
||||
if (!cc.position() || PETRA.getLandAccess(gameState, cc) != access)
|
||||
if (!cc.position() || getLandAccess(gameState, cc) != access)
|
||||
continue;
|
||||
--num;
|
||||
--num1;
|
||||
@@ -352,10 +362,10 @@ PETRA.HQ.prototype.dispatchUnits = function(gameState)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
for (const cc of allycc)
|
||||
{
|
||||
if (!cc.position() || PETRA.getLandAccess(gameState, cc) != access)
|
||||
if (!cc.position() || getLandAccess(gameState, cc) != access)
|
||||
continue;
|
||||
--num;
|
||||
--num2;
|
||||
@@ -371,10 +381,10 @@ PETRA.HQ.prototype.dispatchUnits = function(gameState)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
for (const cc of allycc)
|
||||
{
|
||||
if (!cc.position() || PETRA.getLandAccess(gameState, cc) != access)
|
||||
if (!cc.position() || getLandAccess(gameState, cc) != access)
|
||||
continue;
|
||||
if (!ent.hasClass("Support"))
|
||||
--num;
|
||||
@@ -392,7 +402,7 @@ PETRA.HQ.prototype.dispatchUnits = function(gameState)
|
||||
* - if on a small island, favor fishing
|
||||
* - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion)
|
||||
*/
|
||||
PETRA.HQ.prototype.configFirstBase = function(gameState)
|
||||
Headquather.prototype.configFirstBase = function(gameState)
|
||||
{
|
||||
if (!this.hasPotentialBase())
|
||||
return;
|
||||
@@ -495,14 +505,19 @@ PETRA.HQ.prototype.configFirstBase = function(gameState)
|
||||
const cost = new API3.Resources(gameState.getTemplate(newDP.templateName).cost());
|
||||
gameState.ai.queueManager.setAccounts(gameState, cost, "dropsites");
|
||||
}
|
||||
gameState.ai.queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.baseManagers()[0].ID }, newDP.pos));
|
||||
gameState.ai.queues.dropsites.addPlan(new ConstructionPlan(gameState, newDP.templateName,
|
||||
{ "base": this.baseManagers()[0].ID }, newDP.pos));
|
||||
}
|
||||
}
|
||||
// and build immediately a corral if needed
|
||||
if (this.needCorral)
|
||||
{
|
||||
const template = gameState.applyCiv("structures/{civ}/corral");
|
||||
if (!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() && this.canBuild(gameState, template))
|
||||
gameState.ai.queues.corral.addPlan(new PETRA.ConstructionPlan(gameState, template, { "base": this.baseManagers()[0].ID }));
|
||||
if (!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() &&
|
||||
this.canBuild(gameState, template))
|
||||
{
|
||||
gameState.ai.queues.corral.addPlan(
|
||||
new ConstructionPlan(gameState, template, { "base": this.baseManagers()[0].ID }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,35 +1,44 @@
|
||||
import { newTradeRoute as chatNewTradeRoute } from "simulation/ai/petra/chatHelper.js";
|
||||
import { Config, DIFFICULTY_VERY_EASY } from "simulation/ai/petra/config.js";
|
||||
import { gatherTreasure, getBestBase, getLandAccess, getSeaAccess, isLineInsideEnemyTerritory } from
|
||||
"simulation/ai/petra/entityExtend.js";
|
||||
import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js";
|
||||
import { TrainingPlan } from "simulation/ai/petra/queueplanTraining.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Manage the trade
|
||||
*/
|
||||
PETRA.TradeManager = function(Config)
|
||||
export function TradeManager(config)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.Config = config;
|
||||
this.tradeRoute = undefined;
|
||||
this.potentialTradeRoute = undefined;
|
||||
this.routeProspection = false;
|
||||
this.targetNumTraders = this.Config.Economy.targetNumTraders;
|
||||
this.warnedAllies = {};
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.TradeManager.prototype.init = function(gameState)
|
||||
TradeManager.prototype.init = function(gameState)
|
||||
{
|
||||
this.traders = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "role", PETRA.Worker.ROLE_TRADER));
|
||||
this.traders = gameState.getOwnUnits().filter(
|
||||
API3.Filters.byMetadata(PlayerID, "role", Worker.ROLE_TRADER));
|
||||
this.traders.registerUpdates();
|
||||
this.minimalGain = gameState.ai.HQ.navalMap ? 3 : 5;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.hasTradeRoute = function()
|
||||
TradeManager.prototype.hasTradeRoute = function()
|
||||
{
|
||||
return this.tradeRoute !== undefined;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.assignTrader = function(ent)
|
||||
TradeManager.prototype.assignTrader = function(ent)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_TRADER);
|
||||
ent.setMetadata(PlayerID, "role", Worker.ROLE_TRADER);
|
||||
this.traders.updateEnt(ent);
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
{
|
||||
if (!this.hasTradeRoute() || queues.trader.hasQueuedUnits())
|
||||
return;
|
||||
@@ -41,7 +50,7 @@ PETRA.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
gameState.getOwnTrainingFacilities().forEach(function(ent) {
|
||||
for (const item of ent.trainingQueue())
|
||||
{
|
||||
if (!item.metadata || !item.metadata.role || item.metadata.role !== PETRA.Worker.ROLE_TRADER)
|
||||
if (!item.metadata || !item.metadata.role || item.metadata.role !== Worker.ROLE_TRADER)
|
||||
continue;
|
||||
numTraders += item.count;
|
||||
if (item.metadata.sea !== undefined)
|
||||
@@ -56,7 +65,7 @@ PETRA.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
return;
|
||||
|
||||
let template;
|
||||
const metadata = { "role": PETRA.Worker.ROLE_TRADER };
|
||||
const metadata = { "role": Worker.ROLE_TRADER };
|
||||
if (this.tradeRoute.sea)
|
||||
{
|
||||
// if we have some merchand ships assigned to transport, try first to reassign them
|
||||
@@ -67,7 +76,7 @@ PETRA.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
gameState.ai.HQ.navalManager.seaTransportShips[this.tradeRoute.sea].forEach(function(ship) {
|
||||
if (already || !ship.hasClass("Trader"))
|
||||
return;
|
||||
if (ship.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_SWITCH_TO_TRADER)
|
||||
if (ship.getMetadata(PlayerID, "role") === Worker.ROLE_SWITCH_TO_TRADER)
|
||||
{
|
||||
already = true;
|
||||
return;
|
||||
@@ -79,9 +88,9 @@ PETRA.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
if (shipToSwitch)
|
||||
{
|
||||
if (shipToSwitch.getMetadata(PlayerID, "transporter") === undefined)
|
||||
shipToSwitch.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_TRADER);
|
||||
shipToSwitch.setMetadata(PlayerID, "role", Worker.ROLE_TRADER);
|
||||
else
|
||||
shipToSwitch.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_SWITCH_TO_TRADER);
|
||||
shipToSwitch.setMetadata(PlayerID, "role", Worker.ROLE_SWITCH_TO_TRADER);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,15 +113,17 @@ PETRA.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
|
||||
gameState.getPlayerCiv() + " but no template found.");
|
||||
return;
|
||||
}
|
||||
queues.trader.addPlan(new PETRA.TrainingPlan(gameState, template, metadata, 1, 1));
|
||||
queues.trader.addPlan(new TrainingPlan(gameState, template, metadata, 1, 1));
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.updateTrader = function(gameState, ent)
|
||||
TradeManager.prototype.updateTrader = function(gameState, ent)
|
||||
{
|
||||
if (ent.hasClass("Ship") && gameState.ai.playedTurn % 5 == 0 &&
|
||||
!ent.unitAIState().startsWith("INDIVIDUAL.COLLECTTREASURE") &&
|
||||
PETRA.gatherTreasure(gameState, ent, true))
|
||||
!ent.unitAIState().startsWith("INDIVIDUAL.COLLECTTREASURE") &&
|
||||
gatherTreasure(gameState, ent, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.hasTradeRoute() || !ent.isIdle() || !ent.position())
|
||||
return;
|
||||
@@ -122,7 +133,7 @@ PETRA.TradeManager.prototype.updateTrader = function(gameState, ent)
|
||||
// TODO if the trader is idle and has workOrders, restore them to avoid losing the current gain
|
||||
|
||||
Engine.ProfileStart("Trade Manager");
|
||||
const access = ent.hasClass("Ship") ? PETRA.getSeaAccess(gameState, ent) : PETRA.getLandAccess(gameState, ent);
|
||||
const access = ent.hasClass("Ship") ? getSeaAccess(gameState, ent) : getLandAccess(gameState, ent);
|
||||
const route = this.checkRoutes(gameState, access);
|
||||
if (!route)
|
||||
{
|
||||
@@ -155,7 +166,7 @@ PETRA.TradeManager.prototype.updateTrader = function(gameState, ent)
|
||||
Engine.ProfileStop();
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.setTradingGoods = function(gameState)
|
||||
TradeManager.prototype.setTradingGoods = function(gameState)
|
||||
{
|
||||
const resTradeCodes = Resources.GetTradableCodes();
|
||||
if (!resTradeCodes.length)
|
||||
@@ -212,7 +223,7 @@ PETRA.TradeManager.prototype.setTradingGoods = function(gameState)
|
||||
* Try to barter unneeded resources for needed resources.
|
||||
* only once per turn because the info is not updated within a turn
|
||||
*/
|
||||
PETRA.TradeManager.prototype.performBarter = function(gameState)
|
||||
TradeManager.prototype.performBarter = function(gameState)
|
||||
{
|
||||
const barterers = gameState.getOwnEntitiesByClass("Barter", true).filter(API3.Filters.isBuilt()).toEntityArray();
|
||||
if (barterers.length == 0)
|
||||
@@ -329,7 +340,7 @@ PETRA.TradeManager.prototype.performBarter = function(gameState)
|
||||
return false;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.checkEvents = function(gameState, events)
|
||||
TradeManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
// check if one market from a traderoute is renamed, change the route accordingly
|
||||
for (const evt of events.EntityRenamed)
|
||||
@@ -398,7 +409,7 @@ PETRA.TradeManager.prototype.checkEvents = function(gameState, events)
|
||||
return false;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.activateProspection = function(gameState)
|
||||
TradeManager.prototype.activateProspection = function(gameState)
|
||||
{
|
||||
this.routeProspection = true;
|
||||
gameState.ai.HQ.buildManager.setBuildable(gameState.applyCiv("structures/{civ}/market"));
|
||||
@@ -409,7 +420,7 @@ PETRA.TradeManager.prototype.activateProspection = function(gameState)
|
||||
* fills the best trade route in this.tradeRoute and the best potential route in this.potentialTradeRoute
|
||||
* If an index is given, it returns the best route with this index or the best land route if index is a land index
|
||||
*/
|
||||
PETRA.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
|
||||
TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
|
||||
{
|
||||
// If we cannot trade, do not bother checking routes.
|
||||
if (!Resources.GetTradableCodes().length)
|
||||
@@ -443,21 +454,21 @@ PETRA.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
|
||||
{
|
||||
if (!m1.position())
|
||||
continue;
|
||||
const access1 = PETRA.getLandAccess(gameState, m1);
|
||||
const sea1 = m1.hasClass("Naval") ? PETRA.getSeaAccess(gameState, m1) : undefined;
|
||||
const access1 = getLandAccess(gameState, m1);
|
||||
const sea1 = m1.hasClass("Naval") ? getSeaAccess(gameState, m1) : undefined;
|
||||
for (const m2 of market2.values())
|
||||
{
|
||||
if (onlyOurs && m1.id() >= m2.id())
|
||||
continue;
|
||||
if (!m2.position())
|
||||
continue;
|
||||
const access2 = PETRA.getLandAccess(gameState, m2);
|
||||
const sea2 = m2.hasClass("Naval") ? PETRA.getSeaAccess(gameState, m2) : undefined;
|
||||
const access2 = getLandAccess(gameState, m2);
|
||||
const sea2 = m2.hasClass("Naval") ? getSeaAccess(gameState, m2) : undefined;
|
||||
const land = access1 == access2 ? access1 : undefined;
|
||||
const sea = sea1 && sea1 == sea2 ? sea1 : undefined;
|
||||
if (!land && !sea)
|
||||
continue;
|
||||
if (land && PETRA.isLineInsideEnemyTerritory(gameState, m1.position(), m2.position()))
|
||||
if (land && isLineInsideEnemyTerritory(gameState, m1.position(), m2.position()))
|
||||
continue;
|
||||
let gainMultiplier;
|
||||
if (land && traderTemplatesGains.landGainMultiplier)
|
||||
@@ -531,7 +542,7 @@ PETRA.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
|
||||
owner = this.tradeRoute.target.owner();
|
||||
if (owner != PlayerID && !this.warnedAllies[owner])
|
||||
{ // Warn an ally that we have a trade route with him
|
||||
PETRA.chatNewTradeRoute(gameState, owner);
|
||||
chatNewTradeRoute(gameState, owner);
|
||||
this.warnedAllies[owner] = true;
|
||||
}
|
||||
}
|
||||
@@ -548,7 +559,7 @@ PETRA.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
|
||||
};
|
||||
|
||||
/** Called when a market was built or destroyed, and checks if trader orders should be changed */
|
||||
PETRA.TradeManager.prototype.checkTrader = function(gameState, ent)
|
||||
TradeManager.prototype.checkTrader = function(gameState, ent)
|
||||
{
|
||||
const presentRoute = ent.getMetadata(PlayerID, "route");
|
||||
if (!presentRoute)
|
||||
@@ -561,7 +572,7 @@ PETRA.TradeManager.prototype.checkTrader = function(gameState, ent)
|
||||
return;
|
||||
}
|
||||
|
||||
const access = ent.hasClass("Ship") ? PETRA.getSeaAccess(gameState, ent) : PETRA.getLandAccess(gameState, ent);
|
||||
const access = ent.hasClass("Ship") ? getSeaAccess(gameState, ent) : getLandAccess(gameState, ent);
|
||||
const possibleRoute = this.checkRoutes(gameState, access);
|
||||
// Warning: presentRoute is from metadata, so contains entity ids
|
||||
if (!possibleRoute ||
|
||||
@@ -572,7 +583,7 @@ PETRA.TradeManager.prototype.checkTrader = function(gameState, ent)
|
||||
ent.setMetadata(PlayerID, "route", undefined);
|
||||
if (!possibleRoute && !ent.hasClass("Ship"))
|
||||
{
|
||||
const closestBase = PETRA.getBestBase(gameState, ent, true);
|
||||
const closestBase = getBestBase(gameState, ent, true);
|
||||
if (closestBase.accessIndex == access)
|
||||
{
|
||||
const closestBasePos = closestBase.anchor.position();
|
||||
@@ -584,7 +595,7 @@ PETRA.TradeManager.prototype.checkTrader = function(gameState, ent)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.prospectForNewMarket = function(gameState, queues)
|
||||
TradeManager.prototype.prospectForNewMarket = function(gameState, queues)
|
||||
{
|
||||
if (queues.economicBuilding.hasQueuedUnitsWithClass("Trade") || queues.dock.hasQueuedUnitsWithClass("Trade"))
|
||||
return;
|
||||
@@ -622,13 +633,13 @@ PETRA.TradeManager.prototype.prospectForNewMarket = function(gameState, queues)
|
||||
|
||||
if (!this.tradeRoute)
|
||||
gameState.ai.queueManager.changePriority("economicBuilding", 2 * this.Config.priorities.economicBuilding);
|
||||
const plan = new PETRA.ConstructionPlan(gameState, "structures/{civ}/market");
|
||||
const plan = new ConstructionPlan(gameState, "structures/{civ}/market");
|
||||
if (!this.tradeRoute)
|
||||
plan.queueToReset = "economicBuilding";
|
||||
queues.economicBuilding.addPlan(plan);
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.isNewMarketWorth = function(expectedGain)
|
||||
TradeManager.prototype.isNewMarketWorth = function(expectedGain)
|
||||
{
|
||||
if (!Resources.GetTradableCodes().length)
|
||||
return false;
|
||||
@@ -640,12 +651,12 @@ PETRA.TradeManager.prototype.isNewMarketWorth = function(expectedGain)
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.update = function(gameState, events, queues)
|
||||
TradeManager.prototype.update = function(gameState, events, queues)
|
||||
{
|
||||
if (gameState.ai.HQ.canBarter && Resources.GetBarterableCodes().length)
|
||||
this.performBarter(gameState);
|
||||
|
||||
if (this.Config.difficulty <= PETRA.DIFFICULTY_VERY_EASY)
|
||||
if (this.Config.difficulty <= DIFFICULTY_VERY_EASY)
|
||||
return;
|
||||
|
||||
if (this.checkEvents(gameState, events)) // true if one market was built or destroyed
|
||||
@@ -669,7 +680,7 @@ PETRA.TradeManager.prototype.update = function(gameState, events, queues)
|
||||
this.prospectForNewMarket(gameState, queues);
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.routeEntToId = function(route)
|
||||
TradeManager.prototype.routeEntToId = function(route)
|
||||
{
|
||||
if (!route)
|
||||
return undefined;
|
||||
@@ -689,7 +700,7 @@ PETRA.TradeManager.prototype.routeEntToId = function(route)
|
||||
return ret;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.routeIdToEnt = function(gameState, route)
|
||||
TradeManager.prototype.routeIdToEnt = function(gameState, route)
|
||||
{
|
||||
if (!route)
|
||||
return undefined;
|
||||
@@ -709,7 +720,7 @@ PETRA.TradeManager.prototype.routeIdToEnt = function(gameState, route)
|
||||
return ret;
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.Serialize = function()
|
||||
TradeManager.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"tradeRoute": this.routeEntToId(this.tradeRoute),
|
||||
@@ -720,7 +731,7 @@ PETRA.TradeManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.TradeManager.prototype.Deserialize = function(gameState, data)
|
||||
TradeManager.prototype.Deserialize = function(gameState, data)
|
||||
{
|
||||
for (const key in data)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { getLandAccess } from "simulation/ai/petra/entityExtend.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Describes a transport plan
|
||||
* Constructor assign units (units is an ID array), a destination (position).
|
||||
@@ -20,7 +23,7 @@
|
||||
* transporter = this.ID
|
||||
*/
|
||||
|
||||
PETRA.TransportPlan = function(gameState, units, startIndex, endIndex, endPos, ship)
|
||||
export function TransportPlan(gameState, units, startIndex, endIndex, endPos, ship)
|
||||
{
|
||||
this.ID = gameState.ai.uniqueIDs.transports++;
|
||||
this.debug = gameState.ai.Config.debug;
|
||||
@@ -67,23 +70,23 @@ PETRA.TransportPlan = function(gameState, units, startIndex, endIndex, endPos, s
|
||||
API3.warn("Starting a new transport plan with ID " + this.ID +
|
||||
" to index " + endIndex + " with units length " + units.length);
|
||||
|
||||
this.state = PETRA.TransportPlan.BOARDING;
|
||||
this.state = TransportPlan.BOARDING;
|
||||
this.boardingPos = {};
|
||||
this.needTransportShips = ship === undefined;
|
||||
this.nTry = {};
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to board units onto our ships.
|
||||
*/
|
||||
PETRA.TransportPlan.BOARDING = "boarding";
|
||||
TransportPlan.BOARDING = "boarding";
|
||||
/**
|
||||
* We're moving ships and eventually unload units.
|
||||
*/
|
||||
PETRA.TransportPlan.SAILING = "sailing";
|
||||
TransportPlan.SAILING = "sailing";
|
||||
|
||||
PETRA.TransportPlan.prototype.init = function(gameState)
|
||||
TransportPlan.prototype.init = function(gameState)
|
||||
{
|
||||
this.units = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "transport", this.ID));
|
||||
this.ships = gameState.ai.HQ.navalManager.ships.filter(API3.Filters.byMetadata(PlayerID, "transporter", this.ID));
|
||||
@@ -97,7 +100,7 @@ PETRA.TransportPlan.prototype.init = function(gameState)
|
||||
};
|
||||
|
||||
/** count available slots */
|
||||
PETRA.TransportPlan.prototype.countFreeSlots = function()
|
||||
TransportPlan.prototype.countFreeSlots = function()
|
||||
{
|
||||
let slots = 0;
|
||||
for (const ship of this.transportShips.values())
|
||||
@@ -105,7 +108,7 @@ PETRA.TransportPlan.prototype.countFreeSlots = function()
|
||||
return slots;
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.countFreeSlotsOnShip = function(ship)
|
||||
TransportPlan.prototype.countFreeSlotsOnShip = function(ship)
|
||||
{
|
||||
if (ship.hitpoints() < ship.garrisonEjectHealth() * ship.maxHitpoints())
|
||||
return 0;
|
||||
@@ -114,7 +117,7 @@ PETRA.TransportPlan.prototype.countFreeSlotsOnShip = function(ship)
|
||||
return Math.max(ship.garrisonMax() - occupied, 0);
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.assignUnitToShip = function(gameState, ent)
|
||||
TransportPlan.prototype.assignUnitToShip = function(gameState, ent)
|
||||
{
|
||||
if (this.needTransportShips)
|
||||
return;
|
||||
@@ -126,7 +129,7 @@ PETRA.TransportPlan.prototype.assignUnitToShip = function(gameState, ent)
|
||||
ent.setMetadata(PlayerID, "onBoard", ship.id());
|
||||
if (this.debug > 1)
|
||||
{
|
||||
if (ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_ATTACK)
|
||||
if (ent.getMetadata(PlayerID, "role") === Worker.ROLE_ATTACK)
|
||||
Engine.PostCommand(PlayerID, { "type": "set-shading-color", "entities": [ent.id()], "rgb": [2, 0, 0] });
|
||||
else
|
||||
Engine.PostCommand(PlayerID, { "type": "set-shading-color", "entities": [ent.id()], "rgb": [0, 2, 0] });
|
||||
@@ -146,7 +149,7 @@ PETRA.TransportPlan.prototype.assignUnitToShip = function(gameState, ent)
|
||||
this.needSplit.push(ent);
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.assignShip = function(gameState)
|
||||
TransportPlan.prototype.assignShip = function(gameState)
|
||||
{
|
||||
let pos;
|
||||
// choose a unit of this plan not yet assigned to a ship
|
||||
@@ -186,7 +189,7 @@ PETRA.TransportPlan.prototype.assignShip = function(gameState)
|
||||
};
|
||||
|
||||
/** add a unit to this plan */
|
||||
PETRA.TransportPlan.prototype.addUnit = function(unit, endPos)
|
||||
TransportPlan.prototype.addUnit = function(unit, endPos)
|
||||
{
|
||||
unit.setMetadata(PlayerID, "transport", this.ID);
|
||||
unit.setMetadata(PlayerID, "endPos", endPos);
|
||||
@@ -194,7 +197,7 @@ PETRA.TransportPlan.prototype.addUnit = function(unit, endPos)
|
||||
};
|
||||
|
||||
/** remove a unit from this plan, if not yet on board */
|
||||
PETRA.TransportPlan.prototype.removeUnit = function(gameState, unit)
|
||||
TransportPlan.prototype.removeUnit = function(gameState, unit)
|
||||
{
|
||||
const shipId = unit.getMetadata(PlayerID, "onBoard");
|
||||
if (shipId == "onBoard")
|
||||
@@ -218,7 +221,7 @@ PETRA.TransportPlan.prototype.removeUnit = function(gameState, unit)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.releaseShip = function(ship)
|
||||
TransportPlan.prototype.releaseShip = function(ship)
|
||||
{
|
||||
if (ship.getMetadata(PlayerID, "transporter") != this.ID)
|
||||
{
|
||||
@@ -232,11 +235,11 @@ PETRA.TransportPlan.prototype.releaseShip = function(ship)
|
||||
ship.setStance(defaultStance);
|
||||
|
||||
ship.setMetadata(PlayerID, "transporter", undefined);
|
||||
if (ship.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_SWITCH_TO_TRADER)
|
||||
ship.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_TRADER);
|
||||
if (ship.getMetadata(PlayerID, "role") === Worker.ROLE_SWITCH_TO_TRADER)
|
||||
ship.setMetadata(PlayerID, "role", Worker.ROLE_TRADER);
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.releaseAll = function()
|
||||
TransportPlan.prototype.releaseAll = function()
|
||||
{
|
||||
for (const ship of this.ships.values())
|
||||
this.releaseShip(ship);
|
||||
@@ -256,7 +259,7 @@ PETRA.TransportPlan.prototype.releaseAll = function()
|
||||
};
|
||||
|
||||
/** TODO not currently used ... to be fixed */
|
||||
PETRA.TransportPlan.prototype.cancelTransport = function(gameState)
|
||||
TransportPlan.prototype.cancelTransport = function(gameState)
|
||||
{
|
||||
const ent = this.units.toEntityArray()[0];
|
||||
let base = gameState.ai.HQ.getBaseByID(ent.getMetadata(PlayerID, "base"));
|
||||
@@ -284,17 +287,17 @@ PETRA.TransportPlan.prototype.cancelTransport = function(gameState)
|
||||
/**
|
||||
* Try to move on and then clear the plan.
|
||||
*/
|
||||
PETRA.TransportPlan.prototype.update = function(gameState)
|
||||
TransportPlan.prototype.update = function(gameState)
|
||||
{
|
||||
if (this.state === PETRA.TransportPlan.BOARDING)
|
||||
if (this.state === TransportPlan.BOARDING)
|
||||
this.onBoarding(gameState);
|
||||
else if (this.state === PETRA.TransportPlan.SAILING)
|
||||
else if (this.state === TransportPlan.SAILING)
|
||||
this.onSailing(gameState);
|
||||
|
||||
return this.units.length;
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.onBoarding = function(gameState)
|
||||
TransportPlan.prototype.onBoarding = function(gameState)
|
||||
{
|
||||
let ready = true;
|
||||
const time = gameState.ai.elapsedTime;
|
||||
@@ -427,14 +430,14 @@ PETRA.TransportPlan.prototype.onBoarding = function(gameState)
|
||||
this.boardingPos[ship.id()] = this.getBoardingPos(gameState, ship, this.endIndex, this.sea, this.endPos, true);
|
||||
ship.move(this.boardingPos[ship.id()][0], this.boardingPos[ship.id()][1]);
|
||||
}
|
||||
this.state = PETRA.TransportPlan.SAILING;
|
||||
this.state = TransportPlan.SAILING;
|
||||
this.nTry = {};
|
||||
this.unloaded = [];
|
||||
this.recovered = [];
|
||||
};
|
||||
|
||||
/** tell if a unit is garrisoned in one of the ships of this plan, and update its metadata if yes */
|
||||
PETRA.TransportPlan.prototype.isOnBoard = function(ent)
|
||||
TransportPlan.prototype.isOnBoard = function(ent)
|
||||
{
|
||||
for (const ship of this.transportShips.values())
|
||||
{
|
||||
@@ -447,7 +450,8 @@ PETRA.TransportPlan.prototype.isOnBoard = function(ent)
|
||||
};
|
||||
|
||||
/** when avoidEnnemy is true, we try to not board/unboard in ennemy territory */
|
||||
PETRA.TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex, seaIndex, destination, avoidEnnemy)
|
||||
TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex, seaIndex, destination,
|
||||
avoidEnnemy)
|
||||
{
|
||||
if (!gameState.ai.HQ.navalManager.landingZones[landIndex])
|
||||
{
|
||||
@@ -510,7 +514,7 @@ PETRA.TransportPlan.prototype.getBoardingPos = function(gameState, ship, landInd
|
||||
return posmin;
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.onSailing = function(gameState)
|
||||
TransportPlan.prototype.onSailing = function(gameState)
|
||||
{
|
||||
// Check that the units recovered on the previous turn have been reloaded
|
||||
for (const recov of this.recovered)
|
||||
@@ -528,7 +532,7 @@ PETRA.TransportPlan.prototype.onSailing = function(gameState)
|
||||
if (this.debug > 1)
|
||||
API3.warn(">>> transport " + this.ID + " reloading failed ... <<<");
|
||||
// destroy the unit if inaccessible otherwise leave it there
|
||||
const index = PETRA.getLandAccess(gameState, ent);
|
||||
const index = getLandAccess(gameState, ent);
|
||||
if (gameState.ai.HQ.landRegions[index])
|
||||
{
|
||||
if (this.debug > 1)
|
||||
@@ -576,7 +580,7 @@ PETRA.TransportPlan.prototype.onSailing = function(gameState)
|
||||
ent.destroy();
|
||||
}
|
||||
}
|
||||
else if (PETRA.getLandAccess(gameState, ent) != this.endIndex)
|
||||
else if (getLandAccess(gameState, ent) != this.endIndex)
|
||||
{
|
||||
// unit unloaded on a wrong region - try to regarrison it and move a bit the ship
|
||||
if (this.debug > 1)
|
||||
@@ -680,7 +684,7 @@ PETRA.TransportPlan.prototype.onSailing = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.resetUnit = function(gameState, ent)
|
||||
TransportPlan.prototype.resetUnit = function(gameState, ent)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "transport", undefined);
|
||||
ent.setMetadata(PlayerID, "onBoard", undefined);
|
||||
@@ -700,7 +704,7 @@ PETRA.TransportPlan.prototype.resetUnit = function(gameState, ent)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.Serialize = function()
|
||||
TransportPlan.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"ID": this.ID,
|
||||
@@ -719,7 +723,7 @@ PETRA.TransportPlan.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.TransportPlan.prototype.Deserialize = function(data)
|
||||
TransportPlan.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { AttackPlan } from "simulation/ai/petra/attackPlan.js";
|
||||
import { getAttackBonus, getBestBase, getLandAccess, returnResources } from
|
||||
"simulation/ai/petra/entityExtend.js";
|
||||
import { TrainingPlan } from "simulation/ai/petra/queueplanTraining.js";
|
||||
import { Worker } from "simulation/ai/petra/worker.js";
|
||||
|
||||
/**
|
||||
* Handle events that are important to specific victory conditions:
|
||||
* in capture_the_relic, capture gaia relics and train military guards.
|
||||
@@ -5,7 +11,7 @@
|
||||
* in wonder, train military guards.
|
||||
*/
|
||||
|
||||
PETRA.VictoryManager = function(Config)
|
||||
export function VictoryManager(Config)
|
||||
{
|
||||
this.Config = Config;
|
||||
this.criticalEnts = new Map();
|
||||
@@ -16,12 +22,12 @@ PETRA.VictoryManager = function(Config)
|
||||
this.tryCaptureGaiaRelicLapseTime = -1;
|
||||
// Gaia relics which we are targeting currently and have not captured yet
|
||||
this.targetedGaiaRelics = new Map();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the ids of any inital victory-critical entities.
|
||||
*/
|
||||
PETRA.VictoryManager.prototype.init = function(gameState)
|
||||
VictoryManager.prototype.init = function(gameState)
|
||||
{
|
||||
if (gameState.getVictoryConditions().has("wonder"))
|
||||
{
|
||||
@@ -60,7 +66,7 @@ PETRA.VictoryManager.prototype.init = function(gameState)
|
||||
* If it is less than 40%, try to garrison in the closest possible structure
|
||||
* If the hero cannot garrison, retreat it to the closest base
|
||||
*/
|
||||
PETRA.VictoryManager.prototype.checkEvents = function(gameState, events)
|
||||
VictoryManager.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
if (gameState.getVictoryConditions().has("wonder"))
|
||||
{
|
||||
@@ -78,7 +84,7 @@ PETRA.VictoryManager.prototype.checkEvents = function(gameState, events)
|
||||
for (const worker of builders.values())
|
||||
{
|
||||
worker.setMetadata(PlayerID, "base", base.ID);
|
||||
worker.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
worker.setMetadata(PlayerID, "target-foundation", ent.id());
|
||||
}
|
||||
}
|
||||
@@ -141,7 +147,7 @@ PETRA.VictoryManager.prototype.checkEvents = function(gameState, events)
|
||||
for (const entId of evt.entities)
|
||||
{
|
||||
const ent = gameState.getEntityById(entId);
|
||||
if (ent && ent.isOwn(PlayerID) && ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_CRITICAL_ENT_HEALER)
|
||||
if (ent && ent.isOwn(PlayerID) && ent.getMetadata(PlayerID, "role") === Worker.ROLE_CRITICAL_ENT_HEALER)
|
||||
this.assignGuardToCriticalEnt(gameState, ent);
|
||||
}
|
||||
|
||||
@@ -253,8 +259,8 @@ PETRA.VictoryManager.prototype.checkEvents = function(gameState, events)
|
||||
continue;
|
||||
|
||||
// If this ent travelled to a criticalEnt's accessValue, try again to assign as a guard
|
||||
if ((ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_CRITICAL_ENT_HEALER ||
|
||||
ent.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_CRITICAL_ENT_GUARD) && !this.guardEnts.get(evt.entity))
|
||||
if ((ent.getMetadata(PlayerID, "role") === Worker.ROLE_CRITICAL_ENT_HEALER ||
|
||||
ent.getMetadata(PlayerID, "role") === Worker.ROLE_CRITICAL_ENT_GUARD) && !this.guardEnts.get(evt.entity))
|
||||
{
|
||||
this.assignGuardToCriticalEnt(gameState, ent, ent.getMetadata(PlayerID, "guardedEnt"));
|
||||
continue;
|
||||
@@ -304,7 +310,7 @@ PETRA.VictoryManager.prototype.checkEvents = function(gameState, events)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.removeCriticalEnt = function(gameState, criticalEntId)
|
||||
VictoryManager.prototype.removeCriticalEnt = function(gameState, criticalEntId)
|
||||
{
|
||||
for (const [guardId, role] of this.criticalEnts.get(criticalEntId).guards)
|
||||
{
|
||||
@@ -330,7 +336,7 @@ PETRA.VictoryManager.prototype.removeCriticalEnt = function(gameState, criticalE
|
||||
/**
|
||||
* Train more healers to be later affected to critical entities if needed
|
||||
*/
|
||||
PETRA.VictoryManager.prototype.manageCriticalEntHealers = function(gameState, queues)
|
||||
VictoryManager.prototype.manageCriticalEntHealers = function(gameState, queues)
|
||||
{
|
||||
if (gameState.ai.HQ.saveResources || queues.healer.hasQueuedUnits() ||
|
||||
!gameState.getOwnEntitiesByClass("Temple", true).hasEntities() ||
|
||||
@@ -342,7 +348,8 @@ PETRA.VictoryManager.prototype.manageCriticalEntHealers = function(gameState, qu
|
||||
if (data.healersAssigned === undefined || data.healersAssigned >= this.healersPerCriticalEnt)
|
||||
continue;
|
||||
const template = gameState.applyCiv("units/{civ}/support_healer_b");
|
||||
queues.healer.addPlan(new PETRA.TrainingPlan(gameState, template, { "role": PETRA.Worker.ROLE_CRITICAL_ENT_HEALER, "base": 0 }, 1, 1));
|
||||
queues.healer.addPlan(new TrainingPlan(gameState, template,
|
||||
{ "role": Worker.ROLE_CRITICAL_ENT_HEALER, "base": 0 }, 1, 1));
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -352,9 +359,9 @@ PETRA.VictoryManager.prototype.manageCriticalEntHealers = function(gameState, qu
|
||||
* If we have too low a population and require units for other needs, remove guards so they can be reassigned.
|
||||
* TODO: Swap citizen soldier guards with champions if they become available.
|
||||
*/
|
||||
PETRA.VictoryManager.prototype.manageCriticalEntGuards = function(gameState)
|
||||
VictoryManager.prototype.manageCriticalEntGuards = function(gameState)
|
||||
{
|
||||
let numWorkers = gameState.getOwnEntitiesByRole(PETRA.Worker.ROLE_WORKER, true).length;
|
||||
let numWorkers = gameState.getOwnEntitiesByRole(Worker.ROLE_WORKER, true).length;
|
||||
if (numWorkers < 20)
|
||||
{
|
||||
for (const data of this.criticalEnts.values())
|
||||
@@ -363,7 +370,7 @@ PETRA.VictoryManager.prototype.manageCriticalEntGuards = function(gameState)
|
||||
{
|
||||
const guardEnt = gameState.getEntityById(guardId);
|
||||
if (!guardEnt || !guardEnt.hasClass("CitizenSoldier") ||
|
||||
guardEnt.getMetadata(PlayerID, "role") !== PETRA.Worker.ROLE_CRITICAL_ENT_GUARD)
|
||||
guardEnt.getMetadata(PlayerID, "role") !== Worker.ROLE_CRITICAL_ENT_GUARD)
|
||||
continue;
|
||||
|
||||
guardEnt.removeGuard();
|
||||
@@ -439,23 +446,23 @@ PETRA.VictoryManager.prototype.manageCriticalEntGuards = function(gameState)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.tryAssignMilitaryGuard = function(gameState, guardEnt, criticalEnt, checkForSameAccess)
|
||||
VictoryManager.prototype.tryAssignMilitaryGuard = function(gameState, guardEnt, criticalEnt, checkForSameAccess)
|
||||
{
|
||||
if (guardEnt.getMetadata(PlayerID, "plan") !== undefined ||
|
||||
guardEnt.getMetadata(PlayerID, "transport") !== undefined || this.criticalEnts.has(guardEnt.id()) ||
|
||||
checkForSameAccess && (!guardEnt.position() || !criticalEnt.position() ||
|
||||
PETRA.getLandAccess(gameState, criticalEnt) != PETRA.getLandAccess(gameState, guardEnt)))
|
||||
getLandAccess(gameState, criticalEnt) != getLandAccess(gameState, guardEnt)))
|
||||
return false;
|
||||
|
||||
if (!this.assignGuardToCriticalEnt(gameState, guardEnt, criticalEnt.id()))
|
||||
return false;
|
||||
|
||||
guardEnt.setMetadata(PlayerID, "plan", -2);
|
||||
guardEnt.setMetadata(PlayerID, "role", PETRA.Worker.ROLE_CRITICAL_ENT_GUARD);
|
||||
guardEnt.setMetadata(PlayerID, "role", Worker.ROLE_CRITICAL_ENT_GUARD);
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.pickCriticalEntRetreatLocation = function(gameState, criticalEnt, emergency)
|
||||
VictoryManager.prototype.pickCriticalEntRetreatLocation = function(gameState, criticalEnt, emergency)
|
||||
{
|
||||
gameState.ai.HQ.defenseManager.garrisonAttackedUnit(gameState, criticalEnt, emergency);
|
||||
const plan = criticalEnt.getMetadata(PlayerID, "plan");
|
||||
@@ -469,8 +476,8 @@ PETRA.VictoryManager.prototype.pickCriticalEntRetreatLocation = function(gameSta
|
||||
// Couldn't find a place to garrison, so the ent will flee from attacks
|
||||
if (!criticalEnt.hasClass("Relic") && criticalEnt.getStance() != "passive")
|
||||
criticalEnt.setStance("passive");
|
||||
const accessIndex = PETRA.getLandAccess(gameState, criticalEnt);
|
||||
const bestBase = PETRA.getBestBase(gameState, criticalEnt, true);
|
||||
const accessIndex = getLandAccess(gameState, criticalEnt);
|
||||
const bestBase = getBestBase(gameState, criticalEnt, true);
|
||||
if (bestBase.accessIndex == accessIndex)
|
||||
{
|
||||
const bestBasePos = bestBase.anchor.position();
|
||||
@@ -487,7 +494,7 @@ PETRA.VictoryManager.prototype.pickCriticalEntRetreatLocation = function(gameSta
|
||||
* which will be used once its transport has finished.
|
||||
* Return false if the guardEnt is not a valid guard unit (i.e. cannot guard or is being transported).
|
||||
*/
|
||||
PETRA.VictoryManager.prototype.assignGuardToCriticalEnt = function(gameState, guardEnt, criticalEntId)
|
||||
VictoryManager.prototype.assignGuardToCriticalEnt = function(gameState, guardEnt, criticalEntId)
|
||||
{
|
||||
if (guardEnt.getMetadata(PlayerID, "transport") !== undefined || !guardEnt.canGuard())
|
||||
return false;
|
||||
@@ -542,13 +549,13 @@ PETRA.VictoryManager.prototype.assignGuardToCriticalEnt = function(gameState, gu
|
||||
if (guardEnt.getMetadata(PlayerID, "guardedEnt") != criticalEntId)
|
||||
guardEnt.setMetadata(PlayerID, "guardedEnt", criticalEntId);
|
||||
|
||||
const guardEntAccess = PETRA.getLandAccess(gameState, guardEnt);
|
||||
const criticalEntAccess = PETRA.getLandAccess(gameState, criticalEnt);
|
||||
const guardEntAccess = getLandAccess(gameState, guardEnt);
|
||||
const criticalEntAccess = getLandAccess(gameState, criticalEnt);
|
||||
if (guardEntAccess == criticalEntAccess)
|
||||
{
|
||||
const queued = PETRA.returnResources(gameState, guardEnt);
|
||||
const queued = returnResources(gameState, guardEnt);
|
||||
guardEnt.guard(criticalEnt, queued);
|
||||
const guardRole = guardEnt.getMetadata(PlayerID, "role") === PETRA.Worker.ROLE_CRITICAL_ENT_HEALER ? "healer" : "guard";
|
||||
const guardRole = guardEnt.getMetadata(PlayerID, "role") === Worker.ROLE_CRITICAL_ENT_HEALER ? "healer" : "guard";
|
||||
this.criticalEnts.get(criticalEntId).guards.set(guardEnt.id(), guardRole);
|
||||
|
||||
// Switch this guard ent to the criticalEnt's base
|
||||
@@ -562,14 +569,14 @@ PETRA.VictoryManager.prototype.assignGuardToCriticalEnt = function(gameState, gu
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.resetCaptureGaiaRelic = function(gameState)
|
||||
VictoryManager.prototype.resetCaptureGaiaRelic = function(gameState)
|
||||
{
|
||||
// Do not capture gaia relics too frequently as the ai has access to the entire map
|
||||
this.tryCaptureGaiaRelicLapseTime = gameState.ai.elapsedTime + 240 - 30 * (this.Config.difficulty - 3);
|
||||
this.tryCaptureGaiaRelic = false;
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.update = function(gameState, events, queues)
|
||||
VictoryManager.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)
|
||||
@@ -649,7 +656,7 @@ PETRA.VictoryManager.prototype.update = function(gameState, events, queues)
|
||||
/**
|
||||
* Send an expedition to capture a gaia relic, or reinforce an existing one.
|
||||
*/
|
||||
PETRA.VictoryManager.prototype.captureGaiaRelic = function(gameState, relic)
|
||||
VictoryManager.prototype.captureGaiaRelic = function(gameState, relic)
|
||||
{
|
||||
let capture = -relic.defaultRegenRate();
|
||||
const sumCapturePoints = relic.capturePoints().reduce((a, b) => a + b);
|
||||
@@ -660,13 +667,13 @@ PETRA.VictoryManager.prototype.captureGaiaRelic = function(gameState, relic)
|
||||
if (!attack)
|
||||
continue;
|
||||
for (const ent of attack.unitCollection.values())
|
||||
capture += ent.captureStrength() * PETRA.getAttackBonus(ent, relic, "Capture");
|
||||
capture += ent.captureStrength() * getAttackBonus(ent, relic, "Capture");
|
||||
}
|
||||
// No need to make a new attack if already enough units
|
||||
if (capture > sumCapturePoints / 50)
|
||||
return;
|
||||
const relicPosition = relic.position();
|
||||
const access = PETRA.getLandAccess(gameState, relic);
|
||||
const access = getLandAccess(gameState, relic);
|
||||
const units = gameState.getOwnUnits().filter(ent => {
|
||||
if (!ent.position() || !ent.canCapture(relic))
|
||||
return false;
|
||||
@@ -680,17 +687,20 @@ PETRA.VictoryManager.prototype.captureGaiaRelic = function(gameState, relic)
|
||||
if (plan !== undefined && plan >= 0)
|
||||
{
|
||||
const attack = gameState.ai.HQ.attackManager.getPlan(plan);
|
||||
if (attack && (attack.state !== PETRA.AttackPlan.STATE_UNEXECUTED || attack.type === PETRA.AttackPlan.TYPE_RAID))
|
||||
if (attack && (attack.state !== AttackPlan.STATE_UNEXECUTED ||
|
||||
attack.type === AttackPlan.TYPE_RAID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (PETRA.getLandAccess(gameState, ent) != access)
|
||||
if (getLandAccess(gameState, ent) != access)
|
||||
return false;
|
||||
return true;
|
||||
}).filterNearest(relicPosition);
|
||||
const expedition = [];
|
||||
for (const ent of units.values())
|
||||
{
|
||||
capture += ent.captureStrength() * PETRA.getAttackBonus(ent, relic, "Capture");
|
||||
capture += ent.captureStrength() * getAttackBonus(ent, relic, "Capture");
|
||||
expedition.push(ent);
|
||||
if (capture > sumCapturePoints / 25)
|
||||
break;
|
||||
@@ -713,7 +723,7 @@ PETRA.VictoryManager.prototype.captureGaiaRelic = function(gameState, relic)
|
||||
this.targetedGaiaRelics.get(relic.id()).push(plan);
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.abortCaptureGaiaRelic = function(gameState, relicId)
|
||||
VictoryManager.prototype.abortCaptureGaiaRelic = function(gameState, relicId)
|
||||
{
|
||||
for (const plan of this.targetedGaiaRelics.get(relicId))
|
||||
{
|
||||
@@ -724,7 +734,7 @@ PETRA.VictoryManager.prototype.abortCaptureGaiaRelic = function(gameState, relic
|
||||
this.targetedGaiaRelics.delete(relicId);
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.Serialize = function()
|
||||
VictoryManager.prototype.Serialize = function()
|
||||
{
|
||||
return {
|
||||
"criticalEnts": this.criticalEnts,
|
||||
@@ -736,7 +746,7 @@ PETRA.VictoryManager.prototype.Serialize = function()
|
||||
};
|
||||
};
|
||||
|
||||
PETRA.VictoryManager.prototype.Deserialize = function(data)
|
||||
VictoryManager.prototype.Deserialize = function(data)
|
||||
{
|
||||
for (const key in data)
|
||||
this[key] = data[key];
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
import { allowCapture, gatherTreasure, getBuiltEntity, getLandAccess, getSeaAccess, isFastMoving,
|
||||
isSupplyFull, returnResources } from "simulation/ai/petra/entityExtend.js";
|
||||
import { TransportPlan } from "simulation/ai/petra/transportPlan.js";
|
||||
|
||||
/**
|
||||
* This class makes a worker do as instructed by the economy manager
|
||||
*/
|
||||
PETRA.Worker = function(base)
|
||||
export function Worker(base)
|
||||
{
|
||||
this.ent = undefined;
|
||||
this.base = base;
|
||||
this.baseID = base.ID;
|
||||
};
|
||||
}
|
||||
|
||||
PETRA.Worker.ROLE_ATTACK = "attack";
|
||||
PETRA.Worker.ROLE_TRADER = "trader";
|
||||
PETRA.Worker.ROLE_SWITCH_TO_TRADER = "switchToTrader";
|
||||
PETRA.Worker.ROLE_WORKER = "worker";
|
||||
PETRA.Worker.ROLE_CRITICAL_ENT_GUARD = "criticalEntGuard";
|
||||
PETRA.Worker.ROLE_CRITICAL_ENT_HEALER = "criticalEntHealer";
|
||||
Worker.ROLE_ATTACK = "attack";
|
||||
Worker.ROLE_TRADER = "trader";
|
||||
Worker.ROLE_SWITCH_TO_TRADER = "switchToTrader";
|
||||
Worker.ROLE_WORKER = "worker";
|
||||
Worker.ROLE_CRITICAL_ENT_GUARD = "criticalEntGuard";
|
||||
Worker.ROLE_CRITICAL_ENT_HEALER = "criticalEntHealer";
|
||||
|
||||
PETRA.Worker.SUBROLE_DEFENDER = "defender";
|
||||
PETRA.Worker.SUBROLE_IDLE = "idle";
|
||||
PETRA.Worker.SUBROLE_BUILDER = "builder";
|
||||
PETRA.Worker.SUBROLE_COMPLETING = "completing";
|
||||
PETRA.Worker.SUBROLE_WALKING = "walking";
|
||||
PETRA.Worker.SUBROLE_ATTACKING = "attacking";
|
||||
PETRA.Worker.SUBROLE_GATHERER = "gatherer";
|
||||
PETRA.Worker.SUBROLE_HUNTER = "hunter";
|
||||
PETRA.Worker.SUBROLE_FISHER = "fisher";
|
||||
PETRA.Worker.SUBROLE_GARRISONING = "garrisoning";
|
||||
Worker.SUBROLE_DEFENDER = "defender";
|
||||
Worker.SUBROLE_IDLE = "idle";
|
||||
Worker.SUBROLE_BUILDER = "builder";
|
||||
Worker.SUBROLE_COMPLETING = "completing";
|
||||
Worker.SUBROLE_WALKING = "walking";
|
||||
Worker.SUBROLE_ATTACKING = "attacking";
|
||||
Worker.SUBROLE_GATHERER = "gatherer";
|
||||
Worker.SUBROLE_HUNTER = "hunter";
|
||||
Worker.SUBROLE_FISHER = "fisher";
|
||||
Worker.SUBROLE_GARRISONING = "garrisoning";
|
||||
|
||||
PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
Worker.prototype.update = function(gameState, ent)
|
||||
{
|
||||
if (!ent.position() || ent.getMetadata(PlayerID, "plan") == -2 || ent.getMetadata(PlayerID, "plan") == -3)
|
||||
return;
|
||||
@@ -37,26 +41,26 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
{
|
||||
// Except if builder with their foundation destroyed, in which case cancel the transport if not yet on board
|
||||
if (subrole === PETRA.Worker.SUBROLE_BUILDER && ent.getMetadata(PlayerID, "target-foundation") !== undefined)
|
||||
if (subrole === Worker.SUBROLE_BUILDER && ent.getMetadata(PlayerID, "target-foundation") !== undefined)
|
||||
{
|
||||
const plan = gameState.ai.HQ.navalManager.getPlan(ent.getMetadata(PlayerID, "transport"));
|
||||
const target = gameState.getEntityById(ent.getMetadata(PlayerID, "target-foundation"));
|
||||
if (!target && plan && plan.state === PETRA.TransportPlan.BOARDING && ent.position())
|
||||
if (!target && plan && plan.state === TransportPlan.BOARDING && ent.position())
|
||||
plan.removeUnit(gameState, ent);
|
||||
}
|
||||
// and gatherer if there are no more dropsite accessible in the base the ent is going to
|
||||
if (subrole === PETRA.Worker.SUBROLE_GATHERER || subrole === PETRA.Worker.SUBROLE_HUNTER)
|
||||
if (subrole === Worker.SUBROLE_GATHERER || subrole === Worker.SUBROLE_HUNTER)
|
||||
{
|
||||
const plan = gameState.ai.HQ.navalManager.getPlan(ent.getMetadata(PlayerID, "transport"));
|
||||
if (plan.state === PETRA.TransportPlan.BOARDING && ent.position())
|
||||
if (plan.state === TransportPlan.BOARDING && ent.position())
|
||||
{
|
||||
let hasDropsite = false;
|
||||
const gatherType = ent.getMetadata(PlayerID, "gather-type") || "food";
|
||||
for (const structure of gameState.getOwnStructures().values())
|
||||
{
|
||||
if (PETRA.getLandAccess(gameState, structure) != plan.endIndex)
|
||||
if (getLandAccess(gameState, structure) != plan.endIndex)
|
||||
continue;
|
||||
const resourceDropsiteTypes = PETRA.getBuiltEntity(gameState, structure).resourceDropsiteTypes();
|
||||
const resourceDropsiteTypes = getBuiltEntity(gameState, structure).resourceDropsiteTypes();
|
||||
if (!resourceDropsiteTypes || resourceDropsiteTypes.indexOf(gatherType) == -1)
|
||||
continue;
|
||||
hasDropsite = true;
|
||||
@@ -66,7 +70,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
{
|
||||
for (const unit of gameState.getOwnUnits().filter(API3.Filters.byClass("Support")).values())
|
||||
{
|
||||
if (!unit.position() || PETRA.getLandAccess(gameState, unit) != plan.endIndex)
|
||||
if (!unit.position() || getLandAccess(gameState, unit) != plan.endIndex)
|
||||
continue;
|
||||
const resourceDropsiteTypes = unit.resourceDropsiteTypes();
|
||||
if (!resourceDropsiteTypes || resourceDropsiteTypes.indexOf(gatherType) == -1)
|
||||
@@ -83,7 +87,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
return;
|
||||
}
|
||||
|
||||
this.entAccess = PETRA.getLandAccess(gameState, ent);
|
||||
this.entAccess = getLandAccess(gameState, ent);
|
||||
// Base for unassigned entities has no accessIndex, so take the one from the entity.
|
||||
if (this.baseID == gameState.ai.HQ.basesManager.baselessBase().ID)
|
||||
this.baseAccess = this.entAccess;
|
||||
@@ -92,7 +96,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
|
||||
if (subrole == undefined) // subrole may-be undefined after a transport, garrisoning, army, ...
|
||||
{
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
this.base.reassignIdleWorkers(gameState, [ent]);
|
||||
this.update(gameState, ent);
|
||||
return;
|
||||
@@ -101,7 +105,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
this.ent = ent;
|
||||
|
||||
const unitAIState = ent.unitAIState();
|
||||
if ((subrole === PETRA.Worker.SUBROLE_HUNTER || subrole === PETRA.Worker.SUBROLE_GATHERER) &&
|
||||
if ((subrole === Worker.SUBROLE_HUNTER || subrole === Worker.SUBROLE_GATHERER) &&
|
||||
(unitAIState == "INDIVIDUAL.GATHER.GATHERING" || unitAIState == "INDIVIDUAL.GATHER.APPROACHING" ||
|
||||
unitAIState == "INDIVIDUAL.COMBAT.APPROACHING"))
|
||||
{
|
||||
@@ -130,8 +134,8 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
}
|
||||
else if (!gameState.isPlayerAlly(territoryOwner))
|
||||
{
|
||||
const distanceSquare = PETRA.isFastMoving(ent) ? 90000 : 30000;
|
||||
const targetAccess = PETRA.getLandAccess(gameState, target);
|
||||
const distanceSquare = isFastMoving(ent) ? 90000 : 30000;
|
||||
const targetAccess = getLandAccess(gameState, target);
|
||||
const foodDropsites = gameState.playerData.hasSharedDropsites ?
|
||||
gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food");
|
||||
let hasFoodDropsiteWithinDistance = false;
|
||||
@@ -143,7 +147,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
// owner != PlayerID can only happen when hasSharedDropsites == true, so no need to test it again
|
||||
if (owner != PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
|
||||
continue;
|
||||
if (targetAccess != PETRA.getLandAccess(gameState, dropsite))
|
||||
if (targetAccess != getLandAccess(gameState, dropsite))
|
||||
continue;
|
||||
if (API3.SquareVectorDistance(target.position(), dropsite.position()) < distanceSquare)
|
||||
{
|
||||
@@ -174,7 +178,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
// Also, if we are attacking, do not capture
|
||||
if (unitAIStateOrder == "COMBAT")
|
||||
{
|
||||
if (subrole === PETRA.Worker.SUBROLE_FISHER)
|
||||
if (subrole === Worker.SUBROLE_FISHER)
|
||||
this.startFishing(gameState);
|
||||
else if (unitAIState == "INDIVIDUAL.COMBAT.APPROACHING" && ent.unitAIOrderData().length &&
|
||||
!ent.getMetadata(PlayerID, "PartOfArmy"))
|
||||
@@ -183,7 +187,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
if (orderData && orderData.target)
|
||||
{
|
||||
const target = gameState.getEntityById(orderData.target);
|
||||
if (target && (!target.position() || PETRA.getLandAccess(gameState, target) != this.entAccess))
|
||||
if (target && (!target.position() || getLandAccess(gameState, target) != this.entAccess))
|
||||
{
|
||||
if (this.retryWorking(gameState, subrole))
|
||||
return;
|
||||
@@ -201,7 +205,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
// and UnitAI sent it fight back with allowCapture=true
|
||||
const target = gameState.getEntityById(orderData.target);
|
||||
if (target && target.owner() > 0 && !gameState.isPlayerAlly(target.owner()))
|
||||
ent.attack(orderData.target, PETRA.allowCapture(gameState, ent, target));
|
||||
ent.attack(orderData.target, allowCapture(gameState, ent, target));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -211,7 +215,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
// If we're gathering, we'll check that we haven't run idle.
|
||||
// And we'll also check that we're gathering a resource we want to gather.
|
||||
|
||||
if (subrole === PETRA.Worker.SUBROLE_GATHERER)
|
||||
if (subrole === Worker.SUBROLE_GATHERER)
|
||||
{
|
||||
if (ent.isIdle())
|
||||
{
|
||||
@@ -223,7 +227,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
{
|
||||
this.startGathering(gameState);
|
||||
}
|
||||
else if (!PETRA.returnResources(gameState, ent)) // try to deposit resources
|
||||
else if (!returnResources(gameState, ent)) // try to deposit resources
|
||||
{
|
||||
// no dropsite, abandon old resources and start gathering new ones
|
||||
this.startGathering(gameState);
|
||||
@@ -277,8 +281,11 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
{
|
||||
// Check from time to time that UnitAI does not send us to an inaccessible dropsite
|
||||
const dropsite = gameState.getEntityById(ent.unitAIOrderData()[0].target);
|
||||
if (dropsite && dropsite.position() && this.entAccess != PETRA.getLandAccess(gameState, dropsite))
|
||||
PETRA.returnResources(gameState, this.ent);
|
||||
if (dropsite && dropsite.position() &&
|
||||
this.entAccess != getLandAccess(gameState, dropsite))
|
||||
{
|
||||
returnResources(gameState, this.ent);
|
||||
}
|
||||
}
|
||||
|
||||
// If gathering a sparse resource, we may have been sent to a faraway resource if the one nearby was full.
|
||||
@@ -308,7 +315,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (subrole === PETRA.Worker.SUBROLE_BUILDER)
|
||||
else if (subrole === Worker.SUBROLE_BUILDER)
|
||||
{
|
||||
if (unitAIStateOrder == "REPAIR")
|
||||
{
|
||||
@@ -325,7 +332,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
return;
|
||||
}
|
||||
ent.setMetadata(PlayerID, "target-foundation", undefined);
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
ent.stopMoving();
|
||||
if (this.baseID != gameState.ai.HQ.basesManager.baselessBase().ID)
|
||||
{
|
||||
@@ -346,7 +353,7 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
const target = gameState.getEntityById(ent.getMetadata(PlayerID, "target-foundation"));
|
||||
if (!target || target.foundationProgress() === undefined && target.needsRepair() === false)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
ent.setMetadata(PlayerID, "target-foundation", undefined);
|
||||
// If worker elephant, move away to avoid being trapped in between constructions
|
||||
if (ent.hasClass("Elephant"))
|
||||
@@ -361,15 +368,15 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
}
|
||||
else
|
||||
{
|
||||
const goalAccess = PETRA.getLandAccess(gameState, target);
|
||||
const queued = PETRA.returnResources(gameState, ent);
|
||||
const goalAccess = getLandAccess(gameState, target);
|
||||
const queued = returnResources(gameState, ent);
|
||||
if (this.entAccess == goalAccess)
|
||||
ent.repair(target, target.hasClass("House"), queued); // autocontinue=true for houses
|
||||
else
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, ent, this.entAccess, goalAccess, target.position());
|
||||
}
|
||||
}
|
||||
else if (subrole === PETRA.Worker.SUBROLE_HUNTER)
|
||||
else if (subrole === Worker.SUBROLE_HUNTER)
|
||||
{
|
||||
const lastHuntSearch = ent.getMetadata(PlayerID, "lastHuntSearch");
|
||||
if (ent.isIdle() && (!lastHuntSearch || gameState.ai.elapsedTime - lastHuntSearch > 20))
|
||||
@@ -410,13 +417,16 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
{
|
||||
// Check that UnitAI does not send us to an inaccessible dropsite
|
||||
const dropsite = gameState.getEntityById(ent.unitAIOrderData()[0].target);
|
||||
if (dropsite && dropsite.position() && this.entAccess != PETRA.getLandAccess(gameState, dropsite))
|
||||
PETRA.returnResources(gameState, ent);
|
||||
if (dropsite && dropsite.position() &&
|
||||
this.entAccess != getLandAccess(gameState, dropsite))
|
||||
{
|
||||
returnResources(gameState, ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (subrole === PETRA.Worker.SUBROLE_FISHER)
|
||||
else if (subrole === Worker.SUBROLE_FISHER)
|
||||
{
|
||||
if (ent.isIdle())
|
||||
this.startFishing(gameState);
|
||||
@@ -429,38 +439,38 @@ PETRA.Worker.prototype.update = function(gameState, ent)
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.Worker.prototype.retryWorking = function(gameState, subrole)
|
||||
Worker.prototype.retryWorking = function(gameState, subrole)
|
||||
{
|
||||
switch (subrole)
|
||||
{
|
||||
case PETRA.Worker.SUBROLE_GATHERER:
|
||||
case Worker.SUBROLE_GATHERER:
|
||||
return this.startGathering(gameState);
|
||||
case PETRA.Worker.SUBROLE_HUNTER:
|
||||
case Worker.SUBROLE_HUNTER:
|
||||
return this.startHunting(gameState);
|
||||
case PETRA.Worker.SUBROLE_FISHER:
|
||||
case Worker.SUBROLE_FISHER:
|
||||
return this.startFishing(gameState);
|
||||
case PETRA.Worker.SUBROLE_BUILDER:
|
||||
case Worker.SUBROLE_BUILDER:
|
||||
return this.startBuilding(gameState);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
PETRA.Worker.prototype.startBuilding = function(gameState)
|
||||
Worker.prototype.startBuilding = function(gameState)
|
||||
{
|
||||
const target = gameState.getEntityById(this.ent.getMetadata(PlayerID, "target-foundation"));
|
||||
if (!target || target.foundationProgress() === undefined && target.needsRepair() == false)
|
||||
return false;
|
||||
if (PETRA.getLandAccess(gameState, target) != this.entAccess)
|
||||
if (getLandAccess(gameState, target) != this.entAccess)
|
||||
return false;
|
||||
this.ent.repair(target, target.hasClass("House")); // autocontinue=true for houses
|
||||
return true;
|
||||
};
|
||||
|
||||
PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
Worker.prototype.startGathering = function(gameState)
|
||||
{
|
||||
// First look for possible treasure if any
|
||||
if (PETRA.gatherTreasure(gameState, this.ent))
|
||||
if (gatherTreasure(gameState, this.ent))
|
||||
return true;
|
||||
|
||||
const resource = this.ent.getMetadata(PlayerID, "gather-type");
|
||||
@@ -481,7 +491,7 @@ PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
supplies.splice(i--, 1);
|
||||
continue;
|
||||
}
|
||||
if (PETRA.IsSupplyFull(gameState, supplies[i].ent))
|
||||
if (isSupplyFull(gameState, supplies[i].ent))
|
||||
continue;
|
||||
const inaccessibleTime = supplies[i].ent.getMetadata(PlayerID, "inaccessibleTime");
|
||||
if (inaccessibleTime && gameState.ai.elapsedTime < inaccessibleTime)
|
||||
@@ -600,7 +610,7 @@ PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
// Try to help building one if any accessible foundation available
|
||||
const foundations = gameState.getOwnFoundations().toEntityArray();
|
||||
let shouldBuild = this.ent.isBuilder() && foundations.some(function(foundation) {
|
||||
if (!foundation || PETRA.getLandAccess(gameState, foundation) != this.entAccess)
|
||||
if (!foundation || getLandAccess(gameState, foundation) != this.entAccess)
|
||||
return false;
|
||||
const structure = gameState.getBuiltTemplate(foundation.templateName());
|
||||
if (structure.resourceDropsiteTypes() && structure.resourceDropsiteTypes().indexOf(resource) != -1)
|
||||
@@ -608,7 +618,7 @@ PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
if (foundation.getMetadata(PlayerID, "base") != this.baseID)
|
||||
this.ent.setMetadata(PlayerID, "base", foundation.getMetadata(PlayerID, "base"));
|
||||
this.ent.setMetadata(PlayerID, "target-foundation", foundation.id());
|
||||
this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
this.ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
this.ent.repair(foundation);
|
||||
return true;
|
||||
}
|
||||
@@ -667,18 +677,18 @@ PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
// Okay so we haven't found any appropriate dropsite anywhere.
|
||||
// Try to help building one if any non-accessible foundation available
|
||||
shouldBuild = this.ent.isBuilder() && foundations.some(function(foundation) {
|
||||
if (!foundation || PETRA.getLandAccess(gameState, foundation) == this.entAccess)
|
||||
if (!foundation || getLandAccess(gameState, foundation) == this.entAccess)
|
||||
return false;
|
||||
const structure = gameState.getBuiltTemplate(foundation.templateName());
|
||||
if (structure.resourceDropsiteTypes() && structure.resourceDropsiteTypes().indexOf(resource) != -1)
|
||||
{
|
||||
const foundationAccess = PETRA.getLandAccess(gameState, foundation);
|
||||
const foundationAccess = getLandAccess(gameState, foundation);
|
||||
if (navalManager.requireTransport(gameState, this.ent, this.entAccess, foundationAccess, foundation.position()))
|
||||
{
|
||||
if (foundation.getMetadata(PlayerID, "base") != this.baseID)
|
||||
this.ent.setMetadata(PlayerID, "base", foundation.getMetadata(PlayerID, "base"));
|
||||
this.ent.setMetadata(PlayerID, "target-foundation", foundation.id());
|
||||
this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
|
||||
this.ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_BUILDER);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -741,7 +751,7 @@ PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
gameState.ai.HQ.lastFailedGather[resource] = gameState.ai.elapsedTime;
|
||||
if (gameState.ai.Config.debug > 2)
|
||||
API3.warn(" >>>>> worker with gather-type " + resource + " with nothing to gather ");
|
||||
this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
this.ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -749,10 +759,10 @@ PETRA.Worker.prototype.startGathering = function(gameState)
|
||||
* if position is given, we only check if we could hunt from this position but do nothing
|
||||
* otherwise the position of the entity is taken, and if something is found, we directly start the hunt
|
||||
*/
|
||||
PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
Worker.prototype.startHunting = function(gameState, position)
|
||||
{
|
||||
// First look for possible treasure if any
|
||||
if (!position && PETRA.gatherTreasure(gameState, this.ent))
|
||||
if (!position && gatherTreasure(gameState, this.ent))
|
||||
return true;
|
||||
|
||||
const resources = gameState.getHuntableSupplies();
|
||||
@@ -762,7 +772,7 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
let nearestSupplyDist = Math.min();
|
||||
let nearestSupply;
|
||||
|
||||
const isFastMoving = PETRA.isFastMoving(this.ent);
|
||||
const entIsFastMoving = isFastMoving(this.ent);
|
||||
const isRanged = this.ent.hasClass("Ranged");
|
||||
const entPosition = position ? position : this.ent.position();
|
||||
const foodDropsites = gameState.playerData.hasSharedDropsites ?
|
||||
@@ -778,7 +788,7 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
// owner != PlayerID can only happen when hasSharedDropsites == true, so no need to test it again
|
||||
if (owner != PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
|
||||
continue;
|
||||
if (supplyAccess != PETRA.getLandAccess(gameState, dropsite))
|
||||
if (supplyAccess != getLandAccess(gameState, dropsite))
|
||||
continue;
|
||||
if (API3.SquareVectorDistance(supplyPosition, dropsite.position()) < distSquare)
|
||||
return true;
|
||||
@@ -800,7 +810,7 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
if (!gatherRates[supplyType])
|
||||
continue;
|
||||
|
||||
if (PETRA.IsSupplyFull(gameState, supply))
|
||||
if (isSupplyFull(gameState, supply))
|
||||
continue;
|
||||
// Check if available resource is worth one additionnal gatherer (except for farms).
|
||||
const nbGatherers = supply.resourceSupplyNumGatherers() + this.base.GetTCGatherer(supply.id());
|
||||
@@ -809,10 +819,10 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
|
||||
const canFlee = !supply.hasClass("Domestic") && supply.templateName().indexOf("resource|") == -1;
|
||||
// Only FastMoving and Ranged units should hunt fleeing animals.
|
||||
if (canFlee && !isFastMoving && !isRanged)
|
||||
if (canFlee && !entIsFastMoving && !isRanged)
|
||||
continue;
|
||||
|
||||
const supplyAccess = PETRA.getLandAccess(gameState, supply);
|
||||
const supplyAccess = getLandAccess(gameState, supply);
|
||||
if (supplyAccess != this.entAccess)
|
||||
continue;
|
||||
|
||||
@@ -822,7 +832,7 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
continue;
|
||||
|
||||
// Only FastMoving should hunt faraway.
|
||||
if (!isFastMoving && dist > 25000)
|
||||
if (!entIsFastMoving && dist > 25000)
|
||||
continue;
|
||||
|
||||
// Avoid enemy territory.
|
||||
@@ -834,9 +844,9 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
continue;
|
||||
|
||||
// Only FastMoving should hunt far from dropsite (specially for non-Domestic animals which flee).
|
||||
if (!isFastMoving && canFlee && territoryOwner == 0)
|
||||
if (!entIsFastMoving && canFlee && territoryOwner == 0)
|
||||
continue;
|
||||
const distanceSquare = isFastMoving ? 35000 : (canFlee ? 7000 : 12000);
|
||||
const distanceSquare = entIsFastMoving ? 35000 : (canFlee ? 7000 : 12000);
|
||||
if (!hasFoodDropsiteWithinDistance(supply.position(), supplyAccess, distanceSquare))
|
||||
continue;
|
||||
|
||||
@@ -857,7 +867,7 @@ PETRA.Worker.prototype.startHunting = function(gameState, position)
|
||||
return false;
|
||||
};
|
||||
|
||||
PETRA.Worker.prototype.startFishing = function(gameState)
|
||||
Worker.prototype.startFishing = function(gameState)
|
||||
{
|
||||
if (!this.ent.position())
|
||||
return false;
|
||||
@@ -873,7 +883,7 @@ PETRA.Worker.prototype.startFishing = function(gameState)
|
||||
let nearestSupplyDist = Math.min();
|
||||
let nearestSupply;
|
||||
|
||||
const fisherSea = PETRA.getSeaAccess(gameState, this.ent);
|
||||
const fisherSea = getSeaAccess(gameState, this.ent);
|
||||
const fishDropsites = (gameState.playerData.hasSharedDropsites ? gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food")).
|
||||
filter(API3.Filters.byClass("Dock")).toEntityArray();
|
||||
|
||||
@@ -888,7 +898,7 @@ PETRA.Worker.prototype.startFishing = function(gameState)
|
||||
// owner != PlayerID can only happen when hasSharedDropsites == true, so no need to test it again
|
||||
if (owner != PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
|
||||
continue;
|
||||
if (fisherSea != PETRA.getSeaAccess(gameState, dropsite))
|
||||
if (fisherSea != getSeaAccess(gameState, dropsite))
|
||||
continue;
|
||||
distMin = Math.min(distMin, API3.SquareVectorDistance(pos, dropsite.position()));
|
||||
}
|
||||
@@ -911,7 +921,7 @@ PETRA.Worker.prototype.startFishing = function(gameState)
|
||||
if (!gatherRates[supplyType])
|
||||
return;
|
||||
|
||||
if (PETRA.IsSupplyFull(gameState, supply))
|
||||
if (isSupplyFull(gameState, supply))
|
||||
return;
|
||||
// check if available resource is worth one additionnal gatherer (except for farms)
|
||||
const nbGatherers = supply.resourceSupplyNumGatherers() + this.base.GetTCGatherer(supply.id());
|
||||
@@ -946,12 +956,12 @@ PETRA.Worker.prototype.startFishing = function(gameState)
|
||||
this.ent.setMetadata(PlayerID, "target-foundation", undefined);
|
||||
return true;
|
||||
}
|
||||
if (this.ent.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_FISHER)
|
||||
this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
|
||||
if (this.ent.getMetadata(PlayerID, "subrole") === Worker.SUBROLE_FISHER)
|
||||
this.ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE);
|
||||
return false;
|
||||
};
|
||||
|
||||
PETRA.Worker.prototype.gatherNearestField = function(gameState, baseID)
|
||||
Worker.prototype.gatherNearestField = function(gameState, baseID)
|
||||
{
|
||||
const ownFields = gameState.getOwnEntitiesByClass("Field", true).filter(API3.Filters.isBuilt()).filter(API3.Filters.byMetadata(PlayerID, "base", baseID));
|
||||
let bestFarm;
|
||||
@@ -959,7 +969,7 @@ PETRA.Worker.prototype.gatherNearestField = function(gameState, baseID)
|
||||
const gatherRates = this.ent.resourceGatherRates();
|
||||
for (const field of ownFields.values())
|
||||
{
|
||||
if (PETRA.IsSupplyFull(gameState, field))
|
||||
if (isSupplyFull(gameState, field))
|
||||
continue;
|
||||
const supplyType = field.get("ResourceSupply/Type");
|
||||
if (!gatherRates[supplyType])
|
||||
@@ -991,7 +1001,7 @@ PETRA.Worker.prototype.gatherNearestField = function(gameState, baseID)
|
||||
* WARNING with the present options of AI orders, the unit will not gather after building the farm.
|
||||
* This is done by calling the gatherNearestField function when construction is completed.
|
||||
*/
|
||||
PETRA.Worker.prototype.buildAnyField = function(gameState, baseID)
|
||||
Worker.prototype.buildAnyField = function(gameState, baseID)
|
||||
{
|
||||
if (!this.ent.isBuilder())
|
||||
return false;
|
||||
@@ -1020,17 +1030,17 @@ PETRA.Worker.prototype.buildAnyField = function(gameState, baseID)
|
||||
* For the time being, we move towards the nearest gatherer (providing him a dropsite).
|
||||
* BaseManager does also use that function to deal with its mobile dropsites.
|
||||
*/
|
||||
PETRA.Worker.prototype.moveToGatherer = function(gameState, ent, forced)
|
||||
Worker.prototype.moveToGatherer = function(gameState, ent, forced)
|
||||
{
|
||||
const pos = ent.position();
|
||||
if (!pos || ent.getMetadata(PlayerID, "target-foundation") !== undefined)
|
||||
return;
|
||||
if (!forced && gameState.ai.elapsedTime < (ent.getMetadata(PlayerID, "nextMoveToGatherer") || 5))
|
||||
return;
|
||||
const gatherers = this.base.workersBySubrole(gameState, PETRA.Worker.SUBROLE_GATHERER);
|
||||
const gatherers = this.base.workersBySubrole(gameState, Worker.SUBROLE_GATHERER);
|
||||
let dist = Math.min();
|
||||
let destination;
|
||||
const access = PETRA.getLandAccess(gameState, ent);
|
||||
const access = getLandAccess(gameState, ent);
|
||||
const types = ent.resourceDropsiteTypes();
|
||||
for (const gatherer of gatherers.values())
|
||||
{
|
||||
@@ -1038,8 +1048,10 @@ PETRA.Worker.prototype.moveToGatherer = function(gameState, ent, forced)
|
||||
if (!gathererType || types.indexOf(gathererType) == -1)
|
||||
continue;
|
||||
if (!gatherer.position() || gatherer.getMetadata(PlayerID, "transport") !== undefined ||
|
||||
PETRA.getLandAccess(gameState, gatherer) != access || gatherer.isIdle())
|
||||
getLandAccess(gameState, gatherer) != access || gatherer.isIdle())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const distance = API3.SquareVectorDistance(pos, gatherer.position());
|
||||
if (distance > dist)
|
||||
continue;
|
||||
@@ -1056,7 +1068,7 @@ PETRA.Worker.prototype.moveToGatherer = function(gameState, ent, forced)
|
||||
* inside obstruction of other entities). The resource will be flagged as inaccessible during 10 mn (in case
|
||||
* it will be cleared later).
|
||||
*/
|
||||
PETRA.Worker.prototype.isInaccessibleSupply = function(gameState)
|
||||
Worker.prototype.isInaccessibleSupply = function(gameState)
|
||||
{
|
||||
if (!this.ent.unitAIOrderData()[0] || !this.ent.unitAIOrderData()[0].target)
|
||||
return false;
|
||||
|
||||
@@ -41,7 +41,9 @@
|
||||
#include "ps/scripting/JSInterface_VFS.h"
|
||||
#include "scriptinterface/FunctionWrapper.h"
|
||||
#include "scriptinterface/JSON.h"
|
||||
#include "scriptinterface/ModuleLoader.h"
|
||||
#include "scriptinterface/Object.h"
|
||||
#include "scriptinterface/ScriptContext.h"
|
||||
#include "scriptinterface/ScriptConversions.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "scriptinterface/ScriptRequest.h"
|
||||
@@ -128,10 +130,6 @@ private:
|
||||
|
||||
bool Initialise()
|
||||
{
|
||||
// LoadScripts will only load each script once even though we call it for each player
|
||||
if (!m_Worker.LoadScripts(m_AIName))
|
||||
return false;
|
||||
|
||||
ScriptRequest rq(m_ScriptInterface);
|
||||
|
||||
OsPath path = L"simulation/ai/" + m_AIName + L"/data.json";
|
||||
@@ -144,24 +142,23 @@ private:
|
||||
}
|
||||
|
||||
// Get the constructor name from the metadata
|
||||
std::string moduleName;
|
||||
std::string filename;
|
||||
std::string constructor;
|
||||
JS::RootedValue objectWithConstructor(rq.cx); // object that should contain the constructor function
|
||||
JS::RootedValue global(rq.cx, rq.globalValue());
|
||||
JS::RootedValue ctor(rq.cx);
|
||||
if (!Script::HasProperty(rq, metadata, "moduleName"))
|
||||
if (!Script::HasProperty(rq, metadata, "filename"))
|
||||
{
|
||||
LOGERROR("Failed to create AI player: %s: missing 'moduleName'", path.string8());
|
||||
LOGERROR("Failed to create AI player: %s: missing 'filename'", path.string8());
|
||||
return false;
|
||||
}
|
||||
|
||||
Script::GetProperty(rq, metadata, "moduleName", moduleName);
|
||||
if (!Script::GetProperty(rq, global, moduleName.c_str(), &objectWithConstructor)
|
||||
|| objectWithConstructor.isUndefined())
|
||||
{
|
||||
LOGERROR("Failed to create AI player: %s: can't find the module that should contain the constructor: '%s'", path.string8(), moduleName);
|
||||
return false;
|
||||
}
|
||||
Script::GetProperty(rq, metadata, "filename", filename);
|
||||
|
||||
|
||||
auto result = m_ScriptInterface->GetModuleLoader().LoadModule(rq,
|
||||
"simulation/ai/" + utf8_from_wstring(m_AIName) + "/" + filename);
|
||||
|
||||
g_ScriptContext->RunJobs();
|
||||
JS::RootedValue objectWithConstructor(rq.cx, JS::ObjectValue(*result.begin()->Get()));
|
||||
|
||||
if (!Script::GetProperty(rq, metadata, "constructor", constructor))
|
||||
{
|
||||
@@ -267,7 +264,11 @@ public:
|
||||
{
|
||||
// Create the script interface in the same compartment as the simulation interface.
|
||||
// This will allow us to directly share data from the sim to the AI (and vice versa, should the need arise).
|
||||
m_ScriptInterface = std::make_shared<ScriptInterface>("Engine", "AI", simInterface);
|
||||
m_ScriptInterface = std::make_shared<ScriptInterface>("Engine", "AI", simInterface,
|
||||
[](const VfsPath& path)
|
||||
{
|
||||
return path.string().find(L"simulation/ai/") == 0;
|
||||
});
|
||||
|
||||
ScriptRequest rq(m_ScriptInterface);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user