Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/ai/common-api/entity.js
Show First 20 Lines • Show All 231 Lines • ▼ Show 20 Lines | if (!this.get("Attack/" + type)) | ||||
return undefined; | return undefined; | ||||
return { | return { | ||||
"max": +this.get("Attack/" + type +"/MaxRange"), | "max": +this.get("Attack/" + type +"/MaxRange"), | ||||
"min": +(this.get("Attack/" + type +"/MinRange") || 0) | "min": +(this.get("Attack/" + type +"/MinRange") || 0) | ||||
}; | }; | ||||
}, | }, | ||||
"attackStrengths": function(type) { | "attackStrengths": function(type, againstClassList = [], civ = undefined) { | ||||
let attackDamageTypes = this.get("Attack/" + type + "/Damage"); | // TODO: StatusEffect. | ||||
if (!attackDamageTypes) | let strengths = {}; | ||||
return undefined; | |||||
let damage = {}; | const multiplier = this.getMultiplierAgainst(type, againstClassList, civ); | ||||
for (let damageType in attackDamageTypes) | const attackStrengthsDamage = this.get("Attack/" + type + "/Damage"); | ||||
damage[damageType] = +attackDamageTypes[damageType]; | if (attackStrengthsDamage) | ||||
{ | |||||
strengths.Damage = {}; | |||||
for (let damageType in attackStrengthsDamage) | |||||
strengths.Damage[damageType] = +attackStrengthsDamage[damageType] * multiplier; | |||||
} | |||||
return damage; | strengths.Capture = +this.get("Attack/" + type + "/Capture") * multiplier || 0; | ||||
return strengths; | |||||
}, | }, | ||||
"captureStrength": function() { | "captureStrength": function(againstClassList = [], civ = undefined) { | ||||
if (!this.get("Attack/Capture")) | let strength = 0; | ||||
return undefined; | for (let type in this.get("Attack")) | ||||
strength = Math.max(strength, +(this.get("Attack/"+ type + "/Capture") || 0) * this.getMultiplierAgainst(type, againstClassList, civ)); | |||||
return +this.get("Attack/Capture/Capture") || 0; | return strength; | ||||
}, | }, | ||||
"attackTimes": function(type) { | "attackTimes": function(type) { | ||||
if (!this.get("Attack/" + type)) | if (!this.get("Attack/" + type)) | ||||
return undefined; | return undefined; | ||||
return { | return { | ||||
"prepare": +(this.get("Attack/" + type + "/PrepareTime") || 0), | "prepare": +(this.get("Attack/" + type + "/PrepareTime") || 0), | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | for (let type in attack) | ||||
let bonusClasses = this.get("Attack/" + type + "/Bonuses/" + b + "/Classes"); | let bonusClasses = this.get("Attack/" + type + "/Bonuses/" + b + "/Classes"); | ||||
if (bonusClasses) | if (bonusClasses) | ||||
mcounter.concat(bonusClasses.split(" ")); | mcounter.concat(bonusClasses.split(" ")); | ||||
} | } | ||||
} | } | ||||
return target.hasClasses(mcounter); | return target.hasClasses(mcounter); | ||||
}, | }, | ||||
// returns, if it exists, the multiplier from each attack against a given class | // Returns the multiplier from each attack against a given class and civ. | ||||
"getMultiplierAgainst": function(type, againstClass) { | "getMultiplierAgainst": function(type, againstClassList, civ) { | ||||
if (!this.get("Attack/" + type +"")) | let multiplier = 1; | ||||
return undefined; | if (!this.get("Attack/" + type)) | ||||
return multiplier; | |||||
Freagarach: Is this needed? | |||||
Done Inline ActionsI basicly merged two functions here: the original getMutliplierAgainst and the GetAttackBonus from entityExtend (and this makes some code simpler and better). You are right however that this can/should be split of this patch (was on my radar, but didn't get to that yet) bb: I basicly merged two functions here: the original getMutliplierAgainst and the GetAttackBonus… | |||||
let bonuses = this.get("Attack/" + type + "/Bonuses"); | const bonuses = this.get("Attack/" + type + "/Bonuses"); | ||||
if (bonuses) | if (bonuses) | ||||
for (const bonus in bonuses) | |||||
{ | { | ||||
for (let b in bonuses) | if (civ && bonus.Civ && civ != bonus.Civ) | ||||
{ | |||||
let bonusClasses = this.get("Attack/" + type + "/Bonuses/" + b + "/Classes"); | |||||
if (!bonusClasses) | |||||
continue; | continue; | ||||
for (let bcl of bonusClasses.split(" ")) | |||||
if (bcl == againstClass) | const bonusClasses = this.get("Attack/" + type + "/Bonuses/" + bonus + "/Classes"); | ||||
return +this.get("Attack/" + type + "/Bonuses/" + b + "/Multiplier"); | if (MatchesClassList(againstClassList, this.get("Attack/" + type + "/Bonuses/" + bonus + "/Classes"))) | ||||
} | multiplier *= +this.get("Attack/" + type + "/Bonuses/" + bonus + "/Multiplier"); | ||||
} | } | ||||
return 1; | |||||
return multiplier; | |||||
}, | }, | ||||
"buildableEntities": function(civ) { | "buildableEntities": function(civ) { | ||||
let templates = this.get("Builder/Entities/_string"); | let templates = this.get("Builder/Entities/_string"); | ||||
if (!templates) | if (!templates) | ||||
return []; | return []; | ||||
return templates.replace(/\{native\}/g, this.civ()).replace(/\{civ\}/g, civ).split(/\s+/); | return templates.replace(/\{native\}/g, this.civ()).replace(/\{civ\}/g, civ).split(/\s+/); | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 196 Lines • ▼ Show 20 Lines | "canGather": function(type) { | ||||
return false; | return false; | ||||
}, | }, | ||||
"isGarrisonHolder": function() { return this.get("GarrisonHolder") !== undefined; }, | "isGarrisonHolder": function() { return this.get("GarrisonHolder") !== undefined; }, | ||||
"isTurretHolder": function() { return this.get("TurretHolder") !== undefined; }, | "isTurretHolder": function() { return this.get("TurretHolder") !== undefined; }, | ||||
/** | /** | ||||
* returns true if the tempalte can capture the given target entity | * returns true if the template can capture the given target entity | ||||
* if no target is given, returns true if the template has the Capture attack | * if no target is given, returns true if the template has the Capture attack | ||||
*/ | */ | ||||
"canCapture": function(target) | "canCapture": function(target) | ||||
{ | { | ||||
if (!this.get("Attack/Capture")) | const attack = this.get("Attack") | ||||
if (!attack) | |||||
return false; | |||||
const captureTypes = Object.keys(attack).filter(type => !this.get("Attack/" + type + "/Capture")) | |||||
if (!captureTypes.length) | |||||
return false; | return false; | ||||
if (!target) | if (!target) | ||||
return true; | return true; | ||||
if (!target.get("Capturable")) | if (!target.get("Capturable")) | ||||
return false; | return false; | ||||
let restrictedClasses = this.get("Attack/Capture/RestrictedClasses/_string"); | for (const type of captureTypes) | ||||
return !restrictedClasses || !target.hasClasses(restrictedClasses); | { | ||||
const restrictedClasses = this.get("Attack/" + type + "/RestrictedClasses/_string"); | |||||
if (!restrictedClasses || !target.hasClasses(restrictedClasses)) | |||||
return true; | |||||
} | |||||
return false; | |||||
}, | }, | ||||
Not Done Inline ActionsPerhaps change the order a bit? Freagarach: Perhaps change the order a bit? | |||||
Done Inline ActionsChange what order? bb: Change what order? | |||||
Not Done Inline ActionsE.g., if we check for !target first we don't need to calculate the attacks. Freagarach: E.g., if we check for `!target` first we don't need to calculate the attacks. | |||||
Done Inline Actionsk bb: k | |||||
Done Inline ActionsIf fact, no, the comment tells us it is designed to only return true in case there is no target when we can capture bb: If fact, no, the comment tells us it is designed to only return true in case there is no target… | |||||
"isCapturable": function() { return this.get("Capturable") !== undefined; }, | "isCapturable": function() { return this.get("Capturable") !== undefined; }, | ||||
"canGuard": function() { return this.get("UnitAI/CanGuard") === "true"; }, | "canGuard": function() { return this.get("UnitAI/CanGuard") === "true"; }, | ||||
"canGarrison": function() { return "Garrisonable" in this._template; }, | "canGarrison": function() { return "Garrisonable" in this._template; }, | ||||
"canOccupyTurret": function() { return "Turretable" in this._template; }, | "canOccupyTurret": function() { return "Turretable" in this._template; }, | ||||
▲ Show 20 Lines • Show All 189 Lines • ▼ Show 20 Lines | m.Entity = m.Class({ | ||||
"canAttackClass": function(aClass) | "canAttackClass": function(aClass) | ||||
{ | { | ||||
let attack = this.get("Attack"); | let attack = this.get("Attack"); | ||||
if (!attack) | if (!attack) | ||||
return false; | return false; | ||||
for (let type in attack) | for (let type in attack) | ||||
{ | { | ||||
if (type == "Slaughter") | |||||
continue; | |||||
let restrictedClasses = this.get("Attack/" + type + "/RestrictedClasses/_string"); | let restrictedClasses = this.get("Attack/" + type + "/RestrictedClasses/_string"); | ||||
if (!restrictedClasses || !MatchesClassList([aClass], restrictedClasses)) | if (!restrictedClasses || !MatchesClassList([aClass], restrictedClasses)) | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}, | }, | ||||
/** | /** | ||||
* Derived from Attack.js' similary named function. | * Derived from Attack.js' similary named function. | ||||
* TODO: more general wantedTypes, see attack.js. | |||||
* @return {boolean} - Whether an entity can attack a given target. | * @return {boolean} - Whether an entity can attack a given target. | ||||
*/ | */ | ||||
"canAttackTarget": function(target, allowCapture) | "canAttackTarget": function(target, mustBeInRange = false, ignoreAttackEffects = {}, wantedTypes = [], projectile = undefined) | ||||
{ | { | ||||
let attackTypes = this.get("Attack"); | if (target.isInvulnerable()) | ||||
if (!attackTypes) | |||||
return false; | return false; | ||||
let canCapture = allowCapture && this.canCapture(target); | let types = Object.keys(this.get("Attack")); | ||||
let health = target.get("Health"); | if (wantedTypes.length) | ||||
if (!health) | types = types.filter(type => wantedTypes.includes(type)); | ||||
return canCapture; | |||||
// TODO: Care about the range. | |||||
for (const type of types) | |||||
{ | |||||
const attackStrengths = this.attackStrengths(type); | |||||
if (Object.keys(attackStrengths).every(attackEffect => | |||||
!attackStrengths[attackEffect] || | |||||
// TODO: Storing and getting the component name from the json shouldn't be done. | |||||
!target.get(AttackEffects.GetReceiverFromCode(attackEffect).cmp) || | |||||
ignoreAttackEffects[attackEffect])) | |||||
continue; | |||||
for (let type in attackTypes) | const templateProjectile = this.get("Attack/" + type + "/Projectile"); | ||||
{ | if ((projectile == "required" && !templateProjectile) || (projectile == "disallowed" && templateProjectile)) | ||||
if (type == "Capture" ? !canCapture : target.isInvulnerable()) | |||||
continue; | continue; | ||||
let restrictedClasses = this.get("Attack/" + type + "/RestrictedClasses/_string"); | const restrictedClasses = this.get("Attack/" + type + "/RestrictedClasses/_string"); | ||||
if (!restrictedClasses || !target.hasClasses(restrictedClasses)) | if (!restrictedClasses || !target.hasClasses(restrictedClasses)) | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}, | }, | ||||
"move": function(x, z, queued = false, pushFront = false) { | "move": function(x, z, queued = false, pushFront = false) { | ||||
Engine.PostCommand(PlayerID, { "type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued, "pushFront": pushFront }); | Engine.PostCommand(PlayerID, { "type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued, "pushFront": pushFront }); | ||||
return this; | return this; | ||||
}, | }, | ||||
"moveToRange": function(x, z, min, max, queued = false, pushFront = false) { | "moveToRange": function(x, z, min, max, queued = false, pushFront = false) { | ||||
Engine.PostCommand(PlayerID, { "type": "walk-to-range", "entities": [this.id()], "x": x, "z": z, "min": min, "max": max, "queued": queued, "pushFront": pushFront }); | Engine.PostCommand(PlayerID, { "type": "walk-to-range", "entities": [this.id()], "x": x, "z": z, "min": min, "max": max, "queued": queued, "pushFront": pushFront }); | ||||
return this; | return this; | ||||
}, | }, | ||||
"attackMove": function(x, z, targetClasses, allowCapture = true, queued = false, pushFront = false) { | "attackMove": function(x, z, targetClasses, ignoreAttackEffects = {}, prefAttackTypes = [], projectile = null, queued = false, pushFront = false) { | ||||
Engine.PostCommand(PlayerID, { "type": "attack-walk", "entities": [this.id()], "x": x, "z": z, "targetClasses": targetClasses, "allowCapture": allowCapture, "queued": queued, "pushFront": pushFront }); | Engine.PostCommand(PlayerID, { | ||||
"type": "attack-walk", | |||||
"entities": [this.id()], | |||||
"x": x, | |||||
"z": z, | |||||
"targetClasses": targetClasses, | |||||
"ignoreAttackEffects": ignoreAttackEffects, | |||||
"prefAttackTypes": prefAttackTypes, | |||||
"projectile": projectile, | |||||
"queued": queued, | |||||
"pushFront": pushFront | |||||
}); | |||||
return this; | return this; | ||||
}, | }, | ||||
// violent, aggressive, defensive, passive, standground | // violent, aggressive, defensive, passive, standground | ||||
"setStance": function(stance) { | "setStance": function(stance) { | ||||
if (this.getStance() === undefined) | if (this.getStance() === undefined) | ||||
return undefined; | return undefined; | ||||
Engine.PostCommand(PlayerID, { "type": "stance", "entities": [this.id()], "name": stance}); | Engine.PostCommand(PlayerID, { "type": "stance", "entities": [this.id()], "name": stance}); | ||||
Show All 24 Lines | "garrison": function(target, queued = false, pushFront = false) { | ||||
return this; | return this; | ||||
}, | }, | ||||
"occupy-turret": function(target, queued = false, pushFront = false) { | "occupy-turret": function(target, queued = false, pushFront = false) { | ||||
Engine.PostCommand(PlayerID, { "type": "occupy-turret", "entities": [this.id()], "target": target.id(), "queued": queued, "pushFront": pushFront }); | Engine.PostCommand(PlayerID, { "type": "occupy-turret", "entities": [this.id()], "target": target.id(), "queued": queued, "pushFront": pushFront }); | ||||
return this; | return this; | ||||
}, | }, | ||||
"attack": function(unitId, allowCapture = true, queued = false, pushFront = false) { | "attack": function(unitId, ignoreAttackEffects = {}, prefAttackTypes = [], projectile = null, queued = false, pushFront = false) { | ||||
Engine.PostCommand(PlayerID, { "type": "attack", "entities": [this.id()], "target": unitId, "allowCapture": allowCapture, "queued": queued, "pushFront": pushFront }); | Engine.PostCommand(PlayerID, { | ||||
"type": "attack", | |||||
"entities": [this.id()], | |||||
"target": unitId, | |||||
"ignoreAttackEffects": ignoreAttackEffects, | |||||
"prefAttackTypes": prefAttackTypes, | |||||
"projectile": projectile, | |||||
"queued": queued, | |||||
"pushFront": pushFront | |||||
}); | |||||
return this; | return this; | ||||
}, | }, | ||||
"collectTreasure": function(target, queued = false, pushFront = false) { | "collectTreasure": function(target, queued = false, pushFront = false) { | ||||
Engine.PostCommand(PlayerID, { | Engine.PostCommand(PlayerID, { | ||||
"type": "collect-treasure", | "type": "collect-treasure", | ||||
"entities": [this.id()], | "entities": [this.id()], | ||||
"target": target.id(), | "target": target.id(), | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | m.Entity = m.Class({ | ||||
}, | }, | ||||
"barter": function(buyType, sellType, amount) { | "barter": function(buyType, sellType, amount) { | ||||
Engine.PostCommand(PlayerID, { "type": "barter", "sell": sellType, "buy": buyType, "amount": amount }); | Engine.PostCommand(PlayerID, { "type": "barter", "sell": sellType, "buy": buyType, "amount": amount }); | ||||
return this; | return this; | ||||
}, | }, | ||||
"tradeRoute": function(target, source) { | "tradeRoute": function(target, source) { | ||||
Engine.PostCommand(PlayerID, { "type": "setup-trade-route", "entities": [this.id()], "target": target.id(), "source": source.id(), "route": undefined, "queued": false, "pushFront": false }); | Engine.PostCommand(PlayerID, { "type": "setup-trade-route", "entities": [this.id()], "target": target.id(), "source": source.id(), "route": null, "queued": false, "pushFront": false }); | ||||
return this; | return this; | ||||
}, | }, | ||||
"setRallyPoint": function(target, command) { | "setRallyPoint": function(target, command) { | ||||
let data = { "command": command, "target": target.id() }; | let data = { "command": command, "target": target.id() }; | ||||
Engine.PostCommand(PlayerID, { "type": "set-rallypoint", "entities": [this.id()], "x": target.position()[0], "z": target.position()[1], "data": data }); | Engine.PostCommand(PlayerID, { "type": "set-rallypoint", "entities": [this.id()], "x": target.position()[0], "z": target.position()[1], "data": data }); | ||||
return this; | return this; | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 91 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
Is this needed?