1
0
forked from 0ad/0ad

Substantially rework how Gaia is handled by UnitAI, BuildingAI. Dangerous animals and combat units will be attacked like regular enemies.

This was SVN commit r12317.
This commit is contained in:
Deiz 2012-08-08 05:32:53 +00:00
parent 84305155de
commit 1120701351
3 changed files with 211 additions and 18 deletions

View File

@ -32,6 +32,10 @@ BuildingAI.prototype.OnOwnershipChanged = function(msg)
{ {
if (msg.to != -1) if (msg.to != -1)
this.SetupRangeQuery(msg.to); this.SetupRangeQuery(msg.to);
// Non-Gaia buildings should attack certain Gaia units.
if (msg.to != 0 || this.gaiaUnitsQuery)
this.SetupGaiaRangeQuery(msg.to);
}; };
/** /**
@ -50,6 +54,8 @@ BuildingAI.prototype.OnDestroy = function()
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.enemyUnitsQuery) if (this.enemyUnitsQuery)
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery); cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
if (this.gaiaUnitsQuery)
cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
}; };
/** /**
@ -81,12 +87,58 @@ BuildingAI.prototype.SetupRangeQuery = function(owner)
} }
}; };
// Set up a range query for Gaia units within LOS range which can be attacked.
// This should be called whenever our ownership changes.
BuildingAI.prototype.SetupGaiaRangeQuery = function()
{
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
var owner = cmpOwnership.GetOwner();
var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (this.gaiaUnitsQuery)
{
rangeMan.DestroyActiveQuery(this.gaiaUnitsQuery);
this.gaiaUnitsQuery = undefined;
}
if (owner == -1)
return;
var cmpPlayer = Engine.QueryInterface(playerMan.GetPlayerByID(owner), IID_Player);
if (!cmpPlayer.IsEnemy(0))
return;
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (cmpAttack)
{
var range = cmpAttack.GetRange("Ranged");
// This query is only interested in Gaia entities that can attack.
this.gaiaUnitsQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, [0], IID_Attack, rangeMan.GetEntityFlagMask("normal"));
rangeMan.EnableActiveQuery(this.gaiaUnitsQuery);
}
};
/** /**
* Called when units enter or leave range * Called when units enter or leave range
*/ */
BuildingAI.prototype.OnRangeUpdate = function(msg) BuildingAI.prototype.OnRangeUpdate = function(msg)
{ {
if (msg.tag != this.enemyUnitsQuery) if (msg.tag == this.gaiaUnitsQuery)
{
const filter = function(e) {
var cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI);
return (cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()));
};
if (msg.added.length)
msg.added = msg.added.filter(filter);
if (msg.removed.length)
msg.removed = msg.removed.filter(filter);
}
else if (msg.tag != this.enemyUnitsQuery)
return; return;
if (msg.added.length > 0) if (msg.added.length > 0)

View File

@ -140,6 +140,10 @@ var UnitFsmSpec = {
// ignore newly-seen units by default // ignore newly-seen units by default
}, },
"LosGaiaRangeUpdate": function(msg) {
// ignore newly-seen Gaia units by default
},
"LosHealRangeUpdate": function(msg) { "LosHealRangeUpdate": function(msg) {
// ignore newly-seen injured units by default // ignore newly-seen injured units by default
}, },
@ -188,6 +192,26 @@ var UnitFsmSpec = {
// Individual orders: // Individual orders:
// (these will switch the unit out of formation mode) // (these will switch the unit out of formation mode)
"Order.Stop": function(msg) {
// We have no control over non-domestic animals.
if (this.IsAnimal() && !this.IsDomestic())
{
this.FinishOrder();
return;
}
// Stop moving immediately.
this.StopMoving();
this.FinishOrder();
// No orders left, we're an individual now
if (this.IsAnimal())
this.SetNextState("ANIMAL.IDLE");
else
this.SetNextState("INDIVIDUAL.IDLE");
},
"Order.Walk": function(msg) { "Order.Walk": function(msg) {
// Let players move captured domestic animals around // Let players move captured domestic animals around
if (this.IsAnimal() && !this.IsDomestic()) if (this.IsAnimal() && !this.IsDomestic())
@ -458,6 +482,12 @@ var UnitFsmSpec = {
this.FinishOrder(); this.FinishOrder();
}, },
"Order.Stop": function(msg) {
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
cmpFormation.CallMemberFunction("Stop", [false]);
cmpFormation.Disband();
},
"Order.Attack": function(msg) { "Order.Attack": function(msg) {
// TODO: we should move in formation towards the target, // TODO: we should move in formation towards the target,
// then break up into individuals when close enough to it // then break up into individuals when close enough to it
@ -698,6 +728,14 @@ var UnitFsmSpec = {
} }
}, },
"LosGaiaRangeUpdate": function(msg) {
if (this.GetStance().targetVisibleEnemies)
{
// Start attacking one of the newly-seen enemy (if any)
this.RespondToTargetedEntities(this.GetAttackableEntitiesByPreference(msg.data.added, true));
}
},
"LosHealRangeUpdate": function(msg) { "LosHealRangeUpdate": function(msg) {
this.RespondToHealableEntities(msg.data.added); this.RespondToHealableEntities(msg.data.added);
}, },
@ -1614,9 +1652,7 @@ var UnitFsmSpec = {
{ {
this.Flee(msg.data.attacker, false); this.Flee(msg.data.attacker, false);
} }
else if (this.template.NaturalBehaviour == "violent" || else if (this.IsDangerousAnimal() || this.template.NaturalBehaviour == "defensive")
this.template.NaturalBehaviour == "aggressive" ||
this.template.NaturalBehaviour == "defensive")
{ {
if (this.CanAttack(msg.data.attacker)) if (this.CanAttack(msg.data.attacker))
this.Attack(msg.data.attacker, false); this.Attack(msg.data.attacker, false);
@ -1691,8 +1727,7 @@ var UnitFsmSpec = {
} }
} }
// Start attacking one of the newly-seen enemy (if any) // Start attacking one of the newly-seen enemy (if any)
else if (this.template.NaturalBehaviour == "violent" || else if (this.IsDangerousAnimal())
this.template.NaturalBehaviour == "aggressive")
{ {
this.AttackVisibleEntity(msg.data.added); this.AttackVisibleEntity(msg.data.added);
} }
@ -1781,6 +1816,12 @@ UnitAI.prototype.IsAnimal = function()
return (this.template.NaturalBehaviour ? true : false); return (this.template.NaturalBehaviour ? true : false);
}; };
UnitAI.prototype.IsDangerousAnimal = function()
{
return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" ||
this.template.NaturalBehaviour == "aggressive"));
};
UnitAI.prototype.IsDomestic = function() UnitAI.prototype.IsDomestic = function()
{ {
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
@ -1804,6 +1845,19 @@ UnitAI.prototype.IsGarrisoned = function()
return this.isGarrisoned; return this.isGarrisoned;
}; };
UnitAI.prototype.CanAttackGaia = function()
{
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (!cmpAttack)
return false;
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwnership || cmpOwnership.GetOwner() == 0)
return false;
return true;
};
UnitAI.prototype.OnCreate = function() UnitAI.prototype.OnCreate = function()
{ {
if (this.IsAnimal()) if (this.IsAnimal())
@ -1816,9 +1870,14 @@ UnitAI.prototype.OnCreate = function()
UnitAI.prototype.OnOwnershipChanged = function(msg) UnitAI.prototype.OnOwnershipChanged = function(msg)
{ {
this.SetupRangeQuery(); this.SetupRangeQueries();
if (this.IsHealer())
this.SetupHealRangeQuery(); // If the unit isn't being created or dying, clear orders and reset stance.
if (msg.to != -1 && msg.from != -1)
{
this.SetStance(this.template.DefaultStance);
this.Stop(false);
}
}; };
UnitAI.prototype.OnDestroy = function() UnitAI.prototype.OnDestroy = function()
@ -1832,8 +1891,22 @@ UnitAI.prototype.OnDestroy = function()
rangeMan.DestroyActiveQuery(this.losRangeQuery); rangeMan.DestroyActiveQuery(this.losRangeQuery);
if (this.losHealRangeQuery) if (this.losHealRangeQuery)
rangeMan.DestroyActiveQuery(this.losHealRangeQuery); rangeMan.DestroyActiveQuery(this.losHealRangeQuery);
if (this.losGaiaRangeQuery)
rangeMan.DestroyActiveQuery(this.losGaiaRangeQuery);
}; };
// Wrapper function that sets up the normal, healer, and Gaia range queries.
UnitAI.prototype.SetupRangeQueries = function()
{
this.SetupRangeQuery();
if (this.IsHealer())
this.SetupHealRangeQuery();
if (this.CanAttackGaia() || this.losGaiaRangeQuery)
this.SetupGaiaRangeQuery();
}
// Set up a range query for all enemy units within LOS range // Set up a range query for all enemy units within LOS range
// which can be attacked. // which can be attacked.
// This should be called whenever our ownership changes. // This should be called whenever our ownership changes.
@ -1846,7 +1919,10 @@ UnitAI.prototype.SetupRangeQuery = function()
var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (this.losRangeQuery) if (this.losRangeQuery)
{
rangeMan.DestroyActiveQuery(this.losRangeQuery); rangeMan.DestroyActiveQuery(this.losRangeQuery);
this.losRangeQuery = undefined;
}
var players = []; var players = [];
@ -1905,6 +1981,40 @@ UnitAI.prototype.SetupHealRangeQuery = function()
rangeMan.EnableActiveQuery(this.losHealRangeQuery); rangeMan.EnableActiveQuery(this.losHealRangeQuery);
}; };
// Set up a range query for Gaia units within LOS range which can be attacked.
// This should be called whenever our ownership changes.
UnitAI.prototype.SetupGaiaRangeQuery = function()
{
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
var owner = cmpOwnership.GetOwner();
var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (this.losGaiaRangeQuery)
{
rangeMan.DestroyActiveQuery(this.losGaiaRangeQuery);
this.losGaiaRangeQuery = undefined;
}
if (owner == -1)
return;
var cmpPlayer = Engine.QueryInterface(playerMan.GetPlayerByID(owner), IID_Player);
if (!cmpPlayer.IsEnemy(0))
return;
// Only create the query if Gaia is our enemy and we can attack.
if (this.CanAttackGaia())
{
var range = this.GetQueryRange(IID_Attack);
// This query is only interested in Gaia entities that can attack.
this.losGaiaRangeQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, [0], IID_Attack, rangeMan.GetEntityFlagMask("normal"));
rangeMan.EnableActiveQuery(this.losGaiaRangeQuery);
}
};
//// FSM linkage functions //// //// FSM linkage functions ////
UnitAI.prototype.SetNextState = function(state) UnitAI.prototype.SetNextState = function(state)
@ -2157,6 +2267,8 @@ UnitAI.prototype.OnRangeUpdate = function(msg)
{ {
if (msg.tag == this.losRangeQuery) if (msg.tag == this.losRangeQuery)
UnitFsm.ProcessMessage(this, {"type": "LosRangeUpdate", "data": msg}); UnitFsm.ProcessMessage(this, {"type": "LosRangeUpdate", "data": msg});
else if (msg.tag == this.losGaiaRangeQuery)
UnitFsm.ProcessMessage(this, {"type": "LosGaiaRangeUpdate", "data": msg});
else if (msg.tag == this.losHealRangeQuery) else if (msg.tag == this.losHealRangeQuery)
UnitFsm.ProcessMessage(this, {"type": "LosHealRangeUpdate", "data": msg}); UnitFsm.ProcessMessage(this, {"type": "LosHealRangeUpdate", "data": msg});
}; };
@ -2743,6 +2855,9 @@ UnitAI.prototype.ComputeWalkingDistance = function()
// Return the total distance to the target // Return the total distance to the target
return distance; return distance;
case "Stop":
return 0;
default: default:
error("ComputeWalkingDistance: Unrecognised order type '"+order.type+"'"); error("ComputeWalkingDistance: Unrecognised order type '"+order.type+"'");
return distance; return distance;
@ -2769,6 +2884,14 @@ UnitAI.prototype.Walk = function(x, z, queued)
this.AddOrder("Walk", { "x": x, "z": z, "force": true }, queued); this.AddOrder("Walk", { "x": x, "z": z, "force": true }, queued);
}; };
/**
* Adds stop order to queue, forced by the player.
*/
UnitAI.prototype.Stop = function(queued)
{
this.AddOrder("Stop", undefined, queued);
};
/** /**
* Adds walk-to-target order to queue, this only occurs in response * Adds walk-to-target order to queue, this only occurs in response
* to a player order, and so is forced. * to a player order, and so is forced.
@ -3046,12 +3169,8 @@ UnitAI.prototype.SwitchToStance = function(stance)
if (stance == "standground") if (stance == "standground")
this.StopMoving(); this.StopMoving();
// Reset the range query, since the range depends on stance // Reset the range queries, since the range depends on stance.
this.SetupRangeQuery(); this.SetupRangeQueries();
// Just if we are a healer
// TODO maybe move those two to a SetupRangeQuerys()
if (this.IsHealer())
this.SetupHealRangeQuery();
}; };
/** /**
@ -3060,7 +3179,7 @@ UnitAI.prototype.SwitchToStance = function(stance)
*/ */
UnitAI.prototype.FindNewTargets = function() UnitAI.prototype.FindNewTargets = function()
{ {
if (!this.losRangeQuery) if (!this.losRangeQuery && !this.losGaiaRangeQuery)
return false; return false;
var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
@ -3069,6 +3188,13 @@ UnitAI.prototype.FindNewTargets = function()
if (!this.GetStance().targetVisibleEnemies) if (!this.GetStance().targetVisibleEnemies)
return false; return false;
// If no regular enemies were found, attempt to attack a hostile Gaia entity.
if (this.losGaiaRangeQuery && (!ents || !ents.length))
{
ents = rangeMan.ResetActiveQuery(this.losGaiaRangeQuery);
return this.RespondToTargetedEntities(this.GetAttackableEntitiesByPreference(ents, true));
}
return this.RespondToTargetedEntities(this.GetAttackableEntitiesByPreference(ents)); return this.RespondToTargetedEntities(this.GetAttackableEntitiesByPreference(ents));
}; };
@ -3410,12 +3536,24 @@ UnitAI.prototype.MoveRandomly = function(distance)
cmpMotion.MoveToPointRange(tx, tz, distance, distance); cmpMotion.MoveToPointRange(tx, tz, distance, distance);
}; };
UnitAI.prototype.GetAttackableEntitiesByPreference = function(ents) UnitAI.prototype.GetAttackableEntitiesByPreference = function(ents, filterGaia)
{ {
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (!cmpAttack) if (!cmpAttack)
return []; return [];
if (filterGaia)
{
const filter = function(e) {
var cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI);
return (cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()));
};
return ents
.filter(function (v, i, a) { return cmpAttack.CanAttack(v) && filter(v); })
.sort(function (a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); });
}
return ents return ents
.filter(function (v, i, a) { return cmpAttack.CanAttack(v); }) .filter(function (v, i, a) { return cmpAttack.CanAttack(v); })
.sort(function (a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); }); .sort(function (a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); });

View File

@ -128,8 +128,11 @@ function LoadPlayerSettings(settings, newPlayers)
cmpPlayer.SetCiv(pDefs.Civ); cmpPlayer.SetCiv(pDefs.Civ);
cmpPlayer.SetColour(pDefs.Colour.r, pDefs.Colour.g, pDefs.Colour.b); cmpPlayer.SetColour(pDefs.Colour.r, pDefs.Colour.g, pDefs.Colour.b);
// Gaia should be its own ally.
cmpPlayer.SetAlly(0);
// Gaia is everyone's enemy // Gaia is everyone's enemy
for (var j = 0; j < numPlayers; ++j) for (var j = 1; j < numPlayers; ++j)
cmpPlayer.SetEnemy(j); cmpPlayer.SetEnemy(j);
} }
} }