forked from 0ad/0ad
Adds scaffold support for foundations, includes two examples for 3x3 and 4x4, fixes #1581. Extends CmpVisualActor and CUnit to support random variant seeds. Fixes bug in actor hotloading. Fixes serialization failure caused by destroying entities in OnDestroy handlers. This was SVN commit r13143.
This commit is contained in:
parent
e4fcddaf73
commit
31be9cd0de
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<actor version="1">
|
||||
<castshadow/>
|
||||
<group>
|
||||
<variant>
|
||||
<mesh>props/scaf_3x3.dae</mesh>
|
||||
<textures>
|
||||
<texture file="props/scaffold.png" name="baseTex"/>
|
||||
</textures>
|
||||
</variant>
|
||||
</group>
|
||||
</actor>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<actor version="1">
|
||||
<castshadow/>
|
||||
<group>
|
||||
<variant>
|
||||
<mesh>props/scaf_4x4.dae</mesh>
|
||||
<textures>
|
||||
<texture file="props/scaffold.png" name="baseTex"/>
|
||||
</textures>
|
||||
</variant>
|
||||
</group>
|
||||
</actor>
|
@ -13,12 +13,19 @@
|
||||
</variant>
|
||||
</group>
|
||||
<group>
|
||||
<variant frequency="1">
|
||||
<variant frequency="1" name="Idle">
|
||||
<props>
|
||||
<prop actor="props/structures/decals/dirt_small.xml" attachpoint="root"/>
|
||||
<prop actor="particle/construction_dust.xml" attachpoint="root"/>
|
||||
</props>
|
||||
</variant>
|
||||
<variant name="scaffold">
|
||||
<props>
|
||||
<prop actor="props/structures/construction/scaf_3x3.xml" attachpoint="root"/>
|
||||
<prop actor="props/structures/decals/dirt_small.xml" attachpoint="root"/>
|
||||
<prop actor="particle/construction_dust.xml" attachpoint="root"/>
|
||||
</props>
|
||||
</variant>
|
||||
</group>
|
||||
<group>
|
||||
<variant frequency="1">
|
||||
|
@ -4,14 +4,21 @@
|
||||
<group>
|
||||
<variant frequency="1" name="foundation 4x4 a">
|
||||
<mesh>structural/found_4x4_a.dae</mesh>
|
||||
</variant>
|
||||
<variant frequency="1" name="foundation 4x4 b">
|
||||
<mesh>structural/found_4x4_b.dae</mesh>
|
||||
</variant>
|
||||
</group>
|
||||
<group>
|
||||
<variant frequency="1" name="Idle">
|
||||
<props>
|
||||
<prop actor="props/structures/decals/dirt_4x4.xml" attachpoint="root"/>
|
||||
<prop actor="particle/construction_dust.xml" attachpoint="root"/>
|
||||
</props>
|
||||
</variant>
|
||||
<variant frequency="1" name="foundation 4x4 b">
|
||||
<mesh>structural/found_4x4_b.dae</mesh>
|
||||
<variant name="scaffold">
|
||||
<props>
|
||||
<prop actor="props/structures/construction/scaf_4x4.xml" attachpoint="root"/>
|
||||
<prop actor="props/structures/decals/dirt_4x4.xml" attachpoint="root"/>
|
||||
<prop actor="particle/construction_dust.xml" attachpoint="root"/>
|
||||
</props>
|
||||
|
BIN
binaries/data/mods/public/art/meshes/props/scaf_3x3.dae
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/meshes/props/scaf_3x3.dae
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/meshes/props/scaf_4x4.dae
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/meshes/props/scaf_4x4.dae
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -22,6 +22,8 @@ Foundation.prototype.Init = function()
|
||||
this.recentBuilders = []; // builder entities since the last timeout
|
||||
this.numRecentBuilders = 0; // number of builder entities as of the last timeout
|
||||
this.buildMultiplier = 1; // Multiplier for the amount of work builders do.
|
||||
|
||||
this.previewEntity = INVALID_ENTITY;
|
||||
};
|
||||
|
||||
Foundation.prototype.UpdateTimeout = function()
|
||||
@ -69,6 +71,12 @@ Foundation.prototype.OnDestroy = function()
|
||||
if (!this.initialised) // this happens if the foundation was destroyed because the player had insufficient resources
|
||||
return;
|
||||
|
||||
if (this.previewEntity != INVALID_ENTITY)
|
||||
{
|
||||
Engine.DestroyEntity(this.previewEntity);
|
||||
this.previewEntity = INVALID_ENTITY;
|
||||
}
|
||||
|
||||
if (this.buildProgress == 1.0)
|
||||
return;
|
||||
|
||||
@ -175,6 +183,36 @@ Foundation.prototype.Build = function(builderEnt, work)
|
||||
cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1);
|
||||
}
|
||||
|
||||
// Switch foundation to scaffold variant
|
||||
var cmpFoundationVisual = Engine.QueryInterface(this.entity, IID_Visual);
|
||||
if (cmpFoundationVisual)
|
||||
cmpFoundationVisual.SelectAnimation("scaffold", false, 1.0, "");
|
||||
|
||||
// Create preview entity and copy various parameters from the foundation
|
||||
if (cmpFoundationVisual && cmpFoundationVisual.HasConstructionPreview())
|
||||
{
|
||||
this.previewEntity = Engine.AddEntity("construction|"+this.finalTemplateName);
|
||||
var cmpFoundationOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
var cmpPreviewOwnership = Engine.QueryInterface(this.previewEntity, IID_Ownership);
|
||||
cmpPreviewOwnership.SetOwner(cmpFoundationOwnership.GetOwner());
|
||||
|
||||
// Initially hide the preview underground
|
||||
var cmpPreviewVisual = Engine.QueryInterface(this.previewEntity, IID_Visual);
|
||||
if (cmpPreviewVisual)
|
||||
{
|
||||
cmpPreviewVisual.SetConstructionProgress(0.0);
|
||||
cmpPreviewVisual.SetActorSeed(cmpFoundationVisual.GetActorSeed());
|
||||
}
|
||||
|
||||
var cmpFoundationPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
var pos = cmpFoundationPosition.GetPosition();
|
||||
var rot = cmpFoundationPosition.GetRotation();
|
||||
var cmpPreviewPosition = Engine.QueryInterface(this.previewEntity, IID_Position);
|
||||
cmpPreviewPosition.SetYRotation(rot.y);
|
||||
cmpPreviewPosition.SetXZRotation(rot.x, rot.z);
|
||||
cmpPreviewPosition.JumpTo(pos.x, pos.z);
|
||||
}
|
||||
|
||||
this.committed = true;
|
||||
}
|
||||
|
||||
@ -188,9 +226,14 @@ Foundation.prototype.Build = function(builderEnt, work)
|
||||
this.buildProgress += amount * this.buildMultiplier;
|
||||
if (this.buildProgress > 1.0)
|
||||
this.buildProgress = 1.0;
|
||||
|
||||
|
||||
Engine.PostMessage(this.entity, MT_FoundationProgressChanged, { "to": this.GetBuildPercentage() });
|
||||
|
||||
// Gradually reveal the final building preview
|
||||
var cmpPreviewVisual = Engine.QueryInterface(this.previewEntity, IID_Visual);
|
||||
if (cmpPreviewVisual)
|
||||
cmpPreviewVisual.SetConstructionProgress(this.buildProgress);
|
||||
|
||||
// Add an appropriate proportion of hitpoints
|
||||
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
|
||||
var maxHealth = cmpHealth.GetMaxHitpoints();
|
||||
@ -211,6 +254,11 @@ Foundation.prototype.Build = function(builderEnt, work)
|
||||
|
||||
// Copy various parameters from the foundation
|
||||
|
||||
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
|
||||
var cmpBuildingVisual = Engine.QueryInterface(building, IID_Visual)
|
||||
if (cmpVisual && cmpBuildingVisual)
|
||||
cmpBuildingVisual.SetActorSeed(cmpVisual.GetActorSeed());
|
||||
|
||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);
|
||||
var pos = cmpPosition.GetPosition();
|
||||
|
@ -101,6 +101,7 @@
|
||||
<AlwaysVisible>false</AlwaysVisible>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<ConstructionPreview/>
|
||||
<SilhouetteDisplay>false</SilhouetteDisplay>
|
||||
<SilhouetteOccluder>true</SilhouetteOccluder>
|
||||
</VisualActor>
|
||||
|
@ -67,6 +67,7 @@
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<Actor>structures/plot_field_new.xml</Actor>
|
||||
<ConstructionPreview disable=""/>
|
||||
<FoundationActor>structures/plot_field_found.xml</FoundationActor>
|
||||
<SelectionShape>
|
||||
<Footprint/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -27,10 +27,10 @@
|
||||
#include "UnitAnimation.h"
|
||||
|
||||
CUnit::CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
||||
const std::set<CStr>& actorSelections)
|
||||
const std::set<CStr>& actorSelections, uint32_t seed)
|
||||
: m_Object(object), m_Model(object->m_Model->Clone()),
|
||||
m_ID(INVALID_ENTITY), m_ActorSelections(actorSelections),
|
||||
m_ObjectManager(objectManager)
|
||||
m_ObjectManager(objectManager), m_Seed(seed)
|
||||
{
|
||||
if (m_Model->ToCModel())
|
||||
m_Animation = new CUnitAnimation(m_ID, m_Model->ToCModel(), m_Object);
|
||||
@ -61,7 +61,7 @@ CUnit* CUnit::Create(const CStrW& actorName, uint32_t seed, const std::set<CStr>
|
||||
if (! obj)
|
||||
return NULL;
|
||||
|
||||
return new CUnit(obj, objectManager, actorSelections);
|
||||
return new CUnit(obj, objectManager, actorSelections, seed);
|
||||
}
|
||||
|
||||
void CUnit::UpdateModel(float frameTime)
|
||||
@ -111,7 +111,7 @@ void CUnit::ReloadObject()
|
||||
// see http://trac.wildfiregames.com/ticket/979
|
||||
|
||||
// Use the entity ID as randomization seed (same as when the unit was first created)
|
||||
std::set<CStr> remainingSelections = m_Object->m_Base->CalculateRandomRemainingSelections(m_ID, selections);
|
||||
std::set<CStr> remainingSelections = m_Object->m_Base->CalculateRandomRemainingSelections(m_Seed, selections);
|
||||
if (remainingSelections.size() > 0)
|
||||
selections.push_back(remainingSelections);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -38,7 +38,7 @@ class CUnit
|
||||
private:
|
||||
// Private constructor. Needs complete list of selections for the variation.
|
||||
CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
||||
const std::set<CStr>& actorSelections);
|
||||
const std::set<CStr>& actorSelections, uint32_t seed);
|
||||
|
||||
public:
|
||||
// Attempt to create a unit with the given actor, with a set of
|
||||
@ -89,6 +89,9 @@ private:
|
||||
// permanent way of referencing them.
|
||||
entity_id_t m_ID;
|
||||
|
||||
// seed used when creating unit
|
||||
uint32_t m_Seed;
|
||||
|
||||
// actor-level selections for this unit
|
||||
std::set<CStr> m_ActorSelections;
|
||||
// entity-level selections for this unit
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -175,6 +175,10 @@ private:
|
||||
// (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 non-foundation construction entity
|
||||
// into a new entity template
|
||||
void CopyConstructionSubset(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);
|
||||
@ -328,6 +332,21 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::string& templateName, int
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "construction|foo"
|
||||
if (templateName.find("construction|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(13);
|
||||
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
|
||||
CopyConstructionSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "resource|foo"
|
||||
if (templateName.find("resource|") == 0)
|
||||
{
|
||||
@ -573,6 +592,21 @@ void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
|
||||
}
|
||||
|
||||
void CCmpTemplateManager::CopyConstructionSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// Currently used for buildings rising during construction
|
||||
// Mostly serves to filter out components like Vision, UnitAI, etc.
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
}
|
||||
|
||||
|
||||
void CCmpTemplateManager::CopyResourceSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// Currently used for animals which die and leave a gatherable corpse.
|
||||
|
@ -20,14 +20,17 @@
|
||||
#include "simulation2/system/Component.h"
|
||||
#include "ICmpVisual.h"
|
||||
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "ICmpFootprint.h"
|
||||
#include "ICmpOwnership.h"
|
||||
#include "ICmpPosition.h"
|
||||
#include "ICmpRangeManager.h"
|
||||
#include "ICmpSelectable.h"
|
||||
#include "ICmpTemplateManager.h"
|
||||
#include "ICmpTerrain.h"
|
||||
#include "ICmpUnitMotion.h"
|
||||
#include "ICmpVision.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/components/ICmpFootprint.h"
|
||||
#include "simulation2/components/ICmpSelectable.h"
|
||||
|
||||
#include "graphics/Frustum.h"
|
||||
#include "graphics/Model.h"
|
||||
@ -73,6 +76,11 @@ public:
|
||||
fixed m_AnimDesync;
|
||||
fixed m_AnimSyncRepeatTime; // 0.0 if not synced
|
||||
|
||||
u32 m_Seed; // seed used for random variations
|
||||
|
||||
bool m_ConstructionPreview;
|
||||
fixed m_ConstructionProgress;
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
return
|
||||
@ -93,7 +101,12 @@ public:
|
||||
"</element>"
|
||||
"</optional>"
|
||||
"<optional>"
|
||||
"<element name='Foundation' a:help='Used internally; if present the unit will be rendered as a foundation'>"
|
||||
"<element name='Foundation' a:help='Used internally; if present, the unit will be rendered as a foundation'>"
|
||||
"<empty/>"
|
||||
"</element>"
|
||||
"</optional>"
|
||||
"<optional>"
|
||||
"<element name='ConstructionPreview' a:help='If present, the unit should have a construction preview'>"
|
||||
"<empty/>"
|
||||
"</element>"
|
||||
"</optional>"
|
||||
@ -141,50 +154,19 @@ public:
|
||||
m_PreviouslyRendered = false;
|
||||
m_Unit = NULL;
|
||||
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
|
||||
|
||||
m_R = m_G = m_B = fixed::FromInt(1);
|
||||
|
||||
// TODO: we should do some fancy animation of under-construction buildings rising from the ground,
|
||||
// but for now we'll just use the foundation actor and ignore the normal one
|
||||
m_ConstructionPreview = paramNode.GetChild("ConstructionPreview").IsOk();
|
||||
m_ConstructionProgress = fixed::Zero();
|
||||
|
||||
m_Seed = GetEntityId();
|
||||
|
||||
if (paramNode.GetChild("Foundation").IsOk() && paramNode.GetChild("FoundationActor").IsOk())
|
||||
m_ActorName = paramNode.GetChild("FoundationActor").ToString();
|
||||
else
|
||||
m_ActorName = paramNode.GetChild("Actor").ToString();
|
||||
|
||||
if (GetSimContext().HasUnitManager())
|
||||
{
|
||||
std::set<CStr> selections;
|
||||
m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
|
||||
if (m_Unit)
|
||||
{
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
if (model.ToCModel())
|
||||
{
|
||||
u32 modelFlags = 0;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteDisplay").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
||||
|
||||
CmpPtr<ICmpVision> cmpVision(GetSimContext(), GetEntityId());
|
||||
if (cmpVision && cmpVision->GetAlwaysVisible())
|
||||
modelFlags |= MODELFLAG_IGNORE_LOS;
|
||||
|
||||
model.ToCModel()->AddFlagsRec(modelFlags);
|
||||
}
|
||||
|
||||
// Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
|
||||
// Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
|
||||
// shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
|
||||
// which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
|
||||
// initialize the selection shape descriptor on-demand.
|
||||
InitSelectionShapeDescriptor(model, paramNode);
|
||||
|
||||
m_Unit->SetID(GetEntityId());
|
||||
}
|
||||
}
|
||||
InitModel(paramNode);
|
||||
|
||||
// We need to select animation even if graphics are disabled, as this modifies serialized state
|
||||
SelectAnimation("idle", false, fixed::FromInt(1), L"");
|
||||
@ -202,13 +184,6 @@ public:
|
||||
template<typename S>
|
||||
void SerializeCommon(S& serialize)
|
||||
{
|
||||
// TODO: store random variation. This ought to be synchronised across saved games
|
||||
// and networks, so everyone sees the same thing. Saving the list of selection strings
|
||||
// would be awfully inefficient, so actors should be changed to (by default) represent
|
||||
// variations with a 16-bit RNG seed (selected randomly when creating new units, or
|
||||
// when someone hits the "randomise" button in the map editor), only overridden with
|
||||
// a list of strings if it really needs to be a specific variation.
|
||||
|
||||
serialize.NumberFixed_Unbounded("r", m_R);
|
||||
serialize.NumberFixed_Unbounded("g", m_G);
|
||||
serialize.NumberFixed_Unbounded("b", m_B);
|
||||
@ -221,6 +196,11 @@ public:
|
||||
serialize.NumberFixed_Unbounded("anim desync", m_AnimDesync);
|
||||
serialize.NumberFixed_Unbounded("anim sync repeat time", m_AnimSyncRepeatTime);
|
||||
|
||||
serialize.NumberU32_Unbounded("seed", m_Seed);
|
||||
// TODO: variation/selection strings
|
||||
|
||||
serialize.NumberFixed_Unbounded("constructionprogress", m_ConstructionProgress);
|
||||
|
||||
// TODO: store actor variables?
|
||||
}
|
||||
|
||||
@ -240,8 +220,14 @@ public:
|
||||
{
|
||||
Init(paramNode);
|
||||
|
||||
u32 oldSeed = GetActorSeed();
|
||||
|
||||
SerializeCommon(deserialize);
|
||||
|
||||
// If we serialized a different seed, reload actor
|
||||
if (oldSeed != GetActorSeed())
|
||||
ReloadActor();
|
||||
|
||||
fixed repeattime = m_AnimSyncRepeatTime; // save because SelectAnimation overwrites it
|
||||
|
||||
if (m_AnimRunThreshold.IsZero())
|
||||
@ -444,6 +430,30 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual u32 GetActorSeed()
|
||||
{
|
||||
return m_Seed;
|
||||
}
|
||||
|
||||
virtual void SetActorSeed(u32 seed)
|
||||
{
|
||||
if (seed == m_Seed)
|
||||
return;
|
||||
|
||||
m_Seed = seed;
|
||||
ReloadActor();
|
||||
}
|
||||
|
||||
virtual bool HasConstructionPreview()
|
||||
{
|
||||
return m_ConstructionPreview;
|
||||
}
|
||||
|
||||
virtual void SetConstructionProgress(fixed progress)
|
||||
{
|
||||
m_ConstructionProgress = progress;
|
||||
}
|
||||
|
||||
virtual void Hotload(const VfsPath& name)
|
||||
{
|
||||
if (!m_Unit)
|
||||
@ -452,36 +462,7 @@ public:
|
||||
if (name != m_ActorName)
|
||||
return;
|
||||
|
||||
std::set<CStr> selections;
|
||||
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
|
||||
|
||||
if (!newUnit)
|
||||
return;
|
||||
|
||||
// Save some data from the old unit
|
||||
CColor shading = m_Unit->GetModel().GetShadingColor();
|
||||
player_id_t playerID = m_Unit->GetModel().GetPlayerID();
|
||||
|
||||
// Replace with the new unit
|
||||
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
|
||||
m_Unit = newUnit;
|
||||
|
||||
m_Unit->SetID(GetEntityId());
|
||||
|
||||
m_Unit->SetEntitySelection(m_AnimName);
|
||||
if (m_Unit->GetAnimation())
|
||||
m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
|
||||
|
||||
// We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate
|
||||
if (!m_AnimSyncRepeatTime.IsZero())
|
||||
if (m_Unit->GetAnimation())
|
||||
m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat());
|
||||
|
||||
m_Unit->GetModel().SetShadingColor(shading);
|
||||
|
||||
m_Unit->GetModel().SetPlayerID(playerID);
|
||||
|
||||
// TODO: should copy/reset silhouette flags
|
||||
ReloadActor();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -490,17 +471,13 @@ private:
|
||||
/// which may not occur immediately if the game starts paused.
|
||||
bool m_PreviouslyRendered;
|
||||
|
||||
int32_t GetActorSeed()
|
||||
{
|
||||
return GetEntityId();
|
||||
}
|
||||
/// Helper function shared by component init and actor reloading
|
||||
void InitModel(const CParamNode& paramNode);
|
||||
|
||||
/// Helper method; initializes the model selection shape descriptor from XML. Factored out for readability of @ref Init.
|
||||
/// The @p model argument is technically not really necessary since naturally this method is intended to initialize this
|
||||
/// visual actor's model (I wouldn't know which other one you'd pass), but it's included here to enforce that the
|
||||
/// component's model must have been created before using this method (i.e. to prevent accidentally calls to this method
|
||||
/// before the model was constructed).
|
||||
void InitSelectionShapeDescriptor(CModelAbstract& model, const CParamNode& paramNode);
|
||||
void InitSelectionShapeDescriptor(const CParamNode& paramNode);
|
||||
|
||||
void ReloadActor();
|
||||
|
||||
void Update(fixed turnLength);
|
||||
void UpdateVisibility();
|
||||
@ -512,7 +489,45 @@ REGISTER_COMPONENT_TYPE(VisualActor)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void CCmpVisualActor::InitSelectionShapeDescriptor(CModelAbstract& model, const CParamNode& paramNode)
|
||||
void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
{
|
||||
if (GetSimContext().HasUnitManager())
|
||||
{
|
||||
std::set<CStr> selections;
|
||||
m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
|
||||
if (m_Unit)
|
||||
{
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
if (model.ToCModel())
|
||||
{
|
||||
u32 modelFlags = 0;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteDisplay").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
||||
|
||||
CmpPtr<ICmpVision> cmpVision(GetSimContext(), GetEntityId());
|
||||
if (cmpVision && cmpVision->GetAlwaysVisible())
|
||||
modelFlags |= MODELFLAG_IGNORE_LOS;
|
||||
|
||||
model.ToCModel()->AddFlagsRec(modelFlags);
|
||||
}
|
||||
|
||||
// Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
|
||||
// Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
|
||||
// shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
|
||||
// which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
|
||||
// initialize the selection shape descriptor on-demand.
|
||||
InitSelectionShapeDescriptor(paramNode);
|
||||
|
||||
m_Unit->SetID(GetEntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
|
||||
{
|
||||
// by default, we don't need a custom selection shape and we can just keep the default behaviour
|
||||
CModelAbstract::CustomSelectionShape* shapeDescriptor = NULL;
|
||||
@ -578,8 +593,49 @@ void CCmpVisualActor::InitSelectionShapeDescriptor(CModelAbstract& model, const
|
||||
}
|
||||
}
|
||||
|
||||
ENSURE(m_Unit);
|
||||
// the model is now responsible for cleaning up the descriptor
|
||||
model.SetCustomSelectionShape(shapeDescriptor);
|
||||
m_Unit->GetModel().SetCustomSelectionShape(shapeDescriptor);
|
||||
}
|
||||
|
||||
void CCmpVisualActor::ReloadActor()
|
||||
{
|
||||
if (!m_Unit)
|
||||
return;
|
||||
|
||||
std::set<CStr> selections;
|
||||
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
|
||||
|
||||
if (!newUnit)
|
||||
return;
|
||||
|
||||
// Save some data from the old unit
|
||||
CColor shading = m_Unit->GetModel().GetShadingColor();
|
||||
player_id_t playerID = m_Unit->GetModel().GetPlayerID();
|
||||
|
||||
// Replace with the new unit
|
||||
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
|
||||
|
||||
// HACK: selection shape needs template data, but rather than storing all that data
|
||||
// in the component, we load the template here and pass it into a helper function
|
||||
CmpPtr<ICmpTemplateManager> cmpTemplateManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
const CParamNode* node = cmpTemplateManager->LoadLatestTemplate(GetEntityId());
|
||||
ENSURE(node && node->GetChild("VisualActor").IsOk());
|
||||
|
||||
InitModel(node->GetChild("VisualActor"));
|
||||
|
||||
m_Unit->SetEntitySelection(m_AnimName);
|
||||
if (m_Unit->GetAnimation())
|
||||
m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
|
||||
|
||||
// We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate
|
||||
if (!m_AnimSyncRepeatTime.IsZero())
|
||||
if (m_Unit->GetAnimation())
|
||||
m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat());
|
||||
|
||||
m_Unit->GetModel().SetShadingColor(shading);
|
||||
|
||||
m_Unit->GetModel().SetPlayerID(playerID);
|
||||
}
|
||||
|
||||
void CCmpVisualActor::Update(fixed UNUSED(turnLength))
|
||||
@ -685,6 +741,30 @@ void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
|
||||
|
||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, floating));
|
||||
|
||||
if (!m_ConstructionProgress.IsZero())
|
||||
{
|
||||
// We use selection boxes to calculate the model size, since the model could be offset
|
||||
// TODO: this annoyingly shows decals, would be nice to hide them
|
||||
CBoundingBoxOriented bounds = GetSelectionBox();
|
||||
if (!bounds.IsEmpty())
|
||||
{
|
||||
float dy = 2.0f * bounds.m_HalfSizes.Y;
|
||||
|
||||
// If this is a floating unit, we want it to start all the way under the terrain,
|
||||
// so find the difference between its current position and the terrain
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (floating && cmpTerrain)
|
||||
{
|
||||
CVector3D pos = transform.GetTranslation();
|
||||
float ground = cmpTerrain->GetExactGroundLevel(pos.X, pos.Z);
|
||||
dy += std::max(0.f, pos.Y - ground);
|
||||
}
|
||||
|
||||
transform.Translate(0.0f, (m_ConstructionProgress.ToFloat() - 1.0f) * dy, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
|
||||
model.SetTransform(transform);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -30,4 +30,8 @@ DEFINE_INTERFACE_METHOD_1("SetAnimationSyncRepeat", void, ICmpVisual, SetAnimati
|
||||
DEFINE_INTERFACE_METHOD_1("SetAnimationSyncOffset", void, ICmpVisual, SetAnimationSyncOffset, fixed)
|
||||
DEFINE_INTERFACE_METHOD_4("SetShadingColour", void, ICmpVisual, SetShadingColour, fixed, fixed, fixed, fixed)
|
||||
DEFINE_INTERFACE_METHOD_2("SetVariable", void, ICmpVisual, SetVariable, std::string, float)
|
||||
DEFINE_INTERFACE_METHOD_0("GetActorSeed", u32, ICmpVisual, GetActorSeed)
|
||||
DEFINE_INTERFACE_METHOD_1("SetActorSeed", void, ICmpVisual, SetActorSeed, u32)
|
||||
DEFINE_INTERFACE_METHOD_0("HasConstructionPreview", bool, ICmpVisual, HasConstructionPreview)
|
||||
DEFINE_INTERFACE_METHOD_1("SetConstructionProgress", void, ICmpVisual, SetConstructionProgress, fixed)
|
||||
END_INTERFACE_WRAPPER(Visual)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -146,6 +146,27 @@ public:
|
||||
*/
|
||||
virtual void SetVariable(std::string name, float value) = 0;
|
||||
|
||||
/**
|
||||
* Get actor seed used for random variations
|
||||
*/
|
||||
virtual u32 GetActorSeed() = 0;
|
||||
|
||||
/**
|
||||
* Set actor seed for random variations and reload model
|
||||
*/
|
||||
virtual void SetActorSeed(u32 seed) = 0;
|
||||
|
||||
/**
|
||||
* Returns true if this entity should have a construction preview
|
||||
*/
|
||||
virtual bool HasConstructionPreview() = 0;
|
||||
|
||||
/**
|
||||
* Set construction progress of the model, this affects the rendered position of the model.
|
||||
* 0.0 will be fully underground, 1.0 will be fully visible, 0.5 will be half underground.
|
||||
*/
|
||||
virtual void SetConstructionProgress(fixed progress) = 0;
|
||||
|
||||
/**
|
||||
* Called when an actor file has been modified and reloaded dynamically.
|
||||
* If this component uses the named actor file, it should regenerate its actor
|
||||
|
@ -698,36 +698,39 @@ void CComponentManager::DestroyComponentsSoon(entity_id_t ent)
|
||||
|
||||
void CComponentManager::FlushDestroyedComponents()
|
||||
{
|
||||
// Make a copy of the destruction queue, so that the iterators won't be invalidated if the
|
||||
// CMessageDestroy handlers try to destroy more entities themselves
|
||||
std::vector<entity_id_t> queue;
|
||||
queue.swap(m_DestructionQueue);
|
||||
|
||||
for (std::vector<entity_id_t>::iterator it = queue.begin(); it != queue.end(); ++it)
|
||||
while(!m_DestructionQueue.empty())
|
||||
{
|
||||
entity_id_t ent = *it;
|
||||
// Make a copy of the destruction queue, so that the iterators won't be invalidated if the
|
||||
// CMessageDestroy handlers try to destroy more entities themselves
|
||||
std::vector<entity_id_t> queue;
|
||||
queue.swap(m_DestructionQueue);
|
||||
|
||||
CMessageDestroy msg(ent);
|
||||
PostMessage(ent, msg);
|
||||
|
||||
// Destroy the components, and remove from m_ComponentsByTypeId:
|
||||
std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::iterator iit = m_ComponentsByTypeId.begin();
|
||||
for (; iit != m_ComponentsByTypeId.end(); ++iit)
|
||||
for (std::vector<entity_id_t>::iterator it = queue.begin(); it != queue.end(); ++it)
|
||||
{
|
||||
std::map<entity_id_t, IComponent*>::iterator eit = iit->second.find(ent);
|
||||
if (eit != iit->second.end())
|
||||
entity_id_t ent = *it;
|
||||
|
||||
CMessageDestroy msg(ent);
|
||||
PostMessage(ent, msg);
|
||||
|
||||
// Destroy the components, and remove from m_ComponentsByTypeId:
|
||||
std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::iterator iit = m_ComponentsByTypeId.begin();
|
||||
for (; iit != m_ComponentsByTypeId.end(); ++iit)
|
||||
{
|
||||
eit->second->Deinit();
|
||||
m_ComponentTypesById[iit->first].dealloc(eit->second);
|
||||
iit->second.erase(ent);
|
||||
std::map<entity_id_t, IComponent*>::iterator eit = iit->second.find(ent);
|
||||
if (eit != iit->second.end())
|
||||
{
|
||||
eit->second->Deinit();
|
||||
m_ComponentTypesById[iit->first].dealloc(eit->second);
|
||||
iit->second.erase(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from m_ComponentsByInterface
|
||||
std::vector<boost::unordered_map<entity_id_t, IComponent*> >::iterator ifcit = m_ComponentsByInterface.begin();
|
||||
for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
|
||||
{
|
||||
ifcit->erase(ent);
|
||||
// Remove from m_ComponentsByInterface
|
||||
std::vector<boost::unordered_map<entity_id_t, IComponent*> >::iterator ifcit = m_ComponentsByInterface.begin();
|
||||
for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
|
||||
{
|
||||
ifcit->erase(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user