Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 265 Lines • ▼ Show 20 Lines | "Order.Walk": function(msg) { | ||||
// 2. If unpacked, we first need to pack, then follow case 1. | // 2. If unpacked, we first need to pack, then follow case 1. | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetHeldPosition(this.order.data.x, this.order.data.z); | this.SetHeldPosition(this.order.data.x, this.order.data.z); | ||||
// It's not too bad if we don't arrive at exactly the right position. | |||||
this.order.data.relaxed = true; | |||||
if (this.IsAnimal()) | if (this.IsAnimal()) | ||||
this.SetNextState("ANIMAL.WALKING"); | this.SetNextState("ANIMAL.WALKING"); | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.WALKING"); | this.SetNextState("INDIVIDUAL.WALKING"); | ||||
}, | }, | ||||
"Order.WalkAndFight": function(msg) { | "Order.WalkAndFight": function(msg) { | ||||
// Let players move captured domestic animals around | // Let players move captured domestic animals around | ||||
if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret()) | if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret()) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
// For packable units: | // For packable units: | ||||
// 1. If packed, we can move. | // 1. If packed, we can move. | ||||
// 2. If unpacked, we first need to pack, then follow case 1. | // 2. If unpacked, we first need to pack, then follow case 1. | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetHeldPosition(this.order.data.x, this.order.data.z); | this.SetHeldPosition(this.order.data.x, this.order.data.z); | ||||
// It's not too bad if we don't arrive at exactly the right position. | |||||
this.order.data.relaxed = true; | |||||
if (this.IsAnimal()) | if (this.IsAnimal()) | ||||
this.SetNextState("ANIMAL.WALKING"); // WalkAndFight not applicable for animals | this.SetNextState("ANIMAL.WALKING"); // WalkAndFight not applicable for animals | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.WALKINGANDFIGHTING"); | this.SetNextState("INDIVIDUAL.WALKINGANDFIGHTING"); | ||||
}, | }, | ||||
"Order.WalkToTarget": function(msg) { | "Order.WalkToTarget": function(msg) { | ||||
Show All 16 Lines | "Order.WalkToTarget": function(msg) { | ||||
if (this.CheckRange(this.order.data)) | if (this.CheckRange(this.order.data)) | ||||
{ | { | ||||
// We are already at the target, or can't move at all | // We are already at the target, or can't move at all | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return true; | return true; | ||||
} | } | ||||
// It's not too bad if we don't arrive at exactly the right position. | |||||
this.order.data.relaxed = true; | |||||
if (this.IsAnimal()) | if (this.IsAnimal()) | ||||
this.SetNextState("ANIMAL.WALKING"); | this.SetNextState("ANIMAL.WALKING"); | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.WALKING"); | this.SetNextState("INDIVIDUAL.WALKING"); | ||||
}, | }, | ||||
"Order.PickupUnit": function(msg) { | "Order.PickupUnit": function(msg) { | ||||
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | "Order.Patrol": function(msg) { | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
// It's not too bad if we don't arrive at exactly the right position. | |||||
this.order.data.relaxed = true; | |||||
this.SetNextState("INDIVIDUAL.PATROL"); | this.SetNextState("INDIVIDUAL.PATROL"); | ||||
}, | }, | ||||
"Order.Heal": function(msg) { | "Order.Heal": function(msg) { | ||||
// Check the target is alive | // Check the target is alive | ||||
if (!this.TargetIsAlive(this.order.data.target)) | if (!this.TargetIsAlive(this.order.data.target)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) | ||||
this.SetNextState("INDIVIDUAL.GATHER.GATHERING"); | this.SetNextState("INDIVIDUAL.GATHER.GATHERING"); | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.GATHER.APPROACHING"); | this.SetNextState("INDIVIDUAL.GATHER.APPROACHING"); | ||||
}, | }, | ||||
"Order.GatherNearPosition": function(msg) { | "Order.GatherNearPosition": function(msg) { | ||||
this.SetNextState("INDIVIDUAL.GATHER.WALKING"); | this.SetNextState("INDIVIDUAL.GATHER.WALKING"); | ||||
this.order.data.initPos = { 'x': this.order.data.x, 'z': this.order.data.z }; | this.order.data.initPos = { 'x': this.order.data.x, 'z': this.order.data.z }; | ||||
this.order.data.relaxed = true; | |||||
}, | }, | ||||
"Order.ReturnResource": function(msg) { | "Order.ReturnResource": function(msg) { | ||||
// Check if the dropsite is already in range | // Check if the dropsite is already in range | ||||
if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true)) | if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true)) | ||||
{ | { | ||||
var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); | var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); | ||||
if (cmpResourceDropsite) | if (cmpResourceDropsite) | ||||
▲ Show 20 Lines • Show All 348 Lines • ▼ Show 20 Lines | "WALKING": { | ||||
cmpFormation.MoveMembersIntoFormation(true, true); | cmpFormation.MoveMembersIntoFormation(true, true); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (this.CheckRange(this.order.data) && this.FinishOrder()) | if (msg.likelyFailure || this.CheckRange(this.order.data)) | ||||
{ | |||||
this.FinishOrder(); | |||||
this.CallMemberFunction("ResetFinishOrder", []); | this.CallMemberFunction("ResetFinishOrder", []); | ||||
} | |||||
}, | }, | ||||
}, | }, | ||||
"WALKINGANDFIGHTING": { | "WALKINGANDFIGHTING": { | ||||
"enter": function(msg) { | "enter": function(msg) { | ||||
if (!this.MoveTo(this.order.data)) | if (!this.MoveTo(this.order.data)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
Show All 11 Lines | "WALKINGANDFIGHTING": { | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
// check if there are no enemies to attack | // check if there are no enemies to attack | ||||
this.FindWalkAndFightTargets(); | this.FindWalkAndFightTargets(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (this.CheckRange(this.order.data) && this.FinishOrder()) | if (msg.likelyFailure || this.CheckRange(this.order.data)) | ||||
{ | |||||
this.FinishOrder(); | |||||
this.CallMemberFunction("ResetFinishOrder", []); | this.CallMemberFunction("ResetFinishOrder", []); | ||||
} | |||||
}, | }, | ||||
}, | }, | ||||
"PATROL": { | "PATROL": { | ||||
"enter": function(msg) { | "enter": function(msg) { | ||||
// Memorize the origin position in case that we want to go back | // Memorize the origin position in case that we want to go back | ||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
if (!cmpPosition || !cmpPosition.IsInWorld()) | if (!cmpPosition || !cmpPosition.IsInWorld()) | ||||
Show All 27 Lines | "PATROL": { | ||||
"leave": function(msg) { | "leave": function(msg) { | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.StopMoving(); | this.StopMoving(); | ||||
delete this.patrolStartPosOrder; | delete this.patrolStartPosOrder; | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (!msg.error && !this.CheckRange(this.order.data)) | if (!msg.likelyFailure && !this.CheckRange(this.order.data)) | ||||
return; | return; | ||||
/** | /** | ||||
* A-B-A-B-..: | * A-B-A-B-..: | ||||
* if the user only commands one patrol order, the patrol will be between | * if the user only commands one patrol order, the patrol will be between | ||||
* the last position and the defined waypoint | * the last position and the defined waypoint | ||||
* A-B-C-..-A-B-..: | * A-B-C-..-A-B-..: | ||||
* otherwise, the patrol is only between the given patrol commands and the | * otherwise, the patrol is only between the given patrol commands and the | ||||
* last position is not included (last position = the position where the unit | * last position is not included (last position = the position where the unit | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | "GARRISON":{ | ||||
cmpFormation.MoveMembersIntoFormation(true, true); | cmpFormation.MoveMembersIntoFormation(true, true); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (msg.likelyFailure || msg.likelySuccess) | |||||
this.SetNextState("GARRISONING"); | this.SetNextState("GARRISONING"); | ||||
}, | }, | ||||
}, | }, | ||||
"GARRISONING": { | "GARRISONING": { | ||||
"enter": function() { | "enter": function() { | ||||
// If a pickup has been requested, cancel it as it will be requested by members | // If a pickup has been requested, cancel it as it will be requested by members | ||||
if (this.pickup) | if (this.pickup) | ||||
{ | { | ||||
Show All 18 Lines | "FORMING": { | ||||
cmpFormation.MoveMembersIntoFormation(true, true); | cmpFormation.MoveMembersIntoFormation(true, true); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (!msg.error && !this.CheckRange(this.order.data)) | if (!msg.likelyFailure && !this.CheckRange(this.order.data)) | ||||
return; | return; | ||||
if (this.FinishOrder()) | if (this.FinishOrder()) | ||||
{ | { | ||||
this.CallMemberFunction("ResetFinishOrder", []); | this.CallMemberFunction("ResetFinishOrder", []); | ||||
return; | return; | ||||
} | } | ||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | ||||
▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | "WALKING": { | ||||
// We can only finish this order if the move was really completed. | // We can only finish this order if the move was really completed. | ||||
let cmpPosition = Engine.QueryInterface(this.formationController, IID_Position); | let cmpPosition = Engine.QueryInterface(this.formationController, IID_Position); | ||||
let atDestination = cmpPosition && cmpPosition.IsInWorld(); | let atDestination = cmpPosition && cmpPosition.IsInWorld(); | ||||
if (!atDestination && cmpPosition) | if (!atDestination && cmpPosition) | ||||
{ | { | ||||
let pos = cmpPosition.GetPosition2D(); | let pos = cmpPosition.GetPosition2D(); | ||||
atDestination = this.CheckPointRangeExplicit(pos.X + this.order.data.x, pos.Y + this.order.data.z, 0, 1); | atDestination = this.CheckPointRangeExplicit(pos.X + this.order.data.x, pos.Y + this.order.data.z, 0, 1); | ||||
} | } | ||||
if (!atDestination && !msg.error) | if (!atDestination && !msg.likelyFailure) | ||||
return; | return; | ||||
if (this.FinishOrder()) | if (this.FinishOrder()) | ||||
return; | return; | ||||
let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | ||||
if (cmpFormation) | if (cmpFormation) | ||||
cmpFormation.SetInPosition(this.entity); | cmpFormation.SetInPosition(this.entity); | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | "WALKING": { | ||||
"enter": function() { | "enter": function() { | ||||
if (!this.MoveTo(this.order.data)) | if (!this.MoveTo(this.order.data)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return true; | return true; | ||||
} | } | ||||
}, | }, | ||||
"leave": function () { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (msg.error || this.CheckRange(this.order.data)) | // If it looks like the path is failing, and we are close enough (3 tiles) | ||||
// stop anyways. This avoids pathing for an unreachable goal and reduces lag considerably. | |||||
if (msg.likelyFailure || msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, 12) || | |||||
this.CheckRange(this.order.data)) | |||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
"WALKINGANDFIGHTING": { | "WALKINGANDFIGHTING": { | ||||
"enter": function() { | "enter": function() { | ||||
if (!this.MoveTo(this.order.data)) | if (!this.MoveTo(this.order.data)) | ||||
{ | { | ||||
Show All 12 Lines | "WALKINGANDFIGHTING": { | ||||
"leave": function(msg) { | "leave": function(msg) { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (msg.error || this.CheckRange(this.order.data)) | // If it looks like the path is failing, and we are close enough (3 tiles) | ||||
// stop anyways. This avoids pathing for an unreachable goal and reduces lag considerably. | |||||
if (msg.likelyFailure || msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, 12) || | |||||
this.CheckRange(this.order.data)) | |||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
"PATROL": { | "PATROL": { | ||||
"enter": function() { | "enter": function() { | ||||
// Memorize the origin position in case that we want to go back | // Memorize the origin position in case that we want to go back | ||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
Show All 22 Lines | "PATROL": { | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
this.FindWalkAndFightTargets(); | this.FindWalkAndFightTargets(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (!msg.error && !this.CheckRange(this.order.data)) | if (msg.likelyFailure || msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, 12) || | ||||
return; | this.CheckRange(this.order.data)) | ||||
this.FinishOrder(); | |||||
if (this.orderQueue.length == 1) | if (this.orderQueue.length == 1) | ||||
this.PushOrder("Patrol", this.patrolStartPosOrder); | this.PushOrder("Patrol", this.patrolStartPosOrder); | ||||
this.PushOrder(this.order.type, this.order.data); | this.PushOrder(this.order.type, this.order.data); | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | "GUARD": { | ||||
"leave": function(msg) { | "leave": function(msg) { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.ResetSpeedMultiplier(); | this.ResetSpeedMultiplier(); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (msg.error || this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) | if (msg.likelyFailure || this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) | ||||
this.SetNextState("GUARDING"); | this.SetNextState("GUARDING"); | ||||
}, | }, | ||||
}, | }, | ||||
"GUARDING": { | "GUARDING": { | ||||
"enter": function() { | "enter": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.StartTimer(1000, 1000); | this.StartTimer(1000, 1000); | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | "FLEEING": { | ||||
"leave": function() { | "leave": function() { | ||||
this.ResetSpeedMultiplier(); | this.ResetSpeedMultiplier(); | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// When we've run far enough, stop fleeing | // When we've run far enough, stop fleeing | ||||
if (msg.error || this.CheckTargetRangeExplicit(this.order.data.target, this.order.data.distanceToFlee, -1)) | if (msg.likelyFailure || this.CheckTargetRangeExplicit(this.order.data.target, this.order.data.distanceToFlee, -1)) | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
// TODO: what if we run into more enemies while fleeing? | // TODO: what if we run into more enemies while fleeing? | ||||
}, | }, | ||||
"COMBAT": { | "COMBAT": { | ||||
"Order.LeaveFoundation": function(msg) { | "Order.LeaveFoundation": function(msg) { | ||||
Show All 39 Lines | "COMBAT": { | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
} | } | ||||
else | else | ||||
this.RememberTargetPosition(); | this.RememberTargetPosition(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (msg.error) | if (msg.likelyFailure) | ||||
{ | { | ||||
// This also handles hunting. | // This also handles hunting. | ||||
if (this.orderQueue.length > 1) | if (this.orderQueue.length > 1) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
else if (!this.order.data.force) | else if (!this.order.data.force) | ||||
{ | { | ||||
this.SetNextState("COMBAT.FINDINGNEWTARGET"); | this.SetNextState("COMBAT.FINDINGNEWTARGET"); | ||||
return; | return; | ||||
} | } | ||||
// Go to the last known position and try to find enemies there. | // Go to the last known position and try to find enemies there. | ||||
let lastPos = this.order.data.lastPos; | let lastPos = this.order.data.lastPos; | ||||
this.PushOrder("Walk", { "x": lastPos.x, "z": lastPos.z, "force": false }); | this.PushOrder("Walk", { "x": lastPos.x, "z": lastPos.z, "force": false }); | ||||
this.PushOrder("WalkAndFight", { "x": lastPos.x, "z": lastPos.z, "force": false }); | this.PushOrder("WalkAndFight", { "x": lastPos.x, "z": lastPos.z, "force": false }); | ||||
return; | return; | ||||
} | } | ||||
if (!this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) | if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) | ||||
{ | { | ||||
// Try moving again, | |||||
// attack range uses a height-related formula and our actual max range might have changed. | |||||
if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) | |||||
this.FinishOrder(); | |||||
return; | |||||
} | |||||
// If the unit needs to unpack, do so | // If the unit needs to unpack, do so | ||||
if (this.CanUnpack()) | if (this.CanUnpack()) | ||||
{ | { | ||||
this.PushOrderFront("Unpack", { "force": true }); | this.PushOrderFront("Unpack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetNextState("ATTACKING"); | this.SetNextState("ATTACKING"); | ||||
} | |||||
else if (msg.likelySuccess) | |||||
// Try moving again, | |||||
// attack range uses a height-related formula and our actual max range might have changed. | |||||
if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) | |||||
this.FinishOrder(); | |||||
}, | }, | ||||
}, | }, | ||||
"ATTACKING": { | "ATTACKING": { | ||||
"enter": function() { | "enter": function() { | ||||
let target = this.order.data.target; | let target = this.order.data.target; | ||||
let cmpFormation = Engine.QueryInterface(target, IID_Formation); | let cmpFormation = Engine.QueryInterface(target, IID_Formation); | ||||
// if the target is a formation, save the attacking formation, and pick a member | // if the target is a formation, save the attacking formation, and pick a member | ||||
▲ Show 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
// Return to our original position | // Return to our original position | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
} | } | ||||
}, | }, | ||||
"MovementUpdate": function() { | "MovementUpdate": function(msg) { | ||||
if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) | |||||
{ | |||||
// If the unit needs to unpack, do so | |||||
if (this.CanUnpack()) | |||||
{ | |||||
this.PushOrderFront("Unpack", { "force": true }); | |||||
return; | |||||
} | |||||
this.SetNextState("ATTACKING"); | this.SetNextState("ATTACKING"); | ||||
} | |||||
else if (msg.likelySuccess) | |||||
// Try moving again, | |||||
// attack range uses a height-related formula and our actual max range might have changed. | |||||
if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) | |||||
this.FinishOrder(); | |||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
"GATHER": { | "GATHER": { | ||||
"APPROACHING": { | "APPROACHING": { | ||||
"enter": function() { | "enter": function() { | ||||
this.gatheringTarget = this.order.data.target; // temporary, deleted in "leave". | this.gatheringTarget = this.order.data.target; // temporary, deleted in "leave". | ||||
Show All 10 Lines | "GATHER": { | ||||
this.SetNextState("GATHERING"); | this.SetNextState("GATHERING"); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// The GATHERING timer will handle finding a valid resource. | // The GATHERING timer will handle finding a valid resource. | ||||
if (msg.likelyFailure || this.CheckRange(this.order.data, IID_ResourceGatherer)) | |||||
this.SetNextState("GATHERING"); | this.SetNextState("GATHERING"); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
if (!this.gatheringTarget) | if (!this.gatheringTarget) | ||||
return; | return; | ||||
Show All 17 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)) | if (msg.likelyFailure || msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, 8) || | ||||
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". | ||||
▲ Show 20 Lines • Show All 224 Lines • ▼ Show 20 Lines | "HEAL": { | ||||
"Attacked": function(msg) { | "Attacked": function(msg) { | ||||
// If we stand ground we will rather die than flee | // If we stand ground we will rather die than flee | ||||
if (!this.GetStance().respondStandGround && !this.order.data.force) | if (!this.GetStance().respondStandGround && !this.order.data.force) | ||||
this.Flee(msg.data.attacker, false); | this.Flee(msg.data.attacker, false); | ||||
}, | }, | ||||
"APPROACHING": { | "APPROACHING": { | ||||
"enter": function() { | "enter": function() { | ||||
if (this.CheckTargetRange(this.order.data.target, IID_Heal)) | if (this.CheckRange(this.order.data, IID_Heal)) | ||||
{ | { | ||||
this.SetNextState("HEALING"); | this.SetNextState("HEALING"); | ||||
return true; | return true; | ||||
} | } | ||||
if (!this.MoveTo(this.order.data, IID_Heal)) | if (!this.MoveTo(this.order.data, IID_Heal)) | ||||
{ | { | ||||
this.FinishOrder(); | this.SetNextState("FINDINGNEWTARGET"); | ||||
return true; | return true; | ||||
} | } | ||||
this.StartTimer(1000, 1000); | this.StartTimer(1000, 1000); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force, IID_Heal, null)) | if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force, IID_Heal, null)) | ||||
{ | this.SetNextState("FINDINGNEWTARGET"); | ||||
// Return to our original position unless we have a better order. | |||||
if (!this.FinishOrder() && this.GetStance().respondHoldGround) | |||||
this.WalkToHeldPosition(); | |||||
} | |||||
}, | }, | ||||
"MovementUpdate": function() { | "MovementUpdate": function(msg) { | ||||
if (msg.likelyFailure || this.CheckRange(this.order.data, IID_Heal)) | |||||
this.SetNextState("HEALING"); | this.SetNextState("HEALING"); | ||||
}, | }, | ||||
}, | }, | ||||
"HEALING": { | "HEALING": { | ||||
"enter": function() { | "enter": function() { | ||||
var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | if (!this.CheckRange(this.order.data, IID_Heal)) | ||||
{ | |||||
this.SetNextState("APPROACHING"); | |||||
return true; | |||||
} | |||||
if (!this.TargetIsAlive(this.order.data.target) || | |||||
!this.CanHeal(this.order.data.target)) | |||||
{ | |||||
this.SetNextState("FINDINGNEWTARGET"); | |||||
return true; | |||||
} | |||||
let cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | |||||
this.healTimers = cmpHeal.GetTimers(); | this.healTimers = cmpHeal.GetTimers(); | ||||
// If the repeat time since the last heal hasn't elapsed, | // If the repeat time since the last heal hasn't elapsed, | ||||
// delay the action to avoid healing too fast. | // delay the action to avoid healing too fast. | ||||
var prepare = this.healTimers.prepare; | var prepare = this.healTimers.prepare; | ||||
if (this.lastHealed) | if (this.lastHealed) | ||||
{ | { | ||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | ||||
var repeatLeft = this.lastHealed + this.healTimers.repeat - cmpTimer.GetTime(); | var repeatLeft = this.lastHealed + this.healTimers.repeat - cmpTimer.GetTime(); | ||||
prepare = Math.max(prepare, repeatLeft); | prepare = Math.max(prepare, repeatLeft); | ||||
} | } | ||||
this.StopMoving(); | |||||
this.SelectAnimation("heal"); | this.SelectAnimation("heal"); | ||||
this.SetAnimationSync(prepare, this.healTimers.repeat); | this.SetAnimationSync(prepare, this.healTimers.repeat); | ||||
this.StartTimer(prepare, this.healTimers.repeat); | this.StartTimer(prepare, this.healTimers.repeat); | ||||
// If using a non-default prepare time, re-sync the animation when the timer runs. | // If using a non-default prepare time, re-sync the animation when the timer runs. | ||||
this.resyncAnimation = (prepare != this.healTimers.prepare) ? true : false; | this.resyncAnimation = prepare != this.healTimers.prepare; | ||||
this.FaceTowardsTarget(this.order.data.target); | this.FaceTowardsTarget(this.order.data.target); | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.ResetAnimation(); | this.ResetAnimation(); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
var target = this.order.data.target; | let target = this.order.data.target; | ||||
// Check the target is still alive and healable | // Check the target is still alive and healable | ||||
if (this.TargetIsAlive(target) && this.CanHeal(target)) | if (!this.TargetIsAlive(target) || !this.CanHeal(target)) | ||||
{ | |||||
// Check if we can still reach the target | |||||
if (this.CheckTargetRange(target, IID_Heal)) | |||||
{ | { | ||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | this.SetNextState("FINDINGNEWTARGET"); | ||||
this.lastHealed = cmpTimer.GetTime() - msg.lateness; | |||||
this.FaceTowardsTarget(target); | |||||
var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | |||||
cmpHeal.PerformHeal(target); | |||||
if (this.resyncAnimation) | |||||
{ | |||||
this.SetAnimationSync(this.healTimers.repeat, this.healTimers.repeat); | |||||
this.resyncAnimation = false; | |||||
} | |||||
return; | return; | ||||
} | } | ||||
// Can't reach it - try to chase after it | // Check if we can still reach the target | ||||
if (!this.CheckRange(this.order.data, IID_Heal)) | |||||
{ | |||||
if (this.ShouldChaseTargetedEntity(target, this.order.data.force)) | if (this.ShouldChaseTargetedEntity(target, this.order.data.force)) | ||||
{ | { | ||||
// Can't reach it - try to chase after it | |||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetNextState("HEAL.APPROACHING"); | this.SetNextState("HEAL.APPROACHING"); | ||||
} | } | ||||
else | |||||
this.SetNextState("FINDINGNEWTARGET"); | |||||
return; | |||||
} | |||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
this.lastHealed = cmpTimer.GetTime() - msg.lateness; | |||||
this.FaceTowardsTarget(target); | |||||
let cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | |||||
cmpHeal.PerformHeal(target); | |||||
if (this.resyncAnimation) | |||||
{ | |||||
this.SetAnimationSync(this.healTimers.repeat, this.healTimers.repeat); | |||||
this.resyncAnimation = false; | |||||
} | } | ||||
// Can't reach it, healed to max hp or doesn't exist any more - give up | }, | ||||
}, | |||||
"FINDINGNEWTARGET": { | |||||
"enter": function() { | |||||
// If we have another order, do that instead. | |||||
if (this.FinishOrder()) | if (this.FinishOrder()) | ||||
return; | return true; | ||||
// Heal another one | // Heal another one | ||||
if (this.FindNewHealTargets()) | if (this.FindNewHealTargets()) | ||||
return; | return true; | ||||
// Return to our original position | // Return to our original position | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
// We quit this state right away. | |||||
return true; | |||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
// Returning to dropsite | // Returning to dropsite | ||||
"RETURNRESOURCE": { | "RETURNRESOURCE": { | ||||
"APPROACHING": { | "APPROACHING": { | ||||
"enter": function() { | "enter": function() { | ||||
if (!this.MoveTo(this.order.data, IID_ResourceGatherer)) | if (!this.MoveTo(this.order.data, IID_ResourceGatherer)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return true; | return true; | ||||
} | } | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function() { | "MovementUpdate": function(msg) { | ||||
// Check the dropsite is in range and we can return our resource there | // Check the dropsite is in range and we can return our resource there | ||||
// (we didn't get stopped before reaching it) | // (we didn't get stopped before reaching it) | ||||
if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true)) | if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true)) | ||||
{ | { | ||||
var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); | let cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); | ||||
if (cmpResourceDropsite) | if (cmpResourceDropsite) | ||||
{ | { | ||||
// Dump any resources we can | // Dump any resources we can | ||||
var dropsiteTypes = cmpResourceDropsite.GetTypes(); | let dropsiteTypes = cmpResourceDropsite.GetTypes(); | ||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
cmpResourceGatherer.CommitResources(dropsiteTypes); | cmpResourceGatherer.CommitResources(dropsiteTypes); | ||||
// Stop showing the carried resource animation. | // Stop showing the carried resource animation. | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
// Our next order should always be a Gather, | // Our next order should always be a Gather, | ||||
// so just switch back to that order | // so just switch back to that order | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// The dropsite was destroyed, or we couldn't reach it, or ownership changed | if (msg.obstructed) | ||||
return; | |||||
// If we are here: we are in range but not carrying the right resources (or resources at all), | |||||
// the dropsite was destroyed, or we couldn't reach it, or ownership changed. | |||||
// Look for a new one. | // Look for a new one. | ||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||
var genericType = cmpResourceGatherer.GetMainCarryingType(); | let genericType = cmpResourceGatherer.GetMainCarryingType(); | ||||
var nearby = this.FindNearestDropsite(genericType); | let nearby = this.FindNearestDropsite(genericType); | ||||
if (nearby) | if (nearby) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); | this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); | ||||
return; | return; | ||||
} | } | ||||
// Oh no, couldn't find any drop sites. Give up on returning. | // Oh no, couldn't find any drop sites. Give up on returning. | ||||
Show All 17 Lines | "TRADE": { | ||||
} | } | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (!msg.error && !this.CheckTargetRange(this.order.data.target, IID_Trader)) | if (!msg.likelyFailure && !this.CheckTargetRange(this.order.data.target, IID_Trader)) | ||||
return; | return; | ||||
if (this.waypoints && this.waypoints.length) | if (this.waypoints && this.waypoints.length) | ||||
{ | { | ||||
if (!this.MoveToMarket(this.order.data.target)) | if (!this.MoveToMarket(this.order.data.target)) | ||||
this.StopTrading(); | this.StopTrading(); | ||||
} | } | ||||
else | else | ||||
Show All 21 Lines | "REPAIR": { | ||||
return true; | return true; | ||||
} | } | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function() { | "MovementUpdate": function(msg) { | ||||
if (msg.likelyFailure || msg.likelySuccess) | |||||
this.SetNextState("REPAIRING"); | this.SetNextState("REPAIRING"); | ||||
}, | }, | ||||
}, | }, | ||||
"REPAIRING": { | "REPAIRING": { | ||||
"enter": function() { | "enter": function() { | ||||
// If this order was forced, the player probably gave it, but now we've reached the target | // If this order was forced, the player probably gave it, but now we've reached the target | ||||
// switch to an unforced order (can be interrupted by attacks) | // switch to an unforced order (can be interrupted by attacks) | ||||
if (this.order.data.force) | if (this.order.data.force) | ||||
▲ Show 20 Lines • Show All 192 Lines • ▼ Show 20 Lines | "GARRISON": { | ||||
return true; | return true; | ||||
} | } | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function() { | "MovementUpdate": function(msg) { | ||||
if (msg.likelyFailure || msg.likelySuccess) | |||||
this.SetNextState("GARRISONED"); | this.SetNextState("GARRISONED"); | ||||
}, | }, | ||||
}, | }, | ||||
"GARRISONED": { | "GARRISONED": { | ||||
"enter": function() { | "enter": function() { | ||||
let target = this.order.data.target; | let target = this.order.data.target; | ||||
if (!target) | if (!target) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | "PICKUP": { | ||||
return true; | return true; | ||||
} | } | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function() { | "MovementUpdate": function(msg) { | ||||
if (msg.likelyFailure || msg.likelySuccess) | |||||
this.SetNextState("LOADING"); | this.SetNextState("LOADING"); | ||||
}, | }, | ||||
"PickupCanceled": function() { | "PickupCanceled": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 826 Lines • ▼ Show 20 Lines | UnitAI.prototype.StopTimer = function() | ||||
if (!this.timer) | if (!this.timer) | ||||
return; | return; | ||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | ||||
cmpTimer.CancelTimer(this.timer); | cmpTimer.CancelTimer(this.timer); | ||||
this.timer = undefined; | this.timer = undefined; | ||||
}; | }; | ||||
//// Message handlers ///// | UnitAI.prototype.OnMotionUpdate = function(msg) | ||||
UnitAI.prototype.OnMotionChanged = function(msg) | |||||
{ | { | ||||
this.UnitFsm.ProcessMessage(this, { "type": "MovementUpdate", "error": msg.error }); | this.UnitFsm.ProcessMessage(this, Object.assign({ "type": "MovementUpdate" }, msg)); | ||||
}; | }; | ||||
UnitAI.prototype.OnGlobalConstructionFinished = function(msg) | UnitAI.prototype.OnGlobalConstructionFinished = function(msg) | ||||
{ | { | ||||
// TODO: This is a bit inefficient since every unit listens to every | // TODO: This is a bit inefficient since every unit listens to every | ||||
// construction message - ideally we could scope it to only the one we're building | // construction message - ideally we could scope it to only the one we're building | ||||
this.UnitFsm.ProcessMessage(this, {"type": "ConstructionFinished", "data": msg}); | this.UnitFsm.ProcessMessage(this, {"type": "ConstructionFinished", "data": msg}); | ||||
▲ Show 20 Lines • Show All 586 Lines • ▼ Show 20 Lines | UnitAI.prototype.CheckTargetVisible = function(target) | ||||
if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) == "hidden") | if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) == "hidden") | ||||
return false; | return false; | ||||
// Either visible directly, or visible in fog | // Either visible directly, or visible in fog | ||||
return true; | return true; | ||||
}; | }; | ||||
/** | /** | ||||
* @returns true if the unit is in the relaxed-range from the target. | |||||
*/ | |||||
UnitAI.prototype.RelaxedMaxRangeCheck = function(data, relaxedRange) | |||||
{ | |||||
if (!data.relaxed) | |||||
return false; | |||||
let ndata = data; | |||||
ndata.min = 0; | |||||
ndata.max = relaxedRange; | |||||
return this.CheckRange(ndata); | |||||
}; | |||||
/** | |||||
* Let an entity face its target. | * Let an entity face its target. | ||||
* @param {number} target - The entity-ID of the target. | * @param {number} target - The entity-ID of the target. | ||||
*/ | */ | ||||
UnitAI.prototype.FaceTowardsTarget = function(target) | UnitAI.prototype.FaceTowardsTarget = function(target) | ||||
{ | { | ||||
let cmpTargetPosition = Engine.QueryInterface(target, IID_Position); | let cmpTargetPosition = Engine.QueryInterface(target, IID_Position); | ||||
if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) | if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 1,524 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator