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,15 @@
+
+
+
+
+
+ props/onager_projectile.dae
+
+
+
+
+
+
+
+
+
Index: binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml
===================================================================
--- binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml
+++ binaries/data/mods/public/art/actors/props/units/weapons/rock_flaming.xml
@@ -8,7 +8,9 @@
-
+
+
+
Index: binaries/data/mods/public/simulation/components/Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/Attack.js
+++ binaries/data/mods/public/simulation/components/Attack.js
@@ -81,6 +81,11 @@
"2" +
"" +
"" +
+ "" +
+ "props/units/weapons/rock_flaming.xml" +
+ "props/units/weapons/rock_explosion.xml" +
+ "0.1" +
+ "" +
"Champion" +
"" +
"Circular" +
@@ -142,6 +147,27 @@
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
"" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
"" +
"" +
"" +
@@ -494,7 +520,29 @@
// 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 = Object.assign({}, selfPosition);
+ launchPoint.y += 3;
+ let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+ if (cmpVisual) {
+ 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);
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml
+++ 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
Index: source/simulation2/components/CCmpProjectileManager.cpp
===================================================================
--- source/simulation2/components/CCmpProjectileManager.cpp
+++ 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 @@
}
}
- 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
{
@@ -126,6 +128,9 @@
float gravity;
bool stopped;
uint32_t id;
+ std::wstring impactActorName;
+ float impactAnimationLifetime;
+ bool isImpactAnimationCreated;
CVector3D position(float t)
{
@@ -141,13 +146,22 @@
}
};
+ 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 +172,36 @@
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 +286,58 @@
// Remove the ones that have reached their target
for (size_t i = 0; i < m_Projectiles.size(); )
{
+ if (!m_Projectiles[i].stopped)
+ {
+ ++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].stopped)
+ if (m_Projectiles[i].time - m_Projectiles[i].timeHit > PROJECTILE_DECAY_TIME)
{
- 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
- }
+ // 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 +356,25 @@
}
}
+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 +382,14 @@
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;
-
- CModelAbstract& model = m_Projectiles[i].unit->GetModel();
-
- model.ValidatePosition();
-
- if (culling && !frustum.IsBoxVisible(model.GetWorldBoundsRec()))
- continue;
-
- // TODO: do something about LOS (copy from CCmpVisualActor)
+ RenderModel(projectile.unit->GetModel(), projectile.pos, collector, frustum, culling, los, losRevealAll);
+ }
- collector.SubmitRecursive(&model);
+ for (const ProjectileImpactAnimation& projectileImpactAnimation : m_ProjectileImpactAnimations)
+ {
+ RenderModel(projectileImpactAnimation.unit->GetModel(), projectileImpactAnimation.pos,
+ collector, frustum, culling, los, losRevealAll);
}
}
Index: source/simulation2/components/CCmpVisualActor.cpp
===================================================================
--- source/simulation2/components/CCmpVisualActor.cpp
+++ source/simulation2/components/CCmpVisualActor.cpp
@@ -387,10 +387,10 @@
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 @@
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)
Index: source/simulation2/components/ICmpProjectileManager.h
===================================================================
--- source/simulation2/components/ICmpProjectileManager.h
+++ source/simulation2/components/ICmpProjectileManager.h
@@ -38,9 +38,12 @@
* @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
Index: source/simulation2/components/ICmpProjectileManager.cpp
===================================================================
--- source/simulation2/components/ICmpProjectileManager.cpp
+++ 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)
Index: source/simulation2/components/ICmpVisual.h
===================================================================
--- source/simulation2/components/ICmpVisual.h
+++ 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;
@@ -70,7 +71,7 @@
* ammo prop points).
* 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
Index: source/simulation2/components/ICmpVisual.cpp
===================================================================
--- source/simulation2/components/ICmpVisual.cpp
+++ 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)