1
0
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:
Ykkrosh 2010-11-13 19:15:29 +00:00
parent 6325a1eafe
commit 4100a13e0a
29 changed files with 463 additions and 53 deletions

View File

@ -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>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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);
@ -125,6 +126,11 @@ function determineAction(x, y, fromMinimap)
var playerOwned = ((targetState.player == entState.player)? true : false);
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"))
{
@ -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??

View File

@ -95,7 +95,24 @@ 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;

View File

@ -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 -->

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -146,6 +146,24 @@ var UnitFsmSpec = {
this.SetNextState("INDIVIDUAL.GATHER.GATHERING");
}
},
"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
@ -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

View File

@ -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)

View File

@ -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.

View File

@ -40,7 +40,6 @@
<Range>80</Range>
</Vision>
<ResourceDropsite>
<Radius>60</Radius>
<Types>food wood stone metal</Types>
</ResourceDropsite>
<TrainingQueue>

View File

@ -29,7 +29,6 @@
<Static width="7.0" depth="7.0"/>
</Obstruction>
<ResourceDropsite>
<Radius>12</Radius>
<Types>food wood stone metal</Types>
</ResourceDropsite>
<VisualActor>

View File

@ -53,7 +53,6 @@
<BuffHeal>1</BuffHeal>
</GarrisonHolder>
<ResourceDropsite>
<Radius>60</Radius>
<Types>food wood stone metal</Types>
</ResourceDropsite>
<TrainingQueue>

View File

@ -37,7 +37,6 @@
<Range>60</Range>
</Vision>
<ResourceDropsite>
<Radius>56</Radius>
<Types>food</Types>
</ResourceDropsite>
</Entity>

View File

@ -37,7 +37,6 @@
<Range>60</Range>
</Vision>
<ResourceDropsite>
<Radius>56</Radius>
<Types>wood stone metal</Types>
</ResourceDropsite>
</Entity>

View File

@ -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>

View File

@ -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)

View File

@ -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);

View File

@ -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,39 +665,46 @@ private:
return;
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
// Get a quick list of entities that are potentially in range
std::vector<entity_id_t> ents = m_Subdivision.GetNear(pos, q.maxRange);
for (size_t i = 0; i < ents.size(); ++i)
// Special case: range -1.0 means check all entities ignoring distance
if (q.maxRange == entity_pos_t::FromInt(-1))
{
std::map<entity_id_t, EntityData>::const_iterator it = m_EntityData.find(ents[i]);
debug_assert(it != m_EntityData.end());
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;
// Quick filter to ignore entities with the wrong owner
if (!(CalcOwnerMask(it->second.owner) & q.ownersMask))
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);
// Restrict based on precise location
if (!it->second.inWorld)
continue;
int distVsMax = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange);
if (distVsMax > 0)
continue;
for (size_t i = 0; i < ents.size(); ++i)
{
std::map<entity_id_t, EntityData>::const_iterator it = m_EntityData.find(ents[i]);
debug_assert(it != m_EntityData.end());
// Ignore self
if (it->first == q.source)
continue;
if (!TestEntityQuery(q, it->first, it->second))
continue;
// Ignore if it's missing the required interface
if (q.interface && !GetSimContext().GetComponentManager().QueryInterface(it->first, q.interface))
continue;
// Restrict based on precise distance
int distVsMax = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange);
if (distVsMax > 0)
continue;
r.push_back(it->first);
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;

View File

@ -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.