diff --git a/binaries/data/mods/public/simulation/ai/petra/army.js b/binaries/data/mods/public/simulation/ai/petra/army.js index 666c441843..c681f343c9 100644 --- a/binaries/data/mods/public/simulation/ai/petra/army.js +++ b/binaries/data/mods/public/simulation/ai/petra/army.js @@ -209,7 +209,7 @@ m.Army.prototype.removeOwn = function (gameState, id, Entity) if (this.assignedTo[id] !== 0) { - var temp = this.assignedAgainst[this.assignedTo[id]]; + let temp = this.assignedAgainst[this.assignedTo[id]]; if (temp) temp.splice(temp.indexOf(id), 1); } @@ -235,13 +235,13 @@ m.Army.prototype.removeOwn = function (gameState, id, Entity) // TODO be sure that all units in the transport need the cancelation /* if (!ent.position()) // this unit must still be in a transport plan ... try to cancel it { - var planID = ent.getMetadata(PlayerID, "transport"); + let planID = ent.getMetadata(PlayerID, "transport"); // no plans must mean that the unit was in a ship which was destroyed, so do nothing if (planID) { if (gameState.ai.Config.debug > 0) warn("ent from army still in transport plan: plan " + planID + " canceled"); - var plan = gameState.ai.HQ.navalManager.getPlan(planID); + let plan = gameState.ai.HQ.navalManager.getPlan(planID); if (plan && !plan.canceled) plan.cancelTransport(gameState); } diff --git a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js index 88b93e86a1..4b61eda92e 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -92,7 +92,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data) this.maxCompletingTime = 0; // priority of the queues we'll create. - var priority = 70; + let priority = 70; // unitStat priority is relative. If all are 0, the only relevant criteria is "currentsize/targetsize". // if not, this is a "bonus". The higher the priority, the faster this unit will get built. @@ -158,7 +158,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data) } // Put some randomness on the attack size - var variation = 0.8 + 0.4*Math.random(); + let variation = 0.8 + 0.4*Math.random(); // and lower priority and smaller sizes for easier difficulty levels if (this.Config.difficulty < 2) { @@ -311,7 +311,7 @@ m.AttackPlan.prototype.forceStart = function() } }; -/** Adds a build order. If resetQueue is true, this will reset the queue.*/ +/** Adds a build order. If resetQueue is true, this will reset the queue. */ m.AttackPlan.prototype.addBuildOrder = function(gameState, name, unitStats, resetQueue) { if (!this.isStarted()) @@ -349,7 +349,7 @@ m.AttackPlan.prototype.addSiegeUnits = function(gameState) return true; }; -/** Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start" */ +/** Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start". */ m.AttackPlan.prototype.updatePreparation = function(gameState) { // the completing step is used to return resources and regroup the units @@ -466,8 +466,8 @@ m.AttackPlan.prototype.updatePreparation = function(gameState) Engine.PostCommand(PlayerID, {"type": "attack-request", "source": PlayerID, "target": this.targetPlayer}); } - var rallyPoint = this.rallyPoint; - var rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint); + let rallyPoint = this.rallyPoint; + let rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint); for (let ent of this.unitCollection.values()) { // For the time being, if occupied in a transport, remove the unit from this plan TODO improve that @@ -585,8 +585,8 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState) m.AttackPlan.prototype.assignUnits = function(gameState) { - var plan = this.name; - var added = false; + 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 (!this.canBuildUnits) { @@ -695,7 +695,7 @@ m.AttackPlan.prototype.assignUnits = function(gameState) return added; }; -/** Reassign one (at each turn) Cav unit to fasten raid preparation */ +/** Reassign one (at each turn) Cav unit to fasten raid preparation. */ m.AttackPlan.prototype.reassignCavUnit = function(gameState) { let found; @@ -1176,16 +1176,15 @@ m.AttackPlan.prototype.update = function(gameState, events) Engine.ProfileStart("Update Attack"); this.position = this.unitCollection.getCentrePosition(); - var IDs = this.unitCollection.toIdArray(); - var self = this; + let self = this; // we are transporting our units, let's wait // TODO instead of state "arrived", made a state "walking" with a new path if (this.state === "transporting") - this.UpdateTransporting(gameState, events, IDs); + this.UpdateTransporting(gameState, events); - if (this.state === "walking" && !this.UpdateWalking(gameState, events, IDs)) + if (this.state === "walking" && !this.UpdateWalking(gameState, events)) { Engine.ProfileStop(); return 0; @@ -1214,95 +1213,22 @@ m.AttackPlan.prototype.update = function(gameState, events) // basic state of attacking. if (this.state === "") { - // First update the target position in case it's a unit (and check if it has garrisoned) - if (this.target && this.target.hasClass("Unit")) + // First update the target and/or its position if needed + if (!this.UpdateTarget(gameState, events)) { - this.targetPos = this.target.position(); - if (!this.targetPos) - { - let holder = m.getHolder(gameState, this.target); - if (holder && gameState.isPlayerEnemy(holder.owner())) - { - this.target = holder; - this.targetPos = holder.position(); - } - else - this.target = undefined; - } - } - // Then update the target if needed: - if (this.targetPlayer === undefined) - { - this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this); - if (this.targetPlayer === undefined) - { - Engine.ProfileStop(); - return false; - } - if (this.target && this.target.owner() !== this.targetPlayer) - this.target = undefined; - } - if (this.target && this.target.owner() === 0 && this.targetPlayer !== 0) // this enemy has resigned - this.target = undefined; - if (!this.target || !gameState.getEntityById(this.target.id())) - { - if (this.Config.debug > 1) - API3.warn("Seems like our target has been destroyed. Switching."); - this.target = this.getNearestTarget(gameState, this.position, true); - if (!this.target) - { - // Check if we could help any current attack - let attackManager = gameState.ai.HQ.attackManager; - let accessIndex = gameState.ai.accessibility.getAccessValue(this.position); - for (let attackType in attackManager.startedAttacks) - { - if (this.target) - break; - for (let attack of attackManager.startedAttacks[attackType]) - { - if (attack.name === this.name) - continue; - if (!attack.target || !gameState.getEntityById(attack.target.id())) - continue; - if (accessIndex !== gameState.ai.accessibility.getAccessValue(attack.targetPos)) - continue; - if (attack.target.owner() === 0 && attack.targetPlayer !== 0) // looks like it has resigned - continue; - this.target = attack.target; - this.targetPlayer = attack.targetPlayer; - break; - } - } - - // If not, let's look for another enemy - if (!this.target) - { - this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this); - if (this.targetPlayer !== undefined) - this.target = this.getNearestTarget(gameState, this.position, true); - if (!this.target) - { - if (this.Config.debug > 1) - API3.warn("No new target found. Remaining units " + this.unitCollection.length); - Engine.ProfileStop(); - return false; - } - } - if (this.Config.debug > 1) - API3.warn("We will help one of our other attacks"); - } - this.targetPos = this.target.position(); + Engine.ProfileStop(); + return false; } - var time = gameState.ai.elapsedTime; + let time = gameState.ai.elapsedTime; for (let evt of events.Attacked) { - if (IDs.indexOf(evt.target) == -1) + if (!this.unitCollection.hasEntId(evt.target)) continue; let attacker = gameState.getEntityById(evt.attacker); if (!attacker || !attacker.position() || !attacker.hasClass("Unit")) continue; - var ourUnit = gameState.getEntityById(evt.target); + let ourUnit = gameState.getEntityById(evt.target); if (this.isSiegeUnit(gameState, ourUnit)) { // if our siege units are attacked, we'll send some units to deal with enemies. let collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5); @@ -1358,8 +1284,8 @@ m.AttackPlan.prototype.update = function(gameState, events) } } - var enemyUnits = gameState.getEnemyUnits(this.targetPlayer); - var enemyStructures = gameState.getEnemyStructures(this.targetPlayer); + let enemyUnits = gameState.getEnemyUnits(this.targetPlayer); + let enemyStructures = gameState.getEnemyStructures(this.targetPlayer); // Count the number of times an enemy is targeted, to prevent all units to follow the same target let unitTargets = {}; @@ -1390,8 +1316,8 @@ m.AttackPlan.prototype.update = function(gameState, events) if (unitTargets[target] > 0) veto[target] = true; - var targetClassesUnit; - var targetClassesSiege; + let targetClassesUnit; + let targetClassesSiege; if (this.type === "Rush") targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall", "Tower", "Fortress"], "vetoEntities": veto}; else @@ -1419,7 +1345,7 @@ m.AttackPlan.prototype.update = function(gameState, events) this.unitCollUpdateArray = this.unitCollection.toIdArray(); // Let's check a few units each time we update (currently 10) except when attack starts - var lgth = (this.unitCollUpdateArray.length < 15 || this.startingAttack) ? this.unitCollUpdateArray.length : 10; + let lgth = (this.unitCollUpdateArray.length < 15 || this.startingAttack) ? this.unitCollUpdateArray.length : 10; for (let check = 0; check < lgth; check++) { let ent = gameState.getEntityById(this.unitCollUpdateArray[check]); @@ -1687,7 +1613,7 @@ m.AttackPlan.prototype.update = function(gameState, events) return this.unitCollection.length; }; -m.AttackPlan.prototype.UpdateTransporting = function(gameState, events, IDs) +m.AttackPlan.prototype.UpdateTransporting = function(gameState, events) { let done = true; for (let ent of this.unitCollection.values()) @@ -1711,7 +1637,7 @@ m.AttackPlan.prototype.UpdateTransporting = function(gameState, events, IDs) // if we are attacked while waiting the rest of the army, retaliate for (let evt of events.Attacked) { - if (IDs.indexOf(evt.target) == -1) + if (!this.unitCollection.hasEntId(evt.target)) continue; let attacker = gameState.getEntityById(evt.attacker); if (!attacker || !gameState.getEntityById(evt.target)) @@ -1728,7 +1654,7 @@ m.AttackPlan.prototype.UpdateTransporting = function(gameState, events, IDs) } }; -m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs) +m.AttackPlan.prototype.UpdateWalking = function(gameState, events) { // we're marching towards the target // Let's check if any of our unit has been attacked. @@ -1738,7 +1664,7 @@ m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs) let attackedUnitNB = 0; for (let evt of events.Attacked) { - if (IDs.indexOf(evt.target) === -1) + if (!this.unitCollection.hasEntId(evt.target)) continue; let attacker = gameState.getEntityById(evt.attacker); if (attacker && (attacker.owner() !== 0 || this.targetPlayer === 0)) @@ -1803,9 +1729,9 @@ m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs) API3.warn("Attack Plan " + this.type + " " + this.name + " has met walls and gives up."); return false; } - else - //this.unitCollection.move(this.path[0][0], this.path[0][1]); - this.unitCollection.moveIndiv(this.path[0][0], this.path[0][1]); + + //this.unitCollection.move(this.path[0][0], this.path[0][1]); + this.unitCollection.moveIndiv(this.path[0][0], this.path[0][1]); } // check if our units are close enough from the next waypoint. @@ -1833,6 +1759,87 @@ m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs) return true; }; +m.AttackPlan.prototype.UpdateTarget = function(gameState, events) +{ + // First update the target position in case it's a unit (and check if it has garrisoned) + if (this.target && this.target.hasClass("Unit")) + { + this.targetPos = this.target.position(); + if (!this.targetPos) + { + let holder = m.getHolder(gameState, this.target); + if (holder && gameState.isPlayerEnemy(holder.owner())) + { + this.target = holder; + this.targetPos = holder.position(); + } + else + this.target = undefined; + } + } + // Then update the target if needed: + if (this.targetPlayer === undefined) + { + this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this); + if (this.targetPlayer === undefined) + return false; + + if (this.target && this.target.owner() !== this.targetPlayer) + this.target = undefined; + } + if (this.target && this.target.owner() === 0 && this.targetPlayer !== 0) // this enemy has resigned + this.target = undefined; + + if (!this.target || !gameState.getEntityById(this.target.id())) + { + if (this.Config.debug > 1) + API3.warn("Seems like our target has been destroyed. Switching."); + this.target = this.getNearestTarget(gameState, this.position, true); + if (!this.target) + { + // Check if we could help any current attack + let attackManager = gameState.ai.HQ.attackManager; + let accessIndex = gameState.ai.accessibility.getAccessValue(this.position); + for (let attackType in attackManager.startedAttacks) + { + for (let attack of attackManager.startedAttacks[attackType]) + { + if (attack.name === this.name) + continue; + if (!attack.target || !gameState.getEntityById(attack.target.id())) + continue; + if (accessIndex !== gameState.ai.accessibility.getAccessValue(attack.targetPos)) + continue; + if (attack.target.owner() === 0 && attack.targetPlayer !== 0) // looks like it has resigned + continue; + this.target = attack.target; + this.targetPlayer = attack.targetPlayer; + this.targetPos = this.target.position(); + return true; + } + } + + // If not, let's look for another enemy + if (!this.target) + { + this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this); + if (this.targetPlayer !== undefined) + this.target = this.getNearestTarget(gameState, this.position, true); + if (!this.target) + { + if (this.Config.debug > 1) + API3.warn("No new target found. Remaining units " + this.unitCollection.length); + return false; + } + } + if (this.Config.debug > 1) + API3.warn("We will help one of our other attacks"); + } + this.targetPos = this.target.position(); + } + return true; +}; + /** reset any units */ m.AttackPlan.prototype.Abort = function(gameState) { @@ -1885,7 +1892,7 @@ m.AttackPlan.prototype.checkEvents = function(gameState, events) for (let evt of events.OwnershipChanged) // capture event if (this.target && this.target.id() == evt.entity && gameState.isPlayerAlly(evt.to)) - this.target = undefined; + this.target = undefined; for (let evt of events.PlayerDefeated) { diff --git a/binaries/data/mods/public/simulation/ai/petra/defenseManager.js b/binaries/data/mods/public/simulation/ai/petra/defenseManager.js index 0176b7d7d7..054dbb17ed 100644 --- a/binaries/data/mods/public/simulation/ai/petra/defenseManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/defenseManager.js @@ -77,7 +77,6 @@ m.DefenseManager.prototype.getArmy = function(partOfArmy) return undefined; }; -// TODO: this algorithm needs to be improved, sorta. m.DefenseManager.prototype.isDangerous = function(gameState, entity) { if (!entity.position()) @@ -165,11 +164,10 @@ m.DefenseManager.prototype.isDangerous = function(gameState, entity) return false; }; - m.DefenseManager.prototype.checkEnemyUnits = function(gameState) { const nbPlayers = gameState.sharedScript.playersData.length; - var i = gameState.ai.playedTurn % nbPlayers; + let i = gameState.ai.playedTurn % nbPlayers; this.attackingUnits[i] = undefined; if (i === PlayerID) @@ -346,9 +344,8 @@ m.DefenseManager.prototype.assignDefenders = function(gameState) if (this.armies.length === 0) return; - var armiesNeeding = []; - // Okay, let's add defenders - // TODO: this is dumb. + let armiesNeeding = []; + // let's add defenders for (let army of this.armies) { let needsDef = army.needsDefenders(gameState); @@ -364,7 +361,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState) return; // let's get our potential units - var potentialDefenders = []; + let potentialDefenders = []; gameState.getOwnUnits().forEach(function(ent) { if (!ent.position()) return; @@ -380,7 +377,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState) return; if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1) { - var subrole = ent.getMetadata(PlayerID, "subrole"); + let subrole = ent.getMetadata(PlayerID, "subrole"); if (subrole && (subrole === "completing" || subrole === "walking" || subrole === "attacking")) return; } @@ -423,7 +420,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState) if (!armiesNeeding.length) return; // If shortage of defenders, produce infantry garrisoned in nearest civil centre - var armiesPos = []; + let armiesPos = []; for (let a = 0; a < armiesNeeding.length; ++a) armiesPos.push(armiesNeeding[a].army.foePosition); gameState.ai.HQ.trainEmergencyUnits(gameState, armiesPos); @@ -441,9 +438,11 @@ m.DefenseManager.prototype.abortArmy = function(gameState, army) } }; -// If our defense structures are attacked, garrison soldiers inside when possible -// and if a support unit is attacked and has less than 55% health, garrison it inside the nearest healing structure -// and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress +/** + * If our defense structures are attacked, garrison soldiers inside when possible + * and if a support unit is attacked and has less than 55% health, garrison it inside the nearest healing structure + * and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress + */ m.DefenseManager.prototype.checkEvents = function(gameState, events) { // must be called every turn for all armies @@ -553,10 +552,10 @@ m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, targe if (dist >= range*range) return; } - var index = gameState.ai.accessibility.getAccessValue(target.position()); - var garrisonManager = gameState.ai.HQ.garrisonManager; - var garrisonArrowClasses = target.getGarrisonArrowClasses(); - var units = gameState.getOwnUnits().filter(function (ent) { return MatchesClassList(garrisonArrowClasses, ent.classes()); }).filterNearest(target.position()); + let index = gameState.ai.accessibility.getAccessValue(target.position()); + let garrisonManager = gameState.ai.HQ.garrisonManager; + let garrisonArrowClasses = target.getGarrisonArrowClasses(); + let units = gameState.getOwnUnits().filter(ent => MatchesClassList(garrisonArrowClasses, ent.classes())).filterNearest(target.position()); for (let ent of units.values()) { if (garrisonManager.numberOfGarrisonedUnits(target) >= minGarrison) @@ -569,7 +568,7 @@ m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, targe continue; if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1) { - var subrole = ent.getMetadata(PlayerID, "subrole"); + let subrole = ent.getMetadata(PlayerID, "subrole"); if (subrole && (subrole === "completing" || subrole === "walking" || subrole === "attacking")) continue; } @@ -579,7 +578,7 @@ m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, targe } }; -// garrison a attacked siege ranged unit inside the nearest fortress +/** garrison a attacked siege ranged unit inside the nearest fortress */ m.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit) { let distmin = Math.min(); @@ -593,7 +592,7 @@ m.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit) return; if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints()) return; - var entAccess = ent.getMetadata(PlayerID, "access"); + let entAccess = ent.getMetadata(PlayerID, "access"); if (!entAccess) { entAccess = gameState.ai.accessibility.getAccessValue(ent.position()); @@ -611,7 +610,7 @@ m.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit) garrisonManager.garrison(gameState, unit, nearest, "protection"); }; -// garrison a hurt unit inside the nearest healing structure +/** garrison a hurt unit inside the nearest healing structure */ m.DefenseManager.prototype.garrisonUnitForHealing = function(gameState, unit) { let distmin = Math.min(); diff --git a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js index 36e68dea5f..1e3a0adb6c 100644 --- a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js @@ -15,14 +15,16 @@ m.DiplomacyManager = function(Config) this.nextTributeRequest.set("all", 240); }; -// 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 +/** + * 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 + */ m.DiplomacyManager.prototype.tributes = function(gameState) { this.nextTributeUpdate = gameState.ai.elapsedTime + 30; - var totalResources = gameState.getResources(); - var availableResources = gameState.ai.queueManager.getAvailableResources(gameState); - var mostNeeded; + let totalResources = gameState.getResources(); + let availableResources = gameState.ai.queueManager.getAvailableResources(gameState); + let mostNeeded; for (let i = 1; i < gameState.sharedScript.playersData.length; ++i) { if (i === PlayerID || !gameState.isPlayerAlly(i) || gameState.ai.HQ.attackManager.defeated[i]) diff --git a/binaries/data/mods/public/simulation/ai/petra/entityExtend.js b/binaries/data/mods/public/simulation/ai/petra/entityExtend.js index 1cd831670f..f139492f79 100644 --- a/binaries/data/mods/public/simulation/ai/petra/entityExtend.js +++ b/binaries/data/mods/public/simulation/ai/petra/entityExtend.js @@ -1,11 +1,11 @@ var PETRA = function(m) { -// returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. +/** returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. */ m.getMaxStrength = function(ent, againstClass) { - var strength = 0.0; - var attackTypes = ent.attackTypes(); + let strength = 0; + let attackTypes = ent.attackTypes(); if (!attackTypes) return strength; @@ -58,7 +58,7 @@ m.getMaxStrength = function(ent, againstClass) } } - var armourStrength = ent.armourStrengths(); + let armourStrength = ent.armourStrengths(); for (let str in armourStrength) { let val = parseFloat(armourStrength[str]); @@ -81,24 +81,24 @@ m.getMaxStrength = function(ent, againstClass) return strength * ent.maxHitpoints() / 100.0; }; -// Decide if we should try to capture or destroy +/** Decide if we should try to capture or destroy */ m.allowCapture = function(ent, target) { return !target.hasClass("Siege") || !ent.hasClass("Melee") || !target.isGarrisonHolder() || !target.garrisoned().length; }; -// Makes the worker deposit the currently carried resources at the closest accessible dropsite +/** Makes the worker deposit the currently carried resources at the closest accessible dropsite */ m.returnResources = function(gameState, ent) { if (!ent.resourceCarrying() || !ent.resourceCarrying().length || !ent.position()) return false; - var resource = ent.resourceCarrying()[0].type; + let resource = ent.resourceCarrying()[0].type; - var closestDropsite; - var distmin = Math.min(); - var access = gameState.ai.accessibility.getAccessValue(ent.position()); + let closestDropsite; + let distmin = Math.min(); + let access = gameState.ai.accessibility.getAccessValue(ent.position()); gameState.getOwnDropsites(resource).forEach(function(dropsite) { if (!dropsite.position() || dropsite.getMetadata(PlayerID, "access") !== access) return; @@ -115,13 +115,13 @@ m.returnResources = function(gameState, ent) return true; }; -// is supply full taking into account gatherers affected during this turn +/** is supply full taking into account gatherers affected during this turn */ m.IsSupplyFull = function(gameState, ent) { if (ent.isFull() === true) return true; - var turnCache = gameState.ai.HQ.turnCache; - var count = ent.resourceSupplyNumGatherers(); + let turnCache = gameState.ai.HQ.turnCache; + let count = ent.resourceSupplyNumGatherers(); if (turnCache.resourceGatherer && turnCache.resourceGatherer[ent.id()]) count += turnCache.resourceGatherer[ent.id()]; if (count >= ent.maxGatherers()) @@ -134,7 +134,7 @@ m.IsSupplyFull = function(gameState, ent) */ m.getBestBase = function(gameState, ent) { - var pos = ent.position(); + let pos = ent.position(); if (!pos) { let holder = m.getHolder(gameState, ent); @@ -146,9 +146,9 @@ m.getBestBase = function(gameState, ent) } pos = holder.position(); } - var distmin = Math.min(); - var bestbase; - var accessIndex = gameState.ai.accessibility.getAccessValue(pos); + let distmin = Math.min(); + let bestbase; + let accessIndex = gameState.ai.accessibility.getAccessValue(pos); for (let base of gameState.ai.HQ.baseManagers) { if (!base.anchor) diff --git a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js index 19b6fb6f1a..a69e8ca13f 100644 --- a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js +++ b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js @@ -22,7 +22,7 @@ m.HQ.prototype.gameAnalysis = function(gameState) nobase.init(gameState); nobase.accessIndex = 0; this.baseManagers.push(nobase); // baseManagers[0] will deal with unit/structure without base - var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); + let ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); for (let cc of ccEnts.values()) { let newbase = new m.BaseManager(gameState, this.Config); @@ -133,10 +133,10 @@ m.HQ.prototype.assignStartingEntities = function(gameState) */ m.HQ.prototype.regionAnalysis = function(gameState) { - var accessibility = gameState.ai.accessibility; + let accessibility = gameState.ai.accessibility; let landIndex; let seaIndex; - var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); + let ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); for (let cc of ccEnts.values()) { let land = accessibility.getAccessValue(cc.position()); @@ -170,11 +170,11 @@ m.HQ.prototype.regionAnalysis = function(gameState) return false; } - var passabilityMap = gameState.getMap(); - var totalSize = passabilityMap.width * passabilityMap.width; - var minLandSize = Math.floor(0.1*totalSize); - var minWaterSize = Math.floor(0.2*totalSize); - var cellArea = passabilityMap.cellSize * passabilityMap.cellSize; + let passabilityMap = gameState.getMap(); + let totalSize = passabilityMap.width * passabilityMap.width; + let minLandSize = Math.floor(0.1*totalSize); + let minWaterSize = Math.floor(0.2*totalSize); + let cellArea = passabilityMap.cellSize * passabilityMap.cellSize; for (let i = 0; i < accessibility.regionSize.length; ++i) { if (landIndex && i == landIndex) @@ -227,8 +227,8 @@ m.HQ.prototype.regionAnalysis = function(gameState) */ m.HQ.prototype.structureAnalysis = function(gameState) { - var civref = gameState.playerData.civ; - var civ = civref in this.Config.buildings.base ? civref : 'default'; + let civref = gameState.playerData.civ; + let civ = civref in this.Config.buildings.base ? civref : 'default'; this.bBase = []; for (let base of this.Config.buildings.base[civ]) this.bBase.push(gameState.applyCiv(base)); @@ -246,13 +246,13 @@ m.HQ.prototype.structureAnalysis = function(gameState) */ m.HQ.prototype.buildFirstBase = function(gameState) { - var total = gameState.getResources(); - var template = gameState.applyCiv("structures/{civ}_civil_centre"); + let total = gameState.getResources(); + let template = gameState.applyCiv("structures/{civ}_civil_centre"); if (gameState.isDisabledTemplates(template)) return; template = gameState.getTemplate(template); - var goal = "civil_centre"; - var docks = gameState.getOwnStructures().filter(API3.Filters.byClass("Dock")); + let goal = "civil_centre"; + let docks = gameState.getOwnStructures().filter(API3.Filters.byClass("Dock")); if (!total.canAfford(new API3.Resources(template.cost())) && !docks.hasEntities()) { // not enough resource to build a cc, try with a dock to accumulate resources if none yet @@ -325,22 +325,22 @@ m.HQ.prototype.buildFirstBase = function(gameState) */ m.HQ.prototype.dispatchUnits = function(gameState) { - var allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); + let allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); if (allycc.length) { if (this.Config.debug > 1) API3.warn(" We have allied cc " + allycc.length + " and " + gameState.getOwnUnits().length + " units "); - var units = gameState.getOwnUnits(); - var num = Math.max(Math.min(Math.round(0.08*(1+this.Config.personality.cooperative)*units.length), 20), 5); - var num1 = Math.floor(num / 2); - var num2 = num1; + let units = gameState.getOwnUnits(); + let num = Math.max(Math.min(Math.round(0.08*(1+this.Config.personality.cooperative)*units.length), 20), 5); + let num1 = Math.floor(num / 2); + let num2 = num1; // first pass to affect ranged infantry units.filter(API3.Filters.byClassesAnd(["Infantry", "Ranged"])).forEach(function (ent) { if (!num || !num1) return; if (ent.getMetadata(PlayerID, "allied")) return; - var access = gameState.ai.accessibility.getAccessValue(ent.position()); + let access = gameState.ai.accessibility.getAccessValue(ent.position()); for (let cc of allycc) { if (!cc.position()) @@ -410,8 +410,8 @@ m.HQ.prototype.configFirstBase = function(gameState) if (this.baseManagers.length < 2) return; - var startingSize = 0; - var startingLand = []; + let startingSize = 0; + let startingLand = []; for (let region in this.landRegions) { for (let base of this.baseManagers) @@ -423,7 +423,7 @@ m.HQ.prototype.configFirstBase = function(gameState) break; } } - var cell = gameState.getMap().cellSize; + let cell = gameState.getMap().cellSize; startingSize = startingSize * cell * cell; if (this.Config.debug > 1) API3.warn("starting size " + startingSize + "(cut at 24000 for fish pushing)"); @@ -441,8 +441,8 @@ m.HQ.prototype.configFirstBase = function(gameState) } // - count the available wood resource, and react accordingly - var startingFood = gameState.getResources().food; - var check = {}; + let startingFood = gameState.getResources().food; + let check = {}; for (let proxim of ["nearby", "medium", "faraway"]) { for (let base of this.baseManagers) @@ -467,7 +467,7 @@ m.HQ.prototype.configFirstBase = function(gameState) this.needFarm = true; } // - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion) - var startingWood = gameState.getResources().wood; + let startingWood = gameState.getResources().wood; check = {}; for (let proxim of ["nearby", "medium", "faraway"]) { @@ -501,7 +501,7 @@ m.HQ.prototype.configFirstBase = function(gameState) } // immediatly build a wood dropsite if possible. - var template = gameState.applyCiv("structures/{civ}_storehouse"); + let template = gameState.applyCiv("structures/{civ}_storehouse"); if (!gameState.getOwnEntitiesByClass("Storehouse", true).hasEntities() && this.canBuild(gameState, template)) { let newDP = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood"); diff --git a/binaries/data/mods/public/simulation/ai/petra/tradeManager.js b/binaries/data/mods/public/simulation/ai/petra/tradeManager.js index 970e609dd0..93b2fd0445 100644 --- a/binaries/data/mods/public/simulation/ai/petra/tradeManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/tradeManager.js @@ -33,26 +33,26 @@ m.TradeManager.prototype.assignTrader = function(ent) this.traders.updateEnt(ent); }; -// TODO take trader ships into account m.TradeManager.prototype.trainMoreTraders = function(gameState, queues) { if (!this.tradeRoute || queues.trader.hasQueuedUnits()) return; - var numTraders = this.traders.length; - var numSeaTraders = this.traders.filter(API3.Filters.byClass("Ship")).length; - var numLandTraders = numTraders - numSeaTraders; + let numTraders = this.traders.length; + let numSeaTraders = this.traders.filter(API3.Filters.byClass("Ship")).length; + let numLandTraders = numTraders - numSeaTraders; // add traders already in training gameState.getOwnTrainingFacilities().forEach(function(ent) { - ent.trainingQueue().forEach(function(item) { + for (let item of ent.trainingQueue()) + { if (!item.metadata || !item.metadata.role || item.metadata.role !== "trader") - return; + continue; numTraders += item.count; if (item.metadata.sea !== undefined) numSeaTraders += item.count; else numLandTraders += item.count; - }); + } }); if (numTraders >= this.targetNumTraders && ((!this.tradeRoute.sea && numLandTraders >= Math.floor(this.targetNumTraders/2)) || @@ -118,8 +118,8 @@ m.TradeManager.prototype.updateTrader = function(gameState, ent) return; Engine.ProfileStart("Trade Manager"); - var access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position()); - var route = this.checkRoutes(gameState, access); + let access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position()); + let route = this.checkRoutes(gameState, access); if (!route) { // TODO try to garrison land trader inside merchant ship when only sea routes available @@ -129,7 +129,7 @@ m.TradeManager.prototype.updateTrader = function(gameState, ent) return; } - var nearerSource = true; + let nearerSource = true; if (API3.SquareVectorDistance(route.target.position(), ent.position()) < API3.SquareVectorDistance(route.source.position(), ent.position())) nearerSource = false; @@ -153,13 +153,13 @@ m.TradeManager.prototype.updateTrader = function(gameState, ent) m.TradeManager.prototype.setTradingGoods = function(gameState) { - var tradingGoods = { "food": 0, "wood": 0, "stone": 0, "metal": 0 }; + let tradingGoods = { "food": 0, "wood": 0, "stone": 0, "metal": 0 }; // first, try to anticipate future needs - var stocks = gameState.ai.HQ.getTotalResourceLevel(gameState); - var mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState); - var remaining = 100; - var targetNum = this.Config.Economy.targetNumTraders; - for (var type in stocks) + let stocks = gameState.ai.HQ.getTotalResourceLevel(gameState); + let mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState); + let remaining = 100; + let targetNum = this.Config.Economy.targetNumTraders; + for (let type in stocks) { if (type == "food") continue; @@ -185,8 +185,8 @@ m.TradeManager.prototype.setTradingGoods = function(gameState) // then add what is needed now - var mainNeed = Math.floor(remaining * 70 / 100); - var nextNeed = remaining - mainNeed; + let mainNeed = Math.floor(remaining * 70 / 100); + let nextNeed = remaining - mainNeed; tradingGoods[mostNeeded[0].type] += mainNeed; if (mostNeeded[1].wanted > 0) @@ -198,33 +198,35 @@ m.TradeManager.prototype.setTradingGoods = function(gameState) API3.warn(" trading goods set to " + uneval(tradingGoods)); }; -// Try to barter unneeded resources for needed resources. -// only once per turn because the info doesn't update between a turn and fixing isn't worth it. +/** + * Try to barter unneeded resources for needed resources. + * only once per turn because the info is not updated within a turn + */ m.TradeManager.prototype.performBarter = function(gameState) { - var barterers = gameState.getOwnEntitiesByClass("BarterMarket", true).filter(API3.Filters.isBuilt()).toEntityArray(); + let barterers = gameState.getOwnEntitiesByClass("BarterMarket", true).filter(API3.Filters.isBuilt()).toEntityArray(); if (barterers.length === 0) return false; // Available resources after account substraction - var available = gameState.ai.queueManager.getAvailableResources(gameState); - var needs = gameState.ai.queueManager.currentNeeds(gameState); + let available = gameState.ai.queueManager.getAvailableResources(gameState); + let needs = gameState.ai.queueManager.currentNeeds(gameState); - var rates = gameState.ai.HQ.GetCurrentGatherRates(gameState); + let rates = gameState.ai.HQ.GetCurrentGatherRates(gameState); - var prices = gameState.getBarterPrices(); + let prices = gameState.getBarterPrices(); // calculates conversion rates - var getBarterRate = function (prices,buy,sell) { return Math.round(100 * prices.sell[sell] / prices.buy[buy]); }; + let getBarterRate = function (prices,buy,sell) { return Math.round(100 * prices.sell[sell] / prices.buy[buy]); }; // loop through each missing resource checking if we could barter and help finishing a queue quickly. - for (var buy of needs.types) + for (let buy of needs.types) { if (needs[buy] === 0 || needs[buy] < rates[buy]*30) // check if our rate allows to gather it fast enough continue; // pick the best resource to barter. - var bestToSell = undefined; - var bestRate = 0; + let bestToSell = undefined; + let bestRate = 0; for (let sell of needs.types) { if (sell === buy) @@ -273,8 +275,8 @@ m.TradeManager.prototype.performBarter = function(gameState) // now do contingency bartering, selling food to buy finite resources (and annoy our ennemies by increasing prices) if (available.food < 1000 || needs.food > 0) return false; - var bestToBuy; - var bestChoice = 0; + let bestToBuy; + let bestChoice = 0; for (let buy of needs.types) { if (buy === "food") @@ -371,12 +373,14 @@ m.TradeManager.prototype.checkEvents = function(gameState, events) return false; }; -// 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 +/** + * 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 + */ m.TradeManager.prototype.checkRoutes = function(gameState, accessIndex) { - var market1 = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).toEntityArray(); - var market2 = gameState.updatingCollection("ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).toEntityArray(); + let market1 = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).toEntityArray(); + let market2 = gameState.updatingCollection("ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).toEntityArray(); if (market1.length + market2.length < 2) // We have to wait ... markets will be built soon { this.tradeRoute = undefined; @@ -386,29 +390,29 @@ m.TradeManager.prototype.checkRoutes = function(gameState, accessIndex) if (!market2.length) market2 = market1; - var candidate = { "gain": 0 }; - var potential = { "gain": 0 }; - var bestIndex = { "gain": 0 }; - var bestLand = { "gain": 0 }; + let candidate = { "gain": 0 }; + let potential = { "gain": 0 }; + let bestIndex = { "gain": 0 }; + let bestLand = { "gain": 0 }; let traderTemplatesGains = gameState.getTraderTemplatesGains(); - for (var m1 of market1) + for (let m1 of market1) { if (!m1.position()) continue; - var access1 = gameState.ai.accessibility.getAccessValue(m1.position()); - var sea1 = m1.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m1, true) : undefined; - for (var m2 of market2) + let access1 = gameState.ai.accessibility.getAccessValue(m1.position()); + let sea1 = m1.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m1, true) : undefined; + for (let m2 of market2) { if (m1.id() === m2.id()) continue; if (!m2.position()) continue; - var access2 = gameState.ai.accessibility.getAccessValue(m2.position()); - var sea2 = m2.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m2, true) : undefined; - var land = access1 == access2 ? access1 : undefined; - var sea = (sea1 && sea1 == sea2) ? sea1 : undefined; + let access2 = gameState.ai.accessibility.getAccessValue(m2.position()); + let sea2 = m2.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m2, true) : undefined; + let land = access1 == access2 ? access1 : undefined; + let sea = (sea1 && sea1 == sea2) ? sea1 : undefined; if (!land && !sea) continue; let gainMultiplier; @@ -499,10 +503,10 @@ m.TradeManager.prototype.checkRoutes = function(gameState, accessIndex) return true; }; -// Called when a market was built or destroyed, and checks if trader orders should be changed +/** Called when a market was built or destroyed, and checks if trader orders should be changed */ m.TradeManager.prototype.checkTrader = function(gameState, ent) { - var presentRoute = ent.getMetadata(PlayerID, "route"); + let presentRoute = ent.getMetadata(PlayerID, "route"); if (!presentRoute) return; @@ -513,8 +517,8 @@ m.TradeManager.prototype.checkTrader = function(gameState, ent) return; } - var access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position()); - var possibleRoute = this.checkRoutes(gameState, access); + let access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position()); + let possibleRoute = this.checkRoutes(gameState, access); // Warning: presentRoute is from metadata, so contains entity ids if (!possibleRoute || (possibleRoute.source.id() != presentRoute.source && possibleRoute.source.id() != presentRoute.target) || @@ -535,11 +539,11 @@ m.TradeManager.prototype.prospectForNewMarket = function(gameState, queues) if (!gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).length && !gameState.updatingCollection("ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).length) return; - var template = gameState.getTemplate(gameState.applyCiv("structures/{civ}_market")); + let template = gameState.getTemplate(gameState.applyCiv("structures/{civ}_market")); if (!template) return; this.checkRoutes(gameState); - var marketPos = gameState.ai.HQ.findMarketLocation(gameState, template); + let marketPos = gameState.ai.HQ.findMarketLocation(gameState, template); if (!marketPos || marketPos[3] === 0) // marketPos[3] is the expected gain { // no position found gameState.ai.HQ.stopBuild(gameState, "structures/{civ}_market"); @@ -582,7 +586,7 @@ m.TradeManager.prototype.update = function(gameState, events, queues) if (this.routeProspection) this.prospectForNewMarket(gameState, queues); - var self = this; + let self = this; if (this.checkEvents(gameState, events)) // true if one market was built or destroyed {