1
0
forked from 0ad/0ad

Adds building previews that rise during construction, fixes #1174, refs #21.

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:
historic_bruno 2013-02-03 02:08:20 +00:00
parent e4fcddaf73
commit 31be9cd0de
16 changed files with 366 additions and 127 deletions

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

BIN
binaries/data/mods/public/art/meshes/props/scaf_4x4.dae (Stored with Git LFS) Normal file

Binary file not shown.

View File

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

View File

@ -101,6 +101,7 @@
<AlwaysVisible>false</AlwaysVisible>
</Vision>
<VisualActor>
<ConstructionPreview/>
<SilhouetteDisplay>false</SilhouetteDisplay>
<SilhouetteOccluder>true</SilhouetteOccluder>
</VisualActor>

View File

@ -67,6 +67,7 @@
</Vision>
<VisualActor>
<Actor>structures/plot_field_new.xml</Actor>
<ConstructionPreview disable=""/>
<FoundationActor>structures/plot_field_found.xml</FoundationActor>
<SelectionShape>
<Footprint/>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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