Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/ai/petra/attackManager.js
var PETRA = function(m) | /** | ||||
{ | * Attack Manager | ||||
*/ | |||||
/** Attack Manager */ | PETRA.AttackManager = function(Config) | ||||
m.AttackManager = function(Config) | |||||
{ | { | ||||
this.Config = Config; | this.Config = Config; | ||||
this.totalNumber = 0; | this.totalNumber = 0; | ||||
this.attackNumber = 0; | this.attackNumber = 0; | ||||
this.rushNumber = 0; | this.rushNumber = 0; | ||||
this.raidNumber = 0; | this.raidNumber = 0; | ||||
this.upcomingAttacks = { "Rush": [], "Raid": [], "Attack": [], "HugeAttack": [] }; | this.upcomingAttacks = { "Rush": [], "Raid": [], "Attack": [], "HugeAttack": [] }; | ||||
this.startedAttacks = { "Rush": [], "Raid": [], "Attack": [], "HugeAttack": [] }; | this.startedAttacks = { "Rush": [], "Raid": [], "Attack": [], "HugeAttack": [] }; | ||||
this.bombingAttacks = new Map();// Temporary attacks for siege units while waiting their current attack to start | this.bombingAttacks = new Map();// Temporary attacks for siege units while waiting their current attack to start | ||||
this.debugTime = 0; | this.debugTime = 0; | ||||
this.maxRushes = 0; | this.maxRushes = 0; | ||||
this.rushSize = []; | this.rushSize = []; | ||||
this.currentEnemyPlayer = undefined; // enemy player we are currently targeting | this.currentEnemyPlayer = undefined; // enemy player we are currently targeting | ||||
this.defeated = {}; | this.defeated = {}; | ||||
}; | }; | ||||
/** More initialisation for stuff that needs the gameState */ | /** More initialisation for stuff that needs the gameState */ | ||||
m.AttackManager.prototype.init = function(gameState) | PETRA.AttackManager.prototype.init = function(gameState) | ||||
{ | { | ||||
this.outOfPlan = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", -1)); | this.outOfPlan = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", -1)); | ||||
this.outOfPlan.registerUpdates(); | this.outOfPlan.registerUpdates(); | ||||
}; | }; | ||||
m.AttackManager.prototype.setRushes = function(allowed) | PETRA.AttackManager.prototype.setRushes = function(allowed) | ||||
{ | { | ||||
if (this.Config.personality.aggressive > this.Config.personalityCut.strong && allowed > 2) | if (this.Config.personality.aggressive > this.Config.personalityCut.strong && allowed > 2) | ||||
{ | { | ||||
this.maxRushes = 3; | this.maxRushes = 3; | ||||
this.rushSize = [ 16, 20, 24 ]; | this.rushSize = [ 16, 20, 24 ]; | ||||
} | } | ||||
else if (this.Config.personality.aggressive > this.Config.personalityCut.medium && allowed > 1) | else if (this.Config.personality.aggressive > this.Config.personalityCut.medium && allowed > 1) | ||||
{ | { | ||||
this.maxRushes = 2; | this.maxRushes = 2; | ||||
this.rushSize = [ 18, 22 ]; | this.rushSize = [ 18, 22 ]; | ||||
} | } | ||||
else if (this.Config.personality.aggressive > this.Config.personalityCut.weak && allowed > 0) | else if (this.Config.personality.aggressive > this.Config.personalityCut.weak && allowed > 0) | ||||
{ | { | ||||
this.maxRushes = 1; | this.maxRushes = 1; | ||||
this.rushSize = [ 20 ]; | this.rushSize = [ 20 ]; | ||||
} | } | ||||
}; | }; | ||||
m.AttackManager.prototype.checkEvents = function(gameState, events) | PETRA.AttackManager.prototype.checkEvents = function(gameState, events) | ||||
{ | { | ||||
for (let evt of events.PlayerDefeated) | for (let evt of events.PlayerDefeated) | ||||
this.defeated[evt.playerId] = true; | this.defeated[evt.playerId] = true; | ||||
let answer = "decline"; | let answer = "decline"; | ||||
let other; | let other; | ||||
let targetPlayer; | let targetPlayer; | ||||
for (let evt of events.AttackRequest) | for (let evt of events.AttackRequest) | ||||
Show All 38 Lines | if (available > 12) // launch the attack immediately | ||||
} | } | ||||
answer = "join"; | answer = "join"; | ||||
} | } | ||||
else if (other !== undefined) | else if (other !== undefined) | ||||
answer = "other"; | answer = "other"; | ||||
break; // take only the first attack request into account | break; // take only the first attack request into account | ||||
} | } | ||||
if (targetPlayer !== undefined) | if (targetPlayer !== undefined) | ||||
m.chatAnswerRequestAttack(gameState, targetPlayer, answer, other); | PETRA.chatAnswerRequestAttack(gameState, targetPlayer, answer, other); | ||||
for (let evt of events.EntityRenamed) // take care of packing units in bombing attacks | for (let evt of events.EntityRenamed) // take care of packing units in bombing attacks | ||||
{ | { | ||||
for (let [targetId, unitIds] of this.bombingAttacks) | for (let [targetId, unitIds] of this.bombingAttacks) | ||||
{ | { | ||||
if (targetId == evt.entity) | if (targetId == evt.entity) | ||||
{ | { | ||||
this.bombingAttacks.set(evt.newentity, unitIds); | this.bombingAttacks.set(evt.newentity, unitIds); | ||||
this.bombingAttacks.delete(evt.entity); | this.bombingAttacks.delete(evt.entity); | ||||
} | } | ||||
else if (unitIds.has(evt.entity)) | else if (unitIds.has(evt.entity)) | ||||
{ | { | ||||
unitIds.add(evt.newentity); | unitIds.add(evt.newentity); | ||||
unitIds.delete(evt.entity); | unitIds.delete(evt.entity); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Check for any structure in range from within our territory, and bomb it | * Check for any structure in range from within our territory, and bomb it | ||||
*/ | */ | ||||
m.AttackManager.prototype.assignBombers = function(gameState) | PETRA.AttackManager.prototype.assignBombers = function(gameState) | ||||
{ | { | ||||
// First some cleaning of current bombing attacks | // First some cleaning of current bombing attacks | ||||
for (let [targetId, unitIds] of this.bombingAttacks) | for (let [targetId, unitIds] of this.bombingAttacks) | ||||
{ | { | ||||
let target = gameState.getEntityById(targetId); | let target = gameState.getEntityById(targetId); | ||||
if (!target || !gameState.isPlayerEnemy(target.owner())) | if (!target || !gameState.isPlayerEnemy(target.owner())) | ||||
this.bombingAttacks.delete(targetId); | this.bombingAttacks.delete(targetId); | ||||
else | else | ||||
Show All 37 Lines | for (let unitIds of this.bombingAttacks.values()) | ||||
alreadyBombing = true; | alreadyBombing = true; | ||||
break; | break; | ||||
} | } | ||||
if (alreadyBombing) | if (alreadyBombing) | ||||
break; | break; | ||||
let range = ent.attackRange("Ranged").max; | let range = ent.attackRange("Ranged").max; | ||||
let entPos = ent.position(); | let entPos = ent.position(); | ||||
let access = m.getLandAccess(gameState, ent); | let access = PETRA.getLandAccess(gameState, ent); | ||||
for (let struct of gameState.getEnemyStructures().values()) | for (let struct of gameState.getEnemyStructures().values()) | ||||
{ | { | ||||
let structPos = struct.position(); | let structPos = struct.position(); | ||||
let x; | let x; | ||||
let z; | let z; | ||||
if (struct.hasClass("Field")) | if (struct.hasClass("Field")) | ||||
{ | { | ||||
if (!struct.resourceSupplyNumGatherers() || | if (!struct.resourceSupplyNumGatherers() || | ||||
Show All 38 Lines | for (let ent of bombers.values()) | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Some functions are run every turn | * Some functions are run every turn | ||||
* Others once in a while | * Others once in a while | ||||
*/ | */ | ||||
m.AttackManager.prototype.update = function(gameState, queues, events) | PETRA.AttackManager.prototype.update = function(gameState, queues, events) | ||||
{ | { | ||||
if (this.Config.debug > 2 && gameState.ai.elapsedTime > this.debugTime + 60) | if (this.Config.debug > 2 && gameState.ai.elapsedTime > this.debugTime + 60) | ||||
{ | { | ||||
this.debugTime = gameState.ai.elapsedTime; | this.debugTime = gameState.ai.elapsedTime; | ||||
API3.warn(" upcoming attacks ================="); | API3.warn(" upcoming attacks ================="); | ||||
for (let attackType in this.upcomingAttacks) | for (let attackType in this.upcomingAttacks) | ||||
for (let attack of this.upcomingAttacks[attackType]) | for (let attack of this.upcomingAttacks[attackType]) | ||||
API3.warn(" plan " + attack.name + " type " + attackType + " state " + attack.state + " units " + attack.unitCollection.length); | API3.warn(" plan " + attack.name + " type " + attackType + " state " + attack.state + " units " + attack.unitCollection.length); | ||||
Show All 34 Lines | for (let i = 0; i < this.upcomingAttacks[attackType].length; ++i) | ||||
} | } | ||||
else if (updateStep == 2) | else if (updateStep == 2) | ||||
{ | { | ||||
if (attack.StartAttack(gameState)) | if (attack.StartAttack(gameState)) | ||||
{ | { | ||||
if (this.Config.debug > 1) | if (this.Config.debug > 1) | ||||
API3.warn("Attack Manager: Starting " + attack.getType() + " plan " + attack.getName()); | API3.warn("Attack Manager: Starting " + attack.getType() + " plan " + attack.getName()); | ||||
if (this.Config.chat) | if (this.Config.chat) | ||||
m.chatLaunchAttack(gameState, attack.targetPlayer, attack.getType()); | PETRA.chatLaunchAttack(gameState, attack.targetPlayer, attack.getType()); | ||||
this.startedAttacks[attackType].push(attack); | this.startedAttacks[attackType].push(attack); | ||||
} | } | ||||
else | else | ||||
attack.Abort(gameState); | attack.Abort(gameState); | ||||
this.upcomingAttacks[attackType].splice(i--, 1); | this.upcomingAttacks[attackType].splice(i--, 1); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 22 Lines | PETRA.AttackManager.prototype.update = function(gameState, queues, events) | ||||
let barracksNb = gameState.getOwnEntitiesByClass("Barracks", true).filter(API3.Filters.isBuilt()).length; | let barracksNb = gameState.getOwnEntitiesByClass("Barracks", true).filter(API3.Filters.isBuilt()).length; | ||||
if (this.rushNumber < this.maxRushes && barracksNb >= 1) | if (this.rushNumber < this.maxRushes && barracksNb >= 1) | ||||
{ | { | ||||
if (unexecutedAttacks.Rush === 0) | if (unexecutedAttacks.Rush === 0) | ||||
{ | { | ||||
// we have a barracks and we want to rush, rush. | // we have a barracks and we want to rush, rush. | ||||
let data = { "targetSize": this.rushSize[this.rushNumber] }; | let data = { "targetSize": this.rushSize[this.rushNumber] }; | ||||
let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "Rush", data); | let attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, "Rush", data); | ||||
if (!attackPlan.failed) | if (!attackPlan.failed) | ||||
{ | { | ||||
if (this.Config.debug > 1) | if (this.Config.debug > 1) | ||||
API3.warn("Military Manager: Rushing plan " + this.totalNumber + " with maxRushes " + this.maxRushes); | API3.warn("Military Manager: Rushing plan " + this.totalNumber + " with maxRushes " + this.maxRushes); | ||||
this.totalNumber++; | this.totalNumber++; | ||||
attackPlan.init(gameState); | attackPlan.init(gameState); | ||||
this.upcomingAttacks.Rush.push(attackPlan); | this.upcomingAttacks.Rush.push(attackPlan); | ||||
} | } | ||||
this.rushNumber++; | this.rushNumber++; | ||||
} | } | ||||
} | } | ||||
else if (unexecutedAttacks.Attack == 0 && unexecutedAttacks.HugeAttack == 0 && | else if (unexecutedAttacks.Attack == 0 && unexecutedAttacks.HugeAttack == 0 && | ||||
this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length < Math.min(2, 1 + Math.round(gameState.getPopulationMax()/100)) && | this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length < Math.min(2, 1 + Math.round(gameState.getPopulationMax()/100)) && | ||||
(this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length == 0 || gameState.getPopulationMax() - gameState.getPopulation() > 12)) | (this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length == 0 || gameState.getPopulationMax() - gameState.getPopulation() > 12)) | ||||
{ | { | ||||
if (barracksNb >= 1 && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.getPhaseName(2))) || | if (barracksNb >= 1 && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.getPhaseName(2))) || | ||||
!gameState.ai.HQ.baseManagers[1]) // if we have no base ... nothing else to do than attack | !gameState.ai.HQ.baseManagers[1]) // if we have no base ... nothing else to do than attack | ||||
{ | { | ||||
let type = this.attackNumber < 2 || this.startedAttacks.HugeAttack.length > 0 ? "Attack" : "HugeAttack"; | let type = this.attackNumber < 2 || this.startedAttacks.HugeAttack.length > 0 ? "Attack" : "HugeAttack"; | ||||
let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, type); | let attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, type); | ||||
if (attackPlan.failed) | if (attackPlan.failed) | ||||
this.attackPlansEncounteredWater = true; // hack | this.attackPlansEncounteredWater = true; // hack | ||||
else | else | ||||
{ | { | ||||
if (this.Config.debug > 1) | if (this.Config.debug > 1) | ||||
API3.warn("Military Manager: Creating the plan " + type + " " + this.totalNumber); | API3.warn("Military Manager: Creating the plan " + type + " " + this.totalNumber); | ||||
this.totalNumber++; | this.totalNumber++; | ||||
attackPlan.init(gameState); | attackPlan.init(gameState); | ||||
Show All 19 Lines | if (target) // prepare a raid against this target | ||||
this.raidTargetEntity(gameState, target); | this.raidTargetEntity(gameState, target); | ||||
} | } | ||||
// Check if we have some unused ranged siege unit which could do something useful while waiting | // Check if we have some unused ranged siege unit which could do something useful while waiting | ||||
if (this.Config.difficulty > 1 && gameState.ai.playedTurn % 5 == 0) | if (this.Config.difficulty > 1 && gameState.ai.playedTurn % 5 == 0) | ||||
this.assignBombers(gameState); | this.assignBombers(gameState); | ||||
}; | }; | ||||
m.AttackManager.prototype.getPlan = function(planName) | PETRA.AttackManager.prototype.getPlan = function(planName) | ||||
{ | { | ||||
for (let attackType in this.upcomingAttacks) | for (let attackType in this.upcomingAttacks) | ||||
{ | { | ||||
for (let attack of this.upcomingAttacks[attackType]) | for (let attack of this.upcomingAttacks[attackType]) | ||||
if (attack.getName() == planName) | if (attack.getName() == planName) | ||||
return attack; | return attack; | ||||
} | } | ||||
for (let attackType in this.startedAttacks) | for (let attackType in this.startedAttacks) | ||||
{ | { | ||||
for (let attack of this.startedAttacks[attackType]) | for (let attack of this.startedAttacks[attackType]) | ||||
if (attack.getName() == planName) | if (attack.getName() == planName) | ||||
return attack; | return attack; | ||||
} | } | ||||
return undefined; | return undefined; | ||||
}; | }; | ||||
m.AttackManager.prototype.pausePlan = function(planName) | PETRA.AttackManager.prototype.pausePlan = function(planName) | ||||
{ | { | ||||
let attack = this.getPlan(planName); | let attack = this.getPlan(planName); | ||||
if (attack) | if (attack) | ||||
attack.setPaused(true); | attack.setPaused(true); | ||||
}; | }; | ||||
m.AttackManager.prototype.unpausePlan = function(planName) | PETRA.AttackManager.prototype.unpausePlan = function(planName) | ||||
{ | { | ||||
let attack = this.getPlan(planName); | let attack = this.getPlan(planName); | ||||
if (attack) | if (attack) | ||||
attack.setPaused(false); | attack.setPaused(false); | ||||
}; | }; | ||||
m.AttackManager.prototype.pauseAllPlans = function() | PETRA.AttackManager.prototype.pauseAllPlans = function() | ||||
{ | { | ||||
for (let attackType in this.upcomingAttacks) | for (let attackType in this.upcomingAttacks) | ||||
for (let attack of this.upcomingAttacks[attackType]) | for (let attack of this.upcomingAttacks[attackType]) | ||||
attack.setPaused(true); | attack.setPaused(true); | ||||
for (let attackType in this.startedAttacks) | for (let attackType in this.startedAttacks) | ||||
for (let attack of this.startedAttacks[attackType]) | for (let attack of this.startedAttacks[attackType]) | ||||
attack.setPaused(true); | attack.setPaused(true); | ||||
}; | }; | ||||
m.AttackManager.prototype.unpauseAllPlans = function() | PETRA.AttackManager.prototype.unpauseAllPlans = function() | ||||
{ | { | ||||
for (let attackType in this.upcomingAttacks) | for (let attackType in this.upcomingAttacks) | ||||
for (let attack of this.upcomingAttacks[attackType]) | for (let attack of this.upcomingAttacks[attackType]) | ||||
attack.setPaused(false); | attack.setPaused(false); | ||||
for (let attackType in this.startedAttacks) | for (let attackType in this.startedAttacks) | ||||
for (let attack of this.startedAttacks[attackType]) | for (let attack of this.startedAttacks[attackType]) | ||||
attack.setPaused(false); | attack.setPaused(false); | ||||
}; | }; | ||||
m.AttackManager.prototype.getAttackInPreparation = function(type) | PETRA.AttackManager.prototype.getAttackInPreparation = function(type) | ||||
{ | { | ||||
return this.upcomingAttacks[type].length ? this.upcomingAttacks[type][0] : undefined; | return this.upcomingAttacks[type].length ? this.upcomingAttacks[type][0] : undefined; | ||||
}; | }; | ||||
/** | /** | ||||
* Determine which player should be attacked: when called when starting the attack, | * Determine which player should be attacked: when called when starting the attack, | ||||
* attack.targetPlayer is undefined and in that case, we keep track of the chosen target | * attack.targetPlayer is undefined and in that case, we keep track of the chosen target | ||||
* for future attacks. | * for future attacks. | ||||
*/ | */ | ||||
m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack) | PETRA.AttackManager.prototype.getEnemyPlayer = function(gameState, attack) | ||||
{ | { | ||||
let enemyPlayer; | let enemyPlayer; | ||||
// First check if there is a preferred enemy based on our victory conditions. | // First check if there is a preferred enemy based on our victory conditions. | ||||
// If both wonder and relic, choose randomly between them TODO should combine decisions | // If both wonder and relic, choose randomly between them TODO should combine decisions | ||||
if (gameState.getVictoryConditions().has("wonder")) | if (gameState.getVictoryConditions().has("wonder")) | ||||
enemyPlayer = this.getWonderEnemyPlayer(gameState, attack); | enemyPlayer = this.getWonderEnemyPlayer(gameState, attack); | ||||
Show All 39 Lines | if (attack.type != "HugeAttack") | ||||
let distmin; | let distmin; | ||||
let ccmin; | let ccmin; | ||||
let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); | let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); | ||||
for (let ourcc of ccEnts.values()) | for (let ourcc of ccEnts.values()) | ||||
{ | { | ||||
if (ourcc.owner() != PlayerID) | if (ourcc.owner() != PlayerID) | ||||
continue; | continue; | ||||
let ourPos = ourcc.position(); | let ourPos = ourcc.position(); | ||||
let access = m.getLandAccess(gameState, ourcc); | let access = PETRA.getLandAccess(gameState, ourcc); | ||||
for (let enemycc of ccEnts.values()) | for (let enemycc of ccEnts.values()) | ||||
{ | { | ||||
if (veto[enemycc.owner()]) | if (veto[enemycc.owner()]) | ||||
continue; | continue; | ||||
if (!gameState.isPlayerEnemy(enemycc.owner())) | if (!gameState.isPlayerEnemy(enemycc.owner())) | ||||
continue; | continue; | ||||
if (access != m.getLandAccess(gameState, enemycc)) | if (access != PETRA.getLandAccess(gameState, enemycc)) | ||||
continue; | continue; | ||||
let dist = API3.SquareVectorDistance(ourPos, enemycc.position()); | let dist = API3.SquareVectorDistance(ourPos, enemycc.position()); | ||||
if (distmin && dist > distmin) | if (distmin && dist > distmin) | ||||
continue; | continue; | ||||
ccmin = enemycc; | ccmin = enemycc; | ||||
distmin = dist; | distmin = dist; | ||||
} | } | ||||
} | } | ||||
Show All 34 Lines | if (attack.targetPlayer === undefined) | ||||
this.currentEnemyPlayer = enemyPlayer; | this.currentEnemyPlayer = enemyPlayer; | ||||
return enemyPlayer; | return enemyPlayer; | ||||
}; | }; | ||||
/** | /** | ||||
* Target the player with the most advanced wonder. | * Target the player with the most advanced wonder. | ||||
* TODO currently the first built wonder is kept, should chek on the minimum wonderDuration left instead. | * TODO currently the first built wonder is kept, should chek on the minimum wonderDuration left instead. | ||||
*/ | */ | ||||
m.AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack) | PETRA.AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack) | ||||
{ | { | ||||
let enemyPlayer; | let enemyPlayer; | ||||
let enemyWonder; | let enemyWonder; | ||||
let moreAdvanced; | let moreAdvanced; | ||||
for (let wonder of gameState.getEnemyStructures().filter(API3.Filters.byClass("Wonder")).values()) | for (let wonder of gameState.getEnemyStructures().filter(API3.Filters.byClass("Wonder")).values()) | ||||
{ | { | ||||
if (wonder.owner() == 0) | if (wonder.owner() == 0) | ||||
continue; | continue; | ||||
Show All 15 Lines | if (attack.targetPlayer === undefined) | ||||
this.currentEnemyPlayer = enemyPlayer; | this.currentEnemyPlayer = enemyPlayer; | ||||
} | } | ||||
return enemyPlayer; | return enemyPlayer; | ||||
}; | }; | ||||
/** | /** | ||||
* Target the player with the most relics (including gaia). | * Target the player with the most relics (including gaia). | ||||
*/ | */ | ||||
m.AttackManager.prototype.getRelicEnemyPlayer = function(gameState, attack) | PETRA.AttackManager.prototype.getRelicEnemyPlayer = function(gameState, attack) | ||||
{ | { | ||||
let enemyPlayer; | let enemyPlayer; | ||||
let allRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")); | let allRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")); | ||||
let maxRelicsOwned = 0; | let maxRelicsOwned = 0; | ||||
for (let i = 0; i < gameState.sharedScript.playersData.length; ++i) | for (let i = 0; i < gameState.sharedScript.playersData.length; ++i) | ||||
{ | { | ||||
if (!gameState.isPlayerEnemy(i) || this.defeated[i] || | if (!gameState.isPlayerEnemy(i) || this.defeated[i] || | ||||
i == 0 && !gameState.ai.HQ.victoryManager.tryCaptureGaiaRelic) | i == 0 && !gameState.ai.HQ.victoryManager.tryCaptureGaiaRelic) | ||||
Show All 11 Lines | if (attack.targetPlayer === undefined) | ||||
this.currentEnemyPlayer = enemyPlayer; | this.currentEnemyPlayer = enemyPlayer; | ||||
if (enemyPlayer == 0) | if (enemyPlayer == 0) | ||||
gameState.ai.HQ.victoryManager.resetCaptureGaiaRelic(gameState); | gameState.ai.HQ.victoryManager.resetCaptureGaiaRelic(gameState); | ||||
} | } | ||||
return enemyPlayer; | return enemyPlayer; | ||||
}; | }; | ||||
/** f.e. if we have changed diplomacy with another player. */ | /** f.e. if we have changed diplomacy with another player. */ | ||||
m.AttackManager.prototype.cancelAttacksAgainstPlayer = function(gameState, player) | PETRA.AttackManager.prototype.cancelAttacksAgainstPlayer = function(gameState, player) | ||||
{ | { | ||||
for (let attackType in this.upcomingAttacks) | for (let attackType in this.upcomingAttacks) | ||||
for (let attack of this.upcomingAttacks[attackType]) | for (let attack of this.upcomingAttacks[attackType]) | ||||
if (attack.targetPlayer === player) | if (attack.targetPlayer === player) | ||||
attack.targetPlayer = undefined; | attack.targetPlayer = undefined; | ||||
for (let attackType in this.startedAttacks) | for (let attackType in this.startedAttacks) | ||||
for (let i = 0; i < this.startedAttacks[attackType].length; ++i) | for (let i = 0; i < this.startedAttacks[attackType].length; ++i) | ||||
{ | { | ||||
let attack = this.startedAttacks[attackType][i]; | let attack = this.startedAttacks[attackType][i]; | ||||
if (attack.targetPlayer === player) | if (attack.targetPlayer === player) | ||||
{ | { | ||||
attack.Abort(gameState); | attack.Abort(gameState); | ||||
this.startedAttacks[attackType].splice(i--, 1); | this.startedAttacks[attackType].splice(i--, 1); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
m.AttackManager.prototype.raidTargetEntity = function(gameState, ent) | PETRA.AttackManager.prototype.raidTargetEntity = function(gameState, ent) | ||||
{ | { | ||||
let data = { "target": ent }; | let data = { "target": ent }; | ||||
let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "Raid", data); | let attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, "Raid", data); | ||||
if (attackPlan.failed) | if (attackPlan.failed) | ||||
return null; | return null; | ||||
if (this.Config.debug > 1) | if (this.Config.debug > 1) | ||||
API3.warn("Military Manager: Raiding plan " + this.totalNumber); | API3.warn("Military Manager: Raiding plan " + this.totalNumber); | ||||
this.raidNumber++; | this.raidNumber++; | ||||
this.totalNumber++; | this.totalNumber++; | ||||
attackPlan.init(gameState); | attackPlan.init(gameState); | ||||
this.upcomingAttacks.Raid.push(attackPlan); | this.upcomingAttacks.Raid.push(attackPlan); | ||||
return attackPlan; | return attackPlan; | ||||
}; | }; | ||||
/** | /** | ||||
* Return the number of units from any of our attacking armies around this position | * Return the number of units from any of our attacking armies around this position | ||||
*/ | */ | ||||
m.AttackManager.prototype.numAttackingUnitsAround = function(pos, dist) | PETRA.AttackManager.prototype.numAttackingUnitsAround = function(pos, dist) | ||||
{ | { | ||||
let num = 0; | let num = 0; | ||||
for (let attackType in this.startedAttacks) | for (let attackType in this.startedAttacks) | ||||
for (let attack of this.startedAttacks[attackType]) | for (let attack of this.startedAttacks[attackType]) | ||||
{ | { | ||||
if (!attack.position) // this attack may be inside a transport | if (!attack.position) // this attack may be inside a transport | ||||
continue; | continue; | ||||
if (API3.SquareVectorDistance(pos, attack.position) < dist*dist) | if (API3.SquareVectorDistance(pos, attack.position) < dist*dist) | ||||
num += attack.unitCollection.length; | num += attack.unitCollection.length; | ||||
} | } | ||||
return num; | return num; | ||||
}; | }; | ||||
/** | /** | ||||
* Switch defense armies into an attack one against the given target | * Switch defense armies into an attack one against the given target | ||||
* data.range: transform all defense armies inside range of the target into a new attack | * data.range: transform all defense armies inside range of the target into a new attack | ||||
* data.armyID: transform only the defense army ID into a new attack | * 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 | * data.uniqueTarget: the attack will stop when the target is destroyed or captured | ||||
*/ | */ | ||||
m.AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data) | PETRA.AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data) | ||||
{ | { | ||||
if (!target || !target.position()) | if (!target || !target.position()) | ||||
return false; | return false; | ||||
if (!data.range && !data.armyID) | if (!data.range && !data.armyID) | ||||
{ | { | ||||
API3.warn(" attackManager.switchDefenseToAttack inconsistent data " + uneval(data)); | API3.warn(" attackManager.switchDefenseToAttack inconsistent data " + uneval(data)); | ||||
return false; | return false; | ||||
} | } | ||||
let attackData = data.uniqueTarget ? { "uniqueTargetId": target.id() } : undefined; | let attackData = data.uniqueTarget ? { "uniqueTargetId": target.id() } : undefined; | ||||
let pos = target.position(); | let pos = target.position(); | ||||
let attackType = "Attack"; | let attackType = "Attack"; | ||||
let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, attackType, attackData); | let attackPlan = new PETRA.AttackPlan(gameState, this.Config, this.totalNumber, attackType, attackData); | ||||
if (attackPlan.failed) | if (attackPlan.failed) | ||||
return false; | return false; | ||||
this.totalNumber++; | this.totalNumber++; | ||||
attackPlan.init(gameState); | attackPlan.init(gameState); | ||||
this.startedAttacks[attackType].push(attackPlan); | this.startedAttacks[attackType].push(attackPlan); | ||||
let targetAccess = m.getLandAccess(gameState, target); | let targetAccess = PETRA.getLandAccess(gameState, target); | ||||
for (let army of gameState.ai.HQ.defenseManager.armies) | for (let army of gameState.ai.HQ.defenseManager.armies) | ||||
{ | { | ||||
if (data.range) | if (data.range) | ||||
{ | { | ||||
army.recalculatePosition(gameState); | army.recalculatePosition(gameState); | ||||
if (API3.SquareVectorDistance(pos, army.foePosition) > data.range * data.range) | if (API3.SquareVectorDistance(pos, army.foePosition) > data.range * data.range) | ||||
continue; | continue; | ||||
} | } | ||||
else if (army.ID != +data.armyID) | else if (army.ID != +data.armyID) | ||||
continue; | continue; | ||||
while (army.foeEntities.length > 0) | while (army.foeEntities.length > 0) | ||||
army.removeFoe(gameState, army.foeEntities[0]); | army.removeFoe(gameState, army.foeEntities[0]); | ||||
while (army.ownEntities.length > 0) | while (army.ownEntities.length > 0) | ||||
{ | { | ||||
let unitId = army.ownEntities[0]; | let unitId = army.ownEntities[0]; | ||||
army.removeOwn(gameState, unitId); | army.removeOwn(gameState, unitId); | ||||
let unit = gameState.getEntityById(unitId); | let unit = gameState.getEntityById(unitId); | ||||
let accessOk = unit.getMetadata(PlayerID, "transport") !== undefined || | let accessOk = unit.getMetadata(PlayerID, "transport") !== undefined || | ||||
unit.position() && m.getLandAccess(gameState, unit) == targetAccess; | unit.position() && PETRA.getLandAccess(gameState, unit) == targetAccess; | ||||
if (unit && accessOk && attackPlan.isAvailableUnit(gameState, unit)) | if (unit && accessOk && attackPlan.isAvailableUnit(gameState, unit)) | ||||
{ | { | ||||
unit.setMetadata(PlayerID, "plan", attackPlan.name); | unit.setMetadata(PlayerID, "plan", attackPlan.name); | ||||
unit.setMetadata(PlayerID, "role", "attack"); | unit.setMetadata(PlayerID, "role", "attack"); | ||||
attackPlan.unitCollection.updateEnt(unit); | attackPlan.unitCollection.updateEnt(unit); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!attackPlan.unitCollection.hasEntities()) | if (!attackPlan.unitCollection.hasEntities()) | ||||
{ | { | ||||
attackPlan.Abort(gameState); | attackPlan.Abort(gameState); | ||||
return false; | return false; | ||||
} | } | ||||
for (let unit of attackPlan.unitCollection.values()) | for (let unit of attackPlan.unitCollection.values()) | ||||
unit.setMetadata(PlayerID, "role", "attack"); | unit.setMetadata(PlayerID, "role", "attack"); | ||||
attackPlan.targetPlayer = target.owner(); | attackPlan.targetPlayer = target.owner(); | ||||
attackPlan.targetPos = pos; | attackPlan.targetPos = pos; | ||||
attackPlan.target = target; | attackPlan.target = target; | ||||
attackPlan.state = "arrived"; | attackPlan.state = "arrived"; | ||||
return true; | return true; | ||||
}; | }; | ||||
m.AttackManager.prototype.Serialize = function() | PETRA.AttackManager.prototype.Serialize = function() | ||||
{ | { | ||||
let properties = { | let properties = { | ||||
"totalNumber": this.totalNumber, | "totalNumber": this.totalNumber, | ||||
"attackNumber": this.attackNumber, | "attackNumber": this.attackNumber, | ||||
"rushNumber": this.rushNumber, | "rushNumber": this.rushNumber, | ||||
"raidNumber": this.raidNumber, | "raidNumber": this.raidNumber, | ||||
"debugTime": this.debugTime, | "debugTime": this.debugTime, | ||||
"maxRushes": this.maxRushes, | "maxRushes": this.maxRushes, | ||||
Show All 16 Lines | for (let key in this.startedAttacks) | ||||
startedAttacks[key] = []; | startedAttacks[key] = []; | ||||
for (let attack of this.startedAttacks[key]) | for (let attack of this.startedAttacks[key]) | ||||
startedAttacks[key].push(attack.Serialize()); | startedAttacks[key].push(attack.Serialize()); | ||||
} | } | ||||
return { "properties": properties, "upcomingAttacks": upcomingAttacks, "startedAttacks": startedAttacks }; | return { "properties": properties, "upcomingAttacks": upcomingAttacks, "startedAttacks": startedAttacks }; | ||||
}; | }; | ||||
m.AttackManager.prototype.Deserialize = function(gameState, data) | PETRA.AttackManager.prototype.Deserialize = function(gameState, data) | ||||
{ | { | ||||
for (let key in data.properties) | for (let key in data.properties) | ||||
this[key] = data.properties[key]; | this[key] = data.properties[key]; | ||||
this.upcomingAttacks = {}; | this.upcomingAttacks = {}; | ||||
for (let key in data.upcomingAttacks) | for (let key in data.upcomingAttacks) | ||||
{ | { | ||||
this.upcomingAttacks[key] = []; | this.upcomingAttacks[key] = []; | ||||
for (let dataAttack of data.upcomingAttacks[key]) | for (let dataAttack of data.upcomingAttacks[key]) | ||||
{ | { | ||||
let attack = new m.AttackPlan(gameState, this.Config, dataAttack.properties.name); | let attack = new PETRA.AttackPlan(gameState, this.Config, dataAttack.properties.name); | ||||
attack.Deserialize(gameState, dataAttack); | attack.Deserialize(gameState, dataAttack); | ||||
attack.init(gameState); | attack.init(gameState); | ||||
this.upcomingAttacks[key].push(attack); | this.upcomingAttacks[key].push(attack); | ||||
} | } | ||||
} | } | ||||
this.startedAttacks = {}; | this.startedAttacks = {}; | ||||
for (let key in data.startedAttacks) | for (let key in data.startedAttacks) | ||||
{ | { | ||||
this.startedAttacks[key] = []; | this.startedAttacks[key] = []; | ||||
for (let dataAttack of data.startedAttacks[key]) | for (let dataAttack of data.startedAttacks[key]) | ||||
{ | { | ||||
let attack = new m.AttackPlan(gameState, this.Config, dataAttack.properties.name); | let attack = new PETRA.AttackPlan(gameState, this.Config, dataAttack.properties.name); | ||||
attack.Deserialize(gameState, dataAttack); | attack.Deserialize(gameState, dataAttack); | ||||
attack.init(gameState); | attack.init(gameState); | ||||
this.startedAttacks[key].push(attack); | this.startedAttacks[key].push(attack); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
return m; | |||||
}(PETRA); |
Wildfire Games · Phabricator