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