Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 2,053 Lines • ▼ Show 20 Lines | "GATHER": { | ||||
if ((!cmpMirage || !cmpMirage.Mirages(IID_ResourceSupply)) && | if ((!cmpMirage || !cmpMirage.Mirages(IID_ResourceSupply)) && | ||||
(!cmpSupply || !cmpSupply.AddGatherer(cmpOwnership.GetOwner(), this.entity)) || | (!cmpSupply || !cmpSupply.AddGatherer(cmpOwnership.GetOwner(), this.entity)) || | ||||
!this.MoveTo(this.order.data, IID_ResourceGatherer)) | !this.MoveTo(this.order.data, IID_ResourceGatherer)) | ||||
{ | { | ||||
// The GATHERING timer will handle finding a valid resource. | // The GATHERING timer will handle finding a valid resource. | ||||
this.SetNextState("GATHERING"); | this.SetNextState("GATHERING"); | ||||
return true; | return true; | ||||
} | } | ||||
this.SelectAnimation("move"); | this.SelectAnimation("move"); | ||||
return false; | return false; | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// If we failed, the GATHERING timer will handle finding a valid resource. | // The GATHERING timer will handle finding a valid resource. | ||||
this.SetNextState("GATHERING"); | this.SetNextState("GATHERING"); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
if (!this.gatheringTarget) | if (!this.gatheringTarget) | ||||
Show All 19 Lines | "GATHER": { | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// If we failed, the GATHERING timer will handle finding a valid resource. | // If we failed, the GATHERING timer will handle finding a valid resource. | ||||
if (msg.error || this.CheckRange(this.order.data)) | |||||
this.SetNextState("GATHERING"); | this.SetNextState("GATHERING"); | ||||
}, | }, | ||||
}, | }, | ||||
"GATHERING": { | "GATHERING": { | ||||
"enter": function() { | "enter": function() { | ||||
this.gatheringTarget = this.order.data.target || INVALID_ENTITY; // deleted in "leave". | this.gatheringTarget = this.order.data.target || INVALID_ENTITY; // deleted in "leave". | ||||
// Check if the resource is full. | // Check if the resource is full. | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | "GATHER": { | ||||
cmpSupply.RemoveGatherer(this.entity); | cmpSupply.RemoveGatherer(this.entity); | ||||
delete this.gatheringTarget; | delete this.gatheringTarget; | ||||
// Show the carried resource, if we've gathered anything. | // Show the carried resource, if we've gathered anything. | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
var resourceTemplate = this.order.data.template; | let resourceTemplate = this.order.data.template; | ||||
var resourceType = this.order.data.type; | let resourceType = this.order.data.type; | ||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | ||||
if (!cmpOwnership) | if (!cmpOwnership) | ||||
return; | return; | ||||
var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); | let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); | ||||
if (cmpSupply && cmpSupply.IsAvailable(cmpOwnership.GetOwner(), this.entity)) | if (cmpSupply && cmpSupply.IsAvailable(cmpOwnership.GetOwner(), this.entity)) | ||||
{ | |||||
// Check we can still reach and gather from the target | // Check we can still reach and gather from the target | ||||
if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer) && this.CanGather(this.gatheringTarget)) | if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer) && this.CanGather(this.gatheringTarget)) | ||||
{ | { | ||||
// Gather the resources: | // Gather the resources: | ||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
// Try to gather treasure | // Try to gather treasure | ||||
if (cmpResourceGatherer.TryInstantGather(this.gatheringTarget)) | if (cmpResourceGatherer.TryInstantGather(this.gatheringTarget)) | ||||
return; | return; | ||||
// If we've already got some resources but they're the wrong type, | // If we've already got some resources but they're the wrong type, | ||||
// drop them first to ensure we're only ever carrying one type | // drop them first to ensure we're only ever carrying one type | ||||
if (cmpResourceGatherer.IsCarryingAnythingExcept(resourceType.generic)) | if (cmpResourceGatherer.IsCarryingAnythingExcept(resourceType.generic)) | ||||
cmpResourceGatherer.DropResources(); | cmpResourceGatherer.DropResources(); | ||||
// Collect from the target | // Collect from the target | ||||
var status = cmpResourceGatherer.PerformGather(this.gatheringTarget); | let status = cmpResourceGatherer.PerformGather(this.gatheringTarget); | ||||
// If we've collected as many resources as possible, | // If we've collected as many resources as possible, | ||||
// return to the nearest dropsite | // return to the nearest dropsite | ||||
if (status.filled) | if (status.filled) | ||||
{ | { | ||||
var nearby = this.FindNearestDropsite(resourceType.generic); | let nearby = this.FindNearestDropsite(resourceType.generic); | ||||
if (nearby) | if (nearby) | ||||
{ | { | ||||
// (Keep this Gather order on the stack so we'll | // (Keep this Gather order on the stack so we'll | ||||
// continue gathering after returning) | // continue gathering after returning) | ||||
this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); | this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); | ||||
return; | return; | ||||
} | } | ||||
Show All 22 Lines | "GATHER": { | ||||
this.PushOrderFront("Walk", { | this.PushOrderFront("Walk", { | ||||
"x": this.order.data.lastPos.x, | "x": this.order.data.lastPos.x, | ||||
"z": this.order.data.lastPos.z, | "z": this.order.data.lastPos.z, | ||||
"force": this.order.data.force | "force": this.order.data.force | ||||
}); | }); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | |||||
// We're already in range, can't get anywhere near it or the target is exhausted. | // We're already in range, can't get anywhere near it or the target is exhausted. | ||||
var herdPos = this.order.data.initPos; | let herdPos = this.order.data.initPos; | ||||
// Give up on this order and try our next queued order | // Give up on this order and try our next queued order | ||||
// but first check what is our next order and, if needed, insert a returnResource order | // but first check what is our next order and, if needed, insert a returnResource order | ||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
if (cmpResourceGatherer.IsCarrying(resourceType.generic) && | if (cmpResourceGatherer.IsCarrying(resourceType.generic) && | ||||
this.orderQueue.length > 1 && this.orderQueue[1] !== "ReturnResource" && | this.orderQueue.length > 1 && this.orderQueue[1] !== "ReturnResource" && | ||||
(this.orderQueue[1].type !== "Gather" || this.orderQueue[1].data.type.generic !== resourceType.generic)) | (this.orderQueue[1].type !== "Gather" || this.orderQueue[1].data.type.generic !== resourceType.generic)) | ||||
{ | { | ||||
let nearby = this.FindNearestDropsite(resourceType.generic); | let nearby = this.FindNearestDropsite(resourceType.generic); | ||||
if (nearby) | if (nearby) | ||||
this.orderQueue.splice(1, 0, { "type": "ReturnResource", "data": { "target": nearby, "force": false } }); | this.orderQueue.splice(1, 0, { "type": "ReturnResource", "data": { "target": nearby, "force": false } }); | ||||
} | } | ||||
if (this.FinishOrder()) | if (this.FinishOrder()) | ||||
return; | return; | ||||
// No remaining orders - pick a useful default behaviour | // No remaining orders - pick a useful default behaviour | ||||
// Try to find a new resource of the same specific type near our current position: | // Try to find a new resource of the same specific type near our current position: | ||||
// Also don't switch to a different type of huntable animal | // Also don't switch to a different type of huntable animal | ||||
var nearby = this.FindNearbyResource(function(ent, type, template) { | let nearby = this.FindNearbyResource(function(ent, type, template) { | ||||
return ( | return ( | ||||
(type.generic == "treasure" && resourceType.generic == "treasure") | (type.generic == "treasure" && resourceType.generic == "treasure") || | ||||
|| (type.specific == resourceType.specific | (type.specific == resourceType.specific && | ||||
&& (type.specific != "meat" || resourceTemplate == template)) | (type.specific != "meat" || resourceTemplate == template)) | ||||
); | ); | ||||
}); | }, new Vector2D(herdPos.x, herdPos.z)); | ||||
if (nearby) | if (nearby) | ||||
{ | { | ||||
this.PerformGather(nearby, false, false); | this.PerformGather(nearby, false, false); | ||||
return; | return; | ||||
} | } | ||||
// If hunting, try to go to the initial herd position to see if we are more lucky | // If hunting, try to go to the initial herd position to see if we are more lucky | ||||
if (herdPos) | if (herdPos) | ||||
{ | { | ||||
this.GatherNearPosition(herdPos.x, herdPos.z, resourceType, resourceTemplate); | this.GatherNearPosition(herdPos.x, herdPos.z, resourceType, resourceTemplate); | ||||
return; | return; | ||||
} | } | ||||
// Nothing else to gather - if we're carrying anything then we should | // Nothing else to gather - if we're carrying anything then we should | ||||
// drop it off, and if not then we might as well head to the dropsite | // drop it off, and if not then we might as well head to the dropsite | ||||
// anyway because that's a nice enough place to congregate and idle | // anyway because that's a nice enough place to congregate and idle | ||||
var nearby = this.FindNearestDropsite(resourceType.generic); | nearby = this.FindNearestDropsite(resourceType.generic); | ||||
if (nearby) | if (nearby) | ||||
{ | { | ||||
this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); | this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); | ||||
return; | return; | ||||
} | } | ||||
// No dropsites - just give up | // No dropsites - just give up | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 322 Lines • ▼ Show 20 Lines | "REPAIR": { | ||||
}, | }, | ||||
}, | }, | ||||
"ConstructionFinished": function(msg) { | "ConstructionFinished": function(msg) { | ||||
if (msg.data.entity != this.order.data.target) | if (msg.data.entity != this.order.data.target) | ||||
return; // ignore other buildings | return; // ignore other buildings | ||||
// Save the current order's data in case we need it later | // Save the current order's data in case we need it later | ||||
var oldData = this.order.data; | let oldData = this.order.data; | ||||
// Save the current state so we can continue walking if necessary | // Save the current state so we can continue walking if necessary | ||||
// FinishOrder() below will switch to IDLE if there's no order, which sets the idle animation. | // FinishOrder() below will switch to IDLE if there's no order, which sets the idle animation. | ||||
// Idle animation while moving towards finished construction looks weird (ghosty). | // Idle animation while moving towards finished construction looks weird (ghosty). | ||||
var oldState = this.GetCurrentState(); | let oldState = this.GetCurrentState(); | ||||
// Drop any resource we can if we are in range when the construction finishes | // Drop any resource we can if we are in range when the construction finishes | ||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
var cmpResourceDropsite = Engine.QueryInterface(msg.data.newentity, IID_ResourceDropsite); | let cmpResourceDropsite = Engine.QueryInterface(msg.data.newentity, IID_ResourceDropsite); | ||||
if (cmpResourceGatherer && cmpResourceDropsite && this.CheckTargetRange(msg.data.newentity, IID_Builder) && | if (cmpResourceGatherer && cmpResourceDropsite && this.CheckTargetRange(msg.data.newentity, IID_Builder) && | ||||
this.CanReturnResource(msg.data.newentity, true)) | this.CanReturnResource(msg.data.newentity, true)) | ||||
{ | { | ||||
let dropsiteTypes = cmpResourceDropsite.GetTypes(); | let dropsiteTypes = cmpResourceDropsite.GetTypes(); | ||||
cmpResourceGatherer.CommitResources(dropsiteTypes); | cmpResourceGatherer.CommitResources(dropsiteTypes); | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
} | } | ||||
Show All 28 Lines | "REPAIR": { | ||||
this.PerformGather(msg.data.newentity, true, false); | this.PerformGather(msg.data.newentity, true, false); | ||||
return; | return; | ||||
} | } | ||||
// If this building was e.g. a farmstead of ours, entities that received | // If this building was e.g. a farmstead of ours, entities that received | ||||
// the build command should look for nearby resources to gather | // the build command should look for nearby resources to gather | ||||
if ((oldData.force || oldData.autoharvest) && this.CanReturnResource(msg.data.newentity, false)) | if ((oldData.force || oldData.autoharvest) && this.CanReturnResource(msg.data.newentity, false)) | ||||
{ | { | ||||
var cmpResourceDropsite = Engine.QueryInterface(msg.data.newentity, IID_ResourceDropsite); | let types = cmpResourceDropsite.GetTypes(); | ||||
var types = cmpResourceDropsite.GetTypes(); | let pos; | ||||
let cmpPosition = Engine.QueryInterface(msg.data.newentity, IID_Position); | |||||
if (cmpPosition && cmpPosition.IsInWorld()) | |||||
pos = cmpPosition.GetPosition2D(); | |||||
// TODO: Slightly undefined behavior here, we don't know what type of resource will be collected, | // TODO: Slightly undefined behavior here, we don't know what type of resource will be collected, | ||||
// may cause problems for AIs (especially hunting fast animals), but avoid ugly hacks to fix that! | // may cause problems for AIs (especially hunting fast animals), but avoid ugly hacks to fix that! | ||||
var nearby = this.FindNearbyResource(function(ent, type, template) { | let nearby = this.FindNearbyResource(function(ent, type, template) { | ||||
return (types.indexOf(type.generic) != -1); | return (types.indexOf(type.generic) != -1); | ||||
}, msg.data.newentity); | }, pos); | ||||
if (nearby) | if (nearby) | ||||
{ | { | ||||
this.PerformGather(nearby, true, false); | this.PerformGather(nearby, true, false); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// Look for a nearby foundation to help with | // Look for a nearby foundation to help with | ||||
var nearbyFoundation = this.FindNearbyFoundation(); | let nearbyFoundation = this.FindNearbyFoundation(); | ||||
if (nearbyFoundation) | if (nearbyFoundation) | ||||
{ | { | ||||
this.AddOrder("Repair", { "target": nearbyFoundation, "autocontinue": oldData.autocontinue, "force": false }, true); | this.AddOrder("Repair", { "target": nearbyFoundation, "autocontinue": oldData.autocontinue, "force": false }, true); | ||||
return; | return; | ||||
} | } | ||||
// Unit was approaching and there's nothing to do now, so switch to walking | // Unit was approaching and there's nothing to do now, so switch to walking | ||||
if (oldState === "INDIVIDUAL.REPAIR.APPROACHING") | if (oldState === "INDIVIDUAL.REPAIR.APPROACHING") | ||||
{ | |||||
// We're already walking to the given point, so add this as a order. | // We're already walking to the given point, so add this as a order. | ||||
this.WalkToTarget(msg.data.newentity, true); | this.WalkToTarget(msg.data.newentity, true); | ||||
} | |||||
}, | }, | ||||
}, | }, | ||||
"GARRISON": { | "GARRISON": { | ||||
"enter": function() { | "enter": function() { | ||||
// If the garrisonholder should pickup, warn it so it can take needed action | // If the garrisonholder should pickup, warn it so it can take needed action | ||||
var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder); | var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder); | ||||
if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity)) | if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity)) | ||||
▲ Show 20 Lines • Show All 1,165 Lines • ▼ Show 20 Lines | if (!cmpResourceSupply.GetKillBeforeGather()) | ||||
return false; | return false; | ||||
return this.TargetIsAlive(ent); | return this.TargetIsAlive(ent); | ||||
}; | }; | ||||
/** | /** | ||||
* Returns the entity ID of the nearest resource supply where the given | * Returns the entity ID of the nearest resource supply where the given | ||||
* filter returns true, or undefined if none can be found. | * filter returns true, or undefined if none can be found. | ||||
* if target if given, the nearest is computed versus this target position. | * if position (as a vector2D) is given, the nearest is computed versus this position. | ||||
* TODO: extend this to exclude resources that already have lots of | * TODO: extend this to exclude resources that already have lots of | ||||
* gatherers. | * gatherers. | ||||
*/ | */ | ||||
UnitAI.prototype.FindNearbyResource = function(filter, target) | UnitAI.prototype.FindNearbyResource = function(filter, position) | ||||
{ | { | ||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | ||||
if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER) | if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER) | ||||
return undefined; | return undefined; | ||||
var owner = cmpOwnership.GetOwner(); | let owner = cmpOwnership.GetOwner(); | ||||
// We accept resources owned by Gaia or any player | // We accept resources owned by Gaia or any player | ||||
var players = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers(); | let players = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers(); | ||||
var range = 64; // TODO: what's a sensible number? | let range = 64; // TODO: what's a sensible number? | ||||
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); | let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); | ||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | ||||
let entity = this.entity; | let pos = position; | ||||
if (target) | if (!pos) | ||||
{ | { | ||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
if (cmpPosition && cmpPosition.IsInWorld()) | if (cmpPosition && cmpPosition.IsInWorld()) | ||||
entity = target; | pos = cmpPosition.GetPosition2D(); | ||||
} | } | ||||
var nearby = cmpRangeManager.ExecuteQuery(entity, 0, range, players, IID_ResourceSupply); | let nearby = cmpRangeManager.ExecuteQueryAroundPos(pos, 0, range, players, IID_ResourceSupply); | ||||
return nearby.find(ent => { | return nearby.find(ent => { | ||||
if (!this.CanGather(ent) || !this.CheckTargetVisible(ent)) | if (!this.CanGather(ent) || !this.CheckTargetVisible(ent)) | ||||
return false; | return false; | ||||
var cmpResourceSupply = Engine.QueryInterface(ent, IID_ResourceSupply); | let cmpResourceSupply = Engine.QueryInterface(ent, IID_ResourceSupply); | ||||
var type = cmpResourceSupply.GetType(); | let type = cmpResourceSupply.GetType(); | ||||
var amount = cmpResourceSupply.GetCurrentAmount(); | let amount = cmpResourceSupply.GetCurrentAmount(); | ||||
var template = cmpTemplateManager.GetCurrentTemplateName(ent); | let template = cmpTemplateManager.GetCurrentTemplateName(ent); | ||||
// Remove "resource|" prefix from template names, if present. | // Remove "resource|" prefix from template names, if present. | ||||
if (template.indexOf("resource|") != -1) | if (template.indexOf("resource|") != -1) | ||||
template = template.slice(9); | template = template.slice(9); | ||||
return amount > 0 && cmpResourceSupply.IsAvailable(owner, this.entity) && filter(ent, type, template); | return amount > 0 && cmpResourceSupply.IsAvailable(owner, this.entity) && filter(ent, type, template); | ||||
}); | }); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 1,078 Lines • ▼ Show 20 Lines | UnitAI.prototype.PerformGather = function(target, queued, force) | ||||
let order = { | let order = { | ||||
"target": target, | "target": target, | ||||
"type": type, | "type": type, | ||||
"template": template, | "template": template, | ||||
"force": force, | "force": force, | ||||
}; | }; | ||||
this.RememberTargetPosition(order); | this.RememberTargetPosition(order); | ||||
order.initPos = order.lastPos; | |||||
this.AddOrder("Gather", order, queued); | this.AddOrder("Gather", order, queued); | ||||
}; | }; | ||||
/** | /** | ||||
* Adds gather-near-position order to the queue, not forced, so it can be | * Adds gather-near-position order to the queue, not forced, so it can be | ||||
* interrupted by attacks. | * interrupted by attacks. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 895 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator