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 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
34 // _D3XP : rename all gameLocal.time to gameLocal.slow.time for merge!
36 // a mover will update any gui entities in it's target list with
37 // a key/val pair of "mover" "state" from below.. guis can represent
38 // realtime info like this
40 static const char *guiBinaryMoverStates[] = {
49 ===============================================================================
53 ===============================================================================
56 const idEventDef EV_FindGuiTargets( "<FindGuiTargets>", NULL );
57 const idEventDef EV_TeamBlocked( "<teamblocked>", "ee" );
58 const idEventDef EV_PartBlocked( "<partblocked>", "e" );
59 const idEventDef EV_ReachedPos( "<reachedpos>", NULL );
60 const idEventDef EV_ReachedAng( "<reachedang>", NULL );
61 const idEventDef EV_PostRestore( "<postrestore>", "ddddd" );
62 const idEventDef EV_StopMoving( "stopMoving", NULL );
63 const idEventDef EV_StopRotating( "stopRotating", NULL );
64 const idEventDef EV_Speed( "speed", "f" );
65 const idEventDef EV_Time( "time", "f" );
66 const idEventDef EV_AccelTime( "accelTime", "f" );
67 const idEventDef EV_DecelTime( "decelTime", "f" );
68 const idEventDef EV_MoveTo( "moveTo", "e" );
69 const idEventDef EV_MoveToPos( "moveToPos", "v" );
70 const idEventDef EV_Move( "move", "ff" );
71 const idEventDef EV_MoveAccelerateTo( "accelTo", "ff" );
72 const idEventDef EV_MoveDecelerateTo( "decelTo", "ff" );
73 const idEventDef EV_RotateDownTo( "rotateDownTo", "df" );
74 const idEventDef EV_RotateUpTo( "rotateUpTo", "df" );
75 const idEventDef EV_RotateTo( "rotateTo", "v" );
76 const idEventDef EV_Rotate( "rotate", "v" );
77 const idEventDef EV_RotateOnce( "rotateOnce", "v" );
78 const idEventDef EV_Bob( "bob", "ffv" );
79 const idEventDef EV_Sway( "sway", "ffv" );
80 const idEventDef EV_Mover_OpenPortal( "openPortal" );
81 const idEventDef EV_Mover_ClosePortal( "closePortal" );
82 const idEventDef EV_AccelSound( "accelSound", "s" );
83 const idEventDef EV_DecelSound( "decelSound", "s" );
84 const idEventDef EV_MoveSound( "moveSound", "s" );
85 const idEventDef EV_Mover_InitGuiTargets( "<initguitargets>", NULL );
86 const idEventDef EV_EnableSplineAngles( "enableSplineAngles", NULL );
87 const idEventDef EV_DisableSplineAngles( "disableSplineAngles", NULL );
88 const idEventDef EV_RemoveInitialSplineAngles( "removeInitialSplineAngles", NULL );
89 const idEventDef EV_StartSpline( "startSpline", "e" );
90 const idEventDef EV_StopSpline( "stopSpline", NULL );
91 const idEventDef EV_IsMoving( "isMoving", NULL, 'd' );
92 const idEventDef EV_IsRotating( "isRotating", NULL, 'd' );
94 CLASS_DECLARATION( idEntity, idMover )
95 EVENT( EV_FindGuiTargets, idMover::Event_FindGuiTargets )
96 EVENT( EV_Thread_SetCallback, idMover::Event_SetCallback )
97 EVENT( EV_TeamBlocked, idMover::Event_TeamBlocked )
98 EVENT( EV_PartBlocked, idMover::Event_PartBlocked )
99 EVENT( EV_ReachedPos, idMover::Event_UpdateMove )
100 EVENT( EV_ReachedAng, idMover::Event_UpdateRotation )
101 EVENT( EV_PostRestore, idMover::Event_PostRestore )
102 EVENT( EV_StopMoving, idMover::Event_StopMoving )
103 EVENT( EV_StopRotating, idMover::Event_StopRotating )
104 EVENT( EV_Speed, idMover::Event_SetMoveSpeed )
105 EVENT( EV_Time, idMover::Event_SetMoveTime )
106 EVENT( EV_AccelTime, idMover::Event_SetAccellerationTime )
107 EVENT( EV_DecelTime, idMover::Event_SetDecelerationTime )
108 EVENT( EV_MoveTo, idMover::Event_MoveTo )
109 EVENT( EV_MoveToPos, idMover::Event_MoveToPos )
110 EVENT( EV_Move, idMover::Event_MoveDir )
111 EVENT( EV_MoveAccelerateTo, idMover::Event_MoveAccelerateTo )
112 EVENT( EV_MoveDecelerateTo, idMover::Event_MoveDecelerateTo )
113 EVENT( EV_RotateDownTo, idMover::Event_RotateDownTo )
114 EVENT( EV_RotateUpTo, idMover::Event_RotateUpTo )
115 EVENT( EV_RotateTo, idMover::Event_RotateTo )
116 EVENT( EV_Rotate, idMover::Event_Rotate )
117 EVENT( EV_RotateOnce, idMover::Event_RotateOnce )
118 EVENT( EV_Bob, idMover::Event_Bob )
119 EVENT( EV_Sway, idMover::Event_Sway )
120 EVENT( EV_Mover_OpenPortal, idMover::Event_OpenPortal )
121 EVENT( EV_Mover_ClosePortal, idMover::Event_ClosePortal )
122 EVENT( EV_AccelSound, idMover::Event_SetAccelSound )
123 EVENT( EV_DecelSound, idMover::Event_SetDecelSound )
124 EVENT( EV_MoveSound, idMover::Event_SetMoveSound )
125 EVENT( EV_Mover_InitGuiTargets, idMover::Event_InitGuiTargets )
126 EVENT( EV_EnableSplineAngles, idMover::Event_EnableSplineAngles )
127 EVENT( EV_DisableSplineAngles, idMover::Event_DisableSplineAngles )
128 EVENT( EV_RemoveInitialSplineAngles, idMover::Event_RemoveInitialSplineAngles )
129 EVENT( EV_StartSpline, idMover::Event_StartSpline )
130 EVENT( EV_StopSpline, idMover::Event_StopSpline )
131 EVENT( EV_Activate, idMover::Event_Activate )
132 EVENT( EV_IsMoving, idMover::Event_IsMoving )
133 EVENT( EV_IsRotating, idMover::Event_IsRotating )
141 idMover::idMover( void ) {
142 memset( &move, 0, sizeof( move ) );
143 memset( &rot, 0, sizeof( rot ) );
148 dest_position.Zero();
154 stopRotation = false;
155 useSplineAngles = true;
156 lastCommand = MOVER_NONE;
159 fl.networkSync = true;
167 void idMover::Save( idSaveGame *savefile ) const {
170 savefile->WriteStaticObject( physicsObj );
172 savefile->WriteInt( move.stage );
173 savefile->WriteInt( move.acceleration );
174 savefile->WriteInt( move.movetime );
175 savefile->WriteInt( move.deceleration );
176 savefile->WriteVec3( move.dir );
178 savefile->WriteInt( rot.stage );
179 savefile->WriteInt( rot.acceleration );
180 savefile->WriteInt( rot.movetime );
181 savefile->WriteInt( rot.deceleration );
182 savefile->WriteFloat( rot.rot.pitch );
183 savefile->WriteFloat( rot.rot.yaw );
184 savefile->WriteFloat( rot.rot.roll );
186 savefile->WriteInt( move_thread );
187 savefile->WriteInt( rotate_thread );
189 savefile->WriteAngles( dest_angles );
190 savefile->WriteAngles( angle_delta );
191 savefile->WriteVec3( dest_position );
192 savefile->WriteVec3( move_delta );
194 savefile->WriteFloat( move_speed );
195 savefile->WriteInt( move_time );
196 savefile->WriteInt( deceltime );
197 savefile->WriteInt( acceltime );
198 savefile->WriteBool( stopRotation );
199 savefile->WriteBool( useSplineAngles );
200 savefile->WriteInt( lastCommand );
201 savefile->WriteFloat( damage );
203 savefile->WriteInt( areaPortal );
204 if ( areaPortal > 0 ) {
205 savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
208 savefile->WriteInt( guiTargets.Num() );
209 for( i = 0; i < guiTargets.Num(); i++ ) {
210 guiTargets[ i ].Save( savefile );
213 if ( splineEnt.GetEntity() && splineEnt.GetEntity()->GetSpline() ) {
214 idCurve_Spline<idVec3> *spline = physicsObj.GetSpline();
216 savefile->WriteBool( true );
217 splineEnt.Save( savefile );
218 savefile->WriteInt( spline->GetTime( 0 ) );
219 savefile->WriteInt( spline->GetTime( spline->GetNumValues() - 1 ) - spline->GetTime( 0 ) );
220 savefile->WriteInt( physicsObj.GetSplineAcceleration() );
221 savefile->WriteInt( physicsObj.GetSplineDeceleration() );
222 savefile->WriteInt( (int)physicsObj.UsingSplineAngles() );
225 savefile->WriteBool( false );
234 void idMover::Restore( idRestoreGame *savefile ) {
236 bool hasSpline = false;
238 savefile->ReadStaticObject( physicsObj );
239 RestorePhysics( &physicsObj );
241 savefile->ReadInt( (int&)move.stage );
242 savefile->ReadInt( move.acceleration );
243 savefile->ReadInt( move.movetime );
244 savefile->ReadInt( move.deceleration );
245 savefile->ReadVec3( move.dir );
247 savefile->ReadInt( (int&)rot.stage );
248 savefile->ReadInt( rot.acceleration );
249 savefile->ReadInt( rot.movetime );
250 savefile->ReadInt( rot.deceleration );
251 savefile->ReadFloat( rot.rot.pitch );
252 savefile->ReadFloat( rot.rot.yaw );
253 savefile->ReadFloat( rot.rot.roll );
255 savefile->ReadInt( move_thread );
256 savefile->ReadInt( rotate_thread );
258 savefile->ReadAngles( dest_angles );
259 savefile->ReadAngles( angle_delta );
260 savefile->ReadVec3( dest_position );
261 savefile->ReadVec3( move_delta );
263 savefile->ReadFloat( move_speed );
264 savefile->ReadInt( move_time );
265 savefile->ReadInt( deceltime );
266 savefile->ReadInt( acceltime );
267 savefile->ReadBool( stopRotation );
268 savefile->ReadBool( useSplineAngles );
269 savefile->ReadInt( (int &)lastCommand );
270 savefile->ReadFloat( damage );
272 savefile->ReadInt( areaPortal );
273 if ( areaPortal > 0 ) {
275 savefile->ReadInt( portalState );
276 gameLocal.SetPortalState( areaPortal, portalState );
280 savefile->ReadInt( num );
281 guiTargets.SetNum( num );
282 for( i = 0; i < num; i++ ) {
283 guiTargets[ i ].Restore( savefile );
286 savefile->ReadBool( hasSpline );
294 splineEnt.Restore( savefile );
295 savefile->ReadInt( starttime );
296 savefile->ReadInt( totaltime );
297 savefile->ReadInt( accel );
298 savefile->ReadInt( decel );
299 savefile->ReadInt( useAngles );
301 PostEventMS( &EV_PostRestore, 0, starttime, totaltime, accel, decel, useAngles );
307 idMover::Event_PostRestore
310 void idMover::Event_PostRestore( int start, int total, int accel, int decel, int useSplineAng ) {
311 idCurve_Spline<idVec3> *spline;
313 idEntity *splineEntity = splineEnt.GetEntity();
314 if ( !splineEntity ) {
315 // We should never get this event if splineEnt is invalid
316 common->Warning( "Invalid spline entity during restore\n" );
320 spline = splineEntity->GetSpline();
322 spline->MakeUniform( total );
323 spline->ShiftTime( start - spline->GetTime( 0 ) );
325 physicsObj.SetSpline( spline, accel, decel, ( useSplineAng != 0 ) );
326 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
334 void idMover::Spawn( void ) {
337 stopRotation = false;
338 lastCommand = MOVER_NONE;
340 acceltime = 1000.0f * spawnArgs.GetFloat( "accel_time", "0" );
341 deceltime = 1000.0f * spawnArgs.GetFloat( "decel_time", "0" );
342 move_time = 1000.0f * spawnArgs.GetFloat( "move_time", "1" ); // safe default value
343 move_speed = spawnArgs.GetFloat( "move_speed", "0" );
345 spawnArgs.GetFloat( "damage" , "0", damage );
347 dest_position = GetPhysics()->GetOrigin();
348 dest_angles = GetPhysics()->GetAxis().ToAngles();
350 physicsObj.SetSelf( this );
351 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
352 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
353 physicsObj.SetAxis( GetPhysics()->GetAxis() );
354 physicsObj.SetClipMask( MASK_SOLID );
355 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
356 physicsObj.SetContents( 0 );
358 if ( !renderEntity.hModel || !spawnArgs.GetBool( "nopush" ) ) {
359 physicsObj.SetPusher( 0 );
361 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
362 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
363 SetPhysics( &physicsObj );
365 // see if we are on an areaportal
366 areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
368 if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
369 if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
370 PostEventMS( &EV_FindGuiTargets, 0 );
372 // not during spawn, so it's ok to get the targets
377 health = spawnArgs.GetInt( "health" );
379 fl.takedamage = true;
389 void idMover::Hide( void ) {
391 physicsObj.SetContents( 0 );
399 void idMover::Show( void ) {
401 if ( spawnArgs.GetBool( "solid", "1" ) ) {
402 physicsObj.SetContents( CONTENTS_SOLID );
404 SetPhysics( &physicsObj );
412 void idMover::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
413 fl.takedamage = false;
414 ActivateTargets( this );
420 idMover::Event_SetCallback
423 void idMover::Event_SetCallback( void ) {
424 if ( ( lastCommand == MOVER_ROTATING ) && !rotate_thread ) {
425 lastCommand = MOVER_NONE;
426 rotate_thread = idThread::CurrentThreadNum();
427 idThread::ReturnInt( true );
428 } else if ( ( lastCommand == MOVER_MOVING || lastCommand == MOVER_SPLINE ) && !move_thread ) {
429 lastCommand = MOVER_NONE;
430 move_thread = idThread::CurrentThreadNum();
431 idThread::ReturnInt( true );
433 idThread::ReturnInt( false );
439 idMover::VectorForDir
442 void idMover::VectorForDir( float angle, idVec3 &vec ) {
445 switch( ( int )angle ) {
455 physicsObj.GetLocalAngles( ang );
459 vec = ang.ToForward();
463 physicsObj.GetLocalAngles( ang );
467 vec = ang.ToForward();
471 physicsObj.GetLocalAngles( ang );
474 vec = ang.ToForward();
478 physicsObj.GetLocalAngles( ang );
482 vec = ang.ToForward();
494 physicsObj.GetLocalAngles( ang );
495 ang.ToVectors( NULL, &vec );
500 physicsObj.GetLocalAngles( ang );
501 ang.ToVectors( NULL, &vec );
504 case DIR_REL_FORWARD :
505 physicsObj.GetLocalAngles( ang );
506 vec = ang.ToForward();
510 physicsObj.GetLocalAngles( ang );
511 vec = ang.ToForward() * -1;
515 ang.Set( 0, angle, 0 );
516 vec = GetWorldVector( ang.ToForward() );
523 idMover::FindGuiTargets
526 void idMover::FindGuiTargets( void ) {
527 gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
531 ==============================
534 key/val will be set to any renderEntity->gui's on the list
535 ==============================
537 void idMover::SetGuiState( const char *key, const char *val ) const {
538 gameLocal.Printf( "Setting %s to %s\n", key, val );
539 for( int i = 0; i < guiTargets.Num(); i++ ) {
540 idEntity *ent = guiTargets[ i ].GetEntity();
542 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
543 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
544 ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
545 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
548 ent->UpdateVisuals();
555 idMover::Event_InitGuiTargets
558 void idMover::Event_FindGuiTargets( void ) {
564 idMover::SetGuiStates
567 void idMover::SetGuiStates( const char *state ) {
569 if ( guiTargets.Num() ) {
570 SetGuiState( "movestate", state );
572 for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
573 if ( renderEntity.gui[ i ] ) {
574 renderEntity.gui[ i ]->SetStateString( "movestate", state );
575 renderEntity.gui[ i ]->StateChanged( gameLocal.slow.time, true );
582 idMover::Event_InitGuiTargets
585 void idMover::Event_InitGuiTargets( void ) {
586 SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
589 /***********************************************************************
591 Translation control functions
593 ***********************************************************************/
597 idMover::Event_StopMoving
600 void idMover::Event_StopMoving( void ) {
601 physicsObj.GetLocalOrigin( dest_position );
610 void idMover::DoneMoving( void ) {
612 if ( lastCommand != MOVER_SPLINE ) {
613 // set our final position so that we get rid of any numerical inaccuracy
614 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
617 lastCommand = MOVER_NONE;
618 idThread::ObjectMoveDone( move_thread, this );
621 StopSound( SND_CHANNEL_BODY, false );
626 idMover::UpdateMoveSound
629 void idMover::UpdateMoveSound( moveStage_t stage ) {
631 case ACCELERATION_STAGE: {
632 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
633 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
637 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
640 case DECELERATION_STAGE: {
641 StopSound( SND_CHANNEL_BODY, false );
642 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
645 case FINISHED_STAGE: {
646 StopSound( SND_CHANNEL_BODY, false );
654 idMover::Event_UpdateMove
657 void idMover::Event_UpdateMove( void ) {
660 physicsObj.GetLocalOrigin( org );
662 UpdateMoveSound( move.stage );
664 switch( move.stage ) {
665 case ACCELERATION_STAGE: {
666 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, move.acceleration, org, move.dir, vec3_origin );
667 if ( move.movetime > 0 ) {
668 move.stage = LINEAR_STAGE;
669 } else if ( move.deceleration > 0 ) {
670 move.stage = DECELERATION_STAGE;
672 move.stage = FINISHED_STAGE;
677 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, move.movetime, org, move.dir, vec3_origin );
678 if ( move.deceleration ) {
679 move.stage = DECELERATION_STAGE;
681 move.stage = FINISHED_STAGE;
685 case DECELERATION_STAGE: {
686 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, move.deceleration, org, move.dir, vec3_origin );
687 move.stage = FINISHED_STAGE;
690 case FINISHED_STAGE: {
691 if ( g_debugMover.GetBool() ) {
692 gameLocal.Printf( "%d: '%s' move done\n", gameLocal.slow.time, name.c_str() );
705 void idMover::BeginMove( idThread *thread ) {
714 lastCommand = MOVER_MOVING;
717 physicsObj.GetLocalOrigin( org );
719 move_delta = dest_position - org;
720 if ( move_delta.Compare( vec3_zero ) ) {
725 // scale times up to whole physics frames
726 at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
727 move_time += at - acceltime;
729 dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
730 move_time += dt - deceltime;
733 // if we're moving at a specific speed, we need to calculate the move time
735 dist = move_delta.Length();
737 totalacceltime = acceltime + deceltime;
739 // calculate the distance we'll move during acceleration and deceleration
740 acceldist = totalacceltime * 0.5f * 0.001f * move_speed;
741 if ( acceldist >= dist ) {
742 // going too slow for this distance to move at a constant speed
743 move_time = totalacceltime;
745 // calculate move time taking acceleration into account
746 move_time = totalacceltime + 1000.0f * ( dist - acceldist ) / move_speed;
750 // scale time up to a whole physics frames
751 move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
754 stage = ACCELERATION_STAGE;
755 } else if ( move_time <= deceltime ) {
756 stage = DECELERATION_STAGE;
758 stage = LINEAR_STAGE;
764 if ( at + dt > move_time ) {
765 // there's no real correct way to handle this, so we just scale
766 // the times to fit into the move time in the same proportions
767 at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
771 move_delta = move_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
774 move.acceleration = at;
775 move.movetime = move_time - at - dt;
776 move.deceleration = dt;
777 move.dir = move_delta;
779 ProcessEvent( &EV_ReachedPos );
782 /***********************************************************************
784 Rotation control functions
786 ***********************************************************************/
790 idMover::Event_StopRotating
793 void idMover::Event_StopRotating( void ) {
794 physicsObj.GetLocalAngles( dest_angles );
795 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
801 idMover::DoneRotating
804 void idMover::DoneRotating( void ) {
805 lastCommand = MOVER_NONE;
806 idThread::ObjectMoveDone( rotate_thread, this );
809 StopSound( SND_CHANNEL_BODY, false );
814 idMover::UpdateRotationSound
817 void idMover::UpdateRotationSound( moveStage_t stage ) {
819 case ACCELERATION_STAGE: {
820 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
821 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
825 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
828 case DECELERATION_STAGE: {
829 StopSound( SND_CHANNEL_BODY, false );
830 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
833 case FINISHED_STAGE: {
834 StopSound( SND_CHANNEL_BODY, false );
842 idMover::Event_UpdateRotation
845 void idMover::Event_UpdateRotation( void ) {
848 physicsObj.GetLocalAngles( ang );
850 UpdateRotationSound( rot.stage );
852 switch( rot.stage ) {
853 case ACCELERATION_STAGE: {
854 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, rot.acceleration, ang, rot.rot, ang_zero );
855 if ( rot.movetime > 0 ) {
856 rot.stage = LINEAR_STAGE;
857 } else if ( rot.deceleration > 0 ) {
858 rot.stage = DECELERATION_STAGE;
860 rot.stage = FINISHED_STAGE;
865 if ( !stopRotation && !rot.deceleration ) {
866 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, rot.movetime, ang, rot.rot, ang_zero );
868 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, rot.movetime, ang, rot.rot, ang_zero );
871 if ( rot.deceleration ) {
872 rot.stage = DECELERATION_STAGE;
874 rot.stage = FINISHED_STAGE;
878 case DECELERATION_STAGE: {
879 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, rot.deceleration, ang, rot.rot, ang_zero );
880 rot.stage = FINISHED_STAGE;
883 case FINISHED_STAGE: {
884 lastCommand = MOVER_NONE;
885 if ( stopRotation ) {
886 // set our final angles so that we get rid of any numerical inaccuracy
887 dest_angles.Normalize360();
888 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
889 stopRotation = false;
890 } else if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_ACCELLINEAR ) {
891 // keep our angular velocity constant
892 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, ang, rot.rot, ang_zero );
895 if ( g_debugMover.GetBool() ) {
896 gameLocal.Printf( "%d: '%s' rotation done\n", gameLocal.slow.time, name.c_str() );
907 idMover::BeginRotation
910 void idMover::BeginRotation( idThread *thread, bool stopwhendone ) {
916 lastCommand = MOVER_ROTATING;
919 // rotation always uses move_time so that if a move was started before the rotation,
920 // the rotation will take the same amount of time as the move. If no move has been
921 // started and no time is set, the rotation takes 1 second.
926 physicsObj.GetLocalAngles( ang );
927 angle_delta = dest_angles - ang;
928 if ( angle_delta == ang_zero ) {
929 // set our final angles so that we get rid of any numerical inaccuracy
930 dest_angles.Normalize360();
931 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
932 stopRotation = false;
937 // scale times up to whole physics frames
938 at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
939 move_time += at - acceltime;
941 dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
942 move_time += dt - deceltime;
944 move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
947 stage = ACCELERATION_STAGE;
948 } else if ( move_time <= deceltime ) {
949 stage = DECELERATION_STAGE;
951 stage = LINEAR_STAGE;
957 if ( at + dt > move_time ) {
958 // there's no real correct way to handle this, so we just scale
959 // the times to fit into the move time in the same proportions
960 at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
964 angle_delta = angle_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
966 stopRotation = stopwhendone || ( dt != 0 );
969 rot.acceleration = at;
970 rot.movetime = move_time - at - dt;
971 rot.deceleration = dt;
972 rot.rot = angle_delta;
974 ProcessEvent( &EV_ReachedAng );
978 /***********************************************************************
980 Script callable routines
982 ***********************************************************************/
986 idMover::Event_TeamBlocked
989 void idMover::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
990 if ( g_debugMover.GetBool() ) {
991 gameLocal.Printf( "%d: '%s' stopped due to team member '%s' blocked by '%s'\n", gameLocal.slow.time, name.c_str(), blockedEntity->name.c_str(), blockingEntity->name.c_str() );
997 idMover::Event_PartBlocked
1000 void idMover::Event_PartBlocked( idEntity *blockingEntity ) {
1001 if ( damage > 0.0f ) {
1002 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
1004 if ( g_debugMover.GetBool() ) {
1005 gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.slow.time, name.c_str(), blockingEntity->name.c_str() );
1011 idMover::Event_SetMoveSpeed
1014 void idMover::Event_SetMoveSpeed( float speed ) {
1016 gameLocal.Error( "Cannot set speed less than or equal to 0." );
1020 move_time = 0; // move_time is calculated for each move when move_speed is non-0
1025 idMover::Event_SetMoveTime
1028 void idMover::Event_SetMoveTime( float time ) {
1030 gameLocal.Error( "Cannot set time less than or equal to 0." );
1034 move_time = SEC2MS( time );
1039 idMover::Event_SetAccellerationTime
1042 void idMover::Event_SetAccellerationTime( float time ) {
1044 gameLocal.Error( "Cannot set acceleration time less than 0." );
1047 acceltime = SEC2MS( time );
1052 idMover::Event_SetDecelerationTime
1055 void idMover::Event_SetDecelerationTime( float time ) {
1057 gameLocal.Error( "Cannot set deceleration time less than 0." );
1060 deceltime = SEC2MS( time );
1065 idMover::Event_MoveTo
1068 void idMover::Event_MoveTo( idEntity *ent ) {
1070 gameLocal.Warning( "Entity not found" );
1073 dest_position = GetLocalCoordinates( ent->GetPhysics()->GetOrigin() );
1074 BeginMove( idThread::CurrentThread() );
1082 void idMover::MoveToPos( const idVec3 &pos ) {
1083 dest_position = GetLocalCoordinates( pos );
1089 idMover::Event_MoveToPos
1092 void idMover::Event_MoveToPos( idVec3 &pos ) {
1098 idMover::Event_MoveDir
1101 void idMover::Event_MoveDir( float angle, float distance ) {
1105 physicsObj.GetLocalOrigin( org );
1106 VectorForDir( angle, dir );
1107 dest_position = org + dir * distance;
1109 BeginMove( idThread::CurrentThread() );
1114 idMover::Event_MoveAccelerateTo
1117 void idMover::Event_MoveAccelerateTo( float speed, float time ) {
1123 gameLocal.Error( "idMover::Event_MoveAccelerateTo: cannot set acceleration time less than 0." );
1126 dir = physicsObj.GetLinearVelocity();
1127 v = dir.Normalize();
1129 // if not moving already
1131 gameLocal.Error( "idMover::Event_MoveAccelerateTo: not moving." );
1134 // if already moving faster than the desired speed
1139 at = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
1141 lastCommand = MOVER_MOVING;
1143 physicsObj.GetLocalOrigin( org );
1145 move.stage = ACCELERATION_STAGE;
1146 move.acceleration = at;
1148 move.deceleration = 0;
1150 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
1151 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
1152 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, move.acceleration, org, dir * ( speed - v ), dir * v );
1157 idMover::Event_MoveDecelerateTo
1160 void idMover::Event_MoveDecelerateTo( float speed, float time ) {
1166 gameLocal.Error( "idMover::Event_MoveDecelerateTo: cannot set deceleration time less than 0." );
1169 dir = physicsObj.GetLinearVelocity();
1170 v = dir.Normalize();
1172 // if not moving already
1174 gameLocal.Error( "idMover::Event_MoveDecelerateTo: not moving." );
1177 // if already moving slower than the desired speed
1182 dt = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
1184 lastCommand = MOVER_MOVING;
1186 physicsObj.GetLocalOrigin( org );
1188 move.stage = DECELERATION_STAGE;
1189 move.acceleration = 0;
1191 move.deceleration = dt;
1193 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
1194 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
1195 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, move.deceleration, org, dir * ( v - speed ), dir * speed );
1200 idMover::Event_RotateDownTo
1203 void idMover::Event_RotateDownTo( int axis, float angle ) {
1206 if ( ( axis < 0 ) || ( axis > 2 ) ) {
1207 gameLocal.Error( "Invalid axis" );
1210 physicsObj.GetLocalAngles( ang );
1212 dest_angles[ axis ] = angle;
1213 if ( dest_angles[ axis ] > ang[ axis ] ) {
1214 dest_angles[ axis ] -= 360;
1217 BeginRotation( idThread::CurrentThread(), true );
1222 idMover::Event_RotateUpTo
1225 void idMover::Event_RotateUpTo( int axis, float angle ) {
1228 if ( ( axis < 0 ) || ( axis > 2 ) ) {
1229 gameLocal.Error( "Invalid axis" );
1232 physicsObj.GetLocalAngles( ang );
1234 dest_angles[ axis ] = angle;
1235 if ( dest_angles[ axis ] < ang[ axis ] ) {
1236 dest_angles[ axis ] += 360;
1239 BeginRotation( idThread::CurrentThread(), true );
1244 idMover::Event_RotateTo
1247 void idMover::Event_RotateTo( idAngles &angles ) {
1248 dest_angles = angles;
1249 BeginRotation( idThread::CurrentThread(), true );
1254 idMover::Event_Rotate
1257 void idMover::Event_Rotate( idAngles &angles ) {
1260 if ( rotate_thread ) {
1264 physicsObj.GetLocalAngles( ang );
1265 dest_angles = ang + angles * ( move_time - ( acceltime + deceltime ) / 2 ) * 0.001f;
1267 BeginRotation( idThread::CurrentThread(), false );
1272 idMover::Event_RotateOnce
1275 void idMover::Event_RotateOnce( idAngles &angles ) {
1278 if ( rotate_thread ) {
1282 physicsObj.GetLocalAngles( ang );
1283 dest_angles = ang + angles;
1285 BeginRotation( idThread::CurrentThread(), true );
1293 void idMover::Event_Bob( float speed, float phase, idVec3 &depth ) {
1296 physicsObj.GetLocalOrigin( org );
1297 physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), speed * 1000 * phase, speed * 500, org, depth * 2.0f, vec3_origin );
1305 void idMover::Event_Sway( float speed, float phase, idAngles &depth ) {
1306 idAngles ang, angSpeed;
1309 physicsObj.GetLocalAngles( ang );
1310 assert ( speed > 0.0f );
1311 duration = idMath::Sqrt( depth[0] * depth[0] + depth[1] * depth[1] + depth[2] * depth[2] ) / speed;
1312 angSpeed = depth / ( duration * idMath::SQRT_1OVER2 );
1313 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), duration * 1000.0f * phase, duration * 1000.0f, ang, angSpeed, ang_zero );
1318 idMover::Event_OpenPortal
1320 Sets the portal associtated with this mover to be open
1323 void idMover::Event_OpenPortal( void ) {
1325 SetPortalState( true );
1331 idMover::Event_ClosePortal
1333 Sets the portal associtated with this mover to be closed
1336 void idMover::Event_ClosePortal( void ) {
1338 SetPortalState( false );
1344 idMover::Event_SetAccelSound
1347 void idMover::Event_SetAccelSound( const char *sound ) {
1348 // refSound.SetSound( "accel", sound );
1353 idMover::Event_SetDecelSound
1356 void idMover::Event_SetDecelSound( const char *sound ) {
1357 // refSound.SetSound( "decel", sound );
1362 idMover::Event_SetMoveSound
1365 void idMover::Event_SetMoveSound( const char *sound ) {
1366 // refSound.SetSound( "move", sound );
1371 idMover::Event_EnableSplineAngles
1374 void idMover::Event_EnableSplineAngles( void ) {
1375 useSplineAngles = true;
1380 idMover::Event_DisableSplineAngles
1383 void idMover::Event_DisableSplineAngles( void ) {
1384 useSplineAngles = false;
1389 idMover::Event_RemoveInitialSplineAngles
1392 void idMover::Event_RemoveInitialSplineAngles( void ) {
1393 idCurve_Spline<idVec3> *spline;
1396 spline = physicsObj.GetSpline();
1400 ang = spline->GetCurrentFirstDerivative( 0 ).ToAngles();
1401 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, -ang, ang_zero, ang_zero );
1406 idMover::Event_StartSpline
1409 void idMover::Event_StartSpline( idEntity *splineEntity ) {
1410 idCurve_Spline<idVec3> *spline;
1412 if ( !splineEntity ) {
1416 // Needed for savegames
1417 splineEnt = splineEntity;
1419 spline = splineEntity->GetSpline();
1424 lastCommand = MOVER_SPLINE;
1427 if ( acceltime + deceltime > move_time ) {
1428 acceltime = move_time / 2;
1429 deceltime = move_time - acceltime;
1431 move.stage = FINISHED_STAGE;
1432 move.acceleration = acceltime;
1433 move.movetime = move_time;
1434 move.deceleration = deceltime;
1436 spline->MakeUniform( move_time );
1437 spline->ShiftTime( gameLocal.slow.time - spline->GetTime( 0 ) );
1439 physicsObj.SetSpline( spline, move.acceleration, move.deceleration, useSplineAngles );
1440 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
1445 idMover::Event_StopSpline
1448 void idMover::Event_StopSpline( void ) {
1449 physicsObj.SetSpline( NULL, 0, 0, useSplineAngles );
1455 idMover::Event_Activate
1458 void idMover::Event_Activate( idEntity *activator ) {
1460 Event_StartSpline( this );
1465 idMover::Event_IsMoving
1468 void idMover::Event_IsMoving( void ) {
1469 if ( physicsObj.GetLinearExtrapolationType() == EXTRAPOLATION_NONE ) {
1470 idThread::ReturnInt( false );
1472 idThread::ReturnInt( true );
1478 idMover::Event_IsRotating
1481 void idMover::Event_IsRotating( void ) {
1482 if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_NONE ) {
1483 idThread::ReturnInt( false );
1485 idThread::ReturnInt( true );
1491 idMover::WriteToSnapshot
1494 void idMover::WriteToSnapshot( idBitMsgDelta &msg ) const {
1495 physicsObj.WriteToSnapshot( msg );
1496 msg.WriteBits( move.stage, 3 );
1497 msg.WriteBits( rot.stage, 3 );
1498 WriteBindToSnapshot( msg );
1499 WriteGUIToSnapshot( msg );
1504 idMover::ReadFromSnapshot
1507 void idMover::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1508 moveStage_t oldMoveStage = move.stage;
1509 moveStage_t oldRotStage = rot.stage;
1511 physicsObj.ReadFromSnapshot( msg );
1512 move.stage = (moveStage_t) msg.ReadBits( 3 );
1513 rot.stage = (moveStage_t) msg.ReadBits( 3 );
1514 ReadBindFromSnapshot( msg );
1515 ReadGUIFromSnapshot( msg );
1517 if ( msg.HasChanged() ) {
1518 if ( move.stage != oldMoveStage ) {
1519 UpdateMoveSound( oldMoveStage );
1521 if ( rot.stage != oldRotStage ) {
1522 UpdateRotationSound( oldRotStage );
1530 idMover::SetPortalState
1533 void idMover::SetPortalState( bool open ) {
1534 assert( areaPortal );
1535 gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
1539 ===============================================================================
1541 idSplinePath, holds a spline path to be used by an idMover
1543 ===============================================================================
1546 CLASS_DECLARATION( idEntity, idSplinePath )
1551 idSplinePath::idSplinePath
1554 idSplinePath::idSplinePath() {
1562 void idSplinePath::Spawn( void ) {
1567 ===============================================================================
1571 ===============================================================================
1573 const idEventDef EV_PostArrival( "postArrival", NULL );
1574 const idEventDef EV_GotoFloor( "gotoFloor", "d" );
1576 const idEventDef EV_SetGuiStates( "setGuiStates" );
1579 CLASS_DECLARATION( idMover, idElevator )
1580 EVENT( EV_Activate, idElevator::Event_Activate )
1581 EVENT( EV_TeamBlocked, idElevator::Event_TeamBlocked )
1582 EVENT( EV_PartBlocked, idElevator::Event_PartBlocked )
1583 EVENT( EV_PostArrival, idElevator::Event_PostFloorArrival )
1584 EVENT( EV_GotoFloor, idElevator::Event_GotoFloor )
1585 EVENT( EV_Touch, idElevator::Event_Touch )
1587 EVENT( EV_SetGuiStates, idElevator::Event_SetGuiStates )
1593 idElevator::idElevator
1596 idElevator::idElevator( void ) {
1602 controlsDisabled = false;
1613 void idElevator::Save( idSaveGame *savefile ) const {
1616 savefile->WriteInt( (int)state );
1618 savefile->WriteInt( floorInfo.Num() );
1619 for ( i = 0; i < floorInfo.Num(); i++ ) {
1620 savefile->WriteVec3( floorInfo[ i ].pos );
1621 savefile->WriteString( floorInfo[ i ].door );
1622 savefile->WriteInt( floorInfo[ i ].floor );
1625 savefile->WriteInt( currentFloor );
1626 savefile->WriteInt( pendingFloor );
1627 savefile->WriteInt( lastFloor );
1628 savefile->WriteBool( controlsDisabled );
1629 savefile->WriteFloat( returnTime );
1630 savefile->WriteInt( returnFloor );
1631 savefile->WriteInt( lastTouchTime );
1639 void idElevator::Restore( idRestoreGame *savefile ) {
1642 savefile->ReadInt( (int &)state );
1644 savefile->ReadInt( num );
1645 for ( i = 0; i < num; i++ ) {
1648 savefile->ReadVec3( floor.pos );
1649 savefile->ReadString( floor.door );
1650 savefile->ReadInt( floor.floor );
1652 floorInfo.Append( floor );
1655 savefile->ReadInt( currentFloor );
1656 savefile->ReadInt( pendingFloor );
1657 savefile->ReadInt( lastFloor );
1658 savefile->ReadBool( controlsDisabled );
1659 savefile->ReadFloat( returnTime );
1660 savefile->ReadInt( returnFloor );
1661 savefile->ReadInt( lastTouchTime );
1669 void idElevator::Spawn( void ) {
1675 pendingFloor = spawnArgs.GetInt( "floor", "1" );
1676 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1]);
1678 returnTime = spawnArgs.GetFloat( "returnTime" );
1679 returnFloor = spawnArgs.GetInt( "returnFloor" );
1681 len1 = strlen( "floorPos_" );
1682 const idKeyValue *kv = spawnArgs.MatchPrefix( "floorPos_", NULL );
1684 str = kv->GetKey().Right( kv->GetKey().Length() - len1 );
1686 fi.floor = atoi( str );
1687 fi.door = spawnArgs.GetString( va( "floorDoor_%i", fi.floor ) );
1688 fi.pos = spawnArgs.GetVector( kv->GetKey() );
1689 floorInfo.Append( fi );
1690 kv = spawnArgs.MatchPrefix( "floorPos_", kv );
1694 BecomeActive( TH_THINK | TH_PHYSICS );
1695 PostEventMS( &EV_Mover_InitGuiTargets, 0 );
1696 controlsDisabled = false;
1701 idElevator::Event_Touch
1704 void idElevator::Event_Touch( idEntity *other, trace_t *trace ) {
1706 if ( gameLocal.slow.time < lastTouchTime + 2000 ) {
1710 if ( !other->IsType( idPlayer::Type ) ) {
1714 lastTouchTime = gameLocal.slow.time;
1716 if ( thinkFlags & TH_PHYSICS ) {
1720 int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
1721 if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
1722 PostEventSec( &EV_GotoFloor, 0.25f, triggerFloor );
1731 void idElevator::Think( void ) {
1732 idVec3 masterOrigin;
1734 idDoor *doorent = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1735 if ( state == INIT ) {
1738 doorent->BindTeam( this );
1739 doorent->spawnArgs.Set( "snd_open", "" );
1740 doorent->spawnArgs.Set( "snd_close", "" );
1741 doorent->spawnArgs.Set( "snd_opened", "" );
1743 for ( int i = 0; i < floorInfo.Num(); i++ ) {
1744 idDoor *door = GetDoor( floorInfo[i].door );
1746 door->SetCompanion( doorent );
1750 Event_GotoFloor( pendingFloor );
1752 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
1753 } else if ( state == WAITING_ON_DOORS ) {
1755 state = doorent->IsOpen() ? WAITING_ON_DOORS : IDLE;
1759 if ( state == IDLE ) {
1760 lastFloor = currentFloor;
1761 currentFloor = pendingFloor;
1762 floorInfo_s *fi = GetFloorInfo( currentFloor );
1764 MoveToPos( fi->pos );
1774 idElevator::Event_Activate
1777 void idElevator::Event_Activate( idEntity *activator ) {
1778 int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
1779 if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
1780 Event_GotoFloor( triggerFloor );
1786 idElevator::Event_TeamBlocked
1789 void idElevator::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
1790 if ( blockedEntity == this ) {
1791 Event_GotoFloor( lastFloor );
1792 } else if ( blockedEntity && blockedEntity->IsType( idDoor::Type ) ) {
1793 // open the inner doors if one is blocked
1794 idDoor *blocked = static_cast<idDoor *>( blockedEntity );
1795 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1796 if ( door && blocked->GetMoveMaster() == door->GetMoveMaster() ) {
1797 door->SetBlocked(true);
1799 OpenFloorDoor( currentFloor );
1806 idElevator::HandleSingleGuiCommand
1809 bool idElevator::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
1812 if ( controlsDisabled ) {
1816 if ( !src->ReadToken( &token ) ) {
1820 if ( token == ";" ) {
1824 if ( token.Icmp( "changefloor" ) == 0 ) {
1825 if ( src->ReadToken( &token ) ) {
1826 int newFloor = atoi( token );
1827 if ( newFloor == currentFloor ) {
1828 // open currentFloor and interior doors
1830 OpenFloorDoor( currentFloor );
1832 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1833 if ( door && door->IsOpen() ) {
1834 PostEventSec( &EV_GotoFloor, 0.5f, newFloor );
1836 ProcessEvent( &EV_GotoFloor, newFloor );
1843 src->UnreadToken( &token );
1849 idElevator::OpenFloorDoor
1852 void idElevator::OpenFloorDoor( int floor ) {
1853 floorInfo_s *fi = GetFloorInfo( floor );
1855 idDoor *door = GetDoor( fi->door );
1864 idElevator::OpenInnerDoor
1867 void idElevator::OpenInnerDoor( void ) {
1868 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1876 idElevator::GetFloorInfo
1879 floorInfo_s *idElevator::GetFloorInfo( int floor ) {
1880 for ( int i = 0; i < floorInfo.Num(); i++ ) {
1881 if ( floorInfo[i].floor == floor ) {
1882 return &floorInfo[i];
1890 idElevator::Event_GotoFloor
1893 void idElevator::Event_GotoFloor( int floor ) {
1894 floorInfo_s *fi = GetFloorInfo( floor );
1896 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1898 if ( door->IsBlocked() || door->IsOpen() ) {
1899 PostEventSec( &EV_GotoFloor, 0.5f, floor );
1905 state = WAITING_ON_DOORS;
1906 pendingFloor = floor;
1912 idElevator::BeginMove
1915 void idElevator::BeginMove( idThread *thread ) {
1916 controlsDisabled = true;
1919 const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
1921 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
1923 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
1924 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
1925 ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", "" );
1926 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
1929 ent->UpdateVisuals();
1931 kv = spawnArgs.MatchPrefix( "statusGui", kv );
1933 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[3] : guiBinaryMoverStates[2] );
1934 idMover::BeginMove( thread );
1942 idDoor *idElevator::GetDoor( const char *name ) {
1948 if ( name && *name ) {
1949 ent = gameLocal.FindEntity( name );
1950 if ( ent && ent->IsType( idDoor::Type ) ) {
1951 doorEnt = static_cast<idDoor*>( ent );
1952 master = doorEnt->GetMoveMaster();
1953 if ( master != doorEnt ) {
1954 if ( master->IsType( idDoor::Type ) ) {
1955 doorEnt = static_cast<idDoor*>( master );
1968 idElevator::Event_PostFloorArrival
1971 void idElevator::Event_PostFloorArrival() {
1972 OpenFloorDoor( currentFloor );
1974 SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
1975 controlsDisabled = false;
1976 if ( returnTime > 0.0f && returnFloor != currentFloor ) {
1977 PostEventSec( &EV_GotoFloor, returnTime, returnFloor );
1982 void idElevator::Event_SetGuiStates() {
1983 SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
1989 idElevator::DoneMoving
1992 void idElevator::DoneMoving( void ) {
1993 idMover::DoneMoving();
1994 EnableProperDoors();
1995 const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
1997 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
1999 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
2000 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
2001 ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", va( "%i", currentFloor ) );
2002 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
2005 ent->UpdateVisuals();
2007 kv = spawnArgs.MatchPrefix( "statusGui", kv );
2009 if ( spawnArgs.GetInt( "pauseOnFloor", "-1" ) == currentFloor ) {
2010 PostEventSec( &EV_PostArrival, spawnArgs.GetFloat( "pauseTime" ) );
2012 Event_PostFloorArrival();
2018 idElevator::CloseAllDoors
2021 void idElevator::CloseAllDoors( void ) {
2022 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
2026 for ( int i = 0; i < floorInfo.Num(); i++ ) {
2027 door = GetDoor( floorInfo[i].door );
2036 idElevator::DisableAllDoors
2039 void idElevator::DisableAllDoors( void ) {
2040 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
2042 door->Enable( false );
2044 for ( int i = 0; i < floorInfo.Num(); i++ ) {
2045 door = GetDoor( floorInfo[i].door );
2047 door->Enable( false );
2054 idElevator::EnableProperDoors
2057 void idElevator::EnableProperDoors( void ) {
2058 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
2060 door->Enable( true );
2062 for ( int i = 0; i < floorInfo.Num(); i++ ) {
2063 if ( floorInfo[i].floor == currentFloor ) {
2064 door = GetDoor( floorInfo[i].door );
2066 door->Enable( true );
2075 ===============================================================================
2079 Doors, plats, and buttons are all binary (two position) movers
2080 Pos1 is "at rest", pos2 is "activated"
2082 ===============================================================================
2085 const idEventDef EV_Mover_ReturnToPos1( "<returntopos1>", NULL );
2086 const idEventDef EV_Mover_MatchTeam( "<matchteam>", "dd" );
2087 const idEventDef EV_Mover_Enable( "enable", NULL );
2088 const idEventDef EV_Mover_Disable( "disable", NULL );
2090 CLASS_DECLARATION( idEntity, idMover_Binary )
2091 EVENT( EV_FindGuiTargets, idMover_Binary::Event_FindGuiTargets )
2092 EVENT( EV_Thread_SetCallback, idMover_Binary::Event_SetCallback )
2093 EVENT( EV_Mover_ReturnToPos1, idMover_Binary::Event_ReturnToPos1 )
2094 EVENT( EV_Activate, idMover_Binary::Event_Use_BinaryMover )
2095 EVENT( EV_ReachedPos, idMover_Binary::Event_Reached_BinaryMover )
2096 EVENT( EV_Mover_MatchTeam, idMover_Binary::Event_MatchActivateTeam )
2097 EVENT( EV_Mover_Enable, idMover_Binary::Event_Enable )
2098 EVENT( EV_Mover_Disable, idMover_Binary::Event_Disable )
2099 EVENT( EV_Mover_OpenPortal, idMover_Binary::Event_OpenPortal )
2100 EVENT( EV_Mover_ClosePortal, idMover_Binary::Event_ClosePortal )
2101 EVENT( EV_Mover_InitGuiTargets, idMover_Binary::Event_InitGuiTargets )
2106 idMover_Binary::idMover_Binary()
2109 idMover_Binary::idMover_Binary() {
2112 moverState = MOVER_POS1;
2114 activateChain = NULL;
2136 fl.networkSync = true;
2141 idMover_Binary::~idMover_Binary
2144 idMover_Binary::~idMover_Binary() {
2145 idMover_Binary *mover;
2147 // if this is the mover master
2148 if ( this == moveMaster ) {
2149 // make the next mover in the chain the move master
2150 for ( mover = moveMaster; mover; mover = mover->activateChain ) {
2151 mover->moveMaster = this->activateChain;
2155 // remove mover from the activate chain
2156 for ( mover = moveMaster; mover; mover = mover->activateChain ) {
2157 if ( mover->activateChain == this ) {
2158 mover->activateChain = this->activateChain;
2167 idMover_Binary::Save
2170 void idMover_Binary::Save( idSaveGame *savefile ) const {
2173 savefile->WriteVec3( pos1 );
2174 savefile->WriteVec3( pos2 );
2175 savefile->WriteInt( (moverState_t)moverState );
2177 savefile->WriteObject( moveMaster );
2178 savefile->WriteObject( activateChain );
2180 savefile->WriteInt( soundPos1 );
2181 savefile->WriteInt( sound1to2 );
2182 savefile->WriteInt( sound2to1 );
2183 savefile->WriteInt( soundPos2 );
2184 savefile->WriteInt( soundLoop );
2186 savefile->WriteFloat( wait );
2187 savefile->WriteFloat( damage );
2189 savefile->WriteInt( duration );
2190 savefile->WriteInt( accelTime );
2191 savefile->WriteInt( decelTime );
2193 activatedBy.Save( savefile );
2195 savefile->WriteInt( stateStartTime );
2196 savefile->WriteString( team );
2197 savefile->WriteBool( enabled );
2199 savefile->WriteInt( move_thread );
2200 savefile->WriteInt( updateStatus );
2202 savefile->WriteInt( buddies.Num() );
2203 for ( i = 0; i < buddies.Num(); i++ ) {
2204 savefile->WriteString( buddies[ i ] );
2207 savefile->WriteStaticObject( physicsObj );
2209 savefile->WriteInt( areaPortal );
2211 savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
2213 savefile->WriteBool( blocked );
2215 savefile->WriteBool( playerOnly );
2218 savefile->WriteInt( guiTargets.Num() );
2219 for( i = 0; i < guiTargets.Num(); i++ ) {
2220 guiTargets[ i ].Save( savefile );
2226 idMover_Binary::Restore
2229 void idMover_Binary::Restore( idRestoreGame *savefile ) {
2230 int i, num, portalState;
2233 savefile->ReadVec3( pos1 );
2234 savefile->ReadVec3( pos2 );
2235 savefile->ReadInt( (int &)moverState );
2237 savefile->ReadObject( reinterpret_cast<idClass *&>( moveMaster ) );
2238 savefile->ReadObject( reinterpret_cast<idClass *&>( activateChain ) );
2240 savefile->ReadInt( soundPos1 );
2241 savefile->ReadInt( sound1to2 );
2242 savefile->ReadInt( sound2to1 );
2243 savefile->ReadInt( soundPos2 );
2244 savefile->ReadInt( soundLoop );
2246 savefile->ReadFloat( wait );
2247 savefile->ReadFloat( damage );
2249 savefile->ReadInt( duration );
2250 savefile->ReadInt( accelTime );
2251 savefile->ReadInt( decelTime );
2253 activatedBy.Restore( savefile );
2255 savefile->ReadInt( stateStartTime );
2257 savefile->ReadString( team );
2258 savefile->ReadBool( enabled );
2260 savefile->ReadInt( move_thread );
2261 savefile->ReadInt( updateStatus );
2263 savefile->ReadInt( num );
2264 for ( i = 0; i < num; i++ ) {
2265 savefile->ReadString( temp );
2266 buddies.Append( temp );
2269 savefile->ReadStaticObject( physicsObj );
2270 RestorePhysics( &physicsObj );
2272 savefile->ReadInt( areaPortal );
2274 savefile->ReadInt( portalState );
2275 gameLocal.SetPortalState( areaPortal, portalState );
2277 savefile->ReadBool( blocked );
2279 savefile->ReadBool( playerOnly );
2283 savefile->ReadInt( num );
2284 guiTargets.SetNum( num );
2285 for( i = 0; i < num; i++ ) {
2286 guiTargets[ i ].Restore( savefile );
2292 idMover_Binary::Spawn
2294 Base class for all movers.
2296 "wait" wait before returning (3 default, -1 = never return)
2297 "speed" movement speed
2300 void idMover_Binary::Spawn( void ) {
2308 activateChain = NULL;
2310 spawnArgs.GetFloat( "wait", "0", wait );
2312 spawnArgs.GetInt( "updateStatus", "0", updateStatus );
2314 const idKeyValue *kv = spawnArgs.MatchPrefix( "buddy", NULL );
2316 buddies.Append( kv->GetValue() );
2317 kv = spawnArgs.MatchPrefix( "buddy", kv );
2320 spawnArgs.GetString( "team", "", &temp );
2323 if ( !team.Length() ) {
2326 // find the first entity spawned on this team (which could be us)
2327 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2328 if ( ent->IsType( idMover_Binary::Type ) && !idStr::Icmp( static_cast<idMover_Binary *>(ent)->team.c_str(), temp ) ) {
2336 moveMaster = static_cast<idMover_Binary *>(ent);
2338 // create a physics team for the binary mover parts
2339 if ( ent != this ) {
2343 physicsObj.SetSelf( this );
2344 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
2345 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
2346 physicsObj.SetAxis( GetPhysics()->GetAxis() );
2347 physicsObj.SetClipMask( MASK_SOLID );
2348 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
2349 physicsObj.SetContents( 0 );
2351 if ( !spawnArgs.GetBool( "nopush" ) ) {
2352 physicsObj.SetPusher( 0 );
2354 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
2355 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
2356 SetPhysics( &physicsObj );
2358 if ( moveMaster != this ) {
2359 JoinActivateTeam( moveMaster );
2362 idBounds soundOrigin;
2363 idMover_Binary *slave;
2365 soundOrigin.Clear();
2366 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2367 soundOrigin += slave->GetPhysics()->GetAbsBounds();
2369 moveMaster->refSound.origin = soundOrigin.GetCenter();
2371 if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
2372 if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
2373 PostEventMS( &EV_FindGuiTargets, 0 );
2375 // not during spawn, so it's ok to get the targets
2383 idMover_Binary::GetMovedir
2385 The editor only specifies a single value for angles (yaw),
2386 but we have special constants to generate an up or down direction.
2387 Angles will be cleared, because it is being used to represent a direction
2388 instead of an orientation.
2391 void idMover_Binary::GetMovedir( float angle, idVec3 &movedir ) {
2392 if ( angle == -1 ) {
2393 movedir.Set( 0, 0, 1 );
2394 } else if ( angle == -2 ) {
2395 movedir.Set( 0, 0, -1 );
2397 movedir = idAngles( 0, angle, 0 ).ToForward();
2403 idMover_Binary::Event_SetCallback
2406 void idMover_Binary::Event_SetCallback( void ) {
2407 if ( ( moverState == MOVER_1TO2 ) || ( moverState == MOVER_2TO1 ) ) {
2408 move_thread = idThread::CurrentThreadNum();
2409 idThread::ReturnInt( true );
2411 idThread::ReturnInt( false );
2417 idMover_Binary::UpdateMoverSound
2420 void idMover_Binary::UpdateMoverSound( moverState_t state ) {
2421 if ( moveMaster == this ) {
2428 StartSound( "snd_open", SND_CHANNEL_ANY, 0, false, NULL );
2431 StartSound( "snd_close", SND_CHANNEL_ANY, 0, false, NULL );
2439 idMover_Binary::SetMoverState
2442 void idMover_Binary::SetMoverState( moverState_t newstate, int time ) {
2445 moverState = newstate;
2448 UpdateMoverSound( newstate );
2450 stateStartTime = time;
2451 switch( moverState ) {
2453 Signal( SIG_MOVER_POS1 );
2454 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos1, vec3_origin, vec3_origin );
2458 Signal( SIG_MOVER_POS2 );
2459 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos2, vec3_origin, vec3_origin );
2463 Signal( SIG_MOVER_1TO2 );
2464 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos1, ( pos2 - pos1 ) * 1000.0f / duration, vec3_origin );
2465 if ( accelTime != 0 || decelTime != 0 ) {
2466 physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos1, pos2 );
2468 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
2473 Signal( SIG_MOVER_2TO1 );
2474 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos2, ( pos1 - pos2 ) * 1000.0f / duration, vec3_origin );
2475 if ( accelTime != 0 || decelTime != 0 ) {
2476 physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos2, pos1 );
2478 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
2487 idMover_Binary::MatchActivateTeam
2489 All entities in a mover team will move from pos1 to pos2
2490 in the same amount of time
2493 void idMover_Binary::MatchActivateTeam( moverState_t newstate, int time ) {
2494 idMover_Binary *slave;
2496 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2497 slave->SetMoverState( newstate, time );
2503 idMover_Binary::Enable
2506 void idMover_Binary::Enable( bool b ) {
2512 idMover_Binary::Event_MatchActivateTeam
2515 void idMover_Binary::Event_MatchActivateTeam( moverState_t newstate, int time ) {
2516 MatchActivateTeam( newstate, time );
2521 idMover_Binary::BindTeam
2523 All entities in a mover team will be bound
2526 void idMover_Binary::BindTeam( idEntity *bindTo ) {
2527 idMover_Binary *slave;
2529 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2530 slave->Bind( bindTo, true );
2536 idMover_Binary::JoinActivateTeam
2538 Set all entities in a mover team to be enabled
2541 void idMover_Binary::JoinActivateTeam( idMover_Binary *master ) {
2542 this->activateChain = master->activateChain;
2543 master->activateChain = this;
2548 idMover_Binary::Event_Enable
2550 Set all entities in a mover team to be enabled
2553 void idMover_Binary::Event_Enable( void ) {
2554 idMover_Binary *slave;
2556 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2557 slave->Enable( false );
2563 idMover_Binary::Event_Disable
2565 Set all entities in a mover team to be disabled
2568 void idMover_Binary::Event_Disable( void ) {
2569 idMover_Binary *slave;
2571 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2572 slave->Enable( false );
2578 idMover_Binary::Event_OpenPortal
2580 Sets the portal associtated with this mover to be open
2583 void idMover_Binary::Event_OpenPortal( void ) {
2584 idMover_Binary *slave;
2586 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2587 if ( slave->areaPortal ) {
2588 slave->SetPortalState( true );
2591 if ( slave->playerOnly ) {
2592 gameLocal.SetAASAreaState( slave->GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, false );
2600 idMover_Binary::Event_ClosePortal
2602 Sets the portal associtated with this mover to be closed
2605 void idMover_Binary::Event_ClosePortal( void ) {
2606 idMover_Binary *slave;
2608 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2609 if ( !slave->IsHidden() ) {
2610 if ( slave->areaPortal ) {
2611 slave->SetPortalState( false );
2614 if ( slave->playerOnly ) {
2615 gameLocal.SetAASAreaState( slave->GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, true );
2624 idMover_Binary::Event_ReturnToPos1
2627 void idMover_Binary::Event_ReturnToPos1( void ) {
2628 MatchActivateTeam( MOVER_2TO1, gameLocal.slow.time );
2633 idMover_Binary::Event_Reached_BinaryMover
2636 void idMover_Binary::Event_Reached_BinaryMover( void ) {
2638 if ( moverState == MOVER_1TO2 ) {
2640 idThread::ObjectMoveDone( move_thread, this );
2643 if ( moveMaster == this ) {
2644 StartSound( "snd_opened", SND_CHANNEL_ANY, 0, false, NULL );
2647 SetMoverState( MOVER_POS2, gameLocal.slow.time );
2649 SetGuiStates( guiBinaryMoverStates[MOVER_POS2] );
2653 if ( enabled && wait >= 0 && !spawnArgs.GetBool( "toggle" ) ) {
2654 // return to pos1 after a delay
2655 PostEventSec( &EV_Mover_ReturnToPos1, wait );
2659 ActivateTargets( moveMaster->GetActivator() );
2662 } else if ( moverState == MOVER_2TO1 ) {
2664 idThread::ObjectMoveDone( move_thread, this );
2667 SetMoverState( MOVER_POS1, gameLocal.slow.time );
2669 SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
2673 // close areaportals
2674 if ( moveMaster == this ) {
2675 ProcessEvent( &EV_Mover_ClosePortal );
2678 if ( enabled && wait >= 0 && spawnArgs.GetBool( "continuous" ) ) {
2679 PostEventSec( &EV_Activate, wait, this );
2683 gameLocal.Error( "Event_Reached_BinaryMover: bad moverState" );
2689 idMover_Binary::GotoPosition1
2692 void idMover_Binary::GotoPosition1( void ) {
2693 idMover_Binary *slave;
2696 // only the master should control this
2697 if ( moveMaster != this ) {
2698 moveMaster->GotoPosition1();
2702 SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
2704 if ( ( moverState == MOVER_POS1 ) || ( moverState == MOVER_2TO1 ) ) {
2705 // already there, or on the way
2709 if ( moverState == MOVER_POS2 ) {
2710 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2711 slave->CancelEvents( &EV_Mover_ReturnToPos1 );
2713 if ( !spawnArgs.GetBool( "toggle" ) ) {
2714 ProcessEvent( &EV_Mover_ReturnToPos1 );
2719 // only partway up before reversing
2720 if ( moverState == MOVER_1TO2 ) {
2721 // use the physics times because this might be executed during the physics simulation
2722 partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
2723 assert( partial >= 0 );
2724 if ( partial < 0 ) {
2727 MatchActivateTeam( MOVER_2TO1, physicsObj.GetTime() - partial );
2728 // if already at at position 1 (partial == duration) execute the reached event
2729 if ( partial >= duration ) {
2730 Event_Reached_BinaryMover();
2737 idMover_Binary::GotoPosition2
2740 void idMover_Binary::GotoPosition2( void ) {
2743 // only the master should control this
2744 if ( moveMaster != this ) {
2745 moveMaster->GotoPosition2();
2749 SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
2751 if ( ( moverState == MOVER_POS2 ) || ( moverState == MOVER_1TO2 ) ) {
2752 // already there, or on the way
2756 if ( moverState == MOVER_POS1 ) {
2757 MatchActivateTeam( MOVER_1TO2, gameLocal.slow.time );
2760 ProcessEvent( &EV_Mover_OpenPortal );
2765 // only partway up before reversing
2766 if ( moverState == MOVER_2TO1 ) {
2767 // use the physics times because this might be executed during the physics simulation
2768 partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
2769 assert( partial >= 0 );
2770 if ( partial < 0 ) {
2773 MatchActivateTeam( MOVER_1TO2, physicsObj.GetTime() - partial );
2774 // if already at at position 2 (partial == duration) execute the reached event
2775 if ( partial >= duration ) {
2776 Event_Reached_BinaryMover();
2783 idMover_Binary::UpdateBuddies
2786 void idMover_Binary::UpdateBuddies( int val ) {
2789 if ( updateStatus == 2 ) {
2791 for ( i = 0; i < c; i++ ) {
2792 idEntity *buddy = gameLocal.FindEntity( buddies[i] );
2794 buddy->SetShaderParm( SHADERPARM_MODE, val );
2795 buddy->UpdateVisuals();
2803 idMover_Binary::SetGuiStates
2806 void idMover_Binary::SetGuiStates( const char *state ) {
2807 if ( guiTargets.Num() ) {
2808 SetGuiState( "movestate", state );
2811 idMover_Binary *mb = activateChain;
2813 if ( mb->guiTargets.Num() ) {
2814 mb->SetGuiState( "movestate", state );
2816 mb = mb->activateChain;
2822 idMover_Binary::Use_BinaryMover
2825 void idMover_Binary::Use_BinaryMover( idEntity *activator ) {
2826 // only the master should be used
2827 if ( moveMaster != this ) {
2828 moveMaster->Use_BinaryMover( activator );
2836 activatedBy = activator;
2838 if ( moverState == MOVER_POS1 ) {
2839 // FIXME: start moving USERCMD_MSEC later, because if this was player
2840 // triggered, gameLocal.time hasn't been advanced yet
2841 MatchActivateTeam( MOVER_1TO2, gameLocal.slow.time + USERCMD_MSEC );
2843 SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
2845 ProcessEvent( &EV_Mover_OpenPortal );
2849 // if all the way up, just delay before coming down
2850 if ( moverState == MOVER_POS2 ) {
2851 idMover_Binary *slave;
2857 SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
2859 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2860 slave->CancelEvents( &EV_Mover_ReturnToPos1 );
2861 slave->PostEventSec( &EV_Mover_ReturnToPos1, spawnArgs.GetBool( "toggle" ) ? 0 : wait );
2866 // only partway down before reversing
2867 if ( moverState == MOVER_2TO1 ) {
2872 // only partway up before reversing
2873 if ( moverState == MOVER_1TO2 ) {
2881 idMover_Binary::Event_Use_BinaryMover
2884 void idMover_Binary::Event_Use_BinaryMover( idEntity *activator ) {
2885 Use_BinaryMover( activator );
2890 idMover_Binary::PreBind
2893 void idMover_Binary::PreBind( void ) {
2894 pos1 = GetWorldCoordinates( pos1 );
2895 pos2 = GetWorldCoordinates( pos2 );
2900 idMover_Binary::PostBind
2903 void idMover_Binary::PostBind( void ) {
2904 pos1 = GetLocalCoordinates( pos1 );
2905 pos2 = GetLocalCoordinates( pos2 );
2910 idMover_Binary::FindGuiTargets
2913 void idMover_Binary::FindGuiTargets( void ) {
2914 gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
2918 ==============================
2919 idMover_Binary::SetGuiState
2921 key/val will be set to any renderEntity->gui's on the list
2922 ==============================
2924 void idMover_Binary::SetGuiState( const char *key, const char *val ) const {
2927 for( i = 0; i < guiTargets.Num(); i++ ) {
2928 idEntity *ent = guiTargets[ i ].GetEntity();
2930 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
2931 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
2932 ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
2933 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
2936 ent->UpdateVisuals();
2943 idMover_Binary::Event_InitGuiTargets
2946 void idMover_Binary::Event_FindGuiTargets( void ) {
2952 idMover_Binary::Event_InitGuiTargets
2955 void idMover_Binary::Event_InitGuiTargets( void ) {
2956 if ( guiTargets.Num() ) {
2957 SetGuiState( "movestate", guiBinaryMoverStates[MOVER_POS1] );
2963 idMover_Binary::InitSpeed
2965 pos1, pos2, and speed are passed in so the movement delta can be calculated
2968 void idMover_Binary::InitSpeed( idVec3 &mpos1, idVec3 &mpos2, float mspeed, float maccelTime, float mdecelTime ) {
2976 accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
2977 decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
2979 speed = mspeed ? mspeed : 100;
2981 // calculate time to reach second position from speed
2983 distance = move.Length();
2984 duration = idPhysics::SnapTimeToPhysicsFrame( distance * 1000 / speed );
2985 if ( duration <= 0 ) {
2989 moverState = MOVER_POS1;
2991 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
2992 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
2995 PostEventMS( &EV_Mover_InitGuiTargets, 0 );
3000 idMover_Binary::InitTime
3002 pos1, pos2, and time are passed in so the movement delta can be calculated
3005 void idMover_Binary::InitTime( idVec3 &mpos1, idVec3 &mpos2, float mtime, float maccelTime, float mdecelTime ) {
3010 accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
3011 decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
3013 duration = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mtime ) );
3014 if ( duration <= 0 ) {
3018 moverState = MOVER_POS1;
3020 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
3021 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
3024 PostEventMS( &EV_Mover_InitGuiTargets, 0 );
3029 idMover_Binary::SetBlocked
3032 void idMover_Binary::SetBlocked( bool b ) {
3033 for ( idMover_Binary *slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
3036 const idKeyValue *kv = slave->spawnArgs.MatchPrefix( "triggerBlocked" );
3038 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
3040 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
3042 kv = slave->spawnArgs.MatchPrefix( "triggerBlocked", kv );
3050 idMover_Binary::IsBlocked
3053 bool idMover_Binary::IsBlocked( void ) {
3059 idMover_Binary::GetActivator
3062 idEntity *idMover_Binary::GetActivator( void ) const {
3063 return activatedBy.GetEntity();
3068 idMover_Binary::WriteToSnapshot
3071 void idMover_Binary::WriteToSnapshot( idBitMsgDelta &msg ) const {
3072 physicsObj.WriteToSnapshot( msg );
3073 msg.WriteBits( moverState, 3 );
3074 WriteBindToSnapshot( msg );
3079 idMover_Binary::ReadFromSnapshot
3082 void idMover_Binary::ReadFromSnapshot( const idBitMsgDelta &msg ) {
3083 moverState_t oldMoverState = moverState;
3085 physicsObj.ReadFromSnapshot( msg );
3086 moverState = (moverState_t) msg.ReadBits( 3 );
3087 ReadBindFromSnapshot( msg );
3089 if ( msg.HasChanged() ) {
3090 if ( moverState != oldMoverState ) {
3091 UpdateMoverSound( moverState );
3099 idMover_Binary::SetPortalState
3102 void idMover_Binary::SetPortalState( bool open ) {
3103 assert( areaPortal );
3104 gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
3108 ===============================================================================
3112 A use can be triggered either by a touch function, by being shot, or by being
3113 targeted by another entity.
3115 ===============================================================================
3118 const idEventDef EV_Door_StartOpen( "<startOpen>", NULL );
3119 const idEventDef EV_Door_SpawnDoorTrigger( "<spawnDoorTrigger>", NULL );
3120 const idEventDef EV_Door_SpawnSoundTrigger( "<spawnSoundTrigger>", NULL );
3121 const idEventDef EV_Door_Open( "open", NULL );
3122 const idEventDef EV_Door_Close( "close", NULL );
3123 const idEventDef EV_Door_Lock( "lock", "d" );
3124 const idEventDef EV_Door_IsOpen( "isOpen", NULL, 'f' );
3125 const idEventDef EV_Door_IsLocked( "isLocked", NULL, 'f' );
3127 CLASS_DECLARATION( idMover_Binary, idDoor )
3128 EVENT( EV_TeamBlocked, idDoor::Event_TeamBlocked )
3129 EVENT( EV_PartBlocked, idDoor::Event_PartBlocked )
3130 EVENT( EV_Touch, idDoor::Event_Touch )
3131 EVENT( EV_Activate, idDoor::Event_Activate )
3132 EVENT( EV_Door_StartOpen, idDoor::Event_StartOpen )
3133 EVENT( EV_Door_SpawnDoorTrigger, idDoor::Event_SpawnDoorTrigger )
3134 EVENT( EV_Door_SpawnSoundTrigger, idDoor::Event_SpawnSoundTrigger )
3135 EVENT( EV_Door_Open, idDoor::Event_Open )
3136 EVENT( EV_Door_Close, idDoor::Event_Close )
3137 EVENT( EV_Door_Lock, idDoor::Event_Lock )
3138 EVENT( EV_Door_IsOpen, idDoor::Event_IsOpen )
3139 EVENT( EV_Door_IsLocked, idDoor::Event_Locked )
3140 EVENT( EV_ReachedPos, idDoor::Event_Reached_BinaryMover )
3141 EVENT( EV_SpectatorTouch, idDoor::Event_SpectatorTouch )
3142 EVENT( EV_Mover_OpenPortal, idDoor::Event_OpenPortal )
3143 EVENT( EV_Mover_ClosePortal, idDoor::Event_ClosePortal )
3151 idDoor::idDoor( void ) {
3155 aas_area_closed = false;
3159 nextSndTriggerTime = 0;
3160 localTriggerOrigin.Zero();
3161 localTriggerAxis.Identity();
3165 companionDoor = NULL;
3166 normalAxisIndex = 0;
3174 idDoor::~idDoor( void ) {
3188 void idDoor::Save( idSaveGame *savefile ) const {
3190 savefile->WriteFloat( triggersize );
3191 savefile->WriteBool( crusher );
3192 savefile->WriteBool( noTouch );
3193 savefile->WriteBool( aas_area_closed );
3194 savefile->WriteString( buddyStr );
3195 savefile->WriteInt( nextSndTriggerTime );
3197 savefile->WriteVec3( localTriggerOrigin );
3198 savefile->WriteMat3( localTriggerAxis );
3200 savefile->WriteString( requires );
3201 savefile->WriteInt( removeItem );
3202 savefile->WriteString( syncLock );
3203 savefile->WriteInt( normalAxisIndex );
3205 savefile->WriteClipModel( trigger );
3206 savefile->WriteClipModel( sndTrigger );
3208 savefile->WriteObject( companionDoor );
3216 void idDoor::Restore( idRestoreGame *savefile ) {
3218 savefile->ReadFloat( triggersize );
3219 savefile->ReadBool( crusher );
3220 savefile->ReadBool( noTouch );
3221 savefile->ReadBool( aas_area_closed );
3222 SetAASAreaState( aas_area_closed );
3223 savefile->ReadString( buddyStr );
3224 savefile->ReadInt( nextSndTriggerTime );
3226 savefile->ReadVec3( localTriggerOrigin );
3227 savefile->ReadMat3( localTriggerAxis );
3229 savefile->ReadString( requires );
3230 savefile->ReadInt( removeItem );
3231 savefile->ReadString( syncLock );
3232 savefile->ReadInt( normalAxisIndex );
3234 savefile->ReadClipModel( trigger );
3235 savefile->ReadClipModel( sndTrigger );
3237 savefile->ReadObject( reinterpret_cast<idClass *&>( companionDoor ) );
3245 void idDoor::Spawn( void ) {
3256 // get the direction to move
3257 if ( !spawnArgs.GetFloat( "movedir", "0", dir ) ) {
3258 // no movedir, so angle defines movement direction and not orientation,
3259 // a la oldschool Quake
3260 SetAngles( ang_zero );
3261 spawnArgs.GetFloat( "angle", "0", dir );
3263 GetMovedir( dir, movedir );
3265 // default speed of 400
3266 spawnArgs.GetFloat( "speed", "400", speed );
3268 // default wait of 2 seconds
3269 spawnArgs.GetFloat( "wait", "3", wait );
3271 // default lip of 8 units
3272 spawnArgs.GetFloat( "lip", "8", lip );
3274 // by default no damage
3275 spawnArgs.GetFloat( "damage", "0", damage );
3278 spawnArgs.GetFloat( "triggersize", "120", triggersize );
3280 spawnArgs.GetBool( "crusher", "0", crusher );
3281 spawnArgs.GetBool( "start_open", "0", start_open );
3282 spawnArgs.GetBool( "no_touch", "0", noTouch );
3284 spawnArgs.GetBool( "player_only", "0", playerOnly );
3287 // expects syncLock to be a door that must be closed before this door will open
3288 spawnArgs.GetString( "syncLock", "", syncLock );
3290 spawnArgs.GetString( "buddy", "", buddyStr );
3292 spawnArgs.GetString( "requires", "", requires );
3293 spawnArgs.GetInt( "removeItem", "0", removeItem );
3295 // ever separate piece of a door is considered solid when other team mates push entities
3296 fl.solidForTeam = true;
3298 // first position at start
3299 pos1 = GetPhysics()->GetOrigin();
3301 // calculate second position
3302 abs_movedir[0] = idMath::Fabs( movedir[ 0 ] );
3303 abs_movedir[1] = idMath::Fabs( movedir[ 1 ] );
3304 abs_movedir[2] = idMath::Fabs( movedir[ 2 ] );
3305 size = GetPhysics()->GetAbsBounds()[1] - GetPhysics()->GetAbsBounds()[0];
3306 distance = ( abs_movedir * size ) - lip;
3307 pos2 = pos1 + distance * movedir;
3309 // if "start_open", reverse position 1 and 2
3311 // post it after EV_SpawnBind
3312 PostEventMS( &EV_Door_StartOpen, 1 );
3315 if ( spawnArgs.GetFloat( "time", "1", time ) ) {
3316 InitTime( pos1, pos2, time, 0, 0 );
3318 InitSpeed( pos1, pos2, speed, 0, 0 );
3321 if ( moveMaster == this ) {
3323 fl.takedamage = true;
3325 if ( noTouch || health ) {
3326 // non touch/shoot doors
3327 PostEventMS( &EV_Mover_MatchTeam, 0, moverState, gameLocal.slow.time );
3329 const char *sndtemp = spawnArgs.GetString( "snd_locked" );
3330 if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
3331 PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
3335 PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
3339 // see if we are on an areaportal
3340 areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
3341 if ( !start_open ) {
3343 ProcessEvent( &EV_Mover_ClosePortal );
3347 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, true );
3352 int locked = spawnArgs.GetInt( "locked" );
3354 // make sure all members of the team get locked
3355 PostEventMS( &EV_Door_Lock, 0, locked );
3358 if ( spawnArgs.GetBool( "continuous" ) ) {
3359 PostEventSec( &EV_Activate, spawnArgs.GetFloat( "delay" ), this );
3362 // sounds have a habit of stuttering when portals close, so make them unoccluded
3363 refSound.parms.soundShaderFlags |= SSF_NO_OCCLUSION;
3365 companionDoor = NULL;
3376 void idDoor::Think( void ) {
3377 idVec3 masterOrigin;
3380 idMover_Binary::Think();
3382 if ( thinkFlags & TH_PHYSICS ) {
3383 // update trigger position
3384 if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
3386 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
3389 sndTrigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
3400 void idDoor::PreBind( void ) {
3401 idMover_Binary::PreBind();
3409 void idDoor::PostBind( void ) {
3410 idMover_Binary::PostBind();
3411 GetLocalTriggerPosition( trigger ? trigger : sndTrigger );
3416 idDoor::SetAASAreaState
3419 void idDoor::SetAASAreaState( bool closed ) {
3420 aas_area_closed = closed;
3421 gameLocal.SetAASAreaState( physicsObj.GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL|AREACONTENTS_OBSTACLE, closed );
3429 void idDoor::Hide( void ) {
3430 idMover_Binary *slave;
3431 idMover_Binary *master;
3435 master = GetMoveMaster();
3436 if ( this != master ) {
3439 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
3440 if ( slave->IsType( idDoor::Type ) ) {
3441 slaveDoor = static_cast<idDoor *>( slave );
3442 companion = slaveDoor->companionDoor;
3443 if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
3446 if ( slaveDoor->trigger ) {
3447 slaveDoor->trigger->Disable();
3449 if ( slaveDoor->sndTrigger ) {
3450 slaveDoor->sndTrigger->Disable();
3452 if ( slaveDoor->areaPortal ) {
3453 slaveDoor->SetPortalState( true );
3455 slaveDoor->SetAASAreaState( false );
3457 slave->GetPhysics()->GetClipModel()->Disable();
3458 slave->idMover_Binary::Hide();
3468 void idDoor::Show( void ) {
3469 idMover_Binary *slave;
3470 idMover_Binary *master;
3474 master = GetMoveMaster();
3475 if ( this != master ) {
3478 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
3479 if ( slave->IsType( idDoor::Type ) ) {
3480 slaveDoor = static_cast<idDoor *>( slave );
3481 companion = slaveDoor->companionDoor;
3482 if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
3485 if ( slaveDoor->trigger ) {
3486 slaveDoor->trigger->Enable();
3488 if ( slaveDoor->sndTrigger ) {
3489 slaveDoor->sndTrigger->Enable();
3491 if ( slaveDoor->areaPortal && ( slaveDoor->moverState == MOVER_POS1 ) ) {
3492 slaveDoor->SetPortalState( false );
3494 slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
3496 slave->GetPhysics()->GetClipModel()->Enable();
3497 slave->idMover_Binary::Show();
3504 idDoor::GetLocalTriggerPosition
3507 void idDoor::GetLocalTriggerPosition( const idClipModel *trigger ) {
3515 GetMasterPosition( origin, axis );
3516 localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
3517 localTriggerAxis = trigger->GetAxis() * axis.Transpose();
3525 void idDoor::Use( idEntity *other, idEntity *activator ) {
3526 if ( gameLocal.RequirementMet( activator, requires, removeItem ) ) {
3527 if ( syncLock.Length() ) {
3528 idEntity *sync = gameLocal.FindEntity( syncLock );
3529 if ( sync && sync->IsType( idDoor::Type ) ) {
3530 if ( static_cast<idDoor *>( sync )->IsOpen() ) {
3535 ActivateTargets( activator );
3536 Use_BinaryMover( activator );
3545 void idDoor::Open( void ) {
3554 void idDoor::Close( void ) {
3563 void idDoor::Lock( int f ) {
3564 idMover_Binary *other;
3566 // lock all the doors on the team
3567 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
3568 if ( other->IsType( idDoor::Type ) ) {
3569 idDoor *door = static_cast<idDoor *>( other );
3570 if ( other == moveMaster ) {
3571 if ( door->sndTrigger == NULL ) {
3572 // in this case the sound trigger never got spawned
3573 const char *sndtemp = door->spawnArgs.GetString( "snd_locked" );
3574 if ( sndtemp && *sndtemp ) {
3575 door->PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
3578 if ( !f && ( door->spawnArgs.GetInt( "locked" ) != 0 ) ) {
3579 door->StartSound( "snd_unlocked", SND_CHANNEL_ANY, 0, false, NULL );
3582 door->spawnArgs.SetInt( "locked", f );
3583 if ( ( f == 0 ) || ( !IsHidden() && ( door->moverState == MOVER_POS1 ) ) ) {
3584 door->SetAASAreaState( f != 0 );
3599 int idDoor::IsLocked( void ) {
3600 return spawnArgs.GetInt( "locked" );
3608 bool idDoor::IsOpen( void ) {
3609 return ( moverState != MOVER_POS1 );
3617 bool idDoor::IsNoTouch( void ) {
3624 idDoor::AllowPlayerOnly
3627 bool idDoor::AllowPlayerOnly( idEntity *ent ) {
3628 if ( playerOnly && !ent->IsType(idPlayer::Type) ) {
3637 ======================
3638 idDoor::CalcTriggerBounds
3640 Calcs bounds for a trigger.
3641 ======================
3643 void idDoor::CalcTriggerBounds( float size, idBounds &bounds ) {
3644 idMover_Binary *other;
3648 // find the bounds of everything on the team
3649 bounds = GetPhysics()->GetAbsBounds();
3651 fl.takedamage = true;
3652 for( other = activateChain; other != NULL; other = other->GetActivateChain() ) {
3653 if ( other->IsType( idDoor::Type ) ) {
3654 // find the bounds of everything on the team
3655 bounds.AddBounds( other->GetPhysics()->GetAbsBounds() );
3657 // set all of the slaves as shootable
3658 other->fl.takedamage = true;
3662 // find the thinnest axis, which will be the one we expand
3664 for ( i = 1 ; i < 3 ; i++ ) {
3665 if ( bounds[1][ i ] - bounds[0][ i ] < bounds[1][ best ] - bounds[0][ best ] ) {
3669 normalAxisIndex = best;
3670 bounds[0][ best ] -= size;
3671 bounds[1][ best ] += size;
3672 bounds[0] -= GetPhysics()->GetOrigin();
3673 bounds[1] -= GetPhysics()->GetOrigin();
3677 ======================
3678 idDoor::Event_StartOpen
3680 if "start_open", reverse position 1 and 2
3681 ======================
3683 void idDoor::Event_StartOpen( void ) {
3687 // if "start_open", reverse position 1 and 2
3689 pos2 = GetPhysics()->GetOrigin();
3691 spawnArgs.GetFloat( "speed", "400", speed );
3693 if ( spawnArgs.GetFloat( "time", "1", time ) ) {
3694 InitTime( pos1, pos2, time, 0, 0 );
3696 InitSpeed( pos1, pos2, speed, 0, 0 );
3701 ======================
3702 idDoor::Event_SpawnDoorTrigger
3704 All of the parts of a door have been spawned, so create
3705 a trigger that encloses all of them.
3706 ======================
3708 void idDoor::Event_SpawnDoorTrigger( void ) {
3710 idMover_Binary *other;
3714 // already have a trigger, so don't spawn a new one.
3718 // check if any of the doors are marked as toggled
3720 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
3721 if ( other->IsType( idDoor::Type ) && other->spawnArgs.GetBool( "toggle" ) ) {
3728 // mark them all as toggled
3729 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
3730 if ( other->IsType( idDoor::Type ) ) {
3731 other->spawnArgs.Set( "toggle", "1" );
3734 // don't spawn trigger
3738 const char *sndtemp = spawnArgs.GetString( "snd_locked" );
3739 if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
3740 PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
3743 CalcTriggerBounds( triggersize, bounds );
3745 // create a trigger clip model
3746 trigger = new idClipModel( idTraceModel( bounds ) );
3747 trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
3748 trigger->SetContents( CONTENTS_TRIGGER );
3750 GetLocalTriggerPosition( trigger );
3752 MatchActivateTeam( moverState, gameLocal.slow.time );
3756 ======================
3757 idDoor::Event_SpawnSoundTrigger
3759 Spawn a sound trigger to activate locked sound if it exists.
3760 ======================
3762 void idDoor::Event_SpawnSoundTrigger( void ) {
3769 CalcTriggerBounds( triggersize * 0.5f, bounds );
3771 // create a trigger clip model
3772 sndTrigger = new idClipModel( idTraceModel( bounds ) );
3773 sndTrigger->Link( gameLocal.clip, this, 254, GetPhysics()->GetOrigin(), mat3_identity );
3774 sndTrigger->SetContents( CONTENTS_TRIGGER );
3776 GetLocalTriggerPosition( sndTrigger );
3781 idDoor::Event_Reached_BinaryMover
3784 void idDoor::Event_Reached_BinaryMover( void ) {
3785 if ( moverState == MOVER_2TO1 ) {
3786 SetBlocked( false );
3787 const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerClosed" );
3789 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
3791 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
3793 kv = spawnArgs.MatchPrefix( "triggerClosed", kv );
3795 } else if ( moverState == MOVER_1TO2 ) {
3796 const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerOpened" );
3798 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
3800 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
3802 kv = spawnArgs.MatchPrefix( "triggerOpened", kv );
3805 idMover_Binary::Event_Reached_BinaryMover();
3810 idDoor::Blocked_Door
3813 void idDoor::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
3817 return; // crushers don't reverse
3820 // reverse direction
3821 Use_BinaryMover( moveMaster->GetActivator() );
3823 if ( companionDoor ) {
3824 companionDoor->ProcessEvent( &EV_TeamBlocked, blockedEntity, blockingEntity );
3830 idDoor::SetCompanion
3833 void idDoor::SetCompanion( idDoor *door ) {
3834 companionDoor = door;
3839 idDoor::Event_PartBlocked
3842 void idDoor::Event_PartBlocked( idEntity *blockingEntity ) {
3843 if ( damage > 0.0f ) {
3844 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
3853 void idDoor::Event_Touch( idEntity *other, trace_t *trace ) {
3854 idVec3 contact, translate;
3855 idVec3 planeaxis1, planeaxis2, normal;
3862 if ( trigger && trace->c.id == trigger->GetId() ) {
3863 if ( !IsNoTouch() && !IsLocked() && GetMoverState() != MOVER_1TO2 ) {
3865 if ( AllowPlayerOnly( other ) ) {
3872 } else if ( sndTrigger && trace->c.id == sndTrigger->GetId() ) {
3873 if ( other && other->IsType( idPlayer::Type ) && IsLocked() && gameLocal.slow.time > nextSndTriggerTime ) {
3874 StartSound( "snd_locked", SND_CHANNEL_ANY, 0, false, NULL );
3875 nextSndTriggerTime = gameLocal.slow.time + 10000;
3882 idDoor::Event_SpectatorTouch
3885 void idDoor::Event_SpectatorTouch( idEntity *other, trace_t *trace ) {
3886 idVec3 contact, translate, normal;
3890 assert( other && other->IsType( idPlayer::Type ) && static_cast< idPlayer * >( other )->spectating );
3892 p = static_cast< idPlayer * >( other );
3893 // avoid flicker when stopping right at clip box boundaries
3894 if ( p->lastSpectateTeleport > gameLocal.slow.time - 1000 ) {
3897 if ( trigger && !IsOpen() ) {
3898 // teleport to the other side, center to the middle of the trigger brush
3899 bounds = trigger->GetAbsBounds();
3900 contact = trace->endpos - bounds.GetCenter();
3901 translate = bounds.GetCenter();
3903 normal[ normalAxisIndex ] = 1.0f;
3904 if ( normal * contact > 0 ) {
3905 translate[ normalAxisIndex ] += ( bounds[ 0 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
3907 translate[ normalAxisIndex ] += ( bounds[ 1 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
3909 p->SetOrigin( translate );
3910 p->lastSpectateTeleport = gameLocal.slow.time;
3916 idDoor::Event_Activate
3919 void idDoor::Event_Activate( idEntity *activator ) {
3922 if ( spawnArgs.GetInt( "locked" ) ) {
3924 PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
3926 if ( buddyStr.Length() ) {
3927 idEntity *buddy = gameLocal.FindEntity( buddyStr );
3929 buddy->SetShaderParm( SHADERPARM_MODE, 1 );
3930 buddy->UpdateVisuals();
3934 old_lock = spawnArgs.GetInt( "locked" );
3936 if ( old_lock == 2 ) {
3941 if ( syncLock.Length() ) {
3942 idEntity *sync = gameLocal.FindEntity( syncLock );
3943 if ( sync && sync->IsType( idDoor::Type ) ) {
3944 if ( static_cast<idDoor *>( sync )->IsOpen() ) {
3950 ActivateTargets( activator );
3952 renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
3955 Use_BinaryMover( activator );
3963 void idDoor::Event_Open( void ) {
3972 void idDoor::Event_Close( void ) {
3981 void idDoor::Event_Lock( int f ) {
3987 idDoor::Event_IsOpen
3990 void idDoor::Event_IsOpen( void ) {
3994 idThread::ReturnFloat( state );
3999 idDoor::Event_Locked
4002 void idDoor::Event_Locked( void ) {
4003 idThread::ReturnFloat( spawnArgs.GetInt("locked") );
4008 idDoor::Event_OpenPortal
4010 Sets the portal associtated with this door to be open
4013 void idDoor::Event_OpenPortal( void ) {
4014 idMover_Binary *slave;
4017 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
4018 if ( slave->IsType( idDoor::Type ) ) {
4019 slaveDoor = static_cast<idDoor *>( slave );
4020 if ( slaveDoor->areaPortal ) {
4021 slaveDoor->SetPortalState( true );
4023 slaveDoor->SetAASAreaState( false );
4030 idDoor::Event_ClosePortal
4032 Sets the portal associtated with this door to be closed
4035 void idDoor::Event_ClosePortal( void ) {
4036 idMover_Binary *slave;
4039 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
4040 if ( !slave->IsHidden() ) {
4041 if ( slave->IsType( idDoor::Type ) ) {
4042 slaveDoor = static_cast<idDoor *>( slave );
4043 if ( slaveDoor->areaPortal ) {
4044 slaveDoor->SetPortalState( false );
4046 slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
4054 ===============================================================================
4058 ===============================================================================
4061 CLASS_DECLARATION( idMover_Binary, idPlat )
4062 EVENT( EV_Touch, idPlat::Event_Touch )
4063 EVENT( EV_TeamBlocked, idPlat::Event_TeamBlocked )
4064 EVENT( EV_PartBlocked, idPlat::Event_PartBlocked )
4072 idPlat::idPlat( void ) {
4074 localTriggerOrigin.Zero();
4075 localTriggerAxis.Identity();
4083 idPlat::~idPlat( void ) {
4094 void idPlat::Save( idSaveGame *savefile ) const {
4095 savefile->WriteClipModel( trigger );
4096 savefile->WriteVec3( localTriggerOrigin );
4097 savefile->WriteMat3( localTriggerAxis );
4105 void idPlat::Restore( idRestoreGame *savefile ) {
4106 savefile->ReadClipModel( trigger );
4107 savefile->ReadVec3( localTriggerOrigin );
4108 savefile->ReadMat3( localTriggerAxis );
4116 void idPlat::Spawn( void ) {
4125 spawnArgs.GetFloat( "speed", "100", speed );
4126 spawnArgs.GetFloat( "damage", "0", damage );
4127 spawnArgs.GetFloat( "wait", "1", wait );
4128 spawnArgs.GetFloat( "lip", "8", lip );
4129 spawnArgs.GetFloat( "accel_time", "0.25", accel );
4130 spawnArgs.GetFloat( "decel_time", "0.25", decel );
4132 // create second position
4133 if ( !spawnArgs.GetFloat( "height", "0", height ) ) {
4134 height = ( GetPhysics()->GetBounds()[1][2] - GetPhysics()->GetBounds()[0][2] ) - lip;
4137 spawnArgs.GetBool( "no_touch", "0", noTouch );
4139 // pos1 is the rest (bottom) position, pos2 is the top
4140 pos2 = GetPhysics()->GetOrigin();
4144 if ( spawnArgs.GetFloat( "time", "1", time ) ) {
4145 InitTime( pos1, pos2, time, accel, decel );
4147 InitSpeed( pos1, pos2, speed, accel, decel );
4150 SetMoverState( MOVER_POS1, gameLocal.slow.time );
4153 // spawn the trigger if one hasn't been custom made
4156 SpawnPlatTrigger( pos1 );
4165 void idPlat::Think( void ) {
4166 idVec3 masterOrigin;
4169 idMover_Binary::Think();
4171 if ( thinkFlags & TH_PHYSICS ) {
4172 // update trigger position
4173 if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
4175 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
4186 void idPlat::PreBind( void ) {
4187 idMover_Binary::PreBind();
4195 void idPlat::PostBind( void ) {
4196 idMover_Binary::PostBind();
4197 GetLocalTriggerPosition( trigger );
4202 idPlat::GetLocalTriggerPosition
4205 void idPlat::GetLocalTriggerPosition( const idClipModel *trigger ) {
4213 GetMasterPosition( origin, axis );
4214 localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
4215 localTriggerAxis = trigger->GetAxis() * axis.Transpose();
4220 idPlat::SpawnPlatTrigger
4223 void idPlat::SpawnPlatTrigger( idVec3 &pos ) {
4228 // the middle trigger will be a thin trigger just
4229 // above the starting position
4231 bounds = GetPhysics()->GetBounds();
4233 tmin[0] = bounds[0][0] + 33;
4234 tmin[1] = bounds[0][1] + 33;
4235 tmin[2] = bounds[0][2];
4237 tmax[0] = bounds[1][0] - 33;
4238 tmax[1] = bounds[1][1] - 33;
4239 tmax[2] = bounds[1][2] + 8;
4241 if ( tmax[0] <= tmin[0] ) {
4242 tmin[0] = ( bounds[0][0] + bounds[1][0] ) * 0.5f;
4243 tmax[0] = tmin[0] + 1;
4245 if ( tmax[1] <= tmin[1] ) {
4246 tmin[1] = ( bounds[0][1] + bounds[1][1] ) * 0.5f;
4247 tmax[1] = tmin[1] + 1;
4250 trigger = new idClipModel( idTraceModel( idBounds( tmin, tmax ) ) );
4251 trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
4252 trigger->SetContents( CONTENTS_TRIGGER );
4260 void idPlat::Event_Touch( idEntity *other, trace_t *trace ) {
4261 if ( !other->IsType( idPlayer::Type ) ) {
4265 if ( ( GetMoverState() == MOVER_POS1 ) && trigger && ( trace->c.id == trigger->GetId() ) && ( other->health > 0 ) ) {
4266 Use_BinaryMover( other );
4272 idPlat::Event_TeamBlocked
4275 void idPlat::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
4276 // reverse direction
4277 Use_BinaryMover( activatedBy.GetEntity() );
4282 idPlat::Event_PartBlocked
4285 void idPlat::Event_PartBlocked( idEntity *blockingEntity ) {
4286 if ( damage > 0.0f ) {
4287 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
4293 ===============================================================================
4297 ===============================================================================
4300 CLASS_DECLARATION( idEntity, idMover_Periodic )
4301 EVENT( EV_TeamBlocked, idMover_Periodic::Event_TeamBlocked )
4302 EVENT( EV_PartBlocked, idMover_Periodic::Event_PartBlocked )
4307 idMover_Periodic::idMover_Periodic
4310 idMover_Periodic::idMover_Periodic( void ) {
4312 fl.neverDormant = false;
4317 idMover_Periodic::Spawn
4320 void idMover_Periodic::Spawn( void ) {
4321 spawnArgs.GetFloat( "damage", "0", damage );
4322 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
4323 GetPhysics()->SetContents( 0 );
4329 idMover_Periodic::Save
4332 void idMover_Periodic::Save( idSaveGame *savefile ) const {
4333 savefile->WriteFloat( damage );
4334 savefile->WriteStaticObject( physicsObj );
4339 idMover_Periodic::Restore
4342 void idMover_Periodic::Restore( idRestoreGame *savefile ) {
4343 savefile->ReadFloat( damage );
4344 savefile->ReadStaticObject( physicsObj );
4345 RestorePhysics( &physicsObj );
4350 idMover_Periodic::Think
4353 void idMover_Periodic::Think( void ) {
4354 // if we are completely closed off from the player, don't do anything at all
4355 if ( CheckDormant() ) {
4365 idMover_Periodic::Event_TeamBlocked
4368 void idMover_Periodic::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
4373 idMover_Periodic::Event_PartBlocked
4376 void idMover_Periodic::Event_PartBlocked( idEntity *blockingEntity ) {
4377 if ( damage > 0.0f ) {
4378 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
4384 idMover_Periodic::WriteToSnapshot
4387 void idMover_Periodic::WriteToSnapshot( idBitMsgDelta &msg ) const {
4388 physicsObj.WriteToSnapshot( msg );
4389 WriteBindToSnapshot( msg );
4394 idMover_Periodic::ReadFromSnapshot
4397 void idMover_Periodic::ReadFromSnapshot( const idBitMsgDelta &msg ) {
4398 physicsObj.ReadFromSnapshot( msg );
4399 ReadBindFromSnapshot( msg );
4401 if ( msg.HasChanged() ) {
4408 ===============================================================================
4412 ===============================================================================
4415 CLASS_DECLARATION( idMover_Periodic, idRotater )
4416 EVENT( EV_Activate, idRotater::Event_Activate )
4421 idRotater::idRotater
4424 idRotater::idRotater( void ) {
4433 void idRotater::Spawn( void ) {
4434 physicsObj.SetSelf( this );
4435 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4436 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4437 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4438 physicsObj.SetClipMask( MASK_SOLID );
4439 if ( !spawnArgs.GetBool( "nopush" ) ) {
4440 physicsObj.SetPusher( 0 );
4442 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, gameLocal.slow.time, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
4443 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
4444 SetPhysics( &physicsObj );
4446 if ( spawnArgs.GetBool( "start_on" ) ) {
4447 ProcessEvent( &EV_Activate, this );
4456 void idRotater::Save( idSaveGame *savefile ) const {
4457 activatedBy.Save( savefile );
4465 void idRotater::Restore( idRestoreGame *savefile ) {
4466 activatedBy.Restore( savefile );
4471 idRotater::Event_Activate
4474 void idRotater::Event_Activate( idEntity *activator ) {
4480 activatedBy = activator;
4484 if ( !spawnArgs.GetBool( "rotate" ) ) {
4485 spawnArgs.Set( "rotate", "1" );
4486 spawnArgs.GetFloat( "speed", "100", speed );
4487 spawnArgs.GetBool( "x_axis", "0", x_axis );
4488 spawnArgs.GetBool( "y_axis", "0", y_axis );
4490 // set the axis of rotation
4493 } else if ( y_axis ) {
4499 spawnArgs.Set( "rotate", "0" );
4502 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, physicsObj.GetAxis().ToAngles(), delta, ang_zero );
4507 ===============================================================================
4511 ===============================================================================
4514 CLASS_DECLARATION( idMover_Periodic, idBobber )
4522 idBobber::idBobber( void ) {
4530 void idBobber::Spawn( void ) {
4538 spawnArgs.GetFloat( "speed", "4", speed );
4539 spawnArgs.GetFloat( "height", "32", height );
4540 spawnArgs.GetFloat( "phase", "0", phase );
4541 spawnArgs.GetBool( "x_axis", "0", x_axis );
4542 spawnArgs.GetBool( "y_axis", "0", y_axis );
4544 // set the axis of bobbing
4545 delta = vec3_origin;
4547 delta[ 0 ] = height;
4548 } else if ( y_axis ) {
4549 delta[ 1 ] = height;
4551 delta[ 2 ] = height;
4554 physicsObj.SetSelf( this );
4555 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4556 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4557 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4558 physicsObj.SetClipMask( MASK_SOLID );
4559 if ( !spawnArgs.GetBool( "nopush" ) ) {
4560 physicsObj.SetPusher( 0 );
4562 physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, speed * 500, GetPhysics()->GetOrigin(), delta * 2.0f, vec3_origin );
4563 SetPhysics( &physicsObj );
4568 ===============================================================================
4572 ===============================================================================
4575 CLASS_DECLARATION( idMover_Periodic, idPendulum )
4580 idPendulum::idPendulum
4583 idPendulum::idPendulum( void ) {
4591 void idPendulum::Spawn( void ) {
4597 spawnArgs.GetFloat( "speed", "30", speed );
4598 spawnArgs.GetFloat( "phase", "0", phase );
4600 if ( spawnArgs.GetFloat( "freq", "", freq ) ) {
4601 if ( freq <= 0.0f ) {
4602 gameLocal.Error( "Invalid frequency on entity '%s'", GetName() );
4605 // find pendulum length
4606 length = idMath::Fabs( GetPhysics()->GetBounds()[0][2] );
4611 freq = 1 / ( idMath::TWO_PI ) * idMath::Sqrt( g_gravity.GetFloat() / ( 3 * length ) );
4614 physicsObj.SetSelf( this );
4615 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4616 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4617 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4618 physicsObj.SetClipMask( MASK_SOLID );
4619 if ( !spawnArgs.GetBool( "nopush" ) ) {
4620 physicsObj.SetPusher( 0 );
4622 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
4623 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, 500/freq, GetPhysics()->GetAxis().ToAngles(), idAngles( 0, 0, speed * 2.0f ), ang_zero );
4624 SetPhysics( &physicsObj );
4629 ===============================================================================
4633 ===============================================================================
4636 CLASS_DECLARATION( idMover_Periodic, idRiser )
4637 EVENT( EV_Activate, idRiser::Event_Activate )
4645 idRiser::idRiser( void ) {
4653 void idRiser::Spawn( void ) {
4654 physicsObj.SetSelf( this );
4655 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4656 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4657 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4659 physicsObj.SetClipMask( MASK_SOLID );
4660 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
4661 physicsObj.SetContents( 0 );
4663 if ( !spawnArgs.GetBool( "nopush" ) ) {
4664 physicsObj.SetPusher( 0 );
4666 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
4667 SetPhysics( &physicsObj );
4672 idRiser::Event_Activate
4675 void idRiser::Event_Activate( idEntity *activator ) {
4677 if ( !IsHidden() && spawnArgs.GetBool("hide") ) {
4685 spawnArgs.GetFloat( "time", "4", time );
4686 spawnArgs.GetFloat( "height", "32", height );
4688 delta = vec3_origin;
4689 delta[ 2 ] = height;
4691 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, time * 1000, physicsObj.GetOrigin(), delta, vec3_origin );