Destroy dying 'remain' entities and replace them with visually-identical resources. Fixes #1600.

This was SVN commit r12486.
This commit is contained in:
Deiz 2012-08-20 01:38:39 +00:00
parent 034212a554
commit 66012e2e2c
4 changed files with 78 additions and 19 deletions

View File

@ -82,7 +82,7 @@ BuildingAI.prototype.SetupRangeQuery = function(owner)
if (cmpAttack)
{
var range = cmpAttack.GetRange("Ranged");
this.enemyUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, 0, cmpRangeManager.GetEntityFlagMask("normal"));
this.enemyUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver, cmpRangeManager.GetEntityFlagMask("normal"));
cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
}
};
@ -135,8 +135,11 @@ BuildingAI.prototype.OnRangeUpdate = function(msg)
if (msg.added.length)
msg.added = msg.added.filter(filter);
if (msg.removed.length)
msg.removed = msg.removed.filter(filter);
// Removed entities may not have cmpUnitAI.
for (var i = 0; i < msg.removed.length; ++i)
if (this.targetUnits.indexOf(msg.removed[i]) == -1)
msg.removed.splice(i--, 1);
}
else if (msg.tag != this.enemyUnitsQuery)
return;

View File

@ -124,18 +124,10 @@ Health.prototype.Reduce = function(amount)
}
else if (this.template.DeathType == "remain")
{
// Don't destroy the entity
// TODO: This is a workaround so players don't retain LOS when
// their livestock animals die. See ticket #1600.
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (cmpOwnership)
cmpOwnership.SetOwner(0);
// Make it fall over
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (cmpVisual)
cmpVisual.SelectAnimation("death", true, 1.0, "");
var resource = this.CreateCorpse(true);
if (resource != INVALID_ENTITY)
Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: resource });
Engine.DestroyEntity(this.entity);
}
var old = this.hitpoints;
@ -178,18 +170,23 @@ Health.prototype.Increase = function(amount)
//// Private functions ////
Health.prototype.CreateCorpse = function()
Health.prototype.CreateCorpse = function(leaveResources)
{
// If the unit died while not in the world, don't create any corpse for it
// since there's nowhere for the corpse to be placed
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (!cmpPosition.IsInWorld())
return;
return INVALID_ENTITY;
// Create a static local version of the current entity
// Either creates a static local version of the current entity, or a
// persistent corpse retaining the ResourceSupply element of the parent.
var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
var templateName = cmpTempMan.GetCurrentTemplateName(this.entity);
var corpse = Engine.AddLocalEntity("corpse|" + templateName);
var corpse;
if (leaveResources)
corpse = Engine.AddEntity("resource|" + templateName);
else
corpse = Engine.AddLocalEntity("corpse|" + templateName);
// Copy various parameters so it looks just like us
@ -207,6 +204,8 @@ Health.prototype.CreateCorpse = function()
// Make it fall over
var cmpCorpseVisual = Engine.QueryInterface(corpse, IID_Visual);
cmpCorpseVisual.SelectAnimation("death", true, 1.0, "");
return corpse;
};
Health.prototype.Repair = function(builderEnt, work)

View File

@ -815,6 +815,12 @@ var UnitFsmSpec = {
"EntityRenamed": function(msg) {
if (this.order.data.target == msg.entity)
this.order.data.target = msg.newentity;
// If we're hunting, that means we have a queued gather
// order whose target also needs to be updated.
if (this.order.data.hunting && this.orderQueue[1] &&
this.orderQueue[1].type == "Gather")
this.orderQueue[1].data.target = msg.newentity;
},
"Attacked": function(msg) {
@ -2319,6 +2325,11 @@ UnitAI.prototype.FindNearbyResource = function(filter)
var type = cmpResourceSupply.GetType();
var amount = cmpResourceSupply.GetCurrentAmount();
var template = cmpTemplateManager.GetCurrentTemplateName(ent);
// Remove "resource|" prefix from template names, if present.
if (template.indexOf("resource|") != -1)
template = template.slice(9);
if (amount > 0 && filter(ent, type, template))
return ent;
}
@ -2968,6 +2979,10 @@ UnitAI.prototype.PerformGather = function(target, queued, force)
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
var template = cmpTemplateManager.GetCurrentTemplateName(target);
// Remove "resource|" prefix from template name, if present.
if (template.indexOf("resource|") != -1)
template = template.slice(9);
// Remember the position of our target, if any, in case it disappears
// later and we want to head to its last known position
// (TODO: if the target moves a lot (e.g. it's an animal), maybe we

View File

@ -174,6 +174,10 @@ private:
// Copy the components of an entity necessary for a construction foundation
// (position, actor, armour, health, etc) into a new entity template
void CopyFoundationSubset(CParamNode& out, const CParamNode& in);
// Copy the components of an entity necessary for a gatherable resource
// into a new entity template
void CopyResourceSubset(CParamNode& out, const CParamNode& in);
};
REGISTER_COMPONENT_TYPE(TemplateManager)
@ -324,6 +328,21 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::string& templateName, int
return true;
}
// Handle special case "resource|foo"
if (templateName.find("resource|") == 0)
{
// Load the base entity template, if it wasn't already loaded
std::string baseName = templateName.substr(9);
if (!LoadTemplateFile(baseName, depth+1))
{
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
return false;
}
// Copy a subset to the requested template
CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
return true;
}
// Normal case: templateName is an XML file:
VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml");
@ -553,3 +572,26 @@ void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode
if (out.GetChild("Entity").GetChild("Vision").IsOk())
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
}
void CCmpTemplateManager::CopyResourceSubset(CParamNode& out, const CParamNode& in)
{
// Currently used for animals which die and leave a gatherable corpse.
// Mostly serves to filter out components like Vision, UnitAI, etc.
std::set<std::string> permittedComponentTypes;
permittedComponentTypes.insert("Ownership");
permittedComponentTypes.insert("Position");
permittedComponentTypes.insert("VisualActor");
permittedComponentTypes.insert("Identity");
permittedComponentTypes.insert("Obstruction");
permittedComponentTypes.insert("Minimap");
permittedComponentTypes.insert("ResourceSupply");
permittedComponentTypes.insert("Selectable");
permittedComponentTypes.insert("Footprint");
permittedComponentTypes.insert("StatusBars");
permittedComponentTypes.insert("OverlayRenderer");
permittedComponentTypes.insert("Sound");
permittedComponentTypes.insert("AIProxy");
CParamNode::LoadXMLString(out, "<Entity/>");
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
}