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 // a mover will update any gui entities in it's target list with
35 // a key/val pair of "mover" "state" from below.. guis can represent
36 // realtime info like this
38 static const char *guiBinaryMoverStates[] = {
47 ===============================================================================
51 ===============================================================================
54 const idEventDef EV_FindGuiTargets( "<FindGuiTargets>", NULL );
55 const idEventDef EV_TeamBlocked( "<teamblocked>", "ee" );
56 const idEventDef EV_PartBlocked( "<partblocked>", "e" );
57 const idEventDef EV_ReachedPos( "<reachedpos>", NULL );
58 const idEventDef EV_ReachedAng( "<reachedang>", NULL );
59 const idEventDef EV_PostRestore( "<postrestore>", "ddddd" );
60 const idEventDef EV_StopMoving( "stopMoving", NULL );
61 const idEventDef EV_StopRotating( "stopRotating", NULL );
62 const idEventDef EV_Speed( "speed", "f" );
63 const idEventDef EV_Time( "time", "f" );
64 const idEventDef EV_AccelTime( "accelTime", "f" );
65 const idEventDef EV_DecelTime( "decelTime", "f" );
66 const idEventDef EV_MoveTo( "moveTo", "e" );
67 const idEventDef EV_MoveToPos( "moveToPos", "v" );
68 const idEventDef EV_Move( "move", "ff" );
69 const idEventDef EV_MoveAccelerateTo( "accelTo", "ff" );
70 const idEventDef EV_MoveDecelerateTo( "decelTo", "ff" );
71 const idEventDef EV_RotateDownTo( "rotateDownTo", "df" );
72 const idEventDef EV_RotateUpTo( "rotateUpTo", "df" );
73 const idEventDef EV_RotateTo( "rotateTo", "v" );
74 const idEventDef EV_Rotate( "rotate", "v" );
75 const idEventDef EV_RotateOnce( "rotateOnce", "v" );
76 const idEventDef EV_Bob( "bob", "ffv" );
77 const idEventDef EV_Sway( "sway", "ffv" );
78 const idEventDef EV_Mover_OpenPortal( "openPortal" );
79 const idEventDef EV_Mover_ClosePortal( "closePortal" );
80 const idEventDef EV_AccelSound( "accelSound", "s" );
81 const idEventDef EV_DecelSound( "decelSound", "s" );
82 const idEventDef EV_MoveSound( "moveSound", "s" );
83 const idEventDef EV_Mover_InitGuiTargets( "<initguitargets>", NULL );
84 const idEventDef EV_EnableSplineAngles( "enableSplineAngles", NULL );
85 const idEventDef EV_DisableSplineAngles( "disableSplineAngles", NULL );
86 const idEventDef EV_RemoveInitialSplineAngles( "removeInitialSplineAngles", NULL );
87 const idEventDef EV_StartSpline( "startSpline", "e" );
88 const idEventDef EV_StopSpline( "stopSpline", NULL );
89 const idEventDef EV_IsMoving( "isMoving", NULL, 'd' );
90 const idEventDef EV_IsRotating( "isRotating", NULL, 'd' );
92 CLASS_DECLARATION( idEntity, idMover )
93 EVENT( EV_FindGuiTargets, idMover::Event_FindGuiTargets )
94 EVENT( EV_Thread_SetCallback, idMover::Event_SetCallback )
95 EVENT( EV_TeamBlocked, idMover::Event_TeamBlocked )
96 EVENT( EV_PartBlocked, idMover::Event_PartBlocked )
97 EVENT( EV_ReachedPos, idMover::Event_UpdateMove )
98 EVENT( EV_ReachedAng, idMover::Event_UpdateRotation )
99 EVENT( EV_PostRestore, idMover::Event_PostRestore )
100 EVENT( EV_StopMoving, idMover::Event_StopMoving )
101 EVENT( EV_StopRotating, idMover::Event_StopRotating )
102 EVENT( EV_Speed, idMover::Event_SetMoveSpeed )
103 EVENT( EV_Time, idMover::Event_SetMoveTime )
104 EVENT( EV_AccelTime, idMover::Event_SetAccellerationTime )
105 EVENT( EV_DecelTime, idMover::Event_SetDecelerationTime )
106 EVENT( EV_MoveTo, idMover::Event_MoveTo )
107 EVENT( EV_MoveToPos, idMover::Event_MoveToPos )
108 EVENT( EV_Move, idMover::Event_MoveDir )
109 EVENT( EV_MoveAccelerateTo, idMover::Event_MoveAccelerateTo )
110 EVENT( EV_MoveDecelerateTo, idMover::Event_MoveDecelerateTo )
111 EVENT( EV_RotateDownTo, idMover::Event_RotateDownTo )
112 EVENT( EV_RotateUpTo, idMover::Event_RotateUpTo )
113 EVENT( EV_RotateTo, idMover::Event_RotateTo )
114 EVENT( EV_Rotate, idMover::Event_Rotate )
115 EVENT( EV_RotateOnce, idMover::Event_RotateOnce )
116 EVENT( EV_Bob, idMover::Event_Bob )
117 EVENT( EV_Sway, idMover::Event_Sway )
118 EVENT( EV_Mover_OpenPortal, idMover::Event_OpenPortal )
119 EVENT( EV_Mover_ClosePortal, idMover::Event_ClosePortal )
120 EVENT( EV_AccelSound, idMover::Event_SetAccelSound )
121 EVENT( EV_DecelSound, idMover::Event_SetDecelSound )
122 EVENT( EV_MoveSound, idMover::Event_SetMoveSound )
123 EVENT( EV_Mover_InitGuiTargets, idMover::Event_InitGuiTargets )
124 EVENT( EV_EnableSplineAngles, idMover::Event_EnableSplineAngles )
125 EVENT( EV_DisableSplineAngles, idMover::Event_DisableSplineAngles )
126 EVENT( EV_RemoveInitialSplineAngles, idMover::Event_RemoveInitialSplineAngles )
127 EVENT( EV_StartSpline, idMover::Event_StartSpline )
128 EVENT( EV_StopSpline, idMover::Event_StopSpline )
129 EVENT( EV_Activate, idMover::Event_Activate )
130 EVENT( EV_IsMoving, idMover::Event_IsMoving )
131 EVENT( EV_IsRotating, idMover::Event_IsRotating )
139 idMover::idMover( void ) {
140 memset( &move, 0, sizeof( move ) );
141 memset( &rot, 0, sizeof( rot ) );
146 dest_position.Zero();
152 stopRotation = false;
153 useSplineAngles = true;
154 lastCommand = MOVER_NONE;
157 fl.networkSync = true;
165 void idMover::Save( idSaveGame *savefile ) const {
168 savefile->WriteStaticObject( physicsObj );
170 savefile->WriteInt( move.stage );
171 savefile->WriteInt( move.acceleration );
172 savefile->WriteInt( move.movetime );
173 savefile->WriteInt( move.deceleration );
174 savefile->WriteVec3( move.dir );
176 savefile->WriteInt( rot.stage );
177 savefile->WriteInt( rot.acceleration );
178 savefile->WriteInt( rot.movetime );
179 savefile->WriteInt( rot.deceleration );
180 savefile->WriteFloat( rot.rot.pitch );
181 savefile->WriteFloat( rot.rot.yaw );
182 savefile->WriteFloat( rot.rot.roll );
184 savefile->WriteInt( move_thread );
185 savefile->WriteInt( rotate_thread );
187 savefile->WriteAngles( dest_angles );
188 savefile->WriteAngles( angle_delta );
189 savefile->WriteVec3( dest_position );
190 savefile->WriteVec3( move_delta );
192 savefile->WriteFloat( move_speed );
193 savefile->WriteInt( move_time );
194 savefile->WriteInt( deceltime );
195 savefile->WriteInt( acceltime );
196 savefile->WriteBool( stopRotation );
197 savefile->WriteBool( useSplineAngles );
198 savefile->WriteInt( lastCommand );
199 savefile->WriteFloat( damage );
201 savefile->WriteInt( areaPortal );
202 if ( areaPortal > 0 ) {
203 savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
206 savefile->WriteInt( guiTargets.Num() );
207 for( i = 0; i < guiTargets.Num(); i++ ) {
208 guiTargets[ i ].Save( savefile );
211 if ( splineEnt.GetEntity() && splineEnt.GetEntity()->GetSpline() ) {
212 idCurve_Spline<idVec3> *spline = physicsObj.GetSpline();
214 savefile->WriteBool( true );
215 splineEnt.Save( savefile );
216 savefile->WriteInt( spline->GetTime( 0 ) );
217 savefile->WriteInt( spline->GetTime( spline->GetNumValues() - 1 ) - spline->GetTime( 0 ) );
218 savefile->WriteInt( physicsObj.GetSplineAcceleration() );
219 savefile->WriteInt( physicsObj.GetSplineDeceleration() );
220 savefile->WriteInt( (int)physicsObj.UsingSplineAngles() );
223 savefile->WriteBool( false );
232 void idMover::Restore( idRestoreGame *savefile ) {
234 bool hasSpline = false;
236 savefile->ReadStaticObject( physicsObj );
237 RestorePhysics( &physicsObj );
239 savefile->ReadInt( (int&)move.stage );
240 savefile->ReadInt( move.acceleration );
241 savefile->ReadInt( move.movetime );
242 savefile->ReadInt( move.deceleration );
243 savefile->ReadVec3( move.dir );
245 savefile->ReadInt( (int&)rot.stage );
246 savefile->ReadInt( rot.acceleration );
247 savefile->ReadInt( rot.movetime );
248 savefile->ReadInt( rot.deceleration );
249 savefile->ReadFloat( rot.rot.pitch );
250 savefile->ReadFloat( rot.rot.yaw );
251 savefile->ReadFloat( rot.rot.roll );
253 savefile->ReadInt( move_thread );
254 savefile->ReadInt( rotate_thread );
256 savefile->ReadAngles( dest_angles );
257 savefile->ReadAngles( angle_delta );
258 savefile->ReadVec3( dest_position );
259 savefile->ReadVec3( move_delta );
261 savefile->ReadFloat( move_speed );
262 savefile->ReadInt( move_time );
263 savefile->ReadInt( deceltime );
264 savefile->ReadInt( acceltime );
265 savefile->ReadBool( stopRotation );
266 savefile->ReadBool( useSplineAngles );
267 savefile->ReadInt( (int &)lastCommand );
268 savefile->ReadFloat( damage );
270 savefile->ReadInt( areaPortal );
271 if ( areaPortal > 0 ) {
273 savefile->ReadInt( portalState );
274 gameLocal.SetPortalState( areaPortal, portalState );
278 savefile->ReadInt( num );
279 guiTargets.SetNum( num );
280 for( i = 0; i < num; i++ ) {
281 guiTargets[ i ].Restore( savefile );
284 savefile->ReadBool( hasSpline );
292 splineEnt.Restore( savefile );
293 savefile->ReadInt( starttime );
294 savefile->ReadInt( totaltime );
295 savefile->ReadInt( accel );
296 savefile->ReadInt( decel );
297 savefile->ReadInt( useAngles );
299 PostEventMS( &EV_PostRestore, 0, starttime, totaltime, accel, decel, useAngles );
305 idMover::Event_PostRestore
308 void idMover::Event_PostRestore( int start, int total, int accel, int decel, int useSplineAng ) {
309 idCurve_Spline<idVec3> *spline;
311 idEntity *splineEntity = splineEnt.GetEntity();
312 if ( !splineEntity ) {
313 // We should never get this event if splineEnt is invalid
314 common->Warning( "Invalid spline entity during restore\n" );
318 spline = splineEntity->GetSpline();
320 spline->MakeUniform( total );
321 spline->ShiftTime( start - spline->GetTime( 0 ) );
323 physicsObj.SetSpline( spline, accel, decel, ( useSplineAng != 0 ) );
324 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
332 void idMover::Spawn( void ) {
335 stopRotation = false;
336 lastCommand = MOVER_NONE;
338 acceltime = 1000.0f * spawnArgs.GetFloat( "accel_time", "0" );
339 deceltime = 1000.0f * spawnArgs.GetFloat( "decel_time", "0" );
340 move_time = 1000.0f * spawnArgs.GetFloat( "move_time", "1" ); // safe default value
341 move_speed = spawnArgs.GetFloat( "move_speed", "0" );
343 spawnArgs.GetFloat( "damage" , "0", damage );
345 dest_position = GetPhysics()->GetOrigin();
346 dest_angles = GetPhysics()->GetAxis().ToAngles();
348 physicsObj.SetSelf( this );
349 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
350 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
351 physicsObj.SetAxis( GetPhysics()->GetAxis() );
352 physicsObj.SetClipMask( MASK_SOLID );
353 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
354 physicsObj.SetContents( 0 );
356 if ( !renderEntity.hModel || !spawnArgs.GetBool( "nopush" ) ) {
357 physicsObj.SetPusher( 0 );
359 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
360 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
361 SetPhysics( &physicsObj );
363 // see if we are on an areaportal
364 areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
366 if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
367 if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
368 PostEventMS( &EV_FindGuiTargets, 0 );
370 // not during spawn, so it's ok to get the targets
375 health = spawnArgs.GetInt( "health" );
377 fl.takedamage = true;
387 void idMover::Hide( void ) {
389 physicsObj.SetContents( 0 );
397 void idMover::Show( void ) {
399 if ( spawnArgs.GetBool( "solid", "1" ) ) {
400 physicsObj.SetContents( CONTENTS_SOLID );
402 SetPhysics( &physicsObj );
410 void idMover::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
411 fl.takedamage = false;
412 ActivateTargets( this );
418 idMover::Event_SetCallback
421 void idMover::Event_SetCallback( void ) {
422 if ( ( lastCommand == MOVER_ROTATING ) && !rotate_thread ) {
423 lastCommand = MOVER_NONE;
424 rotate_thread = idThread::CurrentThreadNum();
425 idThread::ReturnInt( true );
426 } else if ( ( lastCommand == MOVER_MOVING || lastCommand == MOVER_SPLINE ) && !move_thread ) {
427 lastCommand = MOVER_NONE;
428 move_thread = idThread::CurrentThreadNum();
429 idThread::ReturnInt( true );
431 idThread::ReturnInt( false );
437 idMover::VectorForDir
440 void idMover::VectorForDir( float angle, idVec3 &vec ) {
443 switch( ( int )angle ) {
453 physicsObj.GetLocalAngles( ang );
457 vec = ang.ToForward();
461 physicsObj.GetLocalAngles( ang );
465 vec = ang.ToForward();
469 physicsObj.GetLocalAngles( ang );
472 vec = ang.ToForward();
476 physicsObj.GetLocalAngles( ang );
480 vec = ang.ToForward();
492 physicsObj.GetLocalAngles( ang );
493 ang.ToVectors( NULL, &vec );
498 physicsObj.GetLocalAngles( ang );
499 ang.ToVectors( NULL, &vec );
502 case DIR_REL_FORWARD :
503 physicsObj.GetLocalAngles( ang );
504 vec = ang.ToForward();
508 physicsObj.GetLocalAngles( ang );
509 vec = ang.ToForward() * -1;
513 ang.Set( 0, angle, 0 );
514 vec = GetWorldVector( ang.ToForward() );
521 idMover::FindGuiTargets
524 void idMover::FindGuiTargets( void ) {
525 gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
529 ==============================
532 key/val will be set to any renderEntity->gui's on the list
533 ==============================
535 void idMover::SetGuiState( const char *key, const char *val ) const {
536 gameLocal.Printf( "Setting %s to %s\n", key, val );
537 for( int i = 0; i < guiTargets.Num(); i++ ) {
538 idEntity *ent = guiTargets[ i ].GetEntity();
540 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
541 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
542 ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
543 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
546 ent->UpdateVisuals();
553 idMover::Event_InitGuiTargets
556 void idMover::Event_FindGuiTargets( void ) {
562 idMover::SetGuiStates
565 void idMover::SetGuiStates( const char *state ) {
567 if ( guiTargets.Num() ) {
568 SetGuiState( "movestate", state );
570 for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
571 if ( renderEntity.gui[ i ] ) {
572 renderEntity.gui[ i ]->SetStateString( "movestate", state );
573 renderEntity.gui[ i ]->StateChanged( gameLocal.time, true );
580 idMover::Event_InitGuiTargets
583 void idMover::Event_InitGuiTargets( void ) {
584 SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
587 /***********************************************************************
589 Translation control functions
591 ***********************************************************************/
595 idMover::Event_StopMoving
598 void idMover::Event_StopMoving( void ) {
599 physicsObj.GetLocalOrigin( dest_position );
608 void idMover::DoneMoving( void ) {
610 if ( lastCommand != MOVER_SPLINE ) {
611 // set our final position so that we get rid of any numerical inaccuracy
612 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
615 lastCommand = MOVER_NONE;
616 idThread::ObjectMoveDone( move_thread, this );
619 StopSound( SND_CHANNEL_BODY, false );
624 idMover::UpdateMoveSound
627 void idMover::UpdateMoveSound( moveStage_t stage ) {
629 case ACCELERATION_STAGE: {
630 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
631 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
635 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
638 case DECELERATION_STAGE: {
639 StopSound( SND_CHANNEL_BODY, false );
640 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
643 case FINISHED_STAGE: {
644 StopSound( SND_CHANNEL_BODY, false );
652 idMover::Event_UpdateMove
655 void idMover::Event_UpdateMove( void ) {
658 physicsObj.GetLocalOrigin( org );
660 UpdateMoveSound( move.stage );
662 switch( move.stage ) {
663 case ACCELERATION_STAGE: {
664 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.time, move.acceleration, org, move.dir, vec3_origin );
665 if ( move.movetime > 0 ) {
666 move.stage = LINEAR_STAGE;
667 } else if ( move.deceleration > 0 ) {
668 move.stage = DECELERATION_STAGE;
670 move.stage = FINISHED_STAGE;
675 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.time, move.movetime, org, move.dir, vec3_origin );
676 if ( move.deceleration ) {
677 move.stage = DECELERATION_STAGE;
679 move.stage = FINISHED_STAGE;
683 case DECELERATION_STAGE: {
684 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.time, move.deceleration, org, move.dir, vec3_origin );
685 move.stage = FINISHED_STAGE;
688 case FINISHED_STAGE: {
689 if ( g_debugMover.GetBool() ) {
690 gameLocal.Printf( "%d: '%s' move done\n", gameLocal.time, name.c_str() );
703 void idMover::BeginMove( idThread *thread ) {
712 lastCommand = MOVER_MOVING;
715 physicsObj.GetLocalOrigin( org );
717 move_delta = dest_position - org;
718 if ( move_delta.Compare( vec3_zero ) ) {
723 // scale times up to whole physics frames
724 at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
725 move_time += at - acceltime;
727 dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
728 move_time += dt - deceltime;
731 // if we're moving at a specific speed, we need to calculate the move time
733 dist = move_delta.Length();
735 totalacceltime = acceltime + deceltime;
737 // calculate the distance we'll move during acceleration and deceleration
738 acceldist = totalacceltime * 0.5f * 0.001f * move_speed;
739 if ( acceldist >= dist ) {
740 // going too slow for this distance to move at a constant speed
741 move_time = totalacceltime;
743 // calculate move time taking acceleration into account
744 move_time = totalacceltime + 1000.0f * ( dist - acceldist ) / move_speed;
748 // scale time up to a whole physics frames
749 move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
752 stage = ACCELERATION_STAGE;
753 } else if ( move_time <= deceltime ) {
754 stage = DECELERATION_STAGE;
756 stage = LINEAR_STAGE;
762 if ( at + dt > move_time ) {
763 // there's no real correct way to handle this, so we just scale
764 // the times to fit into the move time in the same proportions
765 at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
769 move_delta = move_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
772 move.acceleration = at;
773 move.movetime = move_time - at - dt;
774 move.deceleration = dt;
775 move.dir = move_delta;
777 ProcessEvent( &EV_ReachedPos );
780 /***********************************************************************
782 Rotation control functions
784 ***********************************************************************/
788 idMover::Event_StopRotating
791 void idMover::Event_StopRotating( void ) {
792 physicsObj.GetLocalAngles( dest_angles );
793 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
799 idMover::DoneRotating
802 void idMover::DoneRotating( void ) {
803 lastCommand = MOVER_NONE;
804 idThread::ObjectMoveDone( rotate_thread, this );
807 StopSound( SND_CHANNEL_BODY, false );
812 idMover::UpdateRotationSound
815 void idMover::UpdateRotationSound( moveStage_t stage ) {
817 case ACCELERATION_STAGE: {
818 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
819 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
823 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
826 case DECELERATION_STAGE: {
827 StopSound( SND_CHANNEL_BODY, false );
828 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
831 case FINISHED_STAGE: {
832 StopSound( SND_CHANNEL_BODY, false );
840 idMover::Event_UpdateRotation
843 void idMover::Event_UpdateRotation( void ) {
846 physicsObj.GetLocalAngles( ang );
848 UpdateRotationSound( rot.stage );
850 switch( rot.stage ) {
851 case ACCELERATION_STAGE: {
852 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.time, rot.acceleration, ang, rot.rot, ang_zero );
853 if ( rot.movetime > 0 ) {
854 rot.stage = LINEAR_STAGE;
855 } else if ( rot.deceleration > 0 ) {
856 rot.stage = DECELERATION_STAGE;
858 rot.stage = FINISHED_STAGE;
863 if ( !stopRotation && !rot.deceleration ) {
864 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, rot.movetime, ang, rot.rot, ang_zero );
866 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.time, rot.movetime, ang, rot.rot, ang_zero );
869 if ( rot.deceleration ) {
870 rot.stage = DECELERATION_STAGE;
872 rot.stage = FINISHED_STAGE;
876 case DECELERATION_STAGE: {
877 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.time, rot.deceleration, ang, rot.rot, ang_zero );
878 rot.stage = FINISHED_STAGE;
881 case FINISHED_STAGE: {
882 lastCommand = MOVER_NONE;
883 if ( stopRotation ) {
884 // set our final angles so that we get rid of any numerical inaccuracy
885 dest_angles.Normalize360();
886 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
887 stopRotation = false;
888 } else if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_ACCELLINEAR ) {
889 // keep our angular velocity constant
890 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, ang, rot.rot, ang_zero );
893 if ( g_debugMover.GetBool() ) {
894 gameLocal.Printf( "%d: '%s' rotation done\n", gameLocal.time, name.c_str() );
905 idMover::BeginRotation
908 void idMover::BeginRotation( idThread *thread, bool stopwhendone ) {
914 lastCommand = MOVER_ROTATING;
917 // rotation always uses move_time so that if a move was started before the rotation,
918 // the rotation will take the same amount of time as the move. If no move has been
919 // started and no time is set, the rotation takes 1 second.
924 physicsObj.GetLocalAngles( ang );
925 angle_delta = dest_angles - ang;
926 if ( angle_delta == ang_zero ) {
927 // set our final angles so that we get rid of any numerical inaccuracy
928 dest_angles.Normalize360();
929 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
930 stopRotation = false;
935 // scale times up to whole physics frames
936 at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
937 move_time += at - acceltime;
939 dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
940 move_time += dt - deceltime;
942 move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
945 stage = ACCELERATION_STAGE;
946 } else if ( move_time <= deceltime ) {
947 stage = DECELERATION_STAGE;
949 stage = LINEAR_STAGE;
955 if ( at + dt > move_time ) {
956 // there's no real correct way to handle this, so we just scale
957 // the times to fit into the move time in the same proportions
958 at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
962 angle_delta = angle_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
964 stopRotation = stopwhendone || ( dt != 0 );
967 rot.acceleration = at;
968 rot.movetime = move_time - at - dt;
969 rot.deceleration = dt;
970 rot.rot = angle_delta;
972 ProcessEvent( &EV_ReachedAng );
976 /***********************************************************************
978 Script callable routines
980 ***********************************************************************/
984 idMover::Event_TeamBlocked
987 void idMover::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
988 if ( g_debugMover.GetBool() ) {
989 gameLocal.Printf( "%d: '%s' stopped due to team member '%s' blocked by '%s'\n", gameLocal.time, name.c_str(), blockedEntity->name.c_str(), blockingEntity->name.c_str() );
995 idMover::Event_PartBlocked
998 void idMover::Event_PartBlocked( idEntity *blockingEntity ) {
999 if ( damage > 0.0f ) {
1000 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
1002 if ( g_debugMover.GetBool() ) {
1003 gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.time, name.c_str(), blockingEntity->name.c_str() );
1009 idMover::Event_SetMoveSpeed
1012 void idMover::Event_SetMoveSpeed( float speed ) {
1014 gameLocal.Error( "Cannot set speed less than or equal to 0." );
1018 move_time = 0; // move_time is calculated for each move when move_speed is non-0
1023 idMover::Event_SetMoveTime
1026 void idMover::Event_SetMoveTime( float time ) {
1028 gameLocal.Error( "Cannot set time less than or equal to 0." );
1032 move_time = SEC2MS( time );
1037 idMover::Event_SetAccellerationTime
1040 void idMover::Event_SetAccellerationTime( float time ) {
1042 gameLocal.Error( "Cannot set acceleration time less than 0." );
1045 acceltime = SEC2MS( time );
1050 idMover::Event_SetDecelerationTime
1053 void idMover::Event_SetDecelerationTime( float time ) {
1055 gameLocal.Error( "Cannot set deceleration time less than 0." );
1058 deceltime = SEC2MS( time );
1063 idMover::Event_MoveTo
1066 void idMover::Event_MoveTo( idEntity *ent ) {
1068 gameLocal.Warning( "Entity not found" );
1071 dest_position = GetLocalCoordinates( ent->GetPhysics()->GetOrigin() );
1072 BeginMove( idThread::CurrentThread() );
1080 void idMover::MoveToPos( const idVec3 &pos ) {
1081 dest_position = GetLocalCoordinates( pos );
1087 idMover::Event_MoveToPos
1090 void idMover::Event_MoveToPos( idVec3 &pos ) {
1096 idMover::Event_MoveDir
1099 void idMover::Event_MoveDir( float angle, float distance ) {
1103 physicsObj.GetLocalOrigin( org );
1104 VectorForDir( angle, dir );
1105 dest_position = org + dir * distance;
1107 BeginMove( idThread::CurrentThread() );
1112 idMover::Event_MoveAccelerateTo
1115 void idMover::Event_MoveAccelerateTo( float speed, float time ) {
1121 gameLocal.Error( "idMover::Event_MoveAccelerateTo: cannot set acceleration time less than 0." );
1124 dir = physicsObj.GetLinearVelocity();
1125 v = dir.Normalize();
1127 // if not moving already
1129 gameLocal.Error( "idMover::Event_MoveAccelerateTo: not moving." );
1132 // if already moving faster than the desired speed
1137 at = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
1139 lastCommand = MOVER_MOVING;
1141 physicsObj.GetLocalOrigin( org );
1143 move.stage = ACCELERATION_STAGE;
1144 move.acceleration = at;
1146 move.deceleration = 0;
1148 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
1149 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
1150 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.time, move.acceleration, org, dir * ( speed - v ), dir * v );
1155 idMover::Event_MoveDecelerateTo
1158 void idMover::Event_MoveDecelerateTo( float speed, float time ) {
1164 gameLocal.Error( "idMover::Event_MoveDecelerateTo: cannot set deceleration time less than 0." );
1167 dir = physicsObj.GetLinearVelocity();
1168 v = dir.Normalize();
1170 // if not moving already
1172 gameLocal.Error( "idMover::Event_MoveDecelerateTo: not moving." );
1175 // if already moving slower than the desired speed
1180 dt = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
1182 lastCommand = MOVER_MOVING;
1184 physicsObj.GetLocalOrigin( org );
1186 move.stage = DECELERATION_STAGE;
1187 move.acceleration = 0;
1189 move.deceleration = dt;
1191 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
1192 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
1193 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.time, move.deceleration, org, dir * ( v - speed ), dir * speed );
1198 idMover::Event_RotateDownTo
1201 void idMover::Event_RotateDownTo( int axis, float angle ) {
1204 if ( ( axis < 0 ) || ( axis > 2 ) ) {
1205 gameLocal.Error( "Invalid axis" );
1208 physicsObj.GetLocalAngles( ang );
1210 dest_angles[ axis ] = angle;
1211 if ( dest_angles[ axis ] > ang[ axis ] ) {
1212 dest_angles[ axis ] -= 360;
1215 BeginRotation( idThread::CurrentThread(), true );
1220 idMover::Event_RotateUpTo
1223 void idMover::Event_RotateUpTo( int axis, float angle ) {
1226 if ( ( axis < 0 ) || ( axis > 2 ) ) {
1227 gameLocal.Error( "Invalid axis" );
1230 physicsObj.GetLocalAngles( ang );
1232 dest_angles[ axis ] = angle;
1233 if ( dest_angles[ axis ] < ang[ axis ] ) {
1234 dest_angles[ axis ] += 360;
1237 BeginRotation( idThread::CurrentThread(), true );
1242 idMover::Event_RotateTo
1245 void idMover::Event_RotateTo( idAngles &angles ) {
1246 dest_angles = angles;
1247 BeginRotation( idThread::CurrentThread(), true );
1252 idMover::Event_Rotate
1255 void idMover::Event_Rotate( idAngles &angles ) {
1258 if ( rotate_thread ) {
1262 physicsObj.GetLocalAngles( ang );
1263 dest_angles = ang + angles * ( move_time - ( acceltime + deceltime ) / 2 ) * 0.001f;
1265 BeginRotation( idThread::CurrentThread(), false );
1270 idMover::Event_RotateOnce
1273 void idMover::Event_RotateOnce( idAngles &angles ) {
1276 if ( rotate_thread ) {
1280 physicsObj.GetLocalAngles( ang );
1281 dest_angles = ang + angles;
1283 BeginRotation( idThread::CurrentThread(), true );
1291 void idMover::Event_Bob( float speed, float phase, idVec3 &depth ) {
1294 physicsObj.GetLocalOrigin( org );
1295 physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), speed * 1000 * phase, speed * 500, org, depth * 2.0f, vec3_origin );
1303 void idMover::Event_Sway( float speed, float phase, idAngles &depth ) {
1304 idAngles ang, angSpeed;
1307 physicsObj.GetLocalAngles( ang );
1308 assert ( speed > 0.0f );
1309 duration = idMath::Sqrt( depth[0] * depth[0] + depth[1] * depth[1] + depth[2] * depth[2] ) / speed;
1310 angSpeed = depth / ( duration * idMath::SQRT_1OVER2 );
1311 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), duration * 1000.0f * phase, duration * 1000.0f, ang, angSpeed, ang_zero );
1316 idMover::Event_OpenPortal
1318 Sets the portal associtated with this mover to be open
1321 void idMover::Event_OpenPortal( void ) {
1323 SetPortalState( true );
1329 idMover::Event_ClosePortal
1331 Sets the portal associtated with this mover to be closed
1334 void idMover::Event_ClosePortal( void ) {
1336 SetPortalState( false );
1342 idMover::Event_SetAccelSound
1345 void idMover::Event_SetAccelSound( const char *sound ) {
1346 // refSound.SetSound( "accel", sound );
1351 idMover::Event_SetDecelSound
1354 void idMover::Event_SetDecelSound( const char *sound ) {
1355 // refSound.SetSound( "decel", sound );
1360 idMover::Event_SetMoveSound
1363 void idMover::Event_SetMoveSound( const char *sound ) {
1364 // refSound.SetSound( "move", sound );
1369 idMover::Event_EnableSplineAngles
1372 void idMover::Event_EnableSplineAngles( void ) {
1373 useSplineAngles = true;
1378 idMover::Event_DisableSplineAngles
1381 void idMover::Event_DisableSplineAngles( void ) {
1382 useSplineAngles = false;
1387 idMover::Event_RemoveInitialSplineAngles
1390 void idMover::Event_RemoveInitialSplineAngles( void ) {
1391 idCurve_Spline<idVec3> *spline;
1394 spline = physicsObj.GetSpline();
1398 ang = spline->GetCurrentFirstDerivative( 0 ).ToAngles();
1399 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, -ang, ang_zero, ang_zero );
1404 idMover::Event_StartSpline
1407 void idMover::Event_StartSpline( idEntity *splineEntity ) {
1408 idCurve_Spline<idVec3> *spline;
1410 if ( !splineEntity ) {
1414 // Needed for savegames
1415 splineEnt = splineEntity;
1417 spline = splineEntity->GetSpline();
1422 lastCommand = MOVER_SPLINE;
1425 if ( acceltime + deceltime > move_time ) {
1426 acceltime = move_time / 2;
1427 deceltime = move_time - acceltime;
1429 move.stage = FINISHED_STAGE;
1430 move.acceleration = acceltime;
1431 move.movetime = move_time;
1432 move.deceleration = deceltime;
1434 spline->MakeUniform( move_time );
1435 spline->ShiftTime( gameLocal.time - spline->GetTime( 0 ) );
1437 physicsObj.SetSpline( spline, move.acceleration, move.deceleration, useSplineAngles );
1438 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
1443 idMover::Event_StopSpline
1446 void idMover::Event_StopSpline( void ) {
1447 physicsObj.SetSpline( NULL, 0, 0, useSplineAngles );
1453 idMover::Event_Activate
1456 void idMover::Event_Activate( idEntity *activator ) {
1458 Event_StartSpline( this );
1463 idMover::Event_IsMoving
1466 void idMover::Event_IsMoving( void ) {
1467 if ( physicsObj.GetLinearExtrapolationType() == EXTRAPOLATION_NONE ) {
1468 idThread::ReturnInt( false );
1470 idThread::ReturnInt( true );
1476 idMover::Event_IsRotating
1479 void idMover::Event_IsRotating( void ) {
1480 if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_NONE ) {
1481 idThread::ReturnInt( false );
1483 idThread::ReturnInt( true );
1489 idMover::WriteToSnapshot
1492 void idMover::WriteToSnapshot( idBitMsgDelta &msg ) const {
1493 physicsObj.WriteToSnapshot( msg );
1494 msg.WriteBits( move.stage, 3 );
1495 msg.WriteBits( rot.stage, 3 );
1496 WriteBindToSnapshot( msg );
1497 WriteGUIToSnapshot( msg );
1502 idMover::ReadFromSnapshot
1505 void idMover::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1506 moveStage_t oldMoveStage = move.stage;
1507 moveStage_t oldRotStage = rot.stage;
1509 physicsObj.ReadFromSnapshot( msg );
1510 move.stage = (moveStage_t) msg.ReadBits( 3 );
1511 rot.stage = (moveStage_t) msg.ReadBits( 3 );
1512 ReadBindFromSnapshot( msg );
1513 ReadGUIFromSnapshot( msg );
1515 if ( msg.HasChanged() ) {
1516 if ( move.stage != oldMoveStage ) {
1517 UpdateMoveSound( oldMoveStage );
1519 if ( rot.stage != oldRotStage ) {
1520 UpdateRotationSound( oldRotStage );
1528 idMover::SetPortalState
1531 void idMover::SetPortalState( bool open ) {
1532 assert( areaPortal );
1533 gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
1537 ===============================================================================
1539 idSplinePath, holds a spline path to be used by an idMover
1541 ===============================================================================
1544 CLASS_DECLARATION( idEntity, idSplinePath )
1549 idSplinePath::idSplinePath
1552 idSplinePath::idSplinePath() {
1560 void idSplinePath::Spawn( void ) {
1565 ===============================================================================
1569 ===============================================================================
1571 const idEventDef EV_PostArrival( "postArrival", NULL );
1572 const idEventDef EV_GotoFloor( "gotoFloor", "d" );
1574 CLASS_DECLARATION( idMover, idElevator )
1575 EVENT( EV_Activate, idElevator::Event_Activate )
1576 EVENT( EV_TeamBlocked, idElevator::Event_TeamBlocked )
1577 EVENT( EV_PartBlocked, idElevator::Event_PartBlocked )
1578 EVENT( EV_PostArrival, idElevator::Event_PostFloorArrival )
1579 EVENT( EV_GotoFloor, idElevator::Event_GotoFloor )
1580 EVENT( EV_Touch, idElevator::Event_Touch )
1585 idElevator::idElevator
1588 idElevator::idElevator( void ) {
1594 controlsDisabled = false;
1605 void idElevator::Save( idSaveGame *savefile ) const {
1608 savefile->WriteInt( (int)state );
1610 savefile->WriteInt( floorInfo.Num() );
1611 for ( i = 0; i < floorInfo.Num(); i++ ) {
1612 savefile->WriteVec3( floorInfo[ i ].pos );
1613 savefile->WriteString( floorInfo[ i ].door );
1614 savefile->WriteInt( floorInfo[ i ].floor );
1617 savefile->WriteInt( currentFloor );
1618 savefile->WriteInt( pendingFloor );
1619 savefile->WriteInt( lastFloor );
1620 savefile->WriteBool( controlsDisabled );
1621 savefile->WriteFloat( returnTime );
1622 savefile->WriteInt( returnFloor );
1623 savefile->WriteInt( lastTouchTime );
1631 void idElevator::Restore( idRestoreGame *savefile ) {
1634 savefile->ReadInt( (int &)state );
1636 savefile->ReadInt( num );
1637 for ( i = 0; i < num; i++ ) {
1640 savefile->ReadVec3( floor.pos );
1641 savefile->ReadString( floor.door );
1642 savefile->ReadInt( floor.floor );
1644 floorInfo.Append( floor );
1647 savefile->ReadInt( currentFloor );
1648 savefile->ReadInt( pendingFloor );
1649 savefile->ReadInt( lastFloor );
1650 savefile->ReadBool( controlsDisabled );
1651 savefile->ReadFloat( returnTime );
1652 savefile->ReadInt( returnFloor );
1653 savefile->ReadInt( lastTouchTime );
1661 void idElevator::Spawn( void ) {
1667 pendingFloor = spawnArgs.GetInt( "floor", "1" );
1668 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1]);
1670 returnTime = spawnArgs.GetFloat( "returnTime" );
1671 returnFloor = spawnArgs.GetInt( "returnFloor" );
1673 len1 = strlen( "floorPos_" );
1674 const idKeyValue *kv = spawnArgs.MatchPrefix( "floorPos_", NULL );
1676 str = kv->GetKey().Right( kv->GetKey().Length() - len1 );
1678 fi.floor = atoi( str );
1679 fi.door = spawnArgs.GetString( va( "floorDoor_%i", fi.floor ) );
1680 fi.pos = spawnArgs.GetVector( kv->GetKey() );
1681 floorInfo.Append( fi );
1682 kv = spawnArgs.MatchPrefix( "floorPos_", kv );
1686 BecomeActive( TH_THINK | TH_PHYSICS );
1687 PostEventMS( &EV_Mover_InitGuiTargets, 0 );
1688 controlsDisabled = false;
1693 idElevator::Event_Touch
1696 void idElevator::Event_Touch( idEntity *other, trace_t *trace ) {
1698 if ( gameLocal.time < lastTouchTime + 2000 ) {
1702 if ( !other->IsType( idPlayer::Type ) ) {
1706 lastTouchTime = gameLocal.time;
1708 if ( thinkFlags & TH_PHYSICS ) {
1712 int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
1713 if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
1714 PostEventSec( &EV_GotoFloor, 0.25f, triggerFloor );
1723 void idElevator::Think( void ) {
1724 idVec3 masterOrigin;
1726 idDoor *doorent = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1727 if ( state == INIT ) {
1730 doorent->BindTeam( this );
1731 doorent->spawnArgs.Set( "snd_open", "" );
1732 doorent->spawnArgs.Set( "snd_close", "" );
1733 doorent->spawnArgs.Set( "snd_opened", "" );
1735 for ( int i = 0; i < floorInfo.Num(); i++ ) {
1736 idDoor *door = GetDoor( floorInfo[i].door );
1738 door->SetCompanion( doorent );
1742 Event_GotoFloor( pendingFloor );
1744 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
1745 } else if ( state == WAITING_ON_DOORS ) {
1747 state = doorent->IsOpen() ? WAITING_ON_DOORS : IDLE;
1751 if ( state == IDLE ) {
1752 lastFloor = currentFloor;
1753 currentFloor = pendingFloor;
1754 floorInfo_s *fi = GetFloorInfo( currentFloor );
1756 MoveToPos( fi->pos );
1766 idElevator::Event_Activate
1769 void idElevator::Event_Activate( idEntity *activator ) {
1770 int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
1771 if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
1772 Event_GotoFloor( triggerFloor );
1778 idElevator::Event_TeamBlocked
1781 void idElevator::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
1782 if ( blockedEntity == this ) {
1783 Event_GotoFloor( lastFloor );
1784 } else if ( blockedEntity && blockedEntity->IsType( idDoor::Type ) ) {
1785 // open the inner doors if one is blocked
1786 idDoor *blocked = static_cast<idDoor *>( blockedEntity );
1787 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1788 if ( door && blocked->GetMoveMaster() == door->GetMoveMaster() ) {
1789 door->SetBlocked(true);
1791 OpenFloorDoor( currentFloor );
1799 idElevator::HandleSingleGuiCommand
1802 bool idElevator::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
1805 if ( controlsDisabled ) {
1809 if ( !src->ReadToken( &token ) ) {
1813 if ( token == ";" ) {
1817 if ( token.Icmp( "changefloor" ) == 0 ) {
1818 if ( src->ReadToken( &token ) ) {
1819 int newFloor = atoi( token );
1820 if ( newFloor == currentFloor ) {
1821 // open currentFloor and interior doors
1823 OpenFloorDoor( currentFloor );
1825 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1826 if ( door && door->IsOpen() ) {
1827 PostEventSec( &EV_GotoFloor, 0.5f, newFloor );
1829 ProcessEvent( &EV_GotoFloor, newFloor );
1836 src->UnreadToken( &token );
1842 idElevator::OpenFloorDoor
1845 void idElevator::OpenFloorDoor( int floor ) {
1846 floorInfo_s *fi = GetFloorInfo( floor );
1848 idDoor *door = GetDoor( fi->door );
1857 idElevator::OpenInnerDoor
1860 void idElevator::OpenInnerDoor( void ) {
1861 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1869 idElevator::GetFloorInfo
1872 floorInfo_s *idElevator::GetFloorInfo( int floor ) {
1873 for ( int i = 0; i < floorInfo.Num(); i++ ) {
1874 if ( floorInfo[i].floor == floor ) {
1875 return &floorInfo[i];
1883 idElevator::Event_GotoFloor
1886 void idElevator::Event_GotoFloor( int floor ) {
1887 floorInfo_s *fi = GetFloorInfo( floor );
1889 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
1891 if ( door->IsBlocked() || door->IsOpen() ) {
1892 PostEventSec( &EV_GotoFloor, 0.5f, floor );
1898 state = WAITING_ON_DOORS;
1899 pendingFloor = floor;
1905 idElevator::BeginMove
1908 void idElevator::BeginMove( idThread *thread ) {
1909 controlsDisabled = true;
1912 const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
1914 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
1916 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
1917 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
1918 ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", "" );
1919 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
1922 ent->UpdateVisuals();
1924 kv = spawnArgs.MatchPrefix( "statusGui", kv );
1926 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[3] : guiBinaryMoverStates[2] );
1927 idMover::BeginMove( thread );
1935 idDoor *idElevator::GetDoor( const char *name ) {
1941 if ( name && *name ) {
1942 ent = gameLocal.FindEntity( name );
1943 if ( ent && ent->IsType( idDoor::Type ) ) {
1944 doorEnt = static_cast<idDoor*>( ent );
1945 master = doorEnt->GetMoveMaster();
1946 if ( master != doorEnt ) {
1947 if ( master->IsType( idDoor::Type ) ) {
1948 doorEnt = static_cast<idDoor*>( master );
1961 idElevator::Event_PostFloorArrival
1964 void idElevator::Event_PostFloorArrival() {
1965 OpenFloorDoor( currentFloor );
1967 SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
1968 controlsDisabled = false;
1969 if ( returnTime > 0.0f && returnFloor != currentFloor ) {
1970 PostEventSec( &EV_GotoFloor, returnTime, returnFloor );
1976 idElevator::DoneMoving
1979 void idElevator::DoneMoving( void ) {
1980 idMover::DoneMoving();
1981 EnableProperDoors();
1982 const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
1984 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
1986 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
1987 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
1988 ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", va( "%i", currentFloor ) );
1989 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
1992 ent->UpdateVisuals();
1994 kv = spawnArgs.MatchPrefix( "statusGui", kv );
1996 if ( spawnArgs.GetInt( "pauseOnFloor", "-1" ) == currentFloor ) {
1997 PostEventSec( &EV_PostArrival, spawnArgs.GetFloat( "pauseTime" ) );
1999 Event_PostFloorArrival();
2005 idElevator::CloseAllDoors
2008 void idElevator::CloseAllDoors( void ) {
2009 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
2013 for ( int i = 0; i < floorInfo.Num(); i++ ) {
2014 door = GetDoor( floorInfo[i].door );
2023 idElevator::DisableAllDoors
2026 void idElevator::DisableAllDoors( void ) {
2027 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
2029 door->Enable( false );
2031 for ( int i = 0; i < floorInfo.Num(); i++ ) {
2032 door = GetDoor( floorInfo[i].door );
2034 door->Enable( false );
2041 idElevator::EnableProperDoors
2044 void idElevator::EnableProperDoors( void ) {
2045 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
2047 door->Enable( true );
2049 for ( int i = 0; i < floorInfo.Num(); i++ ) {
2050 if ( floorInfo[i].floor == currentFloor ) {
2051 door = GetDoor( floorInfo[i].door );
2053 door->Enable( true );
2062 ===============================================================================
2066 Doors, plats, and buttons are all binary (two position) movers
2067 Pos1 is "at rest", pos2 is "activated"
2069 ===============================================================================
2072 const idEventDef EV_Mover_ReturnToPos1( "<returntopos1>", NULL );
2073 const idEventDef EV_Mover_MatchTeam( "<matchteam>", "dd" );
2074 const idEventDef EV_Mover_Enable( "enable", NULL );
2075 const idEventDef EV_Mover_Disable( "disable", NULL );
2077 CLASS_DECLARATION( idEntity, idMover_Binary )
2078 EVENT( EV_FindGuiTargets, idMover_Binary::Event_FindGuiTargets )
2079 EVENT( EV_Thread_SetCallback, idMover_Binary::Event_SetCallback )
2080 EVENT( EV_Mover_ReturnToPos1, idMover_Binary::Event_ReturnToPos1 )
2081 EVENT( EV_Activate, idMover_Binary::Event_Use_BinaryMover )
2082 EVENT( EV_ReachedPos, idMover_Binary::Event_Reached_BinaryMover )
2083 EVENT( EV_Mover_MatchTeam, idMover_Binary::Event_MatchActivateTeam )
2084 EVENT( EV_Mover_Enable, idMover_Binary::Event_Enable )
2085 EVENT( EV_Mover_Disable, idMover_Binary::Event_Disable )
2086 EVENT( EV_Mover_OpenPortal, idMover_Binary::Event_OpenPortal )
2087 EVENT( EV_Mover_ClosePortal, idMover_Binary::Event_ClosePortal )
2088 EVENT( EV_Mover_InitGuiTargets, idMover_Binary::Event_InitGuiTargets )
2093 idMover_Binary::idMover_Binary()
2096 idMover_Binary::idMover_Binary() {
2099 moverState = MOVER_POS1;
2101 activateChain = NULL;
2120 fl.networkSync = true;
2125 idMover_Binary::~idMover_Binary
2128 idMover_Binary::~idMover_Binary() {
2129 idMover_Binary *mover;
2131 // if this is the mover master
2132 if ( this == moveMaster ) {
2133 // make the next mover in the chain the move master
2134 for ( mover = moveMaster; mover; mover = mover->activateChain ) {
2135 mover->moveMaster = this->activateChain;
2139 // remove mover from the activate chain
2140 for ( mover = moveMaster; mover; mover = mover->activateChain ) {
2141 if ( mover->activateChain == this ) {
2142 mover->activateChain = this->activateChain;
2151 idMover_Binary::Save
2154 void idMover_Binary::Save( idSaveGame *savefile ) const {
2157 savefile->WriteVec3( pos1 );
2158 savefile->WriteVec3( pos2 );
2159 savefile->WriteInt( (moverState_t)moverState );
2161 savefile->WriteObject( moveMaster );
2162 savefile->WriteObject( activateChain );
2164 savefile->WriteInt( soundPos1 );
2165 savefile->WriteInt( sound1to2 );
2166 savefile->WriteInt( sound2to1 );
2167 savefile->WriteInt( soundPos2 );
2168 savefile->WriteInt( soundLoop );
2170 savefile->WriteFloat( wait );
2171 savefile->WriteFloat( damage );
2173 savefile->WriteInt( duration );
2174 savefile->WriteInt( accelTime );
2175 savefile->WriteInt( decelTime );
2177 activatedBy.Save( savefile );
2179 savefile->WriteInt( stateStartTime );
2180 savefile->WriteString( team );
2181 savefile->WriteBool( enabled );
2183 savefile->WriteInt( move_thread );
2184 savefile->WriteInt( updateStatus );
2186 savefile->WriteInt( buddies.Num() );
2187 for ( i = 0; i < buddies.Num(); i++ ) {
2188 savefile->WriteString( buddies[ i ] );
2191 savefile->WriteStaticObject( physicsObj );
2193 savefile->WriteInt( areaPortal );
2195 savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
2197 savefile->WriteBool( blocked );
2199 savefile->WriteInt( guiTargets.Num() );
2200 for( i = 0; i < guiTargets.Num(); i++ ) {
2201 guiTargets[ i ].Save( savefile );
2207 idMover_Binary::Restore
2210 void idMover_Binary::Restore( idRestoreGame *savefile ) {
2211 int i, num, portalState;
2214 savefile->ReadVec3( pos1 );
2215 savefile->ReadVec3( pos2 );
2216 savefile->ReadInt( (int &)moverState );
2218 savefile->ReadObject( reinterpret_cast<idClass *&>( moveMaster ) );
2219 savefile->ReadObject( reinterpret_cast<idClass *&>( activateChain ) );
2221 savefile->ReadInt( soundPos1 );
2222 savefile->ReadInt( sound1to2 );
2223 savefile->ReadInt( sound2to1 );
2224 savefile->ReadInt( soundPos2 );
2225 savefile->ReadInt( soundLoop );
2227 savefile->ReadFloat( wait );
2228 savefile->ReadFloat( damage );
2230 savefile->ReadInt( duration );
2231 savefile->ReadInt( accelTime );
2232 savefile->ReadInt( decelTime );
2234 activatedBy.Restore( savefile );
2236 savefile->ReadInt( stateStartTime );
2238 savefile->ReadString( team );
2239 savefile->ReadBool( enabled );
2241 savefile->ReadInt( move_thread );
2242 savefile->ReadInt( updateStatus );
2244 savefile->ReadInt( num );
2245 for ( i = 0; i < num; i++ ) {
2246 savefile->ReadString( temp );
2247 buddies.Append( temp );
2250 savefile->ReadStaticObject( physicsObj );
2251 RestorePhysics( &physicsObj );
2253 savefile->ReadInt( areaPortal );
2255 savefile->ReadInt( portalState );
2256 gameLocal.SetPortalState( areaPortal, portalState );
2258 savefile->ReadBool( blocked );
2261 savefile->ReadInt( num );
2262 guiTargets.SetNum( num );
2263 for( i = 0; i < num; i++ ) {
2264 guiTargets[ i ].Restore( savefile );
2270 idMover_Binary::Spawn
2272 Base class for all movers.
2274 "wait" wait before returning (3 default, -1 = never return)
2275 "speed" movement speed
2278 void idMover_Binary::Spawn( void ) {
2286 activateChain = NULL;
2288 spawnArgs.GetFloat( "wait", "0", wait );
2290 spawnArgs.GetInt( "updateStatus", "0", updateStatus );
2292 const idKeyValue *kv = spawnArgs.MatchPrefix( "buddy", NULL );
2294 buddies.Append( kv->GetValue() );
2295 kv = spawnArgs.MatchPrefix( "buddy", kv );
2298 spawnArgs.GetString( "team", "", &temp );
2301 if ( !team.Length() ) {
2304 // find the first entity spawned on this team (which could be us)
2305 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2306 if ( ent->IsType( idMover_Binary::Type ) && !idStr::Icmp( static_cast<idMover_Binary *>(ent)->team.c_str(), temp ) ) {
2314 moveMaster = static_cast<idMover_Binary *>(ent);
2316 // create a physics team for the binary mover parts
2317 if ( ent != this ) {
2321 physicsObj.SetSelf( this );
2322 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
2323 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
2324 physicsObj.SetAxis( GetPhysics()->GetAxis() );
2325 physicsObj.SetClipMask( MASK_SOLID );
2326 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
2327 physicsObj.SetContents( 0 );
2329 if ( !spawnArgs.GetBool( "nopush" ) ) {
2330 physicsObj.SetPusher( 0 );
2332 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
2333 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
2334 SetPhysics( &physicsObj );
2336 if ( moveMaster != this ) {
2337 JoinActivateTeam( moveMaster );
2340 idBounds soundOrigin;
2341 idMover_Binary *slave;
2343 soundOrigin.Clear();
2344 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2345 soundOrigin += slave->GetPhysics()->GetAbsBounds();
2347 moveMaster->refSound.origin = soundOrigin.GetCenter();
2349 if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
2350 if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
2351 PostEventMS( &EV_FindGuiTargets, 0 );
2353 // not during spawn, so it's ok to get the targets
2361 idMover_Binary::GetMovedir
2363 The editor only specifies a single value for angles (yaw),
2364 but we have special constants to generate an up or down direction.
2365 Angles will be cleared, because it is being used to represent a direction
2366 instead of an orientation.
2369 void idMover_Binary::GetMovedir( float angle, idVec3 &movedir ) {
2370 if ( angle == -1 ) {
2371 movedir.Set( 0, 0, 1 );
2372 } else if ( angle == -2 ) {
2373 movedir.Set( 0, 0, -1 );
2375 movedir = idAngles( 0, angle, 0 ).ToForward();
2381 idMover_Binary::Event_SetCallback
2384 void idMover_Binary::Event_SetCallback( void ) {
2385 if ( ( moverState == MOVER_1TO2 ) || ( moverState == MOVER_2TO1 ) ) {
2386 move_thread = idThread::CurrentThreadNum();
2387 idThread::ReturnInt( true );
2389 idThread::ReturnInt( false );
2395 idMover_Binary::UpdateMoverSound
2398 void idMover_Binary::UpdateMoverSound( moverState_t state ) {
2399 if ( moveMaster == this ) {
2406 StartSound( "snd_open", SND_CHANNEL_ANY, 0, false, NULL );
2409 StartSound( "snd_close", SND_CHANNEL_ANY, 0, false, NULL );
2417 idMover_Binary::SetMoverState
2420 void idMover_Binary::SetMoverState( moverState_t newstate, int time ) {
2423 moverState = newstate;
2426 UpdateMoverSound( newstate );
2428 stateStartTime = time;
2429 switch( moverState ) {
2431 Signal( SIG_MOVER_POS1 );
2432 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos1, vec3_origin, vec3_origin );
2436 Signal( SIG_MOVER_POS2 );
2437 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos2, vec3_origin, vec3_origin );
2441 Signal( SIG_MOVER_1TO2 );
2442 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos1, ( pos2 - pos1 ) * 1000.0f / duration, vec3_origin );
2443 if ( accelTime != 0 || decelTime != 0 ) {
2444 physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos1, pos2 );
2446 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
2451 Signal( SIG_MOVER_2TO1 );
2452 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos2, ( pos1 - pos2 ) * 1000.0f / duration, vec3_origin );
2453 if ( accelTime != 0 || decelTime != 0 ) {
2454 physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos2, pos1 );
2456 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
2465 idMover_Binary::MatchActivateTeam
2467 All entities in a mover team will move from pos1 to pos2
2468 in the same amount of time
2471 void idMover_Binary::MatchActivateTeam( moverState_t newstate, int time ) {
2472 idMover_Binary *slave;
2474 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2475 slave->SetMoverState( newstate, time );
2481 idMover_Binary::Enable
2484 void idMover_Binary::Enable( bool b ) {
2490 idMover_Binary::Event_MatchActivateTeam
2493 void idMover_Binary::Event_MatchActivateTeam( moverState_t newstate, int time ) {
2494 MatchActivateTeam( newstate, time );
2499 idMover_Binary::BindTeam
2501 All entities in a mover team will be bound
2504 void idMover_Binary::BindTeam( idEntity *bindTo ) {
2505 idMover_Binary *slave;
2507 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2508 slave->Bind( bindTo, true );
2514 idMover_Binary::JoinActivateTeam
2516 Set all entities in a mover team to be enabled
2519 void idMover_Binary::JoinActivateTeam( idMover_Binary *master ) {
2520 this->activateChain = master->activateChain;
2521 master->activateChain = this;
2526 idMover_Binary::Event_Enable
2528 Set all entities in a mover team to be enabled
2531 void idMover_Binary::Event_Enable( void ) {
2532 idMover_Binary *slave;
2534 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2535 slave->Enable( false );
2541 idMover_Binary::Event_Disable
2543 Set all entities in a mover team to be disabled
2546 void idMover_Binary::Event_Disable( void ) {
2547 idMover_Binary *slave;
2549 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2550 slave->Enable( false );
2556 idMover_Binary::Event_OpenPortal
2558 Sets the portal associtated with this mover to be open
2561 void idMover_Binary::Event_OpenPortal( void ) {
2562 idMover_Binary *slave;
2564 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2565 if ( slave->areaPortal ) {
2566 slave->SetPortalState( true );
2573 idMover_Binary::Event_ClosePortal
2575 Sets the portal associtated with this mover to be closed
2578 void idMover_Binary::Event_ClosePortal( void ) {
2579 idMover_Binary *slave;
2581 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
2582 if ( !slave->IsHidden() ) {
2583 if ( slave->areaPortal ) {
2584 slave->SetPortalState( false );
2592 idMover_Binary::Event_ReturnToPos1
2595 void idMover_Binary::Event_ReturnToPos1( void ) {
2596 MatchActivateTeam( MOVER_2TO1, gameLocal.time );
2601 idMover_Binary::Event_Reached_BinaryMover
2604 void idMover_Binary::Event_Reached_BinaryMover( void ) {
2606 if ( moverState == MOVER_1TO2 ) {
2608 idThread::ObjectMoveDone( move_thread, this );
2611 if ( moveMaster == this ) {
2612 StartSound( "snd_opened", SND_CHANNEL_ANY, 0, false, NULL );
2615 SetMoverState( MOVER_POS2, gameLocal.time );
2617 SetGuiStates( guiBinaryMoverStates[MOVER_POS2] );
2621 if ( enabled && wait >= 0 && !spawnArgs.GetBool( "toggle" ) ) {
2622 // return to pos1 after a delay
2623 PostEventSec( &EV_Mover_ReturnToPos1, wait );
2627 ActivateTargets( moveMaster->GetActivator() );
2630 } else if ( moverState == MOVER_2TO1 ) {
2632 idThread::ObjectMoveDone( move_thread, this );
2635 SetMoverState( MOVER_POS1, gameLocal.time );
2637 SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
2641 // close areaportals
2642 if ( moveMaster == this ) {
2643 ProcessEvent( &EV_Mover_ClosePortal );
2646 if ( enabled && wait >= 0 && spawnArgs.GetBool( "continuous" ) ) {
2647 PostEventSec( &EV_Activate, wait, this );
2652 gameLocal.Error( "Event_Reached_BinaryMover: bad moverState" );
2658 idMover_Binary::GotoPosition1
2661 void idMover_Binary::GotoPosition1( void ) {
2662 idMover_Binary *slave;
2665 // only the master should control this
2666 if ( moveMaster != this ) {
2667 moveMaster->GotoPosition1();
2671 SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
2673 if ( ( moverState == MOVER_POS1 ) || ( moverState == MOVER_2TO1 ) ) {
2674 // already there, or on the way
2678 if ( moverState == MOVER_POS2 ) {
2679 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2680 slave->CancelEvents( &EV_Mover_ReturnToPos1 );
2682 if ( !spawnArgs.GetBool( "toggle" ) ) {
2683 ProcessEvent( &EV_Mover_ReturnToPos1 );
2688 // only partway up before reversing
2689 if ( moverState == MOVER_1TO2 ) {
2690 // use the physics times because this might be executed during the physics simulation
2691 partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
2692 assert( partial >= 0 );
2693 if ( partial < 0 ) {
2696 MatchActivateTeam( MOVER_2TO1, physicsObj.GetTime() - partial );
2697 // if already at at position 1 (partial == duration) execute the reached event
2698 if ( partial >= duration ) {
2699 Event_Reached_BinaryMover();
2706 idMover_Binary::GotoPosition2
2709 void idMover_Binary::GotoPosition2( void ) {
2712 // only the master should control this
2713 if ( moveMaster != this ) {
2714 moveMaster->GotoPosition2();
2718 SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
2720 if ( ( moverState == MOVER_POS2 ) || ( moverState == MOVER_1TO2 ) ) {
2721 // already there, or on the way
2725 if ( moverState == MOVER_POS1 ) {
2726 MatchActivateTeam( MOVER_1TO2, gameLocal.time );
2729 ProcessEvent( &EV_Mover_OpenPortal );
2734 // only partway up before reversing
2735 if ( moverState == MOVER_2TO1 ) {
2736 // use the physics times because this might be executed during the physics simulation
2737 partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
2738 assert( partial >= 0 );
2739 if ( partial < 0 ) {
2742 MatchActivateTeam( MOVER_1TO2, physicsObj.GetTime() - partial );
2743 // if already at at position 2 (partial == duration) execute the reached event
2744 if ( partial >= duration ) {
2745 Event_Reached_BinaryMover();
2752 idMover_Binary::UpdateBuddies
2755 void idMover_Binary::UpdateBuddies( int val ) {
2758 if ( updateStatus == 2 ) {
2760 for ( i = 0; i < c; i++ ) {
2761 idEntity *buddy = gameLocal.FindEntity( buddies[i] );
2763 buddy->SetShaderParm( SHADERPARM_MODE, val );
2764 buddy->UpdateVisuals();
2772 idMover_Binary::SetGuiStates
2775 void idMover_Binary::SetGuiStates( const char *state ) {
2776 if ( guiTargets.Num() ) {
2777 SetGuiState( "movestate", state );
2780 idMover_Binary *mb = activateChain;
2782 if ( mb->guiTargets.Num() ) {
2783 mb->SetGuiState( "movestate", state );
2785 mb = mb->activateChain;
2791 idMover_Binary::Use_BinaryMover
2794 void idMover_Binary::Use_BinaryMover( idEntity *activator ) {
2795 // only the master should be used
2796 if ( moveMaster != this ) {
2797 moveMaster->Use_BinaryMover( activator );
2805 activatedBy = activator;
2807 if ( moverState == MOVER_POS1 ) {
2808 // FIXME: start moving USERCMD_MSEC later, because if this was player
2809 // triggered, gameLocal.time hasn't been advanced yet
2810 MatchActivateTeam( MOVER_1TO2, gameLocal.time + USERCMD_MSEC );
2812 SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
2814 ProcessEvent( &EV_Mover_OpenPortal );
2818 // if all the way up, just delay before coming down
2819 if ( moverState == MOVER_POS2 ) {
2820 idMover_Binary *slave;
2826 SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
2828 for ( slave = this; slave != NULL; slave = slave->activateChain ) {
2829 slave->CancelEvents( &EV_Mover_ReturnToPos1 );
2830 slave->PostEventSec( &EV_Mover_ReturnToPos1, spawnArgs.GetBool( "toggle" ) ? 0 : wait );
2835 // only partway down before reversing
2836 if ( moverState == MOVER_2TO1 ) {
2841 // only partway up before reversing
2842 if ( moverState == MOVER_1TO2 ) {
2850 idMover_Binary::Event_Use_BinaryMover
2853 void idMover_Binary::Event_Use_BinaryMover( idEntity *activator ) {
2854 Use_BinaryMover( activator );
2859 idMover_Binary::PreBind
2862 void idMover_Binary::PreBind( void ) {
2863 pos1 = GetWorldCoordinates( pos1 );
2864 pos2 = GetWorldCoordinates( pos2 );
2869 idMover_Binary::PostBind
2872 void idMover_Binary::PostBind( void ) {
2873 pos1 = GetLocalCoordinates( pos1 );
2874 pos2 = GetLocalCoordinates( pos2 );
2879 idMover_Binary::FindGuiTargets
2882 void idMover_Binary::FindGuiTargets( void ) {
2883 gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
2887 ==============================
2888 idMover_Binary::SetGuiState
2890 key/val will be set to any renderEntity->gui's on the list
2891 ==============================
2893 void idMover_Binary::SetGuiState( const char *key, const char *val ) const {
2896 for( i = 0; i < guiTargets.Num(); i++ ) {
2897 idEntity *ent = guiTargets[ i ].GetEntity();
2899 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
2900 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
2901 ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
2902 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
2905 ent->UpdateVisuals();
2912 idMover_Binary::Event_InitGuiTargets
2915 void idMover_Binary::Event_FindGuiTargets( void ) {
2921 idMover_Binary::Event_InitGuiTargets
2924 void idMover_Binary::Event_InitGuiTargets( void ) {
2925 if ( guiTargets.Num() ) {
2926 SetGuiState( "movestate", guiBinaryMoverStates[MOVER_POS1] );
2932 idMover_Binary::InitSpeed
2934 pos1, pos2, and speed are passed in so the movement delta can be calculated
2937 void idMover_Binary::InitSpeed( idVec3 &mpos1, idVec3 &mpos2, float mspeed, float maccelTime, float mdecelTime ) {
2945 accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
2946 decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
2948 speed = mspeed ? mspeed : 100;
2950 // calculate time to reach second position from speed
2952 distance = move.Length();
2953 duration = idPhysics::SnapTimeToPhysicsFrame( distance * 1000 / speed );
2954 if ( duration <= 0 ) {
2958 moverState = MOVER_POS1;
2960 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
2961 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
2964 PostEventMS( &EV_Mover_InitGuiTargets, 0 );
2969 idMover_Binary::InitTime
2971 pos1, pos2, and time are passed in so the movement delta can be calculated
2974 void idMover_Binary::InitTime( idVec3 &mpos1, idVec3 &mpos2, float mtime, float maccelTime, float mdecelTime ) {
2979 accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
2980 decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
2982 duration = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mtime ) );
2983 if ( duration <= 0 ) {
2987 moverState = MOVER_POS1;
2989 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
2990 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
2993 PostEventMS( &EV_Mover_InitGuiTargets, 0 );
2998 idMover_Binary::SetBlocked
3001 void idMover_Binary::SetBlocked( bool b ) {
3002 for ( idMover_Binary *slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
3005 const idKeyValue *kv = slave->spawnArgs.MatchPrefix( "triggerBlocked" );
3007 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
3009 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
3011 kv = slave->spawnArgs.MatchPrefix( "triggerBlocked", kv );
3019 idMover_Binary::IsBlocked
3022 bool idMover_Binary::IsBlocked( void ) {
3028 idMover_Binary::GetActivator
3031 idEntity *idMover_Binary::GetActivator( void ) const {
3032 return activatedBy.GetEntity();
3037 idMover_Binary::WriteToSnapshot
3040 void idMover_Binary::WriteToSnapshot( idBitMsgDelta &msg ) const {
3041 physicsObj.WriteToSnapshot( msg );
3042 msg.WriteBits( moverState, 3 );
3043 WriteBindToSnapshot( msg );
3048 idMover_Binary::ReadFromSnapshot
3051 void idMover_Binary::ReadFromSnapshot( const idBitMsgDelta &msg ) {
3052 moverState_t oldMoverState = moverState;
3054 physicsObj.ReadFromSnapshot( msg );
3055 moverState = (moverState_t) msg.ReadBits( 3 );
3056 ReadBindFromSnapshot( msg );
3058 if ( msg.HasChanged() ) {
3059 if ( moverState != oldMoverState ) {
3060 UpdateMoverSound( moverState );
3068 idMover_Binary::SetPortalState
3071 void idMover_Binary::SetPortalState( bool open ) {
3072 assert( areaPortal );
3073 gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
3077 ===============================================================================
3081 A use can be triggered either by a touch function, by being shot, or by being
3082 targeted by another entity.
3084 ===============================================================================
3087 const idEventDef EV_Door_StartOpen( "<startOpen>", NULL );
3088 const idEventDef EV_Door_SpawnDoorTrigger( "<spawnDoorTrigger>", NULL );
3089 const idEventDef EV_Door_SpawnSoundTrigger( "<spawnSoundTrigger>", NULL );
3090 const idEventDef EV_Door_Open( "open", NULL );
3091 const idEventDef EV_Door_Close( "close", NULL );
3092 const idEventDef EV_Door_Lock( "lock", "d" );
3093 const idEventDef EV_Door_IsOpen( "isOpen", NULL, 'f' );
3094 const idEventDef EV_Door_IsLocked( "isLocked", NULL, 'f' );
3096 CLASS_DECLARATION( idMover_Binary, idDoor )
3097 EVENT( EV_TeamBlocked, idDoor::Event_TeamBlocked )
3098 EVENT( EV_PartBlocked, idDoor::Event_PartBlocked )
3099 EVENT( EV_Touch, idDoor::Event_Touch )
3100 EVENT( EV_Activate, idDoor::Event_Activate )
3101 EVENT( EV_Door_StartOpen, idDoor::Event_StartOpen )
3102 EVENT( EV_Door_SpawnDoorTrigger, idDoor::Event_SpawnDoorTrigger )
3103 EVENT( EV_Door_SpawnSoundTrigger, idDoor::Event_SpawnSoundTrigger )
3104 EVENT( EV_Door_Open, idDoor::Event_Open )
3105 EVENT( EV_Door_Close, idDoor::Event_Close )
3106 EVENT( EV_Door_Lock, idDoor::Event_Lock )
3107 EVENT( EV_Door_IsOpen, idDoor::Event_IsOpen )
3108 EVENT( EV_Door_IsLocked, idDoor::Event_Locked )
3109 EVENT( EV_ReachedPos, idDoor::Event_Reached_BinaryMover )
3110 EVENT( EV_SpectatorTouch, idDoor::Event_SpectatorTouch )
3111 EVENT( EV_Mover_OpenPortal, idDoor::Event_OpenPortal )
3112 EVENT( EV_Mover_ClosePortal, idDoor::Event_ClosePortal )
3120 idDoor::idDoor( void ) {
3124 aas_area_closed = false;
3128 nextSndTriggerTime = 0;
3129 localTriggerOrigin.Zero();
3130 localTriggerAxis.Identity();
3134 companionDoor = NULL;
3135 normalAxisIndex = 0;
3143 idDoor::~idDoor( void ) {
3157 void idDoor::Save( idSaveGame *savefile ) const {
3159 savefile->WriteFloat( triggersize );
3160 savefile->WriteBool( crusher );
3161 savefile->WriteBool( noTouch );
3162 savefile->WriteBool( aas_area_closed );
3163 savefile->WriteString( buddyStr );
3164 savefile->WriteInt( nextSndTriggerTime );
3166 savefile->WriteVec3( localTriggerOrigin );
3167 savefile->WriteMat3( localTriggerAxis );
3169 savefile->WriteString( requires );
3170 savefile->WriteInt( removeItem );
3171 savefile->WriteString( syncLock );
3172 savefile->WriteInt( normalAxisIndex );
3174 savefile->WriteClipModel( trigger );
3175 savefile->WriteClipModel( sndTrigger );
3177 savefile->WriteObject( companionDoor );
3185 void idDoor::Restore( idRestoreGame *savefile ) {
3187 savefile->ReadFloat( triggersize );
3188 savefile->ReadBool( crusher );
3189 savefile->ReadBool( noTouch );
3190 savefile->ReadBool( aas_area_closed );
3191 SetAASAreaState( aas_area_closed );
3192 savefile->ReadString( buddyStr );
3193 savefile->ReadInt( nextSndTriggerTime );
3195 savefile->ReadVec3( localTriggerOrigin );
3196 savefile->ReadMat3( localTriggerAxis );
3198 savefile->ReadString( requires );
3199 savefile->ReadInt( removeItem );
3200 savefile->ReadString( syncLock );
3201 savefile->ReadInt( normalAxisIndex );
3203 savefile->ReadClipModel( trigger );
3204 savefile->ReadClipModel( sndTrigger );
3206 savefile->ReadObject( reinterpret_cast<idClass *&>( companionDoor ) );
3214 void idDoor::Spawn( void ) {
3225 // get the direction to move
3226 if ( !spawnArgs.GetFloat( "movedir", "0", dir ) ) {
3227 // no movedir, so angle defines movement direction and not orientation,
3228 // a la oldschool Quake
3229 SetAngles( ang_zero );
3230 spawnArgs.GetFloat( "angle", "0", dir );
3232 GetMovedir( dir, movedir );
3234 // default speed of 400
3235 spawnArgs.GetFloat( "speed", "400", speed );
3237 // default wait of 2 seconds
3238 spawnArgs.GetFloat( "wait", "3", wait );
3240 // default lip of 8 units
3241 spawnArgs.GetFloat( "lip", "8", lip );
3243 // by default no damage
3244 spawnArgs.GetFloat( "damage", "0", damage );
3247 spawnArgs.GetFloat( "triggersize", "120", triggersize );
3249 spawnArgs.GetBool( "crusher", "0", crusher );
3250 spawnArgs.GetBool( "start_open", "0", start_open );
3251 spawnArgs.GetBool( "no_touch", "0", noTouch );
3253 // expects syncLock to be a door that must be closed before this door will open
3254 spawnArgs.GetString( "syncLock", "", syncLock );
3256 spawnArgs.GetString( "buddy", "", buddyStr );
3258 spawnArgs.GetString( "requires", "", requires );
3259 spawnArgs.GetInt( "removeItem", "0", removeItem );
3261 // ever separate piece of a door is considered solid when other team mates push entities
3262 fl.solidForTeam = true;
3264 // first position at start
3265 pos1 = GetPhysics()->GetOrigin();
3267 // calculate second position
3268 abs_movedir[0] = idMath::Fabs( movedir[ 0 ] );
3269 abs_movedir[1] = idMath::Fabs( movedir[ 1 ] );
3270 abs_movedir[2] = idMath::Fabs( movedir[ 2 ] );
3271 size = GetPhysics()->GetAbsBounds()[1] - GetPhysics()->GetAbsBounds()[0];
3272 distance = ( abs_movedir * size ) - lip;
3273 pos2 = pos1 + distance * movedir;
3275 // if "start_open", reverse position 1 and 2
3277 // post it after EV_SpawnBind
3278 PostEventMS( &EV_Door_StartOpen, 1 );
3281 if ( spawnArgs.GetFloat( "time", "1", time ) ) {
3282 InitTime( pos1, pos2, time, 0, 0 );
3284 InitSpeed( pos1, pos2, speed, 0, 0 );
3287 if ( moveMaster == this ) {
3289 fl.takedamage = true;
3291 if ( noTouch || health ) {
3292 // non touch/shoot doors
3293 PostEventMS( &EV_Mover_MatchTeam, 0, moverState, gameLocal.time );
3295 const char *sndtemp = spawnArgs.GetString( "snd_locked" );
3296 if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
3297 PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
3301 PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
3305 // see if we are on an areaportal
3306 areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
3307 if ( !start_open ) {
3309 ProcessEvent( &EV_Mover_ClosePortal );
3312 int locked = spawnArgs.GetInt( "locked" );
3314 // make sure all members of the team get locked
3315 PostEventMS( &EV_Door_Lock, 0, locked );
3318 if ( spawnArgs.GetBool( "continuous" ) ) {
3319 PostEventSec( &EV_Activate, spawnArgs.GetFloat( "delay" ), this );
3322 // sounds have a habit of stuttering when portals close, so make them unoccluded
3323 refSound.parms.soundShaderFlags |= SSF_NO_OCCLUSION;
3325 companionDoor = NULL;
3336 void idDoor::Think( void ) {
3337 idVec3 masterOrigin;
3340 idMover_Binary::Think();
3342 if ( thinkFlags & TH_PHYSICS ) {
3343 // update trigger position
3344 if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
3346 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
3349 sndTrigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
3360 void idDoor::PreBind( void ) {
3361 idMover_Binary::PreBind();
3369 void idDoor::PostBind( void ) {
3370 idMover_Binary::PostBind();
3371 GetLocalTriggerPosition( trigger ? trigger : sndTrigger );
3376 idDoor::SetAASAreaState
3379 void idDoor::SetAASAreaState( bool closed ) {
3380 aas_area_closed = closed;
3381 gameLocal.SetAASAreaState( physicsObj.GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL|AREACONTENTS_OBSTACLE, closed );
3389 void idDoor::Hide( void ) {
3390 idMover_Binary *slave;
3391 idMover_Binary *master;
3395 master = GetMoveMaster();
3396 if ( this != master ) {
3399 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
3400 if ( slave->IsType( idDoor::Type ) ) {
3401 slaveDoor = static_cast<idDoor *>( slave );
3402 companion = slaveDoor->companionDoor;
3403 if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
3406 if ( slaveDoor->trigger ) {
3407 slaveDoor->trigger->Disable();
3409 if ( slaveDoor->sndTrigger ) {
3410 slaveDoor->sndTrigger->Disable();
3412 if ( slaveDoor->areaPortal ) {
3413 slaveDoor->SetPortalState( true );
3415 slaveDoor->SetAASAreaState( false );
3417 slave->GetPhysics()->GetClipModel()->Disable();
3418 slave->idMover_Binary::Hide();
3428 void idDoor::Show( void ) {
3429 idMover_Binary *slave;
3430 idMover_Binary *master;
3434 master = GetMoveMaster();
3435 if ( this != master ) {
3438 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
3439 if ( slave->IsType( idDoor::Type ) ) {
3440 slaveDoor = static_cast<idDoor *>( slave );
3441 companion = slaveDoor->companionDoor;
3442 if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
3445 if ( slaveDoor->trigger ) {
3446 slaveDoor->trigger->Enable();
3448 if ( slaveDoor->sndTrigger ) {
3449 slaveDoor->sndTrigger->Enable();
3451 if ( slaveDoor->areaPortal && ( slaveDoor->moverState == MOVER_POS1 ) ) {
3452 slaveDoor->SetPortalState( false );
3454 slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
3456 slave->GetPhysics()->GetClipModel()->Enable();
3457 slave->idMover_Binary::Show();
3464 idDoor::GetLocalTriggerPosition
3467 void idDoor::GetLocalTriggerPosition( const idClipModel *trigger ) {
3475 GetMasterPosition( origin, axis );
3476 localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
3477 localTriggerAxis = trigger->GetAxis() * axis.Transpose();
3485 void idDoor::Use( idEntity *other, idEntity *activator ) {
3486 if ( gameLocal.RequirementMet( activator, requires, removeItem ) ) {
3487 if ( syncLock.Length() ) {
3488 idEntity *sync = gameLocal.FindEntity( syncLock );
3489 if ( sync && sync->IsType( idDoor::Type ) ) {
3490 if ( static_cast<idDoor *>( sync )->IsOpen() ) {
3495 ActivateTargets( activator );
3496 Use_BinaryMover( activator );
3505 void idDoor::Open( void ) {
3514 void idDoor::Close( void ) {
3523 void idDoor::Lock( int f ) {
3524 idMover_Binary *other;
3526 // lock all the doors on the team
3527 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
3528 if ( other->IsType( idDoor::Type ) ) {
3529 idDoor *door = static_cast<idDoor *>( other );
3530 if ( other == moveMaster ) {
3531 if ( door->sndTrigger == NULL ) {
3532 // in this case the sound trigger never got spawned
3533 const char *sndtemp = door->spawnArgs.GetString( "snd_locked" );
3534 if ( sndtemp && *sndtemp ) {
3535 door->PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
3538 if ( !f && ( door->spawnArgs.GetInt( "locked" ) != 0 ) ) {
3539 door->StartSound( "snd_unlocked", SND_CHANNEL_ANY, 0, false, NULL );
3542 door->spawnArgs.SetInt( "locked", f );
3543 if ( ( f == 0 ) || ( !IsHidden() && ( door->moverState == MOVER_POS1 ) ) ) {
3544 door->SetAASAreaState( f != 0 );
3559 int idDoor::IsLocked( void ) {
3560 return spawnArgs.GetInt( "locked" );
3568 bool idDoor::IsOpen( void ) {
3569 return ( moverState != MOVER_POS1 );
3577 bool idDoor::IsNoTouch( void ) {
3582 ======================
3583 idDoor::CalcTriggerBounds
3585 Calcs bounds for a trigger.
3586 ======================
3588 void idDoor::CalcTriggerBounds( float size, idBounds &bounds ) {
3589 idMover_Binary *other;
3593 // find the bounds of everything on the team
3594 bounds = GetPhysics()->GetAbsBounds();
3596 fl.takedamage = true;
3597 for( other = activateChain; other != NULL; other = other->GetActivateChain() ) {
3598 if ( other->IsType( idDoor::Type ) ) {
3599 // find the bounds of everything on the team
3600 bounds.AddBounds( other->GetPhysics()->GetAbsBounds() );
3602 // set all of the slaves as shootable
3603 other->fl.takedamage = true;
3607 // find the thinnest axis, which will be the one we expand
3609 for ( i = 1 ; i < 3 ; i++ ) {
3610 if ( bounds[1][ i ] - bounds[0][ i ] < bounds[1][ best ] - bounds[0][ best ] ) {
3614 normalAxisIndex = best;
3615 bounds[0][ best ] -= size;
3616 bounds[1][ best ] += size;
3617 bounds[0] -= GetPhysics()->GetOrigin();
3618 bounds[1] -= GetPhysics()->GetOrigin();
3622 ======================
3623 idDoor::Event_StartOpen
3625 if "start_open", reverse position 1 and 2
3626 ======================
3628 void idDoor::Event_StartOpen( void ) {
3632 // if "start_open", reverse position 1 and 2
3634 pos2 = GetPhysics()->GetOrigin();
3636 spawnArgs.GetFloat( "speed", "400", speed );
3638 if ( spawnArgs.GetFloat( "time", "1", time ) ) {
3639 InitTime( pos1, pos2, time, 0, 0 );
3641 InitSpeed( pos1, pos2, speed, 0, 0 );
3646 ======================
3647 idDoor::Event_SpawnDoorTrigger
3649 All of the parts of a door have been spawned, so create
3650 a trigger that encloses all of them.
3651 ======================
3653 void idDoor::Event_SpawnDoorTrigger( void ) {
3655 idMover_Binary *other;
3659 // already have a trigger, so don't spawn a new one.
3663 // check if any of the doors are marked as toggled
3665 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
3666 if ( other->IsType( idDoor::Type ) && other->spawnArgs.GetBool( "toggle" ) ) {
3673 // mark them all as toggled
3674 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
3675 if ( other->IsType( idDoor::Type ) ) {
3676 other->spawnArgs.Set( "toggle", "1" );
3679 // don't spawn trigger
3683 const char *sndtemp = spawnArgs.GetString( "snd_locked" );
3684 if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
3685 PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
3688 CalcTriggerBounds( triggersize, bounds );
3690 // create a trigger clip model
3691 trigger = new idClipModel( idTraceModel( bounds ) );
3692 trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
3693 trigger->SetContents( CONTENTS_TRIGGER );
3695 GetLocalTriggerPosition( trigger );
3697 MatchActivateTeam( moverState, gameLocal.time );
3701 ======================
3702 idDoor::Event_SpawnSoundTrigger
3704 Spawn a sound trigger to activate locked sound if it exists.
3705 ======================
3707 void idDoor::Event_SpawnSoundTrigger( void ) {
3714 CalcTriggerBounds( triggersize * 0.5f, bounds );
3716 // create a trigger clip model
3717 sndTrigger = new idClipModel( idTraceModel( bounds ) );
3718 sndTrigger->Link( gameLocal.clip, this, 254, GetPhysics()->GetOrigin(), mat3_identity );
3719 sndTrigger->SetContents( CONTENTS_TRIGGER );
3721 GetLocalTriggerPosition( sndTrigger );
3726 idDoor::Event_Reached_BinaryMover
3729 void idDoor::Event_Reached_BinaryMover( void ) {
3730 if ( moverState == MOVER_2TO1 ) {
3731 SetBlocked( false );
3732 const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerClosed" );
3734 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
3736 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
3738 kv = spawnArgs.MatchPrefix( "triggerClosed", kv );
3740 } else if ( moverState == MOVER_1TO2 ) {
3741 const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerOpened" );
3743 idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
3745 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
3747 kv = spawnArgs.MatchPrefix( "triggerOpened", kv );
3750 idMover_Binary::Event_Reached_BinaryMover();
3755 idDoor::Blocked_Door
3758 void idDoor::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
3762 return; // crushers don't reverse
3765 // reverse direction
3766 Use_BinaryMover( moveMaster->GetActivator() );
3768 if ( companionDoor ) {
3769 companionDoor->ProcessEvent( &EV_TeamBlocked, blockedEntity, blockingEntity );
3775 idDoor::SetCompanion
3778 void idDoor::SetCompanion( idDoor *door ) {
3779 companionDoor = door;
3784 idDoor::Event_PartBlocked
3787 void idDoor::Event_PartBlocked( idEntity *blockingEntity ) {
3788 if ( damage > 0.0f ) {
3789 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
3798 void idDoor::Event_Touch( idEntity *other, trace_t *trace ) {
3799 idVec3 contact, translate;
3800 idVec3 planeaxis1, planeaxis2, normal;
3807 if ( trigger && trace->c.id == trigger->GetId() ) {
3808 if ( !IsNoTouch() && !IsLocked() && GetMoverState() != MOVER_1TO2 ) {
3811 } else if ( sndTrigger && trace->c.id == sndTrigger->GetId() ) {
3812 if ( other && other->IsType( idPlayer::Type ) && IsLocked() && gameLocal.time > nextSndTriggerTime ) {
3813 StartSound( "snd_locked", SND_CHANNEL_ANY, 0, false, NULL );
3814 nextSndTriggerTime = gameLocal.time + 10000;
3821 idDoor::Event_SpectatorTouch
3824 void idDoor::Event_SpectatorTouch( idEntity *other, trace_t *trace ) {
3825 idVec3 contact, translate, normal;
3829 assert( other && other->IsType( idPlayer::Type ) && static_cast< idPlayer * >( other )->spectating );
3831 p = static_cast< idPlayer * >( other );
3832 // avoid flicker when stopping right at clip box boundaries
3833 if ( p->lastSpectateTeleport > gameLocal.time - 1000 ) {
3836 if ( trigger && !IsOpen() ) {
3837 // teleport to the other side, center to the middle of the trigger brush
3838 bounds = trigger->GetAbsBounds();
3839 contact = trace->endpos - bounds.GetCenter();
3840 translate = bounds.GetCenter();
3842 normal[ normalAxisIndex ] = 1.0f;
3843 if ( normal * contact > 0 ) {
3844 translate[ normalAxisIndex ] += ( bounds[ 0 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
3846 translate[ normalAxisIndex ] += ( bounds[ 1 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
3848 p->SetOrigin( translate );
3849 p->lastSpectateTeleport = gameLocal.time;
3855 idDoor::Event_Activate
3858 void idDoor::Event_Activate( idEntity *activator ) {
3861 if ( spawnArgs.GetInt( "locked" ) ) {
3863 PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
3865 if ( buddyStr.Length() ) {
3866 idEntity *buddy = gameLocal.FindEntity( buddyStr );
3868 buddy->SetShaderParm( SHADERPARM_MODE, 1 );
3869 buddy->UpdateVisuals();
3873 old_lock = spawnArgs.GetInt( "locked" );
3875 if ( old_lock == 2 ) {
3880 if ( syncLock.Length() ) {
3881 idEntity *sync = gameLocal.FindEntity( syncLock );
3882 if ( sync && sync->IsType( idDoor::Type ) ) {
3883 if ( static_cast<idDoor *>( sync )->IsOpen() ) {
3889 ActivateTargets( activator );
3891 renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
3894 Use_BinaryMover( activator );
3902 void idDoor::Event_Open( void ) {
3911 void idDoor::Event_Close( void ) {
3920 void idDoor::Event_Lock( int f ) {
3926 idDoor::Event_IsOpen
3929 void idDoor::Event_IsOpen( void ) {
3933 idThread::ReturnFloat( state );
3938 idDoor::Event_Locked
3941 void idDoor::Event_Locked( void ) {
3942 idThread::ReturnFloat( spawnArgs.GetInt("locked") );
3947 idDoor::Event_OpenPortal
3949 Sets the portal associtated with this door to be open
3952 void idDoor::Event_OpenPortal( void ) {
3953 idMover_Binary *slave;
3956 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
3957 if ( slave->IsType( idDoor::Type ) ) {
3958 slaveDoor = static_cast<idDoor *>( slave );
3959 if ( slaveDoor->areaPortal ) {
3960 slaveDoor->SetPortalState( true );
3962 slaveDoor->SetAASAreaState( false );
3969 idDoor::Event_ClosePortal
3971 Sets the portal associtated with this door to be closed
3974 void idDoor::Event_ClosePortal( void ) {
3975 idMover_Binary *slave;
3978 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
3979 if ( !slave->IsHidden() ) {
3980 if ( slave->IsType( idDoor::Type ) ) {
3981 slaveDoor = static_cast<idDoor *>( slave );
3982 if ( slaveDoor->areaPortal ) {
3983 slaveDoor->SetPortalState( false );
3985 slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
3993 ===============================================================================
3997 ===============================================================================
4000 CLASS_DECLARATION( idMover_Binary, idPlat )
4001 EVENT( EV_Touch, idPlat::Event_Touch )
4002 EVENT( EV_TeamBlocked, idPlat::Event_TeamBlocked )
4003 EVENT( EV_PartBlocked, idPlat::Event_PartBlocked )
4011 idPlat::idPlat( void ) {
4013 localTriggerOrigin.Zero();
4014 localTriggerAxis.Identity();
4022 idPlat::~idPlat( void ) {
4033 void idPlat::Save( idSaveGame *savefile ) const {
4034 savefile->WriteClipModel( trigger );
4035 savefile->WriteVec3( localTriggerOrigin );
4036 savefile->WriteMat3( localTriggerAxis );
4044 void idPlat::Restore( idRestoreGame *savefile ) {
4045 savefile->ReadClipModel( trigger );
4046 savefile->ReadVec3( localTriggerOrigin );
4047 savefile->ReadMat3( localTriggerAxis );
4055 void idPlat::Spawn( void ) {
4064 spawnArgs.GetFloat( "speed", "100", speed );
4065 spawnArgs.GetFloat( "damage", "0", damage );
4066 spawnArgs.GetFloat( "wait", "1", wait );
4067 spawnArgs.GetFloat( "lip", "8", lip );
4068 spawnArgs.GetFloat( "accel_time", "0.25", accel );
4069 spawnArgs.GetFloat( "decel_time", "0.25", decel );
4071 // create second position
4072 if ( !spawnArgs.GetFloat( "height", "0", height ) ) {
4073 height = ( GetPhysics()->GetBounds()[1][2] - GetPhysics()->GetBounds()[0][2] ) - lip;
4076 spawnArgs.GetBool( "no_touch", "0", noTouch );
4078 // pos1 is the rest (bottom) position, pos2 is the top
4079 pos2 = GetPhysics()->GetOrigin();
4083 if ( spawnArgs.GetFloat( "time", "1", time ) ) {
4084 InitTime( pos1, pos2, time, accel, decel );
4086 InitSpeed( pos1, pos2, speed, accel, decel );
4089 SetMoverState( MOVER_POS1, gameLocal.time );
4092 // spawn the trigger if one hasn't been custom made
4095 SpawnPlatTrigger( pos1 );
4104 void idPlat::Think( void ) {
4105 idVec3 masterOrigin;
4108 idMover_Binary::Think();
4110 if ( thinkFlags & TH_PHYSICS ) {
4111 // update trigger position
4112 if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
4114 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
4125 void idPlat::PreBind( void ) {
4126 idMover_Binary::PreBind();
4134 void idPlat::PostBind( void ) {
4135 idMover_Binary::PostBind();
4136 GetLocalTriggerPosition( trigger );
4141 idPlat::GetLocalTriggerPosition
4144 void idPlat::GetLocalTriggerPosition( const idClipModel *trigger ) {
4152 GetMasterPosition( origin, axis );
4153 localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
4154 localTriggerAxis = trigger->GetAxis() * axis.Transpose();
4159 idPlat::SpawnPlatTrigger
4162 void idPlat::SpawnPlatTrigger( idVec3 &pos ) {
4167 // the middle trigger will be a thin trigger just
4168 // above the starting position
4170 bounds = GetPhysics()->GetBounds();
4172 tmin[0] = bounds[0][0] + 33;
4173 tmin[1] = bounds[0][1] + 33;
4174 tmin[2] = bounds[0][2];
4176 tmax[0] = bounds[1][0] - 33;
4177 tmax[1] = bounds[1][1] - 33;
4178 tmax[2] = bounds[1][2] + 8;
4180 if ( tmax[0] <= tmin[0] ) {
4181 tmin[0] = ( bounds[0][0] + bounds[1][0] ) * 0.5f;
4182 tmax[0] = tmin[0] + 1;
4184 if ( tmax[1] <= tmin[1] ) {
4185 tmin[1] = ( bounds[0][1] + bounds[1][1] ) * 0.5f;
4186 tmax[1] = tmin[1] + 1;
4189 trigger = new idClipModel( idTraceModel( idBounds( tmin, tmax ) ) );
4190 trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
4191 trigger->SetContents( CONTENTS_TRIGGER );
4199 void idPlat::Event_Touch( idEntity *other, trace_t *trace ) {
4200 if ( !other->IsType( idPlayer::Type ) ) {
4204 if ( ( GetMoverState() == MOVER_POS1 ) && trigger && ( trace->c.id == trigger->GetId() ) && ( other->health > 0 ) ) {
4205 Use_BinaryMover( other );
4211 idPlat::Event_TeamBlocked
4214 void idPlat::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
4215 // reverse direction
4216 Use_BinaryMover( activatedBy.GetEntity() );
4221 idPlat::Event_PartBlocked
4224 void idPlat::Event_PartBlocked( idEntity *blockingEntity ) {
4225 if ( damage > 0.0f ) {
4226 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
4232 ===============================================================================
4236 ===============================================================================
4239 CLASS_DECLARATION( idEntity, idMover_Periodic )
4240 EVENT( EV_TeamBlocked, idMover_Periodic::Event_TeamBlocked )
4241 EVENT( EV_PartBlocked, idMover_Periodic::Event_PartBlocked )
4246 idMover_Periodic::idMover_Periodic
4249 idMover_Periodic::idMover_Periodic( void ) {
4251 fl.neverDormant = false;
4256 idMover_Periodic::Spawn
4259 void idMover_Periodic::Spawn( void ) {
4260 spawnArgs.GetFloat( "damage", "0", damage );
4261 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
4262 GetPhysics()->SetContents( 0 );
4268 idMover_Periodic::Save
4271 void idMover_Periodic::Save( idSaveGame *savefile ) const {
4272 savefile->WriteFloat( damage );
4273 savefile->WriteStaticObject( physicsObj );
4278 idMover_Periodic::Restore
4281 void idMover_Periodic::Restore( idRestoreGame *savefile ) {
4282 savefile->ReadFloat( damage );
4283 savefile->ReadStaticObject( physicsObj );
4284 RestorePhysics( &physicsObj );
4289 idMover_Periodic::Think
4292 void idMover_Periodic::Think( void ) {
4293 // if we are completely closed off from the player, don't do anything at all
4294 if ( CheckDormant() ) {
4304 idMover_Periodic::Event_TeamBlocked
4307 void idMover_Periodic::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
4312 idMover_Periodic::Event_PartBlocked
4315 void idMover_Periodic::Event_PartBlocked( idEntity *blockingEntity ) {
4316 if ( damage > 0.0f ) {
4317 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
4323 idMover_Periodic::WriteToSnapshot
4326 void idMover_Periodic::WriteToSnapshot( idBitMsgDelta &msg ) const {
4327 physicsObj.WriteToSnapshot( msg );
4328 WriteBindToSnapshot( msg );
4333 idMover_Periodic::ReadFromSnapshot
4336 void idMover_Periodic::ReadFromSnapshot( const idBitMsgDelta &msg ) {
4337 physicsObj.ReadFromSnapshot( msg );
4338 ReadBindFromSnapshot( msg );
4340 if ( msg.HasChanged() ) {
4347 ===============================================================================
4351 ===============================================================================
4354 CLASS_DECLARATION( idMover_Periodic, idRotater )
4355 EVENT( EV_Activate, idRotater::Event_Activate )
4360 idRotater::idRotater
4363 idRotater::idRotater( void ) {
4372 void idRotater::Spawn( void ) {
4373 physicsObj.SetSelf( this );
4374 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4375 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4376 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4377 physicsObj.SetClipMask( MASK_SOLID );
4378 if ( !spawnArgs.GetBool( "nopush" ) ) {
4379 physicsObj.SetPusher( 0 );
4381 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, gameLocal.time, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
4382 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
4383 SetPhysics( &physicsObj );
4385 if ( spawnArgs.GetBool( "start_on" ) ) {
4386 ProcessEvent( &EV_Activate, this );
4395 void idRotater::Save( idSaveGame *savefile ) const {
4396 activatedBy.Save( savefile );
4404 void idRotater::Restore( idRestoreGame *savefile ) {
4405 activatedBy.Restore( savefile );
4410 idRotater::Event_Activate
4413 void idRotater::Event_Activate( idEntity *activator ) {
4419 activatedBy = activator;
4423 if ( !spawnArgs.GetBool( "rotate" ) ) {
4424 spawnArgs.Set( "rotate", "1" );
4425 spawnArgs.GetFloat( "speed", "100", speed );
4426 spawnArgs.GetBool( "x_axis", "0", x_axis );
4427 spawnArgs.GetBool( "y_axis", "0", y_axis );
4429 // set the axis of rotation
4432 } else if ( y_axis ) {
4438 spawnArgs.Set( "rotate", "0" );
4441 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, physicsObj.GetAxis().ToAngles(), delta, ang_zero );
4446 ===============================================================================
4450 ===============================================================================
4453 CLASS_DECLARATION( idMover_Periodic, idBobber )
4461 idBobber::idBobber( void ) {
4469 void idBobber::Spawn( void ) {
4477 spawnArgs.GetFloat( "speed", "4", speed );
4478 spawnArgs.GetFloat( "height", "32", height );
4479 spawnArgs.GetFloat( "phase", "0", phase );
4480 spawnArgs.GetBool( "x_axis", "0", x_axis );
4481 spawnArgs.GetBool( "y_axis", "0", y_axis );
4483 // set the axis of bobbing
4484 delta = vec3_origin;
4486 delta[ 0 ] = height;
4487 } else if ( y_axis ) {
4488 delta[ 1 ] = height;
4490 delta[ 2 ] = height;
4493 physicsObj.SetSelf( this );
4494 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4495 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4496 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4497 physicsObj.SetClipMask( MASK_SOLID );
4498 if ( !spawnArgs.GetBool( "nopush" ) ) {
4499 physicsObj.SetPusher( 0 );
4501 physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, speed * 500, GetPhysics()->GetOrigin(), delta * 2.0f, vec3_origin );
4502 SetPhysics( &physicsObj );
4507 ===============================================================================
4511 ===============================================================================
4514 CLASS_DECLARATION( idMover_Periodic, idPendulum )
4519 idPendulum::idPendulum
4522 idPendulum::idPendulum( void ) {
4530 void idPendulum::Spawn( void ) {
4536 spawnArgs.GetFloat( "speed", "30", speed );
4537 spawnArgs.GetFloat( "phase", "0", phase );
4539 if ( spawnArgs.GetFloat( "freq", "", freq ) ) {
4540 if ( freq <= 0.0f ) {
4541 gameLocal.Error( "Invalid frequency on entity '%s'", GetName() );
4544 // find pendulum length
4545 length = idMath::Fabs( GetPhysics()->GetBounds()[0][2] );
4550 freq = 1 / ( idMath::TWO_PI ) * idMath::Sqrt( g_gravity.GetFloat() / ( 3 * length ) );
4553 physicsObj.SetSelf( this );
4554 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4555 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4556 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4557 physicsObj.SetClipMask( MASK_SOLID );
4558 if ( !spawnArgs.GetBool( "nopush" ) ) {
4559 physicsObj.SetPusher( 0 );
4561 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
4562 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, 500/freq, GetPhysics()->GetAxis().ToAngles(), idAngles( 0, 0, speed * 2.0f ), ang_zero );
4563 SetPhysics( &physicsObj );
4568 ===============================================================================
4572 ===============================================================================
4575 CLASS_DECLARATION( idMover_Periodic, idRiser )
4576 EVENT( EV_Activate, idRiser::Event_Activate )
4584 idRiser::idRiser( void ) {
4592 void idRiser::Spawn( void ) {
4593 physicsObj.SetSelf( this );
4594 physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
4595 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
4596 physicsObj.SetAxis( GetPhysics()->GetAxis() );
4598 physicsObj.SetClipMask( MASK_SOLID );
4599 if ( !spawnArgs.GetBool( "solid", "1" ) ) {
4600 physicsObj.SetContents( 0 );
4602 if ( !spawnArgs.GetBool( "nopush" ) ) {
4603 physicsObj.SetPusher( 0 );
4605 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
4606 SetPhysics( &physicsObj );
4611 idRiser::Event_Activate
4614 void idRiser::Event_Activate( idEntity *activator ) {
4616 if ( !IsHidden() && spawnArgs.GetBool("hide") ) {
4624 spawnArgs.GetFloat( "time", "4", time );
4625 spawnArgs.GetFloat( "height", "32", height );
4627 delta = vec3_origin;
4628 delta[ 2 ] = height;
4630 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.time, time * 1000, physicsObj.GetOrigin(), delta, vec3_origin );