From 95c03dcc641bc54ff909a7f21abae0dbee126f77 Mon Sep 17 00:00:00 2001 From: wraitii Date: Sat, 23 Dec 2017 09:27:19 +0000 Subject: [PATCH] Let the template define the actor used for the projectile. Also let projectiles have an impact animation (such as an explosion). This will enable us in the future to have technologies that change projectiles. This is also somewhat of a refactoring. Patch By: Mate-86 Reviewed By: wraitii Trac Tickets: #1909 Differential Revision: https://code.wildfiregames.com/D945 This was SVN commit r20676. --- .../props/units/weapons/rock_explosion.xml | 15 ++ .../props/units/weapons/rock_flaming.xml | 4 +- .../public/simulation/components/Attack.js | 56 +++++- .../template_unit_mechanical_siege_onager.xml | 4 + .../components/CCmpProjectileManager.cpp | 163 ++++++++++++------ .../components/CCmpVisualActor.cpp | 11 +- .../components/ICmpProjectileManager.cpp | 2 +- .../components/ICmpProjectileManager.h | 5 +- source/simulation2/components/ICmpVisual.cpp | 2 + source/simulation2/components/ICmpVisual.h | 4 +- 10 files changed, 201 insertions(+), 65 deletions(-) create mode 100644 binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml diff --git a/binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml b/binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml new file mode 100644 index 0000000000..58f9a8765a --- /dev/null +++ b/binaries/data/mods/public/art/actors/props/units/weapons/rock_explosion.xml @@ -0,0 +1,15 @@ + + + + + + props/onager_projectile.dae + + + + + + + + + diff --git a/binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml b/binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml index c257dfbd89..a386c57577 100644 --- a/binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml +++ b/binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml @@ -8,7 +8,9 @@ - + + + diff --git a/binaries/data/mods/public/simulation/components/Attack.js b/binaries/data/mods/public/simulation/components/Attack.js index bf0bee1fd3..a9ef3369ea 100644 --- a/binaries/data/mods/public/simulation/components/Attack.js +++ b/binaries/data/mods/public/simulation/components/Attack.js @@ -81,6 +81,11 @@ Attack.prototype.Schema = "2" + "" + "" + + "" + + "props/units/weapons/rock_flaming.xml" + + "props/units/weapons/rock_explosion.xml" + + "0.1" + + "" + "Champion" + "" + "Circular" + @@ -150,6 +155,27 @@ Attack.prototype.Schema = Attack.prototype.bonusesSchema + Attack.prototype.preferredClassesSchema + Attack.prototype.restrictedClassesSchema + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + "" + "" + "" + @@ -503,7 +529,35 @@ Attack.prototype.PerformAttack = function(type, target) // Launch the graphical projectile. let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); - let id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity); + + let actorName = ""; + let impactActorName = ""; + let impactAnimationLifetime = 0; + if (this.template.Ranged.Projectile) + { + actorName = this.template.Ranged.Projectile.ActorName || ""; + impactActorName = this.template.Ranged.Projectile.ImpactActorName || ""; + impactAnimationLifetime = this.template.Ranged.Projectile.ImpactAnimationLifetime || 0; + } + + let launchPoint = selfPosition.clone(); + // TODO: remove this when all the ranged unit templates are updated with Projectile/Launchpoint + launchPoint.y += 3; + + let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); + if (cmpVisual) + { + // if the projectile definition is missing from the template + // then fallback to the projectile name and launchpoint in the visual actor + if (!actorName) + actorName = cmpVisual.GetProjectileActor(); + + let visualActorLaunchPoint = cmpVisual.GetProjectileLaunchPoint(); + if (visualActorLaunchPoint.length() > 0) + launchPoint = visualActorLaunchPoint; + } + + let id = cmpProjectileManager.LaunchProjectileAtPoint(launchPoint, realTargetPosition, horizSpeed, gravity, actorName, impactActorName, impactAnimationLifetime); let attackImpactSound = ""; let cmpSound = Engine.QueryInterface(this.entity, IID_Sound); diff --git a/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml b/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml index 29c2b4d158..eb5e0e5bbc 100644 --- a/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml +++ b/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml @@ -13,6 +13,10 @@ 5000 4.0 0 + + props/units/weapons/rock_explosion.xml + 0.1 + Circular 10 diff --git a/source/simulation2/components/CCmpProjectileManager.cpp b/source/simulation2/components/CCmpProjectileManager.cpp index 4cc720e37d..a57be3865a 100644 --- a/source/simulation2/components/CCmpProjectileManager.cpp +++ b/source/simulation2/components/CCmpProjectileManager.cpp @@ -25,7 +25,6 @@ #include "ICmpPosition.h" #include "ICmpRangeManager.h" #include "ICmpTerrain.h" -#include "ICmpVisual.h" #include "simulation2/MessageTypes.h" #include "graphics/Frustum.h" @@ -107,13 +106,16 @@ public: } } - virtual uint32_t LaunchProjectileAtPoint(entity_id_t source, const CFixedVector3D& target, fixed speed, fixed gravity) + virtual uint32_t LaunchProjectileAtPoint(const CFixedVector3D& launchPoint, const CFixedVector3D& target, fixed speed, fixed gravity, const std::wstring& actorName, const std::wstring& impactActorName, fixed impactAnimationLifetime) { - return LaunchProjectile(source, target, speed, gravity); + return LaunchProjectile(launchPoint, target, speed, gravity, actorName, impactActorName, impactAnimationLifetime); } virtual void RemoveProjectile(uint32_t); + void RenderModel(CModelAbstract& model, const CVector3D& position, SceneCollector& collector, const CFrustum& frustum, bool culling, + ICmpRangeManager::CLosQuerier los, bool losRevealAll) const; + private: struct Projectile { @@ -124,8 +126,11 @@ private: float time; float timeHit; float gravity; - bool stopped; + float impactAnimationLifetime; uint32_t id; + std::wstring impactActorName; + bool isImpactAnimationCreated; + bool stopped; CVector3D position(float t) { @@ -141,13 +146,23 @@ private: } }; + struct ProjectileImpactAnimation + { + CUnit* unit; + CVector3D pos; + float time; + }; + std::vector m_Projectiles; + std::vector m_ProjectileImpactAnimations; + uint32_t m_ActorSeed; uint32_t m_NextId; - uint32_t LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity); + uint32_t LaunchProjectile(CFixedVector3D launchPoint, CFixedVector3D targetPoint, fixed speed, fixed gravity, + const std::wstring& actorName, const std::wstring& impactActorName, fixed impactAnimationLifetime); void AdvanceProjectile(Projectile& projectile, float dt) const; @@ -158,46 +173,36 @@ private: REGISTER_COMPONENT_TYPE(ProjectileManager) -uint32_t CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity) +uint32_t CCmpProjectileManager::LaunchProjectile(CFixedVector3D launchPoint, CFixedVector3D targetPoint, fixed speed, fixed gravity, const std::wstring& actorName, const std::wstring& impactActorName, fixed impactAnimationLifetime) { // This is network synced so don't use GUI checks before incrementing or it breaks any non GUI simulations uint32_t currentId = m_NextId++; - if (!GetSimContext().HasUnitManager()) + if (!GetSimContext().HasUnitManager() || actorName.empty()) return currentId; // do nothing if graphics are disabled - CmpPtr cmpSourceVisual(GetSimContext(), source); - if (!cmpSourceVisual) - return currentId; - - std::wstring name = cmpSourceVisual->GetProjectileActor(); - if (name.empty()) - { - // If the actor was actually loaded, complain that it doesn't have a projectile - if (!cmpSourceVisual->GetActorShortName().empty()) - LOGERROR("Unit with actor '%s' launched a projectile but has no actor on 'projectile' attachpoint", utf8_from_wstring(cmpSourceVisual->GetActorShortName())); - return currentId; - } - Projectile projectile; projectile.id = currentId; projectile.time = 0.f; projectile.stopped = false; projectile.gravity = gravity.ToFloat(); + projectile.isImpactAnimationCreated = false; - projectile.origin = cmpSourceVisual->GetProjectileLaunchPoint(); - if (!projectile.origin) + if (!impactActorName.empty()) { - // If there's no explicit launch point, take a guess based on the entity position - CmpPtr sourcePos(GetSimContext(), source); - if (!sourcePos) - return currentId; - projectile.origin = sourcePos->GetPosition(); - projectile.origin.Y += 3.f; + projectile.impactActorName = impactActorName; + projectile.impactAnimationLifetime = impactAnimationLifetime.ToFloat(); + } + else + { + projectile.impactActorName = L""; + projectile.impactAnimationLifetime = 0.0f; } + projectile.origin = launchPoint; + std::set selections; - projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, m_ActorSeed++, selections); + projectile.unit = GetSimContext().GetUnitManager().CreateUnit(actorName, m_ActorSeed++, selections); if (!projectile.unit) // The error will have already been logged return currentId; @@ -282,22 +287,58 @@ void CCmpProjectileManager::Interpolate(float frameTime) // Remove the ones that have reached their target for (size_t i = 0; i < m_Projectiles.size(); ) { - // Projectiles hitting targets get removed immediately. - // Those hitting the ground stay for a while, because it looks pretty. - if (m_Projectiles[i].stopped) + if (!m_Projectiles[i].stopped) { - 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()); - GetSimContext().GetUnitManager().DeleteUnit(m_Projectiles.back().unit); - m_Projectiles.pop_back(); - continue; // don't increment i - } + ++i; + continue; } + if (!m_Projectiles[i].impactActorName.empty() && !m_Projectiles[i].isImpactAnimationCreated) + { + m_Projectiles[i].isImpactAnimationCreated = true; + CMatrix3D transform; + CQuaternion quat; + quat.ToMatrix(transform); + transform.Translate(m_Projectiles[i].pos); + + std::set selections; + CUnit* unit = GetSimContext().GetUnitManager().CreateUnit(m_Projectiles[i].impactActorName, m_ActorSeed++, selections); + unit->GetModel().SetTransform(transform); + + ProjectileImpactAnimation projectileImpactAnimation; + projectileImpactAnimation.unit = unit; + projectileImpactAnimation.time = m_Projectiles[i].impactAnimationLifetime; + projectileImpactAnimation.pos = m_Projectiles[i].pos; + m_ProjectileImpactAnimations.push_back(projectileImpactAnimation); + } + + // Projectiles hitting targets get removed immediately. + // Those hitting the ground stay for a while, because it looks pretty. + 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()); + GetSimContext().GetUnitManager().DeleteUnit(m_Projectiles.back().unit); + m_Projectiles.pop_back(); + continue; + } ++i; } + + for (size_t i = 0; i < m_ProjectileImpactAnimations.size();) + { + if (m_ProjectileImpactAnimations[i].time > 0) + { + m_ProjectileImpactAnimations[i].time -= frameTime; + ++i; + } + else + { + std::swap(m_ProjectileImpactAnimations[i], m_ProjectileImpactAnimations.back()); + GetSimContext().GetUnitManager().DeleteUnit(m_ProjectileImpactAnimations.back().unit); + m_ProjectileImpactAnimations.pop_back(); + } + } } void CCmpProjectileManager::RemoveProjectile(uint32_t id) @@ -316,6 +357,25 @@ void CCmpProjectileManager::RemoveProjectile(uint32_t id) } } +void CCmpProjectileManager::RenderModel(CModelAbstract& model, const CVector3D& position, SceneCollector& collector, + const CFrustum& frustum, bool culling, ICmpRangeManager::CLosQuerier los, bool losRevealAll) const +{ + // Don't display objects outside the visible area + ssize_t posi = (ssize_t)(0.5f + position.X / TERRAIN_TILE_SIZE); + ssize_t posj = (ssize_t)(0.5f + position.Z / TERRAIN_TILE_SIZE); + if (!losRevealAll && !los.IsVisible(posi, posj)) + return; + + 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()); @@ -323,23 +383,14 @@ void CCmpProjectileManager::RenderSubmit(SceneCollector& collector, const CFrust ICmpRangeManager::CLosQuerier los(cmpRangeManager->GetLosQuerier(player)); bool losRevealAll = cmpRangeManager->GetLosRevealAll(player); - for (size_t i = 0; i < m_Projectiles.size(); ++i) + for (const Projectile& projectile : m_Projectiles) { - // 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; + RenderModel(projectile.unit->GetModel(), projectile.pos, collector, frustum, culling, los, losRevealAll); + } - 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); + for (const ProjectileImpactAnimation& projectileImpactAnimation : m_ProjectileImpactAnimations) + { + RenderModel(projectileImpactAnimation.unit->GetModel(), projectileImpactAnimation.pos, + collector, frustum, culling, los, losRevealAll); } } diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index c20d624f54..4ab30abb84 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -387,10 +387,10 @@ public: return m_Unit->GetObject().m_ProjectileModelName; } - virtual CVector3D GetProjectileLaunchPoint() const + virtual CFixedVector3D GetProjectileLaunchPoint() const { if (!m_Unit) - return CVector3D(); + return CFixedVector3D(); if (m_Unit->GetModel().ToCModel()) { @@ -407,10 +407,13 @@ public: CModelAbstract* ammo = m_Unit->GetModel().ToCModel()->FindFirstAmmoProp(); if (ammo) - return ammo->GetTransform().GetTranslation(); + { + CVector3D vector = ammo->GetTransform().GetTranslation(); + return CFixedVector3D(fixed::FromFloat(vector.X), fixed::FromFloat(vector.Y), fixed::FromFloat(vector.Z)); + } } - return CVector3D(); + return CFixedVector3D(); } virtual void SetVariant(const CStr& key, const CStr& selection) diff --git a/source/simulation2/components/ICmpProjectileManager.cpp b/source/simulation2/components/ICmpProjectileManager.cpp index c235a96b44..b598a46f83 100644 --- a/source/simulation2/components/ICmpProjectileManager.cpp +++ b/source/simulation2/components/ICmpProjectileManager.cpp @@ -22,6 +22,6 @@ #include "simulation2/system/InterfaceScripted.h" BEGIN_INTERFACE_WRAPPER(ProjectileManager) -DEFINE_INTERFACE_METHOD_4("LaunchProjectileAtPoint", uint32_t, ICmpProjectileManager, LaunchProjectileAtPoint, entity_id_t, CFixedVector3D, fixed, fixed) +DEFINE_INTERFACE_METHOD_7("LaunchProjectileAtPoint", uint32_t, ICmpProjectileManager, LaunchProjectileAtPoint, CFixedVector3D, CFixedVector3D, fixed, fixed, std::wstring, std::wstring, fixed) DEFINE_INTERFACE_METHOD_1("RemoveProjectile", void, ICmpProjectileManager, RemoveProjectile, uint32_t) END_INTERFACE_WRAPPER(ProjectileManager) diff --git a/source/simulation2/components/ICmpProjectileManager.h b/source/simulation2/components/ICmpProjectileManager.h index 0c99b471ad..a4e0378925 100644 --- a/source/simulation2/components/ICmpProjectileManager.h +++ b/source/simulation2/components/ICmpProjectileManager.h @@ -38,9 +38,12 @@ public: * @param target target point * @param speed horizontal speed in m/s * @param gravity gravitational acceleration in m/s^2 (determines the height of the ballistic curve) + * @param actorName name of the flying projectile actor + * @param impactActorName name of the animation actor played when the projectile hits the target or the ground + * @param impactAnimationLifetime animation lenth * @return id of the created projectile */ - virtual uint32_t LaunchProjectileAtPoint(entity_id_t source, const CFixedVector3D& target, fixed speed, fixed gravity) = 0; + virtual uint32_t LaunchProjectileAtPoint(const CFixedVector3D& launchPoint, const CFixedVector3D& target, fixed speed, fixed gravity, const std::wstring& actorName, const std::wstring& impactActorName, fixed impactAnimationLifetime) = 0; /** * Removes a projectile, used when the projectile has hit a target diff --git a/source/simulation2/components/ICmpVisual.cpp b/source/simulation2/components/ICmpVisual.cpp index aff33a8708..7e0893d502 100644 --- a/source/simulation2/components/ICmpVisual.cpp +++ b/source/simulation2/components/ICmpVisual.cpp @@ -24,6 +24,8 @@ BEGIN_INTERFACE_WRAPPER(Visual) DEFINE_INTERFACE_METHOD_2("SetVariant", void, ICmpVisual, SetVariant, CStr, CStr) DEFINE_INTERFACE_METHOD_CONST_0("GetAnimationName", std::string, ICmpVisual, GetAnimationName) +DEFINE_INTERFACE_METHOD_CONST_0("GetProjectileActor", std::wstring, ICmpVisual, GetProjectileActor) +DEFINE_INTERFACE_METHOD_CONST_0("GetProjectileLaunchPoint", CFixedVector3D, ICmpVisual, GetProjectileLaunchPoint) DEFINE_INTERFACE_METHOD_4("SelectAnimation", void, ICmpVisual, SelectAnimation, std::string, bool, fixed, std::wstring) DEFINE_INTERFACE_METHOD_1("SelectMovementAnimation", void, ICmpVisual, SelectMovementAnimation, fixed) DEFINE_INTERFACE_METHOD_1("ResetMoveAnimation", void, ICmpVisual, ResetMoveAnimation, std::string) diff --git a/source/simulation2/components/ICmpVisual.h b/source/simulation2/components/ICmpVisual.h index 7c608b1939..cc19d4ddd2 100644 --- a/source/simulation2/components/ICmpVisual.h +++ b/source/simulation2/components/ICmpVisual.h @@ -24,6 +24,7 @@ #include "maths/BoundingBoxOriented.h" #include "maths/BoundingBoxAligned.h" #include "maths/Fixed.h" +#include "maths/FixedVector3D.h" #include "lib/file/vfs/vfs_path.h" class CUnit; @@ -68,9 +69,10 @@ public: /** * Return the exact position where a projectile should be launched from (based on the actor's * ammo prop points). + * Return type is CFixedVector3D because it is exposed to the JS interface. * Returns (0,0,0) if no point can be found. */ - virtual CVector3D GetProjectileLaunchPoint() const = 0; + virtual CFixedVector3D GetProjectileLaunchPoint() const = 0; /** * Returns the underlying unit of this visual actor. May return NULL to indicate that no unit exists (e.g. may happen if the