Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 289 Lines • ▼ Show 20 Lines | if (cmpFormation) | ||||
cmpFormation.RemoveMembers([this.entity]); | cmpFormation.RemoveMembers([this.entity]); | ||||
cmpFormation.SetRearrange(true); | cmpFormation.SetRearrange(true); | ||||
} | } | ||||
return ACCEPT_ORDER; | return ACCEPT_ORDER; | ||||
}, | }, | ||||
"Order.Stop": function(msg) { | "Order.Stop": function(msg) { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return ACCEPT_ORDER; | return ACCEPT_ORDER; | ||||
Stan: Revert | |||||
}, | }, | ||||
"Order.Walk": function(msg) { | "Order.Walk": function(msg) { | ||||
if (!this.AbleToMove()) | if (!this.AbleToMove()) | ||||
return REJECT_ORDER; | return REJECT_ORDER; | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | UnitAI.prototype.UnitFsmSpec = { | ||||
"Order.Gather": function(msg) { | "Order.Gather": function(msg) { | ||||
if (!this.CanGather(this.order.data.target)) | if (!this.CanGather(this.order.data.target)) | ||||
{ | { | ||||
this.SetNextState("INDIVIDUAL.GATHER.FINDINGNEWTARGET"); | this.SetNextState("INDIVIDUAL.GATHER.FINDINGNEWTARGET"); | ||||
return ACCEPT_ORDER; | return ACCEPT_ORDER; | ||||
} | } | ||||
// If the unit is full go to the nearest dropsite instead of trying to gather. | // If the unit is full go to the nearest dropsite instead of trying to gather. | ||||
// Unless our target is a treasure which we cannot be full enough with (we can't carry treasures). | |||||
let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
if (msg.data.type.generic !== "treasure" && cmpResourceGatherer && !cmpResourceGatherer.CanCarryMore(msg.data.type.generic)) | if (cmpResourceGatherer && !cmpResourceGatherer.CanCarryMore(msg.data.type.generic)) | ||||
{ | { | ||||
let nearestDropsite = this.FindNearestDropsite(msg.data.type.generic); | let nearestDropsite = this.FindNearestDropsite(msg.data.type.generic); | ||||
if (nearestDropsite) | if (nearestDropsite) | ||||
this.PushOrderFront("ReturnResource", { | this.PushOrderFront("ReturnResource", { | ||||
"target": nearestDropsite, | "target": nearestDropsite, | ||||
"force": false, | "force": false, | ||||
"type": msg.data.type | "type": msg.data.type | ||||
}); | }); | ||||
▲ Show 20 Lines • Show All 149 Lines • ▼ Show 20 Lines | UnitAI.prototype.UnitFsmSpec = { | ||||
"Order.MoveToChasingPoint": function(msg) { | "Order.MoveToChasingPoint": function(msg) { | ||||
// Overriden by the CHASING state. | // Overriden by the CHASING state. | ||||
// Can however happen outside of it when renaming... | // Can however happen outside of it when renaming... | ||||
// TODO: don't use an order for that behaviour. | // TODO: don't use an order for that behaviour. | ||||
return REJECT_ORDER; | return REJECT_ORDER; | ||||
}, | }, | ||||
"Order.CollectTreasure": function(msg) { | |||||
let cmpTreasureCollecter = Engine.QueryInterface(this.entity, IID_TreasureCollecter); | |||||
if (!cmpTreasureCollecter || !cmpTreasureCollecter.CanCollect(msg.data.target)) | |||||
return REJECT_ORDER; | |||||
wraitiiUnsubmitted Done Inline Actionsrebase wraitii: rebase | |||||
this.SetNextState("COLLECTTREASURE"); | |||||
return ACCEPT_ORDER; | |||||
}, | |||||
// States for the special entity representing a group of units moving in formation: | // States for the special entity representing a group of units moving in formation: | ||||
"FORMATIONCONTROLLER": { | "FORMATIONCONTROLLER": { | ||||
"Order.Walk": function(msg) { | "Order.Walk": function(msg) { | ||||
this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); | this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); | ||||
this.SetNextState("WALKING"); | this.SetNextState("WALKING"); | ||||
return ACCEPT_ORDER; | return ACCEPT_ORDER; | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 1,890 Lines • ▼ Show 20 Lines | "GATHER": { | ||||
"z": this.order.data.lastPos.z, | "z": this.order.data.lastPos.z, | ||||
"force": this.order.data.force | "force": this.order.data.force | ||||
}); | }); | ||||
else | else | ||||
this.SetNextState("FINDINGNEWTARGET"); | this.SetNextState("FINDINGNEWTARGET"); | ||||
return; | return; | ||||
} | } | ||||
// Gather the resources: | |||||
let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
// Try to gather treasure | |||||
if (cmpResourceGatherer.TryInstantGather(this.gatheringTarget)) | |||||
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(); | ||||
this.FaceTowardsTarget(this.order.data.target); | this.FaceTowardsTarget(this.order.data.target); | ||||
// Collect from the target | |||||
let status = cmpResourceGatherer.PerformGather(this.gatheringTarget); | let status = cmpResourceGatherer.PerformGather(this.gatheringTarget); | ||||
// If we've collected as many resources as possible, | |||||
// return to the nearest dropsite | |||||
if (status.filled) | if (status.filled) | ||||
{ | { | ||||
let nearestDropsite = this.FindNearestDropsite(resourceType.generic); | let nearestDropsite = this.FindNearestDropsite(resourceType.generic); | ||||
if (nearestDropsite) | if (nearestDropsite) | ||||
{ | { | ||||
// (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) | ||||
// However mark our target as invalid if it's exhausted, so we don't waste time | // However mark our target as invalid if it's exhausted, so we don't waste time | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | "GATHER": { | ||||
// Try to find a new resource of the same specific type near the initial resource position: | // Try to find a new resource of the same specific type near the initial resource position: | ||||
// Also don't switch to a different type of huntable animal | // Also don't switch to a different type of huntable animal | ||||
let nearbyResource = this.FindNearbyResource(new Vector2D(initPos.x, initPos.z), | let nearbyResource = this.FindNearbyResource(new Vector2D(initPos.x, initPos.z), | ||||
(ent, type, template) => { | (ent, type, template) => { | ||||
if (previousTarget == ent) | if (previousTarget == ent) | ||||
return false; | return false; | ||||
if (type.generic == "treasure" && resourceType.generic == "treasure") | |||||
return true; | |||||
return type.specific == resourceType.specific && | return type.specific == resourceType.specific && | ||||
(type.specific != "meat" || resourceTemplate == template); | (type.specific != "meat" || resourceTemplate == template); | ||||
}); | }); | ||||
if (nearbyResource) | if (nearbyResource) | ||||
{ | { | ||||
this.PerformGather(nearbyResource, false, false); | this.PerformGather(nearbyResource, false, false); | ||||
return true; | return true; | ||||
} | } | ||||
// Failing that, try to move there and se if we are more lucky: maybe there are resources in FOW. | // Failing that, try to move there and se if we are more lucky: maybe there are resources in FOW. | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | "RETURNRESOURCE": { | ||||
} | } | ||||
// Oh no, couldn't find any drop sites. Give up on returning. | // Oh no, couldn't find any drop sites. Give up on returning. | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
"COLLECTTREASURE": { | |||||
"enter": function() { | |||||
let cmpTreasureCollecter = Engine.QueryInterface(this.entity, IID_TreasureCollecter); | |||||
if (!cmpTreasureCollecter || !cmpTreasureCollecter.CanCollect(this.order.data.target)) | |||||
{ | |||||
this.FinishOrder(); | |||||
return true; | |||||
} | |||||
if (this.CheckTargetRange(this.order.data.target, IID_TreasureCollecter)) | |||||
Done Inline ActionsErr, actually quite inefficient. Freagarach: Err, actually quite inefficient. | |||||
this.SetNextState("COLLECTING"); | |||||
else | |||||
this.SetNextState("APPROACHING"); | |||||
return true; | |||||
}, | |||||
"leave": function() { | |||||
}, | |||||
"APPROACHING": { | |||||
"enter": function() { | |||||
if (!this.MoveToTargetRange(this.order.data.target, IID_TreasureCollecter)) | |||||
{ | |||||
this.FinishOrder(); | |||||
return true; | |||||
} | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
this.StopMoving(); | |||||
}, | |||||
"MovementUpdate": function(msg) { | |||||
if (this.CheckTargetRange(this.order.data.target, IID_TreasureCollecter)) | |||||
this.SetNextState("COLLECTING"); | |||||
Done Inline ActionsUsual behaviour in UnitAI is to call FinishOrder(), and only if that returns false (no pending order) do something generic. wraitii: Usual behaviour in UnitAI is to call FinishOrder(), and only if that returns false (no pending… | |||||
else if (msg.likelyFailure) | |||||
this.FinishOrder(); | |||||
}, | |||||
}, | |||||
"COLLECTING": { | |||||
Done Inline Actions@Imarok I would like to have also your opinion on this method of performing actions in UnitAI. Freagarach: @Imarok I would like to have also your opinion on this method of performing actions in UnitAI. | |||||
Done Inline ActionsWhat exactly do you mean? Imarok: What exactly do you mean?
(I haven't really done anything in this area ;)) | |||||
Done Inline ActionsI've been discussing with @wraitii (and @bb earlier) about how UnitAI performs actions. If one looks at e.g. resource gathering, one can see a lot of gathering-specific stuff is done in UnitAI. I have some diffs moving those specific actions to their respective components, for that is much more fitting, IMHO. This diff uses the same concept:
To me this sounds like it should be. UnitAI should only care about how to react to situations, not precisely how to perform them. But @wraitii does not fully agree. Hence I would like some thought of more people for perhaps I am looking past some important stuff. Freagarach: I've been discussing with @wraitii (and @bb earlier) about how `UnitAI` performs actions. If… | |||||
"enter": function() { | |||||
let cmpTreasureCollecter = Engine.QueryInterface(this.entity, IID_TreasureCollecter); | |||||
if (!cmpTreasureCollecter.StartCollecting(this.order.data.target, IID_UnitAI)) | |||||
{ | |||||
this.ProcessMessage("TargetInvalidated"); | |||||
return true; | |||||
Done Inline ActionsDoesn't look like a callback function? Stan: Doesn't look like a callback function? | |||||
Done Inline ActionsSee also D2662. Freagarach: See also D2662.
It specifies what component to call and what functions under which… | |||||
} | |||||
this.FaceTowardsTarget(this.order.data.target); | |||||
this.SelectAnimation("collecting_treasure"); | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
let cmpTreasureCollecter = Engine.QueryInterface(this.entity, IID_TreasureCollecter); | |||||
if (cmpTreasureCollecter) | |||||
cmpTreasureCollecter.StopCollecting(); | |||||
this.ResetAnimation(); | |||||
}, | |||||
"OutOfRange": function(msg) { | |||||
this.SetNextState("APPROACHING"); | |||||
}, | |||||
"TargetInvalidated": function(msg) { | |||||
this.FinishOrder(); | |||||
}, | |||||
}, | |||||
}, | |||||
"TRADE": { | "TRADE": { | ||||
"Attacked": function(msg) { | "Attacked": function(msg) { | ||||
// Ignore attack | // Ignore attack | ||||
// TODO: Inform player | // TODO: Inform player | ||||
}, | }, | ||||
"APPROACHINGMARKET": { | "APPROACHINGMARKET": { | ||||
"enter": function() { | "enter": function() { | ||||
▲ Show 20 Lines • Show All 1,368 Lines • ▼ Show 20 Lines | else if (msg.tag == this.losAttackRangeQuery) | ||||
this.UnitFsm.ProcessMessage(this, { "type": "LosAttackRangeUpdate", "data": msg }); | this.UnitFsm.ProcessMessage(this, { "type": "LosAttackRangeUpdate", "data": msg }); | ||||
}; | }; | ||||
UnitAI.prototype.OnPackFinished = function(msg) | UnitAI.prototype.OnPackFinished = function(msg) | ||||
{ | { | ||||
this.UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed}); | this.UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed}); | ||||
}; | }; | ||||
/** | |||||
* A general function to process messages sent from components. | |||||
* @param {string} type - The type of message to process. | |||||
* @param {Object} msg - Optionally extra data to use. | |||||
*/ | |||||
UnitAI.prototype.ProcessMessage = function(type, msg) | |||||
{ | |||||
this.UnitFsm.ProcessMessage(this, { "type": type, "data": msg }); | |||||
}; | |||||
//// Helper functions to be called by the FSM //// | //// Helper functions to be called by the FSM //// | ||||
UnitAI.prototype.GetWalkSpeed = function() | UnitAI.prototype.GetWalkSpeed = function() | ||||
{ | { | ||||
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | ||||
if (!cmpUnitMotion) | if (!cmpUnitMotion) | ||||
return 0; | return 0; | ||||
return cmpUnitMotion.GetWalkSpeed(); | return cmpUnitMotion.GetWalkSpeed(); | ||||
▲ Show 20 Lines • Show All 1,351 Lines • ▼ Show 20 Lines | UnitAI.prototype.ReturnResource = function(target, queued) | ||||
{ | { | ||||
this.WalkToTarget(target, queued); | this.WalkToTarget(target, queued); | ||||
return; | return; | ||||
} | } | ||||
this.AddOrder("ReturnResource", { "target": target, "force": true }, queued); | this.AddOrder("ReturnResource", { "target": target, "force": true }, queued); | ||||
}; | }; | ||||
/** | |||||
* Adds order to collect a treasure to queue, forced by the player. | |||||
*/ | |||||
UnitAI.prototype.CollectTreasure = function(target, queued) | |||||
{ | |||||
this.AddOrder("CollectTreasure", { "target": target, "force": true }, queued); | |||||
}; | |||||
UnitAI.prototype.CancelSetupTradeRoute = function(target) | UnitAI.prototype.CancelSetupTradeRoute = function(target) | ||||
{ | { | ||||
let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | ||||
if (!cmpTrader) | if (!cmpTrader) | ||||
return; | return; | ||||
cmpTrader.RemoveTargetMarket(target); | cmpTrader.RemoveTargetMarket(target); | ||||
if (this.IsFormationController()) | if (this.IsFormationController()) | ||||
▲ Show 20 Lines • Show All 892 Lines • Show Last 20 Lines |
Revert