Index: binaries/data/mods/public/simulation/ai/petra/_petrabot.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/_petrabot.js +++ binaries/data/mods/public/simulation/ai/petra/_petrabot.js @@ -34,7 +34,7 @@ this.elapsedTime = this.data.elapsedTime; this.savedEvents = this.data.savedEvents; for (let key in this.savedEvents) - { + for (let i in this.savedEvents[key]) { if (!this.savedEvents[key][i].entityObj) @@ -48,7 +48,7 @@ this.savedEvents[key][i] = evtmod; } } - } + this.Config.Deserialize(this.data.config); Index: binaries/data/mods/public/simulation/ai/petra/attackManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** Attack Manager */ @@ -62,7 +62,7 @@ targetPlayer = evt.player; let available = 0; for (let attackType in this.upcomingAttacks) - { + for (let attack of this.upcomingAttacks[attackType]) { if (attack.state === "completing") @@ -79,12 +79,12 @@ if (attack.unitCollection.length > 2) available += attack.unitCollection.length; } - } + if (available > 12) // launch the attack immediately { for (let attackType in this.upcomingAttacks) - { + for (let attack of this.upcomingAttacks[attackType]) { if (attack.state === "completing" || @@ -94,7 +94,7 @@ attack.forceStart(); attack.requested = true; } - } + answer = "join"; } else if (other !== undefined) @@ -105,9 +105,9 @@ m.chatAnswerRequestAttack(gameState, targetPlayer, answer, other); for (let evt of events.EntityRenamed) // take care of packing units in bombing attacks - { + for (let [targetId, unitIds] of this.bombingAttacks) - { + if (targetId == evt.entity) { this.bombingAttacks.set(evt.newentity, unitIds); @@ -118,8 +118,8 @@ unitIds.add(evt.newentity); unitIds.delete(evt.entity); } - } - } + + }; /** @@ -186,11 +186,11 @@ let x; let z; if (struct.hasClass("Field")) - { + if (!struct.resourceSupplyNumGatherers() || !gameState.isPlayerEnemy(gameState.ai.HQ.territoryMap.getOwner(structPos))) continue; - } + let dist = API3.VectorDistance(entPos, structPos); if (dist > range) { @@ -254,7 +254,7 @@ let unexecutedAttacks = { "Rush": 0, "Raid": 0, "Attack": 0, "HugeAttack": 0 }; for (let attackType in this.upcomingAttacks) - { + for (let i = 0; i < this.upcomingAttacks[attackType].length; ++i) { let attack = this.upcomingAttacks[attackType][i]; @@ -293,10 +293,10 @@ this.upcomingAttacks[attackType].splice(i--, 1); } } - } + for (let attackType in this.startedAttacks) - { + for (let i = 0; i < this.startedAttacks[attackType].length; ++i) { let attack = this.startedAttacks[attackType][i]; @@ -313,7 +313,7 @@ this.startedAttacks[attackType].splice(i--, 1); } } - } + // creating plans after updating because an aborted plan might be reused in that case. @@ -339,7 +339,7 @@ else if (unexecutedAttacks.Attack == 0 && unexecutedAttacks.HugeAttack == 0 && this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length < Math.min(2, 1 + Math.round(gameState.getPopulationMax()/100)) && (this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length == 0 || gameState.getPopulationMax() - gameState.getPopulation() > 12)) - { + if (barracksNb >= 1 && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.getPhaseName(2))) || !gameState.ai.HQ.baseManagers[1]) // if we have no base ... nothing else to do than attack { @@ -357,7 +357,7 @@ } this.attackNumber++; } - } + if (unexecutedAttacks.Raid === 0 && gameState.ai.HQ.defenseManager.targetList.length) { @@ -383,17 +383,17 @@ m.AttackManager.prototype.getPlan = function(planName) { for (let attackType in this.upcomingAttacks) - { + for (let attack of this.upcomingAttacks[attackType]) if (attack.getName() == planName) return attack; - } + for (let attackType in this.startedAttacks) - { + for (let attack of this.startedAttacks[attackType]) if (attack.getName() == planName) return attack; - } + return undefined; }; @@ -465,7 +465,7 @@ veto[i] = true; // No rush if enemy too well defended (i.e. iberians) if (attack.type == "Rush") - { + for (let i = 1; i < gameState.sharedScript.playersData.length; ++i) { if (!gameState.isPlayerEnemy(i) || veto[i]) @@ -479,7 +479,7 @@ if (enemyDefense > 6) veto[i] = true; } - } + // then if not a huge attack, continue attacking our previous target as long as it has some entities, // otherwise target the most accessible one @@ -801,5 +801,4 @@ } }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/attackPlan.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -69,7 +69,7 @@ this.rallyPoint = rallyPoint; this.overseas = 0; if (gameState.ai.HQ.navalMap) - { + for (let structure of gameState.getEnemyStructures().values()) { if (this.target && structure.id() != this.target.id()) @@ -101,7 +101,7 @@ gameState.ai.HQ.navalManager.setMinimalTransportShips(gameState, sea, 1); } } - } + this.paused = false; this.maxCompletingTime = 0; @@ -139,23 +139,23 @@ { priority = 90; // 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]] }; - 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]] }; this.unitStat.ChampRangedInfantry = { "priority": 1, "minSize": 3, "targetSize": 18, "batchSize": 3, "classes": ["Infantry", "Ranged", "Champion"], "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]] }; - this.unitStat.RangedCavalry = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["Cavalry", "Ranged", "CitizenSoldier"], + this.unitStat.RangedCavalry = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["Cavalry", "Ranged", "CitizenSoldier"], "interests": [["strength", 2]] }; - this.unitStat.MeleeCavalry = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["Cavalry", "Melee", "CitizenSoldier"], + this.unitStat.MeleeCavalry = { "priority": 0.7, "minSize": 4, "targetSize": 20, "batchSize": 4, "classes": ["Cavalry", "Melee", "CitizenSoldier"], "interests": [["strength", 2]] }; - this.unitStat.ChampRangedCavalry = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["Cavalry", "Ranged", "Champion"], + this.unitStat.ChampRangedCavalry = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["Cavalry", "Ranged", "Champion"], "interests": [["strength", 3]] }; - this.unitStat.ChampMeleeCavalry = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["Cavalry", "Melee", "Champion"], + this.unitStat.ChampMeleeCavalry = { "priority": 1, "minSize": 3, "targetSize": 15, "batchSize": 3, "classes": ["Cavalry", "Melee", "Champion"], "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]] }; this.neededShips = 5; } @@ -164,7 +164,7 @@ priority = 70; 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"]] }; - 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"]] }; this.unitStat.Cavalry = { "priority": 1, "minSize": 2, "targetSize": 6, "batchSize": 2, "classes": ["Cavalry", "CitizenSoldier"], "interests": [["strength", 1]] }; @@ -399,7 +399,7 @@ // the completing step is used to return resources and regroup the units // so we check that we have no more forced order before starting the attack if (this.state == "completing") - { + // if our target was destroyed, go back to "unexecuted" state if (this.targetPlayer === undefined || !this.target || !gameState.getEntityById(this.target.id())) { @@ -416,7 +416,7 @@ return 1; return 2; } - } + if (this.Config.debug > 3 && gameState.ai.playedTurn % 50 === 0) this.debugAttack(); @@ -525,7 +525,7 @@ // Remove those units which were in a temporary bombing attack for (let unitIds of gameState.ai.HQ.attackManager.bombingAttacks.values()) - { + for (let entId of unitIds.values()) { let ent = gameState.getEntityById(entId); @@ -534,7 +534,7 @@ unitIds.delete(entId); ent.stopMoving(); } - } + let rallyPoint = this.rallyPoint; let rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint); @@ -872,18 +872,18 @@ targets.addEnt(ent); } else + + if (this.type == "Raid") + targets = this.raidTargetFinder(gameState); + else if (this.type == "Rush" || this.type == "Attack") { - if (this.type == "Raid") - targets = this.raidTargetFinder(gameState); - else if (this.type == "Rush" || this.type == "Attack") - { - targets = this.rushTargetFinder(gameState, this.targetPlayer); - if (!targets.hasEntities() && (this.hasSiegeUnits() || this.forced)) - targets = this.defaultTargetFinder(gameState, this.targetPlayer); - } - else + targets = this.rushTargetFinder(gameState, this.targetPlayer); + if (!targets.hasEntities() && (this.hasSiegeUnits() || this.forced)) targets = this.defaultTargetFinder(gameState, this.targetPlayer); } + else + targets = this.defaultTargetFinder(gameState, this.targetPlayer); + if (!targets.hasEntities()) return undefined; @@ -1143,12 +1143,8 @@ if (blocker && blocker.hasClass("StoneWall")) { -/* if (this.hasSiegeUnits()) - { */ - this.isBlocked = true; - return blocker; -/* } - return undefined; */ + this.isBlocked = true; + return blocker; } else if (blocker) { @@ -1345,60 +1341,60 @@ } } else - { - if (this.isBlocked && !ourUnit.hasClass("Ranged") && attacker.hasClass("Ranged")) + + if (this.isBlocked && !ourUnit.hasClass("Ranged") && attacker.hasClass("Ranged")) + + // do not react if our melee units are attacked by ranged one and we are blocked by walls + // TODO check that the attacker is from behind the wall + continue; + + else if (m.isSiegeUnit(attacker)) + { // if our unit is attacked by a siege unit, we'll send some melee units to help it. + let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5); + for (let ent of collec.values()) { - // do not react if our melee units are attacked by ranged one and we are blocked by walls - // TODO check that the attacker is from behind the wall - continue; - } - else if (m.isSiegeUnit(attacker)) - { // if our unit is attacked by a siege unit, we'll send some melee units to help it. - let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5); - for (let ent of collec.values()) - { - ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); - ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); - } + ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); + ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); } - else + } + else + { + // Look first for nearby units to help us if possible + let collec = this.unitCollection.filterNearest(ourUnit.position(), 2); + for (let ent of collec.values()) { - // Look first for nearby units to help us if possible - let collec = this.unitCollection.filterNearest(ourUnit.position(), 2); - for (let ent of collec.values()) - { - if (m.isSiegeUnit(ent)) - continue; - let orderData = ent.unitAIOrderData(); - if (orderData && orderData.length && orderData[0].target) - { - if (orderData[0].target === attacker.id()) - continue; - let target = gameState.getEntityById(orderData[0].target); - if (target && !target.hasClass("Structure") && !target.hasClass("Support")) - continue; - } - ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); - ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); - } - // Then the unit under attack: abandon its target (if it was a structure or a support) and retaliate - // also if our unit is attacking a range unit and the attacker is a melee unit, retaliate - let orderData = ourUnit.unitAIOrderData(); + if (m.isSiegeUnit(ent)) + continue; + let orderData = ent.unitAIOrderData(); if (orderData && orderData.length && orderData[0].target) { if (orderData[0].target === attacker.id()) continue; let target = gameState.getEntityById(orderData[0].target); if (target && !target.hasClass("Structure") && !target.hasClass("Support")) - { - if (!target.hasClass("Ranged") || !attacker.hasClass("Melee")) - continue; - } + continue; } - ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); - ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); + ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); + ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); + } + // Then the unit under attack: abandon its target (if it was a structure or a support) and retaliate + // also if our unit is attacking a range unit and the attacker is a melee unit, retaliate + let orderData = ourUnit.unitAIOrderData(); + if (orderData && orderData.length && orderData[0].target) + { + if (orderData[0].target === attacker.id()) + continue; + let target = gameState.getEntityById(orderData[0].target); + if (target && !target.hasClass("Structure") && !target.hasClass("Support")) + + if (!target.hasClass("Ranged") || !attacker.hasClass("Melee")) + continue; + } + ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); + ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); } + } let enemyUnits = gameState.getEnemyUnits(this.targetPlayer); @@ -1418,14 +1414,14 @@ if (!target || target.hasClass("Structure")) continue; if (!(targetId in unitTargets)) - { + if (m.isSiegeUnit(target) || target.hasClass("Hero")) unitTargets[targetId] = -8; else if (target.hasClass("Champion") || target.hasClass("Ship")) unitTargets[targetId] = -5; else unitTargets[targetId] = -3; - } + ++unitTargets[targetId]; } let veto = {}; @@ -1438,14 +1434,14 @@ if (this.type == "Rush") targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall", "Tower", "Fortress"], "vetoEntities": veto }; else - { - if (this.target.hasClass("Fortress")) - targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall"], "vetoEntities": veto }; - else if (this.target.hasClass("Palisade") || this.target.hasClass("StoneWall")) - targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Fortress"], "vetoEntities": veto }; - else - targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall", "Fortress"], "vetoEntities": veto }; - } + + if (this.target.hasClass("Fortress")) + targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall"], "vetoEntities": veto }; + else if (this.target.hasClass("Palisade") || this.target.hasClass("StoneWall")) + targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Fortress"], "vetoEntities": veto }; + else + targetClassesUnit = { "attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall", "Fortress"], "vetoEntities": veto }; + if (this.target.hasClass("Structure")) targetClassesSiege = { "attack": ["Structure"], "avoid": [], "vetoEntities": veto }; else @@ -1529,19 +1525,19 @@ let range = 60; let attackTypes = ent.attackTypes(); if (this.isBlocked) - { + if (attackTypes && attackTypes.indexOf("Ranged") !== -1) range = ent.attackRange("Ranged").max; else if (attackTypes && attackTypes.indexOf("Melee") !== -1) range = ent.attackRange("Melee").max; else range = 10; - } + else if (attackTypes && attackTypes.indexOf("Ranged") !== -1) range = 30 + ent.attackRange("Ranged").max; else if (ent.hasClass("Cavalry")) range += 30; - range = range * range; + range *= range; let entAccess = m.getLandAccess(gameState, ent); // Checking for gates if we're a siege unit. if (siegeUnit) @@ -1585,15 +1581,15 @@ } } else + + if (!ent.hasClass("Ranged")) { - if (!ent.hasClass("Ranged")) - { - let targetClasses = { "attack": targetClassesSiege.attack, "avoid": targetClassesSiege.avoid.concat("Ship"), "vetoEntities": veto }; - ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); - } - else - ent.attackMove(this.targetPos[0], this.targetPos[1], targetClassesSiege); + let targetClasses = { "attack": targetClassesSiege.attack, "avoid": targetClassesSiege.avoid.concat("Ship"), "vetoEntities": veto }; + ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); } + else + ent.attackMove(this.targetPos[0], this.targetPos[1], targetClassesSiege); + } else { @@ -1647,12 +1643,12 @@ { let targetClasses = targetClassesUnit; if (maybeUpdate && ent.unitAIState() === "INDIVIDUAL.COMBAT.APPROACHING") // we may be blocked by walls, attack everything - { + if (!ent.hasClass("Ranged") && !ent.hasClass("Ship")) targetClasses = { "attack": ["Unit", "Structure"], "avoid": ["Ship"], "vetoEntities": veto }; else targetClasses = { "attack": ["Unit", "Structure"], "vetoEntities": veto }; - } + else if (!ent.hasClass("Ranged") && !ent.hasClass("Ship")) targetClasses = { "attack": targetClassesUnit.attack, "avoid": targetClassesUnit.avoid.concat("Ship"), "vetoEntities": veto }; ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); @@ -1793,13 +1789,13 @@ } // Are we arrived at destination ? if (attackedNB > 1 && (attackedUnitNB || this.hasSiegeUnits())) - { + if (gameState.ai.HQ.territoryMap.getOwner(this.position) === this.targetPlayer || attackedNB > 3) { this.state = "arrived"; return true; } - } + // basically haven't moved an inch: very likely stuck) if (API3.SquareVectorDistance(this.position, this.position5TurnsAgo) < 10 && this.path.length > 0 && gameState.ai.playedTurn % 5 === 0) @@ -1919,7 +1915,7 @@ // Check if we could help any current attack let attackManager = gameState.ai.HQ.attackManager; for (let attackType in attackManager.startedAttacks) - { + for (let attack of attackManager.startedAttacks[attackType]) { if (attack.name == this.name) @@ -1938,7 +1934,7 @@ this.targetPos = this.target.position(); return true; } - } + // If not, let's look for another enemy if (!this.target) @@ -2167,5 +2163,4 @@ this.failed = undefined; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/baseManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/baseManager.js +++ binaries/data/mods/public/simulation/ai/petra/baseManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -181,21 +181,22 @@ let dist = API3.SquareVectorDistance(supply.position(), dropsitePos); if (dist < maxDistResourceSquare) - { + if (dist < maxDistResourceSquare/16) // distmax/4 nearby.push({ "dropsite": dropsiteId, "id": supply.id(), "ent": supply, "dist": dist }); else if (dist < maxDistResourceSquare/4) // distmax/2 medium.push({ "dropsite": dropsiteId, "id": supply.id(), "ent": supply, "dist": dist }); else faraway.push({ "dropsite": dropsiteId, "id": supply.id(), "ent": supply, "dist": dist }); - } + }); nearby.sort((r1, r2) => r1.dist - r2.dist); medium.sort((r1, r2) => r1.dist - r2.dist); faraway.sort((r1, r2) => r1.dist - r2.dist); -/* let debug = false; + /* + let debug = false; if (debug) { faraway.forEach(function(res){ @@ -207,7 +208,8 @@ nearby.forEach(function(res){ Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [res.ent.id()], "rgb": [0,0,2]}); }); - } */ + } + */ } // Allows all allies to use this dropsite except if base anchor to be sure to keep @@ -227,14 +229,14 @@ let removeSupply = function(entId, supply){ for (let i = 0; i < supply.length; ++i) - { + // exhausted resource, remove it from this list if (!supply[i].ent || !gameState.getEntityById(supply[i].id)) supply.splice(i--, 1); // resource assigned to the removed dropsite, remove it else if (supply[i].dropsite == entId) supply.splice(i--, 1); - } + }; for (let type in this.dropsiteSupplies) @@ -430,12 +432,12 @@ if (gameState.ai.playedTurn < this.gatherers[type].nextCheck) continue; for (let ent of this.gatherersByType(gameState, type).values()) - { + if (ent.unitAIState() == "INDIVIDUAL.GATHER.GATHERING") ++this.gatherers[type].used; else if (ent.unitAIState() == "INDIVIDUAL.RETURNRESOURCE.APPROACHING") ++this.gatherers[type].lost; - } + // TODO add also a test on remaining resources. let total = this.gatherers[type].used + this.gatherers[type].lost; if (total > 150 || total > 60 && type != "wood") @@ -447,14 +449,14 @@ if (newDP.quality > 50 && gameState.ai.HQ.canBuild(gameState, "structures/{civ}_storehouse")) queues.dropsites.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse", { "base": this.ID, "type": type }, newDP.pos)); else if (!gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre")).hasEntities() && !queues.civilCentre.hasQueuedUnits()) - { + // No good dropsite, try to build a new base if no base already planned, // and if not possible, be less strict on dropsite quality. if ((!gameState.ai.HQ.canExpand || !gameState.ai.HQ.buildNewBase(gameState, queues, type)) && newDP.quality > Math.min(25, 50*0.15/ratio) && gameState.ai.HQ.canBuild(gameState, "structures/{civ}_storehouse")) queues.dropsites.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse", { "base": this.ID, "type": type }, newDP.pos)); - } + } this.gatherers[type].nextCheck = gameState.ai.playedTurn + 20; this.gatherers[type].used = 0; @@ -510,12 +512,12 @@ roleless = this.units.filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "role"))).values(); for (let ent of roleless) - { + if (ent.hasClass("Worker") || ent.hasClass("CitizenSoldier") || ent.hasClass("FishingBoat")) ent.setMetadata(PlayerID, "role", "worker"); else if (ent.hasClass("Support") && ent.hasClass("Elephant")) ent.setMetadata(PlayerID, "role", "worker"); - } + }; /** @@ -766,7 +768,7 @@ let availableResources = gameState.ai.queueManager.getAvailableResources(gameState); let builderRatio = 1; for (let res of Resources.GetCodes()) - { + if (availableResources[res] < 200) { builderRatio = 0.2; @@ -774,7 +776,7 @@ } else if (availableResources[res] < 1000) builderRatio = Math.min(builderRatio, availableResources[res] / 1000); - } + for (let target of foundations.values()) { @@ -1106,6 +1108,4 @@ this.anchor = this.anchorId !== undefined ? gameState.getEntityById(this.anchorId) : undefined; }; -return m; - -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/buildManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/buildManager.js +++ binaries/data/mods/public/simulation/ai/petra/buildManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -27,7 +27,7 @@ m.BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment) { for (let buildable of ent.buildableEntities(civ)) - { + if (this.builderCounters.has(buildable)) { let count = this.builderCounters.get(buildable) + increment; @@ -42,7 +42,7 @@ this.builderCounters.set(buildable, increment); else API3.warn(" Petra error in incrementBuilderCounters for " + buildable + " not yet set"); - } + }; /** Update the builders counters */ @@ -169,5 +169,4 @@ this[key] = data[key]; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/chatHelper.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/chatHelper.js +++ binaries/data/mods/public/simulation/ai/petra/chatHelper.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { m.launchAttackMessages = { @@ -238,5 +238,4 @@ }); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/config.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/config.js +++ binaries/data/mods/public/simulation/ai/petra/config.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { m.Config = function(difficulty, behavior) @@ -46,7 +46,7 @@ "armyMergeSize": 1400 // squared. }; - // Additional buildings that the AI does not yet know when to build + // Additional buildings that the AI does not yet know when to build // and that it will try to build on phase 3 when enough resources. this.buildings = { @@ -253,5 +253,4 @@ this[key] = data[key]; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/defenseArmy.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/defenseArmy.js +++ binaries/data/mods/public/simulation/ai/petra/defenseArmy.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** Armies used by the defense manager. @@ -169,7 +169,7 @@ plan.removeUnit(gameState, ent); } -/* + /* // 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 { @@ -385,12 +385,12 @@ { // copy over all parameters. for (let i in otherArmy.assignedAgainst) - { + if (this.assignedAgainst[i] === undefined) this.assignedAgainst[i] = otherArmy.assignedAgainst[i]; else this.assignedAgainst[i] = this.assignedAgainst[i].concat(otherArmy.assignedAgainst[i]); - } + for (let i in otherArmy.assignedTo) this.assignedTo[i] = otherArmy.assignedTo[i]; @@ -481,12 +481,12 @@ let entStrength; if (ent.hasClass("Structure")) - { + if (ent.owner() !== PlayerID) entStrength = ent.getDefaultArrow() ? 6*ent.getDefaultArrow() : 4; else // small strength used only when we try to recover capture points entStrength = 2; - } + else entStrength = m.getMaxStrength(ent); @@ -510,7 +510,7 @@ // otherwise it would remove the old entity from this army list // TODO we should may-be reevaluate the strength for (let evt of events.EntityRenamed) // take care of promoted and packed units - { + if (this.foeEntities.indexOf(evt.entity) !== -1) { let ent = gameState.getEntityById(evt.newentity); @@ -538,18 +538,18 @@ this.assignedAgainst[against][this.assignedAgainst[against].indexOf(evt.entity)] = evt.newentity; } } - } + for (let evt of events.Garrison) this.removeFoe(gameState, evt.entity); for (let evt of events.OwnershipChanged) // captured - { + if (!gameState.isPlayerEnemy(evt.to)) this.removeFoe(gameState, evt.entity); else if (evt.from === PlayerID) this.removeOwn(gameState, evt.entity); - } + for (let evt of events.Destroy) { @@ -647,5 +647,4 @@ this[key] = data[key]; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/defenseManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/defenseManager.js +++ binaries/data/mods/public/simulation/ai/petra/defenseManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { m.DefenseManager = function(Config) @@ -52,14 +52,14 @@ } } for (let enemy in attackingArmies) - { + for (let ally in attackingArmies[enemy]) { if (this.attackedAllies[ally] === undefined) this.attackedAllies[ally] = 0; this.attackedAllies[ally] += 1; } - } + this.checkEnemyArmies(gameState); this.checkEnemyUnits(gameState); this.assignDefenders(gameState); @@ -70,12 +70,12 @@ m.DefenseManager.prototype.makeIntoArmy = function(gameState, entityID, type = "default") { if (type == "default") - { + // Try to add it to an existing army. for (let army of this.armies) if (army.getType() == type && army.addFoe(gameState, entityID)) return; // over - } + // Create a new army for it. let army = new m.DefenseArmy(gameState, [entityID], type); @@ -179,7 +179,7 @@ if (territoryOwner != PlayerID && this.attackedAllies[territoryOwner] && this.attackedAllies[territoryOwner] > 1 && this.GetCooperationLevel(territoryOwner) > 0.7) - { + for (let building of gameState.getAllyStructures(territoryOwner).values()) { if (building.foundationProgress() == 0 || @@ -188,7 +188,7 @@ if (!this.territoryMap.isBlinking(building.position())) return true; } - } + // Update the number of enemies attacking this ally let enemy = entity.owner(); @@ -211,7 +211,7 @@ if (i == PlayerID) { if (!this.armies.length) - { + // check if we can recover capture points from any of our notdecaying structures for (let ent of gameState.getOwnStructures().values()) { @@ -229,7 +229,7 @@ this.makeIntoArmy(gameState, ent.id(), "capturing"); break; } - } + return; } else if (!gameState.isPlayerEnemy(i)) @@ -328,7 +328,7 @@ if (!gameState.isPlayerEnemy(owner)) { if (gameState.isPlayerMutualAlly(owner)) - { + // update the number of enemies attacking this ally for (let id of army.foeEntities) { @@ -343,7 +343,7 @@ this.attackingArmies[enemy][owner] += 1; break; } - } + continue; } else if (owner != 0) // enemy army back in its territory @@ -530,14 +530,14 @@ army.checkEvents(gameState, events); for (let evt of events.OwnershipChanged) // capture events - { + if (gameState.isPlayerMutualAlly(evt.from) && evt.to > 0) { let ent = gameState.getEntityById(evt.entity); if (ent && ent.hasClass("CivCentre")) // one of our cc has been captured gameState.ai.HQ.attackManager.switchDefenseToAttack(gameState, ent, { "range": 150 }); } - } + let allAttacked = {}; for (let evt of events.Attacked) @@ -552,14 +552,14 @@ let attacker = gameState.getEntityById(evt.attacker); if (attacker && gameState.isEntityOwn(attacker) && gameState.isEntityEnemy(target) && !attacker.hasClass("Ship") && (!target.hasClass("Structure") || target.attackRange("Ranged"))) - { + // If enemies are in range of one of our defensive structures, garrison it for arrow multiplier // (enemy non-defensive structure are not considered to stay in sync with garrisonManager) if (attacker.position() && attacker.isGarrisonHolder() && attacker.getArrowMultiplier() && (target.owner() != 0 || !target.hasClass("Unit") || target.unitAIState() && target.unitAIState().split(".")[1] == "COMBAT")) this.garrisonUnitsInside(gameState, attacker, { "attacker": target }); - } + if (!gameState.isEntityOwn(target)) continue; @@ -756,13 +756,13 @@ let typeGarrison = data.type || "protection"; let allowMelee = gameState.ai.HQ.garrisonManager.allowMelee(target); if (allowMelee === undefined) - { + // Should be kept in sync with garrisonManager to avoid garrisoning-ungarrisoning some units if (data.attacker) allowMelee = data.attacker.hasClass("Structure") ? data.attacker.attackRange("Ranged") : !m.isSiegeUnit(data.attacker); else allowMelee = true; - } + let units = gameState.getOwnUnits().filter(ent => { if (!ent.position()) return false; @@ -953,5 +953,4 @@ } }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js +++ binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -79,7 +79,7 @@ let tribute = {}; let toSend = false; for (let res in allyResources) - { + if (donor && availableResources[res] > 200 && allyResources[res] < 0.2 * availableResources[res]) { tribute[res] = Math.floor(0.3*availableResources[res] - allyResources[res]); @@ -99,7 +99,7 @@ if (!mostNeeded) mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState); for (let k = 0; k < 2; ++k) - { + if (mostNeeded[k].type == res && mostNeeded[k].wanted > 0) { this.nextTributeRequest.set("all", gameState.ai.elapsedTime + 90); @@ -109,9 +109,9 @@ API3.warn("Tribute on " + res + " requested to player " + i); break; } - } + } - } + if (!toSend) continue; if (this.Config.debug > 1) @@ -156,12 +156,12 @@ continue; let tributes = 0; for (let key in evt.amounts) - { + if (key === "food") tributes += evt.amounts[key]; else tributes += 2*evt.amounts[key]; - } + this.Config.personality.cooperative = Math.min(1, this.Config.personality.cooperative + 0.0001 * tributes); } @@ -235,7 +235,7 @@ // Our diplomacy will have changed already if the response was "accept" if (request.status === "waitingForTribute") - { + Engine.PostCommand(PlayerID, { "type": "tribute-request", "source": PlayerID, @@ -243,7 +243,7 @@ "resourceWanted": request.wanted, "resourceType": request.type }); - } + } // An AI player we sent a diplomacy request to demanded we send them a tribute @@ -432,7 +432,7 @@ { for (let [player, data] of this.receivedDiplomacyRequests) if (data.status === "waitingForTribute" && gameState.ai.elapsedTime > data.warnTime) - { + if (data.sentWarning) { this.receivedDiplomacyRequests.delete(player); @@ -447,7 +447,7 @@ "type": data.type }); } - } + }; /** @@ -555,5 +555,4 @@ this[key] = data[key]; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/entityExtend.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/entityExtend.js +++ binaries/data/mods/public/simulation/ai/petra/entityExtend.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** returns true if this unit should be considered as a siege unit */ @@ -325,10 +325,10 @@ m.getHolder = function(gameState, ent) { for (let holder of gameState.getEntities().values()) - { + if (holder.isGarrisonHolder() && holder.garrisoned().indexOf(ent.id()) !== -1) return holder; - } + return undefined; }; @@ -437,5 +437,4 @@ " PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy")); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/garrisonManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/garrisonManager.js +++ binaries/data/mods/public/simulation/ai/petra/garrisonManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -149,12 +149,12 @@ if (ent.hasClass("Structure")) around.defenseStructure = true; else if (m.isSiegeUnit(ent)) - { + if (ent.attackTypes().indexOf("Melee") !== -1) around.meleeSiege = true; else around.rangeSiege = true; - } + else { around.unit = true; @@ -369,5 +369,4 @@ this[key] = data[key]; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/headquarters.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -209,7 +209,7 @@ } for (let evt of events.Destroy) - { + // Let's check we haven't lost an important building here. if (evt && !evt.SuccessfulFoundation && evt.entityObj && evt.metadata && evt.metadata[PlayerID] && evt.metadata[PlayerID].base) @@ -226,7 +226,7 @@ if (evt.metadata[PlayerID].baseAnchor && evt.metadata[PlayerID].baseAnchor === true) base.anchorLost(gameState, ent); } - } + for (let evt of events.EntityRenamed) { @@ -255,13 +255,13 @@ // Let's get a few units from other bases there to build this. let builders = this.bulkPickWorkers(gameState, newbase, 10); if (builders !== false) - { + builders.forEach(worker => { worker.setMetadata(PlayerID, "base", newbase.ID); worker.setMetadata(PlayerID, "subrole", "builder"); worker.setMetadata(PlayerID, "target-foundation", ent.id()); }); - } + } else if (ent.getMetadata(PlayerID, "base") == -2) // anchorless base around a dock { @@ -269,13 +269,13 @@ // Let's get a few units from other bases there to build this. let builders = this.bulkPickWorkers(gameState, newbase, 4); if (builders != false) - { + builders.forEach(worker => { worker.setMetadata(PlayerID, "base", newbase.ID); worker.setMetadata(PlayerID, "subrole", "builder"); worker.setMetadata(PlayerID, "target-foundation", ent.id()); }); - } + } } @@ -395,7 +395,7 @@ } for (let evt of events.TrainingFinished) - { + for (let entId of evt.entities) { let ent = gameState.getEntityById(entId); @@ -411,10 +411,10 @@ this.garrisonManager.registerHolder(gameState, holder); } else if (ent.getMetadata(PlayerID, "garrisonType")) - { + // we were supposed to be autogarrisoned, but this has failed (may-be full) ent.setMetadata(PlayerID, "garrisonType", undefined); - } + // Check if this unit is no more needed in its attack plan // (happen when the training ends after the attack is started or aborted) @@ -463,7 +463,7 @@ ent.moveToRange(goal[0], goal[1]); } } - } + for (let evt of events.TerritoryDecayChanged) { @@ -482,7 +482,7 @@ } if (addBase) - { + if (!this.firstBaseConfig) { // This is our first base, let us configure our starting resources @@ -495,7 +495,7 @@ this.saveSpace = undefined; this.maxFields = false; } - } + // Then deals with decaying structures: destroy them if being lost to enemy (except in easier difficulties) if (this.Config.difficulty < 2) @@ -773,7 +773,7 @@ let aValue = 0.1; let bValue = 0.1; for (let param of parameters) - { + if (param[0] == "strength") { aValue += m.getMaxStrength(a[1]) * param[1]; @@ -807,7 +807,7 @@ } else API3.warn(" trainMoreUnits avec non prevu " + uneval(param)); - } + return -aValue/aCost + bValue/bCost; }); return units[0][0]; @@ -1051,7 +1051,7 @@ continue; if (minDist > maxAccessDisfavored) // Disfavor if quite far from any allied cc - { + if (!accessible) { if (minDist > maxNoAccessDisfavored) @@ -1061,12 +1061,12 @@ } else norm *= 0.5; - } + // Not near any of our dropsite, except for oversea docks oversea = !accessible && dpList.some(dp => m.getLandAccess(gameState, dp.ent) == index); if (!oversea) - { + for (let dp of dpList) { let dist = API3.SquareVectorDistance(dp.pos, pos); @@ -1078,7 +1078,7 @@ else if (dist < 6400) norm *= 0.5; } - } + if (norm == 0) continue; } @@ -1092,7 +1092,7 @@ val += gameState.sharedScript.ccResourceMaps[res].map[j]; val *= norm; - // If oversea, be just above threshold to be accepted if nothing else + // If oversea, be just above threshold to be accepted if nothing else if (oversea) val = Math.max(val, cut + 0.1); @@ -1378,13 +1378,13 @@ // do not keep it if gain is too small, except if this is our first BarterMarket let idx; if (expectedGain < this.tradeManager.minimalGain) - { + if (template.hasClass("BarterMarket") && !gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities()) idx = -1; // needed by queueplanBuilding manager to keep that market else return false; - } + else idx = this.basesMap.map[bestJdx]; @@ -1565,7 +1565,7 @@ let cost = queues.economicBuilding.plans[0].getCost(); queueManager.setAccounts(gameState, cost, "economicBuilding"); if (!queueManager.canAfford("economicBuilding", cost)) - { + for (let q in queueManager.queues) { if (q == "economicBuilding") @@ -1574,7 +1574,7 @@ if (queueManager.canAfford("economicBuilding", cost)) break; } - } + } return; } @@ -1624,12 +1624,12 @@ let highLevel = 0; let lowLevel = 0; for (let res in cost) - { + if (resources[res] && resources[res] > 0.7 * cost[res]) ++highLevel; else if (!resources[res] || resources[res] < 0.3 * cost[res]) ++lowLevel; - } + if (highLevel == 0 || lowLevel > 1) return; } @@ -1750,7 +1750,7 @@ let freeSlots = gameState.getPopulationLimit() + HouseNb*popBonus - this.getAccountedPopulation(gameState); let priority; if (freeSlots < 5) - { + if (this.buildManager.isUnbuildable(gameState, house)) { if (this.Config.debug > 1) @@ -1759,7 +1759,7 @@ } else priority = 2*this.Config.priorities.house; - } + else priority = this.Config.priorities.house; @@ -1841,7 +1841,7 @@ return; if (!this.saveResources && (this.currentPhase > 2 || gameState.isResearching(gameState.getPhaseName(3)))) - { + // try to build fortresses if (this.canBuild(gameState, "structures/{civ}_fortress")) { @@ -1860,7 +1860,7 @@ return; } } - } + if (this.Config.Military.numSentryTowers && this.currentPhase < 2 && this.canBuild(gameState, "structures/{civ}_sentry_tower")) { @@ -2004,7 +2004,7 @@ nAdvanced += gameState.countEntitiesAndQueuedByType(advanced, true); if (!nAdvanced || nAdvanced < this.bAdvanced.length && this.getAccountedPopulation(gameState) > 110) - { + for (let advanced of this.bAdvanced) { if (gameState.countEntitiesAndQueuedByType(advanced, true) > 0 || !this.canBuild(gameState, advanced)) @@ -2019,7 +2019,7 @@ queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, advanced)); return; } - } + }; /** @@ -2107,15 +2107,15 @@ // in which case we prefer melee units let numGarrisoned = this.garrisonManager.numberOfGarrisonedUnits(nearestAnchor); if (nearestAnchor._entity.trainingQueue) - { + for (let item of nearestAnchor._entity.trainingQueue) - { + if (item.metadata && item.metadata.garrisonType) numGarrisoned += item.count; else if (!item.progress && (!item.metadata || !item.metadata.trainer)) nearestAnchor.stopProduction(item.id); - } - } + + let autogarrison = numGarrisoned < nearestAnchor.garrisonMax() && nearestAnchor.hitpoints() > nearestAnchor.garrisonEjectHealth() * nearestAnchor.maxHitpoints(); let rangedWanted = randBool() && autogarrison; @@ -2149,7 +2149,7 @@ let cost = new API3.Resources(templateFound[1].cost()); queueManager.setAccounts(gameState, cost, "emergency"); if (!queueManager.canAfford("emergency", cost)) - { + for (let q in queueManager.queues) { if (q == "emergency") @@ -2158,7 +2158,7 @@ if (queueManager.canAfford("emergency", cost)) break; } - } + let metadata = { "role": "worker", "base": nearestAnchor.getMetadata(PlayerID, "base"), "plan": -1, "trainer": nearestAnchor.id() }; if (autogarrison) metadata.garrisonType = "protection"; @@ -2439,7 +2439,7 @@ m.HQ.prototype.assignGatherers = function() { for (let base of this.baseManagers) - { + for (let worker of base.workers.values()) { if (worker.unitAIState().split(".")[1] != "RETURNRESOURCE") @@ -2449,7 +2449,7 @@ continue; this.AddTCGatherer(orders[1].target); } - } + }; m.HQ.prototype.isDangerousLocation = function(gameState, pos, radius) @@ -2621,7 +2621,7 @@ { let pop = gameState.getPopulation(); for (let ent of gameState.getOwnTrainingFacilities().values()) - { + for (let item of ent.trainingQueue()) { if (!item.unitTemplate) @@ -2630,7 +2630,7 @@ if (unitPop) pop += item.count * unitPop; } - } + this.turnCache.accountedPopulation = pop; } return this.turnCache.accountedPopulation; @@ -2645,14 +2645,14 @@ { let workers = gameState.getOwnEntitiesByRole("worker", true).length; for (let ent of gameState.getOwnTrainingFacilities().values()) - { + for (let item of ent.trainingQueue()) { if (!item.metadata || !item.metadata.role || item.metadata.role != "worker") continue; workers += item.count; } - } + this.turnCache.accountedWorkers = workers; } return this.turnCache.accountedWorkers; @@ -2683,14 +2683,16 @@ this.phasing = 0; } -/* if (this.Config.debug > 1) + /* + if (this.Config.debug > 1) { gameState.getOwnUnits().forEach (function (ent) { if (!ent.position()) return; m.dumpEntity(ent); }); - } */ + } + */ this.checkEvents(gameState, events); this.navalManager.checkEvents(gameState, queues, events); @@ -2758,7 +2760,7 @@ this.currentBase %= this.baseManagers.length; activeBase = this.baseManagers[this.currentBase++].update(gameState, queues, events); --nbBases; -// TODO what to do with this.reassignTerritories(this.baseManagers[this.currentBase]); + // TODO what to do with this.reassignTerritories(this.baseManagers[this.currentBase]); } while (!activeBase && nbBases != 0); @@ -2891,6 +2893,4 @@ this.victoryManager.Deserialize(data.victoryManager); }; -return m; - -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/mapModule.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/mapModule.js +++ binaries/data/mods/public/simulation/ai/petra/mapModule.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** map functions */ @@ -62,15 +62,15 @@ continue; } else - { - if (!buildEnemy) - continue; - } + + if (!buildEnemy) + continue; + let x = ratio * (k % territoryMap.width); let y = ratio * Math.floor(k / territoryMap.width); for (let ix = 0; ix < ratio; ++ix) - { + for (let iy = 0; iy < ratio; ++iy) { let i = x + ix + (y + iy)*passabilityMap.width; @@ -79,7 +79,7 @@ if (!(passabilityMap.data[i] & obstructionMask)) obstructionTiles[i] = 255; } - } + } let map = new API3.Map(gameState.sharedScript, "passability", obstructionTiles); @@ -193,7 +193,7 @@ } } -// map.dumpIm("border.png", 5); + // map.dumpIm("border.png", 5); return map; }; @@ -217,5 +217,4 @@ }); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/navalManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/navalManager.js +++ binaries/data/mods/public/simulation/ai/petra/navalManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -64,7 +64,7 @@ } for (let i = 0; i < gameState.ai.accessibility.regionSize.length; ++i) - { + if (!gameState.ai.HQ.navalRegions[i]) { // push dummies @@ -101,7 +101,7 @@ this.neededTransportShips.push(0); this.neededWarShips.push(0); } - } + if (deserializing) return; @@ -125,7 +125,7 @@ } // and keep only thoses with enough room around when possible for (let land in this.landingZones) - { + for (let sea in this.landingZones[land]) { let landing = this.landingZones[land][sea]; @@ -147,12 +147,12 @@ } nbcut = Math.min(2, nbcut); for (let i of landing) - { + if (nbaround[i] < nbcut) landing.delete(i); - } + } - } + // Assign our initial docks and ships for (let ship of this.ships.values()) @@ -716,7 +716,7 @@ return; if (gameState.ai.HQ.getAccountedPopulation(gameState) > this.Config.Economy.popForDock) - { + if (queues.dock.countQueuedUnitsWithClass("NavalMarket") === 0 && !gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("NavalMarket"), API3.Filters.isFoundation())).hasEntities() && gameState.ai.HQ.canBuild(gameState, "structures/{civ}_dock")) @@ -741,7 +741,7 @@ } } } - } + if (gameState.currentPhase() < 2 || gameState.ai.HQ.getAccountedPopulation(gameState) < this.Config.Economy.popPhase2 + 15 || queues.militaryBuilding.hasQueuedUnits()) @@ -891,6 +891,4 @@ } }; - -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/queue.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queue.js +++ binaries/data/mods/public/simulation/ai/petra/queue.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -22,7 +22,7 @@ if (!newPlan) return; for (let plan of this.plans) - { + if (newPlan.category === "unit" && plan.type == newPlan.type && plan.number + newPlan.number <= plan.maxMerge) { plan.addItem(newPlan.number); @@ -30,7 +30,7 @@ } else if (newPlan.category === "technology" && plan.type === newPlan.type) return; - } + this.plans.push(newPlan); }; @@ -163,5 +163,4 @@ } }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/queueManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueManager.js +++ binaries/data/mods/public/simulation/ai/petra/queueManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { // This takes the input queues and picks which items to fund with resources until no more resources are left to distribute. @@ -116,12 +116,12 @@ break; let cost = queue.plans[j].getCost(); if (queue.plans[j].isGo(gameState)) - { + if (j === 0) total = totalShort; else total = totalMedium; - } + else total = totalLong; for (let type in total) @@ -303,7 +303,7 @@ available -= toAdd; } if (missing && available > 0) // distribute the rest (due to floor) in any queue - { + for (let j in tempPrio) { let toAdd = Math.min(maxNeed[j], available); @@ -312,7 +312,7 @@ if (available <= 0) break; } - } + if (available < 0) API3.warn("Petra: problem with remaining " + res + " in queueManager " + available); } @@ -365,7 +365,7 @@ { let item = queue.getNext(); if (this.accounts[name].canAfford(item.getCost()) && item.canStart(gameState)) - { + // canStart may update the cost because of the costMultiplier so we must check it again if (this.accounts[name].canAfford(item.getCost())) { @@ -374,7 +374,7 @@ queue.startNext(gameState); queue.switched = 0; } - } + } else if (!queue.hasQueuedUnits()) { @@ -595,5 +595,4 @@ this.queueArrays.sort((a, b) => data.priorities[b[0]] - data.priorities[a[0]]); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/queueplan.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueplan.js +++ binaries/data/mods/public/simulation/ai/petra/queueplan.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** * Common functions and variables to all queue plans. @@ -66,5 +66,4 @@ { }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js +++ binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -97,11 +97,11 @@ else if (pos.xx === undefined || pos.x == pos.xx && pos.z == pos.zz) builder.construct(this.type, pos.x, pos.z, pos.angle, this.metadata); else // try with the lowest, move towards us unless we're same - { + for (let step = 0; step <= 1; step += 0.2) builder.construct(this.type, step*pos.x + (1-step)*pos.xx, step*pos.z + (1-step)*pos.zz, pos.angle, this.metadata); - } + this.onStart(gameState); Engine.ProfileStop(); @@ -131,7 +131,7 @@ } if (!this.position) - { + if (template.hasClass("CivCentre")) { let pos; @@ -177,7 +177,7 @@ else if (!pos) return false; } - } + // Compute each tile's closeness to friendly structures: @@ -204,14 +204,14 @@ placement.set(j, 45); } else - { + for (let j = 0; j < placement.map.length; ++j) if (HQ.basesMap.map[j] != 0) placement.set(j, 45); - } + if (!HQ.requireHouses || !template.hasClass("House")) - { + gameState.getOwnStructures().forEach(function(ent) { let pos = ent.position(); let x = Math.round(pos[0] / cellSize); @@ -219,12 +219,12 @@ let struct = m.getBuiltEntity(gameState, ent); if (struct.resourceDropsiteTypes() && struct.resourceDropsiteTypes().indexOf("food") != -1) - { + if (template.hasClass("Field") || template.hasClass("Corral")) placement.addInfluence(x, z, 80/cellSize, 50); else // If this is not a field add a negative influence because we want to leave this area for fields placement.addInfluence(x, z, 80/cellSize, -20); - } + else if (template.hasClass("House")) { if (ent.hasClass("House")) @@ -245,9 +245,9 @@ else if (template.genericName() == "Rotary Mill" && ent.hasClass("Field")) placement.addInfluence(x, z, 60/cellSize, 40); }); - } + if (template.hasClass("Farmstead")) - { + for (let j = 0; j < placement.map.length; ++j) { let value = placement.map[j] - gameState.sharedScript.resourceMaps.wood.map[j]/3; @@ -255,7 +255,7 @@ value /= 2; // we need space around farmstead, so disfavor map border placement.set(j, value); } - } + } // Requires to be inside our territory, and inside our base territory if required @@ -268,7 +268,7 @@ { let base = this.metadata.base; for (let j = 0; j < placement.map.length; ++j) - { + if (HQ.basesMap.map[j] != base) placement.map[j] = 0; else if (placement.map[j] > 0) @@ -283,12 +283,12 @@ if (HQ.isNearInvadingArmy([x, z])) placement.map[j] = 0; } - } + } else - { + for (let j = 0; j < placement.map.length; ++j) - { + if (HQ.basesMap.map[j] == 0) placement.map[j] = 0; else if (placement.map[j] > 0) @@ -305,8 +305,8 @@ else if (favoredBase && HQ.basesMap.map[j] == favoredBase) placement.set(j, placement.map[j] + 100); } - } - } + + // Find the best non-obstructed: // Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close, @@ -650,24 +650,24 @@ { let count = 0; for (let j = 0; j < length-1; ++j) - { + if ((waterPoints[(i + j) % length]+1) % numPoints == waterPoints[(i + j + 1) % length]) ++count; else break; - } + consec[i] = count; } let start = 0; let count = 0; for (let c in consec) - { + if (consec[c] > count) { start = c; count = consec[c]; } - } + // If we've found a shoreline, stop searching if (count != numPoints-1) @@ -744,7 +744,7 @@ * if wantedSea is given, this tile should be inside this sea */ const around = [[ 1.0, 0.0], [ 0.87, 0.50], [ 0.50, 0.87], [ 0.0, 1.0], [-0.50, 0.87], [-0.87, 0.50], - [-1.0, 0.0], [-0.87,-0.50], [-0.50,-0.87], [ 0.0,-1.0], [ 0.50,-0.87], [ 0.87,-0.50]]; + [-1.0, 0.0], [-0.87, -0.50], [-0.50, -0.87], [ 0.0, -1.0], [ 0.50, -0.87], [ 0.87, -0.50]]; m.ConstructionPlan.prototype.isDockLocation = function(gameState, j, dimension, wantedLand, wantedSea) { @@ -857,7 +857,7 @@ let dxmax = size - dy; let ky = iy + dy; if (ky >= 0 && ky < w) - { + for (let dx = -dxmax; dx <= dxmax; ++dx) { let kx = ix + dx; @@ -868,12 +868,12 @@ total += weight * resourceMaps[k].map[kx + w * ky]; nbcell += weight; } - } + if (dy == 0) continue; ky = iy - dy; if (ky >= 0 && ky < w) - { + for (let dx = -dxmax; dx <= dxmax; ++dx) { let kx = ix + dx; @@ -884,7 +884,7 @@ total += weight * resourceMaps[k].map[kx + w * ky]; nbcell += weight; } - } + } } return nbcell ? total / nbcell : 0; @@ -947,5 +947,4 @@ this.cost.Deserialize(data.cost); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js +++ binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { m.ResearchPlan = function(gameState, type, rush = false) @@ -109,5 +109,4 @@ this.cost.Deserialize(data.cost); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js +++ binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { m.TrainingPlan = function(gameState, type, metadata, number = 1, maxMerge = 5) @@ -189,5 +189,4 @@ this.cost.Deserialize(data.cost); }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/researchManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/researchManager.js +++ binaries/data/mods/public/simulation/ai/petra/researchManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -99,7 +99,7 @@ continue; } for (let i in template.modifications) - { + if (gameState.ai.HQ.navalMap && template.modifications[i].value === "ResourceGatherer/Rates/food.fish") return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; else if (template.modifications[i].value === "ResourceGatherer/Rates/food.fruit") @@ -112,7 +112,7 @@ return { "name": tech[0], "increasePriority": false }; else if (template.modifications[i].value === "Attack/Ranged/MaxRange") return { "name": tech[0], "increasePriority": false }; - } + } return null; }; @@ -138,7 +138,7 @@ continue; } for (let i in template.modifications) - { + if (template.modifications[i].value === "ResourceGatherer/Rates/stone.rock") return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; else if (template.modifications[i].value === "ResourceGatherer/Rates/metal.ore") @@ -149,7 +149,7 @@ return { "name": tech[0], "increasePriority": false }; else if (template.modifications[i].value === "Health/IdleRegenRate") return { "name": tech[0], "increasePriority": false }; - } + } return null; }; @@ -240,5 +240,4 @@ { }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/startingStrategy.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/startingStrategy.js +++ binaries/data/mods/public/simulation/ai/petra/startingStrategy.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** * determines the strategy to adopt when starting a new game, depending on the initial conditions @@ -182,11 +182,11 @@ 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) this.landRegions[i] = true; else if (accessibility.regionType[i] === "land" && cellArea*accessibility.regionSize[i] > 320) - { + if (landIndex) { let sea = this.getSeaBetweenIndices(gameState, landIndex, i); @@ -207,7 +207,7 @@ this.navalRegions[seaIndex] = true; } } - } + else if (accessibility.regionType[i] === "water" && accessibility.regionSize[i] > minWaterSize) { this.navalMap = true; @@ -215,7 +215,7 @@ } else if (accessibility.regionType[i] === "water" && cellArea*accessibility.regionSize[i] > 3600) this.navalRegions[i] = true; - } + if (this.Config.debug < 3) return true; @@ -443,7 +443,7 @@ let startingSize = 0; let startingLand = []; for (let region in this.landRegions) - { + for (let base of this.baseManagers) { if (!base.anchor || base.accessIndex != +region) @@ -452,7 +452,7 @@ startingLand.push(base.accessIndex); break; } - } + let cell = gameState.getPassabilityMap().cellSize; startingSize = startingSize * cell * cell; if (this.Config.debug > 1) @@ -463,11 +463,11 @@ this.Config.Economy.popForDock = Math.min(this.Config.Economy.popForDock, 16); let num = Math.max(this.Config.Economy.targetNumFishers, 2); for (let land of startingLand) - { + for (let sea of gameState.ai.accessibility.regionLinks[land]) if (gameState.ai.HQ.navalRegions[sea]) this.navalManager.updateFishingBoats(sea, num); - } + this.maxFields = 1; this.needCorral = true; } @@ -480,9 +480,9 @@ let startingFood = gameState.getResources().food; let check = {}; for (let proxim of ["nearby", "medium", "faraway"]) - { + for (let base of this.baseManagers) - { + for (let supply of base.dropsiteSupplies.food[proxim]) { if (check[supply.id]) // avoid double counting as same resource can appear several time @@ -490,10 +490,10 @@ check[supply.id] = true; startingFood += supply.ent.resourceSupplyAmount(); } - } - } + + if (startingFood < 800) - { + if (startingSize < 25000) { this.needFish = true; @@ -501,14 +501,14 @@ } else this.needFarm = true; - } + // - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion) let startingWood = gameState.getResources().wood; check = {}; for (let proxim of ["nearby", "medium", "faraway"]) - { + for (let base of this.baseManagers) - { + for (let supply of base.dropsiteSupplies.wood[proxim]) { if (check[supply.id]) // avoid double counting as same resource can appear several time @@ -516,8 +516,8 @@ check[supply.id] = true; startingWood += supply.ent.resourceSupplyAmount(); } - } - } + + if (this.Config.debug > 1) API3.warn("startingWood: " + startingWood + " (cut at 8500 for no rush and 6000 for saveResources)"); if (startingWood < 6000) @@ -536,12 +536,12 @@ let allowed = Math.ceil((startingWood - 8500) / 3000); // Not useful to prepare rushing if too long ceasefire if (gameState.isCeasefireActive()) - { + if (gameState.ceasefireTimeRemaining > 900) allowed = 0; else if (gameState.ceasefireTimeRemaining > 600 && allowed > 1) allowed = 1; - } + this.attackManager.setRushes(allowed); } @@ -573,6 +573,4 @@ } }; -return m; - -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/tradeManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/tradeManager.js +++ binaries/data/mods/public/simulation/ai/petra/tradeManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -424,7 +424,7 @@ let candidate = { "gain": 0 }; let potential = { "gain": 0 }; let bestIndex = { "gain": 0 }; - let bestLand = { "gain": 0 }; + let bestLand = { "gain": 0 }; let mapSize = gameState.sharedScript.mapSize; let traderTemplatesGains = gameState.getTraderTemplatesGains(); @@ -462,7 +462,7 @@ if (m1.foundationProgress() === undefined && m2.foundationProgress() === undefined) { if (accessIndex) - { + if (gameState.ai.accessibility.regionType[accessIndex] == "water" && sea == accessIndex) { if (gain < bestIndex.gain) @@ -481,7 +481,7 @@ continue; bestLand = { "source": m1, "target": m2, "gain": gain, "land": land, "sea": sea }; } - } + if (gain < candidate.gain) continue; candidate = { "source": m1, "target": m2, "gain": gain, "land": land, "sea": sea }; @@ -601,14 +601,14 @@ return; // position found, but not enough gain compared to our present route if (this.Config.debug > 1) - { + if (this.potentialTradeRoute) API3.warn("turn " + gameState.ai.playedTurn + "we could have a new route with gain " + marketPos[3] + " instead of the present " + this.potentialTradeRoute.gain); else API3.warn("turn " + gameState.ai.playedTurn + "we could have a first route with gain " + marketPos[3]); - } + if (!this.tradeRoute) gameState.ai.queueManager.changePriority("economicBuilding", 2*this.Config.priorities.economicBuilding); @@ -664,7 +664,7 @@ let ret = {}; for (let key in route) - { + if (key == "source" || key == "target") { if (!route[key]) @@ -673,7 +673,7 @@ } else ret[key] = route[key]; - } + return ret; }; @@ -684,7 +684,7 @@ let ret = {}; for (let key in route) - { + if (key == "source" || key == "target") { ret[key] = gameState.getEntityById(route[key]); @@ -693,7 +693,7 @@ } else ret[key] = route[key]; - } + return ret; }; @@ -711,13 +711,12 @@ m.TradeManager.prototype.Deserialize = function(gameState, data) { for (let key in data) - { + if (key == "tradeRoute" || key == "potentialTradeRoute") this[key] = this.routeIdToEnt(gameState, data[key]); else this[key] = data[key]; - } + }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/transportPlan.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/transportPlan.js +++ binaries/data/mods/public/simulation/ai/petra/transportPlan.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -119,12 +119,12 @@ continue; ent.setMetadata(PlayerID, "onBoard", ship.id()); if (this.debug > 1) - { + if (ent.getMetadata(PlayerID, "role") == "attack") Engine.PostCommand(PlayerID, { "type": "set-shading-color", "entities": [ent.id()], "rgb": [2, 0, 0] }); else Engine.PostCommand(PlayerID, { "type": "set-shading-color", "entities": [ent.id()], "rgb": [0, 2, 0] }); - } + return; } @@ -240,7 +240,7 @@ ent.setMetadata(PlayerID, "endPos", undefined); ent.setMetadata(PlayerID, "onBoard", undefined); ent.setMetadata(PlayerID, "transport", undefined); - // TODO if the index of the endPos of the entity is !=, + // TODO if the index of the endPos of the entity is !=, // require again another transport (we could need land-sea-land-sea-land) } @@ -299,7 +299,7 @@ let shipTested = {}; for (let ent of this.units.values()) - { + if (!ent.getMetadata(PlayerID, "onBoard")) { ready = false; @@ -408,7 +408,7 @@ ent.setMetadata(PlayerID, "posGarrison", ent.position()); } } - } + if (this.needSplit) { @@ -557,7 +557,7 @@ { let ship = gameState.getEntityById(ent.getMetadata(PlayerID, "onBoard")); if (ship) - { + if (ship.garrisoned().indexOf(entId) != -1) ent.setMetadata(PlayerID, "onBoard", "onBoard"); else @@ -566,7 +566,7 @@ this.resetUnit(gameState, ent); ent.destroy(); } - } + else { API3.warn("Petra transportPlan problem: unit on ship, but no ship ???"); @@ -725,5 +725,4 @@ this.failed = false; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/victoryManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/victoryManager.js +++ binaries/data/mods/public/simulation/ai/petra/victoryManager.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -27,13 +27,13 @@ m.VictoryManager.prototype.init = function(gameState) { if (gameState.getVictoryConditions().has("wonder")) - { + for (let wonder of gameState.getOwnEntitiesByClass("Wonder", true).values()) this.criticalEnts.set(wonder.id(), { "guardsAssigned": 0, "guards": new Map() }); - } + if (gameState.getVictoryConditions().has("regicide")) - { + for (let hero of gameState.getOwnEntitiesByClass("Hero", true).values()) { let defaultStance = hero.hasClass("Soldier") ? "aggressive" : "passive"; @@ -46,16 +46,16 @@ "guards": new Map() // ids of ents who are currently guarding this hero }); } - } + if (gameState.getVictoryConditions().has("capture_the_relic")) - { + for (let relic of gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")).values()) - { + if (relic.owner() == PlayerID) this.criticalEnts.set(relic.id(), { "guardsAssigned": 0, "guards": new Map() }); - } - } + + }; /** @@ -359,7 +359,7 @@ { let numWorkers = gameState.getOwnEntitiesByRole("worker", true).length; if (numWorkers < 20) - { + for (let data of this.criticalEnts.values()) { for (let guardId of data.guards.keys()) @@ -384,7 +384,7 @@ if (numWorkers >= 20) break; } - } + let minWorkers = 25; let deltaWorkers = 3; @@ -744,5 +744,4 @@ this[key] = data[key]; }; -return m; -}(PETRA); +}(PETRA)); Index: binaries/data/mods/public/simulation/ai/petra/worker.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/worker.js +++ binaries/data/mods/public/simulation/ai/petra/worker.js @@ -1,4 +1,4 @@ -var PETRA = function(m) +(function(m) { /** @@ -49,7 +49,7 @@ break; } if (!hasDropsite) - { + for (let unit of gameState.getOwnUnits().filter(API3.Filters.byClass("Support")).values()) { if (!unit.position() || m.getLandAccess(gameState, unit) != plan.endIndex) @@ -60,7 +60,7 @@ hasDropsite = true; break; } - } + if (!hasDropsite) plan.removeUnit(gameState, ent); } @@ -206,14 +206,14 @@ // TODO if we already carry the max we can -> returnresources if (!ent.resourceCarrying() || !ent.resourceCarrying().length || ent.resourceCarrying()[0].type == ent.getMetadata(PlayerID, "gather-type")) - { + this.startGathering(gameState); - } + else if (!m.returnResources(gameState, ent)) // try to deposit resources - { + // no dropsite, abandon old resources and start gathering new ones this.startGathering(gameState); - } + } else if (unitAIStateOrder == "GATHER") { @@ -281,7 +281,7 @@ { let nearby = this.base.dropsiteSupplies[gatherType].nearby; if (!nearby.some(sup => sup.id == supplyId)) - { + if (nearby.length) ent.setMetadata(PlayerID, "supply", undefined); else @@ -290,7 +290,7 @@ if (!medium.some(sup => sup.id == supplyId) && medium.length) ent.setMetadata(PlayerID, "supply", undefined); } - } + } } } @@ -386,25 +386,25 @@ } } else // Perform some sanity checks + + if (unitAIStateOrder == "GATHER" || unitAIStateOrder == "RETURNRESOURCE") { - if (unitAIStateOrder == "GATHER" || unitAIStateOrder == "RETURNRESOURCE") + // we may have drifted towards ennemy territory during the hunt, if yes go home + let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(ent.position()); + if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally + this.startHunting(gameState); + else if (unitAIState == "INDIVIDUAL.RETURNRESOURCE.APPROACHING") { - // we may have drifted towards ennemy territory during the hunt, if yes go home - let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(ent.position()); - if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally - this.startHunting(gameState); - else if (unitAIState == "INDIVIDUAL.RETURNRESOURCE.APPROACHING") - { - // Check that UnitAI does not send us to an inaccessible dropsite - let dropsite = gameState.getEntityById(ent.unitAIOrderData()[0].target); - if (dropsite && dropsite.position() && this.entAccess != m.getLandAccess(gameState, dropsite)) - m.returnResources(gameState, ent); - } + // Check that UnitAI does not send us to an inaccessible dropsite + let dropsite = gameState.getEntityById(ent.unitAIOrderData()[0].target); + if (dropsite && dropsite.position() && this.entAccess != m.getLandAccess(gameState, dropsite)) + m.returnResources(gameState, ent); } } + } else if (subrole == "fisher") - { + if (ent.isIdle()) this.startFishing(gameState); else // if we have drifted towards ennemy territory during the fishing, go home @@ -413,7 +413,7 @@ if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally this.startFishing(gameState); } - } + }; m.Worker.prototype.retryWorking = function(gameState, subrole) @@ -544,7 +544,7 @@ } } if (resource == "food") // --> for food, try to gather from fields if any, otherwise build one if any - { + for (let base of gameState.ai.HQ.baseManagers) { if (base.ID == this.baseID) @@ -566,7 +566,7 @@ return true; } } - } + for (let base of gameState.ai.HQ.baseManagers) { if (base.ID == this.baseID) @@ -617,7 +617,7 @@ } } if (resource == "food") // --> for food, try to gather from fields if any, otherwise build one if any - { + for (let base of gameState.ai.HQ.baseManagers) { if (base.accessIndex == this.entAccess) @@ -637,7 +637,7 @@ return true; } } - } + for (let base of gameState.ai.HQ.baseManagers) { if (base.accessIndex == this.entAccess) @@ -1110,5 +1110,4 @@ return false; }; -return m; -}(PETRA); +}(PETRA));