2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
30 Various utility objects and functions.
34 #include "../idlib/precompiled.h"
37 #include "Game_local.h"
40 ===============================================================================
44 A simple, spawnable entity with a model and no functionable ability of it's own.
45 For example, it can be used as a placeholder during development, for marking
46 locations on maps for script, or for simple placed models without any behavior
47 that can be bound to other entities. Should not be subclassed.
48 ===============================================================================
51 CLASS_DECLARATION( idEntity, idSpawnableEntity )
55 ======================
56 idSpawnableEntity::Spawn
57 ======================
59 void idSpawnableEntity::Spawn() {
60 // this just holds dict information
64 ===============================================================================
68 ===============================================================================
71 const idEventDef EV_TeleportStage( "<TeleportStage>", "e" );
73 CLASS_DECLARATION( idEntity, idPlayerStart )
74 EVENT( EV_Activate, idPlayerStart::Event_TeleportPlayer )
75 EVENT( EV_TeleportStage, idPlayerStart::Event_TeleportStage )
80 idPlayerStart::idPlayerStart
83 idPlayerStart::idPlayerStart( void ) {
92 void idPlayerStart::Spawn( void ) {
101 void idPlayerStart::Save( idSaveGame *savefile ) const {
102 savefile->WriteInt( teleportStage );
107 idPlayerStart::Restore
110 void idPlayerStart::Restore( idRestoreGame *savefile ) {
111 savefile->ReadInt( teleportStage );
116 idPlayerStart::ClientReceiveEvent
119 bool idPlayerStart::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
123 case EVENT_TELEPORTPLAYER: {
124 entityNumber = msg.ReadBits( GENTITYNUM_BITS );
125 idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[entityNumber] );
126 if ( player != NULL && player->IsType( idPlayer::Type ) ) {
127 Event_TeleportPlayer( player );
132 return idEntity::ClientReceiveEvent( event, time, msg );
140 idPlayerStart::Event_TeleportStage
142 FIXME: add functionality to fx system ( could be done with player scripting too )
145 void idPlayerStart::Event_TeleportStage( idEntity *_player ) {
147 if ( !_player->IsType( idPlayer::Type ) ) {
148 common->Warning( "idPlayerStart::Event_TeleportStage: entity is not an idPlayer\n" );
151 player = static_cast<idPlayer*>(_player);
152 float teleportDelay = spawnArgs.GetFloat( "teleportDelay" );
153 switch ( teleportStage ) {
155 player->playerView.Flash( colorWhite, 125 );
156 player->SetInfluenceLevel( INFLUENCE_LEVEL3 );
157 player->SetInfluenceView( spawnArgs.GetString( "mtr_teleportFx" ), NULL, 0.0f, NULL );
158 gameSoundWorld->FadeSoundClasses( 0, -20.0f, teleportDelay );
159 player->StartSound( "snd_teleport_start", SND_CHANNEL_BODY2, 0, false, NULL );
161 PostEventSec( &EV_TeleportStage, teleportDelay, player );
164 gameSoundWorld->FadeSoundClasses( 0, 0.0f, 0.25f );
166 PostEventSec( &EV_TeleportStage, 0.25f, player );
169 player->SetInfluenceView( NULL, NULL, 0.0f, NULL );
170 TeleportPlayer( player );
171 player->StopSound( SND_CHANNEL_BODY2, false );
172 player->SetInfluenceLevel( INFLUENCE_NONE );
182 idPlayerStart::TeleportPlayer
185 void idPlayerStart::TeleportPlayer( idPlayer *player ) {
186 float pushVel = spawnArgs.GetFloat( "push", "300" );
187 float f = spawnArgs.GetFloat( "visualEffect", "0" );
188 const char *viewName = spawnArgs.GetString( "visualView", "" );
189 idEntity *ent = viewName ? gameLocal.FindEntity( viewName ) : NULL;
192 // place in private camera view for some time
193 // the entity needs to teleport to where the camera view is to have the PVS right
194 player->Teleport( ent->GetPhysics()->GetOrigin(), ang_zero, this );
195 player->StartSound( "snd_teleport_enter", SND_CHANNEL_ANY, 0, false, NULL );
196 player->SetPrivateCameraView( static_cast<idCamera*>(ent) );
197 // the player entity knows where to spawn from the previous Teleport call
198 if ( !gameLocal.isClient ) {
199 player->PostEventSec( &EV_Player_ExitTeleporter, f );
202 // direct to exit, Teleport will take care of the killbox
203 player->Teleport( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis().ToAngles(), NULL );
205 // multiplayer hijacked this entity, so only push the player in multiplayer
206 if ( gameLocal.isMultiplayer ) {
207 player->GetPhysics()->SetLinearVelocity( GetPhysics()->GetAxis()[0] * pushVel );
214 idPlayerStart::Event_TeleportPlayer
217 void idPlayerStart::Event_TeleportPlayer( idEntity *activator ) {
220 if ( activator->IsType( idPlayer::Type ) ) {
221 player = static_cast<idPlayer*>( activator );
223 player = gameLocal.GetLocalPlayer();
226 if ( spawnArgs.GetBool( "visualFx" ) ) {
229 Event_TeleportStage( player );
233 if ( gameLocal.isServer ) {
235 byte msgBuf[MAX_EVENT_PARAM_SIZE];
237 msg.Init( msgBuf, sizeof( msgBuf ) );
239 msg.WriteBits( player->entityNumber, GENTITYNUM_BITS );
240 ServerSendEvent( EVENT_TELEPORTPLAYER, &msg, false, -1 );
243 TeleportPlayer( player );
249 ===============================================================================
253 ===============================================================================
256 CLASS_DECLARATION( idEntity, idActivator )
257 EVENT( EV_Activate, idActivator::Event_Activate )
265 void idActivator::Save( idSaveGame *savefile ) const {
266 savefile->WriteBool( stay_on );
274 void idActivator::Restore( idRestoreGame *savefile ) {
275 savefile->ReadBool( stay_on );
278 BecomeActive( TH_THINK );
287 void idActivator::Spawn( void ) {
290 spawnArgs.GetBool( "stay_on", "0", stay_on );
291 spawnArgs.GetBool( "start_off", "0", start_off );
293 GetPhysics()->SetClipBox( idBounds( vec3_origin ).Expand( 4 ), 1.0f );
294 GetPhysics()->SetContents( 0 );
297 BecomeActive( TH_THINK );
306 void idActivator::Think( void ) {
308 if ( thinkFlags & TH_THINK ) {
309 if ( TouchTriggers() ) {
311 BecomeInactive( TH_THINK );
320 idActivator::Activate
323 void idActivator::Event_Activate( idEntity *activator ) {
324 if ( thinkFlags & TH_THINK ) {
325 BecomeInactive( TH_THINK );
327 BecomeActive( TH_THINK );
333 ===============================================================================
337 ===============================================================================
340 CLASS_DECLARATION( idEntity, idPathCorner )
341 EVENT( AI_RandomPath, idPathCorner::Event_RandomPath )
345 =====================
347 =====================
349 void idPathCorner::Spawn( void ) {
353 =====================
354 idPathCorner::DrawDebugInfo
355 =====================
357 void idPathCorner::DrawDebugInfo( void ) {
359 idBounds bnds( idVec3( -4.0, -4.0f, -8.0f ), idVec3( 4.0, 4.0f, 64.0f ) );
361 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
362 if ( !ent->IsType( idPathCorner::Type ) ) {
366 idVec3 org = ent->GetPhysics()->GetOrigin();
367 gameRenderWorld->DebugBounds( colorRed, bnds, org, 0 );
373 idPathCorner::RandomPath
376 idPathCorner *idPathCorner::RandomPath( const idEntity *source, const idEntity *ignore ) {
381 idPathCorner *path[ MAX_GENTITIES ];
384 for( i = 0; i < source->targets.Num(); i++ ) {
385 ent = source->targets[ i ].GetEntity();
386 if ( ent && ( ent != ignore ) && ent->IsType( idPathCorner::Type ) ) {
387 path[ num++ ] = static_cast<idPathCorner *>( ent );
388 if ( num >= MAX_GENTITIES ) {
398 which = gameLocal.random.RandomInt( num );
399 return path[ which ];
403 =====================
404 idPathCorner::Event_RandomPath
405 =====================
407 void idPathCorner::Event_RandomPath( void ) {
410 path = RandomPath( this, NULL );
411 idThread::ReturnEntity( path );
415 ===============================================================================
419 ===============================================================================
422 const idEventDef EV_RestoreDamagable( "<RestoreDamagable>" );
424 CLASS_DECLARATION( idEntity, idDamagable )
425 EVENT( EV_Activate, idDamagable::Event_BecomeBroken )
426 EVENT( EV_RestoreDamagable, idDamagable::Event_RestoreDamagable )
431 idDamagable::idDamagable
434 idDamagable::idDamagable( void ) {
444 void idDamagable::Save( idSaveGame *savefile ) const {
445 savefile->WriteInt( count );
446 savefile->WriteInt( nextTriggerTime );
454 void idDamagable::Restore( idRestoreGame *savefile ) {
455 savefile->ReadInt( count );
456 savefile->ReadInt( nextTriggerTime );
464 void idDamagable::Spawn( void ) {
467 health = spawnArgs.GetInt( "health", "5" );
468 spawnArgs.GetInt( "count", "1", count );
471 // make sure the model gets cached
472 spawnArgs.GetString( "broken", "", broken );
473 if ( broken.Length() && !renderModelManager->CheckModel( broken ) ) {
474 gameLocal.Error( "idDamagable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), broken.c_str() );
477 fl.takedamage = true;
478 GetPhysics()->SetContents( CONTENTS_SOLID );
483 idDamagable::BecomeBroken
486 void idDamagable::BecomeBroken( idEntity *activator ) {
492 if ( gameLocal.time < nextTriggerTime ) {
496 spawnArgs.GetFloat( "wait", "0.1", wait );
497 nextTriggerTime = gameLocal.time + SEC2MS( wait );
501 fl.takedamage = false;
503 health = spawnArgs.GetInt( "health", "5" );
509 spawnArgs.GetString( "broken", "", broken );
510 if ( broken.Length() ) {
514 // offset the start time of the shader to sync it to the gameLocal time
515 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
517 spawnArgs.GetInt( "numstates", "1", numStates );
518 spawnArgs.GetInt( "cycle", "0", cycle );
519 spawnArgs.GetFloat( "forcestate", "0", forceState );
521 // set the state parm
523 renderEntity.shaderParms[ SHADERPARM_MODE ]++;
524 if ( renderEntity.shaderParms[ SHADERPARM_MODE ] > numStates ) {
525 renderEntity.shaderParms[ SHADERPARM_MODE ] = 0;
527 } else if ( forceState ) {
528 renderEntity.shaderParms[ SHADERPARM_MODE ] = forceState;
530 renderEntity.shaderParms[ SHADERPARM_MODE ] = gameLocal.random.RandomInt( numStates ) + 1;
533 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
535 ActivateTargets( activator );
537 if ( spawnArgs.GetBool( "hideWhenBroken" ) ) {
539 PostEventMS( &EV_RestoreDamagable, nextTriggerTime - gameLocal.time );
540 BecomeActive( TH_THINK );
549 void idDamagable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
550 if ( gameLocal.time < nextTriggerTime ) {
555 BecomeBroken( attacker );
560 idDamagable::Event_BecomeBroken
563 void idDamagable::Event_BecomeBroken( idEntity *activator ) {
564 BecomeBroken( activator );
569 idDamagable::Event_RestoreDamagable
572 void idDamagable::Event_RestoreDamagable( void ) {
573 health = spawnArgs.GetInt( "health", "5" );
579 ===============================================================================
583 ===============================================================================
586 CLASS_DECLARATION( idEntity, idExplodable )
587 EVENT( EV_Activate, idExplodable::Event_Explode )
595 void idExplodable::Spawn( void ) {
601 idExplodable::Event_Explode
604 void idExplodable::Event_Explode( idEntity *activator ) {
607 if ( spawnArgs.GetString( "def_damage", "damage_explosion", &temp ) ) {
608 gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), activator, activator, this, this, temp );
611 StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL );
613 // Show() calls UpdateVisuals, so we don't need to call it ourselves after setting the shaderParms
614 renderEntity.shaderParms[SHADERPARM_RED] = 1.0f;
615 renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f;
616 renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f;
617 renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f;
618 renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
619 renderEntity.shaderParms[SHADERPARM_DIVERSITY] = 0.0f;
622 PostEventMS( &EV_Remove, 2000 );
624 ActivateTargets( activator );
629 ===============================================================================
633 ===============================================================================
636 CLASS_DECLARATION( idEntity, idSpring )
637 EVENT( EV_PostSpawn, idSpring::Event_LinkSpring )
645 void idSpring::Think( void ) {
646 idVec3 start, end, origin;
652 if ( thinkFlags & TH_THINK ) {
654 spring.Evaluate( gameLocal.time );
657 if ( ent1->GetPhysics() ) {
658 axis = ent1->GetPhysics()->GetAxis();
659 origin = ent1->GetPhysics()->GetOrigin();
660 start = origin + start * axis;
664 if ( ent2->GetPhysics() ) {
665 axis = ent2->GetPhysics()->GetAxis();
666 origin = ent2->GetPhysics()->GetOrigin();
667 end = origin + p2 * axis;
670 gameRenderWorld->DebugLine( idVec4(1, 1, 0, 1), start, end, 0, true );
678 idSpring::Event_LinkSpring
681 void idSpring::Event_LinkSpring( void ) {
684 spawnArgs.GetString( "ent1", "", name1 );
685 spawnArgs.GetString( "ent2", "", name2 );
687 if ( name1.Length() ) {
688 ent1 = gameLocal.FindEntity( name1 );
690 gameLocal.Error( "idSpring '%s' at (%s): cannot find first entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name1.c_str() );
694 ent1 = gameLocal.entities[ENTITYNUM_WORLD];
697 if ( name2.Length() ) {
698 ent2 = gameLocal.FindEntity( name2 );
700 gameLocal.Error( "idSpring '%s' at (%s): cannot find second entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name2.c_str() );
704 ent2 = gameLocal.entities[ENTITYNUM_WORLD];
706 spring.SetPosition( ent1->GetPhysics(), id1, p1, ent2->GetPhysics(), id2, p2 );
707 BecomeActive( TH_THINK );
715 void idSpring::Spawn( void ) {
716 float Kstretch, damping, restLength;
718 spawnArgs.GetInt( "id1", "0", id1 );
719 spawnArgs.GetInt( "id2", "0", id2 );
720 spawnArgs.GetVector( "point1", "0 0 0", p1 );
721 spawnArgs.GetVector( "point2", "0 0 0", p2 );
722 spawnArgs.GetFloat( "constant", "100.0f", Kstretch );
723 spawnArgs.GetFloat( "damping", "10.0f", damping );
724 spawnArgs.GetFloat( "restlength", "0.0f", restLength );
726 spring.InitSpring( Kstretch, 0.0f, damping, restLength );
730 PostEventMS( &EV_PostSpawn, 0 );
734 ===============================================================================
738 ===============================================================================
741 const idEventDef EV_Toggle( "Toggle", NULL );
743 CLASS_DECLARATION( idEntity, idForceField )
744 EVENT( EV_Activate, idForceField::Event_Activate )
745 EVENT( EV_Toggle, idForceField::Event_Toggle )
746 EVENT( EV_FindTargets, idForceField::Event_FindTargets )
754 void idForceField::Toggle( void ) {
755 if ( thinkFlags & TH_THINK ) {
756 BecomeInactive( TH_THINK );
758 BecomeActive( TH_THINK );
767 void idForceField::Think( void ) {
768 if ( thinkFlags & TH_THINK ) {
770 forceField.Evaluate( gameLocal.time );
780 void idForceField::Save( idSaveGame *savefile ) const {
781 savefile->WriteStaticObject( forceField );
786 idForceField::Restore
789 void idForceField::Restore( idRestoreGame *savefile ) {
790 savefile->ReadStaticObject( forceField );
798 void idForceField::Spawn( void ) {
800 float explosion, implosion, randomTorque;
802 if ( spawnArgs.GetVector( "uniform", "0 0 0", uniform ) ) {
803 forceField.Uniform( uniform );
804 } else if ( spawnArgs.GetFloat( "explosion", "0", explosion ) ) {
805 forceField.Explosion( explosion );
806 } else if ( spawnArgs.GetFloat( "implosion", "0", implosion ) ) {
807 forceField.Implosion( implosion );
810 if ( spawnArgs.GetFloat( "randomTorque", "0", randomTorque ) ) {
811 forceField.RandomTorque( randomTorque );
814 if ( spawnArgs.GetBool( "applyForce", "0" ) ) {
815 forceField.SetApplyType( FORCEFIELD_APPLY_FORCE );
816 } else if ( spawnArgs.GetBool( "applyImpulse", "0" ) ) {
817 forceField.SetApplyType( FORCEFIELD_APPLY_IMPULSE );
819 forceField.SetApplyType( FORCEFIELD_APPLY_VELOCITY );
822 forceField.SetPlayerOnly( spawnArgs.GetBool( "playerOnly", "0" ) );
823 forceField.SetMonsterOnly( spawnArgs.GetBool( "monsterOnly", "0" ) );
825 // set the collision model on the force field
826 forceField.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ) );
828 // remove the collision model from the physics object
829 GetPhysics()->SetClipModel( NULL, 1.0f );
831 if ( spawnArgs.GetBool( "start_on" ) ) {
832 BecomeActive( TH_THINK );
838 idForceField::Event_Toggle
841 void idForceField::Event_Toggle( void ) {
847 idForceField::Event_Activate
850 void idForceField::Event_Activate( idEntity *activator ) {
854 if ( spawnArgs.GetFloat( "wait", "0.01", wait ) ) {
855 PostEventSec( &EV_Toggle, wait );
861 idForceField::Event_FindTargets
864 void idForceField::Event_FindTargets( void ) {
867 if ( targets.Num() ) {
868 forceField.Uniform( targets[0].GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() );
874 ===============================================================================
878 ===============================================================================
881 const idEventDef EV_Animated_Start( "<start>" );
882 const idEventDef EV_LaunchMissiles( "launchMissiles", "ssssdf" );
883 const idEventDef EV_LaunchMissilesUpdate( "<launchMissiles>", "dddd" );
884 const idEventDef EV_AnimDone( "<AnimDone>", "d" );
885 const idEventDef EV_StartRagdoll( "startRagdoll" );
887 CLASS_DECLARATION( idAFEntity_Gibbable, idAnimated )
888 EVENT( EV_Activate, idAnimated::Event_Activate )
889 EVENT( EV_Animated_Start, idAnimated::Event_Start )
890 EVENT( EV_StartRagdoll, idAnimated::Event_StartRagdoll )
891 EVENT( EV_AnimDone, idAnimated::Event_AnimDone )
892 EVENT( EV_Footstep, idAnimated::Event_Footstep )
893 EVENT( EV_FootstepLeft, idAnimated::Event_Footstep )
894 EVENT( EV_FootstepRight, idAnimated::Event_Footstep )
895 EVENT( EV_LaunchMissiles, idAnimated::Event_LaunchMissiles )
896 EVENT( EV_LaunchMissilesUpdate, idAnimated::Event_LaunchMissilesUpdate )
901 idAnimated::idAnimated
904 idAnimated::idAnimated() {
907 soundJoint = INVALID_JOINT;
911 current_anim_index = 0;
918 idAnimated::idAnimated
921 idAnimated::~idAnimated() {
931 void idAnimated::Save( idSaveGame *savefile ) const {
932 savefile->WriteInt( current_anim_index );
933 savefile->WriteInt( num_anims );
934 savefile->WriteInt( anim );
935 savefile->WriteInt( blendFrames );
936 savefile->WriteJoint( soundJoint );
937 activator.Save( savefile );
938 savefile->WriteBool( activated );
946 void idAnimated::Restore( idRestoreGame *savefile ) {
947 savefile->ReadInt( current_anim_index );
948 savefile->ReadInt( num_anims );
949 savefile->ReadInt( anim );
950 savefile->ReadInt( blendFrames );
951 savefile->ReadJoint( soundJoint );
952 activator.Restore( savefile );
953 savefile->ReadBool( activated );
961 void idAnimated::Spawn( void ) {
967 joint = spawnArgs.GetString( "sound_bone", "origin" );
968 soundJoint = animator.GetJointHandle( joint );
969 if ( soundJoint == INVALID_JOINT ) {
970 gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), joint );
975 // allow bullets to collide with a combat model
976 if ( spawnArgs.GetBool( "combatModel", "0" ) ) {
977 combatModel = new idClipModel( modelDefHandle );
980 // allow the entity to take damage
981 if ( spawnArgs.GetBool( "takeDamage", "0" ) ) {
982 fl.takedamage = true;
987 current_anim_index = 0;
988 spawnArgs.GetInt( "num_anims", "0", num_anims );
990 blendFrames = spawnArgs.GetInt( "blend_in" );
992 animname = spawnArgs.GetString( num_anims ? "anim1" : "anim" );
993 if ( !animname.Length() ) {
996 anim = animator.GetAnim( animname );
998 gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
1002 if ( spawnArgs.GetBool( "hide" ) ) {
1008 } else if ( spawnArgs.GetString( "start_anim", "", animname ) ) {
1009 anim2 = animator.GetAnim( animname );
1011 gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
1013 animator.CycleAnim( ANIMCHANNEL_ALL, anim2, gameLocal.time, 0 );
1014 } else if ( anim ) {
1015 // init joints to the first frame of the animation
1016 animator.SetFrame( ANIMCHANNEL_ALL, anim, 1, gameLocal.time, 0 );
1023 spawnArgs.GetFloat( "wait", "-1", wait );
1026 PostEventSec( &EV_Activate, wait, this );
1035 bool idAnimated::LoadAF( void ) {
1038 if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) ) {
1041 af.SetAnimator( GetAnimator() );
1042 return af.Load( this, fileName );
1047 idAnimated::GetPhysicsToSoundTransform
1050 bool idAnimated::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
1051 animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
1052 axis = renderEntity.axis;
1058 idAnimated::StartRagdoll
1061 bool idAnimated::StartRagdoll( void ) {
1063 if ( !af.IsLoaded() ) {
1067 // if the AF is already active
1068 if ( af.IsActive() ) {
1072 // disable any collision model used
1073 GetPhysics()->DisableClip();
1075 // start using the AF
1076 af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
1082 =====================
1083 idAnimated::PlayNextAnim
1084 =====================
1086 void idAnimated::PlayNextAnim( void ) {
1087 const char *animname;
1091 if ( current_anim_index >= num_anims ) {
1093 if ( spawnArgs.GetBool( "remove" ) ) {
1094 PostEventMS( &EV_Remove, 0 );
1096 current_anim_index = 0;
1102 current_anim_index++;
1104 spawnArgs.GetString( va( "anim%d", current_anim_index ), NULL, &animname );
1107 animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, FRAME2MS( blendFrames ) );
1111 anim = animator.GetAnim( animname );
1113 gameLocal.Warning( "missing anim '%s' on %s", animname, name.c_str() );
1117 if ( g_debugCinematic.GetBool() ) {
1118 gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animname );
1121 spawnArgs.GetInt( "cycle", "1", cycle );
1122 if ( ( current_anim_index == num_anims ) && spawnArgs.GetBool( "loop_last_anim" ) ) {
1126 animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
1127 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
1129 len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
1131 PostEventMS( &EV_AnimDone, len, current_anim_index );
1134 // offset the start time of the shader to sync it to the game time
1135 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1137 animator.ForceUpdate();
1145 idAnimated::Event_StartRagdoll
1148 void idAnimated::Event_StartRagdoll( void ) {
1154 idAnimated::Event_AnimDone
1157 void idAnimated::Event_AnimDone( int animindex ) {
1158 if ( g_debugCinematic.GetBool() ) {
1159 const idAnim *animPtr = animator.GetAnim( anim );
1160 gameLocal.Printf( "%d: '%s' end anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
1163 if ( ( animindex >= num_anims ) && spawnArgs.GetBool( "remove" ) ) {
1165 PostEventMS( &EV_Remove, 0 );
1166 } else if ( spawnArgs.GetBool( "auto_advance" ) ) {
1172 ActivateTargets( activator.GetEntity() );
1177 idAnimated::Event_Activate
1180 void idAnimated::Event_Activate( idEntity *_activator ) {
1183 activator = _activator;
1188 // already activated
1193 activator = _activator;
1194 ProcessEvent( &EV_Animated_Start );
1199 idAnimated::Event_Start
1202 void idAnimated::Event_Start( void ) {
1214 if ( g_debugCinematic.GetBool() ) {
1215 const idAnim *animPtr = animator.GetAnim( anim );
1216 gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
1218 spawnArgs.GetInt( "cycle", "1", cycle );
1219 animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
1220 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
1222 len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
1224 PostEventMS( &EV_AnimDone, len, 1 );
1228 // offset the start time of the shader to sync it to the game time
1229 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1231 animator.ForceUpdate();
1239 idAnimated::Event_Footstep
1242 void idAnimated::Event_Footstep( void ) {
1243 StartSound( "snd_footstep", SND_CHANNEL_BODY, 0, false, NULL );
1247 =====================
1248 idAnimated::Event_LaunchMissilesUpdate
1249 =====================
1251 void idAnimated::Event_LaunchMissilesUpdate( int launchjoint, int targetjoint, int numshots, int framedelay ) {
1257 idProjectile * projectile;
1258 const idDict * projectileDef;
1259 const char * projectilename;
1261 projectilename = spawnArgs.GetString( "projectilename" );
1262 projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
1263 if ( !projectileDef ) {
1264 gameLocal.Warning( "idAnimated '%s' at (%s): 'launchMissiles' called with unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
1268 StartSound( "snd_missile", SND_CHANNEL_WEAPON, 0, false, NULL );
1270 animator.GetJointTransform( ( jointHandle_t )launchjoint, gameLocal.time, launchPos, axis );
1271 launchPos = renderEntity.origin + launchPos * renderEntity.axis;
1273 animator.GetJointTransform( ( jointHandle_t )targetjoint, gameLocal.time, targetPos, axis );
1274 targetPos = renderEntity.origin + targetPos * renderEntity.axis;
1276 dir = targetPos - launchPos;
1279 gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
1280 if ( !ent || !ent->IsType( idProjectile::Type ) ) {
1281 gameLocal.Error( "idAnimated '%s' at (%s): in 'launchMissiles' call '%s' is not an idProjectile", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
1283 projectile = ( idProjectile * )ent;
1284 projectile->Create( this, launchPos, dir );
1285 projectile->Launch( launchPos, dir, vec3_origin );
1287 if ( numshots > 0 ) {
1288 PostEventMS( &EV_LaunchMissilesUpdate, FRAME2MS( framedelay ), launchjoint, targetjoint, numshots - 1, framedelay );
1293 =====================
1294 idAnimated::Event_LaunchMissiles
1295 =====================
1297 void idAnimated::Event_LaunchMissiles( const char *projectilename, const char *sound, const char *launchjoint, const char *targetjoint, int numshots, int framedelay ) {
1298 const idDict * projectileDef;
1299 jointHandle_t launch;
1300 jointHandle_t target;
1302 projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
1303 if ( !projectileDef ) {
1304 gameLocal.Warning( "idAnimated '%s' at (%s): unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
1308 launch = animator.GetJointHandle( launchjoint );
1309 if ( launch == INVALID_JOINT ) {
1310 gameLocal.Warning( "idAnimated '%s' at (%s): unknown launch joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), launchjoint );
1311 gameLocal.Error( "Unknown joint '%s'", launchjoint );
1314 target = animator.GetJointHandle( targetjoint );
1315 if ( target == INVALID_JOINT ) {
1316 gameLocal.Warning( "idAnimated '%s' at (%s): unknown target joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), targetjoint );
1319 spawnArgs.Set( "projectilename", projectilename );
1320 spawnArgs.Set( "missilesound", sound );
1322 CancelEvents( &EV_LaunchMissilesUpdate );
1323 ProcessEvent( &EV_LaunchMissilesUpdate, launch, target, numshots - 1, framedelay );
1328 ===============================================================================
1332 Some static entities may be optimized into inline geometry by dmap
1334 ===============================================================================
1337 CLASS_DECLARATION( idEntity, idStaticEntity )
1338 EVENT( EV_Activate, idStaticEntity::Event_Activate )
1343 idStaticEntity::idStaticEntity
1346 idStaticEntity::idStaticEntity( void ) {
1349 fadeFrom.Set( 1, 1, 1, 1 );
1350 fadeTo.Set( 1, 1, 1, 1 );
1358 idStaticEntity::Save
1361 void idStaticEntity::Save( idSaveGame *savefile ) const {
1362 savefile->WriteInt( spawnTime );
1363 savefile->WriteBool( active );
1364 savefile->WriteVec4( fadeFrom );
1365 savefile->WriteVec4( fadeTo );
1366 savefile->WriteInt( fadeStart );
1367 savefile->WriteInt( fadeEnd );
1368 savefile->WriteBool( runGui );
1373 idStaticEntity::Restore
1376 void idStaticEntity::Restore( idRestoreGame *savefile ) {
1377 savefile->ReadInt( spawnTime );
1378 savefile->ReadBool( active );
1379 savefile->ReadVec4( fadeFrom );
1380 savefile->ReadVec4( fadeTo );
1381 savefile->ReadInt( fadeStart );
1382 savefile->ReadInt( fadeEnd );
1383 savefile->ReadBool( runGui );
1388 idStaticEntity::Spawn
1391 void idStaticEntity::Spawn( void ) {
1395 // an inline static model will not do anything at all
1396 if ( spawnArgs.GetBool( "inline" ) || gameLocal.world->spawnArgs.GetBool( "inlineAllStatics" ) ) {
1401 solid = spawnArgs.GetBool( "solid" );
1402 hidden = spawnArgs.GetBool( "hide" );
1404 if ( solid && !hidden ) {
1405 GetPhysics()->SetContents( CONTENTS_SOLID );
1407 GetPhysics()->SetContents( 0 );
1410 spawnTime = gameLocal.time;
1413 idStr model = spawnArgs.GetString( "model" );
1414 if ( model.Find( ".prt" ) >= 0 ) {
1415 // we want the parametric particles out of sync with each other
1416 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = gameLocal.random.RandomInt( 32767 );
1419 fadeFrom.Set( 1, 1, 1, 1 );
1420 fadeTo.Set( 1, 1, 1, 1 );
1424 // NOTE: this should be used very rarely because it is expensive
1425 runGui = spawnArgs.GetBool( "runGui" );
1427 BecomeActive( TH_THINK );
1433 idStaticEntity::ShowEditingDialog
1436 void idStaticEntity::ShowEditingDialog( void ) {
1437 common->InitTool( EDITOR_PARTICLE, &spawnArgs );
1441 idStaticEntity::Think
1444 void idStaticEntity::Think( void ) {
1446 if ( thinkFlags & TH_THINK ) {
1447 if ( runGui && renderEntity.gui[0] ) {
1448 idPlayer *player = gameLocal.GetLocalPlayer();
1450 if ( !player->objectiveSystemOpen ) {
1451 renderEntity.gui[0]->StateChanged( gameLocal.time, true );
1452 if ( renderEntity.gui[1] ) {
1453 renderEntity.gui[1]->StateChanged( gameLocal.time, true );
1455 if ( renderEntity.gui[2] ) {
1456 renderEntity.gui[2]->StateChanged( gameLocal.time, true );
1461 if ( fadeEnd > 0 ) {
1463 if ( gameLocal.time < fadeEnd ) {
1464 color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
1468 BecomeInactive( TH_THINK );
1477 idStaticEntity::Fade
1480 void idStaticEntity::Fade( const idVec4 &to, float fadeTime ) {
1481 GetColor( fadeFrom );
1483 fadeStart = gameLocal.time;
1484 fadeEnd = gameLocal.time + SEC2MS( fadeTime );
1485 BecomeActive( TH_THINK );
1490 idStaticEntity::Hide
1493 void idStaticEntity::Hide( void ) {
1495 GetPhysics()->SetContents( 0 );
1500 idStaticEntity::Show
1503 void idStaticEntity::Show( void ) {
1505 if ( spawnArgs.GetBool( "solid" ) ) {
1506 GetPhysics()->SetContents( CONTENTS_SOLID );
1512 idStaticEntity::Event_Activate
1515 void idStaticEntity::Event_Activate( idEntity *activator ) {
1518 spawnTime = gameLocal.time;
1521 const idKeyValue *kv = spawnArgs.FindKey( "hide" );
1530 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( spawnTime );
1531 renderEntity.shaderParms[5] = active;
1532 // this change should be a good thing, it will automatically turn on
1533 // lights etc.. when triggered so that does not have to be specifically done
1534 // with trigger parms.. it MIGHT break things so need to keep an eye on it
1535 renderEntity.shaderParms[ SHADERPARM_MODE ] = ( renderEntity.shaderParms[ SHADERPARM_MODE ] ) ? 0.0f : 1.0f;
1536 BecomeActive( TH_UPDATEVISUALS );
1541 idStaticEntity::WriteToSnapshot
1544 void idStaticEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
1545 GetPhysics()->WriteToSnapshot( msg );
1546 WriteBindToSnapshot( msg );
1547 WriteColorToSnapshot( msg );
1548 WriteGUIToSnapshot( msg );
1549 msg.WriteBits( IsHidden()?1:0, 1 );
1554 idStaticEntity::ReadFromSnapshot
1557 void idStaticEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1560 GetPhysics()->ReadFromSnapshot( msg );
1561 ReadBindFromSnapshot( msg );
1562 ReadColorFromSnapshot( msg );
1563 ReadGUIFromSnapshot( msg );
1564 hidden = msg.ReadBits( 1 ) == 1;
1565 if ( hidden != IsHidden() ) {
1572 if ( msg.HasChanged() ) {
1579 ===============================================================================
1583 ===============================================================================
1587 CLASS_DECLARATION( idStaticEntity, idFuncEmitter )
1588 EVENT( EV_Activate, idFuncEmitter::Event_Activate )
1593 idFuncEmitter::idFuncEmitter
1596 idFuncEmitter::idFuncEmitter( void ) {
1602 idFuncEmitter::Spawn
1605 void idFuncEmitter::Spawn( void ) {
1606 if ( spawnArgs.GetBool( "start_off" ) ) {
1608 renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( 1 );
1620 void idFuncEmitter::Save( idSaveGame *savefile ) const {
1621 savefile->WriteBool( hidden );
1626 idFuncEmitter::Restore
1629 void idFuncEmitter::Restore( idRestoreGame *savefile ) {
1630 savefile->ReadBool( hidden );
1635 idFuncEmitter::Event_Activate
1638 void idFuncEmitter::Event_Activate( idEntity *activator ) {
1639 if ( hidden || spawnArgs.GetBool( "cycleTrigger" ) ) {
1640 renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = 0;
1641 renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
1644 renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( gameLocal.time );
1652 idFuncEmitter::WriteToSnapshot
1655 void idFuncEmitter::WriteToSnapshot( idBitMsgDelta &msg ) const {
1656 msg.WriteBits( hidden ? 1 : 0, 1 );
1657 msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] );
1658 msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
1663 idFuncEmitter::ReadFromSnapshot
1666 void idFuncEmitter::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1667 hidden = msg.ReadBits( 1 ) != 0;
1668 renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat();
1669 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat();
1670 if ( msg.HasChanged() ) {
1677 ===============================================================================
1681 ===============================================================================
1685 const idEventDef EV_Splat( "<Splat>" );
1686 CLASS_DECLARATION( idFuncEmitter, idFuncSplat )
1687 EVENT( EV_Activate, idFuncSplat::Event_Activate )
1688 EVENT( EV_Splat, idFuncSplat::Event_Splat )
1693 idFuncSplat::idFuncSplat
1696 idFuncSplat::idFuncSplat( void ) {
1704 void idFuncSplat::Spawn( void ) {
1709 idFuncSplat::Event_Splat
1712 void idFuncSplat::Event_Splat( void ) {
1713 const char *splat = NULL;
1714 int count = spawnArgs.GetInt( "splatCount", "1" );
1715 for ( int i = 0; i < count; i++ ) {
1716 splat = spawnArgs.RandomPrefix( "mtr_splat", gameLocal.random );
1717 if ( splat && *splat ) {
1718 float size = spawnArgs.GetFloat( "splatSize", "128" );
1719 float dist = spawnArgs.GetFloat( "splatDistance", "128" );
1720 float angle = spawnArgs.GetFloat( "splatAngle", "0" );
1721 gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis()[2], dist, true, size, splat, angle );
1724 StartSound( "snd_splat", SND_CHANNEL_ANY, 0, false, NULL );
1729 idFuncSplat::Event_Activate
1732 void idFuncSplat::Event_Activate( idEntity *activator ) {
1733 idFuncEmitter::Event_Activate( activator );
1734 PostEventSec( &EV_Splat, spawnArgs.GetFloat( "splatDelay", "0.25" ) );
1735 StartSound( "snd_spurt", SND_CHANNEL_ANY, 0, false, NULL );
1739 ===============================================================================
1743 ===============================================================================
1746 CLASS_DECLARATION( idEntity, idFuncSmoke )
1747 EVENT( EV_Activate, idFuncSmoke::Event_Activate )
1752 idFuncSmoke::idFuncSmoke
1755 idFuncSmoke::idFuncSmoke() {
1766 void idFuncSmoke::Save( idSaveGame *savefile ) const {
1767 savefile->WriteInt( smokeTime );
1768 savefile->WriteParticle( smoke );
1769 savefile->WriteBool( restart );
1774 idFuncSmoke::Restore
1777 void idFuncSmoke::Restore( idRestoreGame *savefile ) {
1778 savefile->ReadInt( smokeTime );
1779 savefile->ReadParticle( smoke );
1780 savefile->ReadBool( restart );
1788 void idFuncSmoke::Spawn( void ) {
1789 const char *smokeName = spawnArgs.GetString( "smoke" );
1790 if ( *smokeName != '\0' ) {
1791 smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1795 if ( spawnArgs.GetBool( "start_off" ) ) {
1798 } else if ( smoke ) {
1799 smokeTime = gameLocal.time;
1800 BecomeActive( TH_UPDATEPARTICLES );
1803 GetPhysics()->SetContents( 0 );
1808 idFuncSmoke::Event_Activate
1811 void idFuncSmoke::Event_Activate( idEntity *activator ) {
1812 if ( thinkFlags & TH_UPDATEPARTICLES ) {
1816 BecomeActive( TH_UPDATEPARTICLES );
1818 smokeTime = gameLocal.time;
1827 void idFuncSmoke::Think( void ) {
1829 // if we are completely closed off from the player, don't do anything at all
1830 if ( CheckDormant() || smoke == NULL || smokeTime == -1 ) {
1834 if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) {
1835 if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
1837 smokeTime = gameLocal.time;
1840 BecomeInactive( TH_UPDATEPARTICLES );
1849 ===============================================================================
1853 ===============================================================================
1856 CLASS_DECLARATION( idEntity, idTextEntity )
1864 void idTextEntity::Spawn( void ) {
1865 // these are cached as the are used each frame
1866 text = spawnArgs.GetString( "text" );
1867 playerOriented = spawnArgs.GetBool( "playerOriented" );
1868 bool force = spawnArgs.GetBool( "force" );
1869 if ( developer.GetBool() || force ) {
1870 BecomeActive(TH_THINK);
1879 void idTextEntity::Save( idSaveGame *savefile ) const {
1880 savefile->WriteString( text );
1881 savefile->WriteBool( playerOriented );
1886 idTextEntity::Restore
1889 void idTextEntity::Restore( idRestoreGame *savefile ) {
1890 savefile->ReadString( text );
1891 savefile->ReadBool( playerOriented );
1899 void idTextEntity::Think( void ) {
1900 if ( thinkFlags & TH_THINK ) {
1901 gameRenderWorld->DrawText( text, GetPhysics()->GetOrigin(), 0.25, colorWhite, playerOriented ? gameLocal.GetLocalPlayer()->viewAngles.ToMat3() : GetPhysics()->GetAxis().Transpose(), 1 );
1902 for ( int i = 0; i < targets.Num(); i++ ) {
1903 if ( targets[i].GetEntity() ) {
1904 gameRenderWorld->DebugArrow( colorBlue, GetPhysics()->GetOrigin(), targets[i].GetEntity()->GetPhysics()->GetOrigin(), 1 );
1908 BecomeInactive( TH_ALL );
1914 ===============================================================================
1916 idVacuumSeperatorEntity
1918 Can be triggered to let vacuum through a portal (blown out window)
1920 ===============================================================================
1923 CLASS_DECLARATION( idEntity, idVacuumSeparatorEntity )
1924 EVENT( EV_Activate, idVacuumSeparatorEntity::Event_Activate )
1930 idVacuumSeparatorEntity::idVacuumSeparatorEntity
1933 idVacuumSeparatorEntity::idVacuumSeparatorEntity( void ) {
1939 idVacuumSeparatorEntity::Save
1942 void idVacuumSeparatorEntity::Save( idSaveGame *savefile ) const {
1943 savefile->WriteInt( (int)portal );
1944 savefile->WriteInt( gameRenderWorld->GetPortalState( portal ) );
1949 idVacuumSeparatorEntity::Restore
1952 void idVacuumSeparatorEntity::Restore( idRestoreGame *savefile ) {
1955 savefile->ReadInt( (int &)portal );
1956 savefile->ReadInt( state );
1958 gameLocal.SetPortalState( portal, state );
1963 idVacuumSeparatorEntity::Spawn
1966 void idVacuumSeparatorEntity::Spawn() {
1969 b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
1970 portal = gameRenderWorld->FindPortal( b );
1972 gameLocal.Warning( "VacuumSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
1975 gameLocal.SetPortalState( portal, PS_BLOCK_AIR | PS_BLOCK_LOCATION );
1980 idVacuumSeparatorEntity::Event_Activate
1983 void idVacuumSeparatorEntity::Event_Activate( idEntity *activator ) {
1987 gameLocal.SetPortalState( portal, PS_BLOCK_NONE );
1992 ===============================================================================
1994 idLocationSeparatorEntity
1996 ===============================================================================
1999 CLASS_DECLARATION( idEntity, idLocationSeparatorEntity )
2004 idLocationSeparatorEntity::Spawn
2007 void idLocationSeparatorEntity::Spawn() {
2010 b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
2011 qhandle_t portal = gameRenderWorld->FindPortal( b );
2013 gameLocal.Warning( "LocationSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
2015 gameLocal.SetPortalState( portal, PS_BLOCK_LOCATION );
2020 ===============================================================================
2024 Levels should only have a single vacuum entity.
2026 ===============================================================================
2029 CLASS_DECLARATION( idEntity, idVacuumEntity )
2034 idVacuumEntity::Spawn
2037 void idVacuumEntity::Spawn() {
2038 if ( gameLocal.vacuumAreaNum != -1 ) {
2039 gameLocal.Warning( "idVacuumEntity::Spawn: multiple idVacuumEntity in level" );
2043 idVec3 org = spawnArgs.GetVector( "origin" );
2045 gameLocal.vacuumAreaNum = gameRenderWorld->PointInArea( org );
2050 ===============================================================================
2054 ===============================================================================
2057 CLASS_DECLARATION( idEntity, idLocationEntity )
2061 ======================
2062 idLocationEntity::Spawn
2063 ======================
2065 void idLocationEntity::Spawn() {
2068 // this just holds dict information
2070 // if "location" not already set, use the entity name.
2071 if ( !spawnArgs.GetString( "location", "", realName ) ) {
2072 spawnArgs.Set( "location", name );
2077 ======================
2078 idLocationEntity::GetLocation
2079 ======================
2081 const char *idLocationEntity::GetLocation( void ) const {
2082 return spawnArgs.GetString( "location" );
2086 ===============================================================================
2090 ===============================================================================
2093 CLASS_DECLARATION( idEntity, idBeam )
2094 EVENT( EV_PostSpawn, idBeam::Event_MatchTarget )
2095 EVENT( EV_Activate, idBeam::Event_Activate )
2113 void idBeam::Save( idSaveGame *savefile ) const {
2114 target.Save( savefile );
2115 master.Save( savefile );
2123 void idBeam::Restore( idRestoreGame *savefile ) {
2124 target.Restore( savefile );
2125 master.Restore( savefile );
2133 void idBeam::Spawn( void ) {
2136 if ( spawnArgs.GetFloat( "width", "0", width ) ) {
2137 renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = width;
2140 SetModel( "_BEAM" );
2142 PostEventMS( &EV_PostSpawn, 0 );
2150 void idBeam::Think( void ) {
2153 if ( !IsHidden() && !target.GetEntity() ) {
2154 // hide if our target is removed
2160 masterEnt = master.GetEntity();
2162 const idVec3 &origin = GetPhysics()->GetOrigin();
2163 masterEnt->SetBeamTarget( origin );
2173 void idBeam::SetMaster( idBeam *masterbeam ) {
2174 master = masterbeam;
2179 idBeam::SetBeamTarget
2182 void idBeam::SetBeamTarget( const idVec3 &origin ) {
2183 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 ) ) {
2184 renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] = origin.x;
2185 renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] = origin.y;
2186 renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] = origin.z;
2196 void idBeam::Show( void ) {
2201 targetEnt = target.GetEntity();
2203 const idVec3 &origin = targetEnt->GetPhysics()->GetOrigin();
2204 SetBeamTarget( origin );
2210 idBeam::Event_MatchTarget
2213 void idBeam::Event_MatchTarget( void ) {
2215 idEntity *targetEnt;
2218 if ( !targets.Num() ) {
2223 for( i = 0; i < targets.Num(); i++ ) {
2224 targetEnt = targets[ i ].GetEntity();
2225 if ( targetEnt && targetEnt->IsType( idBeam::Type ) ) {
2226 targetBeam = static_cast<idBeam *>( targetEnt );
2231 if ( !targetBeam ) {
2232 gameLocal.Error( "Could not find valid beam target for '%s'", name.c_str() );
2235 target = targetBeam;
2236 targetBeam->SetMaster( this );
2237 if ( !spawnArgs.GetBool( "start_off" ) ) {
2244 idBeam::Event_Activate
2247 void idBeam::Event_Activate( idEntity *activator ) {
2257 idBeam::WriteToSnapshot
2260 void idBeam::WriteToSnapshot( idBitMsgDelta &msg ) const {
2261 GetPhysics()->WriteToSnapshot( msg );
2262 WriteBindToSnapshot( msg );
2263 WriteColorToSnapshot( msg );
2264 msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
2265 msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] );
2266 msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] );
2271 idBeam::ReadFromSnapshot
2274 void idBeam::ReadFromSnapshot( const idBitMsgDelta &msg ) {
2275 GetPhysics()->ReadFromSnapshot( msg );
2276 ReadBindFromSnapshot( msg );
2277 ReadColorFromSnapshot( msg );
2278 renderEntity.shaderParms[SHADERPARM_BEAM_END_X] = msg.ReadFloat();
2279 renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] = msg.ReadFloat();
2280 renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] = msg.ReadFloat();
2281 if ( msg.HasChanged() ) {
2288 ===============================================================================
2292 ===============================================================================
2295 CLASS_DECLARATION( idEntity, idLiquid )
2296 EVENT( EV_Touch, idLiquid::Event_Touch )
2304 void idLiquid::Save( idSaveGame *savefile ) const {
2313 void idLiquid::Restore( idRestoreGame *savefile ) {
2323 void idLiquid::Spawn() {
2325 model = dynamic_cast<idRenderModelLiquid *>( renderEntity.hModel );
2327 gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() );
2330 GetPhysics()->SetContents( CONTENTS_TRIGGER );
2336 idLiquid::Event_Touch
2339 void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) {
2340 // FIXME: for QuakeCon
2344 pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
2345 model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f );
2351 ===============================================================================
2355 ===============================================================================
2358 CLASS_DECLARATION( idEntity, idShaking )
2359 EVENT( EV_Activate, idShaking::Event_Activate )
2364 idShaking::idShaking
2367 idShaking::idShaking() {
2376 void idShaking::Save( idSaveGame *savefile ) const {
2377 savefile->WriteBool( active );
2378 savefile->WriteStaticObject( physicsObj );
2386 void idShaking::Restore( idRestoreGame *savefile ) {
2387 savefile->ReadBool( active );
2388 savefile->ReadStaticObject( physicsObj );
2389 RestorePhysics( &physicsObj );
2397 void idShaking::Spawn( void ) {
2398 physicsObj.SetSelf( this );
2399 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
2400 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
2401 physicsObj.SetAxis( GetPhysics()->GetAxis() );
2402 physicsObj.SetClipMask( MASK_SOLID );
2403 SetPhysics( &physicsObj );
2406 if ( !spawnArgs.GetBool( "start_off" ) ) {
2413 idShaking::BeginShaking
2416 void idShaking::BeginShaking( void ) {
2422 phase = gameLocal.random.RandomInt( 1000 );
2423 shake = spawnArgs.GetAngles( "shake", "0.5 0.5 0.5" );
2424 period = spawnArgs.GetFloat( "period", "0.05" ) * 1000;
2425 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase, period * 0.25f, GetPhysics()->GetAxis().ToAngles(), shake, ang_zero );
2430 idShaking::Event_Activate
2433 void idShaking::Event_Activate( idEntity *activator ) {
2438 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, physicsObj.GetAxis().ToAngles(), ang_zero, ang_zero );
2443 ===============================================================================
2447 ===============================================================================
2450 CLASS_DECLARATION( idEntity, idEarthQuake )
2451 EVENT( EV_Activate, idEarthQuake::Event_Activate )
2456 idEarthQuake::idEarthQuake
2459 idEarthQuake::idEarthQuake() {
2462 nextTriggerTime = 0;
2465 playerOriented = false;
2475 void idEarthQuake::Save( idSaveGame *savefile ) const {
2476 savefile->WriteInt( nextTriggerTime );
2477 savefile->WriteInt( shakeStopTime );
2478 savefile->WriteFloat( wait );
2479 savefile->WriteFloat( random );
2480 savefile->WriteBool( triggered );
2481 savefile->WriteBool( playerOriented );
2482 savefile->WriteBool( disabled );
2483 savefile->WriteFloat( shakeTime );
2488 idEarthQuake::Restore
2491 void idEarthQuake::Restore( idRestoreGame *savefile ) {
2492 savefile->ReadInt( nextTriggerTime );
2493 savefile->ReadInt( shakeStopTime );
2494 savefile->ReadFloat( wait );
2495 savefile->ReadFloat( random );
2496 savefile->ReadBool( triggered );
2497 savefile->ReadBool( playerOriented );
2498 savefile->ReadBool( disabled );
2499 savefile->ReadFloat( shakeTime );
2501 if ( shakeStopTime > gameLocal.time ) {
2502 BecomeActive( TH_THINK );
2511 void idEarthQuake::Spawn( void ) {
2512 nextTriggerTime = 0;
2514 wait = spawnArgs.GetFloat( "wait", "15" );
2515 random = spawnArgs.GetFloat( "random", "5" );
2516 triggered = spawnArgs.GetBool( "triggered" );
2517 playerOriented = spawnArgs.GetBool( "playerOriented" );
2519 shakeTime = spawnArgs.GetFloat( "shakeTime", "0" );
2522 PostEventSec( &EV_Activate, spawnArgs.GetFloat( "wait" ), this );
2524 BecomeInactive( TH_THINK );
2529 idEarthQuake::Event_Activate
2532 void idEarthQuake::Event_Activate( idEntity *activator ) {
2534 if ( nextTriggerTime > gameLocal.time ) {
2538 if ( disabled && activator == this ) {
2542 idPlayer *player = gameLocal.GetLocalPlayer();
2543 if ( player == NULL ) {
2547 nextTriggerTime = 0;
2549 if ( !triggered && activator != this ){
2550 // if we are not triggered ( i.e. random ), disable or enable
2555 PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
2559 ActivateTargets( activator );
2561 const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_quake" ) );
2562 if ( playerOriented ) {
2563 player->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
2565 StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
2568 if ( shakeTime > 0.0f ) {
2569 shakeStopTime = gameLocal.time + SEC2MS( shakeTime );
2570 BecomeActive( TH_THINK );
2573 if ( wait > 0.0f ) {
2575 PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
2577 nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
2579 } else if ( shakeTime == 0.0f ) {
2580 PostEventMS( &EV_Remove, 0 );
2590 void idEarthQuake::Think( void ) {
2591 if ( thinkFlags & TH_THINK ) {
2592 if ( gameLocal.time > shakeStopTime ) {
2593 BecomeInactive( TH_THINK );
2594 if ( wait <= 0.0f ) {
2595 PostEventMS( &EV_Remove, 0 );
2599 float shakeVolume = gameSoundWorld->CurrentShakeAmplitudeForPosition( gameLocal.time, gameLocal.GetLocalPlayer()->firstPersonViewOrigin );
2600 gameLocal.RadiusPush( GetPhysics()->GetOrigin(), 256, 1500 * shakeVolume, this, this, 1.0f, true );
2602 BecomeInactive( TH_UPDATEVISUALS );
2606 ===============================================================================
2610 ===============================================================================
2613 CLASS_DECLARATION( idEntity, idFuncPortal )
2614 EVENT( EV_Activate, idFuncPortal::Event_Activate )
2619 idFuncPortal::idFuncPortal
2622 idFuncPortal::idFuncPortal() {
2632 void idFuncPortal::Save( idSaveGame *savefile ) const {
2633 savefile->WriteInt( (int)portal );
2634 savefile->WriteBool( state );
2639 idFuncPortal::Restore
2642 void idFuncPortal::Restore( idRestoreGame *savefile ) {
2643 savefile->ReadInt( (int &)portal );
2644 savefile->ReadBool( state );
2645 gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
2653 void idFuncPortal::Spawn( void ) {
2654 portal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds().Expand( 32.0f ) );
2656 state = spawnArgs.GetBool( "start_on" );
2657 gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
2663 idFuncPortal::Event_Activate
2666 void idFuncPortal::Event_Activate( idEntity *activator ) {
2669 gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
2674 ===============================================================================
2678 ===============================================================================
2681 CLASS_DECLARATION( idEntity, idFuncAASPortal )
2682 EVENT( EV_Activate, idFuncAASPortal::Event_Activate )
2687 idFuncAASPortal::idFuncAASPortal
2690 idFuncAASPortal::idFuncAASPortal() {
2696 idFuncAASPortal::Save
2699 void idFuncAASPortal::Save( idSaveGame *savefile ) const {
2700 savefile->WriteBool( state );
2705 idFuncAASPortal::Restore
2708 void idFuncAASPortal::Restore( idRestoreGame *savefile ) {
2709 savefile->ReadBool( state );
2710 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
2715 idFuncAASPortal::Spawn
2718 void idFuncAASPortal::Spawn( void ) {
2719 state = spawnArgs.GetBool( "start_on" );
2720 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
2725 idFuncAASPortal::Event_Activate
2728 void idFuncAASPortal::Event_Activate( idEntity *activator ) {
2730 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
2734 ===============================================================================
2738 ===============================================================================
2741 CLASS_DECLARATION( idEntity, idFuncAASObstacle )
2742 EVENT( EV_Activate, idFuncAASObstacle::Event_Activate )
2747 idFuncAASObstacle::idFuncAASObstacle
2750 idFuncAASObstacle::idFuncAASObstacle() {
2756 idFuncAASObstacle::Save
2759 void idFuncAASObstacle::Save( idSaveGame *savefile ) const {
2760 savefile->WriteBool( state );
2765 idFuncAASObstacle::Restore
2768 void idFuncAASObstacle::Restore( idRestoreGame *savefile ) {
2769 savefile->ReadBool( state );
2770 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
2775 idFuncAASObstacle::Spawn
2778 void idFuncAASObstacle::Spawn( void ) {
2779 state = spawnArgs.GetBool( "start_on" );
2780 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
2785 idFuncAASObstacle::Event_Activate
2788 void idFuncAASObstacle::Event_Activate( idEntity *activator ) {
2790 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
2796 ===============================================================================
2800 ===============================================================================
2803 const idEventDef EV_ResetRadioHud( "<resetradiohud>", "e" );
2806 CLASS_DECLARATION( idEntity, idFuncRadioChatter )
2807 EVENT( EV_Activate, idFuncRadioChatter::Event_Activate )
2808 EVENT( EV_ResetRadioHud, idFuncRadioChatter::Event_ResetRadioHud )
2813 idFuncRadioChatter::idFuncRadioChatter
2816 idFuncRadioChatter::idFuncRadioChatter() {
2822 idFuncRadioChatter::Save
2825 void idFuncRadioChatter::Save( idSaveGame *savefile ) const {
2826 savefile->WriteFloat( time );
2831 idFuncRadioChatter::Restore
2834 void idFuncRadioChatter::Restore( idRestoreGame *savefile ) {
2835 savefile->ReadFloat( time );
2840 idFuncRadioChatter::Spawn
2843 void idFuncRadioChatter::Spawn( void ) {
2844 time = spawnArgs.GetFloat( "time", "5.0" );
2849 idFuncRadioChatter::Event_Activate
2852 void idFuncRadioChatter::Event_Activate( idEntity *activator ) {
2855 const idSoundShader *shader;
2858 if ( activator->IsType( idPlayer::Type ) ) {
2859 player = static_cast<idPlayer *>( activator );
2861 player = gameLocal.GetLocalPlayer();
2864 player->hud->HandleNamedEvent( "radioChatterUp" );
2866 sound = spawnArgs.GetString( "snd_radiochatter", "" );
2867 if ( sound && *sound ) {
2868 shader = declManager->FindSound( sound );
2869 player->StartSoundShader( shader, SND_CHANNEL_RADIO, SSF_GLOBAL, false, &length );
2870 time = MS2SEC( length + 150 );
2872 // we still put the hud up because this is used with no sound on
2873 // certain frame commands when the chatter is triggered
2874 PostEventSec( &EV_ResetRadioHud, time, player );
2880 idFuncRadioChatter::Event_ResetRadioHud
2883 void idFuncRadioChatter::Event_ResetRadioHud( idEntity *activator ) {
2884 idPlayer *player = ( activator->IsType( idPlayer::Type ) ) ? static_cast<idPlayer *>( activator ) : gameLocal.GetLocalPlayer();
2885 player->hud->HandleNamedEvent( "radioChatterDown" );
2886 ActivateTargets( activator );
2891 ===============================================================================
2895 ===============================================================================
2898 CLASS_DECLARATION( idEntity, idPhantomObjects )
2899 EVENT( EV_Activate, idPhantomObjects::Event_Activate )
2904 idPhantomObjects::idPhantomObjects
2907 idPhantomObjects::idPhantomObjects() {
2916 fl.neverDormant = false;
2921 idPhantomObjects::Save
2924 void idPhantomObjects::Save( idSaveGame *savefile ) const {
2927 savefile->WriteInt( end_time );
2928 savefile->WriteFloat( throw_time );
2929 savefile->WriteFloat( shake_time );
2930 savefile->WriteVec3( shake_ang );
2931 savefile->WriteFloat( speed );
2932 savefile->WriteInt( min_wait );
2933 savefile->WriteInt( max_wait );
2934 target.Save( savefile );
2935 savefile->WriteInt( targetTime.Num() );
2936 for( i = 0; i < targetTime.Num(); i++ ) {
2937 savefile->WriteInt( targetTime[ i ] );
2940 for( i = 0; i < lastTargetPos.Num(); i++ ) {
2941 savefile->WriteVec3( lastTargetPos[ i ] );
2947 idPhantomObjects::Restore
2950 void idPhantomObjects::Restore( idRestoreGame *savefile ) {
2954 savefile->ReadInt( end_time );
2955 savefile->ReadFloat( throw_time );
2956 savefile->ReadFloat( shake_time );
2957 savefile->ReadVec3( shake_ang );
2958 savefile->ReadFloat( speed );
2959 savefile->ReadInt( min_wait );
2960 savefile->ReadInt( max_wait );
2961 target.Restore( savefile );
2963 savefile->ReadInt( num );
2964 targetTime.SetGranularity( 1 );
2965 targetTime.SetNum( num );
2966 lastTargetPos.SetGranularity( 1 );
2967 lastTargetPos.SetNum( num );
2969 for( i = 0; i < num; i++ ) {
2970 savefile->ReadInt( targetTime[ i ] );
2973 if ( savefile->GetBuildNumber() == INITIAL_RELEASE_BUILD_NUMBER ) {
2974 // these weren't saved out in the first release
2975 for( i = 0; i < num; i++ ) {
2976 lastTargetPos[ i ].Zero();
2979 for( i = 0; i < num; i++ ) {
2980 savefile->ReadVec3( lastTargetPos[ i ] );
2987 idPhantomObjects::Spawn
2990 void idPhantomObjects::Spawn( void ) {
2991 throw_time = spawnArgs.GetFloat( "time", "5" );
2992 speed = spawnArgs.GetFloat( "speed", "1200" );
2993 shake_time = spawnArgs.GetFloat( "shake_time", "1" );
2994 throw_time -= shake_time;
2995 if ( throw_time < 0.0f ) {
2998 min_wait = SEC2MS( spawnArgs.GetFloat( "min_wait", "1" ) );
2999 max_wait = SEC2MS( spawnArgs.GetFloat( "max_wait", "3" ) );
3001 shake_ang = spawnArgs.GetVector( "shake_ang", "65 65 65" );
3003 GetPhysics()->SetContents( 0 );
3008 idPhantomObjects::Event_Activate
3011 void idPhantomObjects::Event_Activate( idEntity *activator ) {
3017 if ( thinkFlags & TH_THINK ) {
3018 BecomeInactive( TH_THINK );
3022 RemoveNullTargets();
3023 if ( !targets.Num() ) {
3027 if ( !activator || !activator->IsType( idActor::Type ) ) {
3028 target = gameLocal.GetLocalPlayer();
3030 target = static_cast<idActor *>( activator );
3033 end_time = gameLocal.time + SEC2MS( spawnArgs.GetFloat( "end_time", "0" ) );
3035 targetTime.SetNum( targets.Num() );
3036 lastTargetPos.SetNum( targets.Num() );
3038 const idVec3 &toPos = target.GetEntity()->GetEyePosition();
3040 // calculate the relative times of all the objects
3042 for( i = 0; i < targetTime.Num(); i++ ) {
3043 targetTime[ i ] = SEC2MS( time );
3044 lastTargetPos[ i ] = toPos;
3046 frac = 1.0f - ( float )i / ( float )targetTime.Num();
3047 time += ( gameLocal.random.RandomFloat() + 1.0f ) * 0.5f * frac + 0.1f;
3050 // scale up the times to fit within throw_time
3051 scale = throw_time / time;
3052 for( i = 0; i < targetTime.Num(); i++ ) {
3053 targetTime[ i ] = gameLocal.time + SEC2MS( shake_time )+ targetTime[ i ] * scale;
3056 BecomeActive( TH_THINK );
3061 idPhantomObjects::Think
3064 void idPhantomObjects::Think( void ) {
3075 // if we are completely closed off from the player, don't do anything at all
3076 if ( CheckDormant() ) {
3080 if ( !( thinkFlags & TH_THINK ) ) {
3081 BecomeInactive( thinkFlags & ~TH_THINK );
3085 targetEnt = target.GetEntity();
3086 if ( !targetEnt || ( targetEnt->health <= 0 ) || ( end_time && ( gameLocal.time > end_time ) ) || gameLocal.inCinematic ) {
3087 BecomeInactive( TH_THINK );
3090 const idVec3 &toPos = targetEnt->GetEyePosition();
3093 for ( i = 0; i < targets.Num(); i++ ) {
3094 ent = targets[ i ].GetEntity();
3099 if ( ent->fl.hidden ) {
3100 // don't throw hidden objects
3104 if ( !targetTime[ i ] ) {
3105 // already threw this object
3111 time = MS2SEC( targetTime[ i ] - gameLocal.time );
3112 if ( time > shake_time ) {
3116 entPhys = ent->GetPhysics();
3117 const idVec3 &entOrg = entPhys->GetOrigin();
3119 gameLocal.clip.TracePoint( tr, entOrg, toPos, MASK_OPAQUE, ent );
3120 if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == targetEnt ) ) {
3121 lastTargetPos[ i ] = toPos;
3124 if ( time < 0.0f ) {
3125 idAI::PredictTrajectory( entPhys->GetOrigin(), lastTargetPos[ i ], speed, entPhys->GetGravity(),
3126 entPhys->GetClipModel(), entPhys->GetClipMask(), 256.0f, ent, targetEnt, ai_debugTrajectory.GetBool() ? 1 : 0, vel );
3128 entPhys->SetLinearVelocity( vel );
3130 targetTime[ i ] = 0;
3132 targetTime[ i ] = gameLocal.time + gameLocal.random.RandomInt( max_wait - min_wait ) + min_wait;
3134 if ( ent->IsType( idMoveable::Type ) ) {
3135 idMoveable *ment = static_cast<idMoveable*>( ent );
3136 ment->EnableDamage( true, 2.5f );
3139 // this is not the right way to set the angular velocity, but the effect is nice, so I'm keeping it. :)
3140 ang.Set( gameLocal.random.CRandomFloat() * shake_ang.x, gameLocal.random.CRandomFloat() * shake_ang.y, gameLocal.random.CRandomFloat() * shake_ang.z );
3141 ang *= ( 1.0f - time / shake_time );
3142 entPhys->SetAngularVelocity( ang );
3147 BecomeInactive( TH_THINK );