Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/ai/petra/entityExtend.js
var PETRA = function(m) | |||||
{ | |||||
/** returns true if this unit should be considered as a siege unit */ | /** returns true if this unit should be considered as a siege unit */ | ||||
m.isSiegeUnit = function(ent) | PETRA.isSiegeUnit = function(ent) | ||||
{ | { | ||||
return ent.hasClass("Siege") || ent.hasClass("Elephant") && ent.hasClass("Melee") && ent.hasClass("Champion"); | return ent.hasClass("Siege") || ent.hasClass("Elephant") && ent.hasClass("Melee") && ent.hasClass("Champion"); | ||||
}; | }; | ||||
/** returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. */ | /** returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. */ | ||||
m.getMaxStrength = function(ent, debugLevel, DamageTypeImportance, againstClass) | PETRA.getMaxStrength = function(ent, debugLevel, DamageTypeImportance, againstClass) | ||||
{ | { | ||||
let strength = 0; | let strength = 0; | ||||
let attackTypes = ent.attackTypes(); | let attackTypes = ent.attackTypes(); | ||||
let damageTypes = Object.keys(DamageTypeImportance); | let damageTypes = Object.keys(DamageTypeImportance); | ||||
if (!attackTypes) | if (!attackTypes) | ||||
return strength; | return strength; | ||||
for (let type of attackTypes) | for (let type of attackTypes) | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | for (let str in armourStrength) | ||||
else if (debugLevel > 0) | else if (debugLevel > 0) | ||||
API3.warn("Petra: " + str + " unknown armourStrength in getMaxStrength (please add " + str + " to config.js)."); | API3.warn("Petra: " + str + " unknown armourStrength in getMaxStrength (please add " + str + " to config.js)."); | ||||
} | } | ||||
return strength * ent.maxHitpoints() / 100.0; | return strength * ent.maxHitpoints() / 100.0; | ||||
}; | }; | ||||
/** Get access and cache it (except for units as it can change) in metadata if not already done */ | /** Get access and cache it (except for units as it can change) in metadata if not already done */ | ||||
m.getLandAccess = function(gameState, ent) | PETRA.getLandAccess = function(gameState, ent) | ||||
{ | { | ||||
if (ent.hasClass("Unit")) | if (ent.hasClass("Unit")) | ||||
return gameState.ai.accessibility.getAccessValue(ent.position()); | return gameState.ai.accessibility.getAccessValue(ent.position()); | ||||
let access = ent.getMetadata(PlayerID, "access"); | let access = ent.getMetadata(PlayerID, "access"); | ||||
if (!access) | if (!access) | ||||
{ | { | ||||
access = gameState.ai.accessibility.getAccessValue(ent.position()); | access = gameState.ai.accessibility.getAccessValue(ent.position()); | ||||
Show All 18 Lines | if (access < 2 && ent.buildPlacementType() == "shore") | ||||
} | } | ||||
} | } | ||||
ent.setMetadata(PlayerID, "access", access); | ent.setMetadata(PlayerID, "access", access); | ||||
} | } | ||||
return access; | return access; | ||||
}; | }; | ||||
/** Sea access always cached as it never changes */ | /** Sea access always cached as it never changes */ | ||||
m.getSeaAccess = function(gameState, ent) | PETRA.getSeaAccess = function(gameState, ent) | ||||
{ | { | ||||
let sea = ent.getMetadata(PlayerID, "sea"); | let sea = ent.getMetadata(PlayerID, "sea"); | ||||
if (!sea) | if (!sea) | ||||
{ | { | ||||
sea = gameState.ai.accessibility.getAccessValue(ent.position(), true); | sea = gameState.ai.accessibility.getAccessValue(ent.position(), true); | ||||
// Docks are sometimes not as expected | // Docks are sometimes not as expected | ||||
if (sea < 2 && ent.buildPlacementType() == "shore") | if (sea < 2 && ent.buildPlacementType() == "shore") | ||||
{ | { | ||||
Show All 9 Lines | if (sea < 2 && ent.buildPlacementType() == "shore") | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
ent.setMetadata(PlayerID, "sea", sea); | ent.setMetadata(PlayerID, "sea", sea); | ||||
} | } | ||||
return sea; | return sea; | ||||
}; | }; | ||||
m.setSeaAccess = function(gameState, ent) | PETRA.setSeaAccess = function(gameState, ent) | ||||
{ | { | ||||
m.getSeaAccess(gameState, ent); | PETRA.getSeaAccess(gameState, ent); | ||||
}; | }; | ||||
/** Decide if we should try to capture (returns true) or destroy (return false) */ | /** Decide if we should try to capture (returns true) or destroy (return false) */ | ||||
m.allowCapture = function(gameState, ent, target) | PETRA.allowCapture = function(gameState, ent, target) | ||||
{ | { | ||||
if (!target.isCapturable() || !ent.canCapture(target)) | if (!target.isCapturable() || !ent.canCapture(target)) | ||||
return false; | return false; | ||||
if (target.isInvulnerable()) | if (target.isInvulnerable()) | ||||
return true; | return true; | ||||
// always try to recapture cp from an allied, except if it's decaying | // always try to recapture cp from an allied, except if it's decaying | ||||
if (gameState.isPlayerAlly(target.owner())) | if (gameState.isPlayerAlly(target.owner())) | ||||
return !target.decaying(); | return !target.decaying(); | ||||
let antiCapture = target.defaultRegenRate(); | let antiCapture = target.defaultRegenRate(); | ||||
if (target.isGarrisonHolder() && target.garrisoned()) | if (target.isGarrisonHolder() && target.garrisoned()) | ||||
antiCapture += target.garrisonRegenRate() * target.garrisoned().length; | antiCapture += target.garrisonRegenRate() * target.garrisoned().length; | ||||
if (target.decaying()) | if (target.decaying()) | ||||
antiCapture -= target.territoryDecayRate(); | antiCapture -= target.territoryDecayRate(); | ||||
let capture; | let capture; | ||||
let capturableTargets = gameState.ai.HQ.capturableTargets; | let capturableTargets = gameState.ai.HQ.capturableTargets; | ||||
if (!capturableTargets.has(target.id())) | if (!capturableTargets.has(target.id())) | ||||
{ | { | ||||
capture = ent.captureStrength() * m.getAttackBonus(ent, target, "Capture"); | capture = ent.captureStrength() * PETRA.getAttackBonus(ent, target, "Capture"); | ||||
capturableTargets.set(target.id(), { "strength": capture, "ents": new Set([ent.id()]) }); | capturableTargets.set(target.id(), { "strength": capture, "ents": new Set([ent.id()]) }); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let capturable = capturableTargets.get(target.id()); | let capturable = capturableTargets.get(target.id()); | ||||
if (!capturable.ents.has(ent.id())) | if (!capturable.ents.has(ent.id())) | ||||
{ | { | ||||
capturable.strength += ent.captureStrength() * m.getAttackBonus(ent, target, "Capture"); | capturable.strength += ent.captureStrength() * PETRA.getAttackBonus(ent, target, "Capture"); | ||||
capturable.ents.add(ent.id()); | capturable.ents.add(ent.id()); | ||||
} | } | ||||
capture = capturable.strength; | capture = capturable.strength; | ||||
} | } | ||||
capture *= 1 / (0.1 + 0.9*target.healthLevel()); | capture *= 1 / (0.1 + 0.9*target.healthLevel()); | ||||
let sumCapturePoints = target.capturePoints().reduce((a, b) => a + b); | let sumCapturePoints = target.capturePoints().reduce((a, b) => a + b); | ||||
if (target.hasDefensiveFire() && target.isGarrisonHolder() && target.garrisoned()) | if (target.hasDefensiveFire() && target.isGarrisonHolder() && target.garrisoned()) | ||||
return capture > antiCapture + sumCapturePoints/50; | return capture > antiCapture + sumCapturePoints/50; | ||||
return capture > antiCapture + sumCapturePoints/80; | return capture > antiCapture + sumCapturePoints/80; | ||||
}; | }; | ||||
m.getAttackBonus = function(ent, target, type) | PETRA.getAttackBonus = function(ent, target, type) | ||||
{ | { | ||||
let attackBonus = 1; | let attackBonus = 1; | ||||
if (!ent.get("Attack/" + type) || !ent.get("Attack/" + type + "/Bonuses")) | if (!ent.get("Attack/" + type) || !ent.get("Attack/" + type + "/Bonuses")) | ||||
return attackBonus; | return attackBonus; | ||||
let bonuses = ent.get("Attack/" + type + "/Bonuses"); | let bonuses = ent.get("Attack/" + type + "/Bonuses"); | ||||
for (let key in bonuses) | for (let key in bonuses) | ||||
{ | { | ||||
let bonus = bonuses[key]; | let bonus = bonuses[key]; | ||||
if (bonus.Civ && bonus.Civ !== target.civ()) | if (bonus.Civ && bonus.Civ !== target.civ()) | ||||
continue; | continue; | ||||
if (bonus.Classes && bonus.Classes.split(/\s+/).some(cls => !target.hasClass(cls))) | if (bonus.Classes && bonus.Classes.split(/\s+/).some(cls => !target.hasClass(cls))) | ||||
continue; | continue; | ||||
attackBonus *= bonus.Multiplier; | attackBonus *= bonus.Multiplier; | ||||
} | } | ||||
return attackBonus; | return attackBonus; | ||||
}; | }; | ||||
/** Makes the worker deposit the currently carried resources at the closest accessible dropsite */ | /** Makes the worker deposit the currently carried resources at the closest accessible dropsite */ | ||||
m.returnResources = function(gameState, ent) | PETRA.returnResources = function(gameState, ent) | ||||
{ | { | ||||
if (!ent.resourceCarrying() || !ent.resourceCarrying().length || !ent.position()) | if (!ent.resourceCarrying() || !ent.resourceCarrying().length || !ent.position()) | ||||
return false; | return false; | ||||
let resource = ent.resourceCarrying()[0].type; | let resource = ent.resourceCarrying()[0].type; | ||||
let closestDropsite; | let closestDropsite; | ||||
let distmin = Math.min(); | let distmin = Math.min(); | ||||
let access = m.getLandAccess(gameState, ent); | let access = PETRA.getLandAccess(gameState, ent); | ||||
let dropsiteCollection = gameState.playerData.hasSharedDropsites ? | let dropsiteCollection = gameState.playerData.hasSharedDropsites ? | ||||
gameState.getAnyDropsites(resource) : gameState.getOwnDropsites(resource); | gameState.getAnyDropsites(resource) : gameState.getOwnDropsites(resource); | ||||
for (let dropsite of dropsiteCollection.values()) | for (let dropsite of dropsiteCollection.values()) | ||||
{ | { | ||||
if (!dropsite.position()) | if (!dropsite.position()) | ||||
continue; | continue; | ||||
let owner = dropsite.owner(); | let owner = dropsite.owner(); | ||||
// owner !== PlayerID can only happen when hasSharedDropsites === true, so no need to test it again | // owner !== PlayerID can only happen when hasSharedDropsites === true, so no need to test it again | ||||
if (owner !== PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner))) | if (owner !== PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner))) | ||||
continue; | continue; | ||||
if (m.getLandAccess(gameState, dropsite) != access) | if (PETRA.getLandAccess(gameState, dropsite) != access) | ||||
continue; | continue; | ||||
let dist = API3.SquareVectorDistance(ent.position(), dropsite.position()); | let dist = API3.SquareVectorDistance(ent.position(), dropsite.position()); | ||||
if (dist > distmin) | if (dist > distmin) | ||||
continue; | continue; | ||||
distmin = dist; | distmin = dist; | ||||
closestDropsite = dropsite; | closestDropsite = dropsite; | ||||
} | } | ||||
if (!closestDropsite) | if (!closestDropsite) | ||||
return false; | return false; | ||||
ent.returnResources(closestDropsite); | ent.returnResources(closestDropsite); | ||||
return true; | return true; | ||||
}; | }; | ||||
/** is supply full taking into account gatherers affected during this turn */ | /** is supply full taking into account gatherers affected during this turn */ | ||||
m.IsSupplyFull = function(gameState, ent) | PETRA.IsSupplyFull = function(gameState, ent) | ||||
{ | { | ||||
return ent.isFull() === true || | return ent.isFull() === true || | ||||
ent.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(ent.id()) >= ent.maxGatherers(); | ent.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(ent.id()) >= ent.maxGatherers(); | ||||
}; | }; | ||||
/** | /** | ||||
* Get the best base (in terms of distance and accessIndex) for an entity. | * Get the best base (in terms of distance and accessIndex) for an entity. | ||||
* It should be on the same accessIndex for structures. | * It should be on the same accessIndex for structures. | ||||
* If nothing found, return the base[0] for units and undefined for structures. | * If nothing found, return the base[0] for units and undefined for structures. | ||||
* If exclude is given, we exclude the base with ID = exclude. | * If exclude is given, we exclude the base with ID = exclude. | ||||
*/ | */ | ||||
m.getBestBase = function(gameState, ent, onlyConstructedBase = false, exclude = false) | PETRA.getBestBase = function(gameState, ent, onlyConstructedBase = false, exclude = false) | ||||
{ | { | ||||
let pos = ent.position(); | let pos = ent.position(); | ||||
let accessIndex; | let accessIndex; | ||||
if (!pos) | if (!pos) | ||||
{ | { | ||||
let holder = m.getHolder(gameState, ent); | let holder = PETRA.getHolder(gameState, ent); | ||||
if (!holder || !holder.position()) | if (!holder || !holder.position()) | ||||
{ | { | ||||
API3.warn("Petra error: entity without position, but not garrisoned"); | API3.warn("Petra error: entity without position, but not garrisoned"); | ||||
m.dumpEntity(ent); | PETRA.dumpEntity(ent); | ||||
return gameState.ai.HQ.baseManagers[0]; | return gameState.ai.HQ.baseManagers[0]; | ||||
} | } | ||||
pos = holder.position(); | pos = holder.position(); | ||||
accessIndex = m.getLandAccess(gameState, holder); | accessIndex = PETRA.getLandAccess(gameState, holder); | ||||
} | } | ||||
else | else | ||||
accessIndex = m.getLandAccess(gameState, ent); | accessIndex = PETRA.getLandAccess(gameState, ent); | ||||
let distmin = Math.min(); | let distmin = Math.min(); | ||||
let dist; | let dist; | ||||
let bestbase; | let bestbase; | ||||
for (let base of gameState.ai.HQ.baseManagers) | for (let base of gameState.ai.HQ.baseManagers) | ||||
{ | { | ||||
if (base.ID == gameState.ai.HQ.baseManagers[0].ID || exclude && base.ID == exclude) | if (base.ID == gameState.ai.HQ.baseManagers[0].ID || exclude && base.ID == exclude) | ||||
continue; | continue; | ||||
Show All 26 Lines | for (let base of gameState.ai.HQ.baseManagers) | ||||
distmin = dist; | distmin = dist; | ||||
bestbase = base; | bestbase = base; | ||||
} | } | ||||
if (!bestbase && !ent.hasClass("Structure")) | if (!bestbase && !ent.hasClass("Structure")) | ||||
bestbase = gameState.ai.HQ.baseManagers[0]; | bestbase = gameState.ai.HQ.baseManagers[0]; | ||||
return bestbase; | return bestbase; | ||||
}; | }; | ||||
m.getHolder = function(gameState, ent) | PETRA.getHolder = function(gameState, ent) | ||||
{ | { | ||||
for (let holder of gameState.getEntities().values()) | for (let holder of gameState.getEntities().values()) | ||||
{ | { | ||||
if (holder.isGarrisonHolder() && holder.garrisoned().indexOf(ent.id()) !== -1) | if (holder.isGarrisonHolder() && holder.garrisoned().indexOf(ent.id()) !== -1) | ||||
return holder; | return holder; | ||||
} | } | ||||
return undefined; | return undefined; | ||||
}; | }; | ||||
/** return the template of the built foundation if a foundation, otherwise return the entity itself */ | /** return the template of the built foundation if a foundation, otherwise return the entity itself */ | ||||
m.getBuiltEntity = function(gameState, ent) | PETRA.getBuiltEntity = function(gameState, ent) | ||||
{ | { | ||||
if (ent.foundationProgress() !== undefined) | if (ent.foundationProgress() !== undefined) | ||||
return gameState.getBuiltTemplate(ent.templateName()); | return gameState.getBuiltTemplate(ent.templateName()); | ||||
return ent; | return ent; | ||||
}; | }; | ||||
/** | /** | ||||
* return true if it is not worth finishing this building (it would surely decay) | * return true if it is not worth finishing this building (it would surely decay) | ||||
* TODO implement the other conditions | * TODO implement the other conditions | ||||
*/ | */ | ||||
m.isNotWorthBuilding = function(gameState, ent) | PETRA.isNotWorthBuilding = function(gameState, ent) | ||||
{ | { | ||||
if (gameState.ai.HQ.territoryMap.getOwner(ent.position()) !== PlayerID) | if (gameState.ai.HQ.territoryMap.getOwner(ent.position()) !== PlayerID) | ||||
{ | { | ||||
let buildTerritories = ent.buildTerritories(); | let buildTerritories = ent.buildTerritories(); | ||||
if (buildTerritories && (!buildTerritories.length || buildTerritories.length === 1 && buildTerritories[0] === "own")) | if (buildTerritories && (!buildTerritories.length || buildTerritories.length === 1 && buildTerritories[0] === "own")) | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | }; | ||||
/** | /** | ||||
* Check if the straight line between the two positions crosses an enemy territory | * Check if the straight line between the two positions crosses an enemy territory | ||||
*/ | */ | ||||
m.isLineInsideEnemyTerritory = function(gameState, pos1, pos2, step=70) | PETRA.isLineInsideEnemyTerritory = function(gameState, pos1, pos2, step=70) | ||||
{ | { | ||||
let n = Math.floor(Math.sqrt(API3.SquareVectorDistance(pos1, pos2))/step) + 1; | let n = Math.floor(Math.sqrt(API3.SquareVectorDistance(pos1, pos2))/step) + 1; | ||||
let stepx = (pos2[0] - pos1[0]) / n; | let stepx = (pos2[0] - pos1[0]) / n; | ||||
let stepy = (pos2[1] - pos1[1]) / n; | let stepy = (pos2[1] - pos1[1]) / n; | ||||
for (let i = 1; i < n; ++i) | for (let i = 1; i < n; ++i) | ||||
{ | { | ||||
let pos = [pos1[0]+i*stepx, pos1[1]+i*stepy]; | let pos = [pos1[0]+i*stepx, pos1[1]+i*stepy]; | ||||
let owner = gameState.ai.HQ.territoryMap.getOwner(pos); | let owner = gameState.ai.HQ.territoryMap.getOwner(pos); | ||||
if (owner && gameState.isPlayerEnemy(owner)) | if (owner && gameState.isPlayerEnemy(owner)) | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | }; | ||||
m.gatherTreasure = function(gameState, ent, water = false) | PETRA.gatherTreasure = function(gameState, ent, water = false) | ||||
{ | { | ||||
if (!gameState.ai.HQ.treasures.hasEntities()) | if (!gameState.ai.HQ.treasures.hasEntities()) | ||||
return false; | return false; | ||||
if (!ent || !ent.position()) | if (!ent || !ent.position()) | ||||
return false; | return false; | ||||
let rates = ent.resourceGatherRates(); | let rates = ent.resourceGatherRates(); | ||||
if (!rates || !rates.treasure || rates.treasure <= 0) | if (!rates || !rates.treasure || rates.treasure <= 0) | ||||
return false; | return false; | ||||
let treasureFound; | let treasureFound; | ||||
let distmin = Math.min(); | let distmin = Math.min(); | ||||
let access = water ? m.getSeaAccess(gameState, ent) : m.getLandAccess(gameState, ent); | let access = water ? PETRA.getSeaAccess(gameState, ent) : PETRA.getLandAccess(gameState, ent); | ||||
for (let treasure of gameState.ai.HQ.treasures.values()) | for (let treasure of gameState.ai.HQ.treasures.values()) | ||||
{ | { | ||||
if (m.IsSupplyFull(gameState, treasure)) | if (PETRA.IsSupplyFull(gameState, treasure)) | ||||
continue; | continue; | ||||
// let some time for the previous gatherer to reach the treasure before trying again | // let some time for the previous gatherer to reach the treasure before trying again | ||||
let lastGathered = treasure.getMetadata(PlayerID, "lastGathered"); | let lastGathered = treasure.getMetadata(PlayerID, "lastGathered"); | ||||
if (lastGathered && gameState.ai.elapsedTime - lastGathered < 20) | if (lastGathered && gameState.ai.elapsedTime - lastGathered < 20) | ||||
continue; | continue; | ||||
if (!water && access != m.getLandAccess(gameState, treasure)) | if (!water && access != PETRA.getLandAccess(gameState, treasure)) | ||||
continue; | continue; | ||||
if (water && access != m.getSeaAccess(gameState, treasure)) | if (water && access != PETRA.getSeaAccess(gameState, treasure)) | ||||
continue; | continue; | ||||
let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(treasure.position()); | let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(treasure.position()); | ||||
if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) | if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) | ||||
continue; | continue; | ||||
let dist = API3.SquareVectorDistance(ent.position(), treasure.position()); | let dist = API3.SquareVectorDistance(ent.position(), treasure.position()); | ||||
if (dist > 120000 || territoryOwner != PlayerID && dist > 14000) // AI has no LOS, so restrict it a bit | if (dist > 120000 || territoryOwner != PlayerID && dist > 14000) // AI has no LOS, so restrict it a bit | ||||
continue; | continue; | ||||
if (dist > distmin) | if (dist > distmin) | ||||
continue; | continue; | ||||
distmin = dist; | distmin = dist; | ||||
treasureFound = treasure; | treasureFound = treasure; | ||||
} | } | ||||
if (!treasureFound) | if (!treasureFound) | ||||
return false; | return false; | ||||
treasureFound.setMetadata(PlayerID, "lastGathered", gameState.ai.elapsedTime); | treasureFound.setMetadata(PlayerID, "lastGathered", gameState.ai.elapsedTime); | ||||
ent.gather(treasureFound); | ent.gather(treasureFound); | ||||
gameState.ai.HQ.AddTCGatherer(treasureFound.id()); | gameState.ai.HQ.AddTCGatherer(treasureFound.id()); | ||||
ent.setMetadata(PlayerID, "supply", treasureFound.id()); | ent.setMetadata(PlayerID, "supply", treasureFound.id()); | ||||
return true; | return true; | ||||
}; | }; | ||||
m.dumpEntity = function(ent) | PETRA.dumpEntity = function(ent) | ||||
{ | { | ||||
if (!ent) | if (!ent) | ||||
return; | return; | ||||
API3.warn(" >>> id " + ent.id() + " name " + ent.genericName() + " pos " + ent.position() + | API3.warn(" >>> id " + ent.id() + " name " + ent.genericName() + " pos " + ent.position() + | ||||
" state " + ent.unitAIState()); | " state " + ent.unitAIState()); | ||||
API3.warn(" base " + ent.getMetadata(PlayerID, "base") + " >>> role " + ent.getMetadata(PlayerID, "role") + | API3.warn(" base " + ent.getMetadata(PlayerID, "base") + " >>> role " + ent.getMetadata(PlayerID, "role") + | ||||
" subrole " + ent.getMetadata(PlayerID, "subrole")); | " subrole " + ent.getMetadata(PlayerID, "subrole")); | ||||
API3.warn("owner " + ent.owner() + " health " + ent.hitpoints() + " healthMax " + ent.maxHitpoints() + | API3.warn("owner " + ent.owner() + " health " + ent.hitpoints() + " healthMax " + ent.maxHitpoints() + | ||||
" foundationProgress " + ent.foundationProgress()); | " foundationProgress " + ent.foundationProgress()); | ||||
API3.warn(" garrisoning " + ent.getMetadata(PlayerID, "garrisoning") + | API3.warn(" garrisoning " + ent.getMetadata(PlayerID, "garrisoning") + | ||||
" garrisonHolder " + ent.getMetadata(PlayerID, "garrisonHolder") + | " garrisonHolder " + ent.getMetadata(PlayerID, "garrisonHolder") + | ||||
" plan " + ent.getMetadata(PlayerID, "plan") + " transport " + ent.getMetadata(PlayerID, "transport")); | " plan " + ent.getMetadata(PlayerID, "plan") + " transport " + ent.getMetadata(PlayerID, "transport")); | ||||
API3.warn(" stance " + ent.getStance() + " transporter " + ent.getMetadata(PlayerID, "transporter") + | API3.warn(" stance " + ent.getStance() + " transporter " + ent.getMetadata(PlayerID, "transporter") + | ||||
" gather-type " + ent.getMetadata(PlayerID, "gather-type") + | " gather-type " + ent.getMetadata(PlayerID, "gather-type") + | ||||
" target-foundation " + ent.getMetadata(PlayerID, "target-foundation") + | " target-foundation " + ent.getMetadata(PlayerID, "target-foundation") + | ||||
" PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy")); | " PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy")); | ||||
}; | }; | ||||
return m; | |||||
}(PETRA); |
Wildfire Games · Phabricator