Differential D2103 Diff 9534 ps/trunk/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
var PETRA = function(m) | |||||
{ | |||||
/** | /** | ||||
* Manage the garrisonHolders | * Manage the garrisonHolders | ||||
* When a unit is ordered to garrison, it must be done through this.garrison() function so that | * When a unit is ordered to garrison, it must be done through this.garrison() function so that | ||||
* an object in this.holders is created. This object contains an array with the entities | * an object in this.holders is created. This object contains an array with the entities | ||||
* in the process of being garrisoned. To have all garrisoned units, we must add those in holder.garrisoned(). | * in the process of being garrisoned. To have all garrisoned units, we must add those in holder.garrisoned(). | ||||
* Futhermore garrison units have a metadata garrisonType describing its reason (protection, transport, ...) | * Futhermore garrison units have a metadata garrisonType describing its reason (protection, transport, ...) | ||||
*/ | */ | ||||
m.GarrisonManager = function(Config) | PETRA.GarrisonManager = function(Config) | ||||
{ | { | ||||
this.Config = Config; | this.Config = Config; | ||||
this.holders = new Map(); | this.holders = new Map(); | ||||
this.decayingStructures = new Map(); | this.decayingStructures = new Map(); | ||||
}; | }; | ||||
m.GarrisonManager.prototype.update = function(gameState, events) | PETRA.GarrisonManager.prototype.update = function(gameState, events) | ||||
{ | { | ||||
// First check for possible upgrade of a structure | // First check for possible upgrade of a structure | ||||
for (let evt of events.EntityRenamed) | for (let evt of events.EntityRenamed) | ||||
{ | { | ||||
for (let id of this.holders.keys()) | for (let id of this.holders.keys()) | ||||
{ | { | ||||
if (id != evt.entity) | if (id != evt.entity) | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | for (let j = 0; j < list.length; ++j) | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (gameState.ai.Config.debug > 0) | if (gameState.ai.Config.debug > 0) | ||||
{ | { | ||||
API3.warn("Petra garrison error: unit " + ent.id() + " (" + ent.genericName() + | API3.warn("Petra garrison error: unit " + ent.id() + " (" + ent.genericName() + | ||||
") is expected to garrison in " + id + " (" + holder.genericName() + | ") is expected to garrison in " + id + " (" + holder.genericName() + | ||||
"), but has no such garrison order " + uneval(ent.unitAIOrderData())); | "), but has no such garrison order " + uneval(ent.unitAIOrderData())); | ||||
m.dumpEntity(ent); | PETRA.dumpEntity(ent); | ||||
} | } | ||||
list.splice(j--, 1); | list.splice(j--, 1); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!holder.position()) // could happen with siege unit inside a ship | if (!holder.position()) // could happen with siege unit inside a ship | ||||
Show All 19 Lines | if (gameState.ai.elapsedTime - holder.getMetadata(PlayerID, "holderTimeUpdate") > 3) | ||||
continue; | continue; | ||||
if (!ent.position()) | if (!ent.position()) | ||||
continue; | continue; | ||||
let dist = API3.SquareVectorDistance(ent.position(), holder.position()); | let dist = API3.SquareVectorDistance(ent.position(), holder.position()); | ||||
if (dist > range*range) | if (dist > range*range) | ||||
continue; | continue; | ||||
if (ent.hasClass("Structure")) | if (ent.hasClass("Structure")) | ||||
around.defenseStructure = true; | around.defenseStructure = true; | ||||
else if (m.isSiegeUnit(ent)) | else if (PETRA.isSiegeUnit(ent)) | ||||
{ | { | ||||
if (ent.attackTypes().indexOf("Melee") !== -1) | if (ent.attackTypes().indexOf("Melee") !== -1) | ||||
around.meleeSiege = true; | around.meleeSiege = true; | ||||
else | else | ||||
around.rangeSiege = true; | around.rangeSiege = true; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
Show All 37 Lines | for (let [id, gmin] of this.decayingStructures.entries()) | ||||
if (!ent || ent.owner() !== PlayerID) | if (!ent || ent.owner() !== PlayerID) | ||||
this.decayingStructures.delete(id); | this.decayingStructures.delete(id); | ||||
else if (this.numberOfGarrisonedUnits(ent) < gmin) | else if (this.numberOfGarrisonedUnits(ent) < gmin) | ||||
gameState.ai.HQ.defenseManager.garrisonUnitsInside(gameState, ent, { "min": gmin, "type": "decay" }); | gameState.ai.HQ.defenseManager.garrisonUnitsInside(gameState, ent, { "min": gmin, "type": "decay" }); | ||||
} | } | ||||
}; | }; | ||||
/** TODO should add the units garrisoned inside garrisoned units */ | /** TODO should add the units garrisoned inside garrisoned units */ | ||||
m.GarrisonManager.prototype.numberOfGarrisonedUnits = function(holder) | PETRA.GarrisonManager.prototype.numberOfGarrisonedUnits = function(holder) | ||||
{ | { | ||||
if (!this.holders.has(holder.id())) | if (!this.holders.has(holder.id())) | ||||
return holder.garrisoned().length; | return holder.garrisoned().length; | ||||
return holder.garrisoned().length + this.holders.get(holder.id()).list.length; | return holder.garrisoned().length + this.holders.get(holder.id()).list.length; | ||||
}; | }; | ||||
m.GarrisonManager.prototype.allowMelee = function(holder) | PETRA.GarrisonManager.prototype.allowMelee = function(holder) | ||||
{ | { | ||||
if (!this.holders.has(holder.id())) | if (!this.holders.has(holder.id())) | ||||
return undefined; | return undefined; | ||||
return this.holders.get(holder.id()).allowMelee; | return this.holders.get(holder.id()).allowMelee; | ||||
}; | }; | ||||
/** This is just a pre-garrison state, while the entity walk to the garrison holder */ | /** This is just a pre-garrison state, while the entity walk to the garrison holder */ | ||||
m.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type) | PETRA.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type) | ||||
{ | { | ||||
if (this.numberOfGarrisonedUnits(holder) >= holder.garrisonMax() || !ent.canGarrison()) | if (this.numberOfGarrisonedUnits(holder) >= holder.garrisonMax() || !ent.canGarrison()) | ||||
return; | return; | ||||
this.registerHolder(gameState, holder); | this.registerHolder(gameState, holder); | ||||
this.holders.get(holder.id()).list.push(ent.id()); | this.holders.get(holder.id()).list.push(ent.id()); | ||||
if (gameState.ai.Config.debug > 2) | if (gameState.ai.Config.debug > 2) | ||||
Show All 14 Lines | |||||
}; | }; | ||||
/** | /** | ||||
This is the end of the pre-garrison state, either because the entity is really garrisoned | This is the end of the pre-garrison state, either because the entity is really garrisoned | ||||
or because it has changed its order (i.e. because the garrisonHolder was destroyed) | or because it has changed its order (i.e. because the garrisonHolder was destroyed) | ||||
This function is for internal use inside garrisonManager. From outside, you should also update | This function is for internal use inside garrisonManager. From outside, you should also update | ||||
the holder and then using cancelGarrison should be the preferred solution | the holder and then using cancelGarrison should be the preferred solution | ||||
*/ | */ | ||||
m.GarrisonManager.prototype.leaveGarrison = function(ent) | PETRA.GarrisonManager.prototype.leaveGarrison = function(ent) | ||||
{ | { | ||||
ent.setMetadata(PlayerID, "subrole", undefined); | ent.setMetadata(PlayerID, "subrole", undefined); | ||||
if (ent.getMetadata(PlayerID, "plan") === -2) | if (ent.getMetadata(PlayerID, "plan") === -2) | ||||
ent.setMetadata(PlayerID, "plan", -1); | ent.setMetadata(PlayerID, "plan", -1); | ||||
else | else | ||||
ent.setMetadata(PlayerID, "plan", undefined); | ent.setMetadata(PlayerID, "plan", undefined); | ||||
ent.setMetadata(PlayerID, "garrisonHolder", undefined); | ent.setMetadata(PlayerID, "garrisonHolder", undefined); | ||||
}; | }; | ||||
/** Cancel a pre-garrison state */ | /** Cancel a pre-garrison state */ | ||||
m.GarrisonManager.prototype.cancelGarrison = function(ent) | PETRA.GarrisonManager.prototype.cancelGarrison = function(ent) | ||||
{ | { | ||||
ent.stopMoving(); | ent.stopMoving(); | ||||
this.leaveGarrison(ent); | this.leaveGarrison(ent); | ||||
let holderId = ent.getMetadata(PlayerID, "garrisonHolder"); | let holderId = ent.getMetadata(PlayerID, "garrisonHolder"); | ||||
if (!holderId || !this.holders.has(holderId)) | if (!holderId || !this.holders.has(holderId)) | ||||
return; | return; | ||||
let list = this.holders.get(holderId).list; | let list = this.holders.get(holderId).list; | ||||
let index = list.indexOf(ent.id()); | let index = list.indexOf(ent.id()); | ||||
if (index !== -1) | if (index !== -1) | ||||
list.splice(index, 1); | list.splice(index, 1); | ||||
}; | }; | ||||
m.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around) | PETRA.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around) | ||||
{ | { | ||||
switch (ent.getMetadata(PlayerID, "garrisonType")) | switch (ent.getMetadata(PlayerID, "garrisonType")) | ||||
{ | { | ||||
case 'force': // force the ungarrisoning | case 'force': // force the ungarrisoning | ||||
return false; | return false; | ||||
case 'trade': // trader garrisoned in ship | case 'trade': // trader garrisoned in ship | ||||
return true; | return true; | ||||
case 'protection': // hurt unit for healing or infantry for defense | case 'protection': // hurt unit for healing or infantry for defense | ||||
if (holder.buffHeal() && ent.isHealable() && ent.healthLevel() < this.Config.garrisonHealthLevel.high) | if (holder.buffHeal() && ent.isHealable() && ent.healthLevel() < this.Config.garrisonHealthLevel.high) | ||||
return true; | return true; | ||||
let capture = ent.capturePoints(); | let capture = ent.capturePoints(); | ||||
if (capture && capture[PlayerID] / capture.reduce((a, b) => a + b) < 0.8) | if (capture && capture[PlayerID] / capture.reduce((a, b) => a + b) < 0.8) | ||||
return true; | return true; | ||||
if (MatchesClassList(ent.classes(), holder.getGarrisonArrowClasses())) | if (MatchesClassList(ent.classes(), holder.getGarrisonArrowClasses())) | ||||
{ | { | ||||
if (around.unit || around.defenseStructure) | if (around.unit || around.defenseStructure) | ||||
return true; | return true; | ||||
if (around.meleeSiege || around.rangeSiege) | if (around.meleeSiege || around.rangeSiege) | ||||
return ent.attackTypes().indexOf("Melee") === -1 || ent.healthLevel() < this.Config.garrisonHealthLevel.low; | return ent.attackTypes().indexOf("Melee") === -1 || ent.healthLevel() < this.Config.garrisonHealthLevel.low; | ||||
return false; | return false; | ||||
} | } | ||||
if (ent.attackTypes() && ent.attackTypes().indexOf("Melee") !== -1) | if (ent.attackTypes() && ent.attackTypes().indexOf("Melee") !== -1) | ||||
return false; | return false; | ||||
if (around.unit) | if (around.unit) | ||||
return ent.hasClass("Support") || m.isSiegeUnit(ent); // only ranged siege here and below as melee siege already released above | return ent.hasClass("Support") || PETRA.isSiegeUnit(ent); // only ranged siege here and below as melee siege already released above | ||||
if (m.isSiegeUnit(ent)) | if (PETRA.isSiegeUnit(ent)) | ||||
return around.meleeSiege; | return around.meleeSiege; | ||||
return holder.buffHeal() && ent.needsHeal(); | return holder.buffHeal() && ent.needsHeal(); | ||||
case 'decay': | case 'decay': | ||||
return this.decayingStructures.has(holder.id()); | return this.decayingStructures.has(holder.id()); | ||||
case 'emergency': // f.e. hero in regicide mode | case 'emergency': // f.e. hero in regicide mode | ||||
if (holder.buffHeal() && ent.isHealable() && ent.healthLevel() < this.Config.garrisonHealthLevel.high) | if (holder.buffHeal() && ent.isHealable() && ent.healthLevel() < this.Config.garrisonHealthLevel.high) | ||||
return true; | return true; | ||||
if (around.unit || around.defenseStructure || around.meleeSiege || | if (around.unit || around.defenseStructure || around.meleeSiege || | ||||
around.rangeSiege && ent.healthLevel() < this.Config.garrisonHealthLevel.high) | around.rangeSiege && ent.healthLevel() < this.Config.garrisonHealthLevel.high) | ||||
return true; | return true; | ||||
return holder.buffHeal() && ent.needsHeal(); | return holder.buffHeal() && ent.needsHeal(); | ||||
default: | default: | ||||
if (ent.getMetadata(PlayerID, "onBoard") === "onBoard") // transport is not (yet ?) managed by garrisonManager | if (ent.getMetadata(PlayerID, "onBoard") === "onBoard") // transport is not (yet ?) managed by garrisonManager | ||||
return true; | return true; | ||||
API3.warn("unknown type in garrisonManager " + ent.getMetadata(PlayerID, "garrisonType") + | API3.warn("unknown type in garrisonManager " + ent.getMetadata(PlayerID, "garrisonType") + | ||||
" for " + ent.genericName() + " id " + ent.id() + | " for " + ent.genericName() + " id " + ent.id() + | ||||
" inside " + holder.genericName() + " id " + holder.id()); | " inside " + holder.genericName() + " id " + holder.id()); | ||||
ent.setMetadata(PlayerID, "garrisonType", "protection"); | ent.setMetadata(PlayerID, "garrisonType", "protection"); | ||||
return true; | return true; | ||||
} | } | ||||
}; | }; | ||||
/** Add this holder in the list managed by the garrisonManager */ | /** Add this holder in the list managed by the garrisonManager */ | ||||
m.GarrisonManager.prototype.registerHolder = function(gameState, holder) | PETRA.GarrisonManager.prototype.registerHolder = function(gameState, holder) | ||||
{ | { | ||||
if (this.holders.has(holder.id())) // already registered | if (this.holders.has(holder.id())) // already registered | ||||
return; | return; | ||||
this.holders.set(holder.id(), { "list": [], "allowMelee": true }); | this.holders.set(holder.id(), { "list": [], "allowMelee": true }); | ||||
holder.setMetadata(PlayerID, "holderTimeUpdate", gameState.ai.elapsedTime); | holder.setMetadata(PlayerID, "holderTimeUpdate", gameState.ai.elapsedTime); | ||||
}; | }; | ||||
/** | /** | ||||
* Garrison units in decaying structures to stop their decay | * Garrison units in decaying structures to stop their decay | ||||
* do it only for structures useful for defense, except if we are expanding (justCaptured=true) | * do it only for structures useful for defense, except if we are expanding (justCaptured=true) | ||||
* in which case we also do it for structures useful for unit trainings (TODO only Barracks are done) | * in which case we also do it for structures useful for unit trainings (TODO only Barracks are done) | ||||
*/ | */ | ||||
m.GarrisonManager.prototype.addDecayingStructure = function(gameState, entId, justCaptured) | PETRA.GarrisonManager.prototype.addDecayingStructure = function(gameState, entId, justCaptured) | ||||
{ | { | ||||
if (this.decayingStructures.has(entId)) | if (this.decayingStructures.has(entId)) | ||||
return true; | return true; | ||||
let ent = gameState.getEntityById(entId); | let ent = gameState.getEntityById(entId); | ||||
if (!ent || !(ent.hasClass("Barracks") && justCaptured) && !ent.hasDefensiveFire()) | if (!ent || !(ent.hasClass("Barracks") && justCaptured) && !ent.hasDefensiveFire()) | ||||
return false; | return false; | ||||
if (!ent.territoryDecayRate() || !ent.garrisonRegenRate()) | if (!ent.territoryDecayRate() || !ent.garrisonRegenRate()) | ||||
return false; | return false; | ||||
let gmin = Math.ceil((ent.territoryDecayRate() - ent.defaultRegenRate()) / ent.garrisonRegenRate()); | let gmin = Math.ceil((ent.territoryDecayRate() - ent.defaultRegenRate()) / ent.garrisonRegenRate()); | ||||
this.decayingStructures.set(entId, gmin); | this.decayingStructures.set(entId, gmin); | ||||
return true; | return true; | ||||
}; | }; | ||||
m.GarrisonManager.prototype.removeDecayingStructure = function(entId) | PETRA.GarrisonManager.prototype.removeDecayingStructure = function(entId) | ||||
{ | { | ||||
if (!this.decayingStructures.has(entId)) | if (!this.decayingStructures.has(entId)) | ||||
return; | return; | ||||
this.decayingStructures.delete(entId); | this.decayingStructures.delete(entId); | ||||
}; | }; | ||||
m.GarrisonManager.prototype.Serialize = function() | PETRA.GarrisonManager.prototype.Serialize = function() | ||||
{ | { | ||||
return { "holders": this.holders, "decayingStructures": this.decayingStructures }; | return { "holders": this.holders, "decayingStructures": this.decayingStructures }; | ||||
}; | }; | ||||
m.GarrisonManager.prototype.Deserialize = function(data) | PETRA.GarrisonManager.prototype.Deserialize = function(data) | ||||
{ | { | ||||
for (let key in data) | for (let key in data) | ||||
this[key] = data[key]; | this[key] = data[key]; | ||||
}; | }; | ||||
return m; | |||||
}(PETRA); |
Wildfire Games · Phabricator