diff --git a/binaries/data/mods/public/simulation/components/AnimalAI.js b/binaries/data/mods/public/simulation/components/AnimalAI.js
deleted file mode 100644
index ed4fae0eff..0000000000
--- a/binaries/data/mods/public/simulation/components/AnimalAI.js
+++ /dev/null
@@ -1,387 +0,0 @@
-function AnimalAI() {}
-
-AnimalAI.prototype.Schema =
- "" +
- "" +
- "" +
- "violent" +
- "aggressive" +
- "defensive" +
- "passive" +
- "skittish" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "";
-
-var AnimalFsmSpec = {
-
- "MoveCompleted": function() {
- // ignore spurious movement messages
- // (these can happen when stopping moving at the same time
- // as switching states)
- },
-
- "MoveStarted": function() {
- // ignore spurious movement messages
- },
-
- "HealthChanged": function(msg) {
- // If we died (got reduced to 0 hitpoints), stop the AI and act like a corpse
- if (msg.to == 0)
- this.SetNextState("CORPSE");
- },
-
- "CORPSE": {
- "enter": function() {
- this.StopMoving();
- },
-
- "Attacked": function(msg) {
- // Do nothing, because we're dead already
- },
-
- "LeaveFoundation": function(msg) {
- // We can't walk away from the foundation (since we're dead),
- // but we mustn't block its construction (since the builders would get stuck),
- // and we don't want to trick gatherers into trying to reach us when
- // we're stuck in the middle of a building, so just delete our corpse.
- Engine.DestroyEntity(this.entity);
- },
- },
-
- "SKITTISH": {
-
- "Attacked": function(msg) {
- // If someone's attacking us, then run away
- this.MoveAwayFrom(msg.data.attacker, +this.template.FleeDistance);
- this.SetNextState("FLEEING");
- this.PlaySound("panic");
- },
-
- "LeaveFoundation": function(msg) {
- // Run away from the foundation
- this.MoveAwayFrom(msg.target, +this.template.FleeDistance);
- this.SetNextState("FLEEING");
- this.PlaySound("panic");
- },
-
- "ROAMING": {
- "enter": function() {
- // Walk in a random direction
- this.SelectAnimation("walk", false, this.GetWalkSpeed());
- this.MoveRandomly(+this.template.RoamDistance);
- // Set a random timer to switch to feeding state
- this.StartTimer(RandomInt(+this.template.RoamTimeMin, +this.template.RoamTimeMax));
- },
-
- "leave": function() {
- this.StopTimer();
- },
-
- "Timer": function(msg) {
- this.SetNextState("FEEDING");
- },
-
- "MoveCompleted": function() {
- this.MoveRandomly(+this.template.RoamDistance);
- },
- },
-
- "FEEDING": {
- "enter": function() {
- // Stop and eat for a while
- this.SelectAnimation("feeding");
- this.StopMoving();
- this.StartTimer(RandomInt(+this.template.FeedTimeMin, +this.template.FeedTimeMax));
- },
-
- "leave": function() {
- this.StopTimer();
- },
-
- "MoveCompleted": function() { },
-
- "Timer": function(msg) {
- this.SetNextState("ROAMING");
- },
- },
-
- "FLEEING": {
- "enter": function() {
- // Run quickly
- var speed = this.GetRunSpeed();
- this.SelectAnimation("run", false, speed);
- this.SetMoveSpeed(speed);
- },
-
- "leave": function() {
- // Reset normal speed
- this.SetMoveSpeed(this.GetWalkSpeed());
- },
-
- "MoveCompleted": function() {
- // When we've run far enough, go back to the roaming state
- this.SetNextState("ROAMING");
- },
- },
- },
-
- "PASSIVE": {
-
- "Attacked": function(msg) {
- // Do nothing, just let them kill us
- },
-
- "LeaveFoundation": function(msg) {
- // Walk away from the foundation
- this.MoveAwayFrom(msg.target, 4);
- this.SetNextState("FLEEING");
- },
-
- "ROAMING": {
- "enter": function() {
- // Walk in a random direction
- this.SelectAnimation("walk", false, this.GetWalkSpeed());
- this.MoveRandomly(+this.template.RoamDistance);
- // Set a random timer to switch to feeding state
- this.StartTimer(RandomInt(+this.template.RoamTimeMin, +this.template.RoamTimeMax));
- },
-
- "leave": function() {
- this.StopTimer();
- },
-
- "Timer": function(msg) {
- this.SetNextState("FEEDING");
- },
-
- "MoveCompleted": function() {
- this.MoveRandomly(+this.template.RoamDistance);
- },
- },
-
- "FEEDING": {
- "enter": function() {
- // Stop and eat for a while
- this.SelectAnimation("feeding");
- this.StopMoving();
- this.StartTimer(RandomInt(+this.template.FeedTimeMin, +this.template.FeedTimeMax));
- },
-
- "leave": function() {
- this.StopTimer();
- },
-
- "MoveCompleted": function() { },
-
- "Timer": function(msg) {
- this.SetNextState("ROAMING");
- },
- },
-
- "FLEEING": {
- "enter": function() {
- this.SelectAnimation("walk", false, this.GetWalkSpeed());
- },
-
- "MoveCompleted": function() {
- this.SetNextState("ROAMING");
- },
- },
- },
-
-};
-
-var AnimalFsm = new FSM(AnimalFsmSpec);
-
-AnimalAI.prototype.Init = function()
-{
-};
-
-// FSM linkage functions:
-
-AnimalAI.prototype.OnCreate = function()
-{
- var startingState = this.template.NaturalBehaviour;
- startingState = startingState.toUpperCase(startingState);
-
- if (startingState == "SKITTISH")
- startingState = startingState + ".FEEDING";
- else
- startingState = "PASSIVE.FEEDING";
-
- AnimalFsm.Init(this, startingState);
-};
-
-AnimalAI.prototype.SetNextState = function(state)
-{
- AnimalFsm.SetNextState(this, state);
-};
-
-AnimalAI.prototype.DeferMessage = function(msg)
-{
- AnimalFsm.DeferMessage(this, msg);
-};
-
-AnimalAI.prototype.OnMotionChanged = function(msg)
-{
- if (msg.starting && !msg.error)
- {
- AnimalFsm.ProcessMessage(this, {"type": "MoveStarted", "data": msg});
- }
- else if (!msg.starting || msg.error)
- {
- AnimalFsm.ProcessMessage(this, {"type": "MoveCompleted", "data": msg});
- }
-};
-
-AnimalAI.prototype.OnAttacked = function(msg)
-{
- AnimalFsm.ProcessMessage(this, {"type": "Attacked", "data": msg});
-};
-
-AnimalAI.prototype.OnHealthChanged = function(msg)
-{
- AnimalFsm.ProcessMessage(this, {"type": "HealthChanged", "from": msg.from, "to": msg.to});
-};
-
-AnimalAI.prototype.TimerHandler = function(data, lateness)
-{
- AnimalFsm.ProcessMessage(this, {"type": "Timer", "data": data, "lateness": lateness});
-};
-
-AnimalAI.prototype.LeaveFoundation = function(target)
-{
- AnimalFsm.ProcessMessage(this, {"type": "LeaveFoundation", "target": target});
-};
-
-// Functions to be called by the FSM:
-
-AnimalAI.prototype.GetWalkSpeed = function()
-{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpMotion.GetWalkSpeed();
-};
-
-AnimalAI.prototype.GetRunSpeed = function()
-{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpMotion.GetRunSpeed();
-};
-
-AnimalAI.prototype.PlaySound = function(name)
-{
- PlaySound(name, this.entity);
-};
-
-AnimalAI.prototype.SelectAnimation = function(name, once, speed, sound)
-{
- var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
- if (!cmpVisual)
- return;
-
- var soundgroup;
- if (sound)
- {
- var cmpSound = Engine.QueryInterface(this.entity, IID_Sound);
- if (cmpSound)
- soundgroup = cmpSound.GetSoundGroup(sound);
- }
-
- // Set default values if unspecified
- if (typeof once == "undefined")
- once = false;
- if (typeof speed == "undefined")
- speed = 1.0;
- if (typeof soundgroup == "undefined")
- soundgroup = "";
-
- cmpVisual.SelectAnimation(name, once, speed, soundgroup);
-};
-
-AnimalAI.prototype.MoveRandomly = function(distance)
-{
- // We want to walk in a random direction, but avoid getting stuck
- // in obstacles or narrow spaces.
- // So pick a circular range from approximately our current position,
- // and move outwards to the nearest point on that circle, which will
- // lead to us avoiding obstacles and moving towards free space.
-
- // TODO: we probably ought to have a 'home' point, and drift towards
- // that, so we don't spread out all across the whole map
-
- var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- if (!cmpPosition)
- return;
-
- if (!cmpPosition.IsInWorld())
- return;
-
- var pos = cmpPosition.GetPosition();
-
- var jitter = 0.5;
-
- // Randomly adjust the range's center a bit, so we tend to prefer
- // moving in random directions (if there's nothing in the way)
- var tx = pos.x + (2*Math.random()-1)*jitter;
- var tz = pos.z + (2*Math.random()-1)*jitter;
-
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpMotion.MoveToPointRange(tx, tz, distance, distance);
-};
-
-AnimalAI.prototype.MoveAwayFrom = function(ent, distance)
-{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpMotion.MoveToTargetRange(ent, distance, distance);
-};
-
-AnimalAI.prototype.StopMoving = function()
-{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpMotion.StopMoving();
-};
-
-AnimalAI.prototype.SetMoveSpeed = function(speed)
-{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpMotion.SetSpeed(speed);
-};
-
-AnimalAI.prototype.StartTimer = function(interval, data)
-{
- if (this.timer)
- error("Called StartTimer when there's already an active timer");
-
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- this.timer = cmpTimer.SetTimeout(this.entity, IID_AnimalAI, "TimerHandler", interval, data);
-};
-
-AnimalAI.prototype.StopTimer = function()
-{
- if (!this.timer)
- return;
-
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- cmpTimer.CancelTimer(this.timer);
- this.timer = undefined;
-};
-
-Engine.RegisterComponentType(IID_AnimalAI, "AnimalAI", AnimalAI);
diff --git a/binaries/data/mods/public/simulation/components/Foundation.js b/binaries/data/mods/public/simulation/components/Foundation.js
index db12068058..0f62eb23f5 100644
--- a/binaries/data/mods/public/simulation/components/Foundation.js
+++ b/binaries/data/mods/public/simulation/components/Foundation.js
@@ -90,11 +90,7 @@ Foundation.prototype.Build = function(builderEnt, work)
if (cmpUnitAI)
cmpUnitAI.LeaveFoundation(this.entity);
- var cmpAnimalAI = Engine.QueryInterface(ent, IID_AnimalAI);
- if (cmpAnimalAI)
- cmpAnimalAI.LeaveFoundation(this.entity);
-
- // TODO: What if an obstruction has no UnitAI/AnimalAI?
+ // TODO: What if an obstruction has no UnitAI?
}
// TODO: maybe we should tell the builder to use a special
diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js
index 94d3733312..a35a1316cb 100644
--- a/binaries/data/mods/public/simulation/components/UnitAI.js
+++ b/binaries/data/mods/public/simulation/components/UnitAI.js
@@ -5,7 +5,38 @@ UnitAI.prototype.Schema =
"" +
"" +
"" +
- "";
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "violent" +
+ "aggressive" +
+ "defensive" +
+ "passive" +
+ "skittish" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ ""+
+ "" +
+ "";
// Very basic stance support (currently just for test maps where we don't want
// everyone killing each other immediately after loading)
@@ -18,6 +49,7 @@ var g_Stances = {
},
};
+// See ../helpers/FSM.js for some documentation of this FSM specification syntax
var UnitFsmSpec = {
// Default event handlers:
@@ -44,6 +76,9 @@ var UnitFsmSpec = {
// ignore attacker
},
+ "HealthChanged": function(msg) {
+ // ignore
+ },
// Formation handlers:
@@ -53,6 +88,13 @@ var UnitFsmSpec = {
// Called when being told to walk as part of a formation
"Order.FormationWalk": function(msg) {
+ if (this.IsAnimal())
+ {
+ // TODO: let players move captured animals around
+ this.FinishOrder();
+ return;
+ }
+
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
cmpUnitMotion.MoveToFormationOffset(msg.data.target, msg.data.x, msg.data.z);
@@ -71,11 +113,25 @@ var UnitFsmSpec = {
// (these will switch the unit out of formation mode)
"Order.Walk": function(msg) {
+ if (this.IsAnimal())
+ {
+ // TODO: let players move captured animals around
+ this.FinishOrder();
+ return;
+ }
+
this.MoveToPoint(this.order.data.x, this.order.data.z);
this.SetNextState("INDIVIDUAL.WALKING");
},
"Order.WalkToTarget": function(msg) {
+ if (this.IsAnimal())
+ {
+ // TODO: let players move captured animals around
+ this.FinishOrder();
+ return;
+ }
+
var ok = this.MoveToTarget(this.order.data.target);
if (ok)
{
@@ -111,14 +167,20 @@ var UnitFsmSpec = {
if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))
{
// We've started walking to the given point
- this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING");
+ if (this.IsAnimal())
+ this.SetNextState("ANIMAL.COMBAT.APPROACHING");
+ else
+ this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING");
}
else
{
// We are already at the target, or can't move at all,
// so try attacking it from here.
// TODO: need better handling of the can't-reach-target case
- this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING");
+ if (this.IsAnimal())
+ this.SetNextState("ANIMAL.COMBAT.ATTACKING");
+ else
+ this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING");
}
},
@@ -299,6 +361,12 @@ var UnitFsmSpec = {
// States for entities not part of a formation:
"INDIVIDUAL": {
+ "enter": function() {
+ // Sanity-checking
+ if (this.IsAnimal())
+ error("Animal got moved into INDIVIDUAL.* state");
+ },
+
"Attacked": function(msg) {
// Default behaviour: attack back at our attacker
if (this.CanAttack(msg.data.attacker))
@@ -454,7 +522,7 @@ var UnitFsmSpec = {
// Can't reach it, or it doesn't exist any more - give up
this.FinishOrder();
-
+
// TODO: see if we can switch to a new nearby enemy
},
@@ -841,6 +909,178 @@ var UnitFsmSpec = {
},
},
+
+ "ANIMAL": {
+
+ "HealthChanged": function(msg) {
+ // If we died (got reduced to 0 hitpoints), stop the AI and act like a corpse
+ if (msg.to == 0)
+ this.SetNextState("CORPSE");
+ },
+
+ "Attacked": function(msg) {
+ if (this.template.NaturalBehaviour == "skittish" ||
+ this.template.NaturalBehaviour == "passive")
+ {
+ this.MoveToTargetRangeExplicit(msg.data.attacker, +this.template.FleeDistance, +this.template.FleeDistance);
+ this.SetNextState("FLEEING");
+ this.PlaySound("panic");
+ }
+ else if (this.template.NaturalBehaviour == "violent" ||
+ this.template.NaturalBehaviour == "aggressive" ||
+ this.template.NaturalBehaviour == "defensive")
+ {
+ if (this.CanAttack(msg.data.attacker))
+ this.ReplaceOrder("Attack", { "target": msg.data.attacker });
+ }
+ },
+
+ "Order.LeaveFoundation": function(msg) {
+ // Run away from the foundation
+ this.MoveToTargetRangeExplicit(msg.data.target, +this.template.FleeDistance, +this.template.FleeDistance);
+ this.SetNextState("FLEEING");
+ this.PlaySound("panic");
+ },
+
+ "IDLE": {
+ // (We need an IDLE state so that FinishOrder works)
+
+ "enter": function() {
+ // Start feeding immediately
+ this.SetNextState("FEEDING");
+ return true;
+ },
+ },
+
+ "CORPSE": {
+ "enter": function() {
+ this.StopMoving();
+ },
+
+ // Ignore all orders that animals might otherwise respond to
+ "Order.FormationWalk": function() { },
+ "Order.Walk": function() { },
+ "Order.WalkToTarget": function() { },
+ "Order.Attack": function() { },
+
+ "Attacked": function(msg) {
+ // Do nothing, because we're dead already
+ },
+
+ "Order.LeaveFoundation": function(msg) {
+ // We can't walk away from the foundation (since we're dead),
+ // but we mustn't block its construction (since the builders would get stuck),
+ // and we don't want to trick gatherers into trying to reach us when
+ // we're stuck in the middle of a building, so just delete our corpse.
+ Engine.DestroyEntity(this.entity);
+ },
+ },
+
+ "ROAMING": {
+ "enter": function() {
+ // Walk in a random direction
+ this.SelectAnimation("walk", false, this.GetWalkSpeed());
+ this.MoveRandomly(+this.template.RoamDistance);
+ // Set a random timer to switch to feeding state
+ this.StartTimer(RandomInt(+this.template.RoamTimeMin, +this.template.RoamTimeMax));
+ },
+
+ "leave": function() {
+ this.StopTimer();
+ },
+
+ "LosRangeUpdate": function(msg) {
+ if (this.template.NaturalBehaviour == "skittish")
+ {
+ if (msg.data.added.length > 0)
+ {
+ this.MoveToTargetRangeExplicit(msg.data.added[0], +this.template.FleeDistance, +this.template.FleeDistance);
+ this.SetNextState("FLEEING");
+ this.PlaySound("panic");
+ return;
+ }
+ }
+ // Start attacking one of the newly-seen enemy (if any)
+ else if (this.template.NaturalBehaviour == "violent" ||
+ this.template.NaturalBehaviour == "aggressive")
+ {
+ this.AttackVisibleEntity(msg.data.added);
+ }
+
+ // TODO: if two units enter our range together, we'll attack the
+ // first and then the second won't trigger another LosRangeUpdate
+ // so we won't notice it. Probably we should do something with
+ // ResetActiveQuery in ROAMING.enter/FEEDING.enter in order to
+ // find any units that are already in range.
+ },
+
+ "Timer": function(msg) {
+ this.SetNextState("FEEDING");
+ },
+
+ "MoveCompleted": function() {
+ this.MoveRandomly(+this.template.RoamDistance);
+ },
+ },
+
+ "FEEDING": {
+ "enter": function() {
+ // Stop and eat for a while
+ this.SelectAnimation("feeding");
+ this.StopMoving();
+ this.StartTimer(RandomInt(+this.template.FeedTimeMin, +this.template.FeedTimeMax));
+ },
+
+ "leave": function() {
+ this.StopTimer();
+ },
+
+ "LosRangeUpdate": function(msg) {
+ if (this.template.NaturalBehaviour == "skittish")
+ {
+ if (msg.data.added.length > 0)
+ {
+ this.MoveToTargetRangeExplicit(msg.data.added[0], +this.template.FleeDistance, +this.template.FleeDistance);
+ this.SetNextState("FLEEING");
+ this.PlaySound("panic");
+ return;
+ }
+ }
+ // Start attacking one of the newly-seen enemy (if any)
+ else if (this.template.NaturalBehaviour == "violent")
+ {
+ this.AttackVisibleEntity(msg.data.added);
+ }
+ },
+
+ "MoveCompleted": function() { },
+
+ "Timer": function(msg) {
+ this.SetNextState("ROAMING");
+ },
+ },
+
+ "FLEEING": {
+ "enter": function() {
+ // Run quickly
+ var speed = this.GetRunSpeed();
+ this.SelectAnimation("run", false, speed);
+ this.SetMoveSpeed(speed);
+ },
+
+ "leave": function() {
+ // Reset normal speed
+ this.SetMoveSpeed(this.GetWalkSpeed());
+ },
+
+ "MoveCompleted": function() {
+ // When we've run far enough, go back to the roaming state
+ this.SetNextState("ROAMING");
+ },
+ },
+
+ "COMBAT": "INDIVIDUAL.COMBAT", // reuse the same combat behaviour for animals
+ },
};
var UnitFsm = new FSM(UnitFsmSpec);
@@ -860,6 +1100,11 @@ UnitAI.prototype.IsFormationController = function()
return (this.template.FormationController == "true");
};
+UnitAI.prototype.IsAnimal = function()
+{
+ return (this.template.NaturalBehaviour ? true : false);
+};
+
UnitAI.prototype.IsIdle = function()
{
return this.isIdle;
@@ -867,7 +1112,9 @@ UnitAI.prototype.IsIdle = function()
UnitAI.prototype.OnCreate = function()
{
- if (this.IsFormationController())
+ if (this.IsAnimal())
+ UnitFsm.Init(this, "ANIMAL.FEEDING");
+ else if (this.IsFormationController())
UnitFsm.Init(this, "FORMATIONCONTROLLER.IDLE");
else
UnitFsm.Init(this, "INDIVIDUAL.IDLE");
@@ -916,7 +1163,7 @@ UnitAI.prototype.SetupRangeQuery = function(owner)
// Get our diplomacy array
var diplomacy = player.GetDiplomacy();
var numPlayers = playerMan.GetNumPlayers();
-
+
for (var i = 1; i < numPlayers; ++i)
{
// Exclude gaia, allies, and self
@@ -1076,6 +1323,11 @@ UnitAI.prototype.OnAttacked = function(msg)
UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg});
};
+UnitAI.prototype.OnHealthChanged = function(msg)
+{
+ UnitFsm.ProcessMessage(this, {"type": "HealthChanged", "from": msg.from, "to": msg.to});
+};
+
UnitAI.prototype.OnRangeUpdate = function(msg)
{
if (msg.tag == this.losRangeQuery)
@@ -1544,7 +1796,13 @@ UnitAI.prototype.CanGarrison = function(target)
var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
if (!cmpGarrisonHolder)
return false;
-
+
+ // Don't let animals garrison for now
+ // (If we want to support that, we'll need to change Order.Garrison so it
+ // doesn't move the animal into an INVIDIDUAL.* state)
+ if (this.IsAnimal())
+ return false;
+
return true;
};
@@ -1614,5 +1872,43 @@ UnitAI.prototype.CanRepair = function(target)
return true;
};
+//// Animal specific functions ////
+
+UnitAI.prototype.MoveRandomly = function(distance)
+{
+ // We want to walk in a random direction, but avoid getting stuck
+ // in obstacles or narrow spaces.
+ // So pick a circular range from approximately our current position,
+ // and move outwards to the nearest point on that circle, which will
+ // lead to us avoiding obstacles and moving towards free space.
+
+ // TODO: we probably ought to have a 'home' point, and drift towards
+ // that, so we don't spread out all across the whole map
+
+ var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+ if (!cmpPosition)
+ return;
+
+ if (!cmpPosition.IsInWorld())
+ return;
+
+ var pos = cmpPosition.GetPosition();
+
+ var jitter = 0.5;
+
+ // Randomly adjust the range's center a bit, so we tend to prefer
+ // moving in random directions (if there's nothing in the way)
+ var tx = pos.x + (2*Math.random()-1)*jitter;
+ var tz = pos.z + (2*Math.random()-1)*jitter;
+
+ var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpMotion.MoveToPointRange(tx, tz, distance, distance);
+};
+
+UnitAI.prototype.SetMoveSpeed = function(speed)
+{
+ var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpMotion.SetSpeed(speed);
+};
Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);
diff --git a/binaries/data/mods/public/simulation/components/interfaces/AnimalAI.js b/binaries/data/mods/public/simulation/components/interfaces/AnimalAI.js
deleted file mode 100644
index 51994c1459..0000000000
--- a/binaries/data/mods/public/simulation/components/interfaces/AnimalAI.js
+++ /dev/null
@@ -1 +0,0 @@
-Engine.RegisterInterface("AnimalAI");
diff --git a/binaries/data/mods/public/simulation/helpers/FSM.js b/binaries/data/mods/public/simulation/helpers/FSM.js
index 27539d9e5f..8b0d60cb30 100644
--- a/binaries/data/mods/public/simulation/helpers/FSM.js
+++ b/binaries/data/mods/public/simulation/helpers/FSM.js
@@ -70,6 +70,9 @@ var FsmSpec = {
},
},
+ // Define a new state which is an exact copy of another
+ // state that is defined elsewhere in this FSM:
+ "OTHERSUBSTATENAME": "STATENAME.SUBSTATENAME",
}
}
@@ -143,6 +146,23 @@ function FSM(spec)
function process(fsm, node, path, handlers)
{
+ // Handle string references to nodes defined elsewhere in the FSM spec
+ if (typeof node === "string")
+ {
+ var refpath = node.split(".");
+ var refd = spec;
+ for each (var p in refpath)
+ {
+ refd = refd[p];
+ if (!refd)
+ {
+ error("FSM node "+path.join(".")+" referred to non-defined node "+node);
+ return {};
+ }
+ }
+ node = refd;
+ }
+
var state = {};
fsm.states[path.join(".")] = state;
diff --git a/binaries/data/mods/public/simulation/helpers/Player.js b/binaries/data/mods/public/simulation/helpers/Player.js
index b7663f5d2f..d73d1d1b7b 100644
--- a/binaries/data/mods/public/simulation/helpers/Player.js
+++ b/binaries/data/mods/public/simulation/helpers/Player.js
@@ -53,7 +53,7 @@ function LoadPlayerSettings(settings)
teams[team].push(i);
}
}
-
+
for (var i = 0; i < numPlayers; ++i)
{
// Add player entity to engine
@@ -112,6 +112,7 @@ function LoadPlayerSettings(settings)
player.SetName(pDefs.Name);
player.SetCiv(pDefs.Civ);
player.SetColour(pDefs.Colour.r, pDefs.Colour.g, pDefs.Colour.b);
+ player.SetDiplomacy(diplomacy);
}
// Add player to player manager
diff --git a/binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml b/binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml
index 99dce7bc91..591b21a2d1 100644
--- a/binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml
+++ b/binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml
@@ -22,14 +22,14 @@
1.5
-
+
4.0
12.0
2000
8000
10000
40000
-
+
1.0
diff --git a/binaries/data/mods/public/simulation/templates/gaia/fauna_whale_humpback.xml b/binaries/data/mods/public/simulation/templates/gaia/fauna_whale_humpback.xml
index 2fef6f96bc..2458eaba5c 100644
--- a/binaries/data/mods/public/simulation/templates/gaia/fauna_whale_humpback.xml
+++ b/binaries/data/mods/public/simulation/templates/gaia/fauna_whale_humpback.xml
@@ -35,7 +35,7 @@
-4.0
true
-
+
skittish
20.0
40.0
@@ -43,7 +43,7 @@
30000
1000
2000
-
+
true
2000
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
index 8f7a6144ff..e13218ea0d 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
@@ -25,13 +25,12 @@
6.0
-
-
+
8.0
32.0
2000
8000
15000
60000
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_breed_passive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_breed_passive.xml
index c0064a0745..9bedf79809 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_breed_passive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_breed_passive.xml
@@ -2,7 +2,7 @@
-
+
passive
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml
index 3b10a16dc8..0f638a0288 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml
@@ -18,7 +18,7 @@
true
-
+
false
1000
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_herd_passive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_herd_passive.xml
index 530e979b30..15d6927d22 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_herd_passive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_herd_passive.xml
@@ -2,7 +2,7 @@
-
+
passive
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml
index ee8949679f..9eaedcd2df 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml
@@ -2,7 +2,16 @@
-
+
aggressive
-
+
+
+
+ 1.0
+ 1.0
+ 0.0
+ 4.0
+ 1000
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive.xml
index 37bf7b438b..e4969a8376 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive.xml
@@ -2,7 +2,7 @@
-
+
defensive
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_passive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_passive.xml
index 3ba6cf8fcf..f5aee67c91 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_passive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_passive.xml
@@ -2,7 +2,7 @@
-
+
passive
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_skittish.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_skittish.xml
index 5041f72dbb..4e6b672730 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_skittish.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_skittish.xml
@@ -2,7 +2,7 @@
-
+
skittish
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml
index a894caf37e..88d0a6f6d7 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml
@@ -2,7 +2,16 @@
-
+
violent
-
+
+
+
+ 1.0
+ 1.0
+ 0.0
+ 4.0
+ 1000
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml
index e151926438..6e9d8fa598 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml
@@ -2,7 +2,16 @@
-
- violent
-
+
+ aggressive
+
+
+
+ 1.0
+ 1.0
+ 0.0
+ 4.0
+ 1000
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_defensive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_defensive.xml
index a37f8ea63a..29d6320627 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_defensive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_defensive.xml
@@ -2,7 +2,7 @@
-
+
defensive
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_passive.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_passive.xml
index 4c893034c2..5beb3c57d4 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_passive.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_passive.xml
@@ -2,7 +2,7 @@
-
+
passive
-
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml
index e151926438..0ed57ab7d5 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml
@@ -2,7 +2,16 @@
-
+
violent
-
+
+
+
+ 1.0
+ 1.0
+ 0.0
+ 4.0
+ 1000
+
+