1
0
forked from 0ad/0ad

# Rewrite unit AI code.

Use HFSM for unit AI.
Support queuing orders.
Automatically attack back when attacked.
Automatically gather from farms after building them.

This was SVN commit r7775.
This commit is contained in:
Ykkrosh 2010-07-21 16:09:58 +00:00
parent 8a1aa101c1
commit 4e5c5e2d8f
12 changed files with 608 additions and 408 deletions

View File

@ -213,6 +213,9 @@ Attack.prototype.CauseDamage = function(data)
if (!cmpDamageReceiver)
return;
cmpDamageReceiver.TakeDamage(strengths.hack, strengths.pierce, strengths.crush);
Engine.PostMessage(data.target, MT_Attacked,
{ "attacker": this.entity, "target": data.target });
};
Engine.RegisterComponentType(IID_Attack, "Attack", Attack);

View File

@ -43,6 +43,7 @@ Builder.prototype.GetRange = function()
/**
* Build/repair the target entity. This should only be called after a successful range check.
* It should be called at a rate of once per second.
* Returns obj with obj.finished==true if this is a repair and it's fully repaired.
*/
Builder.prototype.PerformBuilding = function(target)
{
@ -52,8 +53,8 @@ Builder.prototype.PerformBuilding = function(target)
var cmpFoundation = Engine.QueryInterface(target, IID_Foundation);
if (cmpFoundation)
{
var finished = cmpFoundation.Build(this.entity, rate);
return { "finished": finished };
cmpFoundation.Build(this.entity, rate);
return { "finished": false };
}
else
{

View File

@ -58,8 +58,10 @@ Foundation.prototype.OnDestroy = function()
Foundation.prototype.Build = function(builderEnt, work)
{
// Do nothing if we've already finished building
// (The entity will be destroyed soon after completion so
// this won't happen much)
if (this.buildProgress == 1.0)
return true;
return;
// Calculate the amount of progress that will be added (where 1.0 = completion)
var cmpCost = Engine.QueryInterface(this.entity, IID_Cost);
@ -110,13 +112,10 @@ Foundation.prototype.Build = function(builderEnt, work)
var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);
cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints());
Engine.DestroyEntity(this.entity);
Engine.PostMessage(this.entity, MT_ConstructionFinished,
{ "entity": this.entity, "newentity": building });
return true;
}
else
{
return false;
Engine.DestroyEntity(this.entity);
}
};

View File

@ -42,7 +42,7 @@ ResourceGatherer.prototype.GetGatherRates = function()
ResourceGatherer.prototype.GetRange = function()
{
return { "max": 4, "min": 0 };
return { "max": 2, "min": 0 };
// maybe this should depend on the unit or target or something?
}
@ -67,7 +67,11 @@ ResourceGatherer.prototype.PerformGather = function(target)
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(cmpOwnership.GetOwner()), IID_Player);
cmpPlayer.AddResource(type.generic, status.amount);
// Tell the target we're gathering from it
Engine.PostMessage(target, MT_ResourceGather,
{ "entity": target, "gatherer": this.entity });
return status;
};

View File

@ -32,14 +32,19 @@ Timer.prototype.OnUpdate = function(msg)
for each (var id in run)
{
var t = this.timers[id];
if (!t)
continue; // an earlier timer might have cancelled this one, so skip it
var cmp = Engine.QueryInterface(t[0], t[1]);
if (!cmp)
continue; // the entity was probably destroyed
try {
var lateness = this.time - t[3];
cmp[t[2]](t[4], lateness);
} catch (e) {
var stack = e.stack.trimRight().replace(/^/mg, ' '); // indent the stack trace
print("Error in timer on entity "+t[0]+", IID "+t[1]+", function "+t[2]+": "+e+"\n"+stack+"\n");
// TODO: should report in an error log
error("Error in timer on entity "+t[0]+", IID "+t[1]+", function "+t[2]+": "+e+"\n"+stack+"\n");
}
delete this.timers[id];
}

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,6 @@
Engine.RegisterInterface("Attack");
// Message sent from Attack to the target entity, each
// time the target is damaged.
// Data: { attacker: 123, target: 234 }
Engine.RegisterMessageType("Attacked");

View File

@ -1 +1,7 @@
Engine.RegisterInterface("Foundation");
// Message sent from Foundation to its own entity when construction
// has been completed.
// Units can watch for this and change task once it's complete.
// Data: { entity: 123, newentity: 234 }
Engine.RegisterMessageType("ConstructionFinished");

View File

@ -1 +1,5 @@
Engine.RegisterInterface("ResourceGatherer");
// Message sent from ResourceGatherers to a ResourceSupply entity
// each time they gather resources from it
Engine.RegisterMessageType("ResourceGather");

View File

@ -1,5 +1 @@
Engine.RegisterInterface("ResourceSupply");
// Message sent from gatherers to ResourceSupply entities
// when beginning to gather
Engine.RegisterMessageType("ResourceGather");

View File

@ -16,7 +16,7 @@ function ProcessCommand(player, cmd)
{
var ai = Engine.QueryInterface(ent, IID_UnitAI);
if (ai)
ai.Walk(cmd.x, cmd.z);
ai.Walk(cmd.x, cmd.z, cmd.queued);
}
break;
@ -25,7 +25,7 @@ function ProcessCommand(player, cmd)
{
var ai = Engine.QueryInterface(ent, IID_UnitAI);
if (ai)
ai.Attack(cmd.target);
ai.Attack(cmd.target, cmd.queued);
}
break;
@ -35,7 +35,7 @@ function ProcessCommand(player, cmd)
{
var ai = Engine.QueryInterface(ent, IID_UnitAI);
if (ai)
ai.Repair(cmd.target);
ai.Repair(cmd.target, cmd.queued);
}
break;
@ -44,7 +44,7 @@ function ProcessCommand(player, cmd)
{
var ai = Engine.QueryInterface(ent, IID_UnitAI);
if (ai)
ai.Gather(cmd.target);
ai.Gather(cmd.target, cmd.queued);
}
break;
@ -118,7 +118,8 @@ function ProcessCommand(player, cmd)
ProcessCommand(player, {
"type": "repair",
"entities": cmd.entities,
"target": ent
"target": ent,
"queued": cmd.queued
});
break;

View File

@ -152,7 +152,7 @@ FSM.prototype.SetNextState = function(obj, state)
FSM.prototype.ProcessMessage = function(obj, msg)
{
// print("ProcessMessage(obj, "+uneval(msg)+")\n");
// warn("ProcessMessage(obj, "+uneval(msg)+")");
var func = this.states[obj.fsmStateName][msg.type];
if (!func)