forked from 0ad/0ad
# Add resource shuttling.
Add resource-carrying animations/props to an actor for testing. Warn more quietly about invalid cursor names. This was SVN commit r8589.
This commit is contained in:
parent
6325a1eafe
commit
4100a13e0a
@ -6,6 +6,10 @@
|
||||
<animations>
|
||||
<animation file="infantry/spear/idle/isp_01.psa" name="Idle" speed="200"/>
|
||||
<animation file="biped/walk_spearshield.psa" name="Walk" speed="120"/>
|
||||
<animation file="biped/walk_spearshield.psa" name="carry_food" speed="120"/>
|
||||
<animation file="biped/walk_spearshield.psa" name="carry_wood" speed="120"/>
|
||||
<animation file="biped/walk_spearshield.psa" name="carry_stone" speed="120"/>
|
||||
<animation file="biped/walk_spearshield.psa" name="carry_metal" speed="120"/>
|
||||
<animation event="0.5" file="biped/inf_spear_shield_atk_a.psa" name="Melee" speed="100"/>
|
||||
<animation event="0.5" file="biped/inf_spear_shield_atk_b.psa" name="Melee" speed="100"/>
|
||||
<animation file="infantry/sword/move/run/isw_s_off_01.psa" name="Run" speed="5"/>
|
||||
@ -110,6 +114,40 @@
|
||||
<prop actor="props/units/tools/mallet.xml" attachpoint="r_hand"/>
|
||||
</props>
|
||||
</variant>
|
||||
|
||||
<variant name="carry_food">
|
||||
<props>
|
||||
<prop attachpoint="shield"/>
|
||||
<prop attachpoint="r_hand"/>
|
||||
<prop attachpoint="l_hand"/>
|
||||
<prop actor="props/units/tools/basket.xml" attachpoint="l_leg"/>
|
||||
<prop actor="fauna/pig1.xml" attachpoint="head"/>
|
||||
</props>
|
||||
</variant>
|
||||
<variant name="carry_wood">
|
||||
<props>
|
||||
<prop attachpoint="shield"/>
|
||||
<prop attachpoint="r_hand"/>
|
||||
<prop actor="props/units/tools/axe.xml" attachpoint="l_hand"/>
|
||||
<prop actor="props/flora/bush_tropic_a.xml" attachpoint="head"/>
|
||||
</props>
|
||||
</variant>
|
||||
<variant name="carry_stone">
|
||||
<props>
|
||||
<prop attachpoint="shield"/>
|
||||
<prop attachpoint="r_hand"/>
|
||||
<prop actor="props/units/tools/pick.xml" attachpoint="l_hand"/>
|
||||
<prop actor="geology/gray1.xml" attachpoint="head"/>
|
||||
</props>
|
||||
</variant>
|
||||
<variant name="carry_metal">
|
||||
<props>
|
||||
<prop attachpoint="shield"/>
|
||||
<prop attachpoint="r_hand"/>
|
||||
<prop actor="props/units/tools/pick.xml" attachpoint="l_hand"/>
|
||||
<prop actor="geology/gray1.xml" attachpoint="head"/>
|
||||
</props>
|
||||
</variant>
|
||||
</group>
|
||||
<material>player_trans.xml</material>
|
||||
</actor>
|
||||
|
BIN
binaries/data/mods/public/art/textures/cursors/action-return-food.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/cursors/action-return-food.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
1 1
|
BIN
binaries/data/mods/public/art/textures/cursors/action-return-metal.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/cursors/action-return-metal.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
1 1
|
BIN
binaries/data/mods/public/art/textures/cursors/action-return-stone.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/cursors/action-return-stone.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
1 1
|
BIN
binaries/data/mods/public/art/textures/cursors/action-return-wood.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/cursors/action-return-wood.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
1 1
|
@ -114,8 +114,9 @@ function determineAction(x, y, fromMinimap)
|
||||
if (haveRallyPoints && selection.indexOf(targets[0]) != -1)
|
||||
return {"type": "unset-rallypoint"};
|
||||
|
||||
// Check if the target entity is a resource, foundation, or enemy unit.
|
||||
// Check if any entities in the selection can gather the requested resource, can build the foundation, or can attack the enemy
|
||||
// Check if the target entity is a resource, dropsite, foundation, or enemy unit.
|
||||
// Check if any entities in the selection can gather the requested resource,
|
||||
// can return to the dropsite, can build the foundation, or can attack the enemy
|
||||
for each (var entityID in selection)
|
||||
{
|
||||
var entState = GetEntityState(entityID);
|
||||
@ -126,6 +127,11 @@ function determineAction(x, y, fromMinimap)
|
||||
var enemyOwned = ((targetState.player != entState.player && entState.diplomacy && entState.diplomacy[targetState.player - 1] < 0)? true : false);
|
||||
var gaiaOwned = ((targetState.player == 0)? true : false);
|
||||
|
||||
// Find the resource type we're carrying, if any
|
||||
var carriedType = undefined;
|
||||
if (entState.resourceCarrying && entState.resourceCarrying.length)
|
||||
carriedType = entState.resourceCarrying[0].type;
|
||||
|
||||
if (targetState.garrisonHolder && playerOwned && Engine.HotkeyIsPressed("session.garrison"))
|
||||
{
|
||||
return {"type": "garrison", "cursor": "action-garrison", "target": targets[0]};
|
||||
@ -139,6 +145,11 @@ function determineAction(x, y, fromMinimap)
|
||||
if (resource)
|
||||
return {"type": "gather", "cursor": "action-gather-"+resource, "target": targets[0]};
|
||||
}
|
||||
else if (targetState.resourceDropsite && playerOwned && carriedType &&
|
||||
targetState.resourceDropsite.types.indexOf(carriedType) != -1)
|
||||
{
|
||||
return {"type": "returnresource", "cursor": "action-return-"+carriedType, "target": targets[0]};
|
||||
}
|
||||
else if (targetState.foundation && entState.buildEntities && playerOwned)
|
||||
{
|
||||
return {"type": "build", "cursor": "action-build", "target": targets[0]};
|
||||
@ -517,6 +528,11 @@ function handleInputAfterGui(ev)
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
|
||||
return true;
|
||||
|
||||
case "returnresource":
|
||||
Engine.PostNetworkCommand({"type": "returnresource", "entities": selection, "target": action.target, "queued": queued});
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
|
||||
return true;
|
||||
|
||||
case "garrison":
|
||||
Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued});
|
||||
//Need to play some sound here??
|
||||
|
@ -96,6 +96,23 @@ function displaySingle(entState, template)
|
||||
getGUIObjectByName("resources").hidden = true;
|
||||
}
|
||||
|
||||
// Resource carrying
|
||||
if (entState.resourceCarrying && entState.resourceCarrying.length)
|
||||
{
|
||||
var carried = entState.resourceCarrying[0];
|
||||
// (we should only be carrying one resource type at once, so just display the first)
|
||||
|
||||
getGUIObjectByName("resourceCarryingIcon").hidden = false;
|
||||
getGUIObjectByName("resourceCarryingText").hidden = false;
|
||||
getGUIObjectByName("resourceCarryingIcon").cell_id = RESOURCE_ICON_CELL_IDS[carried.type];
|
||||
getGUIObjectByName("resourceCarryingText").caption = carried.amount + "/" + carried.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
getGUIObjectByName("resourceCarryingIcon").hidden = true;
|
||||
getGUIObjectByName("resourceCarryingText").hidden = true;
|
||||
}
|
||||
|
||||
// Set Captions
|
||||
getGUIObjectByName("specific").caption = specificName;
|
||||
getGUIObjectByName("player").caption = playerName;
|
||||
|
@ -468,6 +468,10 @@
|
||||
|
||||
<!-- Armour icon -->
|
||||
<object size="0 48 48 96" type="image" name="armourIcon" sprite="snIconSheetStance" cell_id="3" tooltip_style="snToolTip"/>
|
||||
|
||||
<!-- Resource carrying icon/counter -->
|
||||
<object size="0 96 24 120" type="image" name="resourceCarryingIcon" style="resourceIcon"/>
|
||||
<object size="18 102 56 114" type="text" name="resourceCarryingText" style="resourceText"/>
|
||||
</object>
|
||||
|
||||
<!-- Big unit icon -->
|
||||
|
@ -153,6 +153,15 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
if (cmpResourceGatherer)
|
||||
{
|
||||
ret.resourceGatherRates = cmpResourceGatherer.GetGatherRates();
|
||||
ret.resourceCarrying = cmpResourceGatherer.GetCarryingStatus();
|
||||
}
|
||||
|
||||
var cmpResourceDropsite = Engine.QueryInterface(ent, IID_ResourceDropsite);
|
||||
if (cmpResourceDropsite)
|
||||
{
|
||||
ret.resourceDropsite = {
|
||||
"types": cmpResourceDropsite.GetTypes()
|
||||
};
|
||||
}
|
||||
|
||||
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
|
||||
|
@ -122,20 +122,17 @@ Player.prototype.GetResourceCounts = function()
|
||||
};
|
||||
|
||||
/**
|
||||
* Add resource of specified type to player and increase gathered resources statistics
|
||||
* Add resource of specified type to player
|
||||
* @param type Generic type of resource (string)
|
||||
* @param amount Amount of resource, whick should be added (integer)
|
||||
* @param specificType Specific type of resource (string, optional)
|
||||
*/
|
||||
Player.prototype.AddResource = function(type, amount, specificType)
|
||||
Player.prototype.AddResource = function(type, amount)
|
||||
{
|
||||
this.resourceCount[type] += (+amount);
|
||||
var cmpStatisticsTracker = Engine.QueryInterface(this.entity, IID_StatisticsTracker);
|
||||
cmpStatisticsTracker.IncreaseResourceGatheredCounter(type, amount, specificType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add resources to player but not increase gathered resources statistics
|
||||
* Add resources to player
|
||||
*/
|
||||
Player.prototype.AddResources = function(amounts)
|
||||
{
|
||||
|
@ -1,9 +1,6 @@
|
||||
function ResourceDropsite() {}
|
||||
|
||||
ResourceDropsite.prototype.Schema =
|
||||
"<element name='Radius'>" +
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"<element name='Types'>" +
|
||||
"<list>" +
|
||||
"<oneOrMore>" +
|
||||
@ -17,8 +14,21 @@ ResourceDropsite.prototype.Schema =
|
||||
"</list>" +
|
||||
"</element>";
|
||||
|
||||
/*
|
||||
* TODO: this all needs to be designed and implemented
|
||||
|
||||
/**
|
||||
* Returns the list of resource types accepted by this dropsite.
|
||||
*/
|
||||
ResourceDropsite.prototype.GetTypes = function()
|
||||
{
|
||||
return this.template.Types.split(/\s+/);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether this dropsite accepts the given generic type of resource.
|
||||
*/
|
||||
ResourceDropsite.prototype.AcceptsType = function(type)
|
||||
{
|
||||
return this.GetTypes().indexOf(type) != -1;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_ResourceDropsite, "ResourceDropsite", ResourceDropsite);
|
||||
|
@ -11,6 +11,12 @@ ResourceGatherer.prototype.Schema =
|
||||
"<stone.rock>3</stone.rock>" +
|
||||
"<wood.tree>2</wood.tree>" +
|
||||
"</Rates>" +
|
||||
"<Capacities>" +
|
||||
"<food>10</food>" +
|
||||
"<metal>10</metal>" +
|
||||
"<stone>10</stone>" +
|
||||
"<wood>10</wood>" +
|
||||
"</Capacities>" +
|
||||
"</a:example>" +
|
||||
"<element name='MaxDistance' a:help='Max resource-gathering distance'>" +
|
||||
"<ref name='positiveDecimal'/>" +
|
||||
@ -39,10 +45,53 @@ ResourceGatherer.prototype.Schema =
|
||||
"<optional><element name='metal.ore' a:help='Ore gather rate(overrides \"metal\")'><ref name='positiveDecimal'/></element></optional>" +
|
||||
"<optional><element name='metal.treasure' a:help='Treasure gather rate(overrides \"metal\")'><ref name='positiveDecimal'/></element></optional>" +
|
||||
"</interleave>" +
|
||||
"</element>" +
|
||||
"<element name='Capacities' a:help='Per-resource-type maximum carrying capacity'>" +
|
||||
"<interleave>" +
|
||||
"<element name='food' a:help='Food capacity'><ref name='positiveDecimal'/></element>" +
|
||||
"<element name='wood' a:help='Wood capacity'><ref name='positiveDecimal'/></element>" +
|
||||
"<element name='stone' a:help='Stone capacity'><ref name='positiveDecimal'/></element>" +
|
||||
"<element name='metal' a:help='Metal capacity'><ref name='positiveDecimal'/></element>" +
|
||||
"</interleave>" +
|
||||
"</element>";
|
||||
|
||||
ResourceGatherer.prototype.Init = function()
|
||||
{
|
||||
this.carrying = {}; // { type: integer amount currently carried }
|
||||
// (Note that this component supports carrying multiple types of resources,
|
||||
// each with an independent capacity, but the rest of the game currently
|
||||
// ensures and assumes we'll only be carrying one type at once)
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns data about what resources the unit is currently carrying,
|
||||
* in the form [ {"type":"wood", "amount":7, "max":10} ]
|
||||
*/
|
||||
ResourceGatherer.prototype.GetCarryingStatus = function()
|
||||
{
|
||||
var ret = [];
|
||||
for (var type in this.carrying)
|
||||
{
|
||||
ret.push({
|
||||
"type": type,
|
||||
"amount": this.carrying[type],
|
||||
"max": +this.template.Capacities[type]
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the type of one particular resource this unit is
|
||||
* currently carrying, or undefined if none.
|
||||
*/
|
||||
ResourceGatherer.prototype.GetMainCarryingType = function()
|
||||
{
|
||||
// Return the first key, if any
|
||||
for (var type in this.carrying)
|
||||
return type;
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
ResourceGatherer.prototype.GetGatherRates = function()
|
||||
@ -73,19 +122,33 @@ ResourceGatherer.prototype.PerformGather = function(target)
|
||||
var cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply);
|
||||
var type = cmpResourceSupply.GetType();
|
||||
|
||||
var status = cmpResourceSupply.TakeResources(rate);
|
||||
// Initialise the carried count if necessary
|
||||
if (!this.carrying[type.generic])
|
||||
this.carrying[type.generic] = 0;
|
||||
|
||||
// Give the gathered resources to the player
|
||||
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(cmpOwnership.GetOwner()), IID_Player);
|
||||
cmpPlayer.AddResource(type.generic, status.amount, type.specific);
|
||||
// Find the maximum so we won't exceed our capacity
|
||||
var maxGathered = this.template.Capacities[type.generic] - this.carrying[type.generic];
|
||||
|
||||
var status = cmpResourceSupply.TakeResources(Math.min(rate, maxGathered));
|
||||
|
||||
this.carrying[type.generic] += status.amount;
|
||||
|
||||
// Update stats of how much the player collected.
|
||||
// (We have to do it here rather than at the dropsite, because we
|
||||
// need to know what subtype it was)
|
||||
var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
|
||||
if (cmpStatisticsTracker)
|
||||
cmpStatisticsTracker.IncreaseResourceGatheredCounter(type.generic, status.amount, type.specific);
|
||||
|
||||
// Tell the target we're gathering from it
|
||||
Engine.PostMessage(target, MT_ResourceGather,
|
||||
{ "entity": target, "gatherer": this.entity });
|
||||
|
||||
return status;
|
||||
return {
|
||||
"amount": status.amount,
|
||||
"exhausted": status.exhausted,
|
||||
"filled": (this.carrying[type.generic] >= this.template.Capacities[type.generic])
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -110,4 +173,58 @@ ResourceGatherer.prototype.GetTargetGatherRate = function(target)
|
||||
return (rate || 0) * this.template.BaseSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this unit can carry more of the given type of resource.
|
||||
* (This ignores whether the unit is actually able to gather that
|
||||
* resource type or not.)
|
||||
*/
|
||||
ResourceGatherer.prototype.CanCarryMore = function(type)
|
||||
{
|
||||
var amount = (this.carrying[type] || 0);
|
||||
return (amount < this.template.Capacities[type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether this unit is carrying any resources of a type that is
|
||||
* not the requested type. (This is to support cases where the unit is
|
||||
* only meant to be able to carry one type at once.)
|
||||
*/
|
||||
ResourceGatherer.prototype.IsCarryingAnythingExcept = function(exceptedType)
|
||||
{
|
||||
for (var type in this.carrying)
|
||||
if (type != exceptedType)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer our carried resources to our owner immediately.
|
||||
* Only resources of the given types will be transferred.
|
||||
* (This should typically be called after reaching a dropsite).
|
||||
*/
|
||||
ResourceGatherer.prototype.CommitResources = function(types)
|
||||
{
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
|
||||
for each (var type in types)
|
||||
{
|
||||
if (type in this.carrying)
|
||||
{
|
||||
cmpPlayer.AddResource(type, this.carrying[type]);
|
||||
delete this.carrying[type];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop all currently-carried resources.
|
||||
* (Currently they just vanish after being dropped - we don't bother depositing
|
||||
* them onto the ground.)
|
||||
*/
|
||||
ResourceGatherer.prototype.DropResources = function()
|
||||
{
|
||||
this.carrying = {};
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_ResourceGatherer, "ResourceGatherer", ResourceGatherer);
|
||||
|
@ -147,6 +147,24 @@ var UnitFsmSpec = {
|
||||
}
|
||||
},
|
||||
|
||||
"Order.ReturnResource": function(msg) {
|
||||
// Try to move to the dropsite
|
||||
if (this.MoveToTarget(this.order.data.target))
|
||||
{
|
||||
// We've started walking to the target
|
||||
this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, we can't reach the dropsite.
|
||||
// Maybe we should try to pick another dropsite, to find an
|
||||
// accessible one?
|
||||
// For now, just give up.
|
||||
this.FinishOrder();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
"Order.Repair": function(msg) {
|
||||
// Try to move within range
|
||||
if (this.MoveToTargetRange(this.order.data.target, IID_Builder))
|
||||
@ -209,6 +227,13 @@ var UnitFsmSpec = {
|
||||
cmpFormation.Disband();
|
||||
},
|
||||
|
||||
"Order.ReturnResource": function(msg) {
|
||||
// TODO: see notes in Order.Attack
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
cmpFormation.CallMemberFunction("ReturnResource", [msg.data.target, false]);
|
||||
cmpFormation.Disband();
|
||||
},
|
||||
|
||||
"IDLE": {
|
||||
"enter": function() {
|
||||
this.SelectAnimation("idle");
|
||||
@ -410,8 +435,50 @@ var UnitFsmSpec = {
|
||||
// Check we can still reach the target
|
||||
if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer))
|
||||
{
|
||||
// Gather the resources:
|
||||
|
||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
||||
|
||||
// If we've already got some resources but they're the wrong type,
|
||||
// drop them first to ensure we're only ever carrying one type
|
||||
if (cmpResourceGatherer.IsCarryingAnythingExcept(this.order.data.type.generic))
|
||||
cmpResourceGatherer.DropResources();
|
||||
|
||||
// Collect from the target
|
||||
var status = cmpResourceGatherer.PerformGather(this.order.data.target);
|
||||
|
||||
// TODO: if exhausted, we should probably stop immediately
|
||||
// and choose a new target
|
||||
|
||||
// If we've collected as many resources as possible,
|
||||
// return to the nearest dropsite
|
||||
if (status.filled)
|
||||
{
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
|
||||
// Find dropsites owned by this unit's player
|
||||
var players = [];
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (cmpOwnership)
|
||||
players.push(cmpOwnership.GetOwner());
|
||||
|
||||
var dropsites = cmpRangeManager.ExecuteQuery(this.entity, -1, players, IID_ResourceDropsite);
|
||||
|
||||
// Try to find the first (nearest) dropsite which supports this resource type
|
||||
for each (var dropsite in dropsites)
|
||||
{
|
||||
var cmpDropsite = Engine.QueryInterface(dropsite, IID_ResourceDropsite);
|
||||
if (!cmpDropsite.AcceptsType(this.order.data.type.generic))
|
||||
continue;
|
||||
|
||||
// This dropsite is okay - return our resources to it
|
||||
this.PushOrderFront("ReturnResource", { "target": dropsite });
|
||||
return;
|
||||
}
|
||||
|
||||
// Oh no, couldn't find any drop sites.
|
||||
// Give up and stand here like a lemon.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -462,6 +529,39 @@ var UnitFsmSpec = {
|
||||
},
|
||||
},
|
||||
|
||||
// Returning to dropsite
|
||||
"RETURNRESOURCE": {
|
||||
"APPROACHING": {
|
||||
"enter": function () {
|
||||
// Work out what we're carrying, in order to select an appropriate animation
|
||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
||||
var typename = "carry_" + cmpResourceGatherer.GetMainCarryingType();
|
||||
this.SelectAnimation(typename, false, this.GetWalkSpeed());
|
||||
},
|
||||
|
||||
"MoveCompleted": function() {
|
||||
var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite);
|
||||
if (cmpResourceDropsite)
|
||||
{
|
||||
// TODO: check the dropsite really is in range
|
||||
// (we didn't get stopped before reaching it)
|
||||
|
||||
var dropsiteTypes = cmpResourceDropsite.GetTypes();
|
||||
|
||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
||||
cmpResourceGatherer.CommitResources(dropsiteTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The dropsite was destroyed or something.
|
||||
// TODO: We ought to look for a new one, probably.
|
||||
}
|
||||
|
||||
this.FinishOrder();
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"REPAIR": {
|
||||
"APPROACHING": {
|
||||
"enter": function () {
|
||||
@ -1007,6 +1107,7 @@ UnitAI.prototype.ComputeWalkingDistance = function()
|
||||
case "WalkToTarget":
|
||||
case "Attack":
|
||||
case "Gather":
|
||||
case "ReturnResource":
|
||||
case "Repair":
|
||||
// Find the target unit's position
|
||||
var cmpTargetPosition = Engine.QueryInterface(order.data.target, IID_Position);
|
||||
@ -1024,7 +1125,7 @@ UnitAI.prototype.ComputeWalkingDistance = function()
|
||||
return distance;
|
||||
|
||||
default:
|
||||
error("Unrecognised order type '"+order.type+"'");
|
||||
error("ComputeWalkingDistance: Unrecognised order type '"+order.type+"'");
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
@ -1089,6 +1190,17 @@ UnitAI.prototype.Gather = function(target, queued)
|
||||
this.AddOrder("Gather", { "target": target, "type": type }, queued);
|
||||
};
|
||||
|
||||
UnitAI.prototype.ReturnResource = function(target, queued)
|
||||
{
|
||||
if (!this.CanReturnResource(target))
|
||||
{
|
||||
this.WalkToTarget(target, queued);
|
||||
return;
|
||||
}
|
||||
|
||||
this.AddOrder("ReturnResource", { "target": target }, queued);
|
||||
};
|
||||
|
||||
UnitAI.prototype.Repair = function(target, queued)
|
||||
{
|
||||
if (!this.CanRepair(target))
|
||||
@ -1162,6 +1274,34 @@ UnitAI.prototype.CanGather = function(target)
|
||||
return true;
|
||||
};
|
||||
|
||||
UnitAI.prototype.CanReturnResource = function(target)
|
||||
{
|
||||
// Formation controllers should always respond to commands
|
||||
// (then the individual units can make up their own minds)
|
||||
if (this.IsFormationController())
|
||||
return true;
|
||||
|
||||
// Verify that we're able to respond to ReturnResource commands
|
||||
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
||||
if (!cmpResourceGatherer)
|
||||
return false;
|
||||
|
||||
// Verify that the target is a dropsite
|
||||
var cmpResourceDropsite = Engine.QueryInterface(target, IID_ResourceDropsite);
|
||||
if (!cmpResourceDropsite)
|
||||
return false;
|
||||
|
||||
// Verify that we are carrying some resources,
|
||||
// and can return our current resource to this target
|
||||
var type = cmpResourceGatherer.GetMainCarryingType();
|
||||
if (!type || !cmpResourceDropsite.AcceptsType(type))
|
||||
return false;
|
||||
|
||||
// TODO: should verify it's owned by the correct player, etc
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
UnitAI.prototype.CanRepair = function(target)
|
||||
{
|
||||
// Formation controllers should always respond to commands
|
||||
|
@ -41,6 +41,12 @@ function ProcessCommand(player, cmd)
|
||||
cmpUnitAI.Gather(cmd.target, cmd.queued);
|
||||
break;
|
||||
|
||||
case "returnresource":
|
||||
var cmpUnitAI = GetFormationUnitAI(cmd.entities);
|
||||
if (cmpUnitAI)
|
||||
cmpUnitAI.ReturnResource(cmd.target, cmd.queued);
|
||||
break;
|
||||
|
||||
case "train":
|
||||
var queue = Engine.QueryInterface(cmd.entity, IID_TrainingQueue);
|
||||
if (queue)
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Hierarchical finite state machine implementation.
|
||||
//
|
||||
// FSMs are specified as a JS data structure;
|
||||
// see e.g. AnimalAI.js for an example of the syntax.
|
||||
// see e.g. UnitAI.js for an example of the syntax.
|
||||
//
|
||||
// FSMs are implicitly linked with an external object.
|
||||
// That object stores all FSM-related state.
|
||||
|
@ -40,7 +40,6 @@
|
||||
<Range>80</Range>
|
||||
</Vision>
|
||||
<ResourceDropsite>
|
||||
<Radius>60</Radius>
|
||||
<Types>food wood stone metal</Types>
|
||||
</ResourceDropsite>
|
||||
<TrainingQueue>
|
||||
|
@ -29,7 +29,6 @@
|
||||
<Static width="7.0" depth="7.0"/>
|
||||
</Obstruction>
|
||||
<ResourceDropsite>
|
||||
<Radius>12</Radius>
|
||||
<Types>food wood stone metal</Types>
|
||||
</ResourceDropsite>
|
||||
<VisualActor>
|
||||
|
@ -53,7 +53,6 @@
|
||||
<BuffHeal>1</BuffHeal>
|
||||
</GarrisonHolder>
|
||||
<ResourceDropsite>
|
||||
<Radius>60</Radius>
|
||||
<Types>food wood stone metal</Types>
|
||||
</ResourceDropsite>
|
||||
<TrainingQueue>
|
||||
|
@ -37,7 +37,6 @@
|
||||
<Range>60</Range>
|
||||
</Vision>
|
||||
<ResourceDropsite>
|
||||
<Radius>56</Radius>
|
||||
<Types>food</Types>
|
||||
</ResourceDropsite>
|
||||
</Entity>
|
||||
|
@ -37,7 +37,6 @@
|
||||
<Range>60</Range>
|
||||
</Vision>
|
||||
<ResourceDropsite>
|
||||
<Radius>56</Radius>
|
||||
<Types>wood stone metal</Types>
|
||||
</ResourceDropsite>
|
||||
</Entity>
|
||||
|
@ -48,6 +48,12 @@
|
||||
<stone.treasure>1000</stone.treasure>
|
||||
<metal.treasure>1000</metal.treasure>
|
||||
</Rates>
|
||||
<Capacities>
|
||||
<food>15</food>
|
||||
<metal>15</metal>
|
||||
<stone>15</stone>
|
||||
<wood>15</wood>
|
||||
</Capacities>
|
||||
</ResourceGatherer>
|
||||
<UnitMotion>
|
||||
<FormationController>false</FormationController>
|
||||
|
@ -299,6 +299,9 @@ LibError cursor_draw(const PIVFS& vfs, const wchar_t* name, int x, int y)
|
||||
}
|
||||
|
||||
Handle hc = cursor_load(vfs, name);
|
||||
|
||||
RETURN_ERR(hc); // silently ignore failures
|
||||
|
||||
H_DEREF(hc, Cursor, c);
|
||||
|
||||
switch(c->kind)
|
||||
|
@ -278,9 +278,14 @@ void Render()
|
||||
// Draw the cursor (or set the Windows cursor, on Windows)
|
||||
CStrW cursorName = g_CursorName;
|
||||
if (cursorName.empty())
|
||||
{
|
||||
cursor_draw(g_VFS, NULL, g_mouse_x, g_yres-g_mouse_y);
|
||||
}
|
||||
else
|
||||
cursor_draw(g_VFS, cursorName.c_str(), g_mouse_x, g_yres-g_mouse_y);
|
||||
{
|
||||
if (cursor_draw(g_VFS, cursorName.c_str(), g_mouse_x, g_yres-g_mouse_y) < 0)
|
||||
LOGWARNING(L"Failed to draw cursor '%ls'", cursorName.c_str());
|
||||
}
|
||||
|
||||
// restore
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
|
@ -631,6 +631,30 @@ private:
|
||||
GetSimContext().GetComponentManager().PostMessage(messages[i].first, messages[i].second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given entity matches the given query (ignoring maxRange)
|
||||
*/
|
||||
bool TestEntityQuery(const Query& q, entity_id_t id, const EntityData& entity)
|
||||
{
|
||||
// Quick filter to ignore entities with the wrong owner
|
||||
if (!(CalcOwnerMask(entity.owner) & q.ownersMask))
|
||||
return false;
|
||||
|
||||
// Ignore entities not present in the world
|
||||
if (!entity.inWorld)
|
||||
return false;
|
||||
|
||||
// Ignore self
|
||||
if (id == q.source)
|
||||
return false;
|
||||
|
||||
// Ignore if it's missing the required interface
|
||||
if (q.interface && !GetSimContext().GetComponentManager().QueryInterface(id, q.interface))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of distinct entity IDs that match the given query, sorted by ID.
|
||||
*/
|
||||
@ -641,6 +665,19 @@ private:
|
||||
return;
|
||||
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
|
||||
|
||||
// Special case: range -1.0 means check all entities ignoring distance
|
||||
if (q.maxRange == entity_pos_t::FromInt(-1))
|
||||
{
|
||||
for (std::map<entity_id_t, EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it)
|
||||
{
|
||||
if (!TestEntityQuery(q, it->first, it->second))
|
||||
continue;
|
||||
|
||||
r.push_back(it->first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get a quick list of entities that are potentially in range
|
||||
std::vector<entity_id_t> ents = m_Subdivision.GetNear(pos, q.maxRange);
|
||||
|
||||
@ -649,31 +686,25 @@ private:
|
||||
std::map<entity_id_t, EntityData>::const_iterator it = m_EntityData.find(ents[i]);
|
||||
debug_assert(it != m_EntityData.end());
|
||||
|
||||
// Quick filter to ignore entities with the wrong owner
|
||||
if (!(CalcOwnerMask(it->second.owner) & q.ownersMask))
|
||||
if (!TestEntityQuery(q, it->first, it->second))
|
||||
continue;
|
||||
|
||||
// Restrict based on precise location
|
||||
if (!it->second.inWorld)
|
||||
continue;
|
||||
// Restrict based on precise distance
|
||||
int distVsMax = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange);
|
||||
if (distVsMax > 0)
|
||||
continue;
|
||||
|
||||
// Ignore self
|
||||
if (it->first == q.source)
|
||||
continue;
|
||||
|
||||
// Ignore if it's missing the required interface
|
||||
if (q.interface && !GetSimContext().GetComponentManager().QueryInterface(it->first, q.interface))
|
||||
continue;
|
||||
|
||||
r.push_back(it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Query ConstructQuery(entity_id_t source, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface)
|
||||
{
|
||||
// Range must be non-negative, or else -1
|
||||
if (maxRange < entity_pos_t::Zero() && maxRange != entity_pos_t::FromInt(-1))
|
||||
LOGWARNING(L"CCmpRangeManager: Invalid range %f in query for entity %d", maxRange.ToDouble(), source);
|
||||
|
||||
Query q;
|
||||
q.enabled = false;
|
||||
q.source = source;
|
||||
|
@ -80,7 +80,7 @@ public:
|
||||
/**
|
||||
* Execute a passive query.
|
||||
* @param source the entity around which the range will be computed.
|
||||
* @param maxRange maximum distance in metres (inclusive).
|
||||
* @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance.
|
||||
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
|
||||
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
|
||||
* @return list of entities matching the query, ordered by increasing distance from the source entity.
|
||||
@ -90,7 +90,7 @@ public:
|
||||
/**
|
||||
* Construct an active query. The query will be disabled by default.
|
||||
* @param source the entity around which the range will be computed.
|
||||
* @param maxRange maximum distance in metres (inclusive).
|
||||
* @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance.
|
||||
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
|
||||
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
|
||||
* @return unique non-zero identifier of query.
|
||||
|
Loading…
Reference in New Issue
Block a user