diff --git a/binaries/data/mods/public/simulation/ai/petra/army.js b/binaries/data/mods/public/simulation/ai/petra/army.js index f0ea8ed2ff..01beaf1363 100644 --- a/binaries/data/mods/public/simulation/ai/petra/army.js +++ b/binaries/data/mods/public/simulation/ai/petra/army.js @@ -30,15 +30,11 @@ m.Army = function(gameState, owner, ownEntities, foeEntities) // who we assigned against, for quick removal. this.assignedTo = {}; - // For substrengths, format is "name": [classes] - this.foeEntities = []; this.foeStrength = 0; - this.foeSubStrength = {}; this.ownEntities = []; this.ownStrength = 0; - this.ownSubStrength = {}; // actually add units for (var i in foeEntities) diff --git a/binaries/data/mods/public/simulation/ai/petra/attackManager.js b/binaries/data/mods/public/simulation/ai/petra/attackManager.js index 2ba3c81373..9fe8c446cd 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -12,8 +12,8 @@ m.AttackManager = function(Config) this.attackNumber = 0; this.rushNumber = 0; this.raidNumber = 0; - this.upcomingAttacks = { "CityAttack": [], "Rush": [], "Raid": [] }; - this.startedAttacks = { "CityAttack": [], "Rush": [], "Raid": [] }; + this.upcomingAttacks = { "Rush": [], "Raid": [], "Attack": [], "HugeAttack": [] }; + this.startedAttacks = { "Rush": [], "Raid": [], "Attack": [], "HugeAttack": [] }; this.debugTime = 0; }; @@ -153,13 +153,6 @@ m.AttackManager.prototype.update = function(gameState, queues, events) } } } - - // Number of running attacks - if (this.Config.debug > 0 && gameState.ai.playedTurn%50 === 0) - { - for (var attackType in this.startedAttacks) - warn(" === current number of " + attackType + " = " + this.startedAttacks[attackType].length); - } // creating plans after updating because an aborted plan might be reused in that case. @@ -181,28 +174,30 @@ m.AttackManager.prototype.update = function(gameState, queues, events) } // if we have a barracks, there's no water, we're at age >= 1 and we've decided to attack. else if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_barracks"), true) >= 1 + && (this.startedAttacks["Attack"].length + this.startedAttacks["HugeAttack"].length < Math.round(gameState.getPopulationMax()/100)) && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.townPhase()))) { if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_dock"), true) === 0 && gameState.ai.HQ.waterMap) { // wait till we get a dock. } - else if (this.upcomingAttacks["CityAttack"].length === 0) + else if (this.upcomingAttacks["Attack"].length === 0 && this.upcomingAttacks["HugeAttack"].length === 0) { - if (this.attackNumber < 2) - var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber); + if (this.attackNumber < 2 || this.startedAttacks["HugeAttack"].length > 0) + var type = "Attack"; else - var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "superSized"); + var type = "HugeAttack"; + var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, type); if (attackPlan.failed) this.attackPlansEncounteredWater = true; // hack else { if (this.Config.debug > 0) - warn("Military Manager: Creating the plan " + this.totalNumber); + warn("Military Manager: Creating the plan " + type + " " + this.totalNumber); this.attackNumber++; this.totalNumber++; - this.upcomingAttacks["CityAttack"].push(attackPlan); + this.upcomingAttacks[type].push(attackPlan); } } } diff --git a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js index 6da610d53a..5536f09ee4 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -14,7 +14,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, enemy, target) { this.Config = Config; this.name = uniqueID; - this.type = type || "normal"; + this.type = type || "Attack"; this.state = "unexecuted"; this.targetPlayer = enemy; @@ -72,7 +72,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, enemy, target) priority = 150; this.unitStat["Cavalry"] = { "priority": 1, "minSize": 3, "targetSize": 4, "batchSize": 2, "classes": ["Cavalry", "CitizenSoldier"], "interests": [ ["strength",1], ["cost",1] ] }; } - else if (type === "superSized") + else if (type === "HugeAttack") { priority = 90; // basically we want a mix of citizen soldiers so our barracks have a purpose, and champion units. @@ -105,8 +105,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, enemy, target) return (strength[0] > 15 || strength[1] > 15); };*/ - var filter = API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan", this.name), API3.Filters.byOwner(PlayerID)); - this.unitCollection = gameState.getOwnUnits().filter(filter); + this.unitCollection = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", this.name)); this.unitCollection.registerUpdates(); this.unit = {}; @@ -121,7 +120,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, enemy, target) var cat = unitCat; var Unit = this.unitStat[cat]; - filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]),API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan",this.name),API3.Filters.byOwner(PlayerID))); + var filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]), API3.Filters.byMetadata(PlayerID, "plan",this.name)); this.unit[cat] = gameState.getOwnUnits().filter(filter); this.unit[cat].registerUpdates(); this.buildOrder.push([0, Unit["classes"], this.unit[cat], Unit, cat]); @@ -252,7 +251,7 @@ m.AttackPlan.prototype.addBuildOrder = function(gameState, name, unitStats, rese // no minsize as we don't want the plan to fail at the last minute though. this.unitStat[name] = unitStats; var Unit = this.unitStat[name]; - var filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]),API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan",this.name),API3.Filters.byOwner(PlayerID))); + var filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]), API3.Filters.byMetadata(PlayerID, "plan",this.name)); this.unit[name] = gameState.getOwnUnits().filter(filter); this.unit[name].registerUpdates(); this.buildOrder.push([0, Unit["classes"], this.unit[name], Unit, name]); @@ -329,7 +328,21 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events) this.queueSiege.empty(); } else // Abort the plan so that its units will be reassigned to other plans. + { + if (this.Config.debug > 0) + { + var am = gameState.ai.HQ.attackManager; + warn(" attacks upcoming: raid " + am.upcomingAttacks["Raid"].length + + " raid " + am.upcomingAttacks["Rush"].length + + " attack " + am.upcomingAttacks["Attack"].length + + " huge " + am.upcomingAttacks["HugeAttack"].length); + warn(" attacks started: raid " + am.startedAttacks["Raid"].length + + " raid " + am.startedAttacks["Rush"].length + + " attack " + am.startedAttacks["Attack"].length + + " huge " + am.startedAttacks["HugeAttack"].length); + } return 0; + } } else if (this.mustStart(gameState) && (gameState.countOwnQueuedEntitiesWithMetadata("plan", +this.name) > 0)) { @@ -531,7 +544,7 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position) { if (this.type === "Raid") var targets = this.raidTargetFinder(gameState); - else if (this.type === "Rush" || this.type === "normal") + else if (this.type === "Rush" || this.type === "Attack") var targets = this.rushTargetFinder(gameState); else var targets = this.defaultTargetFinder(gameState); @@ -618,7 +631,7 @@ m.AttackPlan.prototype.rushTargetFinder = function(gameState) if (target) targets.addEnt(target); - if (targets.length == 0 && this.type === "normal") + if (targets.length == 0 && this.type === "Attack") targets = this.defaultTargetFinder(gameState); return targets; @@ -810,7 +823,7 @@ m.AttackPlan.prototype.update = function(gameState, events) ourUnit.flee(attacker); } // Are we arrived at destination ? - if ((gameState.ai.HQ.territoryMap.getOwner(this.position) === this.targetPlayer && attackedNB > 1) || attackedNB > 4) + if ((gameState.ai.HQ.territoryMap.getOwner(this.position) === this.targetPlayer && attackedNB > 1) || attackedNB > 3) this.state = "arrived"; } @@ -833,9 +846,9 @@ m.AttackPlan.prototype.update = function(gameState, events) var farthest = 0; var farthestEnt = -1; sieges.forEach (function (ent) { - if (API3.SquareVectorDistance(ent.position(),self.position) > farthest) + if (API3.SquareVectorDistance(ent.position(), self.position) > farthest) { - farthest = API3.SquareVectorDistance(ent.position(),self.position); + farthest = API3.SquareVectorDistance(ent.position(), self.position); farthestEnt = ent; } }); @@ -952,7 +965,10 @@ m.AttackPlan.prototype.update = function(gameState, events) if (targets.length !== 0) { for (var i in targets._entities) + { this.target = targets._entities[i]; + break; + } this.targetPos = this.target.position(); } } @@ -961,7 +977,6 @@ m.AttackPlan.prototype.update = function(gameState, events) // basic state of attacking. if (this.state === "") { - // events watch: if siege units are attacked, we'll send some units to deal with enemies. var attackedEvents = events["Attacked"]; for (var key in attackedEvents) { @@ -972,11 +987,25 @@ m.AttackPlan.prototype.update = function(gameState, events) if (!attacker || !attacker.position() || !attacker.hasClass("Unit")) continue; var ourUnit = gameState.getEntityById(e.target); - if (!ourUnit.hasClass("Siege")) - continue; - var collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5).toEntityArray(); - for (var unit in collec) - unit.attack(attacker.id()); + if (this.isSiegeUnit(gameState, ourUnit)) + { + // if siege units are attacked, we'll send some units to deal with enemies. + var collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5).toEntityArray(); + for each (var ent in collec) + ent.attack(attacker.id()); + } + else + { + // if other units are attacked, abandon their target (if it was a structure or a support) and retaliate + var orderData = ourUnit.unitAIOrderData(); + if (orderData.length !== 0 && orderData[0]["target"]) + { + var target = gameState.getEntityById(orderData[0]["target"]); + if (target && !target.hasClass("Structure") && !target.hasClass("Support")) + continue; + } + ourUnit.attack(attacker.id()); + } } var enemyUnits = gameState.getEnemyUnits(this.targetPlayer); @@ -1008,11 +1037,9 @@ m.AttackPlan.prototype.update = function(gameState, events) // update the order if needed var needsUpdate = false; var maybeUpdate = false; - var isSiegeUnit = ent.hasClass("Siege") || - (gameState.civ() === "maur" && ent.hasClass("Elephant") && ent.hasClass("Champion")); if (ent.isIdle()) needsUpdate = true; - else if (isSiegeUnit && orderData && orderData["target"]) + else if (this.isSiegeUnit(gameState, ent) && orderData && orderData["target"]) { var target = gameState.getEntityById(orderData["target"]); if (!target) @@ -1064,7 +1091,7 @@ m.AttackPlan.prototype.update = function(gameState, events) // Checking for gates if we're a siege unit. mUnit = mUnit.toEntityArray(); mStruct = mStruct.toEntityArray(); - if (isSiegeUnit) + if (this.isSiegeUnit(gameState, ent)) { mStruct.sort(function (structa,structb) { @@ -1225,6 +1252,12 @@ m.AttackPlan.prototype.hasForceOrder = function(data, value) return forced; }; +m.AttackPlan.prototype.isSiegeUnit = function(gameState, ent) +{ + return (ent.hasClass("Siege") || + (gameState.civ() === "maur" && ent.hasClass("Elephant") && ent.hasClass("Champion"))); +}; + m.AttackPlan.prototype.debugAttack = function() { warn("---------- attack " + this.name); diff --git a/binaries/data/mods/public/simulation/ai/petra/config.js b/binaries/data/mods/public/simulation/ai/petra/config.js index b08cc8408c..b04f0f577a 100644 --- a/binaries/data/mods/public/simulation/ai/petra/config.js +++ b/binaries/data/mods/public/simulation/ai/petra/config.js @@ -4,7 +4,7 @@ var PETRA = function(m) // this defines the medium difficulty m.Config = function() { this.difficulty = 2; // 0 is sandbox, 1 is easy, 2 is medium, 3 is hard, 4 is very hard. - this.debug = 0; + this.debug = 1; this.Military = { "towerLapseTime" : 90, // Time to wait between building 2 towers @@ -46,20 +46,17 @@ m.Config = function() { }, "advanced" : { "default" : [], - "hele" : [ "structures/{civ}_gymnasion" ], - "athen" : [ "structures/{civ}_gymnasion" ], - "spart" : [ "structures/{civ}_syssiton" ], + "athen" : [ "structures/{civ}_gymnasion", "structures/{civ}_prytaneion", "structures/{civ}_theatron" ], "cart" : [ "structures/{civ}_embassy_celtic", "structures/{civ}_embassy_iberian", "structures/{civ}_embassy_italiote" ], - "celt" : [ "structures/{civ}_kennel" ], - "pers" : [ "structures/{civ}_fortress", "structures/{civ}_stables", "structures/{civ}_apadana" ], + "gaul" : [ "structures/{civ}_tavern" ], + "mace" : [ "structures/{civ}_siege_workshop", "structures/{civ}_library", "structures/{civ}_theatron" ], + "maur" : [ "structures/{civ}_elephant_stables" ], + "pers" : [ "structures/{civ}_stables", "structures/{civ}_apadana" ], + "ptol" : [ "structures/{civ}_library" ], "rome" : [ "structures/{civ}_army_camp" ], - "mace" : [ "structures/{civ}_siege_workshop"], - "maur" : [ "structures/{civ}_elephant_stables"] - }, - "fort" : { - "default" : [ "structures/{civ}_fortress" ], - "celt" : [ "structures/{civ}_fortress_b", "structures/{civ}_fortress_g" ] + "sele" : [ "structures/{civ}_library" ], + "spart" : [ "structures/{civ}_syssiton", "structures/{civ}_theatron" ] } }; diff --git a/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js b/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js index 86f4ed1102..16cb1e14d5 100644 --- a/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js +++ b/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js @@ -10,27 +10,6 @@ m.DefenseArmy = function(gameState, defManager, ownEntities, foeEntities) this.watchTSMultiplicator = this.Config.Defense.armyStrengthWariness; this.watchDecrement = this.Config.Defense.prudence; - this.foeSubStrength = { - "spear" : ["Infantry", "Spear"], //also pikemen - "sword" : ["Infantry", "Sword"], - "ranged" : ["Infantry", "Ranged"], - "meleeCav" : ["Cavalry", "Melee"], - "rangedCav" : ["Cavalry", "Ranged"], - "Elephant" : ["Elephant"], - "meleeSiege" : ["Siege", "Melee"], - "rangedSiege" : ["Siege", "Ranged"] - }; - this.ownSubStrength = { - "spear" : ["Infantry", "Spear"], //also pikemen - "sword" : ["Infantry", "Sword"], - "ranged" : ["Infantry", "Ranged"], - "meleeCav" : ["Cavalry", "Melee"], - "rangedCav" : ["Cavalry", "Ranged"], - "Elephant" : ["Elephant"], - "meleeSiege" : ["Siege", "Melee"], - "rangedSiege" : ["Siege", "Ranged"] - }; - this.checkDangerosity(gameState); // might push us to 1. this.watchLevel = this.foeStrength * this.watchTSMultiplicator; diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js index c1dfd16569..64ece30bb5 100644 --- a/binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -157,17 +157,10 @@ m.HQ.prototype.init = function(gameState, queues) else this.bAdvanced = this.Config.buildings.advanced['default']; - if (civ in this.Config.buildings.fort) - this.bFort = this.Config.buildings.fort[civ]; - else - this.bFort = this.Config.buildings.fort['default']; - for (var i in this.bBase) this.bBase[i] = gameState.applyCiv(this.bBase[i]); for (var i in this.bAdvanced) this.bAdvanced[i] = gameState.applyCiv(this.bAdvanced[i]); - for (var i in this.bFort) - this.bFort[i] = gameState.applyCiv(this.bFort[i]); }; m.HQ.prototype.checkEvents = function (gameState, events, queues) @@ -1335,36 +1328,33 @@ m.HQ.prototype.buildDefenses = function(gameState, queues) if (gameState.currentPhase() > 2 || gameState.isResearching(gameState.cityPhase())) { // try to build fortresses - if (queues.defenseBuilding.length() === 0 && this.canBuild(gameState, this.bFort[0])) + if (queues.defenseBuilding.length() === 0 && this.canBuild(gameState, "structures/{civ}_fortress")) { - var numFortresses = 0; - for (var i in this.bFort) - numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]), true); - + var numFortresses = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_fortress"), true); if (gameState.getTimeElapsed() > (1 + 0.10*numFortresses)*this.fortressLapseTime + this.fortressStartTime) { this.fortressStartTime = gameState.getTimeElapsed(); // TODO should affect it to the right base - queues.defenseBuilding.addItem(new m.ConstructionPlan(gameState, this.bFort[0])); + queues.defenseBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_fortress")); } } // let's add a siege building plan to the current attack plan if there is none currently. - var numSiegeBuilder = 0; if (gameState.civ() === "mace" || gameState.civ() === "maur") - numSiegeBuilder = gameState.countEntitiesByType(gameState.applyCiv(this.bAdvanced[0]), true); + var numSiegeBuilder = gameState.countEntitiesByType(gameState.applyCiv(this.bAdvanced[0]), true); else - for (var i in this.bFort) - numSiegeBuilder += gameState.countEntitiesByType(gameState.applyCiv(this.bFort[i]), true); - + var numSiegeBuilder = gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_fortress"), true); if (numSiegeBuilder > 0) { - if (this.attackManager.upcomingAttacks["CityAttack"].length !== 0) - { - var attack = this.attackManager.upcomingAttacks["CityAttack"][0]; - if (!attack.unitStat["Siege"]) - attack.addSiegeUnits(gameState); - } + var attack = undefined; + // There can only be one upcoming attack + if (this.attackManager.upcomingAttacks["Attack"].length !== 0) + var attack = this.attackManager.upcomingAttacks["Attack"][0]; + else if (this.attackManager.upcomingAttacks["HugeAttack"].length !== 0) + var attack = this.attackManager.upcomingAttacks["HugeAttack"][0]; + + if (attack && !attack.unitStat["Siege"]) + attack.addSiegeUnits(gameState); } } @@ -1439,10 +1429,16 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) inConst += gameState.countFoundationsByType(gameState.applyCiv(this.bAdvanced[i])); if (inConst == 0 && this.bAdvanced && this.bAdvanced.length !== 0) { - var i = Math.floor(Math.random() * this.bAdvanced.length); - if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i]), true) < 1 && - this.canBuild(gameState, this.bAdvanced[i])) - queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : bestBase })); + for (var i in this.bAdvanced) + { + if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i]), true) < 1 && + this.canBuild(gameState, this.bAdvanced[i])) + { + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : bestBase })); + break; + } + + } } } } diff --git a/binaries/data/mods/public/simulation/ai/petra/queueManager.js b/binaries/data/mods/public/simulation/ai/petra/queueManager.js index f267eb7f9e..46c15609a9 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueManager.js @@ -311,7 +311,7 @@ m.QueueManager.prototype.printQueues = function(gameState) var q = this.queues[i]; if (q.queue.length > 0) { - warn(i + ": ( paused " + q.paused + " with priority " + this.priorities[i] +" and accounts " + uneval(this.accounts[i]) +")"); + warn(i + ": ( with priority " + this.priorities[i] +" and accounts " + uneval(this.accounts[i]) +")"); warn(" while maxAccountWanted(0.6) is " + uneval(q.maxAccountWanted(gameState, 0.6))); } for (var j in q.queue)