Ykkrosh
d39a4fac21
# Attempted to make compilation faster by including as little as possible in some .h files, and moving it into .cpp. Fixed BaseTechCollection memory leak. This was SVN commit r3992.
278 lines
8.1 KiB
C++
278 lines
8.1 KiB
C++
#include "precompiled.h"
|
|
|
|
#include "Projectile.h"
|
|
#include "Entity.h"
|
|
#include "graphics/Model.h"
|
|
#include "graphics/Unit.h"
|
|
#include "maths/Matrix3D.h"
|
|
#include "ScriptObject.h"
|
|
#include "ps/Game.h"
|
|
#include "Collision.h"
|
|
#include "graphics/ObjectManager.h"
|
|
#include "graphics/ObjectEntry.h"
|
|
#include "graphics/Terrain.h"
|
|
#include "ps/CLogger.h"
|
|
|
|
const double GRAVITY = 0.00001;
|
|
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 );
|
|
}
|
|
|
|
JSBool CProjectile::Construct( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
debug_assert( argc >= 4 );
|
|
CStr ModelString;
|
|
CVector3D Here, There;
|
|
float Speed;
|
|
CEntity* Temp, *Originator = NULL;
|
|
CObjectEntry* oe = NULL;
|
|
CModel* Model = NULL;
|
|
CScriptObject Impact, Miss;
|
|
const char* err = NULL;
|
|
|
|
|
|
Temp = ToNative<CEntity>( argv[0] );
|
|
if(Temp)
|
|
{
|
|
Model = Temp->m_actor->GetObject()->m_ProjectileModel;
|
|
if( !Model )
|
|
{
|
|
err = "No projectile model is defined for that entity's actor.";
|
|
goto fail;
|
|
}
|
|
}
|
|
else if( !ToPrimitive<CStr>( cx, argv[0], ModelString ) || NULL == ( oe = g_ObjMan.FindObject( ModelString ) ) || NULL == ( Model = oe->m_Model ) )
|
|
{
|
|
err = "Invalid actor";
|
|
goto fail;
|
|
}
|
|
|
|
Temp = ToNative<CEntity>( argv[1] );
|
|
if(Temp)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
Temp = ToNative<CEntity>( argv[2] );
|
|
if(Temp)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
Speed = ToPrimitive<float>( cx, argv[3] );
|
|
if( Speed == 0.0f )
|
|
{
|
|
// 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.
|
|
|
|
{
|
|
CProjectile* p = g_ProjectileManager.AddProjectile( Model, Here, There, Speed / 1000.0f, Originator, Impact, Miss );
|
|
|
|
*rval = ToJSVal<CProjectile>( *p );
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
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;
|
|
debug_printf( "CProjectileManager CREATED\n" );
|
|
}
|
|
|
|
CProjectileManager::~CProjectileManager()
|
|
{
|
|
while( m_Projectiles.size() )
|
|
delete( m_Projectiles[0] );
|
|
debug_printf( "CProjectileManager DESTROYED\n" );
|
|
}
|
|
|
|
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 );
|
|
}
|