Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/ai/petra/attackPlan.js
Show First 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | PETRA.AttackPlan = function(gameState, Config, uniqueID, type, data) | ||||
this.unitStat = {}; | this.unitStat = {}; | ||||
// neededShips is the minimal number of ships which should be available for transport | // neededShips is the minimal number of ships which should be available for transport | ||||
if (type == "Rush") | if (type == "Rush") | ||||
{ | { | ||||
priority = 250; | priority = 250; | ||||
this.unitStat.Infantry = { "priority": 1, "minSize": 10, "targetSize": 20, "batchSize": 2, "classes": ["Infantry"], | this.unitStat.Infantry = { "priority": 1, "minSize": 10, "targetSize": 20, "batchSize": 2, "classes": ["Infantry"], | ||||
"interests": [["strength", 1], ["costsResource", 0.5, "stone"], ["costsResource", 0.6, "metal"]] }; | "interests": [["strength", 1], ["costsResource", 0.5, "stone"], ["costsResource", 0.6, "metal"]] }; | ||||
this.unitStat.Cavalry = { "priority": 1, "minSize": 2, "targetSize": 4, "batchSize": 2, "classes": ["Cavalry", "CitizenSoldier"], | this.unitStat.FastMoving = { "priority": 1, "minSize": 2, "targetSize": 4, "batchSize": 2, "classes": ["FastMoving", "CitizenSoldier"], | ||||
"interests": [["strength", 1]] }; | "interests": [["strength", 1]] }; | ||||
if (data && data.targetSize) | if (data && data.targetSize) | ||||
this.unitStat.Infantry.targetSize = data.targetSize; | this.unitStat.Infantry.targetSize = data.targetSize; | ||||
this.neededShips = 1; | this.neededShips = 1; | ||||
} | } | ||||
else if (type == "Raid") | else if (type == "Raid") | ||||
{ | { | ||||
priority = 150; | priority = 150; | ||||
this.unitStat.Cavalry = { "priority": 1, "minSize": 3, "targetSize": 4, "batchSize": 2, "classes": ["Cavalry", "CitizenSoldier"], | this.unitStat.FastMoving = { "priority": 1, "minSize": 3, "targetSize": 4, "batchSize": 2, "classes": ["FastMoving", "CitizenSoldier"], | ||||
"interests": [ ["strength", 1] ] }; | "interests": [ ["strength", 1] ] }; | ||||
this.neededShips = 1; | this.neededShips = 1; | ||||
} | } | ||||
else if (type == "HugeAttack") | else if (type == "HugeAttack") | ||||
{ | { | ||||
priority = 90; | priority = 90; | ||||
// basically we want a mix of citizen soldiers so our barracks have a purpose, and champion units. | // basically we want a mix of citizen soldiers so our barracks have a purpose, and champion units. | ||||
this.unitStat.RangedInfantry = { "priority": 0.7, "minSize": 5, "targetSize": 20, "batchSize": 5, "classes": ["Infantry", "Ranged", "CitizenSoldier"], | this.unitStat.RangedInfantry = { "priority": 0.7, "minSize": 5, "targetSize": 20, "batchSize": 5, "classes": ["Infantry", "Ranged", "CitizenSoldier"], | ||||
"interests": [["strength", 3]] }; | "interests": [["strength", 3]] }; | ||||
this.unitStat.MeleeInfantry = { "priority": 0.7, "minSize": 5, "targetSize": 20, "batchSize": 5, "classes": ["Infantry", "Melee", "CitizenSoldier"], | this.unitStat.MeleeInfantry = { "priority": 0.7, "minSize": 5, "targetSize": 20, "batchSize": 5, "classes": ["Infantry", "Melee", "CitizenSoldier"], | ||||
"interests": [["strength", 3]] }; | "interests": [["strength", 3]] }; | ||||
this.unitStat.ChampRangedInfantry = { "priority": 1, "minSize": 3, "targetSize": 18, "batchSize": 3, "classes": ["Infantry", "Ranged", "Champion"], | this.unitStat.ChampRangedInfantry = { "priority": 1, "minSize": 3, "targetSize": 18, "batchSize": 3, "classes": ["Infantry", "Ranged", "Champion"], | ||||
"interests": [["strength", 3]] }; | "interests": [["strength", 3]] }; | ||||
this.unitStat.ChampMeleeInfantry = { "priority": 1, "minSize": 3, "targetSize": 18, "batchSize": 3, "classes": ["Infantry", "Melee", "Champion"], | this.unitStat.ChampMeleeInfantry = { "priority": 1, "minSize": 3, "targetSize": 18, "batchSize": 3, "classes": ["Infantry", "Melee", "Champion"], | ||||
"interests": [["strength", 3]] }; | "interests": [["strength", 3]] }; | ||||
this.unitStat.RangedCavalry = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["Cavalry", "Ranged", "CitizenSoldier"], | this.unitStat.RangedFastMoving = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["FastMoving", "Ranged", "CitizenSoldier"], | ||||
"interests": [["strength", 2]] }; | "interests": [["strength", 2]] }; | ||||
this.unitStat.MeleeCavalry = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["Cavalry", "Melee", "CitizenSoldier"], | this.unitStat.MeleeFastMoving = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["FastMoving", "Melee", "CitizenSoldier"], | ||||
"interests": [["strength", 2]] }; | "interests": [["strength", 2]] }; | ||||
this.unitStat.ChampRangedCavalry = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["Cavalry", "Ranged", "Champion"], | this.unitStat.ChampRangedFastMoving = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["FastMoving", "Ranged", "Champion"], | ||||
"interests": [["strength", 3]] }; | "interests": [["strength", 3]] }; | ||||
this.unitStat.ChampMeleeCavalry = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["Cavalry", "Melee", "Champion"], | this.unitStat.ChampMeleeFastMoving = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["FastMoving", "Melee", "Champion"], | ||||
"interests": [["strength", 2]] }; | "interests": [["strength", 2]] }; | ||||
this.unitStat.Hero = { "priority": 1, "minSize": 0, "targetSize": 1, "batchSize": 1, "classes": ["Hero"], | this.unitStat.Hero = { "priority": 1, "minSize": 0, "targetSize": 1, "batchSize": 1, "classes": ["Hero"], | ||||
"interests": [["strength", 2]] }; | "interests": [["strength", 2]] }; | ||||
this.neededShips = 5; | this.neededShips = 5; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
priority = 70; | priority = 70; | ||||
this.unitStat.RangedInfantry = { "priority": 1, "minSize": 6, "targetSize": 16, "batchSize": 3, "classes": ["Infantry", "Ranged"], | this.unitStat.RangedInfantry = { "priority": 1, "minSize": 6, "targetSize": 16, "batchSize": 3, "classes": ["Infantry", "Ranged"], | ||||
"interests": [["canGather", 1], ["strength", 1.6], ["costsResource", 0.3, "stone"], ["costsResource", 0.3, "metal"]] }; | "interests": [["canGather", 1], ["strength", 1.6], ["costsResource", 0.3, "stone"], ["costsResource", 0.3, "metal"]] }; | ||||
this.unitStat.MeleeInfantry = { "priority": 1, "minSize": 6, "targetSize": 16, "batchSize": 3, "classes": ["Infantry", "Melee"], | this.unitStat.MeleeInfantry = { "priority": 1, "minSize": 6, "targetSize": 16, "batchSize": 3, "classes": ["Infantry", "Melee"], | ||||
"interests": [["canGather", 1], ["strength", 1.6], ["costsResource", 0.3, "stone"], ["costsResource", 0.3, "metal"]] }; | "interests": [["canGather", 1], ["strength", 1.6], ["costsResource", 0.3, "stone"], ["costsResource", 0.3, "metal"]] }; | ||||
this.unitStat.Cavalry = { "priority": 1, "minSize": 2, "targetSize": 6, "batchSize": 2, "classes": ["Cavalry", "CitizenSoldier"], | this.unitStat.FastMoving = { "priority": 1, "minSize": 2, "targetSize": 6, "batchSize": 2, "classes": ["FastMoving", "CitizenSoldier"], | ||||
"interests": [["strength", 1]] }; | "interests": [["strength", 1]] }; | ||||
this.neededShips = 3; | this.neededShips = 3; | ||||
} | } | ||||
// Put some randomness on the attack size | // Put some randomness on the attack size | ||||
let variation = randFloat(0.8, 1.2); | let variation = randFloat(0.8, 1.2); | ||||
// and lower priority and smaller sizes for easier difficulty levels | // and lower priority and smaller sizes for easier difficulty levels | ||||
if (this.Config.difficulty < 2) | if (this.Config.difficulty < 2) | ||||
▲ Show 20 Lines • Show All 255 Lines • ▼ Show 20 Lines | PETRA.AttackPlan.prototype.updatePreparation = function(gameState) | ||||
// if we need a transport, wait for some transport ships | // if we need a transport, wait for some transport ships | ||||
if (this.overseas && !gameState.ai.HQ.navalManager.seaTransportShips[this.overseas].length) | if (this.overseas && !gameState.ai.HQ.navalManager.seaTransportShips[this.overseas].length) | ||||
return 1; | return 1; | ||||
if (this.type != "Raid" || !this.forced) // Forced Raids have special purposes (as relic capture) | if (this.type != "Raid" || !this.forced) // Forced Raids have special purposes (as relic capture) | ||||
this.assignUnits(gameState); | this.assignUnits(gameState); | ||||
if (this.type != "Raid" && gameState.ai.HQ.attackManager.getAttackInPreparation("Raid") !== undefined) | if (this.type != "Raid" && gameState.ai.HQ.attackManager.getAttackInPreparation("Raid") !== undefined) | ||||
this.reassignCavUnit(gameState); // reassign some cav (if any) to fasten raid preparations | this.reassignFastUnit(gameState); // reassign some fast units (if any) to fasten raid preparations | ||||
// Fasten the end game. | // Fasten the end game. | ||||
if (gameState.ai.playedTurn % 5 == 0 && this.hasSiegeUnits()) | if (gameState.ai.playedTurn % 5 == 0 && this.hasSiegeUnits()) | ||||
{ | { | ||||
let totEnemies = 0; | let totEnemies = 0; | ||||
let hasEnemies = false; | let hasEnemies = false; | ||||
for (let i = 1; i < gameState.sharedScript.playersData.length; ++i) | for (let i = 1; i < gameState.sharedScript.playersData.length; ++i) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 203 Lines • ▼ Show 20 Lines | if (firstOrder[0] < firstOrder[3].targetSize) | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
PETRA.AttackPlan.prototype.assignUnits = function(gameState) | PETRA.AttackPlan.prototype.assignUnits = function(gameState) | ||||
{ | { | ||||
let plan = this.name; | let plan = this.name; | ||||
let added = false; | let added = false; | ||||
// If we can not build units, assign all available except those affected to allied defense to the current attack | // If we can not build units, assign all available except those affected to allied defense to the current attack. | ||||
if (!this.canBuildUnits) | if (!this.canBuildUnits) | ||||
{ | { | ||||
for (let ent of gameState.getOwnUnits().values()) | for (let ent of gameState.getOwnUnits().values()) | ||||
{ | { | ||||
if (ent.getMetadata(PlayerID, "allied") || !this.isAvailableUnit(gameState, ent)) | if (ent.getMetadata(PlayerID, "allied") || !this.isAvailableUnit(gameState, ent)) | ||||
continue; | continue; | ||||
ent.setMetadata(PlayerID, "plan", plan); | ent.setMetadata(PlayerID, "plan", plan); | ||||
this.unitCollection.updateEnt(ent); | this.unitCollection.updateEnt(ent); | ||||
added = true; | added = true; | ||||
} | } | ||||
return added; | return added; | ||||
} | } | ||||
if (this.type == "Raid") | if (this.type == "Raid") | ||||
{ | { | ||||
// Raid are fast cavalry attack: assign all cav except some for hunting | // Raids are quick attacks: assign all FastMoving soldiers except some for hunting. | ||||
let num = 0; | let num = 0; | ||||
for (let ent of gameState.getOwnUnits().values()) | for (let ent of gameState.getOwnUnits().values()) | ||||
{ | { | ||||
if (!ent.hasClass("Cavalry") || !this.isAvailableUnit(gameState, ent)) | if (!ent.hasClass("FastMoving") || !this.isAvailableUnit(gameState, ent)) | ||||
continue; | continue; | ||||
if (num++ < 2) | if (num++ < 2) | ||||
continue; | continue; | ||||
ent.setMetadata(PlayerID, "plan", plan); | ent.setMetadata(PlayerID, "plan", plan); | ||||
this.unitCollection.updateEnt(ent); | this.unitCollection.updateEnt(ent); | ||||
added = true; | added = true; | ||||
} | } | ||||
return added; | return added; | ||||
} | } | ||||
// Assign all units without specific role | // Assign all units without specific role. | ||||
for (let ent of gameState.getOwnEntitiesByRole(undefined, true).values()) | for (let ent of gameState.getOwnEntitiesByRole(undefined, true).values()) | ||||
{ | { | ||||
if (!ent.hasClass("Unit") || !this.isAvailableUnit(gameState, ent)) | if (!ent.hasClass("Unit") || !this.isAvailableUnit(gameState, ent)) | ||||
continue; | continue; | ||||
if (ent.hasClass("Ship") || ent.hasClass("Support") || ent.attackTypes() === undefined) | if (ent.hasClass("Ship") || ent.hasClass("Support") || ent.attackTypes() === undefined) | ||||
continue; | continue; | ||||
ent.setMetadata(PlayerID, "plan", plan); | ent.setMetadata(PlayerID, "plan", plan); | ||||
this.unitCollection.updateEnt(ent); | this.unitCollection.updateEnt(ent); | ||||
added = true; | added = true; | ||||
} | } | ||||
// Add units previously in a plan, but which left it because needed for defense or attack finished | // Add units previously in a plan, but which left it because needed for defense or attack finished. | ||||
for (let ent of gameState.ai.HQ.attackManager.outOfPlan.values()) | for (let ent of gameState.ai.HQ.attackManager.outOfPlan.values()) | ||||
{ | { | ||||
if (!this.isAvailableUnit(gameState, ent)) | if (!this.isAvailableUnit(gameState, ent)) | ||||
continue; | continue; | ||||
ent.setMetadata(PlayerID, "plan", plan); | ent.setMetadata(PlayerID, "plan", plan); | ||||
this.unitCollection.updateEnt(ent); | this.unitCollection.updateEnt(ent); | ||||
added = true; | added = true; | ||||
} | } | ||||
Show All 37 Lines | PETRA.AttackPlan.prototype.isAvailableUnit = function(gameState, ent) | ||||
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1 || | if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1 || | ||||
ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) | ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) | ||||
return false; | return false; | ||||
if (gameState.ai.HQ.victoryManager.criticalEnts.has(ent.id()) && (this.overseas || ent.healthLevel() < 0.8)) | if (gameState.ai.HQ.victoryManager.criticalEnts.has(ent.id()) && (this.overseas || ent.healthLevel() < 0.8)) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
}; | }; | ||||
/** Reassign one (at each turn) Cav unit to fasten raid preparation. */ | /** Reassign one (at each turn) FastMoving unit to fasten raid preparation. */ | ||||
PETRA.AttackPlan.prototype.reassignCavUnit = function(gameState) | PETRA.AttackPlan.prototype.reassignFastUnit = function(gameState) | ||||
{ | { | ||||
for (let ent of this.unitCollection.values()) | for (let ent of this.unitCollection.values()) | ||||
{ | { | ||||
if (!ent.position() || ent.getMetadata(PlayerID, "transport") !== undefined) | if (!ent.position() || ent.getMetadata(PlayerID, "transport") !== undefined) | ||||
continue; | continue; | ||||
if (!ent.hasClass("Cavalry") || !ent.hasClass("CitizenSoldier")) | if (!ent.hasClass("FastMoving") || !ent.hasClass("CitizenSoldier")) | ||||
continue; | continue; | ||||
let raid = gameState.ai.HQ.attackManager.getAttackInPreparation("Raid"); | let raid = gameState.ai.HQ.attackManager.getAttackInPreparation("Raid"); | ||||
ent.setMetadata(PlayerID, "plan", raid.name); | ent.setMetadata(PlayerID, "plan", raid.name); | ||||
this.unitCollection.updateEnt(ent); | this.unitCollection.updateEnt(ent); | ||||
raid.unitCollection.updateEnt(ent); | raid.unitCollection.updateEnt(ent); | ||||
return; | return; | ||||
} | } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 735 Lines • ▼ Show 20 Lines | for (let check = 0; check < lgth; check++) | ||||
{ | { | ||||
needsUpdate = true; | needsUpdate = true; | ||||
--unitTargets[targetId]; | --unitTargets[targetId]; | ||||
} | } | ||||
else if (target.hasClass("Ship") && !ent.hasClass("Ship")) | else if (target.hasClass("Ship") && !ent.hasClass("Ship")) | ||||
maybeUpdate = true; | maybeUpdate = true; | ||||
else if (attackedByStructure[ent.id()] && target.hasClass("Field")) | else if (attackedByStructure[ent.id()] && target.hasClass("Field")) | ||||
maybeUpdate = true; | maybeUpdate = true; | ||||
else if (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged") && | else if (!ent.hasClass("FastMoving") && !ent.hasClass("Ranged") && | ||||
target.hasClass("FemaleCitizen") && target.unitAIState().split(".")[1] == "FLEEING") | target.hasClass("FemaleCitizen") && target.unitAIState().split(".")[1] == "FLEEING") | ||||
maybeUpdate = true; | maybeUpdate = true; | ||||
} | } | ||||
// don't update too soon if not necessary | // don't update too soon if not necessary | ||||
if (!needsUpdate) | if (!needsUpdate) | ||||
{ | { | ||||
if (!maybeUpdate) | if (!maybeUpdate) | ||||
Show All 12 Lines | for (let check = 0; check < lgth; check++) | ||||
range = ent.attackRange("Ranged").max; | range = ent.attackRange("Ranged").max; | ||||
else if (attackTypes && attackTypes.indexOf("Melee") !== -1) | else if (attackTypes && attackTypes.indexOf("Melee") !== -1) | ||||
range = ent.attackRange("Melee").max; | range = ent.attackRange("Melee").max; | ||||
else | else | ||||
range = 10; | range = 10; | ||||
} | } | ||||
else if (attackTypes && attackTypes.indexOf("Ranged") !== -1) | else if (attackTypes && attackTypes.indexOf("Ranged") !== -1) | ||||
range = 30 + ent.attackRange("Ranged").max; | range = 30 + ent.attackRange("Ranged").max; | ||||
else if (ent.hasClass("Cavalry")) | else if (ent.hasClass("FastMoving")) | ||||
range += 30; | range += 30; | ||||
range *= range; | range *= range; | ||||
let entAccess = PETRA.getLandAccess(gameState, ent); | let entAccess = PETRA.getLandAccess(gameState, ent); | ||||
// Checking for gates if we're a siege unit. | // Checking for gates if we're a siege unit. | ||||
if (siegeUnit) | if (siegeUnit) | ||||
{ | { | ||||
let mStruct = enemyStructures.filter(enemy => { | let mStruct = enemyStructures.filter(enemy => { | ||||
if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) | if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | for (let check = 0; check < lgth; check++) | ||||
ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); | ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); | ||||
} | } | ||||
else | else | ||||
ent.attackMove(this.targetPos[0], this.targetPos[1], targetClassesSiege); | ent.attackMove(this.targetPos[0], this.targetPos[1], targetClassesSiege); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let nearby = !ent.hasClass("Cavalry") && !ent.hasClass("Ranged"); | let nearby = !ent.hasClass("FastMoving") && !ent.hasClass("Ranged"); | ||||
let mUnit = enemyUnits.filter(enemy => { | let mUnit = enemyUnits.filter(enemy => { | ||||
if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) | if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) | ||||
return false; | return false; | ||||
if (enemy.hasClass("Animal")) | if (enemy.hasClass("Animal")) | ||||
return false; | return false; | ||||
if (nearby && enemy.hasClass("FemaleCitizen") && enemy.unitAIState().split(".")[1] == "FLEEING") | if (nearby && enemy.hasClass("FemaleCitizen") && enemy.unitAIState().split(".")[1] == "FLEEING") | ||||
return false; | return false; | ||||
let dist = API3.SquareVectorDistance(enemy.position(), ent.position()); | let dist = API3.SquareVectorDistance(enemy.position(), ent.position()); | ||||
▲ Show 20 Lines • Show All 565 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator