2005-05-10 09:13:25 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "Projectile.h"
|
|
|
|
#include "Entity.h"
|
|
|
|
#include "Model.h"
|
|
|
|
#include "Unit.h"
|
|
|
|
#include "Matrix3D.h"
|
|
|
|
#include "ScriptObject.h"
|
|
|
|
#include "Game.h"
|
|
|
|
#include "Collision.h"
|
|
|
|
#include "ObjectManager.h"
|
|
|
|
#include "CLogger.h"
|
|
|
|
|
2005-05-10 14:32:16 +02:00
|
|
|
const double GRAVITY = 0.00001;
|
2005-05-10 09:13:25 +02:00
|
|
|
const double GRAVITY_2 = GRAVITY * 0.5;
|
|
|
|
|
|
|
|
CProjectile::CProjectile( const CModel* Actor, const CVector3D& Position, const CVector3D& Target, float Speed, CEntity* Originator, const CScriptObject& ImpactScript, const CScriptObject& MissScript )
|
|
|
|
{
|
|
|
|
m_Actor = Actor->Clone();
|
|
|
|
m_Position = m_Position_Previous = m_Position_Graphics = Position;
|
|
|
|
m_Speed_H = Speed;
|
|
|
|
m_Originator = Originator;
|
|
|
|
m_ImpactEventHandler = ImpactScript;
|
|
|
|
m_MissEventHandler = MissScript;
|
|
|
|
|
|
|
|
AddHandler( EVENT_IMPACT, &m_ImpactEventHandler );
|
|
|
|
AddHandler( EVENT_MISS, &m_MissEventHandler );
|
|
|
|
|
|
|
|
// That was the easy stuff.
|
|
|
|
// We want horizontal distance only:
|
|
|
|
|
|
|
|
m_Axis = Target - Position;
|
|
|
|
double s = m_Axis.length();
|
|
|
|
m_Axis /= (float)s;
|
|
|
|
|
|
|
|
// Now vertical distance:
|
|
|
|
double d_h = Target.Y - Position.Y;
|
|
|
|
// Time of impact:
|
|
|
|
double t = s / m_Speed_H;
|
|
|
|
// Required vertical velocity at launch:
|
|
|
|
m_Speed_V = (float)( d_h / t + GRAVITY_2 * t );
|
|
|
|
}
|
|
|
|
|
|
|
|
CProjectile::~CProjectile()
|
|
|
|
{
|
|
|
|
std::vector<CProjectile*>::iterator it;
|
|
|
|
for( it = g_ProjectileManager.m_Projectiles.begin(); it != g_ProjectileManager.m_Projectiles.end(); ++it )
|
|
|
|
if( *it == this )
|
|
|
|
{
|
|
|
|
g_ProjectileManager.m_Projectiles.erase( it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
delete( m_Actor );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CProjectile::Update( size_t timestep_millis )
|
|
|
|
{
|
|
|
|
m_Position_Previous = m_Position;
|
|
|
|
m_Position.X += timestep_millis * m_Axis.x * m_Speed_H;
|
|
|
|
m_Position.Z += timestep_millis * m_Axis.y * m_Speed_H;
|
|
|
|
|
|
|
|
m_Position.Y += (float)( timestep_millis * ( m_Speed_V - timestep_millis * GRAVITY_2 ) );
|
|
|
|
m_Speed_V -= (float)( timestep_millis * GRAVITY );
|
|
|
|
|
|
|
|
float height = m_Position.Y - g_Game->GetWorld()->GetTerrain()->getExactGroundLevel( m_Position.X, m_Position.Z );
|
|
|
|
|
|
|
|
if( height < 0.0f )
|
|
|
|
{
|
|
|
|
// We appear to have missed.
|
|
|
|
CEventProjectileMiss evt( m_Originator, m_Position );
|
|
|
|
DispatchEvent( &evt );
|
|
|
|
// Not going to let this be cancelled.
|
|
|
|
return( false );
|
|
|
|
}
|
|
|
|
RayIntersects& r = GetProjectileIntersection( m_Position_Previous, m_Axis, timestep_millis * m_Speed_H );
|
|
|
|
RayIntersects::iterator it;
|
|
|
|
for( it = r.begin(); it != r.end(); it++ )
|
|
|
|
{
|
|
|
|
// Hit something?
|
|
|
|
if( *it != m_Originator ) /* That wouldn't be fair at all... */
|
|
|
|
{
|
|
|
|
// Low enough to hit it?
|
|
|
|
if( height < (*it)->m_bounds->m_height )
|
|
|
|
{
|
|
|
|
CEventProjectileImpact evt( m_Originator, *it, m_Position );
|
|
|
|
if( DispatchEvent( &evt ) )
|
|
|
|
return( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void CProjectile::Interpolate( size_t timestep_millis )
|
|
|
|
{
|
|
|
|
m_Position_Graphics.X = m_Position_Previous.X + timestep_millis * m_Speed_H * m_Axis.x;
|
|
|
|
m_Position_Graphics.Z = m_Position_Previous.Z + timestep_millis * m_Speed_H * m_Axis.y;
|
|
|
|
m_Position_Graphics.Y = (float)( m_Position_Previous.Y + timestep_millis * ( m_Speed_V - timestep_millis * GRAVITY_2 ) );
|
|
|
|
float dh_dt = (float)( m_Speed_V - timestep_millis * GRAVITY );
|
|
|
|
float scale = 1 / sqrt( m_Speed_H * m_Speed_H + dh_dt * dh_dt );
|
|
|
|
float scale2 = m_Speed_H * scale;
|
|
|
|
|
|
|
|
float y = dh_dt * scale;
|
|
|
|
CMatrix3D rotateInc;
|
|
|
|
rotateInc.SetIdentity();
|
|
|
|
rotateInc._22 = rotateInc._33 = y;
|
|
|
|
rotateInc._23 = -( rotateInc._32 = scale2 );
|
|
|
|
|
|
|
|
CMatrix3D rotateDir;
|
|
|
|
rotateDir.SetIdentity();
|
|
|
|
rotateDir._11 = rotateDir._33 = m_Axis.y;
|
|
|
|
rotateDir._31 = -( rotateDir._13 = m_Axis.x );
|
|
|
|
rotateInc.Concatenate( rotateDir );
|
|
|
|
|
|
|
|
rotateInc._14 = m_Position_Graphics.X;
|
|
|
|
rotateInc._24 = m_Position_Graphics.Y;
|
|
|
|
rotateInc._34 = m_Position_Graphics.Z;
|
|
|
|
|
|
|
|
m_Actor->SetTransform( rotateInc );
|
|
|
|
}
|
|
|
|
|
|
|
|
void CProjectile::ScriptingInit()
|
|
|
|
{
|
|
|
|
CJSObject<CProjectile>::ScriptingInit( "Projectile", Construct, 4 );
|
|
|
|
}
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
JSBool CProjectile::Construct( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
|
2005-05-10 09:13:25 +02:00
|
|
|
{
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert( argc >= 4 );
|
2005-05-10 09:13:25 +02:00
|
|
|
CStr ModelString;
|
|
|
|
CVector3D Here, There;
|
|
|
|
float Speed;
|
|
|
|
CEntity* Temp, *Originator = NULL;
|
|
|
|
CObjectEntry* oe = NULL;
|
|
|
|
CModel* Model = NULL;
|
|
|
|
CScriptObject Impact, Miss;
|
|
|
|
const char* err = NULL;
|
|
|
|
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
Temp = ToNative<CEntity>( argv[0] );
|
|
|
|
if(Temp)
|
2005-05-10 09:13:25 +02:00
|
|
|
{
|
|
|
|
Model = Temp->m_actor->GetObject()->m_ProjectileModel;
|
|
|
|
if( !Model )
|
|
|
|
{
|
|
|
|
err = "No projectile model is defined for that entity's actor.";
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
2005-12-09 02:08:08 +01:00
|
|
|
else if( !ToPrimitive<CStr>( cx, argv[0], ModelString ) || NULL == ( oe = g_ObjMan.FindObject( ModelString ) ) || NULL == ( Model = oe->m_Model ) )
|
2005-05-10 09:13:25 +02:00
|
|
|
{
|
|
|
|
err = "Invalid actor";
|
|
|
|
goto fail;
|
|
|
|
}
|
2005-08-09 17:55:44 +02:00
|
|
|
|
|
|
|
Temp = ToNative<CEntity>( argv[1] );
|
|
|
|
if(Temp)
|
2005-05-10 09:13:25 +02:00
|
|
|
{
|
|
|
|
// Use the position vector of this entity, add a bit (so the arrow doesn't appear out of the ground)
|
|
|
|
// In future, find the appropriate position from the entity (location of a specific prop point?)
|
|
|
|
Here = Temp->m_position;
|
|
|
|
Here.Y = g_Game->GetWorld()->GetTerrain()->getExactGroundLevel( Here.X, Here.Z ) + 2.5f;
|
|
|
|
}
|
|
|
|
else if( !( ToPrimitive<CVector3D>( cx, argv[1], Here ) ) )
|
|
|
|
{
|
|
|
|
err = "Invalid vector";
|
|
|
|
goto fail;
|
|
|
|
}
|
2005-08-09 17:55:44 +02:00
|
|
|
|
|
|
|
Temp = ToNative<CEntity>( argv[2] );
|
|
|
|
if(Temp)
|
2005-05-10 09:13:25 +02:00
|
|
|
{
|
|
|
|
// Use the position vector of this entity.
|
|
|
|
// TODO: Maybe: Correct for the movement of this entity.
|
|
|
|
// Then again, that doesn't belong here.
|
|
|
|
There = Temp->m_position;
|
|
|
|
There.Y = g_Game->GetWorld()->GetTerrain()->getExactGroundLevel( There.X, There.Z ) + 2.5f;
|
|
|
|
}
|
|
|
|
else if( !( ToPrimitive<CVector3D>( cx, argv[2], There ) ) )
|
|
|
|
{
|
|
|
|
err = "Invalid vector";
|
|
|
|
goto fail;
|
|
|
|
}
|
2005-08-09 17:55:44 +02:00
|
|
|
|
|
|
|
Speed = ToPrimitive<float>( cx, argv[3] );
|
|
|
|
if( Speed == 0.0f )
|
2005-05-10 09:13:25 +02:00
|
|
|
{
|
|
|
|
// Either wasn't specified, or was zero. In either case,
|
|
|
|
// can't allow it: div/0 errors in the physics
|
|
|
|
err = "Invalid speed";
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
// Ignore errors in these last few and use the defaults if there's a problem.
|
|
|
|
|
|
|
|
if( argc >= 5 )
|
|
|
|
Originator = ToNative<CEntity>( argv[4] );
|
|
|
|
if( argc >= 6 )
|
|
|
|
Impact = argv[5]; // Script to run on impact with an entity.
|
|
|
|
if( argc >= 7 )
|
|
|
|
Miss = argv[6]; // Script to run on impact with the floor.
|
|
|
|
|
2005-05-18 07:32:09 +02:00
|
|
|
{
|
|
|
|
CProjectile* p = g_ProjectileManager.AddProjectile( Model, Here, There, Speed / 1000.0f, Originator, Impact, Miss );
|
2005-05-10 09:13:25 +02:00
|
|
|
|
2005-05-18 07:32:09 +02:00
|
|
|
*rval = ToJSVal<CProjectile>( *p );
|
|
|
|
return( JS_TRUE );
|
|
|
|
}
|
2005-05-10 09:13:25 +02:00
|
|
|
|
|
|
|
fail:
|
|
|
|
*rval = JSVAL_NULL;
|
|
|
|
JS_ReportError( cx, err );
|
|
|
|
return( JS_TRUE );
|
|
|
|
}
|
|
|
|
|
|
|
|
CEventProjectileImpact::CEventProjectileImpact( CEntity* Originator, CEntity* Impact, const CVector3D& Position ) : CScriptEvent( L"ProjectileImpact", EVENT_IMPACT )
|
|
|
|
{
|
|
|
|
m_Originator = Originator;
|
|
|
|
m_Impact = Impact;
|
|
|
|
m_Position = Position;
|
|
|
|
AddLocalProperty( L"originator", &m_Originator, true );
|
|
|
|
AddLocalProperty( L"impacted", &m_Impact );
|
|
|
|
AddLocalProperty( L"position", &m_Position );
|
|
|
|
}
|
|
|
|
|
|
|
|
CEventProjectileMiss::CEventProjectileMiss( CEntity* Originator, const CVector3D& Position ) : CScriptEvent( L"ProjectileMiss", EVENT_MISS )
|
|
|
|
{
|
|
|
|
m_Originator = Originator;
|
|
|
|
m_Position = Position;
|
|
|
|
AddLocalProperty( L"originator", &m_Originator, true );
|
|
|
|
AddLocalProperty( L"position", &m_Position );
|
|
|
|
}
|
|
|
|
|
|
|
|
CProjectileManager::CProjectileManager()
|
|
|
|
{
|
|
|
|
m_LastTurnLength = 0;
|
2005-05-11 20:56:30 +02:00
|
|
|
debug_printf( "CProjectileManager CREATED\n" );
|
2005-05-10 09:13:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CProjectileManager::~CProjectileManager()
|
|
|
|
{
|
|
|
|
while( m_Projectiles.size() )
|
|
|
|
delete( m_Projectiles[0] );
|
2005-05-11 20:56:30 +02:00
|
|
|
debug_printf( "CProjectileManager DESTROYED\n" );
|
2005-05-10 09:13:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CProjectile* CProjectileManager::AddProjectile( const CModel* Actor, const CVector3D& Position, const CVector3D& Target, float Speed, CEntity* Originator, const CScriptObject& ImpactScript, const CScriptObject& MissScript )
|
|
|
|
{
|
|
|
|
CProjectile* p = new CProjectile( Actor, Position, Target, Speed, Originator, ImpactScript, MissScript );
|
|
|
|
m_Projectiles.push_back( p );
|
|
|
|
return( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
void CProjectileManager::DeleteProjectile( CProjectile* p )
|
|
|
|
{
|
|
|
|
delete( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
void CProjectileManager::UpdateAll( size_t timestep )
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
for( i = 0; i < m_Projectiles.size(); i++ )
|
|
|
|
if( !( m_Projectiles[i]->Update( timestep ) ) )
|
|
|
|
{
|
|
|
|
delete( m_Projectiles[i] );
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CProjectileManager::InterpolateAll( double relativeOffset )
|
|
|
|
{
|
|
|
|
size_t absoluteOffset = (size_t)( (double)m_LastTurnLength * relativeOffset );
|
|
|
|
std::vector<CProjectile*>::iterator it;
|
|
|
|
for( it = m_Projectiles.begin(); it != m_Projectiles.end(); ++it )
|
|
|
|
(*it)->Interpolate( absoluteOffset );
|
2005-05-18 07:32:09 +02:00
|
|
|
}
|