From 164af0742ae38e9d3035c407f285a8a2672fb4f1 Mon Sep 17 00:00:00 2001 From: wraitii Date: Sat, 1 Aug 2020 10:35:44 +0000 Subject: [PATCH] Use new FastMoving class instead of Cavalry in AI/petra. This class is an AI hint that such units are to be treated as moving fast, e.g. war dogs, or cavalry. This makes it easier to introduce camels and chariots correctly. Further work is required to make the AI unit choices less hardcoded. Patch by: Nescio Reviewed By: Angen, wraitii Differential Revision: https://code.wildfiregames.com/D2251 This was SVN commit r23916. --- .../public/simulation/ai/petra/attackPlan.js | 38 +++++++++---------- .../public/simulation/ai/petra/baseManager.js | 2 +- .../simulation/ai/petra/entityExtend.js | 7 ++++ .../simulation/ai/petra/startingStrategy.js | 2 +- .../mods/public/simulation/ai/petra/worker.js | 28 +++++++------- .../public/simulation/components/Identity.js | 2 +- .../templates/template_unit_cavalry.xml | 2 +- .../template_unit_champion_cavalry.xml | 1 + .../templates/template_unit_dog.xml | 2 +- .../templates/template_unit_hero_cavalry.xml | 1 + 10 files changed, 47 insertions(+), 38 deletions(-) diff --git a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js index e86824fedc..a1141c5032 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -119,7 +119,7 @@ PETRA.AttackPlan = function(gameState, Config, uniqueID, type, data) priority = 250; this.unitStat.Infantry = { "priority": 1, "minSize": 10, "targetSize": 20, "batchSize": 2, "classes": ["Infantry"], "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]] }; if (data && data.targetSize) this.unitStat.Infantry.targetSize = data.targetSize; @@ -128,7 +128,7 @@ PETRA.AttackPlan = function(gameState, Config, uniqueID, type, data) else if (type == "Raid") { 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] ] }; this.neededShips = 1; } @@ -144,13 +144,13 @@ PETRA.AttackPlan = function(gameState, Config, uniqueID, type, data) "interests": [["strength", 3]] }; this.unitStat.ChampMeleeInfantry = { "priority": 1, "minSize": 3, "targetSize": 18, "batchSize": 3, "classes": ["Infantry", "Melee", "Champion"], "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]] }; - 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]] }; - 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]] }; - 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]] }; this.unitStat.Hero = { "priority": 1, "minSize": 0, "targetSize": 1, "batchSize": 1, "classes": ["Hero"], "interests": [["strength", 2]] }; @@ -163,7 +163,7 @@ PETRA.AttackPlan = function(gameState, Config, uniqueID, type, data) "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"], "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]] }; this.neededShips = 3; } @@ -435,7 +435,7 @@ PETRA.AttackPlan.prototype.updatePreparation = function(gameState) if (this.type != "Raid" || !this.forced) // Forced Raids have special purposes (as relic capture) this.assignUnits(gameState); 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. if (gameState.ai.playedTurn % 5 == 0 && this.hasSiegeUnits()) @@ -655,7 +655,7 @@ PETRA.AttackPlan.prototype.assignUnits = function(gameState) { let plan = this.name; 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) { for (let ent of gameState.getOwnUnits().values()) @@ -671,11 +671,11 @@ PETRA.AttackPlan.prototype.assignUnits = function(gameState) 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; for (let ent of gameState.getOwnUnits().values()) { - if (!ent.hasClass("Cavalry") || !this.isAvailableUnit(gameState, ent)) + if (!ent.hasClass("FastMoving") || !this.isAvailableUnit(gameState, ent)) continue; if (num++ < 2) continue; @@ -686,7 +686,7 @@ PETRA.AttackPlan.prototype.assignUnits = function(gameState) return added; } - // Assign all units without specific role + // Assign all units without specific role. for (let ent of gameState.getOwnEntitiesByRole(undefined, true).values()) { if (!ent.hasClass("Unit") || !this.isAvailableUnit(gameState, ent)) @@ -697,7 +697,7 @@ PETRA.AttackPlan.prototype.assignUnits = function(gameState) this.unitCollection.updateEnt(ent); 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()) { if (!this.isAvailableUnit(gameState, ent)) @@ -751,14 +751,14 @@ PETRA.AttackPlan.prototype.isAvailableUnit = function(gameState, ent) return true; }; -/** Reassign one (at each turn) Cav unit to fasten raid preparation. */ -PETRA.AttackPlan.prototype.reassignCavUnit = function(gameState) +/** Reassign one (at each turn) FastMoving unit to fasten raid preparation. */ +PETRA.AttackPlan.prototype.reassignFastUnit = function(gameState) { for (let ent of this.unitCollection.values()) { if (!ent.position() || ent.getMetadata(PlayerID, "transport") !== undefined) continue; - if (!ent.hasClass("Cavalry") || !ent.hasClass("CitizenSoldier")) + if (!ent.hasClass("FastMoving") || !ent.hasClass("CitizenSoldier")) continue; let raid = gameState.ai.HQ.attackManager.getAttackInPreparation("Raid"); ent.setMetadata(PlayerID, "plan", raid.name); @@ -1510,7 +1510,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events) maybeUpdate = true; else if (attackedByStructure[ent.id()] && target.hasClass("Field")) 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") maybeUpdate = true; } @@ -1539,7 +1539,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events) } else if (attackTypes && attackTypes.indexOf("Ranged") !== -1) range = 30 + ent.attackRange("Ranged").max; - else if (ent.hasClass("Cavalry")) + else if (ent.hasClass("FastMoving")) range += 30; range *= range; let entAccess = PETRA.getLandAccess(gameState, ent); @@ -1597,7 +1597,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events) } else { - let nearby = !ent.hasClass("Cavalry") && !ent.hasClass("Ranged"); + let nearby = !ent.hasClass("FastMoving") && !ent.hasClass("Ranged"); let mUnit = enemyUnits.filter(enemy => { if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) return false; diff --git a/binaries/data/mods/public/simulation/ai/petra/baseManager.js b/binaries/data/mods/public/simulation/ai/petra/baseManager.js index 9390444d59..9f5f7ce8d4 100644 --- a/binaries/data/mods/public/simulation/ai/petra/baseManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/baseManager.js @@ -650,7 +650,7 @@ PETRA.BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorker } } } - else if (ent.hasClass("Cavalry")) + else if (PETRA.isFastMoving(ent)) ent.setMetadata(PlayerID, "subrole", "hunter"); else if (ent.hasClass("FishingBoat")) ent.setMetadata(PlayerID, "subrole", "fisher"); diff --git a/binaries/data/mods/public/simulation/ai/petra/entityExtend.js b/binaries/data/mods/public/simulation/ai/petra/entityExtend.js index 51fd8c0fc6..fd9e61d66c 100644 --- a/binaries/data/mods/public/simulation/ai/petra/entityExtend.js +++ b/binaries/data/mods/public/simulation/ai/petra/entityExtend.js @@ -4,6 +4,13 @@ PETRA.isSiegeUnit = function(ent) return ent.hasClass("Siege") || ent.hasClass("Elephant") && ent.hasClass("Melee") && ent.hasClass("Champion"); }; +/** returns true if this unit should be considered as "fast". */ +PETRA.isFastMoving = function(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) { diff --git a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js index 457328fe09..6d0bbeafb5 100644 --- a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js +++ b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js @@ -306,7 +306,7 @@ PETRA.HQ.prototype.buildFirstBase = function(gameState) { if (!ent.hasClass("Worker") && !(ent.hasClass("Support") && ent.hasClass("Elephant"))) continue; - if (ent.hasClass("Cavalry")) + if (PETRA.isFastMoving(ent)) continue; let pos = ent.position(); if (!pos) diff --git a/binaries/data/mods/public/simulation/ai/petra/worker.js b/binaries/data/mods/public/simulation/ai/petra/worker.js index 332dce1508..bbdfdde53f 100644 --- a/binaries/data/mods/public/simulation/ai/petra/worker.js +++ b/binaries/data/mods/public/simulation/ai/petra/worker.js @@ -112,7 +112,7 @@ PETRA.Worker.prototype.update = function(gameState, ent) } else if (!gameState.isPlayerAlly(territoryOwner)) { - let distanceSquare = ent.hasClass("Cavalry") ? 90000 : 30000; + let distanceSquare = ent.isFastMoving() ? 90000 : 30000; let targetAccess = PETRA.getLandAccess(gameState, target); let foodDropsites = gameState.playerData.hasSharedDropsites ? gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food"); @@ -744,7 +744,7 @@ PETRA.Worker.prototype.startHunting = function(gameState, position) let nearestSupplyDist = Math.min(); let nearestSupply; - let isCavalry = this.ent.hasClass("Cavalry"); + let isFastMoving = PETRA.isFastMoving(this.ent); let isRanged = this.ent.hasClass("Ranged"); let entPosition = position ? position : this.ent.position(); let foodDropsites = gameState.playerData.hasSharedDropsites ? @@ -784,41 +784,41 @@ PETRA.Worker.prototype.startHunting = function(gameState, position) if (PETRA.IsSupplyFull(gameState, supply)) continue; - // check if available resource is worth one additionnal gatherer (except for farms) + // Check if available resource is worth one additionnal gatherer (except for farms). let nbGatherers = supply.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(supply.id()); if (nbGatherers > 0 && supply.resourceSupplyAmount()/(1+nbGatherers) < 30) continue; let canFlee = !supply.hasClass("Domestic") && supply.templateName().indexOf("resource|") == -1; - // Only cavalry and range units should hunt fleeing animals - if (canFlee && !isCavalry && !isRanged) + // Only FastMoving and Ranged units should hunt fleeing animals. + if (canFlee && !isFastMoving && !isRanged) continue; let supplyAccess = PETRA.getLandAccess(gameState, supply); if (supplyAccess != this.entAccess) continue; - // measure the distance to the resource + // measure the distance to the resource. let dist = API3.SquareVectorDistance(entPosition, supply.position()); if (dist > nearestSupplyDist) continue; - // Only cavalry should hunt faraway - if (!isCavalry && dist > 25000) + // Only FastMoving should hunt faraway. + if (!isFastMoving && dist > 25000) continue; - // Avoid ennemy territory + // Avoid enemy territory. let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(supply.position()); - if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally + if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // Player is its own ally. continue; - // And if in ally territory, don't hunt this ally's cattle + // And if in ally territory, don't hunt this ally's cattle. if (territoryOwner != 0 && territoryOwner != PlayerID && supply.owner() == territoryOwner) continue; - // Only cavalry should hunt far from dropsite (specially for non domestic animals which flee) - if (!isCavalry && canFlee && territoryOwner == 0) + // Only FastMoving should hunt far from dropsite (specially for non-Domestic animals which flee). + if (!isFastMoving && canFlee && territoryOwner == 0) continue; - let distanceSquare = isCavalry ? 35000 : (canFlee ? 7000 : 12000); + let distanceSquare = isFastMoving ? 35000 : (canFlee ? 7000 : 12000); if (!hasFoodDropsiteWithinDistance(supply.position(), supplyAccess, distanceSquare)) continue; diff --git a/binaries/data/mods/public/simulation/components/Identity.js b/binaries/data/mods/public/simulation/components/Identity.js index edd0ddd580..77501946e3 100644 --- a/binaries/data/mods/public/simulation/components/Identity.js +++ b/binaries/data/mods/public/simulation/components/Identity.js @@ -57,7 +57,7 @@ Identity.prototype.Schema = "" + "" + "" + - "" + + "" + "" + "tokens" + "" + diff --git a/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml b/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml index d2fbd16d9c..47d01d96b6 100644 --- a/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml +++ b/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml @@ -35,7 +35,7 @@ Cavalry Basic - Human CitizenSoldier + Human FastMoving CitizenSoldier Citizen Soldier Cavalry special/formations/wedge diff --git a/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml b/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml index e5fdd7fdaf..66bc3db32b 100644 --- a/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml +++ b/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml @@ -22,6 +22,7 @@ 240 + FastMoving Cavalry Champion Cavalry diff --git a/binaries/data/mods/public/simulation/templates/template_unit_dog.xml b/binaries/data/mods/public/simulation/templates/template_unit_dog.xml index 1f5ed028b9..22589f4a76 100644 --- a/binaries/data/mods/public/simulation/templates/template_unit_dog.xml +++ b/binaries/data/mods/public/simulation/templates/template_unit_dog.xml @@ -35,7 +35,7 @@ War Dog Cannot attack Structures, Ships, or Siege Engines. - Human + Human FastMoving Dog Melee diff --git a/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml b/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml index 65c7d2b78e..5f3471cd8c 100644 --- a/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml +++ b/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml @@ -21,6 +21,7 @@ 1500 + FastMoving Cavalry Hero Cavalry