diff --git a/binaries/data/mods/public/simulation/ai/common-api/baseAI.js b/binaries/data/mods/public/simulation/ai/common-api/baseAI.js index 92dfe38a59..0230aef112 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/baseAI.js +++ b/binaries/data/mods/public/simulation/ai/common-api/baseAI.js @@ -1,10 +1,6 @@ -var PlayerID = -1; +globalThis.PlayerID = -1; -var API3 = (function() { - -var m = {}; - -m.BaseAI = function(settings) +export function BaseAI(settings) { if (!settings) return; @@ -13,10 +9,10 @@ m.BaseAI = function(settings) // played turn, in case you don't want the AI to play every turn. this.turn = 0; -}; +} /** Return a simple object (using no classes etc) that will be serialized into saved games */ -m.BaseAI.prototype.Serialize = function() +BaseAI.prototype.Serialize = function() { return {}; }; @@ -25,12 +21,12 @@ m.BaseAI.prototype.Serialize = function() * Called after the constructor when loading a saved game, with 'data' being * whatever Serialize() returned */ -m.BaseAI.prototype.Deserialize = function(data, sharedScript) +BaseAI.prototype.Deserialize = function(data, sharedScript) { this.isDeserialized = true; }; -m.BaseAI.prototype.Init = function(state, playerID, sharedAI) +BaseAI.prototype.Init = function(state, playerID, sharedAI) { PlayerID = playerID; @@ -46,11 +42,11 @@ m.BaseAI.prototype.Init = function(state, playerID, sharedAI) }; /** AIs override this function */ -m.BaseAI.prototype.CustomInit = function() +BaseAI.prototype.CustomInit = function() { }; -m.BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI) +BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI) { PlayerID = playerID; this.events = sharedAI.events; @@ -65,16 +61,11 @@ m.BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI) }; /** AIs override this function */ -m.BaseAI.prototype.OnUpdate = function() +BaseAI.prototype.OnUpdate = function() { }; -m.BaseAI.prototype.chat = function(message) +BaseAI.prototype.chat = function(message) { Engine.PostCommand(PlayerID, { "type": "aichat", "message": message }); }; - -return m; - -}()); - diff --git a/binaries/data/mods/public/simulation/ai/common-api/class.js b/binaries/data/mods/public/simulation/ai/common-api/class.js index 6a17b9cfde..2712eb35c9 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/class.js +++ b/binaries/data/mods/public/simulation/ai/common-api/class.js @@ -1,10 +1,8 @@ -API3 = function(m) -{ /** * Provides a nicer syntax for defining classes, * with support for OO-style inheritance. */ -m.Class = function(data) +export function Class(data) { let ctor; if (data._init) @@ -19,8 +17,4 @@ m.Class = function(data) ctor.prototype[key] = data[key]; return ctor; -}; - -return m; - -}(API3); +} diff --git a/binaries/data/mods/public/simulation/ai/common-api/entity.js b/binaries/data/mods/public/simulation/ai/common-api/entity.js index b0870df435..b86cf490e5 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/entity.js +++ b/binaries/data/mods/public/simulation/ai/common-api/entity.js @@ -1,8 +1,8 @@ -API3 = function(m) -{ +import { Class } from "simulation/ai/common-api/class.js"; +import { VectorDistance } from "simulation/ai/common-api/utils.js"; // defines a template. -m.Template = m.Class({ +export const Template = Class({ "_init": function(sharedAI, templateName, template) { @@ -557,8 +557,8 @@ m.Template = m.Class({ // defines an entity, with a super Template. // also redefines several of the template functions where the only change is applying aura and tech modifications. -m.Entity = m.Class({ - "_super": m.Template, +export const Entity = Class({ + "_super": Template, "_init": function(sharedAI, entity) { @@ -860,7 +860,7 @@ m.Entity = m.Class({ if (this.position() !== undefined) { let direction = [this.position()[0] - point[0], this.position()[1] - point[1]]; - const norm = m.VectorDistance(point, this.position()); + const norm = VectorDistance(point, this.position()); if (norm === 0) direction = [1, 0]; else @@ -1009,7 +1009,3 @@ m.Entity = m.Class({ return this; } }); - -return m; - -}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api/entitycollection.js b/binaries/data/mods/public/simulation/ai/common-api/entitycollection.js index 2da47da19f..41123cf329 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/entitycollection.js +++ b/binaries/data/mods/public/simulation/ai/common-api/entitycollection.js @@ -1,7 +1,6 @@ -API3 = function(m) -{ +import { SquareVectorDistance } from "simulation/ai/common-api/utils.js"; -m.EntityCollection = function(sharedAI, entities = new Map(), filters = []) +export function EntityCollection(sharedAI, entities = new Map(), filters = []) { this._ai = sharedAI; this._entities = entities; @@ -13,9 +12,9 @@ m.EntityCollection = function(sharedAI, entities = new Map(), filters = []) Object.defineProperty(this, "length", { "get": () => this._entities.size }); this.frozen = false; -}; +} -m.EntityCollection.prototype.Serialize = function() +EntityCollection.prototype.Serialize = function() { const filters = []; for (const f of this._filters) @@ -27,7 +26,7 @@ m.EntityCollection.prototype.Serialize = function() }; }; -m.EntityCollection.prototype.Deserialize = function(data, sharedAI) +EntityCollection.prototype.Deserialize = function(data, sharedAI) { this._ai = sharedAI; for (const id of data.ents) @@ -48,37 +47,37 @@ m.EntityCollection.prototype.Deserialize = function(data, sharedAI) * this makes it easy to create entity collection that will auto-remove dead units * but never add new ones. */ -m.EntityCollection.prototype.freeze = function() +EntityCollection.prototype.freeze = function() { this.frozen = true; }; -m.EntityCollection.prototype.defreeze = function() +EntityCollection.prototype.defreeze = function() { this.frozen = false; }; -m.EntityCollection.prototype.toIdArray = function() +EntityCollection.prototype.toIdArray = function() { return Array.from(this._entities.keys()); }; -m.EntityCollection.prototype.toEntityArray = function() +EntityCollection.prototype.toEntityArray = function() { return Array.from(this._entities.values()); }; -m.EntityCollection.prototype.values = function() +EntityCollection.prototype.values = function() { return this._entities.values(); }; -m.EntityCollection.prototype.toString = function() +EntityCollection.prototype.toString = function() { return "[EntityCollection " + this.toEntityArray().join(" ") + "]"; }; -m.EntityCollection.prototype.filter = function(filter, thisp) +EntityCollection.prototype.filter = function(filter, thisp) { if (typeof filter === "function") filter = { "func": filter, "dynamicProperties": [] }; @@ -88,19 +87,19 @@ m.EntityCollection.prototype.filter = function(filter, thisp) if (filter.func.call(thisp, ent, id, this)) ret.set(id, ent); - return new m.EntityCollection(this._ai, ret, this._filters.concat([filter])); + return new EntityCollection(this._ai, ret, this._filters.concat([filter])); }; /** * Returns the (at most) n entities nearest to targetPos. */ -m.EntityCollection.prototype.filterNearest = function(targetPos, n) +EntityCollection.prototype.filterNearest = function(targetPos, n) { // Compute the distance of each entity const data = []; // [ [id, ent, distance], ... ] for (const [id, ent] of this._entities) if (ent.position()) - data.push([id, ent, m.SquareVectorDistance(targetPos, ent.position())]); + data.push([id, ent, SquareVectorDistance(targetPos, ent.position())]); // Sort by increasing distance data.sort((a, b) => a[2] - b[2]); @@ -115,10 +114,10 @@ m.EntityCollection.prototype.filterNearest = function(targetPos, n) for (let i = 0; i < n; ++i) ret.set(data[i][0], data[i][1]); - return new m.EntityCollection(this._ai, ret); + return new EntityCollection(this._ai, ret); }; -m.EntityCollection.prototype.filter_raw = function(callback, thisp) +EntityCollection.prototype.filter_raw = function(callback, thisp) { const ret = new Map(); for (const [id, ent] of this._entities) @@ -127,22 +126,22 @@ m.EntityCollection.prototype.filter_raw = function(callback, thisp) if (callback.call(thisp, val, id, this)) ret.set(id, ent); } - return new m.EntityCollection(this._ai, ret); + return new EntityCollection(this._ai, ret); }; -m.EntityCollection.prototype.forEach = function(callback) +EntityCollection.prototype.forEach = function(callback) { for (const ent of this._entities.values()) callback(ent); return this; }; -m.EntityCollection.prototype.hasEntities = function() +EntityCollection.prototype.hasEntities = function() { return this._entities.size !== 0; }; -m.EntityCollection.prototype.move = function(x, z, queued = false, pushFront = false) +EntityCollection.prototype.move = function(x, z, queued = false, pushFront = false) { Engine.PostCommand(PlayerID, { "type": "walk", @@ -155,7 +154,7 @@ m.EntityCollection.prototype.move = function(x, z, queued = false, pushFront = f return this; }; -m.EntityCollection.prototype.moveToRange = function(x, z, min, max, queued = false, pushFront = false) +EntityCollection.prototype.moveToRange = function(x, z, min, max, queued = false, pushFront = false) { Engine.PostCommand(PlayerID, { "type": "walk-to-range", @@ -170,7 +169,7 @@ m.EntityCollection.prototype.moveToRange = function(x, z, min, max, queued = fal return this; }; -m.EntityCollection.prototype.attackMove = function(x, z, targetClasses, allowCapture = true, queued = false, pushFront = false) +EntityCollection.prototype.attackMove = function(x, z, targetClasses, allowCapture = true, queued = false, pushFront = false) { Engine.PostCommand(PlayerID, { "type": "attack-walk", @@ -185,7 +184,7 @@ m.EntityCollection.prototype.attackMove = function(x, z, targetClasses, allowCap return this; }; -m.EntityCollection.prototype.moveIndiv = function(x, z, queued = false, pushFront = false) +EntityCollection.prototype.moveIndiv = function(x, z, queued = false, pushFront = false) { for (const id of this._entities.keys()) Engine.PostCommand(PlayerID, { @@ -199,7 +198,7 @@ m.EntityCollection.prototype.moveIndiv = function(x, z, queued = false, pushFron return this; }; -m.EntityCollection.prototype.garrison = function(target, queued = false, pushFront = false) +EntityCollection.prototype.garrison = function(target, queued = false, pushFront = false) { Engine.PostCommand(PlayerID, { "type": "garrison", @@ -211,7 +210,7 @@ m.EntityCollection.prototype.garrison = function(target, queued = false, pushFro return this; }; -m.EntityCollection.prototype.occupyTurret = function(target, queued = false, pushFront = false) +EntityCollection.prototype.occupyTurret = function(target, queued = false, pushFront = false) { Engine.PostCommand(PlayerID, { "type": "occupy-turret", @@ -223,13 +222,13 @@ m.EntityCollection.prototype.occupyTurret = function(target, queued = false, pus return this; }; -m.EntityCollection.prototype.destroy = function() +EntityCollection.prototype.destroy = function() { Engine.PostCommand(PlayerID, { "type": "delete-entities", "entities": this.toIdArray() }); return this; }; -m.EntityCollection.prototype.attack = function(unitId, queued = false, pushFront = false) +EntityCollection.prototype.attack = function(unitId, queued = false, pushFront = false) { Engine.PostCommand(PlayerID, { "type": "attack", @@ -242,7 +241,7 @@ m.EntityCollection.prototype.attack = function(unitId, queued = false, pushFront }; /** violent, aggressive, defensive, passive, standground */ -m.EntityCollection.prototype.setStance = function(stance) +EntityCollection.prototype.setStance = function(stance) { Engine.PostCommand(PlayerID, { "type": "stance", @@ -253,7 +252,7 @@ m.EntityCollection.prototype.setStance = function(stance) }; /** Returns the average position of all units */ -m.EntityCollection.prototype.getCentrePosition = function() +EntityCollection.prototype.getCentrePosition = function() { const sumPos = [0, 0]; let count = 0; @@ -274,7 +273,7 @@ m.EntityCollection.prototype.getCentrePosition = function() * This might be faster for huge collections, but there's * always a risk that it'll be unprecise. */ -m.EntityCollection.prototype.getApproximatePosition = function(sample) +EntityCollection.prototype.getApproximatePosition = function(sample) { const sumPos = [0, 0]; let i = 0; @@ -292,13 +291,13 @@ m.EntityCollection.prototype.getApproximatePosition = function(sample) return i ? [sumPos[0]/i, sumPos[1]/i] : undefined; }; -m.EntityCollection.prototype.hasEntId = function(id) +EntityCollection.prototype.hasEntId = function(id) { return this._entities.has(id); }; /** Removes an entity from the collection, returns true if the entity was a member, false otherwise */ -m.EntityCollection.prototype.removeEnt = function(ent) +EntityCollection.prototype.removeEnt = function(ent) { if (!this._entities.has(ent.id())) return false; @@ -307,7 +306,7 @@ m.EntityCollection.prototype.removeEnt = function(ent) }; /** Adds an entity to the collection, returns true if the entity was not member, false otherwise */ -m.EntityCollection.prototype.addEnt = function(ent) +EntityCollection.prototype.addEnt = function(ent) { if (this._entities.has(ent.id())) return false; @@ -322,7 +321,7 @@ m.EntityCollection.prototype.addEnt = function(ent) * If an entitycollection is frozen, it will never automatically add a unit. * But can remove one. */ -m.EntityCollection.prototype.updateEnt = function(ent, force) +EntityCollection.prototype.updateEnt = function(ent, force) { let passesFilters = true; for (const filter of this._filters) @@ -338,31 +337,27 @@ m.EntityCollection.prototype.updateEnt = function(ent, force) return this.removeEnt(ent); }; -m.EntityCollection.prototype.registerUpdates = function() +EntityCollection.prototype.registerUpdates = function() { this._ai.registerUpdatingEntityCollection(this); }; -m.EntityCollection.prototype.unregister = function() +EntityCollection.prototype.unregister = function() { this._ai.removeUpdatingEntityCollection(this); }; -m.EntityCollection.prototype.dynamicProperties = function() +EntityCollection.prototype.dynamicProperties = function() { return this.dynamicProp; }; -m.EntityCollection.prototype.setUID = function(id) +EntityCollection.prototype.setUID = function(id) { this._UID = id; }; -m.EntityCollection.prototype.getUID = function() +EntityCollection.prototype.getUID = function() { return this._UID; }; - -return m; - -}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api/filters.js b/binaries/data/mods/public/simulation/ai/common-api/filters.js index 7affafb6a6..615abee977 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/filters.js +++ b/binaries/data/mods/public/simulation/ai/common-api/filters.js @@ -1,118 +1,182 @@ -API3 = function(m) +export function byType(type) { - -m.Filters = { - "byType": type => ({ + return { "func": ent => ent.templateName() == type, "dynamicProperties": [] - }), + }; +} - "byClass": cls => ({ +export function byClass(cls) +{ + return { "func": ent => ent.hasClass(cls), "dynamicProperties": [] - }), + }; +} - "byClasses": clsList => ({ +export function byClasses(clsList) +{ + return { "func": ent => ent.hasClasses(clsList), "dynamicProperties": [] - }), + }; +} - "byMetadata": (player, key, value) => ({ +export function byMetadata(player, key, value) +{ + return { "func": ent => ent.getMetadata(player, key) == value, "dynamicProperties": ['metadata.' + key] - }), + }; +} - "byHasMetadata": (player, key) => ({ +export function byHasMetadata(player, key) +{ + return { "func": ent => ent.getMetadata(player, key) !== undefined, "dynamicProperties": ['metadata.' + key] - }), + }; +} - "and": (filter1, filter2) => ({ +export function and(filter1, filter2) +{ + return { "func": ent => filter1.func(ent) && filter2.func(ent), "dynamicProperties": filter1.dynamicProperties.concat(filter2.dynamicProperties) - }), + }; +} - "or": (filter1, filter2) => ({ +export function or(filter1, filter2) +{ + return { "func": ent => filter1.func(ent) || filter2.func(ent), "dynamicProperties": filter1.dynamicProperties.concat(filter2.dynamicProperties) - }), + }; +} - "not": (filter) => ({ +export function not(filter) +{ + return { "func": ent => !filter.func(ent), "dynamicProperties": filter.dynamicProperties - }), + }; +} - "byOwner": owner => ({ +export function byOwner(owner) +{ + return { "func": ent => ent.owner() == owner, "dynamicProperties": ['owner'] - }), + }; +} - "byNotOwner": owner => ({ +export function byNotOwner(owner) +{ + return { "func": ent => ent.owner() != owner, "dynamicProperties": ['owner'] - }), + }; +} - "byOwners": owners => ({ +export function byOwners(owners) +{ + return { "func": ent => owners.some(owner => owner == ent.owner()), "dynamicProperties": ['owner'] - }), + }; +} - "byCanGarrison": () => ({ +export function byCanGarrison() +{ + return { "func": ent => ent.garrisonMax() > 0, "dynamicProperties": [] - }), + }; +} - "byTrainingQueue": () => ({ +export function byTrainingQueue() +{ + return { "func": ent => ent.trainingQueue(), "dynamicProperties": ['trainingQueue'] - }), + }; +} - "byResearchAvailable": (gameState, civ) => ({ +export function byResearchAvailable(gameState, civ) +{ + return { "func": ent => ent.researchableTechs(gameState, civ) !== undefined, "dynamicProperties": [] - }), + }; +} - "byCanAttackClass": aClass => ({ +export function byCanAttackClass(aClass) +{ + return { "func": ent => ent.canAttackClass(aClass), "dynamicProperties": [] - }), + }; +} - "byCanAttackTarget": target => ({ +export function byCanAttackTarget(target) +{ + return { "func": ent => ent.canAttackTarget(target), "dynamicProperties": [] - }), + }; +} - "isGarrisoned": () => ({ +export function isGarrisoned() +{ + return { "func": ent => ent.position() === undefined, "dynamicProperties": [] - }), + }; +} - "isIdle": () => ({ +export function isIdle() +{ + return { "func": ent => ent.isIdle(), "dynamicProperties": ['idle'] - }), + }; +} - "isFoundation": () => ({ +export function isFoundation() +{ + return { "func": ent => ent.foundationProgress() !== undefined, "dynamicProperties": [] - }), + }; +} - "isBuilt": () => ({ +export function isBuilt() +{ + return { "func": ent => ent.foundationProgress() === undefined, "dynamicProperties": [] - }), + }; +} - "hasDefensiveFire": () => ({ +export function hasDefensiveFire() +{ + return { "func": ent => ent.hasDefensiveFire(), "dynamicProperties": [] - }), + }; +} - "isDropsite": resourceType => ({ +export function isDropsite(resourceType) +{ + return { "func": ent => ent.isResourceDropsite(resourceType), "dynamicProperties": [] - }), + }; +} - "isTreasure": () => ({ +export function isTreasure() +{ + return { "func": ent => { if (!ent.isTreasure()) return false; @@ -124,9 +188,12 @@ m.Filters = { template != "gaia/treasure/shipwreck"; }, "dynamicProperties": [] - }), + }; +} - "byResource": resourceType => ({ +export function byResource(resourceType) +{ + return { "func": ent => { if (!ent.resourceSupplyMax()) return false; @@ -142,23 +209,24 @@ m.Filters = { return resourceType == type.generic; }, "dynamicProperties": [] - }), + }; +} - "isHuntable": () => ({ - // Skip targets that are too hard to hunt and don't go for the fish! TODO: better accessibility checks +export function isHuntable() +{ + // Skip targets that are too hard to hunt and don't go for the fish! TODO: better accessibility checks + return { "func": ent => ent.hasClass("Animal") && ent.resourceSupplyMax() && - ent.isHuntable() && !ent.hasClass("SeaCreature"), + ent.isHuntable() && !ent.hasClass("SeaCreature"), "dynamicProperties": [] - }), + }; +} - "isFishable": () => ({ - // temporarily do not fish moving fish (i.e. whales) +export function isFishable() +{ + // temporarily do not fish moving fish (i.e. whales) + return { "func": ent => !ent.get("UnitMotion") && ent.hasClass("SeaCreature") && ent.resourceSupplyMax(), "dynamicProperties": [] - }) -}; - -return m; - -}(API3); - + }; +} diff --git a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js index 4be37b1a4f..0fb60945ec 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js +++ b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js @@ -1,15 +1,18 @@ -API3 = function(m) -{ +import { Template } from "simulation/ai/common-api/entity.js"; +import * as filters from "simulation/ai/common-api/filters.js"; +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { Technology } from "simulation/ai/common-api/technology.js"; /** * Provides an API for the rest of the AI scripts to query the world state at a * higher level than the raw data. */ -m.GameState = function() { +export function GameState() +{ this.ai = null; // must be updated by the AIs. -}; +} -m.GameState.prototype.init = function(SharedScript, state, player) +GameState.prototype.init = function(SharedScript, state, player) { this.sharedScript = SharedScript; this.EntCollecNames = SharedScript._entityCollectionsName; @@ -63,7 +66,7 @@ m.GameState.prototype.init = function(SharedScript, state, player) })); }; -m.GameState.prototype.update = function(SharedScript) +GameState.prototype.update = function(SharedScript) { this.timeElapsed = SharedScript.timeElapsed; this.playerData = SharedScript.playersData[this.player]; @@ -71,19 +74,19 @@ m.GameState.prototype.update = function(SharedScript) this.ceasefireTimeRemaining = SharedScript.ceasefireTimeRemaining; }; -m.GameState.prototype.updatingCollection = function(id, filter, parentCollection) +GameState.prototype.updatingCollection = function(id, filter, parentCollection) { const gid = "player-" + this.player + "-" + id; // automatically add the player ID return this.updatingGlobalCollection(gid, filter, parentCollection); }; -m.GameState.prototype.destroyCollection = function(id) +GameState.prototype.destroyCollection = function(id) { const gid = "player-" + this.player + "-" + id; // automatically add the player ID this.destroyGlobalCollection(gid); }; -m.GameState.prototype.updatingGlobalCollection = function(gid, filter, parentCollection) +GameState.prototype.updatingGlobalCollection = function(gid, filter, parentCollection) { if (this.EntCollecNames.has(gid)) return this.EntCollecNames.get(gid); @@ -94,7 +97,7 @@ m.GameState.prototype.updatingGlobalCollection = function(gid, filter, parentCol return collection; }; -m.GameState.prototype.destroyGlobalCollection = function(gid) +GameState.prototype.destroyGlobalCollection = function(gid) { if (!this.EntCollecNames.has(gid)) return; @@ -106,51 +109,51 @@ m.GameState.prototype.destroyGlobalCollection = function(gid) /** * Reset the entities collections which depend on diplomacy */ -m.GameState.prototype.resetOnDiplomacyChanged = function() +GameState.prototype.resetOnDiplomacyChanged = function() { for (const name of this.EntCollecNames.keys()) if (name.startsWith("player-" + this.player + "-diplo")) this.destroyGlobalCollection(name); }; -m.GameState.prototype.getTimeElapsed = function() +GameState.prototype.getTimeElapsed = function() { return this.timeElapsed; }; -m.GameState.prototype.getBarterPrices = function() +GameState.prototype.getBarterPrices = function() { return this.playerData.barterPrices; }; -m.GameState.prototype.getVictoryConditions = function() +GameState.prototype.getVictoryConditions = function() { return this.victoryConditions; }; -m.GameState.prototype.getAlliedVictory = function() +GameState.prototype.getAlliedVictory = function() { return this.alliedVictory; }; -m.GameState.prototype.isCeasefireActive = function() +GameState.prototype.isCeasefireActive = function() { return this.ceasefireActive; }; -m.GameState.prototype.getTemplate = function(type) +GameState.prototype.getTemplate = function(type) { if (TechnologyTemplates.Has(type)) - return new m.Technology(type); + return new Technology(type); if (this.templates[type] === undefined) this.sharedScript.GetTemplate(type); - return this.templates[type] ? new m.Template(this.sharedScript, type, this.templates[type]) : null; + return this.templates[type] ? new Template(this.sharedScript, type, this.templates[type]) : null; }; /** Return the template of the structure built from this foundation */ -m.GameState.prototype.getBuiltTemplate = function(foundationName) +GameState.prototype.getBuiltTemplate = function(foundationName) { if (!foundationName.startsWith("foundation|")) { @@ -160,17 +163,17 @@ m.GameState.prototype.getBuiltTemplate = function(foundationName) return this.getTemplate(foundationName.substr(11)); }; -m.GameState.prototype.applyCiv = function(str) +GameState.prototype.applyCiv = function(str) { return str.replace(/\{civ\}/g, this.playerData.civ); }; -m.GameState.prototype.getPlayerCiv = function(player) +GameState.prototype.getPlayerCiv = function(player) { return player !== undefined ? this.sharedScript.playersData[player].civ : this.playerData.civ; }; -m.GameState.prototype.currentPhase = function() +GameState.prototype.currentPhase = function() { for (let i = this.phases.length; i > 0; --i) if (this.isResearched(this.phases[i-1].name)) @@ -178,17 +181,17 @@ m.GameState.prototype.currentPhase = function() return 0; }; -m.GameState.prototype.getNumberOfPhases = function() +GameState.prototype.getNumberOfPhases = function() { return this.phases.length; }; -m.GameState.prototype.getPhaseName = function(i) +GameState.prototype.getPhaseName = function(i) { return this.phases[i-1] ? this.phases[i-1].name : undefined; }; -m.GameState.prototype.getPhaseEntityRequirements = function(i) +GameState.prototype.getPhaseEntityRequirements = function(i) { const entityReqs = []; @@ -207,18 +210,18 @@ m.GameState.prototype.getPhaseEntityRequirements = function(i) return entityReqs; }; -m.GameState.prototype.isResearched = function(template) +GameState.prototype.isResearched = function(template) { return this.playerData.researchedTechs.has(template); }; -m.GameState.prototype.isResearching = function(template) +GameState.prototype.isResearching = function(template) { return this.playerData.researchQueued.has(template); }; /** this is an "in-absolute" check that doesn't check if we have a building to research from. */ -m.GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck) +GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck) { if (this.playerData.disabledTechnologies[techTemplateName]) return false; @@ -251,7 +254,7 @@ m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec * Basically copies TechnologyManager, but compares against * variables only available within the AI */ -m.GameState.prototype.checkTechRequirements = function(reqs) +GameState.prototype.checkTechRequirements = function(reqs) { if (!reqs) return false; @@ -291,42 +294,42 @@ m.GameState.prototype.checkTechRequirements = function(reqs) }); }; -m.GameState.prototype.getPassabilityMap = function() +GameState.prototype.getPassabilityMap = function() { return this.sharedScript.passabilityMap; }; -m.GameState.prototype.getPassabilityClassMask = function(name) +GameState.prototype.getPassabilityClassMask = function(name) { if (!this.sharedScript.passabilityClasses[name]) error("Tried to use invalid passability class name '" + name + "'"); return this.sharedScript.passabilityClasses[name]; }; -m.GameState.prototype.getResources = function() +GameState.prototype.getResources = function() { - return new m.Resources(this.playerData.resourceCounts); + return new ResourcesManager(this.playerData.resourceCounts); }; -m.GameState.prototype.getPopulation = function() +GameState.prototype.getPopulation = function() { return this.playerData.popCount; }; -m.GameState.prototype.getPopulationLimit = function() { +GameState.prototype.getPopulationLimit = function() { return this.playerData.popLimit; }; -m.GameState.prototype.getPopulationMax = function() { +GameState.prototype.getPopulationMax = function() { return this.playerData.popMax; }; -m.GameState.prototype.getPlayerID = function() +GameState.prototype.getPlayerID = function() { return this.player; }; -m.GameState.prototype.hasAllies = function() +GameState.prototype.hasAllies = function() { for (const i in this.playerData.isAlly) if (this.playerData.isAlly[i] && +i !== this.player && @@ -335,7 +338,7 @@ m.GameState.prototype.hasAllies = function() return false; }; -m.GameState.prototype.hasEnemies = function() +GameState.prototype.hasEnemies = function() { for (const i in this.playerData.isEnemy) if (this.playerData.isEnemy[i] && +i !== 0 && @@ -344,7 +347,7 @@ m.GameState.prototype.hasEnemies = function() return false; }; -m.GameState.prototype.hasNeutrals = function() +GameState.prototype.hasNeutrals = function() { for (const i in this.playerData.isNeutral) if (this.playerData.isNeutral[i] && @@ -353,28 +356,28 @@ m.GameState.prototype.hasNeutrals = function() return false; }; -m.GameState.prototype.isPlayerNeutral = function(id) +GameState.prototype.isPlayerNeutral = function(id) { return this.playerData.isNeutral[id]; }; -m.GameState.prototype.isPlayerAlly = function(id) +GameState.prototype.isPlayerAlly = function(id) { return this.playerData.isAlly[id]; }; -m.GameState.prototype.isPlayerMutualAlly = function(id) +GameState.prototype.isPlayerMutualAlly = function(id) { return this.playerData.isMutualAlly[id]; }; -m.GameState.prototype.isPlayerEnemy = function(id) +GameState.prototype.isPlayerEnemy = function(id) { return this.playerData.isEnemy[id]; }; /** Return the number of players currently enemies, not including gaia */ -m.GameState.prototype.getNumPlayerEnemies = function() +GameState.prototype.getNumPlayerEnemies = function() { let num = 0; for (let i = 1; i < this.playerData.isEnemy.length; ++i) @@ -384,7 +387,7 @@ m.GameState.prototype.getNumPlayerEnemies = function() return num; }; -m.GameState.prototype.getEnemies = function() +GameState.prototype.getEnemies = function() { const ret = []; for (const i in this.playerData.isEnemy) @@ -393,7 +396,7 @@ m.GameState.prototype.getEnemies = function() return ret; }; -m.GameState.prototype.getNeutrals = function() +GameState.prototype.getNeutrals = function() { const ret = []; for (const i in this.playerData.isNeutral) @@ -402,7 +405,7 @@ m.GameState.prototype.getNeutrals = function() return ret; }; -m.GameState.prototype.getAllies = function() +GameState.prototype.getAllies = function() { const ret = []; for (const i in this.playerData.isAlly) @@ -411,7 +414,7 @@ m.GameState.prototype.getAllies = function() return ret; }; -m.GameState.prototype.getExclusiveAllies = function() +GameState.prototype.getExclusiveAllies = function() { // Player is not included const ret = []; for (const i in this.playerData.isAlly) @@ -420,7 +423,7 @@ m.GameState.prototype.getExclusiveAllies = function() return ret; }; -m.GameState.prototype.getMutualAllies = function() +GameState.prototype.getMutualAllies = function() { const ret = []; for (const i in this.playerData.isMutualAlly) @@ -430,165 +433,182 @@ m.GameState.prototype.getMutualAllies = function() return ret; }; -m.GameState.prototype.isEntityAlly = function(ent) +GameState.prototype.isEntityAlly = function(ent) { if (!ent) return false; return this.playerData.isAlly[ent.owner()]; }; -m.GameState.prototype.isEntityExclusiveAlly = function(ent) +GameState.prototype.isEntityExclusiveAlly = function(ent) { if (!ent) return false; return this.playerData.isAlly[ent.owner()] && ent.owner() !== this.player; }; -m.GameState.prototype.isEntityEnemy = function(ent) +GameState.prototype.isEntityEnemy = function(ent) { if (!ent) return false; return this.playerData.isEnemy[ent.owner()]; }; -m.GameState.prototype.isEntityOwn = function(ent) +GameState.prototype.isEntityOwn = function(ent) { if (!ent) return false; return ent.owner() === this.player; }; -m.GameState.prototype.getEntityById = function(id) +GameState.prototype.getEntityById = function(id) { return this.entities._entities.get(+id); }; -m.GameState.prototype.getEntities = function(id) +GameState.prototype.getEntities = function(id) { if (id === undefined) return this.entities; - return this.updatingGlobalCollection("player-" + id + "-entities", m.Filters.byOwner(id)); + return this.updatingGlobalCollection("player-" + id + "-entities", filters.byOwner(id)); }; -m.GameState.prototype.getStructures = function() +GameState.prototype.getStructures = function() { - return this.updatingGlobalCollection("structures", m.Filters.byClass("Structure"), this.entities); + return this.updatingGlobalCollection("structures", filters.byClass("Structure"), this.entities); }; -m.GameState.prototype.getOwnEntities = function() +GameState.prototype.getOwnEntities = function() { - return this.updatingGlobalCollection("player-" + this.player + "-entities", m.Filters.byOwner(this.player)); + return this.updatingGlobalCollection("player-" + this.player + "-entities", + filters.byOwner(this.player)); }; -m.GameState.prototype.getOwnStructures = function() +GameState.prototype.getOwnStructures = function() { - return this.updatingGlobalCollection("player-" + this.player + "-structures", m.Filters.byClass("Structure"), this.getOwnEntities()); + return this.updatingGlobalCollection("player-" + this.player + "-structures", + filters.byClass("Structure"), this.getOwnEntities()); }; -m.GameState.prototype.getOwnUnits = function() +GameState.prototype.getOwnUnits = function() { - return this.updatingGlobalCollection("player-" + this.player + "-units", m.Filters.byClass("Unit"), this.getOwnEntities()); + return this.updatingGlobalCollection("player-" + this.player + "-units", filters.byClass("Unit"), + this.getOwnEntities()); }; -m.GameState.prototype.getAllyEntities = function() +GameState.prototype.getAllyEntities = function() { - return this.entities.filter(m.Filters.byOwners(this.getAllies())); + return this.entities.filter(filters.byOwners(this.getAllies())); }; -m.GameState.prototype.getExclusiveAllyEntities = function() +GameState.prototype.getExclusiveAllyEntities = function() { - return this.entities.filter(m.Filters.byOwners(this.getExclusiveAllies())); + return this.entities.filter(filters.byOwners(this.getExclusiveAllies())); }; -m.GameState.prototype.getAllyStructures = function(allyID) +GameState.prototype.getAllyStructures = function(allyID) { if (allyID == undefined) - return this.updatingCollection("diplo-ally-structures", m.Filters.byOwners(this.getAllies()), this.getStructures()); + { + return this.updatingCollection("diplo-ally-structures", filters.byOwners(this.getAllies()), + this.getStructures()); + } - return this.updatingGlobalCollection("player-" + allyID + "-structures", m.Filters.byOwner(allyID), this.getStructures()); + return this.updatingGlobalCollection("player-" + allyID + "-structures", filters.byOwner(allyID), + this.getStructures()); }; -m.GameState.prototype.getNeutralStructures = function() +GameState.prototype.getNeutralStructures = function() { - return this.getStructures().filter(m.Filters.byOwners(this.getNeutrals())); + return this.getStructures().filter(filters.byOwners(this.getNeutrals())); }; -m.GameState.prototype.getEnemyEntities = function() +GameState.prototype.getEnemyEntities = function() { - return this.entities.filter(m.Filters.byOwners(this.getEnemies())); + return this.entities.filter(filters.byOwners(this.getEnemies())); }; -m.GameState.prototype.getEnemyStructures = function(enemyID) +GameState.prototype.getEnemyStructures = function(enemyID) { if (enemyID === undefined) - return this.updatingCollection("diplo-enemy-structures", m.Filters.byOwners(this.getEnemies()), this.getStructures()); + { + return this.updatingCollection("diplo-enemy-structures", filters.byOwners(this.getEnemies()), + this.getStructures()); + } - return this.updatingGlobalCollection("player-" + enemyID + "-structures", m.Filters.byOwner(enemyID), this.getStructures()); + return this.updatingGlobalCollection("player-" + enemyID + "-structures", filters.byOwner(enemyID), + this.getStructures()); }; -m.GameState.prototype.getEnemyUnits = function(enemyID) +GameState.prototype.getEnemyUnits = function(enemyID) { if (enemyID === undefined) - return this.getEnemyEntities().filter(m.Filters.byClass("Unit")); + return this.getEnemyEntities().filter(filters.byClass("Unit")); - return this.updatingGlobalCollection("player-" + enemyID + "-units", m.Filters.byClass("Unit"), this.getEntities(enemyID)); + return this.updatingGlobalCollection("player-" + enemyID + "-units", filters.byClass("Unit"), + this.getEntities(enemyID)); }; /** if maintain is true, this will be stored. Otherwise it's one-shot. */ -m.GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain) +GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain) { if (maintain) - return this.updatingCollection(key + "-" + value, m.Filters.byMetadata(this.player, key, value), this.getOwnEntities()); - return this.getOwnEntities().filter(m.Filters.byMetadata(this.player, key, value)); + { + return this.updatingCollection(key + "-" + value, filters.byMetadata(this.player, key, value), + this.getOwnEntities()); + } + return this.getOwnEntities().filter(filters.byMetadata(this.player, key, value)); }; -m.GameState.prototype.getOwnEntitiesByRole = function(role, maintain) +GameState.prototype.getOwnEntitiesByRole = function(role, maintain) { return this.getOwnEntitiesByMetadata("role", role, maintain); }; -m.GameState.prototype.getOwnEntitiesByType = function(type, maintain) +GameState.prototype.getOwnEntitiesByType = function(type, maintain) { - const filter = m.Filters.byType(type); + const filter = filters.byType(type); if (maintain) return this.updatingCollection("type-" + type, filter, this.getOwnEntities()); return this.getOwnEntities().filter(filter); }; -m.GameState.prototype.getOwnEntitiesByClass = function(cls, maintain) +GameState.prototype.getOwnEntitiesByClass = function(cls, maintain) { - const filter = m.Filters.byClass(cls); + const filter = filters.byClass(cls); if (maintain) return this.updatingCollection("class-" + cls, filter, this.getOwnEntities()); return this.getOwnEntities().filter(filter); }; -m.GameState.prototype.getOwnFoundationsByClass = function(cls, maintain) +GameState.prototype.getOwnFoundationsByClass = function(cls, maintain) { - const filter = m.Filters.byClass(cls); + const filter = filters.byClass(cls); if (maintain) return this.updatingCollection("foundations-class-" + cls, filter, this.getOwnFoundations()); return this.getOwnFoundations().filter(filter); }; -m.GameState.prototype.getOwnTrainingFacilities = function() +GameState.prototype.getOwnTrainingFacilities = function() { - return this.updatingGlobalCollection("player-" + this.player + "-training-facilities", m.Filters.byTrainingQueue(), this.getOwnEntities()); + return this.updatingGlobalCollection("player-" + this.player + "-training-facilities", + filters.byTrainingQueue(), this.getOwnEntities()); }; -m.GameState.prototype.getOwnResearchFacilities = function() +GameState.prototype.getOwnResearchFacilities = function() { - return this.updatingGlobalCollection("player-" + this.player + "-research-facilities", m.Filters.byResearchAvailable(this, this.playerData.civ), this.getOwnEntities()); + return this.updatingGlobalCollection("player-" + this.player + "-research-facilities", + filters.byResearchAvailable(this, this.playerData.civ), this.getOwnEntities()); }; -m.GameState.prototype.countEntitiesByType = function(type, maintain) +GameState.prototype.countEntitiesByType = function(type, maintain) { return this.getOwnEntitiesByType(type, maintain).length; }; -m.GameState.prototype.countEntitiesAndQueuedByType = function(type, maintain) +GameState.prototype.countEntitiesAndQueuedByType = function(type, maintain) { const template = this.getTemplate(type); if (!template) @@ -615,12 +635,15 @@ m.GameState.prototype.countEntitiesAndQueuedByType = function(type, maintain) return count; }; -m.GameState.prototype.countFoundationsByType = function(type, maintain) +GameState.prototype.countFoundationsByType = function(type, maintain) { const foundationType = "foundation|" + type; if (maintain) - return this.updatingCollection("foundation-type-" + type, m.Filters.byType(foundationType), this.getOwnFoundations()).length; + { + return this.updatingCollection("foundation-type-" + type, filters.byType(foundationType), + this.getOwnFoundations()).length; + } let count = 0; this.getOwnStructures().forEach(function(ent) { @@ -630,12 +653,12 @@ m.GameState.prototype.countFoundationsByType = function(type, maintain) return count; }; -m.GameState.prototype.countOwnEntitiesByRole = function(role) +GameState.prototype.countOwnEntitiesByRole = function(role) { return this.getOwnEntitiesByRole(role, "true").length; }; -m.GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role) +GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role) { let count = this.countOwnEntitiesByRole(role); @@ -648,7 +671,7 @@ m.GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role) return count; }; -m.GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) +GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) { // Count entities in building production queues let count = 0; @@ -660,42 +683,47 @@ m.GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) return count; }; -m.GameState.prototype.getOwnFoundations = function() +GameState.prototype.getOwnFoundations = function() { - return this.updatingGlobalCollection("player-" + this.player + "-foundations", m.Filters.isFoundation(), this.getOwnStructures()); + return this.updatingGlobalCollection("player-" + this.player + "-foundations", + filters.isFoundation(), this.getOwnStructures()); }; -m.GameState.prototype.getOwnDropsites = function(resource) +GameState.prototype.getOwnDropsites = function(resource) { if (resource) - return this.updatingCollection("ownDropsite-" + resource, m.Filters.isDropsite(resource), this.getOwnEntities()); - return this.updatingCollection("ownDropsite-all", m.Filters.isDropsite(), this.getOwnEntities()); + { + return this.updatingCollection("ownDropsite-" + resource, filters.isDropsite(resource), + this.getOwnEntities()); + } + return this.updatingCollection("ownDropsite-all", filters.isDropsite(), this.getOwnEntities()); }; -m.GameState.prototype.getAnyDropsites = function(resource) +GameState.prototype.getAnyDropsites = function(resource) { if (resource) - return this.updatingGlobalCollection("anyDropsite-" + resource, m.Filters.isDropsite(resource), this.getEntities()); - return this.updatingGlobalCollection("anyDropsite-all", m.Filters.isDropsite(), this.getEntities()); + return this.updatingGlobalCollection("anyDropsite-" + resource, filters.isDropsite(resource), this.getEntities()); + return this.updatingGlobalCollection("anyDropsite-all", filters.isDropsite(), this.getEntities()); }; -m.GameState.prototype.getResourceSupplies = function(resource) +GameState.prototype.getResourceSupplies = function(resource) { - return this.updatingGlobalCollection("resource-" + resource, m.Filters.byResource(resource), this.getEntities()); + return this.updatingGlobalCollection("resource-" + resource, filters.byResource(resource), + this.getEntities()); }; -m.GameState.prototype.getHuntableSupplies = function() +GameState.prototype.getHuntableSupplies = function() { - return this.updatingGlobalCollection("resource-hunt", m.Filters.isHuntable(), this.getEntities()); + return this.updatingGlobalCollection("resource-hunt", filters.isHuntable(), this.getEntities()); }; -m.GameState.prototype.getFishableSupplies = function() +GameState.prototype.getFishableSupplies = function() { - return this.updatingGlobalCollection("resource-fish", m.Filters.isFishable(), this.getEntities()); + return this.updatingGlobalCollection("resource-fish", filters.isFishable(), this.getEntities()); }; /** This returns only units from buildings. */ -m.GameState.prototype.findTrainableUnits = function(classes, anticlasses) +GameState.prototype.findTrainableUnits = function(classes, anticlasses) { const allTrainable = []; const civ = this.playerData.civ; @@ -737,7 +765,7 @@ m.GameState.prototype.findTrainableUnits = function(classes, anticlasses) * Does not factor cost. * If there are pairs, both techs are returned. */ -m.GameState.prototype.findAvailableTech = function() +GameState.prototype.findAvailableTech = function() { const allResearchable = []; const civ = this.playerData.civ; @@ -776,7 +804,7 @@ m.GameState.prototype.findAvailableTech = function() /** * Return true if we have a building able to train that template */ -m.GameState.prototype.hasTrainer = function(template) +GameState.prototype.hasTrainer = function(template) { const civ = this.playerData.civ; for (const ent of this.getOwnTrainingFacilities().values()) @@ -791,7 +819,7 @@ m.GameState.prototype.hasTrainer = function(template) /** * Find buildings able to train that template. */ -m.GameState.prototype.findTrainers = function(template) +GameState.prototype.findTrainers = function(template) { const civ = this.playerData.civ; return this.getOwnTrainingFacilities().filter(function(ent) { @@ -803,7 +831,7 @@ m.GameState.prototype.findTrainers = function(template) /** * Get any unit that is capable of constructing the given building type. */ -m.GameState.prototype.findBuilder = function(template) +GameState.prototype.findBuilder = function(template) { const civ = this.getPlayerCiv(); for (const ent of this.getOwnUnits().values()) @@ -816,7 +844,7 @@ m.GameState.prototype.findBuilder = function(template) }; /** Return true if one of our buildings is capable of researching the given tech */ -m.GameState.prototype.hasResearchers = function(templateName, noRequirementCheck) +GameState.prototype.hasResearchers = function(templateName, noRequirementCheck) { // let's check we can research the tech. if (!this.canResearch(templateName, noRequirementCheck)) @@ -849,7 +877,7 @@ m.GameState.prototype.hasResearchers = function(templateName, noRequirementCheck }; /** Find buildings that are capable of researching the given tech */ -m.GameState.prototype.findResearchers = function(templateName, noRequirementCheck) +GameState.prototype.findResearchers = function(templateName, noRequirementCheck) { // let's check we can research the tech. if (!this.canResearch(templateName, noRequirementCheck)) @@ -877,29 +905,29 @@ m.GameState.prototype.findResearchers = function(templateName, noRequirementChec }); }; -m.GameState.prototype.getEntityLimits = function() +GameState.prototype.getEntityLimits = function() { return this.playerData.entityLimits; }; -m.GameState.prototype.getEntityMatchCounts = function() +GameState.prototype.getEntityMatchCounts = function() { return this.playerData.matchEntityCounts; }; -m.GameState.prototype.getEntityCounts = function() +GameState.prototype.getEntityCounts = function() { return this.playerData.entityCounts; }; -m.GameState.prototype.isTemplateAvailable = function(templateName) +GameState.prototype.isTemplateAvailable = function(templateName) { if (this.templates[templateName] === undefined) this.sharedScript.GetTemplate(templateName); return this.templates[templateName] && !this.isTemplateDisabled(templateName); }; -m.GameState.prototype.isTemplateDisabled = function(templateName) +GameState.prototype.isTemplateDisabled = function(templateName) { if (!this.playerData.disabledTemplates[templateName]) return false; @@ -907,7 +935,7 @@ m.GameState.prototype.isTemplateDisabled = function(templateName) }; /** Checks whether the maximum number of buildings have been constructed for a certain catergory */ -m.GameState.prototype.isEntityLimitReached = function(category) +GameState.prototype.isEntityLimitReached = function(category) { if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined) @@ -915,7 +943,7 @@ m.GameState.prototype.isEntityLimitReached = function(category) return this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]; }; -m.GameState.prototype.getTraderTemplatesGains = function() +GameState.prototype.getTraderTemplatesGains = function() { const shipMechantTemplateName = this.applyCiv("units/{civ}/ship_merchant"); const supportTraderTemplateName = this.applyCiv("units/{civ}/support_trader"); @@ -929,8 +957,3 @@ m.GameState.prototype.getTraderTemplatesGains = function() ret.navalGainMultiplier = norm * shipMerchantTemplate.gainMultiplier(); return ret; }; - -return m; - -}(API3); - diff --git a/binaries/data/mods/public/simulation/ai/common-api/map-module.js b/binaries/data/mods/public/simulation/ai/common-api/map-module.js index 2ad3c36421..2880f618cb 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/map-module.js +++ b/binaries/data/mods/public/simulation/ai/common-api/map-module.js @@ -1,12 +1,8 @@ -API3 = function(m) -{ - /** * The map module. * Copied with changes from QuantumState's original for qBot, it's a component for storing 8 bit values. */ -// The function needs to be named too because of the copyConstructor functionality -m.Map = function Map(sharedScript, type, originalMap, actualCopy) +export function InfoMap(sharedScript, type, originalMap, actualCopy) { // get the correct dimensions according to the map type const map = type == "territory" || type == "resource" ? sharedScript.territoryMap : sharedScript.passabilityMap; @@ -31,19 +27,19 @@ m.Map = function Map(sharedScript, type, originalMap, actualCopy) this.map = originalMap; else this.map = new Uint8Array(this.length); -}; +} -m.Map.prototype.setMaxVal = function(val) +InfoMap.prototype.setMaxVal = function(val) { this.maxVal = val; }; -m.Map.prototype.gamePosToMapPos = function(p) +InfoMap.prototype.gamePosToMapPos = function(p) { return [Math.floor(p[0]/this.cellSize), Math.floor(p[1]/this.cellSize)]; }; -m.Map.prototype.point = function(p) +InfoMap.prototype.point = function(p) { const q = this.gamePosToMapPos(p); q[0] = q[0] >= this.width ? this.width-1 : q[0] < 0 ? 0 : q[0]; @@ -51,7 +47,7 @@ m.Map.prototype.point = function(p) return this.map[q[0] + this.width * q[1]]; }; -m.Map.prototype.runLoop = function(x0, x1, y0, y1, cx, cy, maxDist2, func) +InfoMap.prototype.runLoop = function(x0, x1, y0, y1, cx, cy, maxDist2, func) { for (let y = y0; y < y1; ++y) { @@ -69,7 +65,7 @@ m.Map.prototype.runLoop = function(x0, x1, y0, y1, cx, cy, maxDist2, func) } }; -m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type = "linear") +InfoMap.prototype.addInfluence = function(cx, cy, maxDist, strength, type = "linear") { strength = strength ? strength : maxDist; @@ -94,7 +90,7 @@ m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type = "linea }; -m.Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type = "constant") +InfoMap.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type = "constant") { strength = strength ? +strength : +maxDist; @@ -119,20 +115,20 @@ m.Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type = " }; /** add to current map by the parameter map pixelwise */ -m.Map.prototype.add = function(map) +InfoMap.prototype.add = function(map) { for (let i = 0; i < this.length; ++i) this.set(i, this.map[i] + map.map[i]); }; /** Set the value taking overflow into account */ -m.Map.prototype.set = function(i, value) +InfoMap.prototype.set = function(i, value) { this.map[i] = value < 0 ? 0 : value > this.maxVal ? this.maxVal : value; }; /** Find the best non-obstructed tile */ -m.Map.prototype.findBestTile = function(radius, obstruction) +InfoMap.prototype.findBestTile = function(radius, obstruction) { let bestIdx; let bestVal = 0; @@ -151,7 +147,7 @@ m.Map.prototype.findBestTile = function(radius, obstruction) }; /** return any non obstructed (small) tile inside the (big) tile i from obstruction map */ -m.Map.prototype.getNonObstructedTile = function(i, radius, obstruction) +InfoMap.prototype.getNonObstructedTile = function(i, radius, obstruction) { const ratio = this.cellSize / obstruction.cellSize; const ix = (i % this.width) * ratio; @@ -178,7 +174,7 @@ m.Map.prototype.getNonObstructedTile = function(i, radius, obstruction) }; /** return true if the area centered on tile kx-ky and with radius is obstructed */ -m.Map.prototype.isObstructedTile = function(kx, ky, radius) +InfoMap.prototype.isObstructedTile = function(kx, ky, radius) { const w = this.width; if (kx < radius || kx >= w - radius || ky < radius || ky >= w - radius || this.map[kx+ky*w] == 0) @@ -210,11 +206,7 @@ m.Map.prototype.isObstructedTile = function(kx, ky, radius) return null; }; -m.Map.prototype.dumpIm = function(name = "default.png", threshold = this.maxVal) +InfoMap.prototype.dumpIm = function(name = "default.png", threshold = this.maxVal) { Engine.DumpImage(name, this.map, this.width, this.height, threshold); }; - -return m; - -}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api/resources.js b/binaries/data/mods/public/simulation/ai/common-api/resources.js index 3d716abb51..c61e8a6d3f 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/resources.js +++ b/binaries/data/mods/public/simulation/ai/common-api/resources.js @@ -1,24 +1,21 @@ Resources = new Resources(); -API3 = function(m) -{ - -m.Resources = function(amounts = {}, population = 0) +export function ResourcesManager(amounts = {}, population = 0) { for (const key of Resources.GetCodes()) this[key] = amounts[key] || 0; this.population = population > 0 ? population : 0; -}; +} -m.Resources.prototype.reset = function() +ResourcesManager.prototype.reset = function() { for (const key of Resources.GetCodes()) this[key] = 0; this.population = 0; }; -m.Resources.prototype.canAfford = function(that) +ResourcesManager.prototype.canAfford = function(that) { for (const key of Resources.GetCodes()) if (this[key] < that[key]) @@ -26,28 +23,28 @@ m.Resources.prototype.canAfford = function(that) return true; }; -m.Resources.prototype.add = function(that) +ResourcesManager.prototype.add = function(that) { for (const key of Resources.GetCodes()) this[key] += that[key]; this.population += that.population; }; -m.Resources.prototype.subtract = function(that) +ResourcesManager.prototype.subtract = function(that) { for (const key of Resources.GetCodes()) this[key] -= that[key]; this.population += that.population; }; -m.Resources.prototype.multiply = function(n) +ResourcesManager.prototype.multiply = function(n) { for (const key of Resources.GetCodes()) this[key] *= n; this.population *= n; }; -m.Resources.prototype.Serialize = function() +ResourcesManager.prototype.Serialize = function() { const amounts = {}; for (const key of Resources.GetCodes()) @@ -55,13 +52,9 @@ m.Resources.prototype.Serialize = function() return { "amounts": amounts, "population": this.population }; }; -m.Resources.prototype.Deserialize = function(data) +ResourcesManager.prototype.Deserialize = function(data) { for (const key in data.amounts) this[key] = data.amounts[key]; this.population = data.population; }; - -return m; - -}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api/shared.js b/binaries/data/mods/public/simulation/ai/common-api/shared.js index 63f0025787..2d0f735ef5 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/shared.js +++ b/binaries/data/mods/public/simulation/ai/common-api/shared.js @@ -1,8 +1,11 @@ -API3 = function(m) -{ +import { Entity } from "simulation/ai/common-api/entity.js"; +import { EntityCollection } from "simulation/ai/common-api/entitycollection.js"; +import { GameState } from "simulation/ai/common-api/gamestate.js"; +import { InfoMap } from "simulation/ai/common-api/map-module.js"; +import { Accessibility, TerrainAnalysis } from "simulation/ai/common-api/terrain-analysis.js"; /** Shared script handling templates and basic terrain analysis */ -m.SharedScript = function(settings) +export function SharedScript(settings) { if (!settings) return; @@ -22,10 +25,10 @@ m.SharedScript = function(settings) this._entityCollectionsName = new Map(); this._entityCollectionsByDynProp = {}; this._entityCollectionsUID = 0; -}; +} /** Return a simple object (using no classes etc) that will be serialized into saved games */ -m.SharedScript.prototype.Serialize = function() +SharedScript.prototype.Serialize = function() { return { "players": this._players, @@ -39,7 +42,7 @@ m.SharedScript.prototype.Serialize = function() * Called after the constructor when loading a saved game, with 'data' being * whatever Serialize() returned */ -m.SharedScript.prototype.Deserialize = function(data) +SharedScript.prototype.Deserialize = function(data) { this._players = data.players; this._templatesModifications = data.templatesModifications; @@ -49,7 +52,7 @@ m.SharedScript.prototype.Deserialize = function(data) this.isDeserialized = true; }; -m.SharedScript.prototype.GetTemplate = function(name) +SharedScript.prototype.GetTemplate = function(name) { if (this._templates[name] === undefined) this._templates[name] = Engine.GetTemplate(name) || null; @@ -62,7 +65,7 @@ m.SharedScript.prototype.GetTemplate = function(name) * We need to know the initial state of the game for this, as we will use it. * This is called right at the end of the map generation. */ -m.SharedScript.prototype.init = function(state, deserialization) +SharedScript.prototype.init = function(state, deserialization) { if (!deserialization) this._entitiesModifications = new Map(); @@ -105,14 +108,14 @@ m.SharedScript.prototype.init = function(state, deserialization) this._entities = new Map(); if (state.entities) for (const id in state.entities) - this._entities.set(+id, new m.Entity(this, state.entities[id])); + this._entities.set(+id, new Entity(this, state.entities[id])); // entity collection updated on create/destroy event. - this.entities = new m.EntityCollection(this, this._entities); + this.entities = new EntityCollection(this, this._entities); // create the terrain analyzer - this.terrainAnalyzer = new m.TerrainAnalysis(); + this.terrainAnalyzer = new TerrainAnalysis(); this.terrainAnalyzer.init(this, state); - this.accessibility = new m.Accessibility(); + this.accessibility = new Accessibility(); this.accessibility.init(state, this.terrainAnalyzer); // Resource types: ignore = not used for resource maps @@ -130,7 +133,7 @@ m.SharedScript.prototype.init = function(state, deserialization) this.gameState = {}; for (const player of this._players) { - this.gameState[player] = new m.GameState(); + this.gameState[player] = new GameState(); this.gameState[player].init(this, state, player); } }; @@ -139,7 +142,7 @@ m.SharedScript.prototype.init = function(state, deserialization) * General update of the shared script, before each AI's update * applies entity deltas, and each gamestate. */ -m.SharedScript.prototype.onUpdate = function(state) +SharedScript.prototype.onUpdate = function(state) { if (this.isDeserialized) { @@ -176,7 +179,7 @@ m.SharedScript.prototype.onUpdate = function(state) Engine.ProfileStop(); }; -m.SharedScript.prototype.ApplyEntitiesDelta = function(state) +SharedScript.prototype.ApplyEntitiesDelta = function(state) { Engine.ProfileStart("Shared ApplyEntitiesDelta"); @@ -190,7 +193,7 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state) if (!state.entities[evt.entity]) continue; // Sometimes there are things like foundations which get destroyed too fast - const entity = new m.Entity(this, state.entities[evt.entity]); + const entity = new Entity(this, state.entities[evt.entity]); this._entities.set(evt.entity, entity); this.entities.addEnt(entity); @@ -285,7 +288,7 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state) Engine.ProfileStop(); }; -m.SharedScript.prototype.ApplyTemplatesDelta = function(state) +SharedScript.prototype.ApplyTemplatesDelta = function(state) { Engine.ProfileStart("Shared ApplyTemplatesDelta"); @@ -307,7 +310,7 @@ m.SharedScript.prototype.ApplyTemplatesDelta = function(state) Engine.ProfileStop(); }; -m.SharedScript.prototype.registerUpdatingEntityCollection = function(entCollection) +SharedScript.prototype.registerUpdatingEntityCollection = function(entCollection) { entCollection.setUID(this._entityCollectionsUID); this._entityCollections.set(this._entityCollectionsUID, entCollection); @@ -320,7 +323,7 @@ m.SharedScript.prototype.registerUpdatingEntityCollection = function(entCollecti this._entityCollectionsUID++; }; -m.SharedScript.prototype.removeUpdatingEntityCollection = function(entCollection) +SharedScript.prototype.removeUpdatingEntityCollection = function(entCollection) { const uid = entCollection.getUID(); @@ -332,7 +335,7 @@ m.SharedScript.prototype.removeUpdatingEntityCollection = function(entCollection this._entityCollectionsByDynProp[prop].delete(uid); }; -m.SharedScript.prototype.updateEntityCollections = function(property, ent) +SharedScript.prototype.updateEntityCollections = function(property, ent) { if (this._entityCollectionsByDynProp[property] === undefined) return; @@ -341,7 +344,7 @@ m.SharedScript.prototype.updateEntityCollections = function(property, ent) entCol.updateEnt(ent); }; -m.SharedScript.prototype.setMetadata = function(player, ent, key, value) +SharedScript.prototype.setMetadata = function(player, ent, key, value) { let metadata = this._entityMetadata[player][ent.id()]; if (!metadata) @@ -355,12 +358,12 @@ m.SharedScript.prototype.setMetadata = function(player, ent, key, value) this.updateEntityCollections('metadata.' + key, ent); }; -m.SharedScript.prototype.getMetadata = function(player, ent, key) +SharedScript.prototype.getMetadata = function(player, ent, key) { return this._entityMetadata[player][ent.id()]?.[key]; }; -m.SharedScript.prototype.deleteMetadata = function(player, ent, key) +SharedScript.prototype.deleteMetadata = function(player, ent, key) { const metadata = this._entityMetadata[player][ent.id()]; @@ -373,7 +376,7 @@ m.SharedScript.prototype.deleteMetadata = function(player, ent, key) return true; }; -m.copyPrototype = function(descendant, parent) +export function copyPrototype(descendant, parent) { const sConstructor = parent.toString(); const aMatch = sConstructor.match(/\s*function (.*)\(/); @@ -383,10 +386,10 @@ m.copyPrototype = function(descendant, parent) for (const p in parent.prototype) descendant.prototype[p] = parent.prototype[p]; -}; +} /** creates a map of resource density */ -m.SharedScript.prototype.createResourceMaps = function() +SharedScript.prototype.createResourceMaps = function() { for (const resource of Resources.GetCodes()) { @@ -395,8 +398,8 @@ m.SharedScript.prototype.createResourceMaps = function() continue; // We're creating them 8-bit. Things could go above 255 if there are really tons of resources // But at that point the precision is not really important anyway. And it saves memory. - this.resourceMaps[resource] = new m.Map(this, "resource"); - this.ccResourceMaps[resource] = new m.Map(this, "resource"); + this.resourceMaps[resource] = new InfoMap(this, "resource"); + this.ccResourceMaps[resource] = new InfoMap(this, "resource"); } for (const ent of this._entities.values()) this.addEntityToResourceMap(ent); @@ -405,7 +408,7 @@ m.SharedScript.prototype.createResourceMaps = function() /** * @param {Object} events - The events from a turn. */ -m.SharedScript.prototype.updateResourceMaps = function(events) +SharedScript.prototype.updateResourceMaps = function(events) { for (const e of events.Destroy) if (e.entityObj) @@ -419,7 +422,7 @@ m.SharedScript.prototype.updateResourceMaps = function(events) /** * @param {entity} entity - The entity to add to the resource map. */ -m.SharedScript.prototype.addEntityToResourceMap = function(entity) +SharedScript.prototype.addEntityToResourceMap = function(entity) { this.changeEntityInResourceMapHelper(entity, 1); }; @@ -427,7 +430,7 @@ m.SharedScript.prototype.addEntityToResourceMap = function(entity) /** * @param {entity} entity - The entity to remove from the resource map. */ -m.SharedScript.prototype.removeEntityFromResourceMap = function(entity) +SharedScript.prototype.removeEntityFromResourceMap = function(entity) { this.changeEntityInResourceMapHelper(entity, -1); }; @@ -435,7 +438,7 @@ m.SharedScript.prototype.removeEntityFromResourceMap = function(entity) /** * @param {entity} ent - The entity to add to the resource map. */ -m.SharedScript.prototype.changeEntityInResourceMapHelper = function(ent, multiplication = 1) +SharedScript.prototype.changeEntityInResourceMapHelper = function(ent, multiplication = 1) { if (!ent) return; @@ -454,8 +457,3 @@ m.SharedScript.prototype.changeEntityInResourceMapHelper = function(ent, multipl this.resourceMaps[resource].addInfluence(x, y, this.influenceRadius[grp] / cellSize, strength / 2); this.ccResourceMaps[resource].addInfluence(x, y, this.ccInfluenceRadius[grp] / cellSize, strength, "constant"); }; - -return m; - -}(API3); - diff --git a/binaries/data/mods/public/simulation/ai/common-api/technology.js b/binaries/data/mods/public/simulation/ai/common-api/technology.js index 5a5539feba..b6b74f7a54 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/technology.js +++ b/binaries/data/mods/public/simulation/ai/common-api/technology.js @@ -1,10 +1,7 @@ LoadModificationTemplates(); -API3 = function(m) -{ - /** Wrapper around a technology template */ -m.Technology = function(templateName) +export function Technology(templateName) { this._templateName = templateName; const template = TechnologyTemplates.Get(templateName); @@ -20,10 +17,10 @@ m.Technology = function(templateName) // check if it only defines a pair: this._definesPair = template.top !== undefined; this._template = template; -}; +} /** returns generic, or specific if civ provided. */ -m.Technology.prototype.name = function(civ) +Technology.prototype.name = function(civ) { if (civ === undefined) return this._template.genericName; @@ -33,38 +30,38 @@ m.Technology.prototype.name = function(civ) return this._template.specificName[civ]; }; -m.Technology.prototype.pairDef = function() +Technology.prototype.pairDef = function() { return this._definesPair; }; /** in case this defines a pair only, returns the two paired technologies. */ -m.Technology.prototype.getPairedTechs = function() +Technology.prototype.getPairedTechs = function() { if (!this._definesPair) return undefined; return [ - new m.Technology(this._template.top), - new m.Technology(this._template.bottom) + new Technology(this._template.top), + new Technology(this._template.bottom) ]; }; -m.Technology.prototype.pair = function() +Technology.prototype.pair = function() { if (!this._isPair) return undefined; return this._template.pair; }; -m.Technology.prototype.pairedWith = function() +Technology.prototype.pairedWith = function() { if (!this._isPair) return undefined; return this._pairedWith; }; -m.Technology.prototype.cost = function(researcher) +Technology.prototype.cost = function(researcher) { if (!this._template.cost) return undefined; @@ -78,7 +75,7 @@ m.Technology.prototype.cost = function(researcher) return cost; }; -m.Technology.prototype.costSum = function(researcher) +Technology.prototype.costSum = function(researcher) { const cost = this.cost(researcher); if (!cost) @@ -89,49 +86,45 @@ m.Technology.prototype.costSum = function(researcher) return ret; }; -m.Technology.prototype.researchTime = function() +Technology.prototype.researchTime = function() { return this._template.researchTime || 0; }; -m.Technology.prototype.requirements = function(civ) +Technology.prototype.requirements = function(civ) { return DeriveTechnologyRequirements(this._template, civ); }; -m.Technology.prototype.autoResearch = function() +Technology.prototype.autoResearch = function() { if (!this._template.autoResearch) return undefined; return this._template.autoResearch; }; -m.Technology.prototype.supersedes = function() +Technology.prototype.supersedes = function() { if (!this._template.supersedes) return undefined; return this._template.supersedes; }; -m.Technology.prototype.modifications = function() +Technology.prototype.modifications = function() { if (!this._template.modifications) return undefined; return this._template.modifications; }; -m.Technology.prototype.affects = function() +Technology.prototype.affects = function() { if (!this._template.affects) return undefined; return this._template.affects; }; -m.Technology.prototype.isAffected = function(classes) +Technology.prototype.isAffected = function(classes) { return this._template.affects && this._template.affects.some(affect => MatchesClassList(classes, affect)); }; - -return m; - -}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js b/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js index 82d156ced8..98a815fb7f 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js +++ b/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js @@ -1,5 +1,5 @@ -API3 = function(m) -{ +import { InfoMap } from "simulation/ai/common-api/map-module.js"; +import { copyPrototype } from "simulation/ai/common-api/shared.js"; /** * TerrainAnalysis, inheriting from the Map Component. @@ -11,27 +11,27 @@ API3 = function(m) * This is intended for use with 8 bit maps for reduced memory usage. * Upgraded from QuantumState's original TerrainAnalysis for qBot. */ -m.TerrainAnalysis = function() +export function TerrainAnalysis() { -}; +} -m.copyPrototype(m.TerrainAnalysis, m.Map); +copyPrototype(TerrainAnalysis, InfoMap); -m.TerrainAnalysis.prototype.IMPASSABLE = 0; +TerrainAnalysis.prototype.IMPASSABLE = 0; /** * non-passable by land units */ -m.TerrainAnalysis.prototype.DEEP_WATER = 200; +TerrainAnalysis.prototype.DEEP_WATER = 200; /** * passable by land units and water units */ -m.TerrainAnalysis.prototype.SHALLOW_WATER = 201; +TerrainAnalysis.prototype.SHALLOW_WATER = 201; /** * passable by land units */ -m.TerrainAnalysis.prototype.LAND = 255; +TerrainAnalysis.prototype.LAND = 255; -m.TerrainAnalysis.prototype.init = function(sharedScript, rawState) +TerrainAnalysis.prototype.init = function(sharedScript, rawState) { const passabilityMap = rawState.passabilityMap; this.width = passabilityMap.width; @@ -54,7 +54,7 @@ m.TerrainAnalysis.prototype.init = function(sharedScript, rawState) obstructionTiles[i] = this.SHALLOW_WATER; } - this.Map(rawState, "passability", obstructionTiles); + this.InfoMap(rawState, "passability", obstructionTiles); }; /** @@ -65,15 +65,15 @@ m.TerrainAnalysis.prototype.init = function(sharedScript, rawState) * for optimizations it's called after the TerrainAnalyser has finished initializing his map * so this can use the land regions already. */ -m.Accessibility = function() +export function Accessibility() { -}; +} -m.copyPrototype(m.Accessibility, m.TerrainAnalysis); +copyPrototype(Accessibility, TerrainAnalysis); -m.Accessibility.prototype.init = function(rawState, terrainAnalyser) +Accessibility.prototype.init = function(rawState, terrainAnalyser) { - this.Map(rawState, "passability", terrainAnalyser.map); + this.InfoMap(rawState, "passability", terrainAnalyser.map); this.landPassMap = new Uint16Array(terrainAnalyser.length); this.navalPassMap = new Uint16Array(terrainAnalyser.length); @@ -146,7 +146,7 @@ m.Accessibility.prototype.init = function(rawState, terrainAnalyser) // Engine.DumpImage("NavalPassMap.png", this.navalPassMap, this.width, this.height, 255); }; -m.Accessibility.prototype.getAccessValue = function(position, onWater) +Accessibility.prototype.getAccessValue = function(position, onWater) { const gamePos = this.gamePosToMapPos(position); if (onWater) @@ -170,7 +170,7 @@ m.Accessibility.prototype.getAccessValue = function(position, onWater) return ret; }; -m.Accessibility.prototype.getTrajectTo = function(start, end) +Accessibility.prototype.getTrajectTo = function(start, end) { const pstart = this.gamePosToMapPos(start); const istart = pstart[0] + pstart[1]*this.width; @@ -198,7 +198,7 @@ m.Accessibility.prototype.getTrajectTo = function(start, end) * this can tell you what sea zone you need to have a dock on, for example. * assumes a land unit unless start point is over deep water. */ -m.Accessibility.prototype.getTrajectToIndex = function(istart, iend) +Accessibility.prototype.getTrajectToIndex = function(istart, iend) { if (istart === iend) return [istart]; @@ -227,7 +227,7 @@ m.Accessibility.prototype.getTrajectToIndex = function(istart, iend) return undefined; }; -m.Accessibility.prototype.getRegionSize = function(position, onWater) +Accessibility.prototype.getRegionSize = function(position, onWater) { const pos = this.gamePosToMapPos(position); const index = pos[0] + pos[1]*this.width; @@ -237,7 +237,7 @@ m.Accessibility.prototype.getRegionSize = function(position, onWater) return this.regionSize[ID]; }; -m.Accessibility.prototype.getRegionSizei = function(index, onWater) +Accessibility.prototype.getRegionSizei = function(index, onWater) { if (this.regionSize[this.landPassMap[index]] === undefined && (!onWater || this.regionSize[this.navalPassMap[index]] === undefined)) return 0; @@ -247,7 +247,7 @@ m.Accessibility.prototype.getRegionSizei = function(index, onWater) }; /** Implementation of a fast flood fill. Reasonably good performances for JS. */ -m.Accessibility.prototype.floodFill = function(startIndex, value, onWater) +Accessibility.prototype.floodFill = function(startIndex, value, onWater) { if (value > this.maxRegions) { @@ -383,7 +383,3 @@ m.Accessibility.prototype.floodFill = function(startIndex, value, onWater) } return true; }; - -return m; - -}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api/utils.js b/binaries/data/mods/public/simulation/ai/common-api/utils.js index dcc42f6450..83b72b7eb3 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/utils.js +++ b/binaries/data/mods/public/simulation/ai/common-api/utils.js @@ -1,31 +1,28 @@ -API3 = function(m) -{ - -m.warn = function(output) +export function warn(output) { if (typeof output === "string") warn("PlayerID " + PlayerID + " | " + output); else warn("PlayerID " + PlayerID + " | " + uneval(output)); -}; +} /** * Useful for simulating consecutive AI matches. */ -m.exit = function() +export function exit() { Engine.Exit(); -}; +} -m.VectorDistance = function(a, b) +export function VectorDistance(a, b) { return Math.euclidDistance2D(a[0], a[1], b[0], b[1]); -}; +} -m.SquareVectorDistance = function(a, b) +export function SquareVectorDistance(a, b) { return Math.euclidDistance2DSquared(a[0], a[1], b[0], b[1]); -}; +} /** Utility functions for conversions of maps of different sizes */ @@ -33,7 +30,7 @@ m.SquareVectorDistance = function(a, b) * Returns the index of map2 with max content from indices contained inside the cell i of map1 * map1.cellSize must be a multiple of map2.cellSize */ -m.getMaxMapIndex = function(i, map1, map2) +function getMaxMapIndex(i, map1, map2) { const ratio = map1.cellSize / map2.cellSize; const ix = (i % map1.width) * ratio; @@ -44,13 +41,13 @@ m.getMaxMapIndex = function(i, map1, map2) if (!index || map2.map[ix+kx+(iy+ky)*map2.width] > map2.map[index]) index = ix+kx+(iy+ky)*map2.width; return index; -}; +} /** * Returns the list of indices of map2 contained inside the cell i of map1 * map1.cellSize must be a multiple of map2.cellSize */ -m.getMapIndices = function(i, map1, map2) +export function getMapIndices(i, map1, map2) { const ratio = map1.cellSize / map2.cellSize; // TODO check that this is integer >= 1 ? const ix = (i % map1.width) * ratio; @@ -60,13 +57,13 @@ m.getMapIndices = function(i, map1, map2) for (let ky = 0; ky < ratio; ++ky) ret.push(ix+kx+(iy+ky)*map2.width); return ret; -}; +} /** * Returns the list of points of map2 contained inside the cell i of map1 * map1.cellSize must be a multiple of map2.cellSize */ -m.getMapPoints = function(i, map1, map2) +function getMapPoints(i, map1, map2) { const ratio = map1.cellSize / map2.cellSize; // TODO check that this is integer >= 1 ? const ix = (i % map1.width) * ratio; @@ -76,8 +73,4 @@ m.getMapPoints = function(i, map1, map2) for (let ky = 0; ky < ratio; ++ky) ret.push([ix+kx, iy+ky]); return ret; -}; - -return m; - -}(API3); +} diff --git a/binaries/data/mods/public/simulation/ai/petra/_petrabot.js b/binaries/data/mods/public/simulation/ai/petra/_petrabot.js index 905f291058..f4f167f91b 100644 --- a/binaries/data/mods/public/simulation/ai/petra/_petrabot.js +++ b/binaries/data/mods/public/simulation/ai/petra/_petrabot.js @@ -1,13 +1,13 @@ +import { BaseAI } from "simulation/ai/common-api/baseAI.js"; +import { Entity } from "simulation/ai/common-api/entity.js"; import { Config } from "simulation/ai/petra/config.js"; import { Headquather } from "simulation/ai/petra/headquarters.js"; import { Queue } from "simulation/ai/petra/queue.js"; import { QueueManager } from "simulation/ai/petra/queueManager.js"; -Engine.IncludeModule("common-api"); - export function PetraBot(settings) { - API3.BaseAI.call(this, settings); + BaseAI.call(this, settings); this.playedTurn = 0; this.elapsedTime = 0; @@ -24,7 +24,7 @@ export function PetraBot(settings) this.savedEvents = {}; } -PetraBot.prototype = Object.create(API3.BaseAI.prototype); +PetraBot.prototype = Object.create(BaseAI.prototype); PetraBot.prototype.CustomInit = function(gameState) { @@ -46,7 +46,7 @@ PetraBot.prototype.CustomInit = function(gameState) for (const keyevt in evt) { evtmod[keyevt] = evt[keyevt]; - evtmod.entityObj = new API3.Entity(gameState.sharedScript, evt.entityObj); + evtmod.entityObj = new Entity(gameState.sharedScript, evt.entityObj); this.savedEvents[key][i] = evtmod; } } diff --git a/binaries/data/mods/public/simulation/ai/petra/attackManager.js b/binaries/data/mods/public/simulation/ai/petra/attackManager.js index 3fb0fbb992..7b5061a0a6 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { warn as aiWarn, SquareVectorDistance, VectorDistance } from "simulation/ai/common-api/utils.js"; import { AttackPlan } from "simulation/ai/petra/attackPlan.js"; import * as chat from "simulation/ai/petra/chatHelper.js"; import { Config, DIFFICULTY_VERY_EASY } from "simulation/ai/petra/config.js"; @@ -35,7 +37,7 @@ export function AttackManager(config) /** More initialisation for stuff that needs the gameState */ AttackManager.prototype.init = function(gameState) { - this.outOfPlan = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", -1)); + this.outOfPlan = gameState.getOwnUnits().filter(filters.byMetadata(PlayerID, "plan", -1)); this.outOfPlan.registerUpdates(); }; @@ -164,7 +166,8 @@ AttackManager.prototype.assignBombers = function(gameState) } } - const bombers = gameState.updatingCollection("bombers", API3.Filters.byClasses(["BoltShooter", "StoneThrower"]), gameState.getOwnUnits()); + const bombers = gameState.updatingCollection("bombers", + filters.byClasses(["BoltShooter", "StoneThrower"]), gameState.getOwnUnits()); for (const ent of bombers.values()) { if (!ent.position() || !ent.isIdle() || !ent.attackRange("Ranged")) @@ -206,7 +209,7 @@ AttackManager.prototype.assignBombers = function(gameState) !gameState.isPlayerEnemy(gameState.ai.HQ.territoryMap.getOwner(structPos))) continue; } - const dist = API3.VectorDistance(entPos, structPos); + const dist = VectorDistance(entPos, structPos); if (dist > range) { const safety = struct.footprintRadius() + 30; @@ -254,15 +257,25 @@ AttackManager.prototype.update = function(gameState, queues, events) if (this.Config.debug > 2 && gameState.ai.elapsedTime > this.debugTime + 60) { this.debugTime = gameState.ai.elapsedTime; - API3.warn(" upcoming attacks ================="); + aiWarn(" upcoming attacks ================="); for (const attackType in this.upcomingAttacks) + { for (const attack of this.upcomingAttacks[attackType]) - API3.warn(" plan " + attack.name + " type " + attackType + " state " + attack.state + " units " + attack.unitCollection.length); - API3.warn(" started attacks =================="); + { + aiWarn(" plan " + attack.name + " type " + attackType + " state " + attack.state + + " units " + attack.unitCollection.length); + } + } + aiWarn(" started attacks =================="); for (const attackType in this.startedAttacks) + { for (const attack of this.startedAttacks[attackType]) - API3.warn(" plan " + attack.name + " type " + attackType + " state " + attack.state + " units " + attack.unitCollection.length); - API3.warn(" =================================="); + { + aiWarn(" plan " + attack.name + " type " + attackType + " state " + attack.state + + " units " + attack.unitCollection.length); + } + } + aiWarn(" =================================="); } this.checkEvents(gameState, events); @@ -280,7 +293,10 @@ AttackManager.prototype.update = function(gameState, queues, events) attack.checkEvents(gameState, events); if (attack.isStarted()) - API3.warn("Petra problem in attackManager: attack in preparation has already started ???"); + { + aiWarn("Petra problem in attackManager: attack in preparation has already " + + "started ???"); + } const updateStep = attack.updatePreparation(gameState); // now we're gonna check if the preparation time is over @@ -293,7 +309,10 @@ AttackManager.prototype.update = function(gameState, queues, events) else if (updateStep === AttackPlan.PREPARATION_FAILED) { if (this.Config.debug > 1) - API3.warn("Attack Manager: " + attack.getType() + " plan " + attack.getName() + " aborted."); + { + aiWarn("Attack Manager: " + attack.getType() + " plan " + attack.getName() + + " aborted."); + } attack.Abort(gameState); this.upcomingAttacks[attackType].splice(i--, 1); } @@ -302,7 +321,10 @@ AttackManager.prototype.update = function(gameState, queues, events) if (attack.StartAttack(gameState)) { if (this.Config.debug > 1) - API3.warn("Attack Manager: Starting " + attack.getType() + " plan " + attack.getName()); + { + aiWarn("Attack Manager: Starting " + attack.getType() + " plan " + + attack.getName()); + } if (this.Config.chat) chat.launchAttack(gameState, attack.targetPlayer, attack.getType()); this.startedAttacks[attackType].push(attack); @@ -327,7 +349,10 @@ AttackManager.prototype.update = function(gameState, queues, events) if (!remaining) { if (this.Config.debug > 1) - API3.warn("Military Manager: " + attack.getType() + " plan " + attack.getName() + " is finished with remaining " + remaining); + { + aiWarn("Military Manager: " + attack.getType() + " plan " + + attack.getName() + " is finished with remaining " + remaining); + } attack.Abort(gameState); this.startedAttacks[attackType].splice(i--, 1); } @@ -336,7 +361,7 @@ AttackManager.prototype.update = function(gameState, queues, events) // creating plans after updating because an aborted plan might be reused in that case. - const barracksNb = gameState.getOwnEntitiesByClass("Barracks", true).filter(API3.Filters.isBuilt()).length; + const barracksNb = gameState.getOwnEntitiesByClass("Barracks", true).filter(filters.isBuilt()).length; if (this.rushNumber < this.maxRushes && barracksNb >= 1) { if (unexecutedAttacks[AttackPlan.TYPE_RUSH] === 0) @@ -348,7 +373,10 @@ AttackManager.prototype.update = function(gameState, queues, events) if (!attackPlan.failed) { if (this.Config.debug > 1) - API3.warn("Military Manager: Rushing plan " + this.totalNumber + " with maxRushes " + this.maxRushes); + { + aiWarn("Military Manager: Rushing plan " + this.totalNumber + + " with maxRushes " + this.maxRushes); + } this.totalNumber++; attackPlan.init(gameState); this.upcomingAttacks[AttackPlan.TYPE_RUSH].push(attackPlan); @@ -377,7 +405,10 @@ AttackManager.prototype.update = function(gameState, queues, events) else { if (this.Config.debug > 1) - API3.warn("Military Manager: Creating the plan " + type + " " + this.totalNumber); + { + aiWarn("Military Manager: Creating the plan " + type + " " + + this.totalNumber); + } this.totalNumber++; attackPlan.init(gameState); this.upcomingAttacks[type].push(attackPlan); @@ -521,7 +552,7 @@ AttackManager.prototype.getEnemyPlayer = function(gameState, attack) let distmin; let ccmin; - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); + const ccEnts = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")); for (const ourcc of ccEnts.values()) { if (ourcc.owner() != PlayerID) @@ -536,7 +567,7 @@ AttackManager.prototype.getEnemyPlayer = function(gameState, attack) continue; if (access !== getLandAccess(gameState, enemycc)) continue; - const dist = API3.SquareVectorDistance(ourPos, enemycc.position()); + const dist = SquareVectorDistance(ourPos, enemycc.position()); if (distmin && dist > distmin) continue; ccmin = enemycc; @@ -590,7 +621,7 @@ AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack) let enemyPlayer; let enemyWonder; let moreAdvanced; - for (const wonder of gameState.getEnemyStructures().filter(API3.Filters.byClass("Wonder")).values()) + for (const wonder of gameState.getEnemyStructures().filter(filters.byClass("Wonder")).values()) { if (wonder.owner() == 0) continue; @@ -620,7 +651,7 @@ AttackManager.prototype.getWonderEnemyPlayer = function(gameState, attack) AttackManager.prototype.getRelicEnemyPlayer = function(gameState, attack) { let enemyPlayer; - const allRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")); + const allRelics = gameState.updatingGlobalCollection("allRelics", filters.byClass("Relic")); let maxRelicsOwned = 0; for (let i = 0; i < gameState.sharedScript.playersData.length; ++i) { @@ -672,7 +703,7 @@ AttackManager.prototype.raidTargetEntity = function(gameState, ent) if (attackPlan.failed) return null; if (this.Config.debug > 1) - API3.warn("Military Manager: Raiding plan " + this.totalNumber); + aiWarn("Military Manager: Raiding plan " + this.totalNumber); this.raidNumber++; this.totalNumber++; attackPlan.init(gameState); @@ -691,7 +722,7 @@ AttackManager.prototype.numAttackingUnitsAround = function(pos, dist) { if (!attack.position) // this attack may be inside a transport continue; - if (API3.SquareVectorDistance(pos, attack.position) < dist*dist) + if (SquareVectorDistance(pos, attack.position) < dist*dist) num += attack.unitCollection.length; } return num; @@ -709,7 +740,7 @@ AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data return false; if (!data.range && !data.armyID) { - API3.warn(" attackManager.switchDefenseToAttack inconsistent data " + uneval(data)); + aiWarn(" attackManager.switchDefenseToAttack inconsistent data " + uneval(data)); return false; } const attackData = data.uniqueTarget ? { "uniqueTargetId": target.id() } : undefined; @@ -728,7 +759,7 @@ AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data if (data.range) { army.recalculatePosition(gameState); - if (API3.SquareVectorDistance(pos, army.foePosition) > data.range * data.range) + if (SquareVectorDistance(pos, army.foePosition) > data.range * data.range) continue; } else if (army.ID != +data.armyID) diff --git a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js index 68f1fd22b8..5ec9b75ea5 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -1,3 +1,6 @@ +import { EntityCollection } from "simulation/ai/common-api/entitycollection.js"; +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, VectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { Config, DIFFICULTY_EASY, DIFFICULTY_MEDIUM } from "simulation/ai/petra/config.js"; import { allowCapture, dumpEntity, getHolder, getLandAccess, isSiegeUnit, returnResources } from "simulation/ai/petra/entityExtend.js"; @@ -92,7 +95,7 @@ export function AttackPlan(gameState, config, uniqueID, type = AttackPlan.TYPE_D { if (this.target) { - API3.warn("Petra: " + this.type + " " + this.name + " has an inaccessible target " + + aiWarn("Petra: " + this.type + " " + this.name + " has an inaccessible target " + this.target.templateName() + " indices " + rallyAccess + " " + access); this.failed = true; return false; @@ -259,7 +262,8 @@ AttackPlan.prototype.init = function(gameState) this.queueChamp = gameState.ai.queues["plan_" + this.name +"_champ"]; this.queueSiege = gameState.ai.queues["plan_" + this.name +"_siege"]; - this.unitCollection = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", this.name)); + this.unitCollection = gameState.getOwnUnits().filter( + filters.byMetadata(PlayerID, "plan", this.name)); this.unitCollection.registerUpdates(); this.unit = {}; @@ -269,7 +273,7 @@ AttackPlan.prototype.init = function(gameState) for (const cat in this.unitStat) { const Unit = this.unitStat[cat]; - this.unit[cat] = this.unitCollection.filter(API3.Filters.byClasses(Unit.classes)); + this.unit[cat] = this.unitCollection.filter(filters.byClasses(Unit.classes)); this.unit[cat].registerUpdates(); if (this.canBuildUnits) this.buildOrders.push([0, Unit.classes, this.unit[cat], Unit, cat]); @@ -380,7 +384,7 @@ AttackPlan.prototype.addBuildOrder = function(gameState, name, unitStats, resetQ // no minsize as we don't want the plan to fail at the last minute though. this.unitStat[name] = unitStats; const Unit = this.unitStat[name]; - this.unit[name] = this.unitCollection.filter(API3.Filters.byClasses(Unit.classes)); + this.unit[name] = this.unitCollection.filter(filters.byClasses(Unit.classes)); this.unit[name].registerUpdates(); this.buildOrders.push([0, Unit.classes, this.unit[name], Unit, name]); if (resetQueue) @@ -510,11 +514,12 @@ AttackPlan.prototype.updatePreparation = function(gameState) if (this.Config.debug > 1) { const am = gameState.ai.HQ.attackManager; - API3.warn(" attacks upcoming: raid " + am.upcomingAttacks[AttackPlan.TYPE_RAID].length + + aiWarn(" attacks upcoming: raid " + + am.upcomingAttacks[AttackPlan.TYPE_RAID].length + " rush " + am.upcomingAttacks[AttackPlan.TYPE_RUSH].length + " attack " + am.upcomingAttacks[AttackPlan.TYPE_DEFAULT].length + " huge " + am.upcomingAttacks[AttackPlan.TYPE_HUGE_ATTACK].length); - API3.warn(" attacks started: raid " + am.startedAttacks[AttackPlan.TYPE_RAID].length + + aiWarn(" attacks started: raid " + am.startedAttacks[AttackPlan.TYPE_RAID].length + " rush " + am.startedAttacks[AttackPlan.TYPE_RUSH].length + " attack " + am.startedAttacks[AttackPlan.TYPE_DEFAULT].length + " huge " + am.startedAttacks[AttackPlan.TYPE_HUGE_ATTACK].length); @@ -635,8 +640,8 @@ AttackPlan.prototype.trainMoreUnits = function(gameState) if (this.Config.debug > 1 && gameState.ai.playedTurn%50 === 0) { - API3.warn("===================================="); - API3.warn("======== build order for plan " + this.name); + aiWarn("===================================="); + aiWarn("======== build order for plan " + this.name); for (const order of this.buildOrders) { const specialData = "Plan_"+this.name+"_"+order[4]; @@ -644,10 +649,10 @@ AttackPlan.prototype.trainMoreUnits = function(gameState) const queue1 = this.queue.countQueuedUnitsWithMetadata("special", specialData); const queue2 = this.queueChamp.countQueuedUnitsWithMetadata("special", specialData); const queue3 = this.queueSiege.countQueuedUnitsWithMetadata("special", specialData); - API3.warn(" >>> " + order[4] + " done " + order[2].length + " training " + inTraining + + aiWarn(" >>> " + order[4] + " done " + order[2].length + " training " + inTraining + " queue " + queue1 + " champ " + queue2 + " siege " + queue3 + " >> need " + order[3].targetSize); } - API3.warn("===================================="); + aiWarn("===================================="); } const firstOrder = this.buildOrders[0]; @@ -670,14 +675,14 @@ AttackPlan.prototype.trainMoreUnits = function(gameState) if (template === undefined) { if (this.Config.debug > 1) - API3.warn("attack no template found " + firstOrder[1]); + aiWarn("attack no template found " + firstOrder[1]); delete this.unitStat[firstOrder[4]]; // deleting the associated unitstat. this.buildOrders.splice(0, 1); } else { if (this.Config.debug > 2) - API3.warn("attack template " + template + " added for plan " + this.name); + aiWarn("attack template " + template + " added for plan " + this.name); const max = firstOrder[3].batchSize; const specialData = "Plan_" + this.name + "_" + firstOrder[4]; const data = { "plan": this.name, "special": specialData, "base": 0 }; @@ -687,8 +692,11 @@ AttackPlan.prototype.trainMoreUnits = function(gameState) if (trainingPlan.template) queue.addPlan(trainingPlan); else if (this.Config.debug > 1) - API3.warn("training plan canceled because no template for " + template + " build1 " + uneval(firstOrder[1]) + - " build3 " + uneval(firstOrder[3].interests)); + { + aiWarn("training plan canceled because no template for " + template + + " build1 " + uneval(firstOrder[1]) + + " build3 " + uneval(firstOrder[3].interests)); + } } } } @@ -770,7 +778,7 @@ AttackPlan.prototype.assignUnits = function(gameState) numbase[baseID] = numbase[baseID] ? ++numbase[baseID] : 1; else { - API3.warn("Petra problem ent without base "); + aiWarn("Petra problem ent without base "); dumpEntity(ent); continue; } @@ -852,7 +860,7 @@ AttackPlan.prototype.chooseTarget = function(gameState) const anchor = base.anchor; if (!anchor || !anchor.position()) continue; - let dist = API3.SquareVectorDistance(anchor.position(), this.targetPos); + let dist = SquareVectorDistance(anchor.position(), this.targetPos); if (base.accessIndex == targetIndex) { if (dist >= distminSame) @@ -887,7 +895,7 @@ AttackPlan.prototype.chooseTarget = function(gameState) } else { - API3.warn("Petra: " + this.type + " " + this.name + " has an inaccessible target" + + aiWarn("Petra: " + this.type + " " + this.name + " has an inaccessible target" + " with indices " + rallyIndex + " " + targetIndex + " from " + this.target.templateName()); return false; } @@ -911,7 +919,7 @@ AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand) let targets; if (this.uniqueTargetId) { - targets = new API3.EntityCollection(gameState.sharedScript); + targets = new EntityCollection(gameState.sharedScript); const ent = gameState.getEntityById(this.uniqueTargetId); if (ent) targets.addEnt(ent); @@ -943,7 +951,7 @@ AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand) // Do not bother with some pointless targets if (!this.isValidTarget(ent)) continue; - let dist = API3.SquareVectorDistance(ent.position(), position); + let dist = SquareVectorDistance(ent.position(), position); // In normal attacks, disfavor fields if (this.type !== AttackPlan.TYPE_RUSH && this.type !== AttackPlan.TYPE_RAID && ent.hasClass("Field")) dist += 100000; @@ -975,34 +983,51 @@ AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand) */ AttackPlan.prototype.defaultTargetFinder = function(gameState, playerEnemy) { - let targets = new API3.EntityCollection(gameState.sharedScript); + let targets = new EntityCollection(gameState.sharedScript); if (gameState.getVictoryConditions().has("wonder")) - for (const ent of gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("Wonder")).values()) + { + for (const ent of + gameState.getEnemyStructures(playerEnemy).filter(filters.byClass("Wonder")).values()) + { targets.addEnt(ent); + } + } if (gameState.getVictoryConditions().has("regicide")) - for (const ent of gameState.getEnemyUnits(playerEnemy).filter(API3.Filters.byClass("Hero")).values()) + { + for (const ent of gameState.getEnemyUnits(playerEnemy).filter(filters.byClass("Hero")) + .values()) + { targets.addEnt(ent); + } + } if (gameState.getVictoryConditions().has("capture_the_relic")) - for (const ent of gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")).filter(relic => relic.owner() == playerEnemy).values()) + { + for (const ent of gameState.updatingGlobalCollection("allRelics", + filters.byClass("Relic")).filter(relic => relic.owner() == playerEnemy).values()) + { targets.addEnt(ent); + } + } targets = targets.filter(this.isValidTarget, this); if (targets.hasEntities()) return targets; const validTargets = gameState.getEnemyStructures(playerEnemy).filter(this.isValidTarget, this); - targets = validTargets.filter(API3.Filters.byClass("CivCentre")); + targets = validTargets.filter(filters.byClass("CivCentre")); if (!targets.hasEntities()) - targets = validTargets.filter(API3.Filters.byClass("ConquestCritical")); + targets = validTargets.filter(filters.byClass("ConquestCritical")); // If there's nothing, attack anything else that's less critical if (!targets.hasEntities()) - targets = validTargets.filter(API3.Filters.byClass("Town")); + targets = validTargets.filter(filters.byClass("Town")); if (!targets.hasEntities()) - targets = validTargets.filter(API3.Filters.byClass("Village")); + targets = validTargets.filter(filters.byClass("Village")); // No buildings, attack anything conquest critical, units included. // TODO Should add naval attacks against the last remaining ships. if (!targets.hasEntities()) - targets = gameState.getEntities(playerEnemy).filter(API3.Filters.byClass("ConquestCritical")). - filter(API3.Filters.not(API3.Filters.byClass("Ship"))); + { + targets = gameState.getEntities(playerEnemy).filter(filters.byClass("ConquestCritical")) + .filter(filters.not(filters.byClass("Ship"))); + } return targets; }; @@ -1018,7 +1043,7 @@ AttackPlan.prototype.isValidTarget = function(ent) /** Rush target finder aims at isolated non-defended buildings */ AttackPlan.prototype.rushTargetFinder = function(gameState, playerEnemy) { - let targets = new API3.EntityCollection(gameState.sharedScript); + let targets = new EntityCollection(gameState.sharedScript); let buildings; if (playerEnemy !== undefined) buildings = gameState.getEnemyStructures(playerEnemy).toEntityArray(); @@ -1047,7 +1072,7 @@ AttackPlan.prototype.rushTargetFinder = function(gameState, playerEnemy) { if (!defense.hasDefensiveFire()) continue; - const dist = API3.SquareVectorDistance(pos, defense.position()); + const dist = SquareVectorDistance(pos, defense.position()); if (dist < 6400) // TODO check on defense range rather than this fixed 80*80 { defended = true; @@ -1056,7 +1081,7 @@ AttackPlan.prototype.rushTargetFinder = function(gameState, playerEnemy) } if (defended) continue; - const dist = API3.SquareVectorDistance(pos, this.position); + const dist = SquareVectorDistance(pos, this.position); if (dist > minDist) continue; minDist = dist; @@ -1074,7 +1099,7 @@ AttackPlan.prototype.rushTargetFinder = function(gameState, playerEnemy) /** Raid target finder aims at destructing foundations from which our defenseManager has attacked the builders */ AttackPlan.prototype.raidTargetFinder = function(gameState) { - const targets = new API3.EntityCollection(gameState.sharedScript); + const targets = new EntityCollection(gameState.sharedScript); for (const targetId of gameState.ai.HQ.defenseManager.targetList) { const target = gameState.getEntityById(targetId); @@ -1103,7 +1128,7 @@ AttackPlan.prototype.checkTargetObstruction = function(gameState, target, positi return undefined; const pathPos = [path[0].x, path[0].y]; - const dist = API3.VectorDistance(pathPos, targetPos); + const dist = VectorDistance(pathPos, targetPos); const radius = target.obstructionRadius().max; for (const struct of gameState.getEnemyStructures().values()) { @@ -1251,7 +1276,7 @@ AttackPlan.prototype.setRallyPoint = function(gameState) AttackPlan.prototype.StartAttack = function(gameState) { if (this.Config.debug > 1) - API3.warn("start attack " + this.name + " with type " + this.type); + aiWarn("start attack " + this.name + " with type " + this.type); // if our target was destroyed during preparation, choose a new one if ((this.targetPlayer === undefined || !this.target || !gameState.getEntityById(this.target.id())) && @@ -1369,7 +1394,8 @@ AttackPlan.prototype.update = function(gameState, events) } if (isSiegeUnit(ourUnit)) { // if our siege units are attacked, we'll send some units to deal with enemies. - const collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5); + const collec = this.unitCollection.filter(filters.not(filters.byClass("Siege"))) + .filterNearest(ourUnit.position(), 5); for (const ent of collec.values()) { if (isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out @@ -1400,7 +1426,8 @@ AttackPlan.prototype.update = function(gameState, events) } else if (isSiegeUnit(attacker)) { // if our unit is attacked by a siege unit, we'll send some melee units to help it. - const collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5); + const collec = this.unitCollection.filter(filters.byClass("Melee")) + .filterNearest(ourUnit.position(), 5); for (const ent of collec.values()) { const shouldCapture = allowCapture(gameState, ent, attacker); @@ -1603,7 +1630,7 @@ AttackPlan.prototype.update = function(gameState, events) const mStruct = enemyStructures.filter(enemy => { if (!enemy.position() || !ent.canAttackTarget(enemy, allowCapture(gameState, ent, enemy))) return false; - if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range) + if (SquareVectorDistance(enemy.position(), ent.position()) > range) return false; if (enemy.foundationProgress() == 0) return false; @@ -1659,14 +1686,17 @@ AttackPlan.prototype.update = function(gameState, events) return false; if (nearby && enemy.hasClass("FemaleCitizen") && enemy.unitAIState().split(".")[1] == "FLEEING") return false; - const dist = API3.SquareVectorDistance(enemy.position(), ent.position()); + const dist = SquareVectorDistance(enemy.position(), ent.position()); if (dist > range) return false; if (getLandAccess(gameState, enemy) != entAccess) return false; // if already too much units targeting this enemy, let's continue towards our main target - if (veto[enemy.id()] && API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500) + if (veto[enemy.id()] && + SquareVectorDistance(this.targetPos, ent.position()) > 2500) + { return false; + } enemy.setMetadata(PlayerID, "distance", Math.sqrt(dist)); return true; }, this).toEntityArray(); @@ -1699,7 +1729,7 @@ AttackPlan.prototype.update = function(gameState, events) // cannot attack. See similar behaviour at #5741. else if (this.isBlocked && ent.canAttackTarget(this.target, false)) ent.attack(this.target.id(), false); - else if (API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500) + else if (SquareVectorDistance(this.targetPos, ent.position()) > 2500) { let targetClasses = targetClassesUnit; if (maybeUpdate && ent.unitAIState() === "INDIVIDUAL.COMBAT.APPROACHING") // we may be blocked by walls, attack everything @@ -1720,7 +1750,7 @@ AttackPlan.prototype.update = function(gameState, events) return false; if (!enemy.position() || !ent.canAttackTarget(enemy, allowCapture(gameState, ent, enemy))) return false; - if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range) + if (SquareVectorDistance(enemy.position(), ent.position()) > range) return false; if (getLandAccess(gameState, enemy) != entAccess) return false; @@ -1762,7 +1792,7 @@ AttackPlan.prototype.update = function(gameState, events) const target = gameState.getEntityById(unit.unitAIOrderData()[0].target); if (!target) return; - const dist = API3.SquareVectorDistance(unit.position(), ent.position()); + const dist = SquareVectorDistance(unit.position(), ent.position()); if (dist > distmin) return; distmin = dist; @@ -1863,14 +1893,15 @@ AttackPlan.prototype.UpdateWalking = function(gameState, events) } // 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) + if (SquareVectorDistance(this.position, this.position5TurnsAgo) < 10 && + this.path.length > 0 && gameState.ai.playedTurn % 5 === 0) { // check for stuck siege units let farthest = 0; let farthestEnt; - for (const ent of this.unitCollection.filter(API3.Filters.byClass("Siege")).values()) + for (const ent of this.unitCollection.filter(filters.byClass("Siege")).values()) { - const dist = API3.SquareVectorDistance(ent.position(), this.position); + const dist = SquareVectorDistance(ent.position(), this.position); if (dist < farthest) continue; farthest = dist; @@ -1882,27 +1913,35 @@ AttackPlan.prototype.UpdateWalking = function(gameState, events) if (gameState.ai.playedTurn % 5 === 0) this.position5TurnsAgo = this.position; - if (this.lastPosition && API3.SquareVectorDistance(this.position, this.lastPosition) < 16 && this.path.length > 0) + if (this.lastPosition && SquareVectorDistance(this.position, this.lastPosition) < 16 && + this.path.length > 0) { if (!this.path[0][0] || !this.path[0][1]) - API3.warn("Start: Problem with path " + uneval(this.path)); + aiWarn("Start: Problem with path " + uneval(this.path)); // We're stuck, presumably. Check if there are no walls just close to us. - for (const ent of gameState.getEnemyStructures().filter(API3.Filters.byClass(["Palisade", "Wall"])).values()) + for (const ent of gameState.getEnemyStructures().filter(filters.byClass(["Palisade", "Wall"])) + .values()) { - if (API3.SquareVectorDistance(this.position, ent.position()) > 800) + if (SquareVectorDistance(this.position, ent.position()) > 800) continue; const enemyClass = ent.hasClass("Wall") ? "Wall" : "Palisade"; // there are walls, so check if we can attack - if (this.unitCollection.filter(API3.Filters.byCanAttackClass(enemyClass)).hasEntities()) + if (this.unitCollection.filter(filters.byCanAttackClass(enemyClass)).hasEntities()) { if (this.Config.debug > 1) - API3.warn("Attack Plan " + this.type + " " + this.name + " has met walls and is not happy."); + { + aiWarn("Attack Plan " + this.type + " " + this.name + + " has met walls and is not happy."); + } this.state = AttackPlan.STATE_ARRIVED; return true; } // abort plan if (this.Config.debug > 1) - API3.warn("Attack Plan " + this.type + " " + this.name + " has met walls and gives up."); + { + aiWarn("Attack Plan " + this.type + " " + this.name + + " has met walls and gives up."); + } return false; } @@ -1911,14 +1950,14 @@ AttackPlan.prototype.UpdateWalking = function(gameState, events) } // check if our units are close enough from the next waypoint. - if (API3.SquareVectorDistance(this.position, this.targetPos) < 10000) + if (SquareVectorDistance(this.position, this.targetPos) < 10000) { if (this.Config.debug > 1) - API3.warn("Attack Plan " + this.type + " " + this.name + " has arrived to destination."); + aiWarn("Attack Plan " + this.type + " " + this.name + " has arrived to destination."); this.state = AttackPlan.STATE_ARRIVED; return true; } - else if (this.path.length && API3.SquareVectorDistance(this.position, this.path[0]) < 1600) + else if (this.path.length && SquareVectorDistance(this.position, this.path[0]) < 1600) { this.path.shift(); if (this.path.length) @@ -1926,7 +1965,10 @@ AttackPlan.prototype.UpdateWalking = function(gameState, events) else { if (this.Config.debug > 1) - API3.warn("Attack Plan " + this.type + " " + this.name + " has arrived to destination."); + { + aiWarn("Attack Plan " + this.type + " " + this.name + + " has arrived to destination."); + } this.state = AttackPlan.STATE_ARRIVED; return true; } @@ -1969,7 +2011,10 @@ AttackPlan.prototype.UpdateTarget = function(gameState) if (!this.target || !gameState.getEntityById(this.target.id())) { if (this.Config.debug > 1) - API3.warn("Seems like our target for plan " + this.name + " has been destroyed or captured. Switching."); + { + aiWarn("Seems like our target for plan " + this.name + + " has been destroyed or captured. Switching."); + } const accessIndex = this.getAttackAccess(gameState); this.target = this.getNearestTarget(gameState, this.position, accessIndex); if (!this.target) @@ -2010,12 +2055,15 @@ AttackPlan.prototype.UpdateTarget = function(gameState) if (!this.target) { if (this.Config.debug > 1) - API3.warn("No new target found. Remaining units " + this.unitCollection.length); + { + aiWarn("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"); + aiWarn("We will help one of our other attacks"); } this.targetPos = this.target.position(); } @@ -2037,7 +2085,7 @@ AttackPlan.prototype.Abort = function(gameState) if (this.rallyPoint && gameState.ai.accessibility.getAccessValue(this.rallyPoint) == access) { rallyPoint = this.rallyPoint; - dist = API3.SquareVectorDistance(this.position, rallyPoint); + dist = SquareVectorDistance(this.position, rallyPoint); } // Then check if we have a nearer base (in case this attack has captured one) for (const base of gameState.ai.HQ.baseManagers()) @@ -2046,7 +2094,7 @@ AttackPlan.prototype.Abort = function(gameState) continue; if (getLandAccess(gameState, base.anchor) != access) continue; - const newdist = API3.SquareVectorDistance(this.position, base.anchor.position()); + const newdist = SquareVectorDistance(this.position, base.anchor.position()); if (newdist > dist) continue; dist = newdist; @@ -2178,13 +2226,14 @@ AttackPlan.prototype.getAttackAccess = function(gameState) AttackPlan.prototype.debugAttack = function() { - API3.warn("---------- attack " + this.name); + aiWarn("---------- attack " + this.name); for (const unitCat in this.unitStat) { const Unit = this.unitStat[unitCat]; - API3.warn(unitCat + " num=" + this.unit[unitCat].length + " min=" + Unit.minSize + " need=" + Unit.targetSize); + aiWarn(unitCat + " num=" + this.unit[unitCat].length + " min=" + Unit.minSize + " need=" + + Unit.targetSize); } - API3.warn("------------------------------"); + aiWarn("------------------------------"); }; AttackPlan.prototype.Serialize = function() diff --git a/binaries/data/mods/public/simulation/ai/petra/baseManager.js b/binaries/data/mods/public/simulation/ai/petra/baseManager.js index 9d09e83816..b1a9447eb9 100644 --- a/binaries/data/mods/public/simulation/ai/petra/baseManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/baseManager.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { Config, DIFFICULTY_EASY, DIFFICULTY_MEDIUM } from "simulation/ai/petra/config.js"; import { getBestBase, getBuiltEntity, getLandAccess, isFastMoving, isNotWorthBuilding } from "simulation/ai/petra/entityExtend.js"; @@ -68,10 +70,10 @@ BaseManager.prototype.init = function(gameState, state) this.neededDefenders = 0; this.workerObject = new Worker(this); // entitycollections - this.units = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID)); - this.workers = this.units.filter(API3.Filters.byMetadata(PlayerID, "role", Worker.ROLE_WORKER)); - this.buildings = gameState.getOwnStructures().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID)); - this.mobileDropsites = this.units.filter(API3.Filters.isDropsite()); + this.units = gameState.getOwnUnits().filter(filters.byMetadata(PlayerID, "base", this.ID)); + this.workers = this.units.filter(filters.byMetadata(PlayerID, "role", Worker.ROLE_WORKER)); + this.buildings = gameState.getOwnStructures().filter(filters.byMetadata(PlayerID, "base", this.ID)); + this.mobileDropsites = this.units.filter(filters.isDropsite()); this.units.registerUpdates(); this.workers.registerUpdates(); @@ -114,7 +116,10 @@ BaseManager.prototype.assignEntity = function(gameState, ent) BaseManager.prototype.setAnchor = function(gameState, anchorEntity) { if (!anchorEntity.hasClass("CivCentre")) - API3.warn("Error: Petra base " + this.ID + " has been assigned " + ent.templateName() + " as anchor."); + { + aiWarn("Error: Petra base " + this.ID + " has been assigned " + ent.templateName() + + " as anchor."); + } else { this.anchor = anchorEntity; @@ -143,13 +148,18 @@ BaseManager.prototype.setAnchorlessEntity = function(gameState, ent) if (!this.buildings.hasEntities()) { if (!getBuiltEntity(gameState, ent).resourceDropsiteTypes()) - API3.warn("Error: Petra base " + this.ID + " has been assigned " + ent.templateName() + " as origin."); + { + aiWarn("Error: Petra base " + this.ID + " has been assigned " + ent.templateName() + + " as origin."); + } this.accessIndex = getLandAccess(gameState, ent); } else if (this.accessIndex !== getLandAccess(gameState, ent)) - API3.warn(" Error: Petra base " + this.ID + " with access " + this.accessIndex + + { + aiWarn(" Error: Petra base " + this.ID + " with access " + this.accessIndex + " has been assigned " + ent.templateName() + " with access" + getLandAccess(gameState, ent)); + } ent.setMetadata(PlayerID, "base", this.ID); this.buildings.updateEnt(ent); @@ -199,7 +209,7 @@ BaseManager.prototype.assignResourceToDropsite = function(gameState, dropsite) if (getLandAccess(gameState, supply) != accessIndex) return; - const dist = API3.SquareVectorDistance(supply.position(), dropsitePos); + const dist = SquareVectorDistance(supply.position(), dropsitePos); if (dist < maxDistResourceSquare) { if (dist < maxDistResourceSquare/16) // distmax/4 @@ -291,7 +301,8 @@ BaseManager.prototype.findBestDropsiteAndLocation = function(gameState, resource "quality": 0, "pos": [0, 0] }; - for (const templateName of gameState.ai.HQ.buildManager.findStructuresByFilter(gameState, API3.Filters.isDropsite(resource))) + for (const templateName of gameState.ai.HQ.buildManager.findStructuresByFilter(gameState, + filters.isDropsite(resource))) { const dp = this.findBestDropsiteLocation(gameState, resource, templateName); if (dp.quality < bestResult.quality) @@ -326,7 +337,7 @@ BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource, t const obstructions = createObstructionMap(gameState, this.accessIndex, template); - const dpEnts = gameState.getOwnStructures().filter(API3.Filters.isDropsite(resource)).toEntityArray(); + const dpEnts = gameState.getOwnStructures().filter(filters.isDropsite(resource)).toEntityArray(); // Foundations don't have the dropsite properties yet, so treat them separately. for (const foundation of gameState.getOwnFoundations().toEntityArray()) @@ -366,7 +377,7 @@ BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource, t const dpPos = dp.position(); if (!dpPos) continue; - const dist = API3.SquareVectorDistance(dpPos, pos); + const dist = SquareVectorDistance(dpPos, pos); if (dist < 3600) { total = 0; @@ -423,7 +434,8 @@ BaseManager.prototype.checkResourceLevels = function(gameState, queues) if (gameState.ai.HQ.canBuild(gameState, "structures/{civ}/field")) // let's see if we need to add new farms. { const count = this.getResourceLevel(gameState, type, prox); // animals are not accounted - const numFarms = gameState.getOwnStructures().filter(API3.Filters.byClass("Field")).length; // including foundations + const numFarms = gameState.getOwnStructures().filter(filters.byClass("Field")) + .length; // including foundations const numQueue = queues.field.countQueuedUnits(); // TODO if not yet farms, add a check on time used/lost and build farmstead if needed @@ -438,7 +450,8 @@ BaseManager.prototype.checkResourceLevels = function(gameState, queues) } else if (!gameState.ai.HQ.maxFields || numFarms + numQueue < gameState.ai.HQ.maxFields) { - const numFound = gameState.getOwnFoundations().filter(API3.Filters.byClass("Field")).length; + const numFound = gameState.getOwnFoundations().filter(filters.byClass("Field")) + .length; let goal = this.Config.Economy.provisionFields; if (gameState.ai.HQ.saveResources || gameState.ai.HQ.saveSpace || count > 300 || numFarms > 5) goal = Math.max(goal-1, 1); @@ -473,7 +486,7 @@ BaseManager.prototype.checkResourceLevels = function(gameState, queues) } // Non food stuff if (!gameState.sharedScript.resourceMaps[type] || queues.dropsites.hasQueuedUnits() || - gameState.getOwnFoundations().filter(API3.Filters.byClass("Storehouse")).hasEntities()) + gameState.getOwnFoundations().filter(filters.byClass("Storehouse")).hasEntities()) { this.gatherers[type].nextCheck = gameState.ai.playedTurn; this.gatherers[type].used = 0; @@ -502,7 +515,9 @@ BaseManager.prototype.checkResourceLevels = function(gameState, queues) queues.dropsites.addPlan(new ConstructionPlan(gameState, newDP.templateName, { "base": this.ID, "type": type }, newDP.pos)); } - else if (!gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre")).hasEntities() && !queues.civilCentre.hasQueuedUnits()) + else if (!gameState.getOwnFoundations().filter(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. @@ -567,7 +582,7 @@ BaseManager.prototype.addGatherRates = function(gameState, currentRates) BaseManager.prototype.assignRolelessUnits = function(gameState, roleless) { if (!roleless) - roleless = this.units.filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "role"))).values(); + roleless = this.units.filter(filters.not(filters.byHasMetadata(PlayerID, "role"))).values(); for (const ent of roleless) { @@ -644,9 +659,9 @@ BaseManager.prototype.switchGatherer = function(gameState, from, to, number) let num = number; let only; const gatherers = this.gatherersByType(gameState, from); - if (from == "food" && gatherers.filter(API3.Filters.byClass("CitizenSoldier")).hasEntities()) + if (from == "food" && gatherers.filter(filters.byClass("CitizenSoldier")).hasEntities()) only = "CitizenSoldier"; - else if (to == "food" && gatherers.filter(API3.Filters.byClass("FemaleCitizen")).hasEntities()) + else if (to == "food" && gatherers.filter(filters.byClass("FemaleCitizen")).hasEntities()) only = "FemaleCitizen"; for (const ent of gatherers.values()) @@ -670,7 +685,7 @@ BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorkers) // Search for idle workers, and tell them to gather resources based on demand if (!idleWorkers) { - const filter = API3.Filters.byMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE); + const filter = filters.byMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE); idleWorkers = gameState.updatingCollection("idle-workers-base-" + this.ID, filter, this.workers).values(); } @@ -720,13 +735,14 @@ BaseManager.prototype.reassignIdleWorkers = function(gameState, idleWorkers) BaseManager.prototype.workersBySubrole = function(gameState, subrole) { - return gameState.updatingCollection("subrole-" + subrole +"-base-" + this.ID, API3.Filters.byMetadata(PlayerID, "subrole", subrole), this.workers); + return gameState.updatingCollection("subrole-" + subrole +"-base-" + this.ID, + filters.byMetadata(PlayerID, "subrole", subrole), this.workers); }; BaseManager.prototype.gatherersByType = function(gameState, type) { return gameState.updatingCollection("workers-gathering-" + type +"-base-" + - this.ID, API3.Filters.byMetadata(PlayerID, "gather-type", type), + this.ID, filters.byMetadata(PlayerID, "gather-type", type), this.workersBySubrole(gameState, Worker.SUBROLE_GATHERER)); }; @@ -779,7 +795,8 @@ BaseManager.prototype.pickBuilders = function(gameState, workers, number) */ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { - let foundations = this.buildings.filter(API3.Filters.and(API3.Filters.isFoundation(), API3.Filters.not(API3.Filters.byClass("Field")))); + let foundations = this.buildings.filter(filters.and(filters.isFoundation(), + filters.not(filters.byClass("Field")))); const damagedBuildings = this.buildings.filter(ent => ent.foundationProgress() === undefined && ent.needsRepair()); @@ -789,12 +806,13 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) const workers = this.workers.filter(ent => ent.isBuilder()); const builderWorkers = this.workersBySubrole(gameState, Worker.SUBROLE_BUILDER); - const idleBuilderWorkers = builderWorkers.filter(API3.Filters.isIdle()); + const idleBuilderWorkers = builderWorkers.filter(filters.isIdle()); // if we're constructing and we have the foundations to our base anchor, only try building that. - if (this.constructing && foundations.filter(API3.Filters.byMetadata(PlayerID, "baseAnchor", true)).hasEntities()) + if (this.constructing && foundations.filter(filters.byMetadata(PlayerID, "baseAnchor", true)) + .hasEntities()) { - foundations = foundations.filter(API3.Filters.byMetadata(PlayerID, "baseAnchor", true)); + foundations = foundations.filter(filters.byMetadata(PlayerID, "baseAnchor", true)); const tID = foundations.toEntityArray()[0].id(); workers.forEach(ent => { const target = ent.getMetadata(PlayerID, "target-foundation"); @@ -887,8 +905,10 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) if (ent.getMetadata(PlayerID, "target-foundation") !== undefined) return; if (assigned >= targetNB || !ent.position() || - API3.SquareVectorDistance(ent.position(), target.position()) > 40000) + SquareVectorDistance(ent.position(), target.position()) > 40000) + { return; + } ++assigned; ++builderTot; ent.setMetadata(PlayerID, "target-foundation", target.id()); @@ -908,13 +928,13 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) }).toEntityArray(); const time = target.buildTime(); nonBuilderWorkers.sort((workerA, workerB) => { - let coeffA = API3.SquareVectorDistance(target.position(), workerA.position()); + let coeffA = SquareVectorDistance(target.position(), workerA.position()); // elephant moves slowly, so when far away they are only useful if build time is long if (workerA.hasClass("Elephant")) coeffA *= 0.5 * (1 + Math.sqrt(coeffA)/5/time); else if (workerA.getMetadata(PlayerID, "gather-type") == "food") coeffA *= 3; - let coeffB = API3.SquareVectorDistance(target.position(), workerB.position()); + let coeffB = SquareVectorDistance(target.position(), workerB.position()); if (workerB.hasClass("Elephant")) coeffB *= 0.5 * (1 + Math.sqrt(coeffB)/5/time); else if (workerB.getMetadata(PlayerID, "gather-type") == "food") @@ -975,7 +995,7 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) if (ent.getMetadata(PlayerID, "target-foundation") !== undefined) return; if (assigned >= targetNB || !ent.position() || - API3.SquareVectorDistance(ent.position(), target.position()) > 40000) + SquareVectorDistance(ent.position(), target.position()) > 40000) return; ++assigned; ++builderTot; @@ -1028,7 +1048,10 @@ BaseManager.prototype.update = function(gameState, queues, events) if (!bestBase) { if (ent.hasClass("Dock")) - API3.warn("Petra: dock in 'noBase' baseManager. It may be useful to do an anchorless base for " + ent.templateName()); + { + aiWarn("Petra: dock in 'noBase' baseManager. It may be useful to " + + "do an anchorless base for " + ent.templateName()); + } continue; } if (ent.resourceDropsiteTypes()) @@ -1111,12 +1134,13 @@ BaseManager.prototype.update = function(gameState, queues, events) if (owner != 0 && !gameState.isPlayerAlly(owner)) { // we're in enemy territory. If we're too close from the enemy, destroy us. - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); + const ccEnts = gameState.updatingGlobalCollection("allCCs", + filters.byClass("CivCentre")); for (const cc of ccEnts.values()) { if (cc.owner() != owner) continue; - if (API3.SquareVectorDistance(cc.position(), this.anchor.position()) > 8000) + if (SquareVectorDistance(cc.position(), this.anchor.position()) > 8000) continue; this.anchor.destroy(); this.basesManager.resetBaseCache(); diff --git a/binaries/data/mods/public/simulation/ai/petra/basesManager.js b/binaries/data/mods/public/simulation/ai/petra/basesManager.js index 9eaf97a3dd..5757cdc815 100644 --- a/binaries/data/mods/public/simulation/ai/petra/basesManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/basesManager.js @@ -1,3 +1,7 @@ +import { EntityCollection } from "simulation/ai/common-api/entitycollection.js"; +import * as filters from "simulation/ai/common-api/filters.js"; +import { InfoMap } from "simulation/ai/common-api/map-module.js"; +import { SquareVectorDistance, warn as aiWarn, getMapIndices } from "simulation/ai/common-api/utils.js"; import { BaseManager } from "simulation/ai/petra/baseManager.js"; import { getBestBase, getLandAccess } from "simulation/ai/petra/entityExtend.js"; import { Worker } from "simulation/ai/petra/worker.js"; @@ -26,13 +30,13 @@ export function BasesManager(Config) BasesManager.prototype.init = function(gameState) { // Initialize base map. Each pixel is a base ID, or 0 if not or not accessible. - this.basesMap = new API3.Map(gameState.sharedScript, "territory"); + this.basesMap = new InfoMap(gameState.sharedScript, "territory"); this.noBase = new BaseManager(gameState, this); this.noBase.init(gameState, BaseManager.STATE_WITH_ANCHOR); this.noBase.accessIndex = 0; - for (const cc of gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).values()) + for (const cc of gameState.getOwnStructures().filter(filters.byClass("CivCentre")).values()) if (cc.foundationProgress() === undefined) this.createBase(gameState, cc, BaseManager.STATE_WITH_ANCHOR); else @@ -45,7 +49,7 @@ BasesManager.prototype.init = function(gameState) BasesManager.prototype.postinit = function(gameState) { // Rebuild the base maps from the territory indices of each base. - this.basesMap = new API3.Map(gameState.sharedScript, "territory"); + this.basesMap = new InfoMap(gameState.sharedScript, "territory"); for (const base of this.baseManagers) for (const j of base.territoryIndices) this.basesMap.map[j] = base.ID; @@ -100,9 +104,9 @@ BasesManager.prototype.createBase = function(gameState, ent, type = BaseManager. if (this.Config.debug > 0) { - API3.warn(" ----------------------------------------------------------"); - API3.warn(" BasesManager createBase entrance avec access " + access + " and type " + type); - API3.warn(" with access " + uneval(this.baseManagers.map(base => base.accessIndex)) + + aiWarn(" ----------------------------------------------------------"); + aiWarn(" BasesManager createBase entrance avec access " + access + " and type " + type); + aiWarn(" with access " + uneval(this.baseManagers.map(base => base.accessIndex)) + " and base nbr " + uneval(this.baseManagers.map(base => base.ID)) + " and anchor " + uneval(this.baseManagers.map(base => !!base.anchor))); } @@ -327,7 +331,7 @@ BasesManager.prototype.checkEvents = function(gameState, events) { if (!dropsite.position() || getLandAccess(gameState, dropsite) != access) continue; - const dist = API3.SquareVectorDistance(pos, dropsite.position()); + const dist = SquareVectorDistance(pos, dropsite.position()); if (dist > distmin) continue; distmin = dist; @@ -364,7 +368,7 @@ BasesManager.prototype.bulkPickWorkers = function(gameState, baseRef, number) }); let needed = number; - const workers = new API3.EntityCollection(gameState.sharedScript); + const workers = new EntityCollection(gameState.sharedScript); for (const base of baseBest) { if (base.ID == baseRef.ID) @@ -659,10 +663,10 @@ BasesManager.prototype.removeBaseFromTerritoryIndex = function(territoryIndex) if (index != -1) base.territoryIndices.splice(index, 1); else - API3.warn(" problem in headquarters::updateTerritories for base " + baseID); + aiWarn(" problem in headquarters::updateTerritories for base " + baseID); } else - API3.warn(" problem in headquarters::updateTerritories without base " + baseID); + aiWarn(" problem in headquarters::updateTerritories without base " + baseID); this.basesMap.map[territoryIndex] = 0; }; @@ -674,7 +678,7 @@ BasesManager.prototype.addTerritoryIndexToBase = function(gameState, territoryIn if (this.baseAtIndex(territoryIndex) != 0) return false; let landPassable = false; - const ind = API3.getMapIndices(territoryIndex, gameState.ai.HQ.territoryMap, passabilityMap); + const ind = getMapIndices(territoryIndex, gameState.ai.HQ.territoryMap, passabilityMap); let access; for (const k of ind) { @@ -695,7 +699,7 @@ BasesManager.prototype.addTerritoryIndexToBase = function(gameState, territoryIn continue; if (base.accessIndex != access) continue; - const dist = API3.SquareVectorDistance(base.anchor.position(), pos); + const dist = SquareVectorDistance(base.anchor.position(), pos); if (dist >= distmin) continue; distmin = dist; @@ -719,7 +723,7 @@ BasesManager.prototype.reassignTerritories = function(deletedBase, territoryMap) continue; if (territoryMap.getOwnerIndex(j) != PlayerID) { - API3.warn("Petra reassignTerritories: should never happen"); + aiWarn("Petra reassignTerritories: should never happen"); this.basesMap.map[j] = 0; continue; } @@ -733,7 +737,7 @@ BasesManager.prototype.reassignTerritories = function(deletedBase, territoryMap) continue; if (base.accessIndex != deletedBase.accessIndex) continue; - const dist = API3.SquareVectorDistance(base.anchor.position(), pos); + const dist = SquareVectorDistance(base.anchor.position(), pos); if (dist >= distmin) continue; distmin = dist; diff --git a/binaries/data/mods/public/simulation/ai/petra/buildManager.js b/binaries/data/mods/public/simulation/ai/petra/buildManager.js index dedecca40f..12a5911a1c 100644 --- a/binaries/data/mods/public/simulation/ai/petra/buildManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/buildManager.js @@ -1,3 +1,6 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; + /** * One task of this manager is to cache the list of structures we have builders for, * to avoid having to loop on all entities each time. @@ -30,7 +33,8 @@ BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment) const count = this.builderCounters.get(buildable) + increment; if (count < 0) { - API3.warn(" Petra error in incrementBuilderCounters for " + buildable + " with count < 0"); + aiWarn(" Petra error in incrementBuilderCounters for " + buildable + + " with count < 0"); continue; } this.builderCounters.set(buildable, count); @@ -38,7 +42,7 @@ BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment) else if (increment > 0) this.builderCounters.set(buildable, increment); else - API3.warn(" Petra error in incrementBuilderCounters for " + buildable + " not yet set"); + aiWarn(" Petra error in incrementBuilderCounters for " + buildable + " not yet set"); } }; @@ -121,7 +125,7 @@ BuildManager.prototype.findStructuresByFilter = function(gameState, filter) */ BuildManager.prototype.findStructureWithClass = function(gameState, classes) { - return this.findStructuresByFilter(gameState, API3.Filters.byClasses(classes))[0]; + return this.findStructuresByFilter(gameState, filters.byClasses(classes))[0]; }; BuildManager.prototype.hasBuilder = function(template) diff --git a/binaries/data/mods/public/simulation/ai/petra/config.js b/binaries/data/mods/public/simulation/ai/petra/config.js index 778af2ebea..c8f1a98554 100644 --- a/binaries/data/mods/public/simulation/ai/petra/config.js +++ b/binaries/data/mods/public/simulation/ai/petra/config.js @@ -1,3 +1,5 @@ +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; + // These integers must be sequential /* eslint-disable prefer-const -- Mods should be able to change them */ export let DIFFICULTY_SANDBOX = 0; @@ -326,7 +328,7 @@ Config.prototype.setConfig = function(gameState) if (this.debug < 2) return; - API3.warn(" >>> Petra bot: personality = " + uneval(this.personality)); + aiWarn(" >>> Petra bot: personality = " + uneval(this.personality)); }; Config.prototype.Cheat = function(gameState) diff --git a/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js b/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js index d7c6627ab8..f5d6567e20 100644 --- a/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js +++ b/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js @@ -1,3 +1,4 @@ +import { SquareVectorDistance } from "simulation/ai/common-api/utils.js"; import { allowCapture, getLandAccess, getMaxStrength, isSiegeUnit, returnResources } from "simulation/ai/petra/entityExtend.js"; import { TransportPlan } from "simulation/ai/petra/transportPlan.js"; @@ -62,7 +63,7 @@ DefenseArmy.prototype.addFoe = function(gameState, enemyId, force) return false; // check distance - if (!force && API3.SquareVectorDistance(ent.position(), this.foePosition) > this.compactSize) + if (!force && SquareVectorDistance(ent.position(), this.foePosition) > this.compactSize) return false; this.foeEntities.push(enemyId); @@ -257,7 +258,7 @@ DefenseArmy.prototype.clear = function(gameState) const defensiveStruct = struct.hasDefensiveFire(); if (defensiveFound && !defensiveStruct) continue; - const dist = API3.SquareVectorDistance(posOther, pos); + const dist = SquareVectorDistance(posOther, pos); if (distmin && dist > distmin && (defensiveFound || !defensiveStruct)) continue; if (defensiveStruct) @@ -332,7 +333,7 @@ DefenseArmy.prototype.assignUnit = function(gameState, entID) this.assignedAgainst[id].length > 5 && !eEnt.hasClass("Hero") && !isSiegeUnit(eEnt)) continue; - const dist = API3.SquareVectorDistance(ent.position(), eEnt.position()); + const dist = SquareVectorDistance(ent.position(), eEnt.position()); if (idMinAll === undefined || dist < distMinAll) { idMinAll = id; @@ -620,7 +621,7 @@ DefenseArmy.prototype.update = function(gameState) const ent = gameState.getEntityById(id); if (!ent || !ent.position()) continue; - if (API3.SquareVectorDistance(ent.position(), this.foePosition) > this.breakawaySize) + if (SquareVectorDistance(ent.position(), this.foePosition) > this.breakawaySize) { breakaways.push(id); if (this.removeFoe(gameState, id)) diff --git a/binaries/data/mods/public/simulation/ai/petra/defenseManager.js b/binaries/data/mods/public/simulation/ai/petra/defenseManager.js index 0609d23f38..171c5b4134 100644 --- a/binaries/data/mods/public/simulation/ai/petra/defenseManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/defenseManager.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { AttackPlan } from "simulation/ai/petra/attackPlan.js"; import { DefenseArmy } from "simulation/ai/petra/defenseArmy.js"; import { allowCapture, getLandAccess, getMaxStrength, isSiegeUnit } from @@ -121,7 +123,7 @@ DefenseManager.prototype.isDangerous = function(gameState, entity) { if (building.foundationProgress() == 0) continue; - if (API3.SquareVectorDistance(building.position(), entity.position()) > 30000) + if (SquareVectorDistance(building.position(), entity.position()) > 30000) continue; this.targetList.push(targetId); return true; @@ -143,11 +145,11 @@ DefenseManager.prototype.isDangerous = function(gameState, entity) // The enemy base is either destroyed or built. if (!target || !target.position()) continue; - if (API3.SquareVectorDistance(target.position(), entity.position()) < dist2Min) + if (SquareVectorDistance(target.position(), entity.position()) < dist2Min) return true; } - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); + const ccEnts = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")); for (const cc of ccEnts.values()) { if (!gameState.isEntityExclusiveAlly(cc) || cc.foundationProgress() == 0) @@ -155,15 +157,17 @@ DefenseManager.prototype.isDangerous = function(gameState, entity) const cooperation = this.GetCooperationLevel(cc.owner()); if (cooperation < 0.3 || cooperation < 0.6 && !!cc.foundationProgress()) continue; - if (API3.SquareVectorDistance(cc.position(), entity.position()) < dist2Min) + if (SquareVectorDistance(cc.position(), entity.position()) < dist2Min) return true; } for (const building of gameState.getOwnStructures().values()) { if (building.foundationProgress() == 0 || - API3.SquareVectorDistance(building.position(), entity.position()) > dist2Min) + SquareVectorDistance(building.position(), entity.position()) > dist2Min) + { continue; + } if (!this.territoryMap.isBlinking(building.position()) || gameState.ai.HQ.isDefendable(building)) return true; } @@ -178,8 +182,10 @@ DefenseManager.prototype.isDangerous = function(gameState, entity) for (const building of gameState.getAllyStructures(territoryOwner).values()) { if (building.foundationProgress() == 0 || - API3.SquareVectorDistance(building.position(), entity.position()) > dist2Min) + SquareVectorDistance(building.position(), entity.position()) > dist2Min) + { continue; + } if (!this.territoryMap.isBlinking(building.position())) return true; } @@ -302,8 +308,10 @@ DefenseManager.prototype.checkEnemyArmies = function(gameState) { const otherArmy = this.armies[j]; if (otherArmy.getType() != "default" || - API3.SquareVectorDistance(army.foePosition, otherArmy.foePosition) > this.armyMergeSize) + SquareVectorDistance(army.foePosition, otherArmy.foePosition) > this.armyMergeSize) + { continue; + } // No need to clear here. army.merge(gameState, otherArmy); this.armies.splice(j--, 1); @@ -351,7 +359,7 @@ DefenseManager.prototype.checkEnemyArmies = function(gameState) // Army in neutral territory. // TODO check smaller distance with all our buildings instead of only ccs with big distance. let stillDangerous = false; - const bases = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); + const bases = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")); for (const base of bases.values()) { if (!gameState.isEntityAlly(base)) @@ -359,19 +367,19 @@ DefenseManager.prototype.checkEnemyArmies = function(gameState) const cooperation = this.GetCooperationLevel(base.owner()); if (cooperation < 0.3 && !gameState.isEntityOwn(base)) continue; - if (API3.SquareVectorDistance(base.position(), army.foePosition) > 40000) + if (SquareVectorDistance(base.position(), army.foePosition) > 40000) continue; if (this.Config.debug > 1) - API3.warn("army in neutral territory, but still near one of our CC"); + aiWarn("army in neutral territory, but still near one of our CC"); stillDangerous = true; break; } if (stillDangerous) continue; // Need to also check docks because of oversea bases. - for (const dock of gameState.getOwnStructures().filter(API3.Filters.byClass("Dock")).values()) + for (const dock of gameState.getOwnStructures().filter(filters.byClass("Dock")).values()) { - if (API3.SquareVectorDistance(dock.position(), army.foePosition) > 10000) + if (SquareVectorDistance(dock.position(), army.foePosition) > 10000) continue; stillDangerous = true; break; @@ -409,7 +417,7 @@ DefenseManager.prototype.assignDefenders = function(gameState) break; } if (!armyAccess) - API3.warn(" Petra error: attacking army " + army.ID + " without access"); + aiWarn(" Petra error: attacking army " + army.ID + " without access"); army.recalculatePosition(gameState); armiesNeeding.push({ "army": army, "access": armyAccess, "need": needsDef }); } @@ -472,7 +480,8 @@ DefenseManager.prototype.assignDefenders = function(gameState) })) continue; - const dist = API3.SquareVectorDistance(ent.position(), armiesNeeding[a].army.foePosition); + const dist = SquareVectorDistance(ent.position(), + armiesNeeding[a].army.foePosition); if (aMin !== undefined && dist > distMin) continue; aMin = a; @@ -584,7 +593,7 @@ DefenseManager.prototype.checkEvents = function(gameState, events) { const pos = attacker.position(); const range = attacker.attackRange("Ranged") ? attacker.attackRange("Ranged").max + 15 : 25; - if (range * range > API3.SquareVectorDistance(pos, target.position())) + if (range * range > SquareVectorDistance(pos, target.position())) target.moveToRange(pos[0], pos[1], range, range + 5); } continue; @@ -727,7 +736,7 @@ DefenseManager.prototype.checkEvents = function(gameState, events) if (!entOrderData || !entOrderData.length || !entOrderData[0].target || entOrderData[0].target != orderData[0].target) continue; - const dist = API3.SquareVectorDistance(pos, ent.position()); + const dist = SquareVectorDistance(pos, ent.position()); if (minEnt && dist > minDist) continue; minDist = dist; @@ -759,7 +768,7 @@ DefenseManager.prototype.garrisonUnitsInside = function(gameState, target, data) const attackTypes = target.attackTypes(); if (!attackTypes || attackTypes.indexOf("Ranged") == -1) return false; - const dist = API3.SquareVectorDistance(data.attacker.position(), target.position()); + const dist = SquareVectorDistance(data.attacker.position(), target.position()); const range = target.attackRange("Ranged").max; if (dist >= range*range) return false; @@ -847,7 +856,7 @@ DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit) continue; if (getLandAccess(gameState, ent) != unitAccess) continue; - const dist = API3.SquareVectorDistance(ent.position(), unit.position()); + const dist = SquareVectorDistance(ent.position(), unit.position()); if (dist > distmin) continue; distmin = dist; @@ -884,7 +893,7 @@ DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit, emerge continue; if (getLandAccess(gameState, ent) != unitAccess) continue; - const dist = API3.SquareVectorDistance(ent.position(), unit.position()); + const dist = SquareVectorDistance(ent.position(), unit.position()); if (dist > distmin) continue; distmin = dist; @@ -936,7 +945,7 @@ DefenseManager.prototype.switchToAttack = function(gameState, army) const ent = gameState.getEntityById(entId); if (!ent || !ent.position() || getLandAccess(gameState, ent) != targetAccess) continue; - if (API3.SquareVectorDistance(targetPos, ent.position()) > 14400) + if (SquareVectorDistance(targetPos, ent.position()) > 14400) continue; gameState.ai.HQ.attackManager.switchDefenseToAttack(gameState, target, { "armyID": army.ID, "uniqueTarget": true }); return; diff --git a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js index 565ece4111..2377848518 100644 --- a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js @@ -1,3 +1,4 @@ +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; import * as chat from "simulation/ai/petra/chatHelper.js"; /** @@ -108,7 +109,7 @@ DiplomacyManager.prototype.tributes = function(gameState) this.nextTributeRequest.set(res, gameState.ai.elapsedTime + 240); chat.requestTribute(gameState, res); if (this.Config.debug > 1) - API3.warn("Tribute on " + res + " requested to player " + i); + aiWarn("Tribute on " + res + " requested to player " + i); break; } } @@ -117,7 +118,7 @@ DiplomacyManager.prototype.tributes = function(gameState) if (!toSend) continue; if (this.Config.debug > 1) - API3.warn("Tribute " + uneval(tribute) + " sent to player " + i); + aiWarn("Tribute " + uneval(tribute) + " sent to player " + i); if (this.Config.chat) chat.sentTribute(gameState, i); Engine.PostCommand(PlayerID, { "type": "tribute", "player": i, "amounts": tribute }); @@ -140,7 +141,10 @@ DiplomacyManager.prototype.checkEvents = function(gameState, events) if (request.wanted <= 0) { if (this.Config.debug > 1) - API3.warn("Player " + uneval(evt.from) + " has sent the required tribute amount"); + { + aiWarn("Player " + uneval(evt.from) + + " has sent the required tribute amount"); + } this.changePlayerDiplomacy(gameState, evt.from, request.requestType); request.status = "accepted"; @@ -239,7 +243,10 @@ DiplomacyManager.prototype.checkEvents = function(gameState, events) this.handleDiplomacyRequest(gameState, evt.source, evt.to); const request = this.receivedDiplomacyRequests.get(evt.source); if (this.Config.debug > 0) - API3.warn("Responding to diplomacy request from AI player " + evt.source + " with " + uneval(request)); + { + aiWarn("Responding to diplomacy request from AI player " + evt.source + " with " + + uneval(request)); + } // Our diplomacy will have changed already if the response was "accept" if (request.status === "waitingForTribute") @@ -267,7 +274,10 @@ DiplomacyManager.prototype.checkEvents = function(gameState, events) const responseTribute = {}; responseTribute[evt.resourceType] = evt.resourceWanted; if (this.Config.debug > 0) - API3.warn("Responding to tribute request from AI player " + evt.source + " with " + uneval(responseTribute)); + { + aiWarn("Responding to tribute request from AI player " + evt.source + " with " + + uneval(responseTribute)); + } Engine.PostCommand(PlayerID, { "type": "tribute", "player": evt.source, "amounts": responseTribute }); this.nextTributeUpdate = gameState.ai.elapsedTime + 15; } @@ -324,7 +334,7 @@ DiplomacyManager.prototype.lastManStandingCheck = function(gameState) if (gameState.getVictoryConditions().has("wonder")) { - const wonder = gameState.getEnemyStructures(i).filter(API3.Filters.byClass("Wonder"))[0]; + const wonder = gameState.getEnemyStructures(i).filter(filters.byClass("Wonder"))[0]; if (wonder) { const wonderProgess = wonder.foundationProgress(); @@ -339,8 +349,8 @@ DiplomacyManager.prototype.lastManStandingCheck = function(gameState) if (gameState.getVictoryConditions().has("capture_the_relic")) { - const relicsCount = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")) - .filter(relic => relic.owner() === i).length; + const relicsCount = gameState.updatingGlobalCollection("allRelics", + filters.byClass("Relic")).filter(relic => relic.owner() === i).length; turnFactor += relicsCount * this.betrayWeighting; } @@ -442,7 +452,7 @@ DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, n gameState.ai.HQ.attackManager.cancelAttacksAgainstPlayer(gameState, player); Engine.PostCommand(PlayerID, { "type": "diplomacy", "player": player, "to": newDiplomaticStance }); if (this.Config.debug > 1) - API3.warn("diplomacy stance with player " + player + " is now " + newDiplomaticStance); + aiWarn("diplomacy stance with player " + player + " is now " + newDiplomaticStance); if (this.Config.chat) chat.newDiplomacy(gameState, player, newDiplomaticStance); }; @@ -510,7 +520,7 @@ DiplomacyManager.prototype.sendDiplomacyRequest = function(gameState) }); if (this.Config.debug > 0) - API3.warn("Sending diplomacy request to player " + player + " with " + requestType); + aiWarn("Sending diplomacy request to player " + player + " with " + requestType); Engine.PostCommand(PlayerID, { "type": "diplomacy-request", "source": PlayerID, "player": player, "to": requestType }); chat.newRequestDiplomacy(gameState, player, requestType, "sendRequest"); }; diff --git a/binaries/data/mods/public/simulation/ai/petra/entityExtend.js b/binaries/data/mods/public/simulation/ai/petra/entityExtend.js index 4ed9e2b546..2048c844ae 100644 --- a/binaries/data/mods/public/simulation/ai/petra/entityExtend.js +++ b/binaries/data/mods/public/simulation/ai/petra/entityExtend.js @@ -1,3 +1,5 @@ +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; + /** returns true if this unit should be considered as a siege unit */ export function isSiegeUnit(ent) { @@ -34,7 +36,10 @@ export function getMaxStrength(ent, debugLevel, DamageTypeImportance, againstCla if (DamageTypeImportance[str]) strength += DamageTypeImportance[str] * val / damageTypes.length; else if (debugLevel > 0) - API3.warn("Petra: " + str + " unknown attackStrength in getMaxStrength (please add " + str + " to config.js)."); + { + aiWarn("Petra: " + str + " unknown attackStrength in getMaxStrength (please add " + + str + " to config.js)."); + } } const attackRange = ent.attackRange(type); @@ -54,7 +59,7 @@ export function getMaxStrength(ent, debugLevel, DamageTypeImportance, againstCla strength -= val / 100000; break; default: - API3.warn("Petra: " + str + " unknown attackTimes in getMaxStrength"); + aiWarn("Petra: " + str + " unknown attackTimes in getMaxStrength"); } } } @@ -68,7 +73,8 @@ export function getMaxStrength(ent, debugLevel, DamageTypeImportance, againstCla if (DamageTypeImportance[str]) strength += DamageTypeImportance[str] * val / damageTypes.length; else if (debugLevel > 0) - API3.warn("Petra: " + str + " unknown resistanceStrength in getMaxStrength (please add " + str + " to config.js)."); + aiWarn("Petra: " + str + " unknown resistanceStrength in getMaxStrength " + + "(please add " + str + " to config.js)."); } // ToDo: Add support for StatusEffects and Capture. @@ -88,7 +94,7 @@ export function getLandAccess(gameState, ent) if (holder) return getLandAccess(gameState, holder); - API3.warn("Petra error: entity without position, but not garrisoned"); + aiWarn("Petra error: entity without position, but not garrisoned"); dumpEntity(ent); return undefined; } @@ -242,7 +248,7 @@ export function returnResources(gameState, ent) continue; if (getLandAccess(gameState, dropsite) != access) continue; - const dist = API3.SquareVectorDistance(ent.position(), dropsite.position()); + const dist = SquareVectorDistance(ent.position(), dropsite.position()); if (dist > distmin) continue; distmin = dist; @@ -277,7 +283,7 @@ export function getBestBase(gameState, ent, onlyConstructedBase = false, exclude const holder = getHolder(gameState, ent); if (!holder || !holder.position()) { - API3.warn("Petra error: entity without position, but not garrisoned"); + aiWarn("Petra error: entity without position, but not garrisoned"); dumpEntity(ent); return gameState.ai.HQ.basesManager.baselessBase(); } @@ -299,7 +305,7 @@ export function getBestBase(gameState, ent, onlyConstructedBase = false, exclude if (ent.hasClass("Structure") && base.accessIndex != accessIndex) continue; if (base.anchor && base.anchor.position()) - dist = API3.SquareVectorDistance(base.anchor.position(), pos); + dist = SquareVectorDistance(base.anchor.position(), pos); else { let found = false; @@ -307,7 +313,7 @@ export function getBestBase(gameState, ent, onlyConstructedBase = false, exclude { if (!structure.position()) continue; - dist = API3.SquareVectorDistance(structure.position(), pos); + dist = SquareVectorDistance(structure.position(), pos); found = true; break; } @@ -367,7 +373,7 @@ export function isNotWorthBuilding(gameState, ent) */ export function isLineInsideEnemyTerritory(gameState, pos1, pos2, step=70) { - const n = Math.floor(Math.sqrt(API3.SquareVectorDistance(pos1, pos2))/step) + 1; + const n = Math.floor(Math.sqrt(SquareVectorDistance(pos1, pos2))/step) + 1; const stepx = (pos2[0] - pos1[0]) / n; const stepy = (pos2[1] - pos1[1]) / n; for (let i = 1; i < n; ++i) @@ -404,7 +410,7 @@ export function gatherTreasure(gameState, ent, water = false) const territoryOwner = gameState.ai.HQ.territoryMap.getOwner(treasure.position()); if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) continue; - const dist = API3.SquareVectorDistance(ent.position(), treasure.position()); + const dist = SquareVectorDistance(ent.position(), treasure.position()); if (dist > 120000 || territoryOwner != PlayerID && dist > 14000) // AI has no LOS, so restrict it a bit continue; if (dist > distmin) @@ -424,17 +430,17 @@ export function dumpEntity(ent) { if (!ent) return; - API3.warn(" >>> id " + ent.id() + " name " + ent.genericName() + " pos " + ent.position() + - " state " + ent.unitAIState()); - API3.warn(" base " + ent.getMetadata(PlayerID, "base") + " >>> role " + ent.getMetadata(PlayerID, "role") + - " subrole " + ent.getMetadata(PlayerID, "subrole")); - API3.warn("owner " + ent.owner() + " health " + ent.hitpoints() + " healthMax " + ent.maxHitpoints() + - " foundationProgress " + ent.foundationProgress()); - API3.warn(" garrisoning " + ent.getMetadata(PlayerID, "garrisoning") + - " garrisonHolder " + ent.getMetadata(PlayerID, "garrisonHolder") + - " plan " + ent.getMetadata(PlayerID, "plan") + " transport " + ent.getMetadata(PlayerID, "transport")); - API3.warn(" stance " + ent.getStance() + " transporter " + ent.getMetadata(PlayerID, "transporter") + - " gather-type " + ent.getMetadata(PlayerID, "gather-type") + - " target-foundation " + ent.getMetadata(PlayerID, "target-foundation") + - " PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy")); + aiWarn(" >>> id " + ent.id() + " name " + ent.genericName() + " pos " + ent.position() + " state " + + ent.unitAIState()); + aiWarn(" base " + ent.getMetadata(PlayerID, "base") + " >>> role " + + ent.getMetadata(PlayerID, "role") + " subrole " + ent.getMetadata(PlayerID, "subrole")); + aiWarn("owner " + ent.owner() + " health " + ent.hitpoints() + " healthMax " + ent.maxHitpoints() + + " foundationProgress " + ent.foundationProgress()); + aiWarn(" garrisoning " + ent.getMetadata(PlayerID, "garrisoning") + " garrisonHolder " + + ent.getMetadata(PlayerID, "garrisonHolder") + " plan " + ent.getMetadata(PlayerID, "plan") + + " transport " + ent.getMetadata(PlayerID, "transport")); + aiWarn(" stance " + ent.getStance() + " transporter " + ent.getMetadata(PlayerID, "transporter") + + " gather-type " + ent.getMetadata(PlayerID, "gather-type") + " target-foundation " + + ent.getMetadata(PlayerID, "target-foundation") + " PartOfArmy " + + ent.getMetadata(PlayerID, "PartOfArmy")); } diff --git a/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js b/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js index 2e7ee19af9..21011bdb24 100644 --- a/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js @@ -1,3 +1,4 @@ +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { dumpEntity, isSiegeUnit } from "simulation/ai/petra/entityExtend.js"; import { Worker } from "simulation/ai/petra/worker.js"; @@ -115,9 +116,10 @@ GarrisonManager.prototype.update = function(gameState, events) { if (gameState.ai.Config.debug > 0) { - API3.warn("Petra garrison error: unit " + ent.id() + " (" + ent.genericName() + - ") is expected to garrison in " + id + " (" + holder.genericName() + - "), but has no such garrison order " + uneval(ent.unitAIOrderData())); + aiWarn("Petra garrison error: unit " + ent.id() + " (" + + ent.genericName() + ") is expected to garrison in " + id + " (" + + holder.genericName() + "), but has no such garrison order " + + uneval(ent.unitAIOrderData())); dumpEntity(ent); } list.splice(j--, 1); @@ -149,7 +151,7 @@ GarrisonManager.prototype.update = function(gameState, events) continue; if (!ent.position()) continue; - const dist = API3.SquareVectorDistance(ent.position(), holder.position()); + const dist = SquareVectorDistance(ent.position(), holder.position()); if (dist > range*range) continue; if (ent.hasClass("Structure")) @@ -332,9 +334,9 @@ GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around) default: if (ent.getMetadata(PlayerID, "onBoard") === "onBoard") // transport is not (yet ?) managed by garrisonManager return true; - API3.warn("unknown type in garrisonManager " + ent.getMetadata(PlayerID, "garrisonType") + - " for " + ent.genericName() + " id " + ent.id() + - " inside " + holder.genericName() + " id " + holder.id()); + aiWarn("unknown type in garrisonManager " + ent.getMetadata(PlayerID, "garrisonType") + + " for " + ent.genericName() + " id " + ent.id() + " inside " + holder.genericName() + + " id " + holder.id()); ent.setMetadata(PlayerID, "garrisonType", GarrisonManager.TYPE_PROTECTION); return true; } diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js index f1940886ad..86cf323901 100644 --- a/binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -1,3 +1,6 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { AttackManager } from "simulation/ai/petra/attackManager.js"; import { AttackPlan } from "simulation/ai/petra/attackPlan.js"; import { BasesManager } from "simulation/ai/petra/basesManager.js"; @@ -115,9 +118,9 @@ Headquather.prototype.getSeaBetweenIndices = function(gameState, index1, index2) if (this.Config.debug > 1) { - API3.warn("bad path from " + index1 + " to " + index2 + " ??? " + uneval(path)); - API3.warn(" regionLinks start " + uneval(gameState.ai.accessibility.regionLinks[index1])); - API3.warn(" regionLinks end " + uneval(gameState.ai.accessibility.regionLinks[index2])); + aiWarn("bad path from " + index1 + " to " + index2 + " ??? " + uneval(path)); + aiWarn(" regionLinks start " + uneval(gameState.ai.accessibility.regionLinks[index1])); + aiWarn(" regionLinks end " + uneval(gameState.ai.accessibility.regionLinks[index2])); } return undefined; }; @@ -575,7 +578,7 @@ Headquather.prototype.findBestTrainableUnit = function(gameState, classes, requi bValue *= param[1]; } else - API3.warn(" trainMoreUnits avec non prevu " + uneval(param)); + aiWarn(" trainMoreUnits avec non prevu " + uneval(param)); } return -aValue/aCost + bValue/bCost; }); @@ -675,8 +678,9 @@ Headquather.prototype.findEconomicCCLocation = function(gameState, template, res else if (template.get("Footprint/Circle")) halfSize = +template.get("Footprint/Circle/@radius"); - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); - const dpEnts = gameState.getOwnDropsites().filter(API3.Filters.not(API3.Filters.byClasses(["CivCentre", "Unit"]))); + const ccEnts = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")); + const dpEnts = gameState.getOwnDropsites().filter( + filters.not(filters.byClasses(["CivCentre", "Unit"]))); const ccList = []; for (const cc of ccEnts.values()) ccList.push({ "ent": cc, "pos": cc.position(), "ally": gameState.isPlayerAlly(cc.owner()) }); @@ -740,7 +744,7 @@ Headquather.prototype.findEconomicCCLocation = function(gameState, template, res let oversea = false; if (proximity) // This is our first cc, let's do it near our units - norm /= 1 + API3.SquareVectorDistance(proximity, pos) / scale; + norm /= 1 + SquareVectorDistance(proximity, pos) / scale; else { let minDist = Math.min(); @@ -748,7 +752,7 @@ Headquather.prototype.findEconomicCCLocation = function(gameState, template, res for (const cc of ccList) { - const dist = API3.SquareVectorDistance(cc.pos, pos); + const dist = SquareVectorDistance(cc.pos, pos); if (dist < nearbyRejected) { norm = 0; @@ -793,7 +797,7 @@ Headquather.prototype.findEconomicCCLocation = function(gameState, template, res { for (const dp of dpList) { - const dist = API3.SquareVectorDistance(dp.pos, pos); + const dist = SquareVectorDistance(dp.pos, pos); if (dist < 3600) { norm = 0; @@ -833,7 +837,7 @@ Headquather.prototype.findEconomicCCLocation = function(gameState, template, res if (bestVal === undefined) return false; if (this.Config.debug > 1) - API3.warn("we have found a base for " + resource + " with best (cut=" + cut + ") = " + bestVal); + aiWarn("we have found a base for " + resource + " with best (cut=" + cut + ") = " + bestVal); // not good enough. if (bestVal < cut) return false; @@ -866,7 +870,7 @@ Headquather.prototype.findStrategicCCLocation = function(gameState, template) // with the constraints that all CC have dist > 200 and at least one have dist < 400 // This needs at least 2 CC. Otherwise, go back to economic CC. - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); + const ccEnts = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")); const ccList = []; let numAllyCC = 0; for (const cc of ccEnts.values()) @@ -919,7 +923,7 @@ Headquather.prototype.findStrategicCCLocation = function(gameState, template) for (const cc of ccList) { - const dist = API3.SquareVectorDistance(cc.pos, pos); + const dist = SquareVectorDistance(cc.pos, pos); if (dist < 14000) // Reject if too near from any cc { minDist = 0; @@ -974,7 +978,7 @@ Headquather.prototype.findStrategicCCLocation = function(gameState, template) } if (this.Config.debug > 1) - API3.warn("We've found a strategic base with bestVal = " + bestVal); + aiWarn("We've found a strategic base with bestVal = " + bestVal); Engine.ProfileStop(); @@ -1007,9 +1011,10 @@ Headquather.prototype.findStrategicCCLocation = function(gameState, template) */ Headquather.prototype.findMarketLocation = function(gameState, template) { - let markets = gameState.updatingCollection("diplo-ExclusiveAllyMarkets", API3.Filters.byClass("Trade"), gameState.getExclusiveAllyEntities()).toEntityArray(); + let markets = gameState.updatingCollection("diplo-ExclusiveAllyMarkets", filters.byClass("Trade"), + gameState.getExclusiveAllyEntities()).toEntityArray(); if (!markets.length) - markets = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Trade"), gameState.getOwnStructures()).toEntityArray(); + markets = gameState.updatingCollection("OwnMarkets", filters.byClass("Trade"), gameState.getOwnStructures()).toEntityArray(); if (!markets.length) // this is the first market. For the time being, place it arbitrarily by the ConstructionPlan return [-1, -1, -1, 0]; @@ -1074,7 +1079,7 @@ Headquather.prototype.findMarketLocation = function(gameState, template) continue; if (!gainMultiplier) continue; - const distSq = API3.SquareVectorDistance(market.position(), pos); + const distSq = SquareVectorDistance(market.position(), pos); if (gainMultiplier * distSq > maxVal) { maxVal = gainMultiplier * distSq; @@ -1096,13 +1101,13 @@ Headquather.prototype.findMarketLocation = function(gameState, template) } if (this.Config.debug > 1) - API3.warn("We found a market position with bestVal = " + bestVal); + aiWarn("We found a market position with bestVal = " + bestVal); if (bestVal === undefined) // no constraints. For the time being, place it arbitrarily by the ConstructionPlan return [-1, -1, -1, 0]; const expectedGain = Math.round(bestGainMult * TradeGain(bestDistSq, gameState.sharedScript.mapSize)); if (this.Config.debug > 1) - API3.warn("this would give a trading gain of " + expectedGain); + aiWarn("this would give a trading gain of " + expectedGain); // Do not keep it if gain is too small, except if this is our first Market. let idx; if (expectedGain < this.tradeManager.minimalGain) @@ -1131,16 +1136,20 @@ Headquather.prototype.findDefensiveLocation = function(gameState, template) // but requiring a minimal distance with our other defensive structures // and not in range of any enemy defensive structure to avoid building under fire. - const ownStructures = gameState.getOwnStructures().filter(API3.Filters.byClasses(["Fortress", "Tower"])).toEntityArray(); - let enemyStructures = gameState.getEnemyStructures().filter(API3.Filters.not(API3.Filters.byOwner(0))). - filter(API3.Filters.byClasses(["CivCentre", "Fortress", "Tower"])); + const ownStructures = gameState.getOwnStructures().filter(filters.byClasses(["Fortress", "Tower"])) + .toEntityArray(); + let enemyStructures = gameState.getEnemyStructures().filter(filters.not(filters.byOwner(0))) + .filter(filters.byClasses(["CivCentre", "Fortress", "Tower"])); if (!enemyStructures.hasEntities()) // we may be in cease fire mode, build defense against neutrals { - enemyStructures = gameState.getNeutralStructures().filter(API3.Filters.not(API3.Filters.byOwner(0))). - filter(API3.Filters.byClasses(["CivCentre", "Fortress", "Tower"])); + enemyStructures = gameState.getNeutralStructures().filter(filters.not(filters.byOwner(0))) + .filter(filters.byClasses(["CivCentre", "Fortress", "Tower"])); if (!enemyStructures.hasEntities() && !gameState.getAlliedVictory()) - enemyStructures = gameState.getAllyStructures().filter(API3.Filters.not(API3.Filters.byOwner(PlayerID))). - filter(API3.Filters.byClasses(["CivCentre", "Fortress", "Tower"])); + { + enemyStructures = gameState.getAllyStructures().filter( + filters.not(filters.byOwner(PlayerID))).filter( + filters.byClasses(["CivCentre", "Fortress", "Tower"])); + } if (!enemyStructures.hasEntities()) return undefined; } @@ -1151,7 +1160,7 @@ Headquather.prototype.findDefensiveLocation = function(gameState, template) let wonders; if (wonderMode) { - wonders = gameState.getOwnStructures().filter(API3.Filters.byClass("Wonder")).toEntityArray(); + wonders = gameState.getOwnStructures().filter(filters.byClass("Wonder")).toEntityArray(); wonderMode = wonders.length != 0; if (wonderMode) wonderDistmin = (50 + wonders[0].footprintRadius()) * (50 + wonders[0].footprintRadius()); @@ -1203,7 +1212,7 @@ Headquather.prototype.findDefensiveLocation = function(gameState, template) let dista = 0; if (wonderMode) { - dista = API3.SquareVectorDistance(wonders[0].position(), pos); + dista = SquareVectorDistance(wonders[0].position(), pos); if (dista < wonderDistmin) continue; dista *= 200; // empirical factor (TODO should depend on map size) to stay near the wonder @@ -1216,7 +1225,7 @@ Headquather.prototype.findDefensiveLocation = function(gameState, template) const strPos = str.position(); if (!strPos) continue; - const dist = API3.SquareVectorDistance(strPos, pos); + const dist = SquareVectorDistance(strPos, pos); if (dist < 6400) // TODO check on true attack range instead of this 80×80 { minDist = -1; @@ -1234,7 +1243,7 @@ Headquather.prototype.findDefensiveLocation = function(gameState, template) const strPos = str.position(); if (!strPos) continue; - if (API3.SquareVectorDistance(strPos, pos) < cutDist) + if (SquareVectorDistance(strPos, pos) < cutDist) { minDist = -1; break; @@ -1446,11 +1455,12 @@ Headquather.prototype.buildMoreHouses = function(gameState, queues) if (!houseTemplate.hasClass(entityReq.class)) continue; - const count = gameState.getOwnStructures().filter(API3.Filters.byClass(entityReq.class)).length; + const count = gameState.getOwnStructures().filter(filters.byClass(entityReq.class)) + .length; if (count < entityReq.count && this.buildManager.isUnbuildable(gameState, houseTemplateName)) { if (this.Config.debug > 1) - API3.warn("no room to place a house ... try to be less restrictive"); + aiWarn("no room to place a house ... try to be less restrictive"); this.buildManager.setBuildable(houseTemplateName); this.requireHouses = true; } @@ -1472,15 +1482,18 @@ Headquather.prototype.buildMoreHouses = function(gameState, queues) { const houseTemplate = gameState.getTemplate(gameState.applyCiv(houseTemplateString)); if (!this.phasing || gameState.getPhaseEntityRequirements(this.phasing).every(req => - !houseTemplate.hasClass(req.class) || gameState.getOwnStructures().filter(API3.Filters.byClass(req.class)).length >= req.count)) + !houseTemplate.hasClass(req.class) || + gameState.getOwnStructures().filter(filters.byClass(req.class)).length >= req.count)) + { this.requireHouses = undefined; + } } // When population limit too tight // - if no room to build, try to improve with technology // - otherwise increase temporarily the priority of houses const house = gameState.applyCiv(houseTemplateString); - const HouseNb = gameState.getOwnFoundations().filter(API3.Filters.byClass("House")).length; + const HouseNb = gameState.getOwnFoundations().filter(filters.byClass("House")).length; const popBonus = gameState.getTemplate(house).getPopulationBonus(); const freeSlots = gameState.getPopulationLimit() + HouseNb*popBonus - this.getAccountedPopulation(gameState); let priority; @@ -1489,7 +1502,7 @@ Headquather.prototype.buildMoreHouses = function(gameState, queues) if (this.buildManager.isUnbuildable(gameState, house)) { if (this.Config.debug > 1) - API3.warn("no room to place a house ... try to improve with technology"); + aiWarn("no room to place a house ... try to improve with technology"); this.researchManager.researchPopulationBonus(gameState, queues); } else @@ -1517,7 +1530,7 @@ Headquather.prototype.checkBaseExpansion = function(gameState, queues) if (this.buildManager.numberMissingRoom(gameState) > 1) { if (this.Config.debug > 2) - API3.warn("try to build a new base because not enough room to build "); + aiWarn("try to build a new base because not enough room to build "); this.buildNewBase(gameState, queues); return; } @@ -1531,7 +1544,10 @@ Headquather.prototype.checkBaseExpansion = function(gameState, queues) if (numUnits > activeBases * (65 + numvar + (10 + numvar)*(activeBases-1)) || this.saveResources && numUnits > 50) { if (this.Config.debug > 2) - API3.warn("try to build a new base because of population " + numUnits + " for " + activeBases + " CCs"); + { + aiWarn("try to build a new base because of population " + numUnits + " for " + + activeBases + " CCs"); + } this.buildNewBase(gameState, queues); } }; @@ -1540,13 +1556,13 @@ Headquather.prototype.buildNewBase = function(gameState, queues, resource) { if (this.hasPotentialBase() && this.currentPhase == 1 && !gameState.isResearching(gameState.getPhaseName(2))) return false; - if (gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre")).hasEntities() || queues.civilCentre.hasQueuedUnits()) + if (gameState.getOwnFoundations().filter(filters.byClass("CivCentre")).hasEntities() || queues.civilCentre.hasQueuedUnits()) return false; let template; // We require at least one of this civ civCentre as they may allow specific units or techs let hasOwnCC = false; - for (const ent of gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")).values()) + for (const ent of gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")).values()) { if (ent.owner() != PlayerID || ent.templateName() != gameState.applyCiv("structures/{civ}/civil_centre")) continue; @@ -1564,7 +1580,7 @@ Headquather.prototype.buildNewBase = function(gameState, queues, resource) // base "-1" means new base. if (this.Config.debug > 1) - API3.warn("new base " + gameState.applyCiv(template) + " planned with resource " + resource); + aiWarn("new base " + gameState.applyCiv(template) + " planned with resource " + resource); queues.civilCentre.addPlan(new ConstructionPlan(gameState, template, { "base": -1, "resource": resource })); return true; }; @@ -1664,7 +1680,7 @@ Headquather.prototype.constructTrainingBuildings = function(gameState, queues) const numStables = gameState.getOwnEntitiesByClass("Stable", true).length; if (this.getAccountedPopulation(gameState) > this.Config.Military.popForBarracks1 || - this.phasing == 2 && gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length < 5) + this.phasing == 2 && gameState.getOwnStructures().filter(filters.byClass("Village")).length < 5) { // First barracks/range and stable. if (numBarracks + numRanges == 0) @@ -1763,7 +1779,7 @@ Headquather.prototype.constructTrainingBuildings = function(gameState, queues) */ Headquather.prototype.findBestBaseForMilitary = function(gameState) { - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")).toEntityArray(); + const ccEnts = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")).toEntityArray(); let bestBase; let enemyFound = false; let distMin = Math.min(); @@ -1781,7 +1797,7 @@ Headquather.prototype.findBestBaseForMilitary = function(gameState) continue; if (getLandAccess(gameState, cc) != access) continue; - const dist = API3.SquareVectorDistance(cc.position(), cce.position()); + const dist = SquareVectorDistance(cc.position(), cce.position()); if (!enemyFound && isEnemy) enemyFound = true; else if (dist > distMin) @@ -1829,7 +1845,7 @@ Headquather.prototype.trainEmergencyUnits = function(gameState, positions) if (time/1000 > 5) continue; } - const dist = API3.SquareVectorDistance(base.anchor.position(), pos); + const dist = SquareVectorDistance(base.anchor.position(), pos); if (nearestAnchor && dist > distmin) continue; distmin = dist; @@ -1869,7 +1885,7 @@ Headquather.prototype.trainEmergencyUnits = function(gameState, positions) continue; if (autogarrison && !template.hasClasses(garrisonArrowClasses)) continue; - if (!total.canAfford(new API3.Resources(template.cost()))) + if (!total.canAfford(new ResourcesManager(template.cost()))) continue; templateFound = [trainable, template]; if (template.hasClass("Ranged") == rangedWanted) @@ -1882,7 +1898,7 @@ Headquather.prototype.trainEmergencyUnits = function(gameState, positions) // and if not, take some of other accounted resources // TODO sort the queues to be substracted const queueManager = gameState.ai.queueManager; - const cost = new API3.Resources(templateFound[1].cost()); + const cost = new ResourcesManager(templateFound[1].cost()); queueManager.setAccounts(gameState, cost, "emergency"); if (!queueManager.canAfford("emergency", cost)) { @@ -2070,7 +2086,7 @@ Headquather.prototype.isDangerousLocation = function(gameState, pos, radius) Headquather.prototype.isNearInvadingArmy = function(pos) { for (const army of this.defenseManager.armies) - if (army.foePosition && API3.SquareVectorDistance(army.foePosition, pos) < 12000) + if (army.foePosition && SquareVectorDistance(army.foePosition, pos) < 12000) return true; return false; }; @@ -2078,11 +2094,14 @@ Headquather.prototype.isNearInvadingArmy = function(pos) Headquather.prototype.isUnderEnemyFire = function(gameState, pos, radius = 0) { if (!this.turnCache.firingStructures) - this.turnCache.firingStructures = gameState.updatingCollection("diplo-FiringStructures", API3.Filters.hasDefensiveFire(), gameState.getEnemyStructures()); + { + this.turnCache.firingStructures = gameState.updatingCollection("diplo-FiringStructures", + filters.hasDefensiveFire(), gameState.getEnemyStructures()); + } for (const ent of this.turnCache.firingStructures.values()) { const range = radius + ent.attackRange("Ranged").max; - if (API3.SquareVectorDistance(ent.position(), pos) < range*range) + if (SquareVectorDistance(ent.position(), pos) < range*range) return true; } return false; @@ -2221,14 +2240,15 @@ Headquather.prototype.update = function(gameState, queues, events) this.emergencyManager.update(gameState); this.turnCache = {}; this.territoryMap = createTerritoryMap(gameState); - this.canBarter = gameState.getOwnEntitiesByClass("Market", true).filter(API3.Filters.isBuilt()).hasEntities(); + this.canBarter = gameState.getOwnEntitiesByClass("Market", true).filter(filters.isBuilt()) + .hasEntities(); // TODO find a better way to update if (this.currentPhase != gameState.currentPhase()) { if (this.Config.debug > 0) - API3.warn(" civ " + gameState.getPlayerCiv() + " has phasedUp from " + this.currentPhase + - " to " + gameState.currentPhase() + " at time " + gameState.ai.elapsedTime + - " phasing " + this.phasing); + aiWarn(" civ " + gameState.getPlayerCiv() + " has phasedUp from " + this.currentPhase + + " to " + gameState.currentPhase() + " at time " + gameState.ai.elapsedTime + + " phasing " + this.phasing); this.currentPhase = gameState.currentPhase(); // In principle, this.phasing should be already reset to 0 when starting the research @@ -2354,19 +2374,19 @@ Headquather.prototype.Serialize = function() if (this.Config.debug == -100) { - API3.warn(" HQ serialization ---------------------"); - API3.warn(" properties " + uneval(properties)); - API3.warn(" basesManager " + uneval(this.basesManager.Serialize())); - API3.warn(" attackManager " + uneval(this.attackManager.Serialize())); - API3.warn(" buildManager " + uneval(this.buildManager.Serialize())); - API3.warn(" defenseManager " + uneval(this.defenseManager.Serialize())); - API3.warn(" tradeManager " + uneval(this.tradeManager.Serialize())); - API3.warn(" navalManager " + uneval(this.navalManager.Serialize())); - API3.warn(" researchManager " + uneval(this.researchManager.Serialize())); - API3.warn(" diplomacyManager " + uneval(this.diplomacyManager.Serialize())); - API3.warn(" garrisonManager " + uneval(this.garrisonManager.Serialize())); - API3.warn(" victoryManager " + uneval(this.victoryManager.Serialize())); - API3.warn(" emergencyManager " + uneval(this.emergencyManager.Serialize())); + aiWarn(" HQ serialization ---------------------"); + aiWarn(" properties " + uneval(properties)); + aiWarn(" basesManager " + uneval(this.basesManager.Serialize())); + aiWarn(" attackManager " + uneval(this.attackManager.Serialize())); + aiWarn(" buildManager " + uneval(this.buildManager.Serialize())); + aiWarn(" defenseManager " + uneval(this.defenseManager.Serialize())); + aiWarn(" tradeManager " + uneval(this.tradeManager.Serialize())); + aiWarn(" navalManager " + uneval(this.navalManager.Serialize())); + aiWarn(" researchManager " + uneval(this.researchManager.Serialize())); + aiWarn(" diplomacyManager " + uneval(this.diplomacyManager.Serialize())); + aiWarn(" garrisonManager " + uneval(this.garrisonManager.Serialize())); + aiWarn(" victoryManager " + uneval(this.victoryManager.Serialize())); + aiWarn(" emergencyManager " + uneval(this.emergencyManager.Serialize())); } return { diff --git a/binaries/data/mods/public/simulation/ai/petra/mapModule.js b/binaries/data/mods/public/simulation/ai/petra/mapModule.js index 25b9626b3e..0522f06c8b 100644 --- a/binaries/data/mods/public/simulation/ai/petra/mapModule.js +++ b/binaries/data/mods/public/simulation/ai/petra/mapModule.js @@ -1,3 +1,7 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { InfoMap } from "simulation/ai/common-api/map-module.js"; +import { getMapIndices } from "simulation/ai/common-api/utils.js"; + /** map functions */ /* eslint-disable prefer-const -- Mods should be able to change them */ @@ -81,7 +85,7 @@ export function createObstructionMap(gameState, accessIndex, template) } } - const map = new API3.Map(gameState.sharedScript, "passability", obstructionTiles); + const map = new InfoMap(gameState.sharedScript, "passability", obstructionTiles); map.setMaxVal(255); if (template && template.buildDistance()) @@ -96,7 +100,7 @@ export function createObstructionMap(gameState, accessIndex, template) const fromClass = distance.FromClass; const cellSize = passabilityMap.cellSize; const cellDist = 1 + minDist / cellSize; - const structures = gameState.getOwnStructures().filter(API3.Filters.byClass(fromClass)); + const structures = gameState.getOwnStructures().filter(filters.byClass(fromClass)); for (const ent of structures.values()) { if (!ent.position()) @@ -117,7 +121,7 @@ export function createTerritoryMap(gameState) { const map = gameState.ai.territoryMap; - const ret = new API3.Map(gameState.sharedScript, "territory", map.data); + const ret = new InfoMap(gameState.sharedScript, "territory", map.data); ret.getOwner = function(p) { return this.point(p) & TERRITORY_PLAYER_MASK; }; ret.getOwnerIndex = function(p) { return this.map[p] & TERRITORY_PLAYER_MASK; }; ret.isBlinking = function(p) { return (this.point(p) & TERRITORY_BLINKING_MASK) != 0; }; @@ -145,7 +149,7 @@ export let fullFrontier_Mask = narrowFrontier_Mask | largeFrontier_Mask; export function createBorderMap(gameState) { - const map = new API3.Map(gameState.sharedScript, "territory"); + const map = new InfoMap(gameState.sharedScript, "territory"); const width = map.width; const border = Math.round(80 / map.cellSize); const passabilityMap = gameState.getPassabilityMap(); @@ -162,7 +166,7 @@ export function createBorderMap(gameState) if (radius < radcut) continue; map.map[j] = outside_Mask; - const ind = API3.getMapIndices(j, map, passabilityMap); + const ind = getMapIndices(j, map, passabilityMap); for (const k of ind) { if (passabilityMap.data[k] & obstructionMask) @@ -182,7 +186,7 @@ export function createBorderMap(gameState) if (ix < border || ix >= borderCut || iy < border || iy >= borderCut) { map.map[j] = outside_Mask; - const ind = API3.getMapIndices(j, map, passabilityMap); + const ind = getMapIndices(j, map, passabilityMap); for (const k of ind) { if (passabilityMap.data[k] & obstructionMask) diff --git a/binaries/data/mods/public/simulation/ai/petra/navalManager.js b/binaries/data/mods/public/simulation/ai/petra/navalManager.js index cbcfcf187e..25677c4b46 100644 --- a/binaries/data/mods/public/simulation/ai/petra/navalManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/navalManager.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { gatherTreasure, getLandAccess, getSeaAccess, isSiegeUnit, setSeaAccess } from "simulation/ai/petra/entityExtend.js"; import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js"; @@ -42,15 +44,16 @@ export function NavalManager(Config) NavalManager.prototype.init = function(gameState, deserializing) { // docks - this.docks = gameState.getOwnStructures().filter(API3.Filters.byClasses(["Dock", "Shipyard"])); + this.docks = gameState.getOwnStructures().filter(filters.byClasses(["Dock", "Shipyard"])); this.docks.registerUpdates(); - this.ships = gameState.getOwnUnits().filter(API3.Filters.and(API3.Filters.byClass("Ship"), - API3.Filters.not(API3.Filters.byMetadata(PlayerID, "role", Worker.ROLE_TRADER)))); + this.ships = gameState.getOwnUnits().filter(filters.and(filters.byClass("Ship"), + filters.not(filters.byMetadata(PlayerID, "role", Worker.ROLE_TRADER)))); // note: those two can overlap (some transport ships are warships too and vice-versa). - this.transportShips = this.ships.filter(API3.Filters.and(API3.Filters.byCanGarrison(), API3.Filters.not(API3.Filters.byClass("FishingBoat")))); - this.warShips = this.ships.filter(API3.Filters.byClass("Warship")); - this.fishShips = this.ships.filter(API3.Filters.byClass("FishingBoat")); + this.transportShips = this.ships.filter(filters.and(filters.byCanGarrison(), + filters.not(filters.byClass("FishingBoat")))); + this.warShips = this.ships.filter(filters.byClass("Warship")); + this.fishShips = this.ships.filter(filters.byClass("FishingBoat")); this.ships.registerUpdates(); this.transportShips.registerUpdates(); @@ -84,16 +87,16 @@ NavalManager.prototype.init = function(gameState, deserializing) } else { - let collec = this.ships.filter(API3.Filters.byMetadata(PlayerID, "sea", i)); + let collec = this.ships.filter(filters.byMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaShips.push(collec); - collec = this.transportShips.filter(API3.Filters.byMetadata(PlayerID, "sea", i)); + collec = this.transportShips.filter(filters.byMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaTransportShips.push(collec); - collec = this.warShips.filter(API3.Filters.byMetadata(PlayerID, "sea", i)); + collec = this.warShips.filter(filters.byMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaWarShips.push(collec); - collec = this.fishShips.filter(API3.Filters.byMetadata(PlayerID, "sea", i)); + collec = this.fishShips.filter(filters.byMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaFishShips.push(collec); this.wantedTransportShips.push(0); @@ -292,7 +295,10 @@ NavalManager.prototype.checkEvents = function(gameState, queues, events) const shipId = evt.entityObj.id(); if (this.Config.debug > 1) - API3.warn("one ship " + shipId + " from plan " + plan.ID + " destroyed during " + plan.state); + { + aiWarn("one ship " + shipId + " from plan " + plan.ID + " destroyed during " + + plan.state); + } if (plan.state === TransportPlan.BOARDING) { // just reset the units onBoard metadata and wait for a new ship to be assigned to this plan @@ -362,7 +368,10 @@ NavalManager.prototype.requireTransport = function(gameState, ent, startIndex, e if (ent.getMetadata(PlayerID, "transport") !== undefined) { if (this.Config.debug > 0) - API3.warn("Petra naval manager error: unit " + ent.id() + " has already required a transport"); + { + aiWarn("Petra naval manager error: unit " + ent.id() + + " has already required a transport"); + } return false; } @@ -391,7 +400,7 @@ NavalManager.prototype.requireTransport = function(gameState, ent, startIndex, e if (plan.failed) { if (this.Config.debug > 1) - API3.warn(">>>> transport plan aborted <<<<"); + aiWarn(">>>> transport plan aborted <<<<"); return false; } plan.init(gameState); @@ -403,12 +412,12 @@ NavalManager.prototype.requireTransport = function(gameState, ent, startIndex, e NavalManager.prototype.splitTransport = function(gameState, plan) { if (this.Config.debug > 1) - API3.warn(">>>> split of transport plan started <<<<"); + aiWarn(">>>> split of transport plan started <<<<"); const newplan = new TransportPlan(gameState, [], plan.startIndex, plan.endIndex, plan.endPos); if (newplan.failed) { if (this.Config.debug > 1) - API3.warn(">>>> split of transport plan aborted <<<<"); + aiWarn(">>>> split of transport plan aborted <<<<"); return false; } newplan.init(gameState); @@ -499,7 +508,7 @@ NavalManager.prototype.maintainFleet = function(gameState, queues) { if (queues.ships.hasQueuedUnits()) return; - if (!this.docks.filter(API3.Filters.isBuilt()).hasEntities()) + if (!this.docks.filter(filters.isBuilt()).hasEntities()) return; // check if we have enough transport ships per region. for (let sea = 0; sea < this.seaShips.length; ++sea) @@ -549,7 +558,7 @@ NavalManager.prototype.isShipBoarding = function(ship) const plan = this.getPlan(ship.getMetadata(PlayerID, "transporter")); if (!plan || !plan.boardingPos[ship.id()]) return false; - return API3.SquareVectorDistance(plan.boardingPos[ship.id()], ship.position()) < plan.boardingRange; + return SquareVectorDistance(plan.boardingPos[ship.id()], ship.position()) < plan.boardingRange; }; /** let blocking ships move apart from active ships (waiting for a better pathfinder) @@ -607,11 +616,12 @@ NavalManager.prototype.moveApart = function(gameState) continue; // Do not stay idle near a dock to not disturb other ships const sea = ship.getMetadata(PlayerID, "sea"); - for (const dock of gameState.getAllyStructures().filter(API3.Filters.byClass("Dock")).values()) + for (const dock of + gameState.getAllyStructures().filter(filters.byClass("Dock")).values()) { if (getSeaAccess(gameState, dock) != sea) continue; - if (API3.SquareVectorDistance(shipPosition, dock.position()) > 4900) + if (SquareVectorDistance(shipPosition, dock.position()) > 4900) continue; ship.moveToRange(dock.position()[0], dock.position()[1], 70, 75); } @@ -619,7 +629,7 @@ NavalManager.prototype.moveApart = function(gameState) } } - for (const ship of gameState.ai.HQ.tradeManager.traders.filter(API3.Filters.byClass("Ship")).values()) + for (const ship of gameState.ai.HQ.tradeManager.traders.filter(filters.byClass("Ship")).values()) { const shipPosition = ship.position(); if (!shipPosition) @@ -664,11 +674,11 @@ NavalManager.prototype.moveApart = function(gameState) continue; // Do not stay idle near a dock to not disturb other ships const sea = ship.getMetadata(PlayerID, "sea"); - for (const dock of gameState.getAllyStructures().filter(API3.Filters.byClass("Dock")).values()) + for (const dock of gameState.getAllyStructures().filter(filters.byClass("Dock")).values()) { if (getSeaAccess(gameState, dock) != sea) continue; - if (API3.SquareVectorDistance(shipPosition, dock.position()) > 4900) + if (SquareVectorDistance(shipPosition, dock.position()) > 4900) continue; ship.moveToRange(dock.position()[0], dock.position()[1], 70, 75); } @@ -683,7 +693,7 @@ NavalManager.prototype.moveApart = function(gameState) { if (blockedIds.indexOf(blockingShip.id()) != -1 || !blockingShip.position()) continue; - const distSquare = API3.SquareVectorDistance(shipPosition, blockingShip.position()); + const distSquare = SquareVectorDistance(shipPosition, blockingShip.position()); const unitAIState = blockingShip.unitAIState(); if (blockingShip.getMetadata(PlayerID, "transporter") === undefined && unitAIState != "INDIVIDUAL.GATHER.APPROACHING" && @@ -696,7 +706,8 @@ NavalManager.prototype.moveApart = function(gameState) blockingShip.moveToRange(shipPosition[0], shipPosition[1], 30, 35); } - for (const blockingShip of gameState.ai.HQ.tradeManager.traders.filter(API3.Filters.byClass("Ship")).values()) + for (const blockingShip of + gameState.ai.HQ.tradeManager.traders.filter(filters.byClass("Ship")).values()) { if (blockingShip.getMetadata(PlayerID, "sea") != sea) continue; @@ -705,7 +716,7 @@ NavalManager.prototype.moveApart = function(gameState) const role = blockingShip.getMetadata(PlayerID, "role"); if (role === undefined || role !== Worker.ROLE_TRADER) // already accounted before continue; - const distSquare = API3.SquareVectorDistance(shipPosition, blockingShip.position()); + const distSquare = SquareVectorDistance(shipPosition, blockingShip.position()); const unitAIState = blockingShip.unitAIState(); if (unitAIState != "INDIVIDUAL.TRADE.APPROACHINGMARKET") { @@ -726,7 +737,8 @@ NavalManager.prototype.buildNavalStructures = function(gameState, queues) if (gameState.ai.HQ.getAccountedPopulation(gameState) > this.Config.Economy.popForDock) { if (queues.dock.countQueuedUnitsWithClass("Dock") === 0 && - !gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("Dock"), API3.Filters.isFoundation())).hasEntities() && + !gameState.getOwnStructures().filter(filters.and(filters.byClass("Dock"), + filters.isFoundation())).hasEntities() && gameState.ai.HQ.canBuild(gameState, "structures/{civ}/dock")) { let dockStarted = false; @@ -755,9 +767,11 @@ NavalManager.prototype.buildNavalStructures = function(gameState, queues) if (gameState.currentPhase() < 2 || gameState.ai.HQ.getAccountedPopulation(gameState) < this.Config.Economy.popPhase2 + 15 || queues.militaryBuilding.hasQueuedUnits()) return; - if (!this.docks.filter(API3.Filters.byClass("Dock")).hasEntities() || - this.docks.filter(API3.Filters.byClass("Shipyard")).hasEntities()) + if (!this.docks.filter(filters.byClass("Dock")).hasEntities() || + this.docks.filter(filters.byClass("Shipyard")).hasEntities()) + { return; + } // Use in priority resources to build a Market. if (!gameState.getOwnEntitiesByClass("Market", true).hasEntities() && gameState.ai.HQ.canBuild(gameState, "structures/{civ}/market")) @@ -783,17 +797,18 @@ NavalManager.prototype.getBestShip = function(gameState, sea, goal) { const civ = gameState.getPlayerCiv(); const trainableShips = []; - gameState.getOwnTrainingFacilities().filter(API3.Filters.byMetadata(PlayerID, "sea", sea)).forEach(function(ent) { - const trainables = ent.trainableEntities(civ); - for (const trainable of trainables) - { - if (gameState.isTemplateDisabled(trainable)) - continue; - const template = gameState.getTemplate(trainable); - if (template && template.hasClass("Ship") && trainableShips.indexOf(trainable) === -1) - trainableShips.push(trainable); - } - }); + gameState.getOwnTrainingFacilities().filter(filters.byMetadata(PlayerID, "sea", sea)).forEach( + function(ent) { + const trainables = ent.trainableEntities(civ); + for (const trainable of trainables) + { + if (gameState.isTemplateDisabled(trainable)) + continue; + const template = gameState.getTemplate(trainable); + if (template && template.hasClass("Ship") && trainableShips.indexOf(trainable) === -1) + trainableShips.push(trainable); + } + }); let best = 0; let bestShip; @@ -847,7 +862,7 @@ NavalManager.prototype.update = function(gameState, queues, events) if (remaining) continue; if (this.Config.debug > 1) - API3.warn("no more units on transport plan " + this.transportPlans[i].ID); + aiWarn("no more units on transport plan " + this.transportPlans[i].ID); this.transportPlans[i].releaseAll(); this.transportPlans.splice(i--, 1); } diff --git a/binaries/data/mods/public/simulation/ai/petra/queue.js b/binaries/data/mods/public/simulation/ai/petra/queue.js index cb3d34779c..f274aab70e 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queue.js +++ b/binaries/data/mods/public/simulation/ai/petra/queue.js @@ -1,3 +1,5 @@ +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { ConstructionPlan } from "simulation/ai/petra/queueplanBuilding.js"; import { ResearchPlan } from "simulation/ai/petra/queueplanResearch.js"; import { TrainingPlan } from "simulation/ai/petra/queueplanTraining.js"; @@ -69,7 +71,7 @@ Queue.prototype.startNext = function(gameState) */ Queue.prototype.maxAccountWanted = function(gameState, fraction) { - const cost = new API3.Resources(); + const cost = new ResourcesManager(); if (this.plans.length > 0 && this.plans[0].isGo(gameState)) cost.add(this.plans[0].getCost()); if (this.plans.length > 1 && this.plans[1].isGo(gameState) && fraction > 0) @@ -83,7 +85,7 @@ Queue.prototype.maxAccountWanted = function(gameState, fraction) Queue.prototype.queueCost = function() { - const cost = new API3.Resources(); + const cost = new ResourcesManager(); for (const plan of this.plans) cost.add(plan.getCost()); return cost; @@ -155,7 +157,7 @@ Queue.prototype.Deserialize = function(gameState, data) plan = new ResearchPlan(gameState, dataPlan.type); else { - API3.warn("Petra deserialization error: plan unknown " + uneval(dataPlan)); + aiWarn("Petra deserialization error: plan unknown " + uneval(dataPlan)); continue; } plan.Deserialize(gameState, dataPlan); diff --git a/binaries/data/mods/public/simulation/ai/petra/queueManager.js b/binaries/data/mods/public/simulation/ai/petra/queueManager.js index b543a5ee29..9a2eaed53a 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueManager.js @@ -1,3 +1,6 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { Queue } from "simulation/ai/petra/queue.js"; import { Worker } from "simulation/ai/petra/worker.js"; @@ -35,7 +38,7 @@ export function QueueManager(Config, queues) this.queueArrays = []; for (const q in this.queues) { - this.accounts[q] = new API3.Resources(); + this.accounts[q] = new ResourcesManager(); this.queueArrays.push([q, this.queues[q]]); } const priorities = this.priorities; @@ -52,7 +55,7 @@ QueueManager.prototype.getAvailableResources = function(gameState) QueueManager.prototype.getTotalAccountedResources = function() { - const resources = new API3.Resources(); + const resources = new ResourcesManager(); for (const key in this.queues) resources.add(this.accounts[key]); return resources; @@ -60,7 +63,7 @@ QueueManager.prototype.getTotalAccountedResources = function() QueueManager.prototype.currentNeeds = function(gameState) { - const needed = new API3.Resources(); + const needed = new ResourcesManager(); // queueArrays because it's faster. for (const q of this.queueArrays) { @@ -167,14 +170,16 @@ QueueManager.prototype.printQueues = function(gameState) numWorkers++; } }); - API3.warn("---------- QUEUES ------------ with pop " + gameState.getPopulation() + " and workers " + numWorkers); + aiWarn("---------- QUEUES ------------ with pop " + gameState.getPopulation() + " and workers " + + numWorkers); for (const i in this.queues) { const q = this.queues[i]; if (q.hasQueuedUnits()) { - API3.warn(i + ": ( with priority " + this.priorities[i] +" and accounts " + uneval(this.accounts[i]) +")"); - API3.warn(" while maxAccountWanted(0.6) is " + uneval(q.maxAccountWanted(gameState, 0.6))); + aiWarn(i + ": ( with priority " + this.priorities[i] +" and accounts " + + uneval(this.accounts[i]) +")"); + aiWarn(" while maxAccountWanted(0.6) is " + uneval(q.maxAccountWanted(gameState, 0.6))); } for (const plan of q.plans) { @@ -182,18 +187,18 @@ QueueManager.prototype.printQueues = function(gameState) if (plan.number) qStr += "x" + plan.number; qStr += " isGo " + plan.isGo(gameState); - API3.warn(qStr); + aiWarn(qStr); } } - API3.warn("Accounts"); + aiWarn("Accounts"); for (const p in this.accounts) - API3.warn(p + ": " + uneval(this.accounts[p])); - API3.warn("Current Resources: " + uneval(gameState.getResources())); - API3.warn("Available Resources: " + uneval(this.getAvailableResources(gameState))); - API3.warn("Wanted Gather Rates: " + uneval(gameState.ai.HQ.GetWantedGatherRates(gameState))); - API3.warn("Current Gather Rates: " + uneval(gameState.ai.HQ.GetCurrentGatherRates(gameState))); - API3.warn("Most needed resources: " + uneval(gameState.ai.HQ.pickMostNeededResources(gameState))); - API3.warn("------------------------------------"); + aiWarn(p + ": " + uneval(this.accounts[p])); + aiWarn("Current Resources: " + uneval(gameState.getResources())); + aiWarn("Available Resources: " + uneval(this.getAvailableResources(gameState))); + aiWarn("Wanted Gather Rates: " + uneval(gameState.ai.HQ.GetWantedGatherRates(gameState))); + aiWarn("Current Gather Rates: " + uneval(gameState.ai.HQ.GetCurrentGatherRates(gameState))); + aiWarn("Most needed resources: " + uneval(gameState.ai.HQ.pickMostNeededResources(gameState))); + aiWarn("------------------------------------"); }; QueueManager.prototype.clear = function() @@ -319,7 +324,7 @@ QueueManager.prototype.distributeResources = function(gameState) } } if (available < 0) - API3.warn("Petra: problem with remaining " + res + " in queueManager " + available); + aiWarn("Petra: problem with remaining " + res + " in queueManager " + available); } }; @@ -353,7 +358,10 @@ QueueManager.prototype.switchResource = function(gameState, res) this.accounts[i][res] -= diff; ++otherQueue.switched; if (this.Config.debug > 2) - API3.warn ("switching queue " + res + " from " + i + " to " + j + " in amount " + diff); + { + aiWarn("switching queue " + res + " from " + i + " to " + j + " in amount " + + diff); + } break; } } @@ -398,7 +406,8 @@ QueueManager.prototype.update = function(gameState) this.queues[i].check(gameState); // do basic sanity checks on the queue if (this.priorities[i] > 0) continue; - API3.warn("QueueManager received bad priorities, please report this error: " + uneval(this.priorities)); + aiWarn("QueueManager received bad priorities, please report this error: " + + uneval(this.priorities)); this.priorities[i] = 1; // TODO: make the Queue Manager not die when priorities are zero. } @@ -440,17 +449,26 @@ QueueManager.prototype.checkPausedQueues = function(gameState) if (toBePaused) { if (q == "field" && gameState.ai.HQ.needFarm && - !gameState.getOwnStructures().filter(API3.Filters.byClass("Field")).hasEntities()) + !gameState.getOwnStructures().filter(filters.byClass("Field")).hasEntities()) + { toBePaused = false; + } if (q == "corral" && gameState.ai.HQ.needCorral && - !gameState.getOwnStructures().filter(API3.Filters.byClass("Field")).hasEntities()) + !gameState.getOwnStructures().filter(filters.byClass("Field")).hasEntities()) + { toBePaused = false; + } if (q == "dock" && gameState.ai.HQ.needFish && - !gameState.getOwnStructures().filter(API3.Filters.byClass("Dock")).hasEntities()) + !gameState.getOwnStructures().filter(filters.byClass("Dock")).hasEntities()) + { toBePaused = false; + } if (q == "ships" && gameState.ai.HQ.needFish && - !gameState.ai.HQ.navalManager.ships.filter(API3.Filters.byClass("FishingBoat")).hasEntities()) + !gameState.ai.HQ.navalManager.ships.filter(filters.byClass("FishingBoat")) + .hasEntities()) + { toBePaused = false; + } } const queue = this.queues[q]; @@ -521,7 +539,7 @@ QueueManager.prototype.addQueue = function(queueName, priority) this.queues[queueName] = new Queue(); this.priorities[queueName] = priority; - this.accounts[queueName] = new API3.Resources(); + this.accounts[queueName] = new ResourcesManager(); this.queueArrays = []; for (const q in this.queues) @@ -554,7 +572,10 @@ QueueManager.prototype.getPriority = function(queueName) QueueManager.prototype.changePriority = function(queueName, newPriority) { if (this.Config.debug > 1) - API3.warn(">>> Priority of queue " + queueName + " changed from " + this.priorities[queueName] + " to " + newPriority); + { + aiWarn(">>> Priority of queue " + queueName + " changed from " + this.priorities[queueName] + + " to " + newPriority); + } if (this.queues[queueName] !== undefined) this.priorities[queueName] = newPriority; const priorities = this.priorities; @@ -570,8 +591,10 @@ QueueManager.prototype.Serialize = function() queues[q] = this.queues[q].Serialize(); accounts[q] = this.accounts[q].Serialize(); if (this.Config.debug == -100) - API3.warn("queueManager serialization: queue " + q + " >>> " + - uneval(queues[q]) + " with accounts " + uneval(accounts[q])); + { + aiWarn("queueManager serialization: queue " + q + " >>> " + uneval(queues[q]) + + " with accounts " + uneval(accounts[q])); + } } return { @@ -593,7 +616,7 @@ QueueManager.prototype.Deserialize = function(gameState, data) { this.queues[q] = new Queue(); this.queues[q].Deserialize(gameState, data.queues[q]); - this.accounts[q] = new API3.Resources(); + this.accounts[q] = new ResourcesManager(); this.accounts[q].Deserialize(data.accounts[q]); this.queueArrays.push([q, this.queues[q]]); } diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplan.js b/binaries/data/mods/public/simulation/ai/petra/queueplan.js index c63aa4e428..510a98e09f 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplan.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplan.js @@ -1,3 +1,6 @@ +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; + /** * Common functions and variables to all queue plans. */ @@ -10,11 +13,11 @@ export function QueuePlan(gameState, type, metadata) this.template = gameState.getTemplate(this.type); if (!this.template) { - API3.warn("Tried to add the inexisting template " + this.type + " to Petra."); + aiWarn("Tried to add the inexisting template " + this.type + " to Petra."); return false; } this.ID = gameState.ai.uniqueIDs.plans++; - this.cost = new API3.Resources(this.template.cost()); + this.cost = new ResourcesManager(this.template.cost()); this.number = 1; this.category = ""; @@ -47,7 +50,7 @@ QueuePlan.prototype.start = function(gameState) QueuePlan.prototype.getCost = function() { - const costs = new API3.Resources(); + const costs = new ResourcesManager(); costs.add(this.cost); if (this.number !== 1) costs.multiply(this.number); diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js b/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js index 719663b108..97014507ce 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js @@ -1,3 +1,7 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { InfoMap } from "simulation/ai/common-api/map-module.js"; +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { SquareVectorDistance, VectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { getBuiltEntity, getLandAccess, getSeaAccess } from "simulation/ai/petra/entityExtend.js"; import { border_Mask, createObstructionMap, fullBorder_Mask, outside_Mask } from "simulation/ai/petra/mapModule.js"; @@ -46,7 +50,7 @@ ConstructionPlan.prototype.start = function(gameState) const builder = gameState.findBuilder(this.type); if (!builder) { - API3.warn("petra error: builder not found when starting construction."); + aiWarn("petra error: builder not found when starting construction."); Engine.ProfileStop(); return; } @@ -152,8 +156,11 @@ ConstructionPlan.prototype.findGoodPosition = function(gameState) if (gameState.ai.HQ.canBuild(gameState, templateName) && !gameState.isTemplateDisabled(templateName)) { template = gameState.getTemplate(templateName); - if (template && gameState.getResources().canAfford(new API3.Resources(template.cost()))) + if (template && gameState.getResources().canAfford( + new ResourcesManager(template.cost()))) + { this.buildOverseaDock(gameState, template); + } } return false; } @@ -183,7 +190,7 @@ ConstructionPlan.prototype.findGoodPosition = function(gameState) // Compute each tile's closeness to friendly structures: - const placement = new API3.Map(gameState.sharedScript, "territory"); + const placement = new InfoMap(gameState.sharedScript, "territory"); const cellSize = placement.cellSize; // size of each tile let alreadyHasHouses = false; @@ -416,8 +423,9 @@ ConstructionPlan.prototype.findDockPosition = function(gameState) // water is a measure of the water space around, and maxWater is the max value that can be returned by checkDockPlacement const maxRes = 10; const maxWater = 16; - const ccEnts = oversea ? gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")) : null; - const docks = oversea ? gameState.getOwnStructures().filter(API3.Filters.byClass("Dock")) : null; + const ccEnts = oversea ? gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")) : + null; + const docks = oversea ? gameState.getOwnStructures().filter(filters.byClass("Dock")) : null; // Normalisation factors (only guessed, no attempt to optimize them) const factor = proxyAccess ? 1 : oversea ? 0.2 : 40; for (let j = 0; j < territoryMap.length; ++j) @@ -444,7 +452,7 @@ ConstructionPlan.prototype.findDockPosition = function(gameState) // If proximity is given, we look for the nearest point if (proxyAccess) - score = API3.VectorDistance(this.metadata.proximity, pos); + score = VectorDistance(this.metadata.proximity, pos); // Bonus for resources score += 20 * (maxRes - res); @@ -459,7 +467,7 @@ ConstructionPlan.prototype.findDockPosition = function(gameState) const owner = cc.owner(); if (owner != PlayerID && !gameState.isPlayerEnemy(owner)) continue; - const dist = API3.SquareVectorDistance(pos, cc.position()); + const dist = SquareVectorDistance(pos, cc.position()); if (owner == PlayerID && (!ownDist || dist < ownDist)) ownDist = dist; if (gameState.isPlayerEnemy(owner) && (!enemyDist || dist < enemyDist)) @@ -474,7 +482,7 @@ ConstructionPlan.prototype.findDockPosition = function(gameState) { if (getSeaAccess(gameState, dock) != navalPassMap[i]) continue; - const dist = API3.SquareVectorDistance(pos, dock.position()); + const dist = SquareVectorDistance(pos, dock.position()); if (dist > dockDist) dockDist = dist; } @@ -541,13 +549,13 @@ ConstructionPlan.prototype.findDockPosition = function(gameState) */ ConstructionPlan.prototype.buildOverseaDock = function(gameState, template) { - const docks = gameState.getOwnStructures().filter(API3.Filters.byClass("Dock")); + const docks = gameState.getOwnStructures().filter(filters.byClass("Dock")); if (!docks.hasEntities()) return; const passabilityMap = gameState.getPassabilityMap(); const cellArea = passabilityMap.cellSize * passabilityMap.cellSize; - const ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); + const ccEnts = gameState.updatingGlobalCollection("allCCs", filters.byClass("CivCentre")); const land = {}; let found; @@ -591,7 +599,7 @@ ConstructionPlan.prototype.buildOverseaDock = function(gameState, template) if (!found) return; if (!gameState.ai.HQ.navalMap) - API3.warn("petra.findOverseaLand on a non-naval map??? we should never go there "); + aiWarn("petra.findOverseaLand on a non-naval map??? we should never go there "); const oldTemplate = this.template; const oldMetadata = this.metadata; @@ -944,6 +952,6 @@ ConstructionPlan.prototype.Deserialize = function(gameState, data) for (const key in data) this[key] = data[key]; - this.cost = new API3.Resources(); + this.cost = new ResourcesManager(); this.cost.Deserialize(data.cost); }; diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js b/binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js index 0079aa9d87..51385dbb6a 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplanResearch.js @@ -1,3 +1,4 @@ +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; import { QueuePlan } from "simulation/ai/petra/queueplan.js"; export function ResearchPlan(gameState, type, rush = false) @@ -11,7 +12,7 @@ export function ResearchPlan(gameState, type, rush = false) // Refine the estimated cost const researchers = this.getBestResearchers(gameState, true); if (researchers) - this.cost = new API3.Resources(this.template.cost(researchers[0])); + this.cost = new ResourcesManager(this.template.cost(researchers[0])); this.category = "technology"; this.rush = rush; @@ -26,7 +27,7 @@ ResearchPlan.prototype.canStart = function(gameState) this.researchers = this.getBestResearchers(gameState); if (!this.researchers) return false; - this.cost = new API3.Resources(this.template.cost(this.researchers[0])); + this.cost = new ResourcesManager(this.template.cost(this.researchers[0])); return true; }; @@ -104,6 +105,6 @@ ResearchPlan.prototype.Deserialize = function(gameState, data) for (const key in data) this[key] = data[key]; - this.cost = new API3.Resources(); + this.cost = new ResourcesManager(); this.cost.Deserialize(data.cost); }; diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js b/binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js index 0c26a88f50..d617f2a846 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplanTraining.js @@ -1,3 +1,6 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { QueuePlan } from "simulation/ai/petra/queueplan.js"; import { Worker } from "simulation/ai/petra/worker.js"; @@ -5,14 +8,14 @@ export function TrainingPlan(gameState, type, metadata, number = 1, maxMerge = 5 { if (!QueuePlan.call(this, gameState, type, metadata)) { - API3.warn(" Plan training " + type + " canceled"); + aiWarn(" Plan training " + type + " canceled"); return false; } // Refine the estimated cost and add pop cost const trainers = this.getBestTrainers(gameState); const trainer = trainers ? trainers[0] : undefined; - this.cost = new API3.Resources(this.template.cost(trainer), +this.template._template.Cost.Population); + this.cost = new ResourcesManager(this.template.cost(trainer), +this.template._template.Cost.Population); this.category = "unit"; this.number = number; @@ -28,7 +31,7 @@ TrainingPlan.prototype.canStart = function(gameState) this.trainers = this.getBestTrainers(gameState); if (!this.trainers) return false; - this.cost = new API3.Resources(this.template.cost(this.trainers[0]), +this.template._template.Cost.Population); + this.cost = new ResourcesManager(this.template.cost(this.trainers[0]), +this.template._template.Cost.Population); return true; }; @@ -43,9 +46,9 @@ TrainingPlan.prototype.getBestTrainers = function(gameState) let allTrainers = gameState.findTrainers(this.type); if (this.metadata && this.metadata.sea) - allTrainers = allTrainers.filter(API3.Filters.byMetadata(PlayerID, "sea", this.metadata.sea)); + allTrainers = allTrainers.filter(filters.byMetadata(PlayerID, "sea", this.metadata.sea)); if (this.metadata && this.metadata.base) - allTrainers = allTrainers.filter(API3.Filters.byMetadata(PlayerID, "base", this.metadata.base)); + allTrainers = allTrainers.filter(filters.byMetadata(PlayerID, "base", this.metadata.base)); if (!allTrainers || !allTrainers.hasEntities()) return undefined; @@ -156,6 +159,6 @@ TrainingPlan.prototype.Deserialize = function(gameState, data) for (const key in data) this[key] = data[key]; - this.cost = new API3.Resources(); + this.cost = new ResourcesManager(); this.cost.Deserialize(data.cost); }; diff --git a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js index 0e1ebbdcf9..d67fc858eb 100644 --- a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js +++ b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js @@ -1,3 +1,6 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { ResourcesManager } from "simulation/ai/common-api/resources.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { Config, DIFFICULTY_SANDBOX } from "simulation/ai/petra/config.js"; import { gatherTreasure, getLandAccess, isFastMoving } from "simulation/ai/petra/entityExtend.js"; import { Headquather } from "simulation/ai/petra/headquarters.js"; @@ -35,13 +38,13 @@ Headquather.prototype.gameAnalysis = function(gameState) this.canExpand = this.Config.difficulty != DIFFICULTY_SANDBOX; // If no base yet, check if we can construct one. If not, dispatch our units to possible tasks/attacks this.canBuildUnits = true; - if (!gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).hasEntities()) + if (!gameState.getOwnStructures().filter(filters.byClass("CivCentre")).hasEntities()) { const template = gameState.applyCiv("structures/{civ}/civil_centre"); if (!gameState.isTemplateAvailable(template) || !gameState.getTemplate(template).available(gameState)) { if (this.Config.debug > 1) - API3.warn(" this AI is unable to produce any units"); + aiWarn(" this AI is unable to produce any units"); this.canBuildUnits = false; this.dispatchUnits(gameState); } @@ -70,7 +73,10 @@ Headquather.prototype.assignStartingEntities = function(gameState) { // TODO should support recursive garrisoning. Make a warning for now if (ent.isGarrisonHolder() && ent.garrisoned().length) - API3.warn("Petra warning: support for garrisoned units inside garrisoned holders not yet implemented"); + { + aiWarn("Petra warning: support for garrisoned units inside garrisoned holders " + + "not yet implemented"); + } continue; } @@ -106,7 +112,7 @@ Headquather.prototype.regionAnalysis = function(gameState) const accessibility = gameState.ai.accessibility; let landIndex; let seaIndex; - const ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); + const ccEnts = gameState.getOwnStructures().filter(filters.byClass("CivCentre")); for (const cc of ccEnts.values()) { const land = accessibility.getAccessValue(cc.position()); @@ -136,7 +142,7 @@ Headquather.prototype.regionAnalysis = function(gameState) } if (!landIndex && !seaIndex) { - API3.warn("Petra error: it does not know how to interpret this map"); + aiWarn("Petra error: it does not know how to interpret this map"); return false; } @@ -184,10 +190,13 @@ Headquather.prototype.regionAnalysis = function(gameState) if (this.Config.debug < 3) return true; for (const region in this.landRegions) - API3.warn(" >>> zone " + region + " taille " + cellArea*gameState.ai.accessibility.regionSize[region]); - API3.warn(" navalMap " + this.navalMap); - API3.warn(" landRegions " + uneval(this.landRegions)); - API3.warn(" navalRegions " + uneval(this.navalRegions)); + { + aiWarn(" >>> zone " + region + " taille " + + cellArea * gameState.ai.accessibility.regionSize[region]); + } + aiWarn(" navalMap " + this.navalMap); + aiWarn(" landRegions " + uneval(this.landRegions)); + aiWarn(" navalRegions " + uneval(this.navalRegions)); return true; }; @@ -221,7 +230,7 @@ Headquather.prototype.buildFirstBase = function(gameState) return; const total = gameState.getResources(); let goal = "civil_centre"; - if (!total.canAfford(new API3.Resources(template.cost()))) + if (!total.canAfford(new ResourcesManager(template.cost()))) { const totalExpected = gameState.getResources(); // Check for treasures around available in some maps at startup @@ -244,12 +253,12 @@ Headquather.prototype.buildFirstBase = function(gameState) if (type in totalExpected) totalExpected[type] += types[type]; // If we can collect enough resources from these treasures, wait for them. - if (totalExpected.canAfford(new API3.Resources(template.cost()))) + if (totalExpected.canAfford(new ResourcesManager(template.cost()))) return; } // not enough resource to build a cc, try with a dock to accumulate resources if none yet - if (!this.navalManager.docks.filter(API3.Filters.byClass("Dock")).hasEntities()) + if (!this.navalManager.docks.filter(filters.byClass("Dock")).hasEntities()) { if (gameState.ai.queues.dock.hasQueuedUnits()) return; @@ -257,7 +266,7 @@ Headquather.prototype.buildFirstBase = function(gameState) if (gameState.isTemplateDisabled(templateName)) return; template = gameState.getTemplate(templateName); - if (!template || !total.canAfford(new API3.Resources(template.cost()))) + if (!template || !total.canAfford(new ResourcesManager(template.cost()))) return; goal = "dock"; } @@ -290,7 +299,7 @@ Headquather.prototype.buildFirstBase = function(gameState) { if (land !== point.land || sea !== point.sea) continue; - if (API3.SquareVectorDistance(point.pos, pos) > 2500) + if (SquareVectorDistance(point.pos, pos) > 2500) continue; point.weight += 1; found = true; @@ -328,17 +337,21 @@ Headquather.prototype.buildFirstBase = function(gameState) */ Headquather.prototype.dispatchUnits = function(gameState) { - const allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); + const allycc = gameState.getExclusiveAllyEntities().filter(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 "); + { + aiWarn(" We have allied cc " + allycc.length + " and " + gameState.getOwnUnits().length + + " units "); + } const 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.byClasses(["Infantry+Ranged"])).forEach(ent => { + units.filter(filters.byClasses(["Infantry+Ranged"])).forEach(ent => { if (!num || !num1) return; if (ent.getMetadata(PlayerID, "allied")) @@ -357,7 +370,7 @@ Headquather.prototype.dispatchUnits = function(gameState) } }); // second pass to affect melee infantry - units.filter(API3.Filters.byClasses(["Infantry+Melee"])).forEach(ent => { + units.filter(filters.byClasses(["Infantry+Melee"])).forEach(ent => { if (!num || !num2) return; if (ent.getMetadata(PlayerID, "allied")) @@ -425,7 +438,7 @@ Headquather.prototype.configFirstBase = function(gameState) const cell = gameState.getPassabilityMap().cellSize; startingSize = startingSize * cell * cell; if (this.Config.debug > 1) - API3.warn("starting size " + startingSize + "(cut at 24000 for fish pushing)"); + aiWarn("starting size " + startingSize + "(cut at 24000 for fish pushing)"); if (startingSize < 25000) { this.saveSpace = true; @@ -464,7 +477,10 @@ Headquather.prototype.configFirstBase = function(gameState) startingWood += this.getTotalResourceLevel(gameState, ["wood"], ["nearby", "medium", "faraway"]).wood; if (this.Config.debug > 1) - API3.warn("startingWood: " + startingWood + " (cut at 8500 for no rush and 6000 for saveResources)"); + { + aiWarn("startingWood: " + startingWood + + " (cut at 8500 for no rush and 6000 for saveResources)"); + } if (startingWood < 6000) { this.saveResources = true; @@ -498,11 +514,11 @@ Headquather.prototype.configFirstBase = function(gameState) { // if we start with enough workers, put our available resources in this first dropsite // same thing if our pop exceed the allowed one, as we will need several houses - const numWorkers = gameState.getOwnUnits().filter(API3.Filters.byClass("Worker")).length; + const numWorkers = gameState.getOwnUnits().filter(filters.byClass("Worker")).length; if (numWorkers > 12 && newDP.quality > 60 || gameState.getPopulation() > gameState.getPopulationLimit() + 20) { - const cost = new API3.Resources(gameState.getTemplate(newDP.templateName).cost()); + const cost = new ResourcesManager(gameState.getTemplate(newDP.templateName).cost()); gameState.ai.queueManager.setAccounts(gameState, cost, "dropsites"); } gameState.ai.queues.dropsites.addPlan(new ConstructionPlan(gameState, newDP.templateName, diff --git a/binaries/data/mods/public/simulation/ai/petra/tradeManager.js b/binaries/data/mods/public/simulation/ai/petra/tradeManager.js index 0662740d20..3067c1d4b8 100644 --- a/binaries/data/mods/public/simulation/ai/petra/tradeManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/tradeManager.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { newTradeRoute as chatNewTradeRoute } from "simulation/ai/petra/chatHelper.js"; import { Config, DIFFICULTY_VERY_EASY } from "simulation/ai/petra/config.js"; import { gatherTreasure, getBestBase, getLandAccess, getSeaAccess, isLineInsideEnemyTerritory } from @@ -22,7 +24,7 @@ export function TradeManager(config) TradeManager.prototype.init = function(gameState) { this.traders = gameState.getOwnUnits().filter( - API3.Filters.byMetadata(PlayerID, "role", Worker.ROLE_TRADER)); + filters.byMetadata(PlayerID, "role", Worker.ROLE_TRADER)); this.traders.registerUpdates(); this.minimalGain = gameState.ai.HQ.navalMap ? 3 : 5; }; @@ -44,7 +46,7 @@ TradeManager.prototype.trainMoreTraders = function(gameState, queues) return; let numTraders = this.traders.length; - let numSeaTraders = this.traders.filter(API3.Filters.byClass("Ship")).length; + let numSeaTraders = this.traders.filter(filters.byClass("Ship")).length; let numLandTraders = numTraders - numSeaTraders; // add traders already in training gameState.getOwnTrainingFacilities().forEach(function(ent) { @@ -109,8 +111,10 @@ TradeManager.prototype.trainMoreTraders = function(gameState, queues) if (!gameState.getTemplate(template)) { if (this.Config.debug > 0) - API3.warn("Petra error: trying to train " + template + " for civ " + - gameState.getPlayerCiv() + " but no template found."); + { + aiWarn("Petra error: trying to train " + template + " for civ " + + gameState.getPlayerCiv() + " but no template found."); + } return; } queues.trader.addPlan(new TrainingPlan(gameState, template, metadata, 1, 1)); @@ -139,14 +143,17 @@ TradeManager.prototype.updateTrader = function(gameState, ent) { // TODO try to garrison land trader inside merchant ship when only sea routes available if (this.Config.debug > 0) - API3.warn(" no available route for " + ent.genericName() + " " + ent.id()); + aiWarn(" no available route for " + ent.genericName() + " " + ent.id()); Engine.ProfileStop(); return; } let nearerSource = true; - if (API3.SquareVectorDistance(route.target.position(), ent.position()) < API3.SquareVectorDistance(route.source.position(), ent.position())) + if (SquareVectorDistance(route.target.position(), ent.position()) < + SquareVectorDistance(route.source.position(), ent.position())) + { nearerSource = false; + } if (!ent.hasClass("Ship") && route.land != access) { @@ -216,7 +223,7 @@ TradeManager.prototype.setTradingGoods = function(gameState) tradingGoods[mostNeeded[0].type] += nextNeed; Engine.PostCommand(PlayerID, { "type": "set-trading-goods", "tradingGoods": tradingGoods }); if (this.Config.debug > 2) - API3.warn(" trading goods set to " + uneval(tradingGoods)); + aiWarn(" trading goods set to " + uneval(tradingGoods)); }; /** @@ -225,7 +232,8 @@ TradeManager.prototype.setTradingGoods = function(gameState) */ TradeManager.prototype.performBarter = function(gameState) { - const barterers = gameState.getOwnEntitiesByClass("Barter", true).filter(API3.Filters.isBuilt()).toEntityArray(); + const barterers = gameState.getOwnEntitiesByClass("Barter", true).filter(filters.isBuilt()) + .toEntityArray(); if (barterers.length == 0) return false; const resBarterCodes = Resources.GetBarterableCodes(); @@ -294,11 +302,13 @@ TradeManager.prototype.performBarter = function(gameState) const amount = available[bestToSell] > 5000 ? 500 : 100; barterers[0].barter(buy, bestToSell, amount); if (this.Config.debug > 2) - API3.warn("Necessity bartering: sold " + bestToSell +" for " + buy + - " >> need sell " + needs[bestToSell] + " need buy " + needs[buy] + - " rate buy " + rates[buy] + " available sell " + available[bestToSell] + - " available buy " + available[buy] + " barterRate " + bestRate + - " amount " + amount); + { + aiWarn("Necessity bartering: sold " + bestToSell +" for " + buy + + " >> need sell " + needs[bestToSell] + " need buy " + needs[buy] + + " rate buy " + rates[buy] + " available sell " + available[bestToSell] + + " available buy " + available[buy] + " barterRate " + bestRate + + " amount " + amount); + } return true; } } @@ -330,10 +340,11 @@ TradeManager.prototype.performBarter = function(gameState) const amount = available.food > 5000 ? 500 : 100; barterers[0].barter(bestToBuy, "food", amount); if (this.Config.debug > 2) - API3.warn("Contingency bartering: sold food for " + bestToBuy + - " available sell " + available.food + " available buy " + available[bestToBuy] + - " barterRate " + getBarterRate(barterPrices, bestToBuy, "food") + - " amount " + amount); + { + aiWarn("Contingency bartering: sold food for " + bestToBuy + " available sell " + + available.food + " available buy " + available[bestToBuy] + " barterRate " + + getBarterRate(barterPrices, bestToBuy, "food") + " amount " + amount); + } return true; } @@ -430,8 +441,9 @@ TradeManager.prototype.checkRoutes = function(gameState, accessIndex) return false; } - const market1 = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Trade"), gameState.getOwnStructures()); - let market2 = gameState.updatingCollection("diplo-ExclusiveAllyMarkets", API3.Filters.byClass("Trade"), gameState.getExclusiveAllyEntities()); + const market1 = gameState.updatingCollection("OwnMarkets", filters.byClass("Trade"), gameState.getOwnStructures()); + let market2 = gameState.updatingCollection("diplo-ExclusiveAllyMarkets", filters.byClass("Trade"), + gameState.getExclusiveAllyEntities()); if (market1.length + market2.length < 2) // We have to wait ... markets will be built soon { this.tradeRoute = undefined; @@ -477,7 +489,8 @@ TradeManager.prototype.checkRoutes = function(gameState, accessIndex) gainMultiplier = traderTemplatesGains.navalGainMultiplier; else continue; - const gain = Math.round(gainMultiplier * TradeGain(API3.SquareVectorDistance(m1.position(), m2.position()), mapSize)); + const gain = Math.round(gainMultiplier * + TradeGain(SquareVectorDistance(m1.position(), m2.position()), mapSize)); if (gain < this.minimalGain) continue; if (m1.foundationProgress() === undefined && m2.foundationProgress() === undefined) @@ -521,7 +534,7 @@ TradeManager.prototype.checkRoutes = function(gameState, accessIndex) if (candidate.gain < 1) { if (this.Config.debug > 2) - API3.warn("no better trade route possible"); + aiWarn("no better trade route possible"); this.tradeRoute = undefined; return false; } @@ -529,10 +542,13 @@ TradeManager.prototype.checkRoutes = function(gameState, accessIndex) if (this.Config.debug > 1 && this.tradeRoute) { if (candidate.gain > this.tradeRoute.gain) - API3.warn("one better trade route set with gain " + candidate.gain + " instead of " + this.tradeRoute.gain); + { + aiWarn("one better trade route set with gain " + candidate.gain + " instead of " + + this.tradeRoute.gain); + } } else if (this.Config.debug > 1) - API3.warn("one trade route set with gain " + candidate.gain); + aiWarn("one trade route set with gain " + candidate.gain); this.tradeRoute = candidate; if (this.Config.chat) @@ -601,9 +617,13 @@ TradeManager.prototype.prospectForNewMarket = function(gameState, queues) return; if (!gameState.ai.HQ.canBuild(gameState, "structures/{civ}/market")) return; - if (!gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Trade"), gameState.getOwnStructures()).hasEntities() && - !gameState.updatingCollection("diplo-ExclusiveAllyMarkets", API3.Filters.byClass("Trade"), gameState.getExclusiveAllyEntities()).hasEntities()) + if (!gameState.updatingCollection("OwnMarkets", filters.byClass("Trade"), + gameState.getOwnStructures()).hasEntities() && + !gameState.updatingCollection("diplo-ExclusiveAllyMarkets", filters.byClass("Trade"), + gameState.getExclusiveAllyEntities()).hasEntities()) + { return; + } const template = gameState.getTemplate(gameState.applyCiv("structures/{civ}/market")); if (!template) return; @@ -624,11 +644,15 @@ TradeManager.prototype.prospectForNewMarket = function(gameState, queues) if (this.Config.debug > 1) { if (this.potentialTradeRoute) - API3.warn("turn " + gameState.ai.playedTurn + "we could have a new route with gain " + + { + aiWarn("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 " + + { + aiWarn("turn " + gameState.ai.playedTurn + "we could have a first route with gain " + marketPos[3]); + } } if (!this.tradeRoute) diff --git a/binaries/data/mods/public/simulation/ai/petra/transportPlan.js b/binaries/data/mods/public/simulation/ai/petra/transportPlan.js index b1bddcfa27..0c767748d7 100644 --- a/binaries/data/mods/public/simulation/ai/petra/transportPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/transportPlan.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, VectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { getLandAccess } from "simulation/ai/petra/entityExtend.js"; import { Worker } from "simulation/ai/petra/worker.js"; @@ -55,7 +57,10 @@ export function TransportPlan(gameState, units, startIndex, endIndex, endPos, sh { this.failed = true; if (this.debug > 1) - API3.warn("transport plan with bad path: startIndex " + startIndex + " endIndex " + endIndex); + { + aiWarn("transport plan with bad path: startIndex " + startIndex + " endIndex " + + endIndex); + } return false; } } @@ -67,8 +72,10 @@ export function TransportPlan(gameState, units, startIndex, endIndex, endPos, sh } if (this.debug > 1) - API3.warn("Starting a new transport plan with ID " + this.ID + - " to index " + endIndex + " with units length " + units.length); + { + aiWarn("Starting a new transport plan with ID " + this.ID + " to index " + endIndex + + " with units length " + units.length); + } this.state = TransportPlan.BOARDING; this.boardingPos = {}; @@ -88,9 +95,11 @@ TransportPlan.SAILING = "sailing"; TransportPlan.prototype.init = function(gameState) { - this.units = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "transport", this.ID)); - this.ships = gameState.ai.HQ.navalManager.ships.filter(API3.Filters.byMetadata(PlayerID, "transporter", this.ID)); - this.transportShips = gameState.ai.HQ.navalManager.transportShips.filter(API3.Filters.byMetadata(PlayerID, "transporter", this.ID)); + this.units = gameState.getOwnUnits().filter(filters.byMetadata(PlayerID, "transport", this.ID)); + this.ships = gameState.ai.HQ.navalManager.ships.filter(filters.byMetadata(PlayerID, "transporter", + this.ID)); + this.transportShips = gameState.ai.HQ.navalManager.transportShips.filter(filters.byMetadata(PlayerID, + "transporter", this.ID)); this.units.registerUpdates(); this.ships.registerUpdates(); @@ -113,7 +122,7 @@ TransportPlan.prototype.countFreeSlotsOnShip = function(ship) if (ship.hitpoints() < ship.garrisonEjectHealth() * ship.maxHitpoints()) return 0; const occupied = ship.garrisoned().length + - this.units.filter(API3.Filters.byMetadata(PlayerID, "onBoard", ship.id())).length; + this.units.filter(filters.byMetadata(PlayerID, "onBoard", ship.id())).length; return Math.max(ship.garrisonMax() - occupied, 0); }; @@ -168,7 +177,7 @@ TransportPlan.prototype.assignShip = function(gameState) return; if (pos) { - const dist = API3.SquareVectorDistance(pos, ship.position()); + const dist = SquareVectorDistance(pos, ship.position()); if (dist > distmin) return; distmin = dist; @@ -212,7 +221,7 @@ TransportPlan.prototype.removeUnit = function(gameState, unit) unit.setMetadata(PlayerID, "onBoard", undefined); const ship = gameState.getEntityById(shipId); if (ship && !ship.garrisoned().length && - !this.units.filter(API3.Filters.byMetadata(PlayerID, "onBoard", shipId)).length) + !this.units.filter(filters.byMetadata(PlayerID, "onBoard", shipId)).length) { this.releaseShip(ship); this.ships.updateEnt(ship); @@ -225,8 +234,9 @@ TransportPlan.prototype.releaseShip = function(ship) { if (ship.getMetadata(PlayerID, "transporter") != this.ID) { - API3.warn(" Petra: try removing a transporter ship with " + ship.getMetadata(PlayerID, "transporter") + - " from " + this.ID + " and stance " + ship.getStance()); + aiWarn(" Petra: try removing a transporter ship with " + + ship.getMetadata(PlayerID, "transporter") + " from " + this.ID + " and stance " + + ship.getStance()); return; } @@ -334,7 +344,7 @@ TransportPlan.prototype.onBoarding = function(gameState) ent.setMetadata(PlayerID, "onBoard", undefined); continue; } - const distShip = API3.SquareVectorDistance(this.boardingPos[shipId], ship.position()); + const distShip = SquareVectorDistance(this.boardingPos[shipId], ship.position()); if (!shipTested[shipId] && distShip > this.boardingRange) { shipTested[shipId] = true; @@ -372,7 +382,7 @@ TransportPlan.prototype.onBoarding = function(gameState) { this.nTry[shipId] = 0; if (this.debug > 1) - API3.warn("ship " + shipId + " new attempt for a landing point "); + aiWarn("ship " + shipId + " new attempt for a landing point "); this.boardingPos[shipId] = this.getBoardingPos(gameState, ship, this.startIndex, this.sea, undefined, false); } ship.move(this.boardingPos[shipId][0], this.boardingPos[shipId][1]); @@ -395,7 +405,10 @@ TransportPlan.prototype.onBoarding = function(gameState) if (this.nTry[ent.id()] > 5) { if (this.debug > 1) - API3.warn("unit blocked, but no ways out of the trap ... destroy it"); + { + aiWarn("unit blocked, but no ways out of the trap ... " + + "destroy it"); + } this.resetUnit(gameState, ent); ent.destroy(); continue; @@ -404,7 +417,7 @@ TransportPlan.prototype.onBoarding = function(gameState) ent.moveToRange(newPos[0], newPos[1], 30, 35); ent.garrison(ship, true); } - else if (API3.SquareVectorDistance(this.boardingPos[shipId], newPos) > 225) + else if (SquareVectorDistance(this.boardingPos[shipId], newPos) > 225) ent.moveToRange(this.boardingPos[shipId][0], this.boardingPos[shipId][1], 0, 15); } else @@ -455,12 +468,12 @@ TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex, se { if (!gameState.ai.HQ.navalManager.landingZones[landIndex]) { - API3.warn(" >>> no landing zone for land " + landIndex); + aiWarn(" >>> no landing zone for land " + landIndex); return destination; } else if (!gameState.ai.HQ.navalManager.landingZones[landIndex][seaIndex]) { - API3.warn(" >>> no landing zone for land " + landIndex + " and sea " + seaIndex); + aiWarn(" >>> no landing zone for land " + landIndex + " and sea " + seaIndex); return destination; } @@ -469,15 +482,15 @@ TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex, se let posmin = destination; const width = gameState.getPassabilityMap().width; const cell = gameState.getPassabilityMap().cellSize; - const alliedDocks = gameState.getAllyStructures().filter(API3.Filters.and( - API3.Filters.byClass("Dock"), API3.Filters.byMetadata(PlayerID, "sea", seaIndex))).toEntityArray(); + const alliedDocks = gameState.getAllyStructures().filter(filters.and(filters.byClass("Dock"), + filters.byMetadata(PlayerID, "sea", seaIndex))).toEntityArray(); for (const i of gameState.ai.HQ.navalManager.landingZones[landIndex][seaIndex]) { let pos = [i%width+0.5, Math.floor(i/width)+0.5]; pos = [cell*pos[0], cell*pos[1]]; - let dist = API3.VectorDistance(startPos, pos); + let dist = VectorDistance(startPos, pos); if (destination) - dist += API3.VectorDistance(pos, destination); + dist += VectorDistance(pos, destination); if (avoidEnnemy) { const territoryOwner = gameState.ai.HQ.territoryMap.getOwner(pos); @@ -487,9 +500,13 @@ TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex, se // require a small distance between all ships of the transport plan to avoid path finder problems // this is also used when the ship is blocked and we want to find a new boarding point for (const shipId in this.boardingPos) + { if (this.boardingPos[shipId] !== undefined && - API3.SquareVectorDistance(this.boardingPos[shipId], pos) < this.boardingRange) + SquareVectorDistance(this.boardingPos[shipId], pos) < this.boardingRange) + { dist += 1000000; + } + } // and not too near our allied docks to not disturb naval traffic let distSquare; for (const dock of alliedDocks) @@ -498,7 +515,7 @@ TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex, se distSquare = 900; else distSquare = 4900; - const dockDist = API3.SquareVectorDistance(dock.position(), pos); + const dockDist = SquareVectorDistance(dock.position(), pos); if (dockDist < distSquare) dist += 100000 * (distSquare - dockDist) / distSquare; } @@ -530,13 +547,13 @@ TransportPlan.prototype.onSailing = function(gameState) continue; } if (this.debug > 1) - API3.warn(">>> transport " + this.ID + " reloading failed ... <<<"); + aiWarn(">>> transport " + this.ID + " reloading failed ... <<<"); // destroy the unit if inaccessible otherwise leave it there const index = getLandAccess(gameState, ent); if (gameState.ai.HQ.landRegions[index]) { if (this.debug > 1) - API3.warn(" recovered entity kept " + ent.id()); + aiWarn(" recovered entity kept " + ent.id()); this.resetUnit(gameState, ent); // TODO we should not destroy it, but now the unit could still be reloaded on the next turn // and mess everything @@ -545,7 +562,7 @@ TransportPlan.prototype.onSailing = function(gameState) else { if (this.debug > 1) - API3.warn("recovered entity destroyed " + ent.id()); + aiWarn("recovered entity destroyed " + ent.id()); this.resetUnit(gameState, ent); ent.destroy(); } @@ -568,14 +585,14 @@ TransportPlan.prototype.onSailing = function(gameState) ent.setMetadata(PlayerID, "onBoard", "onBoard"); else { - API3.warn("Petra transportPlan problem: unit not on ship without position ???"); + aiWarn("Petra transportPlan problem: unit not on ship without position ???"); this.resetUnit(gameState, ent); ent.destroy(); } } else { - API3.warn("Petra transportPlan problem: unit on ship, but no ship ???"); + aiWarn("Petra transportPlan problem: unit on ship, but no ship ???"); this.resetUnit(gameState, ent); ent.destroy(); } @@ -584,7 +601,7 @@ TransportPlan.prototype.onSailing = function(gameState) { // unit unloaded on a wrong region - try to regarrison it and move a bit the ship if (this.debug > 1) - API3.warn(">>> unit unloaded on a wrong region ! try to garrison it again <<<"); + aiWarn(">>> unit unloaded on a wrong region ! try to garrison it again <<<"); const ship = gameState.getEntityById(ent.getMetadata(PlayerID, "onBoard")); if (ship && !this.canceled) { @@ -596,7 +613,7 @@ TransportPlan.prototype.onSailing = function(gameState) else { if (this.debug > 1) - API3.warn("no way ... we destroy it"); + aiWarn("no way ... we destroy it"); this.resetUnit(gameState, ent); ent.destroy(); } @@ -606,7 +623,7 @@ TransportPlan.prototype.onSailing = function(gameState) // And make some room for other units const pos = ent.position(); const goal = ent.getMetadata(PlayerID, "endPos"); - const dist = goal ? API3.VectorDistance(pos, goal) : 0; + const dist = goal ? VectorDistance(pos, goal) : 0; if (dist > 30) ent.moveToRange(goal[0], goal[1], dist-25, dist-20); else @@ -639,7 +656,7 @@ TransportPlan.prototype.onSailing = function(gameState) if (ship.unitAIState() == "INDIVIDUAL.WALKING") continue; const shipId = ship.id(); - const dist = API3.SquareVectorDistance(ship.position(), this.boardingPos[shipId]); + const dist = SquareVectorDistance(ship.position(), this.boardingPos[shipId]); let remaining = 0; for (const entId of ship.garrisoned()) { @@ -676,7 +693,7 @@ TransportPlan.prototype.onSailing = function(gameState) { this.nTry[shipId] = 0; if (this.debug > 1) - API3.warn(shipId + " new attempt for a landing point "); + aiWarn(shipId + " new attempt for a landing point "); this.boardingPos[shipId] = this.getBoardingPos(gameState, ship, this.endIndex, this.sea, undefined, true); } ship.move(this.boardingPos[shipId][0], this.boardingPos[shipId][1]); diff --git a/binaries/data/mods/public/simulation/ai/petra/victoryManager.js b/binaries/data/mods/public/simulation/ai/petra/victoryManager.js index facb6e80f5..0896d59128 100644 --- a/binaries/data/mods/public/simulation/ai/petra/victoryManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/victoryManager.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance } from "simulation/ai/common-api/utils.js"; import { AttackPlan } from "simulation/ai/petra/attackPlan.js"; import { getAttackBonus, getBestBase, getLandAccess, returnResources } from "simulation/ai/petra/entityExtend.js"; @@ -53,7 +55,8 @@ VictoryManager.prototype.init = function(gameState) if (gameState.getVictoryConditions().has("capture_the_relic")) { - for (const relic of gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")).values()) + for (const relic of + gameState.updatingGlobalCollection("allRelics", filters.byClass("Relic")).values()) { if (relic.owner() == PlayerID) this.criticalEnts.set(relic.id(), { "guardsAssigned": 0, "guards": new Map() }); @@ -622,7 +625,8 @@ VictoryManager.prototype.update = function(gameState, events, queues) } // And look for some new gaia relics visible by any of our units // or that may be on our territory - const allGaiaRelics = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic")).filter(relic => relic.owner() == 0); + const allGaiaRelics = gameState.updatingGlobalCollection("allRelics", filters.byClass("Relic")) + .filter(relic => relic.owner() == 0); for (const relic of allGaiaRelics.values()) { const relicPosition = relic.position(); @@ -643,7 +647,7 @@ VictoryManager.prototype.update = function(gameState, events, queues) { if (!ent.position() || !ent.visionRange()) continue; - if (API3.SquareVectorDistance(ent.position(), relicPosition) > Math.square(ent.visionRange())) + if (SquareVectorDistance(ent.position(), relicPosition) > Math.square(ent.visionRange())) continue; this.targetedGaiaRelics.set(relic.id(), []); this.captureGaiaRelic(gameState, relic); diff --git a/binaries/data/mods/public/simulation/ai/petra/worker.js b/binaries/data/mods/public/simulation/ai/petra/worker.js index 22550b9daa..603e64a2d1 100644 --- a/binaries/data/mods/public/simulation/ai/petra/worker.js +++ b/binaries/data/mods/public/simulation/ai/petra/worker.js @@ -1,3 +1,5 @@ +import * as filters from "simulation/ai/common-api/filters.js"; +import { SquareVectorDistance, warn as aiWarn } from "simulation/ai/common-api/utils.js"; import { allowCapture, gatherTreasure, getBuiltEntity, getLandAccess, getSeaAccess, isFastMoving, isSupplyFull, returnResources } from "simulation/ai/petra/entityExtend.js"; import { TransportPlan } from "simulation/ai/petra/transportPlan.js"; @@ -68,7 +70,7 @@ Worker.prototype.update = function(gameState, ent) } if (!hasDropsite) { - for (const unit of gameState.getOwnUnits().filter(API3.Filters.byClass("Support")).values()) + for (const unit of gameState.getOwnUnits().filter(filters.byClass("Support")).values()) { if (!unit.position() || getLandAccess(gameState, unit) != plan.endIndex) continue; @@ -149,7 +151,7 @@ Worker.prototype.update = function(gameState, ent) continue; if (targetAccess != getLandAccess(gameState, dropsite)) continue; - if (API3.SquareVectorDistance(target.position(), dropsite.position()) < distanceSquare) + if (SquareVectorDistance(target.position(), dropsite.position()) < distanceSquare) { hasFoodDropsiteWithinDistance = true; break; @@ -750,7 +752,7 @@ Worker.prototype.startGathering = function(gameState) // If we are here, we have nothing left to gather ... certainly no more resources of this type gameState.ai.HQ.lastFailedGather[resource] = gameState.ai.elapsedTime; if (gameState.ai.Config.debug > 2) - API3.warn(" >>>>> worker with gather-type " + resource + " with nothing to gather "); + aiWarn(" >>>>> worker with gather-type " + resource + " with nothing to gather "); this.ent.setMetadata(PlayerID, "subrole", Worker.SUBROLE_IDLE); return false; }; @@ -790,7 +792,7 @@ Worker.prototype.startHunting = function(gameState, position) continue; if (supplyAccess != getLandAccess(gameState, dropsite)) continue; - if (API3.SquareVectorDistance(supplyPosition, dropsite.position()) < distSquare) + if (SquareVectorDistance(supplyPosition, dropsite.position()) < distSquare) return true; } return false; @@ -827,7 +829,7 @@ Worker.prototype.startHunting = function(gameState, position) continue; // measure the distance to the resource. - const dist = API3.SquareVectorDistance(entPosition, supply.position()); + const dist = SquareVectorDistance(entPosition, supply.position()); if (dist > nearestSupplyDist) continue; @@ -884,8 +886,8 @@ Worker.prototype.startFishing = function(gameState) let nearestSupply; const fisherSea = getSeaAccess(gameState, this.ent); - const fishDropsites = (gameState.playerData.hasSharedDropsites ? gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food")). - filter(API3.Filters.byClass("Dock")).toEntityArray(); + const fishDropsites = (gameState.playerData.hasSharedDropsites ? gameState.getAnyDropsites("food") : + gameState.getOwnDropsites("food")).filter(filters.byClass("Dock")).toEntityArray(); const nearestDropsiteDist = function(supply) { let distMin = 1000000; @@ -900,7 +902,7 @@ Worker.prototype.startFishing = function(gameState) continue; if (fisherSea != getSeaAccess(gameState, dropsite)) continue; - distMin = Math.min(distMin, API3.SquareVectorDistance(pos, dropsite.position())); + distMin = Math.min(distMin, SquareVectorDistance(pos, dropsite.position())); } return distMin; }; @@ -963,7 +965,8 @@ Worker.prototype.startFishing = function(gameState) Worker.prototype.gatherNearestField = function(gameState, baseID) { - const ownFields = gameState.getOwnEntitiesByClass("Field", true).filter(API3.Filters.isBuilt()).filter(API3.Filters.byMetadata(PlayerID, "base", baseID)); + const ownFields = gameState.getOwnEntitiesByClass("Field", true).filter(filters.isBuilt()) + .filter(filters.byMetadata(PlayerID, "base", baseID)); let bestFarm; const gatherRates = this.ent.resourceGatherRates(); @@ -984,14 +987,17 @@ Worker.prototype.gatherNearestField = function(gameState, baseID) rate = Math.pow(diminishing, num); } // Add a penalty distance depending on rate - const dist = API3.SquareVectorDistance(field.position(), this.ent.position()) + (1 - rate) * 160000; + const dist = SquareVectorDistance(field.position(), this.ent.position()) + (1 - rate) * 160000; if (!bestFarm || dist < bestFarm.dist) bestFarm = { "ent": field, "dist": dist, "rate": rate }; } // If other field foundations available, better build them when rate becomes too small - if (!bestFarm || bestFarm.rate < 0.70 && - gameState.getOwnFoundations().filter(API3.Filters.byClass("Field")).filter(API3.Filters.byMetadata(PlayerID, "base", baseID)).hasEntities()) + if (!bestFarm || bestFarm.rate < 0.70 && gameState.getOwnFoundations() + .filter(filters.byClass("Field")).filter(filters.byMetadata(PlayerID, "base", baseID)) + .hasEntities()) + { return false; + } this.base.AddTCGatherer(bestFarm.ent.id()); this.ent.setMetadata(PlayerID, "supply", bestFarm.ent.id()); return bestFarm.ent; @@ -1016,7 +1022,7 @@ Worker.prototype.buildAnyField = function(gameState, baseID) if (current === undefined || current >= gameState.getBuiltTemplate(found.templateName()).maxGatherers()) continue; - const dist = API3.SquareVectorDistance(found.position(), pos); + const dist = SquareVectorDistance(found.position(), pos); if (dist > bestFarmDist) continue; bestFarmEnt = found; @@ -1052,7 +1058,7 @@ Worker.prototype.moveToGatherer = function(gameState, ent, forced) { continue; } - const distance = API3.SquareVectorDistance(pos, gatherer.position()); + const distance = SquareVectorDistance(pos, gatherer.position()); if (distance > dist) continue; dist = distance; diff --git a/source/simulation2/components/CCmpAIManager.cpp b/source/simulation2/components/CCmpAIManager.cpp index 8421fbfcf9..5e285a00eb 100644 --- a/source/simulation2/components/CCmpAIManager.cpp +++ b/source/simulation2/components/CCmpAIManager.cpp @@ -297,7 +297,6 @@ public: ScriptFunction::Register<&CAIWorker::func, ScriptInterface::ObjectFromCBData>(rq, name); REGISTER_FUNC_NAME(PostCommand, "PostCommand"); - REGISTER_FUNC_NAME(LoadScripts, "IncludeModule"); ScriptFunction::Register(rq, "Exit"); REGISTER_FUNC_NAME(ComputePathScript, "ComputePath"); @@ -316,35 +315,6 @@ public: bool HasLoadedEntityTemplates() const { return m_HasLoadedEntityTemplates; } - bool LoadScripts(const std::wstring& moduleName) - { - // Ignore modules that are already loaded - if (m_LoadedModules.find(moduleName) != m_LoadedModules.end()) - return true; - - // Mark this as loaded, to prevent it recursively loading itself - m_LoadedModules.insert(moduleName); - - // Load and execute *.js - VfsPaths pathnames; - if (vfs::GetPathnames(g_VFS, L"simulation/ai/" + moduleName + L"/", L"*.js", pathnames) < 0) - { - LOGERROR("Failed to load AI scripts for module %s", utf8_from_wstring(moduleName)); - return false; - } - - for (const VfsPath& path : pathnames) - { - if (!m_ScriptInterface->LoadGlobalScriptFile(path)) - { - LOGERROR("Failed to load script %s", path.string8()); - return false; - } - } - - return true; - } - void PostCommand(int playerid, JS::HandleValue cmd) { ScriptRequest rq(m_ScriptInterface); @@ -442,30 +412,21 @@ public: ScriptRequest rq(m_ScriptInterface); // we don't need to load it. - if (!m_HasSharedComponent) + if (!std::exchange(m_HasSharedComponent, true)) return false; - // reset the value so it can be used to determine if we actually initialized it. - m_HasSharedComponent = false; + auto result = m_ScriptInterface->GetModuleLoader().LoadModule(rq, + "simulation/ai/common-api/shared.js"); - if (LoadScripts(L"common-api")) - m_HasSharedComponent = true; - else - return false; + g_ScriptContext->RunJobs(); // mainly here for the error messages OsPath path = L"simulation/ai/common-api/"; // Constructor name is SharedScript, it's in the module API3 // TODO: Hardcoding this is bad, we need a smarter way. - JS::RootedValue AIModule(rq.cx); - JS::RootedValue global(rq.cx, rq.globalValue()); + JS::RootedValue AIModule(rq.cx, JS::ObjectValue(*result.begin()->Get())); JS::RootedValue ctor(rq.cx); - if (!Script::GetProperty(rq, global, "API3", &AIModule) || AIModule.isUndefined()) - { - LOGERROR("Failed to create shared AI component: %s: can't find module '%s'", path.string8(), "API3"); - return false; - } if (!Script::GetProperty(rq, AIModule, "SharedScript", &ctor) || ctor.isUndefined())