mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-27 14:42:23 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
3158 lines
72 KiB
C++
3158 lines
72 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
/*
|
|
|
|
Various utility objects and functions.
|
|
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "renderer/ModelManager.h"
|
|
|
|
#include "gamesys/SysCvar.h"
|
|
#include "script/Script_Thread.h"
|
|
#include "ai/AI.h"
|
|
#include "Player.h"
|
|
#include "Camera.h"
|
|
#include "WorldSpawn.h"
|
|
#include "Moveable.h"
|
|
#include "SmokeParticles.h"
|
|
|
|
#include "Misc.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idSpawnableEntity
|
|
|
|
A simple, spawnable entity with a model and no functionable ability of it's own.
|
|
For example, it can be used as a placeholder during development, for marking
|
|
locations on maps for script, or for simple placed models without any behavior
|
|
that can be bound to other entities. Should not be subclassed.
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idSpawnableEntity )
|
|
END_CLASS
|
|
|
|
/*
|
|
======================
|
|
idSpawnableEntity::Spawn
|
|
======================
|
|
*/
|
|
void idSpawnableEntity::Spawn() {
|
|
// this just holds dict information
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idPlayerStart
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_TeleportStage( "<TeleportStage>", "e" );
|
|
|
|
CLASS_DECLARATION( idEntity, idPlayerStart )
|
|
EVENT( EV_Activate, idPlayerStart::Event_TeleportPlayer )
|
|
EVENT( EV_TeleportStage, idPlayerStart::Event_TeleportStage )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idPlayerStart::idPlayerStart
|
|
================
|
|
*/
|
|
idPlayerStart::idPlayerStart( void ) {
|
|
teleportStage = 0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerStart::Spawn
|
|
================
|
|
*/
|
|
void idPlayerStart::Spawn( void ) {
|
|
teleportStage = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayerStart::Save
|
|
================
|
|
*/
|
|
void idPlayerStart::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( teleportStage );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayerStart::Restore
|
|
================
|
|
*/
|
|
void idPlayerStart::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( teleportStage );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayerStart::ClientReceiveEvent
|
|
================
|
|
*/
|
|
bool idPlayerStart::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
|
|
int entityNumber;
|
|
|
|
switch( event ) {
|
|
case EVENT_TELEPORTPLAYER: {
|
|
entityNumber = msg.ReadBits( GENTITYNUM_BITS );
|
|
idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[entityNumber] );
|
|
if ( player != NULL && player->IsType( idPlayer::Type ) ) {
|
|
Event_TeleportPlayer( player );
|
|
}
|
|
return true;
|
|
}
|
|
default: {
|
|
return idEntity::ClientReceiveEvent( event, time, msg );
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerStart::Event_TeleportStage
|
|
|
|
FIXME: add functionality to fx system ( could be done with player scripting too )
|
|
================
|
|
*/
|
|
void idPlayerStart::Event_TeleportStage( idEntity *_player ) {
|
|
idPlayer *player;
|
|
if ( !_player->IsType( idPlayer::Type ) ) {
|
|
common->Warning( "idPlayerStart::Event_TeleportStage: entity is not an idPlayer\n" );
|
|
return;
|
|
}
|
|
player = static_cast<idPlayer*>(_player);
|
|
float teleportDelay = spawnArgs.GetFloat( "teleportDelay" );
|
|
switch ( teleportStage ) {
|
|
case 0:
|
|
player->playerView.Flash( colorWhite, 125 );
|
|
player->SetInfluenceLevel( INFLUENCE_LEVEL3 );
|
|
player->SetInfluenceView( spawnArgs.GetString( "mtr_teleportFx" ), NULL, 0.0f, NULL );
|
|
gameSoundWorld->FadeSoundClasses( 0, -20.0f, teleportDelay );
|
|
player->StartSound( "snd_teleport_start", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
teleportStage++;
|
|
PostEventSec( &EV_TeleportStage, teleportDelay, player );
|
|
break;
|
|
case 1:
|
|
gameSoundWorld->FadeSoundClasses( 0, 0.0f, 0.25f );
|
|
teleportStage++;
|
|
PostEventSec( &EV_TeleportStage, 0.25f, player );
|
|
break;
|
|
case 2:
|
|
player->SetInfluenceView( NULL, NULL, 0.0f, NULL );
|
|
TeleportPlayer( player );
|
|
player->StopSound( SND_CHANNEL_BODY2, false );
|
|
player->SetInfluenceLevel( INFLUENCE_NONE );
|
|
teleportStage = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerStart::TeleportPlayer
|
|
================
|
|
*/
|
|
void idPlayerStart::TeleportPlayer( idPlayer *player ) {
|
|
float pushVel = spawnArgs.GetFloat( "push", "300" );
|
|
float f = spawnArgs.GetFloat( "visualEffect", "0" );
|
|
const char *viewName = spawnArgs.GetString( "visualView", "" );
|
|
idEntity *ent = viewName ? gameLocal.FindEntity( viewName ) : NULL;
|
|
|
|
if ( f && ent ) {
|
|
// place in private camera view for some time
|
|
// the entity needs to teleport to where the camera view is to have the PVS right
|
|
player->Teleport( ent->GetPhysics()->GetOrigin(), ang_zero, this );
|
|
player->StartSound( "snd_teleport_enter", SND_CHANNEL_ANY, 0, false, NULL );
|
|
player->SetPrivateCameraView( static_cast<idCamera*>(ent) );
|
|
// the player entity knows where to spawn from the previous Teleport call
|
|
if ( !gameLocal.isClient ) {
|
|
player->PostEventSec( &EV_Player_ExitTeleporter, f );
|
|
}
|
|
} else {
|
|
// direct to exit, Teleport will take care of the killbox
|
|
player->Teleport( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis().ToAngles(), NULL );
|
|
|
|
// multiplayer hijacked this entity, so only push the player in multiplayer
|
|
if ( gameLocal.isMultiplayer ) {
|
|
player->GetPhysics()->SetLinearVelocity( GetPhysics()->GetAxis()[0] * pushVel );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerStart::Event_TeleportPlayer
|
|
================
|
|
*/
|
|
void idPlayerStart::Event_TeleportPlayer( idEntity *activator ) {
|
|
idPlayer *player;
|
|
|
|
if ( activator->IsType( idPlayer::Type ) ) {
|
|
player = static_cast<idPlayer*>( activator );
|
|
} else {
|
|
player = gameLocal.GetLocalPlayer();
|
|
}
|
|
if ( player ) {
|
|
if ( spawnArgs.GetBool( "visualFx" ) ) {
|
|
|
|
teleportStage = 0;
|
|
Event_TeleportStage( player );
|
|
|
|
} else {
|
|
|
|
if ( gameLocal.isServer ) {
|
|
idBitMsg msg;
|
|
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
|
|
msg.Init( msgBuf, sizeof( msgBuf ) );
|
|
msg.BeginWriting();
|
|
msg.WriteBits( player->entityNumber, GENTITYNUM_BITS );
|
|
ServerSendEvent( EVENT_TELEPORTPLAYER, &msg, false, -1 );
|
|
}
|
|
|
|
TeleportPlayer( player );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idActivator
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idActivator )
|
|
EVENT( EV_Activate, idActivator::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idActivator::Save
|
|
================
|
|
*/
|
|
void idActivator::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteBool( stay_on );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idActivator::Restore
|
|
================
|
|
*/
|
|
void idActivator::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadBool( stay_on );
|
|
|
|
if ( stay_on ) {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idActivator::Spawn
|
|
================
|
|
*/
|
|
void idActivator::Spawn( void ) {
|
|
bool start_off;
|
|
|
|
spawnArgs.GetBool( "stay_on", "0", stay_on );
|
|
spawnArgs.GetBool( "start_off", "0", start_off );
|
|
|
|
GetPhysics()->SetClipBox( idBounds( vec3_origin ).Expand( 4 ), 1.0f );
|
|
GetPhysics()->SetContents( 0 );
|
|
|
|
if ( !start_off ) {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idActivator::Think
|
|
================
|
|
*/
|
|
void idActivator::Think( void ) {
|
|
RunPhysics();
|
|
if ( thinkFlags & TH_THINK ) {
|
|
if ( TouchTriggers() ) {
|
|
if ( !stay_on ) {
|
|
BecomeInactive( TH_THINK );
|
|
}
|
|
}
|
|
}
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idActivator::Activate
|
|
================
|
|
*/
|
|
void idActivator::Event_Activate( idEntity *activator ) {
|
|
if ( thinkFlags & TH_THINK ) {
|
|
BecomeInactive( TH_THINK );
|
|
} else {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idPathCorner
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idPathCorner )
|
|
EVENT( AI_RandomPath, idPathCorner::Event_RandomPath )
|
|
END_CLASS
|
|
|
|
/*
|
|
=====================
|
|
idPathCorner::Spawn
|
|
=====================
|
|
*/
|
|
void idPathCorner::Spawn( void ) {
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idPathCorner::DrawDebugInfo
|
|
=====================
|
|
*/
|
|
void idPathCorner::DrawDebugInfo( void ) {
|
|
idEntity *ent;
|
|
idBounds bnds( idVec3( -4.0, -4.0f, -8.0f ), idVec3( 4.0, 4.0f, 64.0f ) );
|
|
|
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
|
if ( !ent->IsType( idPathCorner::Type ) ) {
|
|
continue;
|
|
}
|
|
|
|
idVec3 org = ent->GetPhysics()->GetOrigin();
|
|
gameRenderWorld->DebugBounds( colorRed, bnds, org, 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPathCorner::RandomPath
|
|
============
|
|
*/
|
|
idPathCorner *idPathCorner::RandomPath( const idEntity *source, const idEntity *ignore ) {
|
|
int i;
|
|
int num;
|
|
int which;
|
|
idEntity *ent;
|
|
idPathCorner *path[ MAX_GENTITIES ];
|
|
|
|
num = 0;
|
|
for( i = 0; i < source->targets.Num(); i++ ) {
|
|
ent = source->targets[ i ].GetEntity();
|
|
if ( ent && ( ent != ignore ) && ent->IsType( idPathCorner::Type ) ) {
|
|
path[ num++ ] = static_cast<idPathCorner *>( ent );
|
|
if ( num >= MAX_GENTITIES ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !num ) {
|
|
return NULL;
|
|
}
|
|
|
|
which = gameLocal.random.RandomInt( num );
|
|
return path[ which ];
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idPathCorner::Event_RandomPath
|
|
=====================
|
|
*/
|
|
void idPathCorner::Event_RandomPath( void ) {
|
|
idPathCorner *path;
|
|
|
|
path = RandomPath( this, NULL );
|
|
idThread::ReturnEntity( path );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idDamagable
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_RestoreDamagable( "<RestoreDamagable>" );
|
|
|
|
CLASS_DECLARATION( idEntity, idDamagable )
|
|
EVENT( EV_Activate, idDamagable::Event_BecomeBroken )
|
|
EVENT( EV_RestoreDamagable, idDamagable::Event_RestoreDamagable )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idDamagable::idDamagable
|
|
================
|
|
*/
|
|
idDamagable::idDamagable( void ) {
|
|
count = 0;
|
|
nextTriggerTime = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::Save
|
|
================
|
|
*/
|
|
void idDamagable::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( count );
|
|
savefile->WriteInt( nextTriggerTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::Restore
|
|
================
|
|
*/
|
|
void idDamagable::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( count );
|
|
savefile->ReadInt( nextTriggerTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::Spawn
|
|
================
|
|
*/
|
|
void idDamagable::Spawn( void ) {
|
|
idStr broken;
|
|
|
|
health = spawnArgs.GetInt( "health", "5" );
|
|
spawnArgs.GetInt( "count", "1", count );
|
|
nextTriggerTime = 0;
|
|
|
|
// make sure the model gets cached
|
|
spawnArgs.GetString( "broken", "", broken );
|
|
if ( broken.Length() && !renderModelManager->CheckModel( broken ) ) {
|
|
gameLocal.Error( "idDamagable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), broken.c_str() );
|
|
}
|
|
|
|
fl.takedamage = true;
|
|
GetPhysics()->SetContents( CONTENTS_SOLID );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::BecomeBroken
|
|
================
|
|
*/
|
|
void idDamagable::BecomeBroken( idEntity *activator ) {
|
|
float forceState;
|
|
int numStates;
|
|
int cycle;
|
|
float wait;
|
|
|
|
if ( gameLocal.time < nextTriggerTime ) {
|
|
return;
|
|
}
|
|
|
|
spawnArgs.GetFloat( "wait", "0.1", wait );
|
|
nextTriggerTime = gameLocal.time + SEC2MS( wait );
|
|
if ( count > 0 ) {
|
|
count--;
|
|
if ( !count ) {
|
|
fl.takedamage = false;
|
|
} else {
|
|
health = spawnArgs.GetInt( "health", "5" );
|
|
}
|
|
}
|
|
|
|
idStr broken;
|
|
|
|
spawnArgs.GetString( "broken", "", broken );
|
|
if ( broken.Length() ) {
|
|
SetModel( broken );
|
|
}
|
|
|
|
// offset the start time of the shader to sync it to the gameLocal time
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
|
|
spawnArgs.GetInt( "numstates", "1", numStates );
|
|
spawnArgs.GetInt( "cycle", "0", cycle );
|
|
spawnArgs.GetFloat( "forcestate", "0", forceState );
|
|
|
|
// set the state parm
|
|
if ( cycle ) {
|
|
renderEntity.shaderParms[ SHADERPARM_MODE ]++;
|
|
if ( renderEntity.shaderParms[ SHADERPARM_MODE ] > numStates ) {
|
|
renderEntity.shaderParms[ SHADERPARM_MODE ] = 0;
|
|
}
|
|
} else if ( forceState ) {
|
|
renderEntity.shaderParms[ SHADERPARM_MODE ] = forceState;
|
|
} else {
|
|
renderEntity.shaderParms[ SHADERPARM_MODE ] = gameLocal.random.RandomInt( numStates ) + 1;
|
|
}
|
|
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
|
|
ActivateTargets( activator );
|
|
|
|
if ( spawnArgs.GetBool( "hideWhenBroken" ) ) {
|
|
Hide();
|
|
PostEventMS( &EV_RestoreDamagable, nextTriggerTime - gameLocal.time );
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::Killed
|
|
================
|
|
*/
|
|
void idDamagable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
if ( gameLocal.time < nextTriggerTime ) {
|
|
health += damage;
|
|
return;
|
|
}
|
|
|
|
BecomeBroken( attacker );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::Event_BecomeBroken
|
|
================
|
|
*/
|
|
void idDamagable::Event_BecomeBroken( idEntity *activator ) {
|
|
BecomeBroken( activator );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDamagable::Event_RestoreDamagable
|
|
================
|
|
*/
|
|
void idDamagable::Event_RestoreDamagable( void ) {
|
|
health = spawnArgs.GetInt( "health", "5" );
|
|
Show();
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idExplodable
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idExplodable )
|
|
EVENT( EV_Activate, idExplodable::Event_Explode )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idExplodable::Spawn
|
|
================
|
|
*/
|
|
void idExplodable::Spawn( void ) {
|
|
Hide();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idExplodable::Event_Explode
|
|
================
|
|
*/
|
|
void idExplodable::Event_Explode( idEntity *activator ) {
|
|
const char *temp;
|
|
|
|
if ( spawnArgs.GetString( "def_damage", "damage_explosion", &temp ) ) {
|
|
gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), activator, activator, this, this, temp );
|
|
}
|
|
|
|
StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL );
|
|
|
|
// Show() calls UpdateVisuals, so we don't need to call it ourselves after setting the shaderParms
|
|
renderEntity.shaderParms[SHADERPARM_RED] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
|
|
renderEntity.shaderParms[SHADERPARM_DIVERSITY] = 0.0f;
|
|
Show();
|
|
|
|
PostEventMS( &EV_Remove, 2000 );
|
|
|
|
ActivateTargets( activator );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idSpring
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idSpring )
|
|
EVENT( EV_PostSpawn, idSpring::Event_LinkSpring )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idSpring::Think
|
|
================
|
|
*/
|
|
void idSpring::Think( void ) {
|
|
idVec3 start, end, origin;
|
|
idMat3 axis;
|
|
|
|
// run physics
|
|
RunPhysics();
|
|
|
|
if ( thinkFlags & TH_THINK ) {
|
|
// evaluate force
|
|
spring.Evaluate( gameLocal.time );
|
|
|
|
start = p1;
|
|
if ( ent1->GetPhysics() ) {
|
|
axis = ent1->GetPhysics()->GetAxis();
|
|
origin = ent1->GetPhysics()->GetOrigin();
|
|
start = origin + start * axis;
|
|
}
|
|
|
|
end = p2;
|
|
if ( ent2->GetPhysics() ) {
|
|
axis = ent2->GetPhysics()->GetAxis();
|
|
origin = ent2->GetPhysics()->GetOrigin();
|
|
end = origin + p2 * axis;
|
|
}
|
|
|
|
gameRenderWorld->DebugLine( idVec4(1, 1, 0, 1), start, end, 0, true );
|
|
}
|
|
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idSpring::Event_LinkSpring
|
|
================
|
|
*/
|
|
void idSpring::Event_LinkSpring( void ) {
|
|
idStr name1, name2;
|
|
|
|
spawnArgs.GetString( "ent1", "", name1 );
|
|
spawnArgs.GetString( "ent2", "", name2 );
|
|
|
|
if ( name1.Length() ) {
|
|
ent1 = gameLocal.FindEntity( name1 );
|
|
if ( !ent1 ) {
|
|
gameLocal.Error( "idSpring '%s' at (%s): cannot find first entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name1.c_str() );
|
|
}
|
|
}
|
|
else {
|
|
ent1 = gameLocal.entities[ENTITYNUM_WORLD];
|
|
}
|
|
|
|
if ( name2.Length() ) {
|
|
ent2 = gameLocal.FindEntity( name2 );
|
|
if ( !ent2 ) {
|
|
gameLocal.Error( "idSpring '%s' at (%s): cannot find second entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name2.c_str() );
|
|
}
|
|
}
|
|
else {
|
|
ent2 = gameLocal.entities[ENTITYNUM_WORLD];
|
|
}
|
|
spring.SetPosition( ent1->GetPhysics(), id1, p1, ent2->GetPhysics(), id2, p2 );
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idSpring::Spawn
|
|
================
|
|
*/
|
|
void idSpring::Spawn( void ) {
|
|
float Kstretch, damping, restLength;
|
|
|
|
spawnArgs.GetInt( "id1", "0", id1 );
|
|
spawnArgs.GetInt( "id2", "0", id2 );
|
|
spawnArgs.GetVector( "point1", "0 0 0", p1 );
|
|
spawnArgs.GetVector( "point2", "0 0 0", p2 );
|
|
spawnArgs.GetFloat( "constant", "100.0f", Kstretch );
|
|
spawnArgs.GetFloat( "damping", "10.0f", damping );
|
|
spawnArgs.GetFloat( "restlength", "0.0f", restLength );
|
|
|
|
spring.InitSpring( Kstretch, 0.0f, damping, restLength );
|
|
|
|
ent1 = ent2 = NULL;
|
|
|
|
PostEventMS( &EV_PostSpawn, 0 );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idForceField
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_Toggle( "Toggle", NULL );
|
|
|
|
CLASS_DECLARATION( idEntity, idForceField )
|
|
EVENT( EV_Activate, idForceField::Event_Activate )
|
|
EVENT( EV_Toggle, idForceField::Event_Toggle )
|
|
EVENT( EV_FindTargets, idForceField::Event_FindTargets )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idForceField::Toggle
|
|
================
|
|
*/
|
|
void idForceField::Toggle( void ) {
|
|
if ( thinkFlags & TH_THINK ) {
|
|
BecomeInactive( TH_THINK );
|
|
} else {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idForceField::Think
|
|
================
|
|
*/
|
|
void idForceField::Think( void ) {
|
|
if ( thinkFlags & TH_THINK ) {
|
|
// evaluate force
|
|
forceField.Evaluate( gameLocal.time );
|
|
}
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idForceField::Save
|
|
================
|
|
*/
|
|
void idForceField::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteStaticObject( forceField );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idForceField::Restore
|
|
================
|
|
*/
|
|
void idForceField::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadStaticObject( forceField );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idForceField::Spawn
|
|
================
|
|
*/
|
|
void idForceField::Spawn( void ) {
|
|
idVec3 uniform;
|
|
float explosion, implosion, randomTorque;
|
|
|
|
if ( spawnArgs.GetVector( "uniform", "0 0 0", uniform ) ) {
|
|
forceField.Uniform( uniform );
|
|
} else if ( spawnArgs.GetFloat( "explosion", "0", explosion ) ) {
|
|
forceField.Explosion( explosion );
|
|
} else if ( spawnArgs.GetFloat( "implosion", "0", implosion ) ) {
|
|
forceField.Implosion( implosion );
|
|
}
|
|
|
|
if ( spawnArgs.GetFloat( "randomTorque", "0", randomTorque ) ) {
|
|
forceField.RandomTorque( randomTorque );
|
|
}
|
|
|
|
if ( spawnArgs.GetBool( "applyForce", "0" ) ) {
|
|
forceField.SetApplyType( FORCEFIELD_APPLY_FORCE );
|
|
} else if ( spawnArgs.GetBool( "applyImpulse", "0" ) ) {
|
|
forceField.SetApplyType( FORCEFIELD_APPLY_IMPULSE );
|
|
} else {
|
|
forceField.SetApplyType( FORCEFIELD_APPLY_VELOCITY );
|
|
}
|
|
|
|
forceField.SetPlayerOnly( spawnArgs.GetBool( "playerOnly", "0" ) );
|
|
forceField.SetMonsterOnly( spawnArgs.GetBool( "monsterOnly", "0" ) );
|
|
|
|
// set the collision model on the force field
|
|
forceField.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ) );
|
|
|
|
// remove the collision model from the physics object
|
|
GetPhysics()->SetClipModel( NULL, 1.0f );
|
|
|
|
if ( spawnArgs.GetBool( "start_on" ) ) {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idForceField::Event_Toggle
|
|
================
|
|
*/
|
|
void idForceField::Event_Toggle( void ) {
|
|
Toggle();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idForceField::Event_Activate
|
|
================
|
|
*/
|
|
void idForceField::Event_Activate( idEntity *activator ) {
|
|
float wait;
|
|
|
|
Toggle();
|
|
if ( spawnArgs.GetFloat( "wait", "0.01", wait ) ) {
|
|
PostEventSec( &EV_Toggle, wait );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idForceField::Event_FindTargets
|
|
================
|
|
*/
|
|
void idForceField::Event_FindTargets( void ) {
|
|
FindTargets();
|
|
RemoveNullTargets();
|
|
if ( targets.Num() ) {
|
|
forceField.Uniform( targets[0].GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idAnimated
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_Animated_Start( "<start>" );
|
|
const idEventDef EV_LaunchMissiles( "launchMissiles", "ssssdf" );
|
|
const idEventDef EV_LaunchMissilesUpdate( "<launchMissiles>", "dddd" );
|
|
const idEventDef EV_AnimDone( "<AnimDone>", "d" );
|
|
const idEventDef EV_StartRagdoll( "startRagdoll" );
|
|
|
|
CLASS_DECLARATION( idAFEntity_Gibbable, idAnimated )
|
|
EVENT( EV_Activate, idAnimated::Event_Activate )
|
|
EVENT( EV_Animated_Start, idAnimated::Event_Start )
|
|
EVENT( EV_StartRagdoll, idAnimated::Event_StartRagdoll )
|
|
EVENT( EV_AnimDone, idAnimated::Event_AnimDone )
|
|
EVENT( EV_Footstep, idAnimated::Event_Footstep )
|
|
EVENT( EV_FootstepLeft, idAnimated::Event_Footstep )
|
|
EVENT( EV_FootstepRight, idAnimated::Event_Footstep )
|
|
EVENT( EV_LaunchMissiles, idAnimated::Event_LaunchMissiles )
|
|
EVENT( EV_LaunchMissilesUpdate, idAnimated::Event_LaunchMissilesUpdate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idAnimated::idAnimated
|
|
================
|
|
*/
|
|
idAnimated::idAnimated() {
|
|
anim = 0;
|
|
blendFrames = 0;
|
|
soundJoint = INVALID_JOINT;
|
|
activated = false;
|
|
combatModel = NULL;
|
|
activator = NULL;
|
|
current_anim_index = 0;
|
|
num_anims = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::idAnimated
|
|
================
|
|
*/
|
|
idAnimated::~idAnimated() {
|
|
delete combatModel;
|
|
combatModel = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Save
|
|
================
|
|
*/
|
|
void idAnimated::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( current_anim_index );
|
|
savefile->WriteInt( num_anims );
|
|
savefile->WriteInt( anim );
|
|
savefile->WriteInt( blendFrames );
|
|
savefile->WriteJoint( soundJoint );
|
|
activator.Save( savefile );
|
|
savefile->WriteBool( activated );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Restore
|
|
================
|
|
*/
|
|
void idAnimated::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( current_anim_index );
|
|
savefile->ReadInt( num_anims );
|
|
savefile->ReadInt( anim );
|
|
savefile->ReadInt( blendFrames );
|
|
savefile->ReadJoint( soundJoint );
|
|
activator.Restore( savefile );
|
|
savefile->ReadBool( activated );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Spawn
|
|
================
|
|
*/
|
|
void idAnimated::Spawn( void ) {
|
|
idStr animname;
|
|
int anim2;
|
|
float wait;
|
|
const char *joint;
|
|
|
|
joint = spawnArgs.GetString( "sound_bone", "origin" );
|
|
soundJoint = animator.GetJointHandle( joint );
|
|
if ( soundJoint == INVALID_JOINT ) {
|
|
gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), joint );
|
|
}
|
|
|
|
LoadAF();
|
|
|
|
// allow bullets to collide with a combat model
|
|
if ( spawnArgs.GetBool( "combatModel", "0" ) ) {
|
|
combatModel = new idClipModel( modelDefHandle );
|
|
}
|
|
|
|
// allow the entity to take damage
|
|
if ( spawnArgs.GetBool( "takeDamage", "0" ) ) {
|
|
fl.takedamage = true;
|
|
}
|
|
|
|
blendFrames = 0;
|
|
|
|
current_anim_index = 0;
|
|
spawnArgs.GetInt( "num_anims", "0", num_anims );
|
|
|
|
blendFrames = spawnArgs.GetInt( "blend_in" );
|
|
|
|
animname = spawnArgs.GetString( num_anims ? "anim1" : "anim" );
|
|
if ( !animname.Length() ) {
|
|
anim = 0;
|
|
} else {
|
|
anim = animator.GetAnim( animname );
|
|
if ( !anim ) {
|
|
gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
|
|
}
|
|
}
|
|
|
|
if ( spawnArgs.GetBool( "hide" ) ) {
|
|
Hide();
|
|
|
|
if ( !num_anims ) {
|
|
blendFrames = 0;
|
|
}
|
|
} else if ( spawnArgs.GetString( "start_anim", "", animname ) ) {
|
|
anim2 = animator.GetAnim( animname );
|
|
if ( !anim2 ) {
|
|
gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
|
|
}
|
|
animator.CycleAnim( ANIMCHANNEL_ALL, anim2, gameLocal.time, 0 );
|
|
} else if ( anim ) {
|
|
// init joints to the first frame of the animation
|
|
animator.SetFrame( ANIMCHANNEL_ALL, anim, 1, gameLocal.time, 0 );
|
|
|
|
if ( !num_anims ) {
|
|
blendFrames = 0;
|
|
}
|
|
}
|
|
|
|
spawnArgs.GetFloat( "wait", "-1", wait );
|
|
|
|
if ( wait >= 0 ) {
|
|
PostEventSec( &EV_Activate, wait, this );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::LoadAF
|
|
===============
|
|
*/
|
|
bool idAnimated::LoadAF( void ) {
|
|
idStr fileName;
|
|
|
|
if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) ) {
|
|
return false;
|
|
}
|
|
af.SetAnimator( GetAnimator() );
|
|
return af.Load( this, fileName );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::GetPhysicsToSoundTransform
|
|
===============
|
|
*/
|
|
bool idAnimated::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
|
|
animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
|
|
axis = renderEntity.axis;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAnimated::StartRagdoll
|
|
================
|
|
*/
|
|
bool idAnimated::StartRagdoll( void ) {
|
|
// if no AF loaded
|
|
if ( !af.IsLoaded() ) {
|
|
return false;
|
|
}
|
|
|
|
// if the AF is already active
|
|
if ( af.IsActive() ) {
|
|
return true;
|
|
}
|
|
|
|
// disable any collision model used
|
|
GetPhysics()->DisableClip();
|
|
|
|
// start using the AF
|
|
af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idAnimated::PlayNextAnim
|
|
=====================
|
|
*/
|
|
void idAnimated::PlayNextAnim( void ) {
|
|
const char *animname;
|
|
int len;
|
|
int cycle;
|
|
|
|
if ( current_anim_index >= num_anims ) {
|
|
Hide();
|
|
if ( spawnArgs.GetBool( "remove" ) ) {
|
|
PostEventMS( &EV_Remove, 0 );
|
|
} else {
|
|
current_anim_index = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
Show();
|
|
current_anim_index++;
|
|
|
|
spawnArgs.GetString( va( "anim%d", current_anim_index ), NULL, &animname );
|
|
if ( !animname ) {
|
|
anim = 0;
|
|
animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, FRAME2MS( blendFrames ) );
|
|
return;
|
|
}
|
|
|
|
anim = animator.GetAnim( animname );
|
|
if ( !anim ) {
|
|
gameLocal.Warning( "missing anim '%s' on %s", animname, name.c_str() );
|
|
return;
|
|
}
|
|
|
|
if ( g_debugCinematic.GetBool() ) {
|
|
gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animname );
|
|
}
|
|
|
|
spawnArgs.GetInt( "cycle", "1", cycle );
|
|
if ( ( current_anim_index == num_anims ) && spawnArgs.GetBool( "loop_last_anim" ) ) {
|
|
cycle = -1;
|
|
}
|
|
|
|
animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
|
|
animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
|
|
|
|
len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
|
|
if ( len >= 0 ) {
|
|
PostEventMS( &EV_AnimDone, len, current_anim_index );
|
|
}
|
|
|
|
// offset the start time of the shader to sync it to the game time
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
|
|
animator.ForceUpdate();
|
|
UpdateAnimation();
|
|
UpdateVisuals();
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Event_StartRagdoll
|
|
================
|
|
*/
|
|
void idAnimated::Event_StartRagdoll( void ) {
|
|
StartRagdoll();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Event_AnimDone
|
|
================
|
|
*/
|
|
void idAnimated::Event_AnimDone( int animindex ) {
|
|
if ( g_debugCinematic.GetBool() ) {
|
|
const idAnim *animPtr = animator.GetAnim( anim );
|
|
gameLocal.Printf( "%d: '%s' end anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
|
|
}
|
|
|
|
if ( ( animindex >= num_anims ) && spawnArgs.GetBool( "remove" ) ) {
|
|
Hide();
|
|
PostEventMS( &EV_Remove, 0 );
|
|
} else if ( spawnArgs.GetBool( "auto_advance" ) ) {
|
|
PlayNextAnim();
|
|
} else {
|
|
activated = false;
|
|
}
|
|
|
|
ActivateTargets( activator.GetEntity() );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Event_Activate
|
|
================
|
|
*/
|
|
void idAnimated::Event_Activate( idEntity *_activator ) {
|
|
if ( num_anims ) {
|
|
PlayNextAnim();
|
|
activator = _activator;
|
|
return;
|
|
}
|
|
|
|
if ( activated ) {
|
|
// already activated
|
|
return;
|
|
}
|
|
|
|
activated = true;
|
|
activator = _activator;
|
|
ProcessEvent( &EV_Animated_Start );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Event_Start
|
|
================
|
|
*/
|
|
void idAnimated::Event_Start( void ) {
|
|
int cycle;
|
|
int len;
|
|
|
|
Show();
|
|
|
|
if ( num_anims ) {
|
|
PlayNextAnim();
|
|
return;
|
|
}
|
|
|
|
if ( anim ) {
|
|
if ( g_debugCinematic.GetBool() ) {
|
|
const idAnim *animPtr = animator.GetAnim( anim );
|
|
gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
|
|
}
|
|
spawnArgs.GetInt( "cycle", "1", cycle );
|
|
animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
|
|
animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
|
|
|
|
len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
|
|
if ( len >= 0 ) {
|
|
PostEventMS( &EV_AnimDone, len, 1 );
|
|
}
|
|
}
|
|
|
|
// offset the start time of the shader to sync it to the game time
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
|
|
animator.ForceUpdate();
|
|
UpdateAnimation();
|
|
UpdateVisuals();
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idAnimated::Event_Footstep
|
|
===============
|
|
*/
|
|
void idAnimated::Event_Footstep( void ) {
|
|
StartSound( "snd_footstep", SND_CHANNEL_BODY, 0, false, NULL );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idAnimated::Event_LaunchMissilesUpdate
|
|
=====================
|
|
*/
|
|
void idAnimated::Event_LaunchMissilesUpdate( int launchjoint, int targetjoint, int numshots, int framedelay ) {
|
|
idVec3 launchPos;
|
|
idVec3 targetPos;
|
|
idMat3 axis;
|
|
idVec3 dir;
|
|
idEntity * ent;
|
|
idProjectile * projectile;
|
|
const idDict * projectileDef;
|
|
const char * projectilename;
|
|
|
|
projectilename = spawnArgs.GetString( "projectilename" );
|
|
projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
|
|
if ( !projectileDef ) {
|
|
gameLocal.Warning( "idAnimated '%s' at (%s): 'launchMissiles' called with unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
|
|
return;
|
|
}
|
|
|
|
StartSound( "snd_missile", SND_CHANNEL_WEAPON, 0, false, NULL );
|
|
|
|
animator.GetJointTransform( ( jointHandle_t )launchjoint, gameLocal.time, launchPos, axis );
|
|
launchPos = renderEntity.origin + launchPos * renderEntity.axis;
|
|
|
|
animator.GetJointTransform( ( jointHandle_t )targetjoint, gameLocal.time, targetPos, axis );
|
|
targetPos = renderEntity.origin + targetPos * renderEntity.axis;
|
|
|
|
dir = targetPos - launchPos;
|
|
dir.Normalize();
|
|
|
|
gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
|
|
if ( !ent || !ent->IsType( idProjectile::Type ) ) {
|
|
gameLocal.Error( "idAnimated '%s' at (%s): in 'launchMissiles' call '%s' is not an idProjectile", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
|
|
}
|
|
projectile = ( idProjectile * )ent;
|
|
projectile->Create( this, launchPos, dir );
|
|
projectile->Launch( launchPos, dir, vec3_origin );
|
|
|
|
if ( numshots > 0 ) {
|
|
PostEventMS( &EV_LaunchMissilesUpdate, FRAME2MS( framedelay ), launchjoint, targetjoint, numshots - 1, framedelay );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idAnimated::Event_LaunchMissiles
|
|
=====================
|
|
*/
|
|
void idAnimated::Event_LaunchMissiles( const char *projectilename, const char *sound, const char *launchjoint, const char *targetjoint, int numshots, int framedelay ) {
|
|
const idDict * projectileDef;
|
|
jointHandle_t launch;
|
|
jointHandle_t target;
|
|
|
|
projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
|
|
if ( !projectileDef ) {
|
|
gameLocal.Warning( "idAnimated '%s' at (%s): unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
|
|
return;
|
|
}
|
|
|
|
launch = animator.GetJointHandle( launchjoint );
|
|
if ( launch == INVALID_JOINT ) {
|
|
gameLocal.Warning( "idAnimated '%s' at (%s): unknown launch joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), launchjoint );
|
|
gameLocal.Error( "Unknown joint '%s'", launchjoint );
|
|
}
|
|
|
|
target = animator.GetJointHandle( targetjoint );
|
|
if ( target == INVALID_JOINT ) {
|
|
gameLocal.Warning( "idAnimated '%s' at (%s): unknown target joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), targetjoint );
|
|
}
|
|
|
|
spawnArgs.Set( "projectilename", projectilename );
|
|
spawnArgs.Set( "missilesound", sound );
|
|
|
|
CancelEvents( &EV_LaunchMissilesUpdate );
|
|
ProcessEvent( &EV_LaunchMissilesUpdate, launch, target, numshots - 1, framedelay );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idStaticEntity
|
|
|
|
Some static entities may be optimized into inline geometry by dmap
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idStaticEntity )
|
|
EVENT( EV_Activate, idStaticEntity::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idStaticEntity::idStaticEntity
|
|
===============
|
|
*/
|
|
idStaticEntity::idStaticEntity( void ) {
|
|
spawnTime = 0;
|
|
active = false;
|
|
fadeFrom.Set( 1, 1, 1, 1 );
|
|
fadeTo.Set( 1, 1, 1, 1 );
|
|
fadeStart = 0;
|
|
fadeEnd = 0;
|
|
runGui = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idStaticEntity::Save
|
|
===============
|
|
*/
|
|
void idStaticEntity::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( spawnTime );
|
|
savefile->WriteBool( active );
|
|
savefile->WriteVec4( fadeFrom );
|
|
savefile->WriteVec4( fadeTo );
|
|
savefile->WriteInt( fadeStart );
|
|
savefile->WriteInt( fadeEnd );
|
|
savefile->WriteBool( runGui );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idStaticEntity::Restore
|
|
===============
|
|
*/
|
|
void idStaticEntity::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( spawnTime );
|
|
savefile->ReadBool( active );
|
|
savefile->ReadVec4( fadeFrom );
|
|
savefile->ReadVec4( fadeTo );
|
|
savefile->ReadInt( fadeStart );
|
|
savefile->ReadInt( fadeEnd );
|
|
savefile->ReadBool( runGui );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idStaticEntity::Spawn
|
|
===============
|
|
*/
|
|
void idStaticEntity::Spawn( void ) {
|
|
bool solid;
|
|
bool hidden;
|
|
|
|
// an inline static model will not do anything at all
|
|
if ( spawnArgs.GetBool( "inline" ) || gameLocal.world->spawnArgs.GetBool( "inlineAllStatics" ) ) {
|
|
Hide();
|
|
return;
|
|
}
|
|
|
|
solid = spawnArgs.GetBool( "solid" );
|
|
hidden = spawnArgs.GetBool( "hide" );
|
|
|
|
if ( solid && !hidden ) {
|
|
GetPhysics()->SetContents( CONTENTS_SOLID );
|
|
} else {
|
|
GetPhysics()->SetContents( 0 );
|
|
}
|
|
|
|
spawnTime = gameLocal.time;
|
|
active = false;
|
|
|
|
idStr model = spawnArgs.GetString( "model" );
|
|
if ( model.Find( ".prt" ) >= 0 ) {
|
|
// we want the parametric particles out of sync with each other
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = gameLocal.random.RandomInt( 32767 );
|
|
}
|
|
|
|
fadeFrom.Set( 1, 1, 1, 1 );
|
|
fadeTo.Set( 1, 1, 1, 1 );
|
|
fadeStart = 0;
|
|
fadeEnd = 0;
|
|
|
|
// NOTE: this should be used very rarely because it is expensive
|
|
runGui = spawnArgs.GetBool( "runGui" );
|
|
if ( runGui ) {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::ShowEditingDialog
|
|
================
|
|
*/
|
|
void idStaticEntity::ShowEditingDialog( void ) {
|
|
common->InitTool( EDITOR_PARTICLE, &spawnArgs );
|
|
}
|
|
/*
|
|
================
|
|
idStaticEntity::Think
|
|
================
|
|
*/
|
|
void idStaticEntity::Think( void ) {
|
|
idEntity::Think();
|
|
if ( thinkFlags & TH_THINK ) {
|
|
if ( runGui && renderEntity.gui[0] ) {
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player ) {
|
|
if ( !player->objectiveSystemOpen ) {
|
|
renderEntity.gui[0]->StateChanged( gameLocal.time, true );
|
|
if ( renderEntity.gui[1] ) {
|
|
renderEntity.gui[1]->StateChanged( gameLocal.time, true );
|
|
}
|
|
if ( renderEntity.gui[2] ) {
|
|
renderEntity.gui[2]->StateChanged( gameLocal.time, true );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( fadeEnd > 0 ) {
|
|
idVec4 color;
|
|
if ( gameLocal.time < fadeEnd ) {
|
|
color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
|
|
} else {
|
|
color = fadeTo;
|
|
fadeEnd = 0;
|
|
BecomeInactive( TH_THINK );
|
|
}
|
|
SetColor( color );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::Fade
|
|
================
|
|
*/
|
|
void idStaticEntity::Fade( const idVec4 &to, float fadeTime ) {
|
|
GetColor( fadeFrom );
|
|
fadeTo = to;
|
|
fadeStart = gameLocal.time;
|
|
fadeEnd = gameLocal.time + SEC2MS( fadeTime );
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::Hide
|
|
================
|
|
*/
|
|
void idStaticEntity::Hide( void ) {
|
|
idEntity::Hide();
|
|
GetPhysics()->SetContents( 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::Show
|
|
================
|
|
*/
|
|
void idStaticEntity::Show( void ) {
|
|
idEntity::Show();
|
|
if ( spawnArgs.GetBool( "solid" ) ) {
|
|
GetPhysics()->SetContents( CONTENTS_SOLID );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::Event_Activate
|
|
================
|
|
*/
|
|
void idStaticEntity::Event_Activate( idEntity *activator ) {
|
|
idStr activateGui;
|
|
|
|
spawnTime = gameLocal.time;
|
|
active = !active;
|
|
|
|
const idKeyValue *kv = spawnArgs.FindKey( "hide" );
|
|
if ( kv ) {
|
|
if ( IsHidden() ) {
|
|
Show();
|
|
} else {
|
|
Hide();
|
|
}
|
|
}
|
|
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( spawnTime );
|
|
renderEntity.shaderParms[5] = active;
|
|
// this change should be a good thing, it will automatically turn on
|
|
// lights etc.. when triggered so that does not have to be specifically done
|
|
// with trigger parms.. it MIGHT break things so need to keep an eye on it
|
|
renderEntity.shaderParms[ SHADERPARM_MODE ] = ( renderEntity.shaderParms[ SHADERPARM_MODE ] ) ? 0.0f : 1.0f;
|
|
BecomeActive( TH_UPDATEVISUALS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idStaticEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
GetPhysics()->WriteToSnapshot( msg );
|
|
WriteBindToSnapshot( msg );
|
|
WriteColorToSnapshot( msg );
|
|
WriteGUIToSnapshot( msg );
|
|
msg.WriteBits( IsHidden()?1:0, 1 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idStaticEntity::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idStaticEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
bool hidden;
|
|
|
|
GetPhysics()->ReadFromSnapshot( msg );
|
|
ReadBindFromSnapshot( msg );
|
|
ReadColorFromSnapshot( msg );
|
|
ReadGUIFromSnapshot( msg );
|
|
hidden = msg.ReadBits( 1 ) == 1;
|
|
if ( hidden != IsHidden() ) {
|
|
if ( hidden ) {
|
|
Hide();
|
|
} else {
|
|
Show();
|
|
}
|
|
}
|
|
if ( msg.HasChanged() ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncEmitter
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
CLASS_DECLARATION( idStaticEntity, idFuncEmitter )
|
|
EVENT( EV_Activate, idFuncEmitter::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncEmitter::idFuncEmitter
|
|
===============
|
|
*/
|
|
idFuncEmitter::idFuncEmitter( void ) {
|
|
hidden = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncEmitter::Spawn
|
|
===============
|
|
*/
|
|
void idFuncEmitter::Spawn( void ) {
|
|
if ( spawnArgs.GetBool( "start_off" ) ) {
|
|
hidden = true;
|
|
renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( 1 );
|
|
UpdateVisuals();
|
|
} else {
|
|
hidden = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncEmitter::Save
|
|
===============
|
|
*/
|
|
void idFuncEmitter::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteBool( hidden );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncEmitter::Restore
|
|
===============
|
|
*/
|
|
void idFuncEmitter::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadBool( hidden );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncEmitter::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncEmitter::Event_Activate( idEntity *activator ) {
|
|
if ( hidden || spawnArgs.GetBool( "cycleTrigger" ) ) {
|
|
renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = 0;
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
|
|
hidden = false;
|
|
} else {
|
|
renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( gameLocal.time );
|
|
hidden = true;
|
|
}
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncEmitter::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idFuncEmitter::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
msg.WriteBits( hidden ? 1 : 0, 1 );
|
|
msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] );
|
|
msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncEmitter::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idFuncEmitter::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
hidden = msg.ReadBits( 1 ) != 0;
|
|
renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat();
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat();
|
|
if ( msg.HasChanged() ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncSplat
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
const idEventDef EV_Splat( "<Splat>" );
|
|
CLASS_DECLARATION( idFuncEmitter, idFuncSplat )
|
|
EVENT( EV_Activate, idFuncSplat::Event_Activate )
|
|
EVENT( EV_Splat, idFuncSplat::Event_Splat )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncSplat::idFuncSplat
|
|
===============
|
|
*/
|
|
idFuncSplat::idFuncSplat( void ) {
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncSplat::Spawn
|
|
===============
|
|
*/
|
|
void idFuncSplat::Spawn( void ) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncSplat::Event_Splat
|
|
================
|
|
*/
|
|
void idFuncSplat::Event_Splat( void ) {
|
|
const char *splat = NULL;
|
|
int count = spawnArgs.GetInt( "splatCount", "1" );
|
|
for ( int i = 0; i < count; i++ ) {
|
|
splat = spawnArgs.RandomPrefix( "mtr_splat", gameLocal.random );
|
|
if ( splat && *splat ) {
|
|
float size = spawnArgs.GetFloat( "splatSize", "128" );
|
|
float dist = spawnArgs.GetFloat( "splatDistance", "128" );
|
|
float angle = spawnArgs.GetFloat( "splatAngle", "0" );
|
|
gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis()[2], dist, true, size, splat, angle );
|
|
}
|
|
}
|
|
StartSound( "snd_splat", SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncSplat::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncSplat::Event_Activate( idEntity *activator ) {
|
|
idFuncEmitter::Event_Activate( activator );
|
|
PostEventSec( &EV_Splat, spawnArgs.GetFloat( "splatDelay", "0.25" ) );
|
|
StartSound( "snd_spurt", SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncSmoke
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idFuncSmoke )
|
|
EVENT( EV_Activate, idFuncSmoke::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncSmoke::idFuncSmoke
|
|
===============
|
|
*/
|
|
idFuncSmoke::idFuncSmoke() {
|
|
smokeTime = 0;
|
|
smoke = NULL;
|
|
restart = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncSmoke::Save
|
|
===============
|
|
*/
|
|
void idFuncSmoke::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( smokeTime );
|
|
savefile->WriteParticle( smoke );
|
|
savefile->WriteBool( restart );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncSmoke::Restore
|
|
===============
|
|
*/
|
|
void idFuncSmoke::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( smokeTime );
|
|
savefile->ReadParticle( smoke );
|
|
savefile->ReadBool( restart );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncSmoke::Spawn
|
|
===============
|
|
*/
|
|
void idFuncSmoke::Spawn( void ) {
|
|
const char *smokeName = spawnArgs.GetString( "smoke" );
|
|
if ( *smokeName != '\0' ) {
|
|
smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
} else {
|
|
smoke = NULL;
|
|
}
|
|
if ( spawnArgs.GetBool( "start_off" ) ) {
|
|
smokeTime = 0;
|
|
restart = false;
|
|
} else if ( smoke ) {
|
|
smokeTime = gameLocal.time;
|
|
BecomeActive( TH_UPDATEPARTICLES );
|
|
restart = true;
|
|
}
|
|
GetPhysics()->SetContents( 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncSmoke::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncSmoke::Event_Activate( idEntity *activator ) {
|
|
if ( thinkFlags & TH_UPDATEPARTICLES ) {
|
|
restart = false;
|
|
return;
|
|
} else {
|
|
BecomeActive( TH_UPDATEPARTICLES );
|
|
restart = true;
|
|
smokeTime = gameLocal.time;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncSmoke::Think
|
|
================
|
|
*/
|
|
void idFuncSmoke::Think( void ) {
|
|
|
|
// if we are completely closed off from the player, don't do anything at all
|
|
if ( CheckDormant() || smoke == NULL || smokeTime == -1 ) {
|
|
return;
|
|
}
|
|
|
|
if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) {
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
|
|
if ( restart ) {
|
|
smokeTime = gameLocal.time;
|
|
} else {
|
|
smokeTime = 0;
|
|
BecomeInactive( TH_UPDATEPARTICLES );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idTextEntity
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idTextEntity )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idTextEntity::Spawn
|
|
================
|
|
*/
|
|
void idTextEntity::Spawn( void ) {
|
|
// these are cached as the are used each frame
|
|
text = spawnArgs.GetString( "text" );
|
|
playerOriented = spawnArgs.GetBool( "playerOriented" );
|
|
bool force = spawnArgs.GetBool( "force" );
|
|
if ( developer.GetBool() || force ) {
|
|
BecomeActive(TH_THINK);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idTextEntity::Save
|
|
================
|
|
*/
|
|
void idTextEntity::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteString( text );
|
|
savefile->WriteBool( playerOriented );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idTextEntity::Restore
|
|
================
|
|
*/
|
|
void idTextEntity::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadString( text );
|
|
savefile->ReadBool( playerOriented );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idTextEntity::Think
|
|
================
|
|
*/
|
|
void idTextEntity::Think( void ) {
|
|
if ( thinkFlags & TH_THINK ) {
|
|
gameRenderWorld->DrawText( text, GetPhysics()->GetOrigin(), 0.25, colorWhite, playerOriented ? gameLocal.GetLocalPlayer()->viewAngles.ToMat3() : GetPhysics()->GetAxis().Transpose(), 1 );
|
|
for ( int i = 0; i < targets.Num(); i++ ) {
|
|
if ( targets[i].GetEntity() ) {
|
|
gameRenderWorld->DebugArrow( colorBlue, GetPhysics()->GetOrigin(), targets[i].GetEntity()->GetPhysics()->GetOrigin(), 1 );
|
|
}
|
|
}
|
|
} else {
|
|
BecomeInactive( TH_ALL );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idVacuumSeperatorEntity
|
|
|
|
Can be triggered to let vacuum through a portal (blown out window)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idVacuumSeparatorEntity )
|
|
EVENT( EV_Activate, idVacuumSeparatorEntity::Event_Activate )
|
|
END_CLASS
|
|
|
|
|
|
/*
|
|
================
|
|
idVacuumSeparatorEntity::idVacuumSeparatorEntity
|
|
================
|
|
*/
|
|
idVacuumSeparatorEntity::idVacuumSeparatorEntity( void ) {
|
|
portal = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idVacuumSeparatorEntity::Save
|
|
================
|
|
*/
|
|
void idVacuumSeparatorEntity::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( (int)portal );
|
|
savefile->WriteInt( gameRenderWorld->GetPortalState( portal ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idVacuumSeparatorEntity::Restore
|
|
================
|
|
*/
|
|
void idVacuumSeparatorEntity::Restore( idRestoreGame *savefile ) {
|
|
int state;
|
|
|
|
savefile->ReadInt( (int &)portal );
|
|
savefile->ReadInt( state );
|
|
|
|
gameLocal.SetPortalState( portal, state );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idVacuumSeparatorEntity::Spawn
|
|
================
|
|
*/
|
|
void idVacuumSeparatorEntity::Spawn() {
|
|
idBounds b;
|
|
|
|
b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
|
|
portal = gameRenderWorld->FindPortal( b );
|
|
if ( !portal ) {
|
|
gameLocal.Warning( "VacuumSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
|
|
return;
|
|
}
|
|
gameLocal.SetPortalState( portal, PS_BLOCK_AIR | PS_BLOCK_LOCATION );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idVacuumSeparatorEntity::Event_Activate
|
|
================
|
|
*/
|
|
void idVacuumSeparatorEntity::Event_Activate( idEntity *activator ) {
|
|
if ( !portal ) {
|
|
return;
|
|
}
|
|
gameLocal.SetPortalState( portal, PS_BLOCK_NONE );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idLocationSeparatorEntity
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idLocationSeparatorEntity )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idLocationSeparatorEntity::Spawn
|
|
================
|
|
*/
|
|
void idLocationSeparatorEntity::Spawn() {
|
|
idBounds b;
|
|
|
|
b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
|
|
qhandle_t portal = gameRenderWorld->FindPortal( b );
|
|
if ( !portal ) {
|
|
gameLocal.Warning( "LocationSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
|
|
}
|
|
gameLocal.SetPortalState( portal, PS_BLOCK_LOCATION );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idVacuumEntity
|
|
|
|
Levels should only have a single vacuum entity.
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idVacuumEntity )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idVacuumEntity::Spawn
|
|
================
|
|
*/
|
|
void idVacuumEntity::Spawn() {
|
|
if ( gameLocal.vacuumAreaNum != -1 ) {
|
|
gameLocal.Warning( "idVacuumEntity::Spawn: multiple idVacuumEntity in level" );
|
|
return;
|
|
}
|
|
|
|
idVec3 org = spawnArgs.GetVector( "origin" );
|
|
|
|
gameLocal.vacuumAreaNum = gameRenderWorld->PointInArea( org );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idLocationEntity
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idLocationEntity )
|
|
END_CLASS
|
|
|
|
/*
|
|
======================
|
|
idLocationEntity::Spawn
|
|
======================
|
|
*/
|
|
void idLocationEntity::Spawn() {
|
|
idStr realName;
|
|
|
|
// this just holds dict information
|
|
|
|
// if "location" not already set, use the entity name.
|
|
if ( !spawnArgs.GetString( "location", "", realName ) ) {
|
|
spawnArgs.Set( "location", name );
|
|
}
|
|
}
|
|
|
|
/*
|
|
======================
|
|
idLocationEntity::GetLocation
|
|
======================
|
|
*/
|
|
const char *idLocationEntity::GetLocation( void ) const {
|
|
return spawnArgs.GetString( "location" );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idBeam
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idBeam )
|
|
EVENT( EV_PostSpawn, idBeam::Event_MatchTarget )
|
|
EVENT( EV_Activate, idBeam::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idBeam::idBeam
|
|
===============
|
|
*/
|
|
idBeam::idBeam() {
|
|
target = NULL;
|
|
master = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idBeam::Save
|
|
===============
|
|
*/
|
|
void idBeam::Save( idSaveGame *savefile ) const {
|
|
target.Save( savefile );
|
|
master.Save( savefile );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idBeam::Restore
|
|
===============
|
|
*/
|
|
void idBeam::Restore( idRestoreGame *savefile ) {
|
|
target.Restore( savefile );
|
|
master.Restore( savefile );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idBeam::Spawn
|
|
===============
|
|
*/
|
|
void idBeam::Spawn( void ) {
|
|
float width;
|
|
|
|
if ( spawnArgs.GetFloat( "width", "0", width ) ) {
|
|
renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = width;
|
|
}
|
|
|
|
SetModel( "_BEAM" );
|
|
Hide();
|
|
PostEventMS( &EV_PostSpawn, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::Think
|
|
================
|
|
*/
|
|
void idBeam::Think( void ) {
|
|
idBeam *masterEnt;
|
|
|
|
if ( !IsHidden() && !target.GetEntity() ) {
|
|
// hide if our target is removed
|
|
Hide();
|
|
}
|
|
|
|
RunPhysics();
|
|
|
|
masterEnt = master.GetEntity();
|
|
if ( masterEnt ) {
|
|
const idVec3 &origin = GetPhysics()->GetOrigin();
|
|
masterEnt->SetBeamTarget( origin );
|
|
}
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::SetMaster
|
|
================
|
|
*/
|
|
void idBeam::SetMaster( idBeam *masterbeam ) {
|
|
master = masterbeam;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::SetBeamTarget
|
|
================
|
|
*/
|
|
void idBeam::SetBeamTarget( const idVec3 &origin ) {
|
|
if ( ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] != origin.x ) || ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] != origin.y ) || ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] != origin.z ) ) {
|
|
renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] = origin.x;
|
|
renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] = origin.y;
|
|
renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] = origin.z;
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::Show
|
|
================
|
|
*/
|
|
void idBeam::Show( void ) {
|
|
idBeam *targetEnt;
|
|
|
|
idEntity::Show();
|
|
|
|
targetEnt = target.GetEntity();
|
|
if ( targetEnt ) {
|
|
const idVec3 &origin = targetEnt->GetPhysics()->GetOrigin();
|
|
SetBeamTarget( origin );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::Event_MatchTarget
|
|
================
|
|
*/
|
|
void idBeam::Event_MatchTarget( void ) {
|
|
int i;
|
|
idEntity *targetEnt;
|
|
idBeam *targetBeam;
|
|
|
|
if ( !targets.Num() ) {
|
|
return;
|
|
}
|
|
|
|
targetBeam = NULL;
|
|
for( i = 0; i < targets.Num(); i++ ) {
|
|
targetEnt = targets[ i ].GetEntity();
|
|
if ( targetEnt && targetEnt->IsType( idBeam::Type ) ) {
|
|
targetBeam = static_cast<idBeam *>( targetEnt );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !targetBeam ) {
|
|
gameLocal.Error( "Could not find valid beam target for '%s'", name.c_str() );
|
|
}
|
|
|
|
target = targetBeam;
|
|
targetBeam->SetMaster( this );
|
|
if ( !spawnArgs.GetBool( "start_off" ) ) {
|
|
Show();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::Event_Activate
|
|
================
|
|
*/
|
|
void idBeam::Event_Activate( idEntity *activator ) {
|
|
if ( IsHidden() ) {
|
|
Show();
|
|
} else {
|
|
Hide();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idBeam::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
GetPhysics()->WriteToSnapshot( msg );
|
|
WriteBindToSnapshot( msg );
|
|
WriteColorToSnapshot( msg );
|
|
msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
|
|
msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] );
|
|
msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBeam::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idBeam::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
GetPhysics()->ReadFromSnapshot( msg );
|
|
ReadBindFromSnapshot( msg );
|
|
ReadColorFromSnapshot( msg );
|
|
renderEntity.shaderParms[SHADERPARM_BEAM_END_X] = msg.ReadFloat();
|
|
renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] = msg.ReadFloat();
|
|
renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] = msg.ReadFloat();
|
|
if ( msg.HasChanged() ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idLiquid
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idLiquid )
|
|
EVENT( EV_Touch, idLiquid::Event_Touch )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idLiquid::Save
|
|
================
|
|
*/
|
|
void idLiquid::Save( idSaveGame *savefile ) const {
|
|
// Nothing to save
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLiquid::Restore
|
|
================
|
|
*/
|
|
void idLiquid::Restore( idRestoreGame *savefile ) {
|
|
//FIXME: NO!
|
|
Spawn();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLiquid::Spawn
|
|
================
|
|
*/
|
|
void idLiquid::Spawn() {
|
|
/*
|
|
model = dynamic_cast<idRenderModelLiquid *>( renderEntity.hModel );
|
|
if ( !model ) {
|
|
gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() );
|
|
}
|
|
model->Reset();
|
|
GetPhysics()->SetContents( CONTENTS_TRIGGER );
|
|
*/
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLiquid::Event_Touch
|
|
================
|
|
*/
|
|
void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) {
|
|
// FIXME: for QuakeCon
|
|
/*
|
|
idVec3 pos;
|
|
|
|
pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
|
|
model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f );
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idShaking
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idShaking )
|
|
EVENT( EV_Activate, idShaking::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idShaking::idShaking
|
|
===============
|
|
*/
|
|
idShaking::idShaking() {
|
|
active = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idShaking::Save
|
|
===============
|
|
*/
|
|
void idShaking::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteBool( active );
|
|
savefile->WriteStaticObject( physicsObj );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idShaking::Restore
|
|
===============
|
|
*/
|
|
void idShaking::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadBool( active );
|
|
savefile->ReadStaticObject( physicsObj );
|
|
RestorePhysics( &physicsObj );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idShaking::Spawn
|
|
===============
|
|
*/
|
|
void idShaking::Spawn( void ) {
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
SetPhysics( &physicsObj );
|
|
|
|
active = false;
|
|
if ( !spawnArgs.GetBool( "start_off" ) ) {
|
|
BeginShaking();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idShaking::BeginShaking
|
|
================
|
|
*/
|
|
void idShaking::BeginShaking( void ) {
|
|
int phase;
|
|
idAngles shake;
|
|
int period;
|
|
|
|
active = true;
|
|
phase = gameLocal.random.RandomInt( 1000 );
|
|
shake = spawnArgs.GetAngles( "shake", "0.5 0.5 0.5" );
|
|
period = spawnArgs.GetFloat( "period", "0.05" ) * 1000;
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase, period * 0.25f, GetPhysics()->GetAxis().ToAngles(), shake, ang_zero );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idShaking::Event_Activate
|
|
================
|
|
*/
|
|
void idShaking::Event_Activate( idEntity *activator ) {
|
|
if ( !active ) {
|
|
BeginShaking();
|
|
} else {
|
|
active = false;
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, physicsObj.GetAxis().ToAngles(), ang_zero, ang_zero );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idEarthQuake
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idEarthQuake )
|
|
EVENT( EV_Activate, idEarthQuake::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idEarthQuake::idEarthQuake
|
|
===============
|
|
*/
|
|
idEarthQuake::idEarthQuake() {
|
|
wait = 0.0f;
|
|
random = 0.0f;
|
|
nextTriggerTime = 0;
|
|
shakeStopTime = 0;
|
|
triggered = false;
|
|
playerOriented = false;
|
|
disabled = false;
|
|
shakeTime = 0.0f;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idEarthQuake::Save
|
|
===============
|
|
*/
|
|
void idEarthQuake::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( nextTriggerTime );
|
|
savefile->WriteInt( shakeStopTime );
|
|
savefile->WriteFloat( wait );
|
|
savefile->WriteFloat( random );
|
|
savefile->WriteBool( triggered );
|
|
savefile->WriteBool( playerOriented );
|
|
savefile->WriteBool( disabled );
|
|
savefile->WriteFloat( shakeTime );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idEarthQuake::Restore
|
|
===============
|
|
*/
|
|
void idEarthQuake::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( nextTriggerTime );
|
|
savefile->ReadInt( shakeStopTime );
|
|
savefile->ReadFloat( wait );
|
|
savefile->ReadFloat( random );
|
|
savefile->ReadBool( triggered );
|
|
savefile->ReadBool( playerOriented );
|
|
savefile->ReadBool( disabled );
|
|
savefile->ReadFloat( shakeTime );
|
|
|
|
if ( shakeStopTime > gameLocal.time ) {
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idEarthQuake::Spawn
|
|
===============
|
|
*/
|
|
void idEarthQuake::Spawn( void ) {
|
|
nextTriggerTime = 0;
|
|
shakeStopTime = 0;
|
|
wait = spawnArgs.GetFloat( "wait", "15" );
|
|
random = spawnArgs.GetFloat( "random", "5" );
|
|
triggered = spawnArgs.GetBool( "triggered" );
|
|
playerOriented = spawnArgs.GetBool( "playerOriented" );
|
|
disabled = false;
|
|
shakeTime = spawnArgs.GetFloat( "shakeTime", "0" );
|
|
|
|
if ( !triggered ){
|
|
PostEventSec( &EV_Activate, spawnArgs.GetFloat( "wait" ), this );
|
|
}
|
|
BecomeInactive( TH_THINK );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEarthQuake::Event_Activate
|
|
================
|
|
*/
|
|
void idEarthQuake::Event_Activate( idEntity *activator ) {
|
|
|
|
if ( nextTriggerTime > gameLocal.time ) {
|
|
return;
|
|
}
|
|
|
|
if ( disabled && activator == this ) {
|
|
return;
|
|
}
|
|
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL ) {
|
|
return;
|
|
}
|
|
|
|
nextTriggerTime = 0;
|
|
|
|
if ( !triggered && activator != this ){
|
|
// if we are not triggered ( i.e. random ), disable or enable
|
|
disabled ^= 1;
|
|
if (disabled) {
|
|
return;
|
|
} else {
|
|
PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
|
|
}
|
|
}
|
|
|
|
ActivateTargets( activator );
|
|
|
|
const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_quake" ) );
|
|
if ( playerOriented ) {
|
|
player->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
|
|
} else {
|
|
StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
|
|
}
|
|
|
|
if ( shakeTime > 0.0f ) {
|
|
shakeStopTime = gameLocal.time + SEC2MS( shakeTime );
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
|
|
if ( wait > 0.0f ) {
|
|
if ( !triggered ) {
|
|
PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
|
|
} else {
|
|
nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
|
|
}
|
|
} else if ( shakeTime == 0.0f ) {
|
|
PostEventMS( &EV_Remove, 0 );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
idEarthQuake::Think
|
|
================
|
|
*/
|
|
void idEarthQuake::Think( void ) {
|
|
if ( thinkFlags & TH_THINK ) {
|
|
if ( gameLocal.time > shakeStopTime ) {
|
|
BecomeInactive( TH_THINK );
|
|
if ( wait <= 0.0f ) {
|
|
PostEventMS( &EV_Remove, 0 );
|
|
}
|
|
return;
|
|
}
|
|
float shakeVolume = gameSoundWorld->CurrentShakeAmplitudeForPosition( gameLocal.time, gameLocal.GetLocalPlayer()->firstPersonViewOrigin );
|
|
gameLocal.RadiusPush( GetPhysics()->GetOrigin(), 256, 1500 * shakeVolume, this, this, 1.0f, true );
|
|
}
|
|
BecomeInactive( TH_UPDATEVISUALS );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncPortal
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idFuncPortal )
|
|
EVENT( EV_Activate, idFuncPortal::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncPortal::idFuncPortal
|
|
===============
|
|
*/
|
|
idFuncPortal::idFuncPortal() {
|
|
portal = 0;
|
|
state = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncPortal::Save
|
|
===============
|
|
*/
|
|
void idFuncPortal::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( (int)portal );
|
|
savefile->WriteBool( state );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncPortal::Restore
|
|
===============
|
|
*/
|
|
void idFuncPortal::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( (int &)portal );
|
|
savefile->ReadBool( state );
|
|
gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncPortal::Spawn
|
|
===============
|
|
*/
|
|
void idFuncPortal::Spawn( void ) {
|
|
portal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds().Expand( 32.0f ) );
|
|
if ( portal > 0 ) {
|
|
state = spawnArgs.GetBool( "start_on" );
|
|
gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncPortal::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncPortal::Event_Activate( idEntity *activator ) {
|
|
if ( portal > 0 ) {
|
|
state = !state;
|
|
gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncAASPortal
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idFuncAASPortal )
|
|
EVENT( EV_Activate, idFuncAASPortal::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncAASPortal::idFuncAASPortal
|
|
===============
|
|
*/
|
|
idFuncAASPortal::idFuncAASPortal() {
|
|
state = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncAASPortal::Save
|
|
===============
|
|
*/
|
|
void idFuncAASPortal::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteBool( state );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncAASPortal::Restore
|
|
===============
|
|
*/
|
|
void idFuncAASPortal::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadBool( state );
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncAASPortal::Spawn
|
|
===============
|
|
*/
|
|
void idFuncAASPortal::Spawn( void ) {
|
|
state = spawnArgs.GetBool( "start_on" );
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncAASPortal::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncAASPortal::Event_Activate( idEntity *activator ) {
|
|
state ^= 1;
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncAASObstacle
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idFuncAASObstacle )
|
|
EVENT( EV_Activate, idFuncAASObstacle::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncAASObstacle::idFuncAASObstacle
|
|
===============
|
|
*/
|
|
idFuncAASObstacle::idFuncAASObstacle() {
|
|
state = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncAASObstacle::Save
|
|
===============
|
|
*/
|
|
void idFuncAASObstacle::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteBool( state );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncAASObstacle::Restore
|
|
===============
|
|
*/
|
|
void idFuncAASObstacle::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadBool( state );
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncAASObstacle::Spawn
|
|
===============
|
|
*/
|
|
void idFuncAASObstacle::Spawn( void ) {
|
|
state = spawnArgs.GetBool( "start_on" );
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncAASObstacle::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncAASObstacle::Event_Activate( idEntity *activator ) {
|
|
state ^= 1;
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idFuncRadioChatter
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_ResetRadioHud( "<resetradiohud>", "e" );
|
|
|
|
|
|
CLASS_DECLARATION( idEntity, idFuncRadioChatter )
|
|
EVENT( EV_Activate, idFuncRadioChatter::Event_Activate )
|
|
EVENT( EV_ResetRadioHud, idFuncRadioChatter::Event_ResetRadioHud )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idFuncRadioChatter::idFuncRadioChatter
|
|
===============
|
|
*/
|
|
idFuncRadioChatter::idFuncRadioChatter() {
|
|
time = 0.0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncRadioChatter::Save
|
|
===============
|
|
*/
|
|
void idFuncRadioChatter::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteFloat( time );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncRadioChatter::Restore
|
|
===============
|
|
*/
|
|
void idFuncRadioChatter::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadFloat( time );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idFuncRadioChatter::Spawn
|
|
===============
|
|
*/
|
|
void idFuncRadioChatter::Spawn( void ) {
|
|
time = spawnArgs.GetFloat( "time", "5.0" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncRadioChatter::Event_Activate
|
|
================
|
|
*/
|
|
void idFuncRadioChatter::Event_Activate( idEntity *activator ) {
|
|
idPlayer *player;
|
|
const char *sound;
|
|
const idSoundShader *shader;
|
|
int length;
|
|
|
|
if ( activator->IsType( idPlayer::Type ) ) {
|
|
player = static_cast<idPlayer *>( activator );
|
|
} else {
|
|
player = gameLocal.GetLocalPlayer();
|
|
}
|
|
|
|
player->hud->HandleNamedEvent( "radioChatterUp" );
|
|
|
|
sound = spawnArgs.GetString( "snd_radiochatter", "" );
|
|
if ( sound && *sound ) {
|
|
shader = declManager->FindSound( sound );
|
|
player->StartSoundShader( shader, SND_CHANNEL_RADIO, SSF_GLOBAL, false, &length );
|
|
time = MS2SEC( length + 150 );
|
|
}
|
|
// we still put the hud up because this is used with no sound on
|
|
// certain frame commands when the chatter is triggered
|
|
PostEventSec( &EV_ResetRadioHud, time, player );
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
idFuncRadioChatter::Event_ResetRadioHud
|
|
================
|
|
*/
|
|
void idFuncRadioChatter::Event_ResetRadioHud( idEntity *activator ) {
|
|
idPlayer *player = ( activator->IsType( idPlayer::Type ) ) ? static_cast<idPlayer *>( activator ) : gameLocal.GetLocalPlayer();
|
|
player->hud->HandleNamedEvent( "radioChatterDown" );
|
|
ActivateTargets( activator );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idPhantomObjects
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idPhantomObjects )
|
|
EVENT( EV_Activate, idPhantomObjects::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idPhantomObjects::idPhantomObjects
|
|
===============
|
|
*/
|
|
idPhantomObjects::idPhantomObjects() {
|
|
target = NULL;
|
|
end_time = 0;
|
|
throw_time = 0.0f;
|
|
shake_time = 0.0f;
|
|
shake_ang.Zero();
|
|
speed = 0.0f;
|
|
min_wait = 0;
|
|
max_wait = 0;
|
|
fl.neverDormant = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPhantomObjects::Save
|
|
===============
|
|
*/
|
|
void idPhantomObjects::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
|
|
savefile->WriteInt( end_time );
|
|
savefile->WriteFloat( throw_time );
|
|
savefile->WriteFloat( shake_time );
|
|
savefile->WriteVec3( shake_ang );
|
|
savefile->WriteFloat( speed );
|
|
savefile->WriteInt( min_wait );
|
|
savefile->WriteInt( max_wait );
|
|
target.Save( savefile );
|
|
savefile->WriteInt( targetTime.Num() );
|
|
for( i = 0; i < targetTime.Num(); i++ ) {
|
|
savefile->WriteInt( targetTime[ i ] );
|
|
}
|
|
|
|
for( i = 0; i < lastTargetPos.Num(); i++ ) {
|
|
savefile->WriteVec3( lastTargetPos[ i ] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPhantomObjects::Restore
|
|
===============
|
|
*/
|
|
void idPhantomObjects::Restore( idRestoreGame *savefile ) {
|
|
int num;
|
|
int i;
|
|
|
|
savefile->ReadInt( end_time );
|
|
savefile->ReadFloat( throw_time );
|
|
savefile->ReadFloat( shake_time );
|
|
savefile->ReadVec3( shake_ang );
|
|
savefile->ReadFloat( speed );
|
|
savefile->ReadInt( min_wait );
|
|
savefile->ReadInt( max_wait );
|
|
target.Restore( savefile );
|
|
|
|
savefile->ReadInt( num );
|
|
targetTime.SetGranularity( 1 );
|
|
targetTime.SetNum( num );
|
|
lastTargetPos.SetGranularity( 1 );
|
|
lastTargetPos.SetNum( num );
|
|
|
|
for( i = 0; i < num; i++ ) {
|
|
savefile->ReadInt( targetTime[ i ] );
|
|
}
|
|
|
|
if ( savefile->GetBuildNumber() == INITIAL_RELEASE_BUILD_NUMBER ) {
|
|
// these weren't saved out in the first release
|
|
for( i = 0; i < num; i++ ) {
|
|
lastTargetPos[ i ].Zero();
|
|
}
|
|
} else {
|
|
for( i = 0; i < num; i++ ) {
|
|
savefile->ReadVec3( lastTargetPos[ i ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPhantomObjects::Spawn
|
|
===============
|
|
*/
|
|
void idPhantomObjects::Spawn( void ) {
|
|
throw_time = spawnArgs.GetFloat( "time", "5" );
|
|
speed = spawnArgs.GetFloat( "speed", "1200" );
|
|
shake_time = spawnArgs.GetFloat( "shake_time", "1" );
|
|
throw_time -= shake_time;
|
|
if ( throw_time < 0.0f ) {
|
|
throw_time = 0.0f;
|
|
}
|
|
min_wait = SEC2MS( spawnArgs.GetFloat( "min_wait", "1" ) );
|
|
max_wait = SEC2MS( spawnArgs.GetFloat( "max_wait", "3" ) );
|
|
|
|
shake_ang = spawnArgs.GetVector( "shake_ang", "65 65 65" );
|
|
Hide();
|
|
GetPhysics()->SetContents( 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhantomObjects::Event_Activate
|
|
================
|
|
*/
|
|
void idPhantomObjects::Event_Activate( idEntity *activator ) {
|
|
int i;
|
|
float time;
|
|
float frac;
|
|
float scale;
|
|
|
|
if ( thinkFlags & TH_THINK ) {
|
|
BecomeInactive( TH_THINK );
|
|
return;
|
|
}
|
|
|
|
RemoveNullTargets();
|
|
if ( !targets.Num() ) {
|
|
return;
|
|
}
|
|
|
|
if ( !activator || !activator->IsType( idActor::Type ) ) {
|
|
target = gameLocal.GetLocalPlayer();
|
|
} else {
|
|
target = static_cast<idActor *>( activator );
|
|
}
|
|
|
|
end_time = gameLocal.time + SEC2MS( spawnArgs.GetFloat( "end_time", "0" ) );
|
|
|
|
targetTime.SetNum( targets.Num() );
|
|
lastTargetPos.SetNum( targets.Num() );
|
|
|
|
const idVec3 &toPos = target.GetEntity()->GetEyePosition();
|
|
|
|
// calculate the relative times of all the objects
|
|
time = 0.0f;
|
|
for( i = 0; i < targetTime.Num(); i++ ) {
|
|
targetTime[ i ] = SEC2MS( time );
|
|
lastTargetPos[ i ] = toPos;
|
|
|
|
frac = 1.0f - ( float )i / ( float )targetTime.Num();
|
|
time += ( gameLocal.random.RandomFloat() + 1.0f ) * 0.5f * frac + 0.1f;
|
|
}
|
|
|
|
// scale up the times to fit within throw_time
|
|
scale = throw_time / time;
|
|
for( i = 0; i < targetTime.Num(); i++ ) {
|
|
targetTime[ i ] = gameLocal.time + SEC2MS( shake_time )+ targetTime[ i ] * scale;
|
|
}
|
|
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPhantomObjects::Think
|
|
================
|
|
*/
|
|
void idPhantomObjects::Think( void ) {
|
|
int i;
|
|
int num;
|
|
float time;
|
|
idVec3 vel;
|
|
idVec3 ang;
|
|
idEntity *ent;
|
|
idActor *targetEnt;
|
|
idPhysics *entPhys;
|
|
trace_t tr;
|
|
|
|
// if we are completely closed off from the player, don't do anything at all
|
|
if ( CheckDormant() ) {
|
|
return;
|
|
}
|
|
|
|
if ( !( thinkFlags & TH_THINK ) ) {
|
|
BecomeInactive( thinkFlags & ~TH_THINK );
|
|
return;
|
|
}
|
|
|
|
targetEnt = target.GetEntity();
|
|
if ( !targetEnt || ( targetEnt->health <= 0 ) || ( end_time && ( gameLocal.time > end_time ) ) || gameLocal.inCinematic ) {
|
|
BecomeInactive( TH_THINK );
|
|
}
|
|
|
|
const idVec3 &toPos = targetEnt->GetEyePosition();
|
|
|
|
num = 0;
|
|
for ( i = 0; i < targets.Num(); i++ ) {
|
|
ent = targets[ i ].GetEntity();
|
|
if ( !ent ) {
|
|
continue;
|
|
}
|
|
|
|
if ( ent->fl.hidden ) {
|
|
// don't throw hidden objects
|
|
continue;
|
|
}
|
|
|
|
if ( !targetTime[ i ] ) {
|
|
// already threw this object
|
|
continue;
|
|
}
|
|
|
|
num++;
|
|
|
|
time = MS2SEC( targetTime[ i ] - gameLocal.time );
|
|
if ( time > shake_time ) {
|
|
continue;
|
|
}
|
|
|
|
entPhys = ent->GetPhysics();
|
|
const idVec3 &entOrg = entPhys->GetOrigin();
|
|
|
|
gameLocal.clip.TracePoint( tr, entOrg, toPos, MASK_OPAQUE, ent );
|
|
if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == targetEnt ) ) {
|
|
lastTargetPos[ i ] = toPos;
|
|
}
|
|
|
|
if ( time < 0.0f ) {
|
|
idAI::PredictTrajectory( entPhys->GetOrigin(), lastTargetPos[ i ], speed, entPhys->GetGravity(),
|
|
entPhys->GetClipModel(), entPhys->GetClipMask(), 256.0f, ent, targetEnt, ai_debugTrajectory.GetBool() ? 1 : 0, vel );
|
|
vel *= speed;
|
|
entPhys->SetLinearVelocity( vel );
|
|
if ( !end_time ) {
|
|
targetTime[ i ] = 0;
|
|
} else {
|
|
targetTime[ i ] = gameLocal.time + gameLocal.random.RandomInt( max_wait - min_wait ) + min_wait;
|
|
}
|
|
if ( ent->IsType( idMoveable::Type ) ) {
|
|
idMoveable *ment = static_cast<idMoveable*>( ent );
|
|
ment->EnableDamage( true, 2.5f );
|
|
}
|
|
} else {
|
|
// this is not the right way to set the angular velocity, but the effect is nice, so I'm keeping it. :)
|
|
ang.Set( gameLocal.random.CRandomFloat() * shake_ang.x, gameLocal.random.CRandomFloat() * shake_ang.y, gameLocal.random.CRandomFloat() * shake_ang.z );
|
|
ang *= ( 1.0f - time / shake_time );
|
|
entPhys->SetAngularVelocity( ang );
|
|
}
|
|
}
|
|
|
|
if ( !num ) {
|
|
BecomeInactive( TH_THINK );
|
|
}
|
|
}
|