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,18 @@
// Launch the graphical projectile.
let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
- let id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity);
+
+ let actorName = "";
+ let hitActorName = "";
+ let hitAnimationTime = 0;
+ if (this.template.Ranged.Projectile) {
+ actorName = this.template.Ranged.Projectile.Actor || "";
+ hitActorName = this.template.Ranged.Projectile.HitActor || "";
+ hitAnimationTime = this.template.Ranged.Projectile.HitAnimationTime || 0;
+ }
+
+ let id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity,
+ actorName, hitActorName, hitAnimationTime);
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,11 @@
5000
4.0
0
+
+ props/units/weapons/rock_flaming.xml
+ 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
@@ -107,13 +107,17 @@
}
}
- virtual uint32_t LaunchProjectileAtPoint(entity_id_t source, const CFixedVector3D& target, fixed speed, fixed gravity)
+ virtual uint32_t LaunchProjectileAtPoint(entity_id_t source, const CFixedVector3D& target, fixed speed, fixed gravity,
+ const std::wstring& actorName, const std::wstring& hitActorName, fixed hitAnimationTime)
{
- return LaunchProjectile(source, target, speed, gravity);
+ return LaunchProjectile(source, target, speed, gravity, actorName, hitActorName, hitAnimationTime);
}
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 +130,8 @@
float gravity;
bool stopped;
uint32_t id;
+ std::wstring hitName;
+ float hitAnimationTime;
CVector3D position(float t)
{
@@ -141,13 +147,22 @@
}
};
+ struct ProjectileHitAnimation {
+ CUnit* unit;
+ CVector3D pos;
+ float time;
+ };
+
std::vector m_Projectiles;
+ std::vector m_ProjectileHitAnimations;
+
uint32_t m_ActorSeed;
uint32_t m_NextId;
- uint32_t LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity);
+ uint32_t LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity,
+ const std::wstring& actorName, const std::wstring& hitActorName, fixed hitAnimationTime);
void AdvanceProjectile(Projectile& projectile, float dt) const;
@@ -158,7 +173,8 @@
REGISTER_COMPONENT_TYPE(ProjectileManager)
-uint32_t CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity)
+uint32_t CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity,
+ const std::wstring& actorName, const std::wstring& hitActorName, fixed hitAnimationTime)
{
// This is network synced so don't use GUI checks before incrementing or it breaks any non GUI simulations
uint32_t currentId = m_NextId++;
@@ -166,26 +182,48 @@
if (!GetSimContext().HasUnitManager())
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.origin = cmpSourceVisual->GetProjectileLaunchPoint();
+ CmpPtr cmpSourceVisualActor(GetSimContext(), source);
+ if (!cmpSourceVisualActor)
+ return currentId;
+
+ std::wstring name;
+ if (!actorName.empty())
+ {
+ name = actorName;
+ }
+ else
+ {
+ // TODO: remove this fallback to the cmpSourceVisualActor when all the templates are updated with the projectile actor name
+ name = cmpSourceVisualActor->GetProjectileActor();
+ if (name.empty())
+ {
+ // If the actor was actually loaded, complain that it doesn't have a projectile
+ if (!cmpSourceVisualActor->GetActorShortName().empty())
+ LOGERROR(
+ "Unit with actor '%s' launched a projectile but has no actor on 'projectile' attachpoint",
+ utf8_from_wstring(cmpSourceVisualActor->GetActorShortName()));
+ return currentId;
+ }
+ }
+
+ if (!hitActorName.empty())
+ {
+ projectile.hitName = hitActorName;
+ projectile.hitAnimationTime = hitAnimationTime.ToFloat();
+ }
+ else
+ {
+ projectile.hitName = L"";
+ projectile.hitAnimationTime = 0.0f;
+ }
+
+ projectile.origin = cmpSourceVisualActor->GetProjectileLaunchPoint();
if (!projectile.origin)
{
// If there's no explicit launch point, take a guess based on the entity position
@@ -284,19 +322,49 @@
{
// 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].hitName.empty())
+ {
+ CMatrix3D transform;
+ CQuaternion quat;
+ quat.ToMatrix(transform);
+ transform.Translate(m_Projectiles[i].pos);
+
+ std::set selections;
+ CUnit* unit = GetSimContext().GetUnitManager().CreateUnit(m_Projectiles[i].hitName, m_ActorSeed++, selections);
+ unit->GetModel().SetTransform(transform);
+
+ ProjectileHitAnimation projectileHitAnimation;
+ projectileHitAnimation.unit = unit;
+ projectileHitAnimation.time = m_Projectiles[i].hitAnimationTime;
+ projectileHitAnimation.pos = m_Projectiles[i].pos;
+ m_ProjectileHitAnimations.push_back(projectileHitAnimation);
}
- ++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();
+ }
+
+ for (size_t i = 0; i < m_ProjectileHitAnimations.size();)
+ {
+ if (m_ProjectileHitAnimations[i].time > 0)
+ {
+ m_ProjectileHitAnimations[i].time -= frameTime;
+ ++i;
+ }
+ else
+ {
+ std::swap(m_ProjectileHitAnimations[i], m_ProjectileHitAnimations.back());
+ GetSimContext().GetUnitManager().DeleteUnit(m_ProjectileHitAnimations.back().unit);
+ m_ProjectileHitAnimations.pop_back();
+ }
}
}
@@ -316,6 +384,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 +410,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 ProjectileHitAnimation& projectileHitAnimation : m_ProjectileHitAnimations)
+ {
+ RenderModel(projectileHitAnimation.unit->GetModel(), projectileHitAnimation.pos,
+ collector, frustum, culling, los, losRevealAll);
}
}
Index: source/simulation2/components/ICmpProjectileManager.h
===================================================================
--- source/simulation2/components/ICmpProjectileManager.h
+++ source/simulation2/components/ICmpProjectileManager.h
@@ -38,9 +38,13 @@
* @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 hitActorName name of the animation actor played when the projectile hits the target or the ground
+ * @param hitAnimationTime 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(entity_id_t source, const CFixedVector3D& target, fixed speed, fixed gravity,
+ const std::wstring& actorName, const std::wstring& hitActorName, fixed hitAnimationTime) = 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, entity_id_t, CFixedVector3D, fixed, fixed, std::wstring, std::wstring, fixed)
DEFINE_INTERFACE_METHOD_1("RemoveProjectile", void, ICmpProjectileManager, RemoveProjectile, uint32_t)
END_INTERFACE_WRAPPER(ProjectileManager)