Import changes from Doom3: The Lost Mission

Straight from the diff of their released source vs vanilla SDK 1.3.1
Completely untested so far.

Thanks to the Doom3: Lost Mission team for releasing the source and
allowing me to relicense their changes under the GPL!
(see https://github.com/dhewm/dhewm3/issues/265)
This commit is contained in:
Daniel Gibson 2019-12-29 05:42:47 +01:00
parent c198d4ebeb
commit aac103aa31
13 changed files with 677 additions and 0 deletions

View file

@ -208,6 +208,12 @@ public:
idEntityPtr<type> & operator=( type *ent );
//added for LM
idEntityPtr & operator=( const idEntityPtr & ep );
bool operator==( const idEntityPtr & ep ) { return spawnId == ep.spawnId; }
type * operator->() const { return GetEntity(); }
operator type * () const { return GetEntity(); }
// synchronize entity pointers over the network
int GetSpawnId( void ) const { return spawnId; }
bool SetSpawnId( int id );
@ -653,6 +659,13 @@ ID_INLINE idEntityPtr<type> &idEntityPtr<type>::operator=( type *ent ) {
return *this;
}
//added for LM
template< class type >
ID_INLINE idEntityPtr< type > &idEntityPtr<type>::operator=( const idEntityPtr & ep ) {
spawnId = ep.spawnId;
return *this;
}
template< class type >
ID_INLINE bool idEntityPtr<type>::SetSpawnId( int id ) {
// the reason for this first check is unclear:

View file

@ -385,6 +385,12 @@ void idGrabber::StopDrag( bool dropOnly ) {
ent->GetPhysics()->SetContents( savedContents );
ent->GetPhysics()->SetClipMask( savedClipmask );
//added for LM
idProjectile *projectile = static_cast< idProjectile* >( ent );
if ( projectile != NULL ) {
projectile->SetLaunchedFromGrabber( true );
}
} else if ( ent->IsType( idMoveable::Type ) ) {
// Turn on damage for this object
idMoveable *obj = static_cast<idMoveable*>(ent);

View file

@ -1747,6 +1747,161 @@ void idFuncEmitter::ReadFromSnapshot( const idBitMsgDelta &msg ) {
}
}
//added for LM
/*
===============================================================================
idFuncShootProjectile
===============================================================================
*/
CLASS_DECLARATION( idStaticEntity, idFuncShootProjectile )
EVENT( EV_Activate, idFuncShootProjectile::Event_Activate )
END_CLASS
/*
===============
idFuncShootProjectile::idFuncShootProjectile
===============
*/
idFuncShootProjectile::idFuncShootProjectile() {
mRespawnDelay = 1000;
mRespawnTime = 0;
mShootSpeed = 1000;
mShootDir = idVec3( 0.0f, 0.0f, 1.0f );
}
/*
===============
idFuncShootProjectile::Spawn
===============
*/
void idFuncShootProjectile::Spawn() {
}
/*
===============
idFuncShootProjectile::Think
===============
*/
void idFuncShootProjectile::Think() {
if ( thinkFlags & TH_THINK ) {
// time to spawn a new projectile?
if ( mRespawnTime > 0 && mRespawnTime <= gameLocal.GetTime() ) {
const idDict *dict = gameLocal.FindEntityDefDict( mEntityDefName );
idEntity *ent = NULL;
gameLocal.SpawnEntityDef( *dict, &ent );
if ( ent != NULL ) {
idProjectile *proj = static_cast<idProjectile *>(ent);
idVec3 pushVel = mShootDir * mShootSpeed;
proj->Create( this, GetPhysics()->GetOrigin(), mShootDir );
proj->Launch( GetPhysics()->GetOrigin(), mShootDir, pushVel );
if ( mShootSpeed == 0.0f ) {
proj->GetPhysics()->SetLinearVelocity( vec3_zero );
} else {
proj->GetPhysics()->SetLinearVelocity( pushVel );
}
mLastProjectile = proj;
}
if ( mShootSpeed == 0.0f ) {
mRespawnTime = 0; // stationary, respawn when triggered
} else {
mRespawnTime = gameLocal.GetTime() + mRespawnDelay; // moving, respawn after delay
}
}
}
}
/*
===============
idFuncShootProjectile::Save
===============
*/
void idFuncShootProjectile::Save( idSaveGame *savefile ) const {
savefile->WriteInt( mRespawnDelay );
savefile->WriteInt( mRespawnTime );
savefile->WriteFloat( mShootSpeed );
savefile->WriteVec3( mShootDir );
savefile->WriteString( mEntityDefName );
}
/*
===============
idFuncShootProjectile::Restore
===============
*/
void idFuncShootProjectile::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( mRespawnDelay );
savefile->ReadInt( mRespawnTime );
savefile->ReadFloat( mShootSpeed );
savefile->ReadVec3( mShootDir );
savefile->ReadString( mEntityDefName );
}
/*
================
idFuncShootProjectile::Event_Activate
================
*/
void idFuncShootProjectile::Event_Activate( idEntity *activator ) {
if ( ( thinkFlags & TH_THINK ) != 0 ) {
if ( mShootSpeed == 0.0f && mRespawnTime == 0 ) {
mRespawnTime = gameLocal.GetTime();
return;
}
}
mRespawnDelay = spawnArgs.GetInt( "spawn_delay_ms" );
mShootSpeed = spawnArgs.GetFloat( "speed" );
mEntityDefName = spawnArgs.GetString( "def_projectile" );
if ( targets.Num() > 0 && targets[0].IsValid() ) {
mShootDir = targets[0]->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
//mShootDir = targets[0].GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
mShootDir.Normalize();
} else {
// stationary projectile, doesn't move and only respawns when triggered
mShootSpeed = 0.0f;
mRespawnTime = 0;
}
if ( ( thinkFlags & TH_THINK ) != 0 ) {
// currently active, deactivate
BecomeInactive( TH_THINK );
} else {
// currently inactive, activate
BecomeActive( TH_THINK );
mRespawnTime = gameLocal.GetTime();
}
}
/*
================
idFuncShootProjectile::WriteToSnapshot
================
*/
void idFuncShootProjectile::WriteToSnapshot( idBitMsg &msg ) const {
// msg.WriteBits( hidden ? 1 : 0, 1 );
// msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] );
// msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
}
/*
================
idFuncShootProjectile::ReadFromSnapshot
================
*/
void idFuncShootProjectile::ReadFromSnapshot( const idBitMsg &msg ) {
// hidden = msg.ReadBits( 1 ) != 0;
// renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat();
// renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat();
// if ( msg.HasChanged() ) {
// UpdateVisuals();
// }
}
/*
===============================================================================

View file

@ -345,6 +345,42 @@ private:
};
//added for LM
/*
===============================================================================
idFuncShootProjectile
===============================================================================
*/
class idFuncShootProjectile : public idStaticEntity {
public:
CLASS_PROTOTYPE( idFuncShootProjectile );
idFuncShootProjectile();
void Save( idSaveGame *savefile ) const;
void Restore( idRestoreGame *savefile );
void Spawn();
void Event_Activate( idEntity *activator );
virtual void Think();
virtual void WriteToSnapshot( idBitMsg &msg ) const;
virtual void ReadFromSnapshot( const idBitMsg &msg );
private:
int mRespawnDelay;
int mRespawnTime;
float mShootSpeed;
idVec3 mShootDir;
idStr mEntityDefName;
idEntityPtr< idEntity > mLastProjectile;
};
/*
===============================================================================

View file

@ -1901,6 +1901,13 @@ void idPlayer::Spawn( void ) {
inventory.selPDA = 0;
if ( !gameLocal.isMultiplayer ) {
int startingHealth = gameLocal.world->spawnArgs.GetInt( "startingHealth", "0");
if ( (startingHealth > 0) && (health > startingHealth) ) {
health = startingHealth;
}
if ( g_skill.GetInteger() < 2 ) {
if ( health < 25 ) {
health = 25;

View file

@ -98,6 +98,11 @@ idProjectile::idProjectile( void ) {
memset( &projectileFlags, 0, sizeof( projectileFlags ) );
memset( &renderLight, 0, sizeof( renderLight ) );
//added for LM
launchedFromGrabber = false;
mTouchTriggers = false;
mNoExplodeDisappear = false;
// note: for net_instanthit projectiles, we will force this back to false at spawn time
fl.networkSync = true;
@ -116,6 +121,10 @@ void idProjectile::Spawn( void ) {
physicsObj.SetClipMask( 0 );
physicsObj.PutToRest();
SetPhysics( &physicsObj );
//added for LM
mNoExplodeDisappear = spawnArgs.GetBool( "no_explode_disappear", mNoExplodeDisappear );
mTouchTriggers = spawnArgs.GetBool( "touch_triggers", mTouchTriggers );
}
/*
@ -487,6 +496,11 @@ void idProjectile::Think( void ) {
}
}
//Added for LM
if ( mTouchTriggers ) {
TouchTriggers();
}
// run physics
RunPhysics();
@ -2654,3 +2668,211 @@ idDebris::Event_Fizzle
void idDebris::Event_Fizzle( void ) {
Fizzle();
}
/*
===============================================================================
idHomingProjectile
===============================================================================
*/
CLASS_DECLARATION( idProjectile, idHomingProjectile )
EVENT( EV_SetEnemy, idHomingProjectile::Event_SetEnemy )
END_CLASS
/*
================
idHomingProjectile::idHomingProjectile
================
*/
idHomingProjectile::idHomingProjectile() {
enemy = NULL;
speed = 0.0f;
turn_max = 0.0f;
clamp_dist = 0.0f;
rndScale = ang_zero;
rndAng = ang_zero;
angles = ang_zero;
burstMode = false;
burstDist = 0;
burstVelocity = 0.0f;
unGuided = false;
seekPos = vec3_origin;
}
/*
=================
idHomingProjectile::~idHomingProjectile
=================
*/
idHomingProjectile::~idHomingProjectile() {
}
/*
================
idHomingProjectile::Spawn
================
*/
void idHomingProjectile::Spawn() {
}
/*
================
idHomingProjectile::Save
================
*/
void idHomingProjectile::Save( idSaveGame *savefile ) const {
enemy.Save( savefile );
savefile->WriteFloat( speed );
savefile->WriteAngles( rndScale );
savefile->WriteAngles( rndAng );
savefile->WriteFloat( turn_max );
savefile->WriteFloat( clamp_dist );
savefile->WriteAngles( angles );
savefile->WriteBool( burstMode );
savefile->WriteBool( unGuided );
savefile->WriteFloat( burstDist );
savefile->WriteFloat( burstVelocity );
savefile->WriteVec3( seekPos );
}
/*
================
idHomingProjectile::Restore
================
*/
void idHomingProjectile::Restore( idRestoreGame *savefile ) {
enemy.Restore( savefile );
savefile->ReadFloat( speed );
savefile->ReadAngles( rndScale );
savefile->ReadAngles( rndAng );
savefile->ReadFloat( turn_max );
savefile->ReadFloat( clamp_dist );
savefile->ReadAngles( angles );
savefile->ReadBool( burstMode );
savefile->ReadBool( unGuided );
savefile->ReadFloat( burstDist );
savefile->ReadFloat( burstVelocity );
savefile->ReadVec3( seekPos );
}
/*
================
idHomingProjectile::Think
================
*/
void idHomingProjectile::Think() {
if ( seekPos == vec3_zero ) {
// ai def uses a single def_projectile .. guardian has two projectile types so when seekPos is zero, just run regular projectile
idProjectile::Think();
return;
}
idVec3 dir;
idVec3 velocity;
idVec3 nose;
idVec3 tmp;
idMat3 axis;
idAngles dirAng;
idAngles diff;
float dist;
float frac;
int i;
nose = physicsObj.GetOrigin() + 10.0f * physicsObj.GetAxis()[0];
dir = seekPos - nose;
dist = dir.Normalize();
dirAng = dir.ToAngles();
// make it more accurate as it gets closer
frac = ( dist * 2.0f ) / clamp_dist;
if ( frac > 1.0f ) {
frac = 1.0f;
}
diff = dirAng - angles * frac;
// clamp the to the max turn rate
diff.Normalize180();
for( i = 0; i < 3; i++ ) {
if ( diff[ i ] > turn_max ) {
diff[ i ] = turn_max;
} else if ( diff[ i ] < -turn_max ) {
diff[ i ] = -turn_max;
}
}
angles += diff;
// make the visual model always points the dir we're traveling
dir = angles.ToForward();
velocity = dir * speed;
if ( burstMode && dist < burstDist ) {
unGuided = true;
velocity *= burstVelocity;
}
physicsObj.SetLinearVelocity( velocity );
// align z-axis of model with the direction
axis = dir.ToMat3();
tmp = axis[2];
axis[2] = axis[0];
axis[0] = -tmp;
GetPhysics()->SetAxis( axis );
idProjectile::Think();
}
/*
=================
idHomingProjectile::Launch
=================
*/
void idHomingProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, float dmgPower ) {
idProjectile::Launch( start, dir, pushVelocity, timeSinceFire, launchPower, dmgPower );
if ( owner.GetEntity() ) {
if ( owner.GetEntity()->IsType( idAI::Type ) ) {
enemy = static_cast<idAI *>( owner.GetEntity() )->GetEnemy();
} else if ( owner.GetEntity()->IsType( idPlayer::Type ) ) {
trace_t tr;
idPlayer *player = static_cast<idPlayer*>( owner.GetEntity() );
idVec3 start = player->GetEyePosition();
idVec3 end = start + player->viewAxis[0] * 1000.0f;
gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL | CONTENTS_BODY, owner.GetEntity() );
if ( tr.fraction < 1.0f ) {
enemy = gameLocal.GetTraceEntity( tr );
}
// ignore actors on the player's team
if ( enemy.GetEntity() == NULL || !enemy.GetEntity()->IsType( idActor::Type ) || ( static_cast<idActor *>( enemy.GetEntity() )->team == player->team ) ) {
enemy = player->EnemyWithMostHealth();
}
}
}
const idVec3 &vel = physicsObj.GetLinearVelocity();
float com_engineHz_latched = 60.0f;
angles = vel.ToAngles();
speed = vel.Length();
rndScale = spawnArgs.GetAngles( "random", "15 15 0" );
turn_max = spawnArgs.GetFloat( "turn_max", "180" ) / com_engineHz_latched;
clamp_dist = spawnArgs.GetFloat( "clamp_dist", "256" );
burstMode = spawnArgs.GetBool( "burstMode" );
unGuided = false;
burstDist = spawnArgs.GetFloat( "burstDist", "64" );
burstVelocity = spawnArgs.GetFloat( "burstVelocity", "1.25" );
UpdateVisuals();
}
void idHomingProjectile::SetEnemy( idEntity *ent ) {
enemy = ent;
}
void idHomingProjectile::SetSeekPos( idVec3 pos ) {
seekPos = pos;
}
void idHomingProjectile::Event_SetEnemy(idEntity *ent) {
SetEnemy(ent);
}

View file

@ -82,6 +82,10 @@ public :
EVENT_MAXEVENTS
};
//Added for LM
void SetLaunchedFromGrabber( bool bl ) { launchedFromGrabber = bl; }
bool GetLaunchedFromGrabber() { return launchedFromGrabber; }
static void DefaultDamageEffect( idEntity *soundEnt, const idDict &projectileDef, const trace_t &collision, const idVec3 &velocity );
static bool ClientPredictionCollide( idEntity *soundEnt, const idDict &projectileDef, const trace_t &collision, const idVec3 &velocity, bool addDamageEffect );
virtual void ClientPredictionThink( void );
@ -100,6 +104,9 @@ protected:
bool noSplashDamage : 1;
} projectileFlags;
//Added for LM
bool launchedFromGrabber;
float thrust;
int thrust_end;
float damagePower;
@ -117,6 +124,10 @@ protected:
const idDeclParticle * smokeFly;
int smokeFlyTime;
//Code for LM
bool mNoExplodeDisappear;
bool mTouchTriggers;
#ifdef _D3XP
int originalTimeGroup;
#endif
@ -243,6 +254,43 @@ private:
void ApplyDamage();
};
//Added for LM (Lost mission)
class idHomingProjectile : public idProjectile {
public :
CLASS_PROTOTYPE( idHomingProjectile );
idHomingProjectile();
~idHomingProjectile();
void Save( idSaveGame *savefile ) const;
void Restore( idRestoreGame *savefile );
void Spawn();
virtual void Think();
virtual void Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire = 0.0f, const float launchPower = 1.0f, const float dmgPower = 1.0f );
void SetEnemy( idEntity *ent );
void SetSeekPos( idVec3 pos );
void Event_SetEnemy(idEntity *ent);
protected:
float speed;
idEntityPtr<idEntity> enemy;
idVec3 seekPos;
private:
idAngles rndScale;
idAngles rndAng;
idAngles angles;
float turn_max;
float clamp_dist;
bool burstMode;
bool unGuided;
float burstDist;
float burstVelocity;
};
/*
===============================================================================

View file

@ -537,6 +537,9 @@ idTrigger_EntityName::idTrigger_EntityName( void ) {
random_delay = 0.0f;
nextTriggerTime = 0;
triggerFirst = false;
//added for LM
testPartialName = false;
}
/*
@ -552,6 +555,8 @@ void idTrigger_EntityName::Save( idSaveGame *savefile ) const {
savefile->WriteInt( nextTriggerTime );
savefile->WriteBool( triggerFirst );
savefile->WriteString( entityName );
//added For LM
savefile->WriteBool( testPartialName );
}
/*
@ -567,6 +572,8 @@ void idTrigger_EntityName::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( nextTriggerTime );
savefile->ReadBool( triggerFirst );
savefile->ReadString( entityName );
//added for LM
savefile->ReadBool( testPartialName );
}
/*
@ -602,6 +609,9 @@ void idTrigger_EntityName::Spawn( void ) {
if ( !spawnArgs.GetBool( "noTouch" ) ) {
GetPhysics()->SetContents( CONTENTS_TRIGGER );
}
//added for LM
testPartialName = spawnArgs.GetBool( "testPartialName", testPartialName );
}
/*
@ -648,9 +658,24 @@ void idTrigger_EntityName::Event_Trigger( idEntity *activator ) {
return;
}
//added for LM
bool validEntity = false;
if ( activator ) {
if ( testPartialName ) {
if ( activator->name.Find( entityName, false ) >= 0 ) {
validEntity = true;
}
}
if ( activator->name == entityName ) {
validEntity = true;
}
}
/*
if ( !activator || ( activator->name != entityName ) ) {
return;
}
*/
if ( triggerFirst ) {
triggerFirst = false;
@ -684,9 +709,27 @@ void idTrigger_EntityName::Event_Touch( idEntity *other, trace_t *trace ) {
return;
}
//added for LM
bool validEntity = false;
if ( other ) {
if ( testPartialName ) {
if ( other->name.Find( entityName, false ) >= 0 ) {
validEntity = true;
}
}
if ( other->name == entityName ) {
validEntity = true;
}
}
if ( !validEntity ) {
return;
}
/*
if ( !other || ( other->name != entityName ) ) {
return;
}
*/
nextTriggerTime = gameLocal.time + 1;
if ( delay > 0 ) {

View file

@ -142,6 +142,8 @@ private:
int nextTriggerTime;
bool triggerFirst;
idStr entityName;
//added for LM
bool testPartialName;
void TriggerAction( idEntity *activator );
void Event_TriggerAction( idEntity *activator );

View file

@ -359,6 +359,8 @@ protected:
idVec3 projectileGravity;
idEntityPtr<idProjectile> projectile;
idStr attack;
//Added for the LM
idVec3 homingMissileGoal;
// chatter/talking
const idSoundShader *chat_snd;
@ -585,8 +587,14 @@ protected:
void Event_AttackMissile( const char *jointname );
void Event_FireMissileAtTarget( const char *jointname, const char *targetname );
void Event_LaunchMissile( const idVec3 &muzzle, const idAngles &ang );
#ifdef _D3XP
//Added for the LM
void Event_LaunchHomingMissile();
void Event_SetHomingMissileGoal();
void Event_LaunchProjectile( const char *entityDefName );
#endif
void Event_AttackMelee( const char *meleeDefName );
void Event_DirectDamage( idEntity *damageTarget, const char *damageDefName );

View file

@ -51,8 +51,14 @@ const idEventDef AI_CreateMissile( "createMissile", "s", 'e' );
const idEventDef AI_AttackMissile( "attackMissile", "s", 'e' );
const idEventDef AI_FireMissileAtTarget( "fireMissileAtTarget", "ss", 'e' );
const idEventDef AI_LaunchMissile( "launchMissile", "vv", 'e' );
#ifdef _D3XP
//Added for the LM
const idEventDef AI_LaunchHomingMissile( "launchHomingMissile" );
const idEventDef AI_SetHomingMissileGoal( "setHomingMissileGoal" );
const idEventDef AI_LaunchProjectile( "launchProjectile", "s" );
#endif
const idEventDef AI_AttackMelee( "attackMelee", "s", 'd' );
const idEventDef AI_DirectDamage( "directDamage", "es" );
@ -191,6 +197,11 @@ CLASS_DECLARATION( idActor, idAI )
EVENT( AI_AttackMissile, idAI::Event_AttackMissile )
EVENT( AI_FireMissileAtTarget, idAI::Event_FireMissileAtTarget )
EVENT( AI_LaunchMissile, idAI::Event_LaunchMissile )
//Added for the LM
EVENT( AI_LaunchHomingMissile, idAI::Event_LaunchHomingMissile )
EVENT( AI_SetHomingMissileGoal, idAI::Event_SetHomingMissileGoal )
#ifdef _D3XP
EVENT( AI_LaunchProjectile, idAI::Event_LaunchProjectile )
#endif
@ -2903,4 +2914,95 @@ void idAI::Event_StopEmitter( const char* name ) {
StopEmitter(name);
}
//Added for LM (Lost mission)
/*
=====================
idAI::Event_LaunchHomingMissile
=====================
*/
void idAI::Event_LaunchHomingMissile() {
idVec3 start;
trace_t tr;
idBounds projBounds;
const idClipModel *projClip;
idMat3 axis;
float distance;
if ( !projectileDef ) {
gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
idThread::ReturnEntity( NULL );
return;
}
idActor *enemy = GetEnemy();
if ( enemy == NULL ) {
idThread::ReturnEntity( NULL );
return;
}
idVec3 org = GetPhysics()->GetOrigin() + idVec3( 0.0f, 0.0f, 250.0f );
idVec3 goal = enemy->GetPhysics()->GetOrigin();
homingMissileGoal = goal;
// axis = ( goal - org ).ToMat3();
// axis.Identity();
if ( !projectile.GetEntity() ) {
idHomingProjectile *homing = ( idHomingProjectile * ) CreateProjectile( org, idVec3( 0.0f, 0.0f, 1.0f ) );
if ( homing != NULL ) {
homing->SetEnemy( enemy );
homing->SetSeekPos( homingMissileGoal );
}
}
// make sure the projectile starts inside the monster bounding box
const idBounds &ownerBounds = physicsObj.GetAbsBounds();
projClip = projectile.GetEntity()->GetPhysics()->GetClipModel();
projBounds = projClip->GetBounds().Rotate( projClip->GetAxis() );
// check if the owner bounds is bigger than the projectile bounds
if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
start = org + distance * viewAxis[ 0 ];
} else {
start = ownerBounds.GetCenter();
}
} else {
// projectile bounds bigger than the owner bounds, so just start it from the center
start = ownerBounds.GetCenter();
}
gameLocal.clip.Translation( tr, start, org, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this );
// launch the projectile
idThread::ReturnEntity( projectile.GetEntity() );
idVec3 dir = homingMissileGoal - org;
idAngles ang = dir.ToAngles();
ang.pitch = -45.0f;
projectile.GetEntity()->Launch( org, ang.ToForward(), vec3_origin );
projectile = NULL;
TriggerWeaponEffects( tr.endpos );
lastAttackTime = gameLocal.time;
}
/*
=====================
idAI::Event_SetHomingMissileGoal
=====================
*/
void idAI::Event_SetHomingMissileGoal() {
idActor *enemy = GetEnemy();
if ( enemy == NULL ) {
idThread::ReturnEntity( NULL );
return;
}
homingMissileGoal = enemy->GetPhysics()->GetOrigin();
}
#endif

View file

@ -339,6 +339,24 @@ bool idDict::GetBool( const char *key, const char *defaultString, bool &out ) co
return found;
}
//Added for LM
/*
================
idDict::GetBool
================
*/
bool idDict::GetBool( const char *key, const bool defaultBool, bool &out ) const {
const idKeyValue *kv = FindKey( key );
if ( kv ) {
out = ( atoi( kv->GetValue() ) != 0 );
return true;
} else {
out = defaultBool;
return false;
}
}
/*
================
idDict::GetAngles

View file

@ -117,6 +117,9 @@ public:
idAngles GetAngles( const char *key, const char *defaultString = NULL ) const;
idMat3 GetMatrix( const char *key, const char *defaultString = NULL ) const;
//added for LM
bool GetBool( const char *key, const bool defaultBool ) const;
bool GetString( const char *key, const char *defaultString, const char **out ) const;
bool GetString( const char *key, const char *defaultString, idStr &out ) const;
bool GetFloat( const char *key, const char *defaultString, float &out ) const;
@ -128,6 +131,10 @@ public:
bool GetAngles( const char *key, const char *defaultString, idAngles &out ) const;
bool GetMatrix( const char *key, const char *defaultString, idMat3 &out ) const;
//added for LM
bool GetBool( const char *key, const bool defaultBool, bool &out ) const;
int GetNumKeyVals( void ) const;
const idKeyValue * GetKeyVal( int index ) const;
// returns the key/value pair with the given key
@ -263,6 +270,16 @@ ID_INLINE bool idDict::GetBool( const char *key, const char *defaultString ) con
return ( atoi( GetString( key, defaultString ) ) != 0 );
}
//Added for LM
ID_INLINE bool idDict::GetBool( const char *key, const bool defaultBool ) const {
const idKeyValue *kv = FindKey( key );
if ( kv ) {
return atoi( kv->GetValue() ) != 0;
}
return defaultBool;
}
ID_INLINE idVec3 idDict::GetVector( const char *key, const char *defaultString ) const {
idVec3 out;
GetVector( key, defaultString, out );