Index: binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml
===================================================================
--- binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml
+++ binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ props/onager_projectile.dae
+
+
+
+
+
+
+
Index: binaries/data/mods/public/art/actors/units/athenians/siege_rock.xml
===================================================================
--- binaries/data/mods/public/art/actors/units/athenians/siege_rock.xml
+++ binaries/data/mods/public/art/actors/units/athenians/siege_rock.xml
@@ -10,6 +10,7 @@
structural/hele_lithobolos.dae
+
Index: source/graphics/ObjectEntry.h
===================================================================
--- source/graphics/ObjectEntry.h
+++ source/graphics/ObjectEntry.h
@@ -63,6 +63,8 @@
std::wstring m_ProjectileModelName;
+ std::wstring m_ProjectileHitModelName;
+
/**
* Returns a randomly-chosen animation matching the given ID, or animationName if ID is empty.
* The chosen animation is picked randomly from the GetAnimations list
Index: source/graphics/ObjectEntry.cpp
===================================================================
--- source/graphics/ObjectEntry.cpp
+++ source/graphics/ObjectEntry.cpp
@@ -209,6 +209,13 @@
continue;
}
+ // Pluck out the special attachpoint 'projectile-hit'
+ if (prop.m_PropPointName == "projectile-hit")
+ {
+ m_ProjectileHitModelName = prop.m_ModelName;
+ continue;
+ }
+
CObjectEntry* oe = objectManager.FindObjectVariation(prop.m_ModelName.c_str(), selections);
if (!oe)
{
Index: source/simulation2/components/CCmpProjectileManager.cpp
===================================================================
--- source/simulation2/components/CCmpProjectileManager.cpp
+++ source/simulation2/components/CCmpProjectileManager.cpp
@@ -126,6 +126,7 @@
float gravity;
bool stopped;
uint32_t id;
+ std::wstring projectileHitName;
CVector3D position(float t)
{
@@ -141,8 +142,16 @@
}
};
+ struct ProjectileHitAnimation {
+ CUnit* unit;
+ CVector3D pos;
+ float time;
+ };
+
std::vector m_Projectiles;
+ std::vector m_ProjectileHitAnimations;
+
uint32_t m_ActorSeed;
uint32_t m_NextId;
@@ -184,6 +193,7 @@
projectile.time = 0.f;
projectile.stopped = false;
projectile.gravity = gravity.ToFloat();
+ projectile.projectileHitName = cmpSourceVisual->GetProjectileHitActor();
projectile.origin = cmpSourceVisual->GetProjectileLaunchPoint();
if (!projectile.origin)
@@ -286,7 +296,27 @@
// Those hitting the ground stay for a while, because it looks pretty.
if (m_Projectiles[i].stopped)
{
- if (m_Projectiles[i].time - m_Projectiles[i].timeHit > PROJECTILE_DECAY_TIME)
+ if (!m_Projectiles[i].projectileHitName.empty())
+ {
+ ProjectileHitAnimation projectileHitAnimation;
+ CMatrix3D transform;
+ CQuaternion quat;
+ quat.ToMatrix(transform);
+ transform.Translate(m_Projectiles[i].pos);
+
+ std::set selections;
+ CUnit* unit = GetSimContext().GetUnitManager().CreateUnit(m_Projectiles[i].projectileHitName, m_ActorSeed++, selections);
+ unit->GetModel().SetTransform(transform);
+
+ projectileHitAnimation.unit = unit;
+ // TODO: explosion duration from the template instead of hard-coding?
+ projectileHitAnimation.time = 0.1f;
+ projectileHitAnimation.pos = m_Projectiles[i].pos;
+ m_ProjectileHitAnimations.push_back(projectileHitAnimation);
+ }
+
+ // TODO: rethink this hardcoded decay time and animation as part of #1912
+ //if (m_Projectiles[i].time - m_Projectiles[i].timeHit > PROJECTILE_DECAY_TIME)
{
// Delete in-place by swapping with the last in the list
std::swap(m_Projectiles[i], m_Projectiles.back());
@@ -298,6 +328,22 @@
++i;
}
+
+ for (size_t i = 0; i < m_ProjectileHitAnimations.size();)
+ {
+ if (m_ProjectileHitAnimations[i].time > 0)
+ {
+ m_ProjectileHitAnimations[i].time -= frameTime;
+ }
+ else
+ {
+ std::swap(m_ProjectileHitAnimations[i], m_ProjectileHitAnimations.back());
+ GetSimContext().GetUnitManager().DeleteUnit(m_ProjectileHitAnimations.back().unit);
+ m_ProjectileHitAnimations.pop_back();
+ continue;
+ }
+ ++i;
+ }
}
void CCmpProjectileManager::RemoveProjectile(uint32_t id)
@@ -316,6 +362,28 @@
}
}
+template
+void renderObject(T object, SceneCollector& collector, const CFrustum& frustum, bool culling,
+ CmpPtr cmpRangeManager, int player, ICmpRangeManager::CLosQuerier los, bool losRevealAll)
+{
+ // Don't display objects outside the visible area
+ ssize_t posi = (ssize_t)(0.5f + object.pos.X / TERRAIN_TILE_SIZE);
+ ssize_t posj = (ssize_t)(0.5f + object.pos.Z / TERRAIN_TILE_SIZE);
+ if (!losRevealAll && !los.IsVisible(posi, posj))
+ return;
+
+ CModelAbstract& model = object.unit->GetModel();
+
+ model.ValidatePosition();
+
+ if (culling && !frustum.IsBoxVisible(model.GetWorldBoundsRec()))
+ return;
+
+ // TODO: do something about LOS (copy from CCmpVisualActor)
+
+ collector.SubmitRecursive(&model);
+}
+
void CCmpProjectileManager::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling) const
{
CmpPtr cmpRangeManager(GetSystemEntity());
@@ -325,21 +393,27 @@
for (size_t i = 0; i < m_Projectiles.size(); ++i)
{
- // Don't display projectiles outside the visible area
- ssize_t posi = (ssize_t)(0.5f + m_Projectiles[i].pos.X / TERRAIN_TILE_SIZE);
- ssize_t posj = (ssize_t)(0.5f + m_Projectiles[i].pos.Z / TERRAIN_TILE_SIZE);
- if (!losRevealAll && !los.IsVisible(posi, posj))
- continue;
-
- CModelAbstract& model = m_Projectiles[i].unit->GetModel();
-
- model.ValidatePosition();
-
- if (culling && !frustum.IsBoxVisible(model.GetWorldBoundsRec()))
- continue;
-
- // TODO: do something about LOS (copy from CCmpVisualActor)
-
- collector.SubmitRecursive(&model);
+ renderObject(
+ m_Projectiles[i],
+ collector,
+ frustum,
+ culling,
+ cmpRangeManager,
+ player,
+ los,
+ losRevealAll);
+ }
+
+ for (size_t i = 0; i < m_ProjectileHitAnimations.size(); ++i)
+ {
+ renderObject(
+ m_ProjectileHitAnimations[i],
+ collector,
+ frustum,
+ culling,
+ cmpRangeManager,
+ player,
+ los,
+ losRevealAll);
}
}
Index: source/simulation2/components/CCmpVisualActor.cpp
===================================================================
--- source/simulation2/components/CCmpVisualActor.cpp
+++ source/simulation2/components/CCmpVisualActor.cpp
@@ -387,6 +387,13 @@
return m_Unit->GetObject().m_ProjectileModelName;
}
+ virtual std::wstring GetProjectileHitActor() const
+ {
+ if (!m_Unit)
+ return L"";
+ return m_Unit->GetObject().m_ProjectileHitModelName;
+ }
+
virtual CVector3D GetProjectileLaunchPoint() const
{
if (!m_Unit)
Index: source/simulation2/components/ICmpVisual.h
===================================================================
--- source/simulation2/components/ICmpVisual.h
+++ source/simulation2/components/ICmpVisual.h
@@ -65,6 +65,13 @@
*/
virtual std::wstring GetProjectileActor() const = 0;
+
+ /**
+ * Return the filename of the actor to be used for projectiles hit from this unit, or the empty string if none.
+ * (Not safe for use in simulation code.)
+ */
+ virtual std::wstring GetProjectileHitActor() const = 0;
+
/**
* Return the exact position where a projectile should be launched from (based on the actor's
* ammo prop points).