diff --git a/binaries/data/mods/public/art/actors/props/structures/construction/scaf_3x3.xml b/binaries/data/mods/public/art/actors/props/structures/construction/scaf_3x3.xml
new file mode 100644
index 0000000000..ef6f471055
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/structures/construction/scaf_3x3.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ props/scaf_3x3.dae
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/art/actors/props/structures/construction/scaf_4x4.xml b/binaries/data/mods/public/art/actors/props/structures/construction/scaf_4x4.xml
new file mode 100644
index 0000000000..89c56f33ab
--- /dev/null
+++ b/binaries/data/mods/public/art/actors/props/structures/construction/scaf_4x4.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ props/scaf_4x4.dae
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/art/actors/structures/fndn_3x3.xml b/binaries/data/mods/public/art/actors/structures/fndn_3x3.xml
index 0bce90ca1a..a83f31b3c3 100644
--- a/binaries/data/mods/public/art/actors/structures/fndn_3x3.xml
+++ b/binaries/data/mods/public/art/actors/structures/fndn_3x3.xml
@@ -13,12 +13,19 @@
-
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/art/actors/structures/fndn_4x4.xml b/binaries/data/mods/public/art/actors/structures/fndn_4x4.xml
index 7c91d58dbb..f4614b5ef5 100644
--- a/binaries/data/mods/public/art/actors/structures/fndn_4x4.xml
+++ b/binaries/data/mods/public/art/actors/structures/fndn_4x4.xml
@@ -4,14 +4,21 @@
structural/found_4x4_a.dae
+
+
+ structural/found_4x4_b.dae
+
+
+
+
-
- structural/found_4x4_b.dae
+
+
diff --git a/binaries/data/mods/public/art/meshes/props/scaf_3x3.dae b/binaries/data/mods/public/art/meshes/props/scaf_3x3.dae
new file mode 100644
index 0000000000..c775d68a93
--- /dev/null
+++ b/binaries/data/mods/public/art/meshes/props/scaf_3x3.dae
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:008ebe79930d2d2e924252dcbecd733b1eade1cbf1ac8faa2ef67ab07d28ee13
+size 96176
diff --git a/binaries/data/mods/public/art/meshes/props/scaf_4x4.dae b/binaries/data/mods/public/art/meshes/props/scaf_4x4.dae
new file mode 100644
index 0000000000..0a41e7a9a3
--- /dev/null
+++ b/binaries/data/mods/public/art/meshes/props/scaf_4x4.dae
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c830834928a212d8ba334f33db982b47177bc6eebd71da8d70b519e9034a0972
+size 452509
diff --git a/binaries/data/mods/public/simulation/components/Foundation.js b/binaries/data/mods/public/simulation/components/Foundation.js
index 75d91e61a4..5012ca142e 100644
--- a/binaries/data/mods/public/simulation/components/Foundation.js
+++ b/binaries/data/mods/public/simulation/components/Foundation.js
@@ -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();
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
index 6b7bee3d63..bfe0e5ec91 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -101,6 +101,7 @@
false
+
false
true
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml b/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
index 8d7c8be381..f2e183d65e 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
@@ -67,6 +67,7 @@
structures/plot_field_new.xml
+
structures/plot_field_found.xml
diff --git a/source/graphics/Unit.cpp b/source/graphics/Unit.cpp
index 3d4316d918..bd5501927c 100644
--- a/source/graphics/Unit.cpp
+++ b/source/graphics/Unit.cpp
@@ -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& actorSelections)
+ const std::set& 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
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 remainingSelections = m_Object->m_Base->CalculateRandomRemainingSelections(m_ID, selections);
+ std::set remainingSelections = m_Object->m_Base->CalculateRandomRemainingSelections(m_Seed, selections);
if (remainingSelections.size() > 0)
selections.push_back(remainingSelections);
diff --git a/source/graphics/Unit.h b/source/graphics/Unit.h
index 0b7a4fcb13..32e67d48c9 100644
--- a/source/graphics/Unit.h
+++ b/source/graphics/Unit.h
@@ -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& actorSelections);
+ const std::set& 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 m_ActorSelections;
// entity-level selections for this unit
diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp
index 60b087b8d6..65098cec46 100644
--- a/source/simulation2/components/CCmpTemplateManager.cpp
+++ b/source/simulation2/components/CCmpTemplateManager.cpp
@@ -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, "0");
}
+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 permittedComponentTypes;
+ permittedComponentTypes.insert("Footprint");
+ permittedComponentTypes.insert("Ownership");
+ permittedComponentTypes.insert("Position");
+ permittedComponentTypes.insert("VisualActor");
+
+ CParamNode::LoadXMLString(out, "");
+ out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
+}
+
+
void CCmpTemplateManager::CopyResourceSubset(CParamNode& out, const CParamNode& in)
{
// Currently used for animals which die and leave a gatherable corpse.
diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp
index 02db4fbb40..376ddbbee2 100644
--- a/source/simulation2/components/CCmpVisualActor.cpp
+++ b/source/simulation2/components/CCmpVisualActor.cpp
@@ -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:
""
""
""
- ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
""
""
""
@@ -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 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 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
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 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 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 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 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 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 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);
diff --git a/source/simulation2/components/ICmpVisual.cpp b/source/simulation2/components/ICmpVisual.cpp
index bc1e1c74f9..7a4d52e713 100644
--- a/source/simulation2/components/ICmpVisual.cpp
+++ b/source/simulation2/components/ICmpVisual.cpp
@@ -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)
diff --git a/source/simulation2/components/ICmpVisual.h b/source/simulation2/components/ICmpVisual.h
index 134b58a82e..fb945517e4 100644
--- a/source/simulation2/components/ICmpVisual.h
+++ b/source/simulation2/components/ICmpVisual.h
@@ -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
diff --git a/source/simulation2/system/ComponentManager.cpp b/source/simulation2/system/ComponentManager.cpp
index 427e09b8fd..2105d370f5 100644
--- a/source/simulation2/system/ComponentManager.cpp
+++ b/source/simulation2/system/ComponentManager.cpp
@@ -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 queue;
- queue.swap(m_DestructionQueue);
-
- for (std::vector::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 queue;
+ queue.swap(m_DestructionQueue);
- CMessageDestroy msg(ent);
- PostMessage(ent, msg);
-
- // Destroy the components, and remove from m_ComponentsByTypeId:
- std::map >::iterator iit = m_ComponentsByTypeId.begin();
- for (; iit != m_ComponentsByTypeId.end(); ++iit)
+ for (std::vector::iterator it = queue.begin(); it != queue.end(); ++it)
{
- std::map::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 >::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::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 >::iterator ifcit = m_ComponentsByInterface.begin();
- for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
- {
- ifcit->erase(ent);
+ // Remove from m_ComponentsByInterface
+ std::vector >::iterator ifcit = m_ComponentsByInterface.begin();
+ for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
+ {
+ ifcit->erase(ent);
+ }
}
}
}