Mover.cpp (117817B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition 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. 13 14 Doom 3 BFG Edition 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. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 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. 25 26 =========================================================================== 27 */ 28 29 #include "../idlib/precompiled.h" 30 #pragma hdrstop 31 32 #include "Game_local.h" 33 34 // _D3XP : rename all gameLocal.time to gameLocal.slow.time for merge! 35 36 // a mover will update any gui entities in it's target list with 37 // a key/val pair of "mover" "state" from below.. guis can represent 38 // realtime info like this 39 // binary only 40 static const char *guiBinaryMoverStates[] = { 41 "1", // pos 1 42 "2", // pos 2 43 "3", // moving 1 to 2 44 "4" // moving 2 to 1 45 }; 46 47 48 /* 49 =============================================================================== 50 51 idMover 52 53 =============================================================================== 54 */ 55 56 const idEventDef EV_FindGuiTargets( "<FindGuiTargets>", NULL ); 57 const idEventDef EV_TeamBlocked( "<teamblocked>", "ee" ); 58 const idEventDef EV_PartBlocked( "<partblocked>", "e" ); 59 const idEventDef EV_ReachedPos( "<reachedpos>", NULL ); 60 const idEventDef EV_ReachedAng( "<reachedang>", NULL ); 61 const idEventDef EV_PostRestore( "<postrestore>", "ddddd" ); 62 const idEventDef EV_StopMoving( "stopMoving", NULL ); 63 const idEventDef EV_StopRotating( "stopRotating", NULL ); 64 const idEventDef EV_Speed( "speed", "f" ); 65 const idEventDef EV_Time( "time", "f" ); 66 const idEventDef EV_AccelTime( "accelTime", "f" ); 67 const idEventDef EV_DecelTime( "decelTime", "f" ); 68 const idEventDef EV_MoveTo( "moveTo", "e" ); 69 const idEventDef EV_MoveToPos( "moveToPos", "v" ); 70 const idEventDef EV_Move( "move", "ff" ); 71 const idEventDef EV_MoveAccelerateTo( "accelTo", "ff" ); 72 const idEventDef EV_MoveDecelerateTo( "decelTo", "ff" ); 73 const idEventDef EV_RotateDownTo( "rotateDownTo", "df" ); 74 const idEventDef EV_RotateUpTo( "rotateUpTo", "df" ); 75 const idEventDef EV_RotateTo( "rotateTo", "v" ); 76 const idEventDef EV_Rotate( "rotate", "v" ); 77 const idEventDef EV_RotateOnce( "rotateOnce", "v" ); 78 const idEventDef EV_Bob( "bob", "ffv" ); 79 const idEventDef EV_Sway( "sway", "ffv" ); 80 const idEventDef EV_Mover_OpenPortal( "openPortal" ); 81 const idEventDef EV_Mover_ClosePortal( "closePortal" ); 82 const idEventDef EV_AccelSound( "accelSound", "s" ); 83 const idEventDef EV_DecelSound( "decelSound", "s" ); 84 const idEventDef EV_MoveSound( "moveSound", "s" ); 85 const idEventDef EV_Mover_InitGuiTargets( "<initguitargets>", NULL ); 86 const idEventDef EV_EnableSplineAngles( "enableSplineAngles", NULL ); 87 const idEventDef EV_DisableSplineAngles( "disableSplineAngles", NULL ); 88 const idEventDef EV_RemoveInitialSplineAngles( "removeInitialSplineAngles", NULL ); 89 const idEventDef EV_StartSpline( "startSpline", "e" ); 90 const idEventDef EV_StopSpline( "stopSpline", NULL ); 91 const idEventDef EV_IsMoving( "isMoving", NULL, 'd' ); 92 const idEventDef EV_IsRotating( "isRotating", NULL, 'd' ); 93 94 CLASS_DECLARATION( idEntity, idMover ) 95 EVENT( EV_FindGuiTargets, idMover::Event_FindGuiTargets ) 96 EVENT( EV_Thread_SetCallback, idMover::Event_SetCallback ) 97 EVENT( EV_TeamBlocked, idMover::Event_TeamBlocked ) 98 EVENT( EV_PartBlocked, idMover::Event_PartBlocked ) 99 EVENT( EV_ReachedPos, idMover::Event_UpdateMove ) 100 EVENT( EV_ReachedAng, idMover::Event_UpdateRotation ) 101 EVENT( EV_PostRestore, idMover::Event_PostRestore ) 102 EVENT( EV_StopMoving, idMover::Event_StopMoving ) 103 EVENT( EV_StopRotating, idMover::Event_StopRotating ) 104 EVENT( EV_Speed, idMover::Event_SetMoveSpeed ) 105 EVENT( EV_Time, idMover::Event_SetMoveTime ) 106 EVENT( EV_AccelTime, idMover::Event_SetAccellerationTime ) 107 EVENT( EV_DecelTime, idMover::Event_SetDecelerationTime ) 108 EVENT( EV_MoveTo, idMover::Event_MoveTo ) 109 EVENT( EV_MoveToPos, idMover::Event_MoveToPos ) 110 EVENT( EV_Move, idMover::Event_MoveDir ) 111 EVENT( EV_MoveAccelerateTo, idMover::Event_MoveAccelerateTo ) 112 EVENT( EV_MoveDecelerateTo, idMover::Event_MoveDecelerateTo ) 113 EVENT( EV_RotateDownTo, idMover::Event_RotateDownTo ) 114 EVENT( EV_RotateUpTo, idMover::Event_RotateUpTo ) 115 EVENT( EV_RotateTo, idMover::Event_RotateTo ) 116 EVENT( EV_Rotate, idMover::Event_Rotate ) 117 EVENT( EV_RotateOnce, idMover::Event_RotateOnce ) 118 EVENT( EV_Bob, idMover::Event_Bob ) 119 EVENT( EV_Sway, idMover::Event_Sway ) 120 EVENT( EV_Mover_OpenPortal, idMover::Event_OpenPortal ) 121 EVENT( EV_Mover_ClosePortal, idMover::Event_ClosePortal ) 122 EVENT( EV_AccelSound, idMover::Event_SetAccelSound ) 123 EVENT( EV_DecelSound, idMover::Event_SetDecelSound ) 124 EVENT( EV_MoveSound, idMover::Event_SetMoveSound ) 125 EVENT( EV_Mover_InitGuiTargets, idMover::Event_InitGuiTargets ) 126 EVENT( EV_EnableSplineAngles, idMover::Event_EnableSplineAngles ) 127 EVENT( EV_DisableSplineAngles, idMover::Event_DisableSplineAngles ) 128 EVENT( EV_RemoveInitialSplineAngles, idMover::Event_RemoveInitialSplineAngles ) 129 EVENT( EV_StartSpline, idMover::Event_StartSpline ) 130 EVENT( EV_StopSpline, idMover::Event_StopSpline ) 131 EVENT( EV_Activate, idMover::Event_Activate ) 132 EVENT( EV_IsMoving, idMover::Event_IsMoving ) 133 EVENT( EV_IsRotating, idMover::Event_IsRotating ) 134 END_CLASS 135 136 /* 137 ================ 138 idMover::idMover 139 ================ 140 */ 141 idMover::idMover() { 142 memset( &move, 0, sizeof( move ) ); 143 memset( &rot, 0, sizeof( rot ) ); 144 move_thread = 0; 145 rotate_thread = 0; 146 dest_angles.Zero(); 147 angle_delta.Zero(); 148 dest_position.Zero(); 149 move_delta.Zero(); 150 move_speed = 0.0f; 151 move_time = 0; 152 deceltime = 0; 153 acceltime = 0; 154 stopRotation = false; 155 useSplineAngles = true; 156 lastCommand = MOVER_NONE; 157 damage = 0.0f; 158 areaPortal = 0; 159 fl.networkSync = true; 160 } 161 162 /* 163 ================ 164 idMover::Save 165 ================ 166 */ 167 void idMover::Save( idSaveGame *savefile ) const { 168 int i; 169 170 savefile->WriteStaticObject( physicsObj ); 171 172 savefile->WriteInt( move.stage ); 173 savefile->WriteInt( move.acceleration ); 174 savefile->WriteInt( move.movetime ); 175 savefile->WriteInt( move.deceleration ); 176 savefile->WriteVec3( move.dir ); 177 178 savefile->WriteInt( rot.stage ); 179 savefile->WriteInt( rot.acceleration ); 180 savefile->WriteInt( rot.movetime ); 181 savefile->WriteInt( rot.deceleration ); 182 savefile->WriteFloat( rot.rot.pitch ); 183 savefile->WriteFloat( rot.rot.yaw ); 184 savefile->WriteFloat( rot.rot.roll ); 185 186 savefile->WriteInt( move_thread ); 187 savefile->WriteInt( rotate_thread ); 188 189 savefile->WriteAngles( dest_angles ); 190 savefile->WriteAngles( angle_delta ); 191 savefile->WriteVec3( dest_position ); 192 savefile->WriteVec3( move_delta ); 193 194 savefile->WriteFloat( move_speed ); 195 savefile->WriteInt( move_time ); 196 savefile->WriteInt( deceltime ); 197 savefile->WriteInt( acceltime ); 198 savefile->WriteBool( stopRotation ); 199 savefile->WriteBool( useSplineAngles ); 200 savefile->WriteInt( lastCommand ); 201 savefile->WriteFloat( damage ); 202 203 savefile->WriteInt( areaPortal ); 204 if ( areaPortal > 0 ) { 205 savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) ); 206 } 207 208 savefile->WriteInt( guiTargets.Num() ); 209 for( i = 0; i < guiTargets.Num(); i++ ) { 210 guiTargets[ i ].Save( savefile ); 211 } 212 213 if ( splineEnt.GetEntity() && splineEnt.GetEntity()->GetSpline() ) { 214 idCurve_Spline<idVec3> *spline = physicsObj.GetSpline(); 215 216 savefile->WriteBool( true ); 217 splineEnt.Save( savefile ); 218 savefile->WriteInt( spline->GetTime( 0 ) ); 219 savefile->WriteInt( spline->GetTime( spline->GetNumValues() - 1 ) - spline->GetTime( 0 ) ); 220 savefile->WriteInt( physicsObj.GetSplineAcceleration() ); 221 savefile->WriteInt( physicsObj.GetSplineDeceleration() ); 222 savefile->WriteInt( (int)physicsObj.UsingSplineAngles() ); 223 224 } else { 225 savefile->WriteBool( false ); 226 } 227 } 228 229 /* 230 ================ 231 idMover::Restore 232 ================ 233 */ 234 void idMover::Restore( idRestoreGame *savefile ) { 235 int i, num; 236 bool hasSpline = false; 237 238 savefile->ReadStaticObject( physicsObj ); 239 RestorePhysics( &physicsObj ); 240 241 savefile->ReadInt( (int&)move.stage ); 242 savefile->ReadInt( move.acceleration ); 243 savefile->ReadInt( move.movetime ); 244 savefile->ReadInt( move.deceleration ); 245 savefile->ReadVec3( move.dir ); 246 247 savefile->ReadInt( (int&)rot.stage ); 248 savefile->ReadInt( rot.acceleration ); 249 savefile->ReadInt( rot.movetime ); 250 savefile->ReadInt( rot.deceleration ); 251 savefile->ReadFloat( rot.rot.pitch ); 252 savefile->ReadFloat( rot.rot.yaw ); 253 savefile->ReadFloat( rot.rot.roll ); 254 255 savefile->ReadInt( move_thread ); 256 savefile->ReadInt( rotate_thread ); 257 258 savefile->ReadAngles( dest_angles ); 259 savefile->ReadAngles( angle_delta ); 260 savefile->ReadVec3( dest_position ); 261 savefile->ReadVec3( move_delta ); 262 263 savefile->ReadFloat( move_speed ); 264 savefile->ReadInt( move_time ); 265 savefile->ReadInt( deceltime ); 266 savefile->ReadInt( acceltime ); 267 savefile->ReadBool( stopRotation ); 268 savefile->ReadBool( useSplineAngles ); 269 savefile->ReadInt( (int &)lastCommand ); 270 savefile->ReadFloat( damage ); 271 272 savefile->ReadInt( areaPortal ); 273 if ( areaPortal > 0 ) { 274 int portalState = 0; 275 savefile->ReadInt( portalState ); 276 gameLocal.SetPortalState( areaPortal, portalState ); 277 } 278 279 guiTargets.Clear(); 280 savefile->ReadInt( num ); 281 guiTargets.SetNum( num ); 282 for( i = 0; i < num; i++ ) { 283 guiTargets[ i ].Restore( savefile ); 284 } 285 286 savefile->ReadBool( hasSpline ); 287 if ( hasSpline ) { 288 int starttime; 289 int totaltime; 290 int accel; 291 int decel; 292 int useAngles; 293 294 splineEnt.Restore( savefile ); 295 savefile->ReadInt( starttime ); 296 savefile->ReadInt( totaltime ); 297 savefile->ReadInt( accel ); 298 savefile->ReadInt( decel ); 299 savefile->ReadInt( useAngles ); 300 301 PostEventMS( &EV_PostRestore, 0, starttime, totaltime, accel, decel, useAngles ); 302 } 303 } 304 305 /* 306 ================ 307 idMover::Event_PostRestore 308 ================ 309 */ 310 void idMover::Event_PostRestore( int start, int total, int accel, int decel, int useSplineAng ) { 311 idCurve_Spline<idVec3> *spline; 312 313 idEntity *splineEntity = splineEnt.GetEntity(); 314 if ( !splineEntity ) { 315 // We should never get this event if splineEnt is invalid 316 common->Warning( "Invalid spline entity during restore\n" ); 317 return; 318 } 319 320 spline = splineEntity->GetSpline(); 321 322 spline->MakeUniform( total ); 323 spline->ShiftTime( start - spline->GetTime( 0 ) ); 324 325 physicsObj.SetSpline( spline, accel, decel, ( useSplineAng != 0 ) ); 326 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin ); 327 } 328 329 /* 330 ================ 331 idMover::Spawn 332 ================ 333 */ 334 void idMover::Spawn() { 335 move_thread = 0; 336 rotate_thread = 0; 337 stopRotation = false; 338 lastCommand = MOVER_NONE; 339 340 acceltime = 1000.0f * spawnArgs.GetFloat( "accel_time", "0" ); 341 deceltime = 1000.0f * spawnArgs.GetFloat( "decel_time", "0" ); 342 move_time = 1000.0f * spawnArgs.GetFloat( "move_time", "1" ); // safe default value 343 move_speed = spawnArgs.GetFloat( "move_speed", "0" ); 344 345 spawnArgs.GetFloat( "damage" , "0", damage ); 346 347 dest_position = GetPhysics()->GetOrigin(); 348 dest_angles = GetPhysics()->GetAxis().ToAngles(); 349 350 physicsObj.SetSelf( this ); 351 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_MOVER) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 352 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 353 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 354 physicsObj.SetClipMask( MASK_SOLID ); 355 if ( !spawnArgs.GetBool( "solid", "1" ) ) { 356 physicsObj.SetContents( 0 ); 357 } 358 if ( !renderEntity.hModel || !spawnArgs.GetBool( "nopush" ) ) { 359 physicsObj.SetPusher( 0 ); 360 } 361 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin ); 362 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero ); 363 SetPhysics( &physicsObj ); 364 365 // see if we are on an areaportal 366 areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() ); 367 368 if ( spawnArgs.MatchPrefix( "guiTarget" ) ) { 369 if ( gameLocal.GameState() == GAMESTATE_STARTUP ) { 370 PostEventMS( &EV_FindGuiTargets, 0 ); 371 } else { 372 // not during spawn, so it's ok to get the targets 373 FindGuiTargets(); 374 } 375 } 376 377 health = spawnArgs.GetInt( "health" ); 378 if ( health ) { 379 fl.takedamage = true; 380 } 381 382 } 383 384 /* 385 ================ 386 idMover::Hide 387 ================ 388 */ 389 void idMover::Hide() { 390 idEntity::Hide(); 391 physicsObj.SetContents( 0 ); 392 } 393 394 /* 395 ================ 396 idMover::Show 397 ================ 398 */ 399 void idMover::Show() { 400 idEntity::Show(); 401 if ( spawnArgs.GetBool( "solid", "1" ) ) { 402 physicsObj.SetContents( CONTENTS_SOLID ); 403 } 404 SetPhysics( &physicsObj ); 405 } 406 407 /* 408 ============ 409 idMover::Killed 410 ============ 411 */ 412 void idMover::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) { 413 fl.takedamage = false; 414 ActivateTargets( this ); 415 } 416 417 418 /* 419 ================ 420 idMover::Event_SetCallback 421 ================ 422 */ 423 void idMover::Event_SetCallback() { 424 if ( ( lastCommand == MOVER_ROTATING ) && !rotate_thread ) { 425 lastCommand = MOVER_NONE; 426 rotate_thread = idThread::CurrentThreadNum(); 427 idThread::ReturnInt( true ); 428 } else if ( ( lastCommand == MOVER_MOVING || lastCommand == MOVER_SPLINE ) && !move_thread ) { 429 lastCommand = MOVER_NONE; 430 move_thread = idThread::CurrentThreadNum(); 431 idThread::ReturnInt( true ); 432 } else { 433 idThread::ReturnInt( false ); 434 } 435 } 436 437 /* 438 ================ 439 idMover::VectorForDir 440 ================ 441 */ 442 void idMover::VectorForDir( float angle, idVec3 &vec ) { 443 idAngles ang; 444 445 switch( ( int )angle ) { 446 case DIR_UP : 447 vec.Set( 0, 0, 1 ); 448 break; 449 450 case DIR_DOWN : 451 vec.Set( 0, 0, -1 ); 452 break; 453 454 case DIR_LEFT : 455 physicsObj.GetLocalAngles( ang ); 456 ang.pitch = 0; 457 ang.roll = 0; 458 ang.yaw += 90; 459 vec = ang.ToForward(); 460 break; 461 462 case DIR_RIGHT : 463 physicsObj.GetLocalAngles( ang ); 464 ang.pitch = 0; 465 ang.roll = 0; 466 ang.yaw -= 90; 467 vec = ang.ToForward(); 468 break; 469 470 case DIR_FORWARD : 471 physicsObj.GetLocalAngles( ang ); 472 ang.pitch = 0; 473 ang.roll = 0; 474 vec = ang.ToForward(); 475 break; 476 477 case DIR_BACK : 478 physicsObj.GetLocalAngles( ang ); 479 ang.pitch = 0; 480 ang.roll = 0; 481 ang.yaw += 180; 482 vec = ang.ToForward(); 483 break; 484 485 case DIR_REL_UP : 486 vec.Set( 0, 0, 1 ); 487 break; 488 489 case DIR_REL_DOWN : 490 vec.Set( 0, 0, -1 ); 491 break; 492 493 case DIR_REL_LEFT : 494 physicsObj.GetLocalAngles( ang ); 495 ang.ToVectors( NULL, &vec ); 496 vec *= -1; 497 break; 498 499 case DIR_REL_RIGHT : 500 physicsObj.GetLocalAngles( ang ); 501 ang.ToVectors( NULL, &vec ); 502 break; 503 504 case DIR_REL_FORWARD : 505 physicsObj.GetLocalAngles( ang ); 506 vec = ang.ToForward(); 507 break; 508 509 case DIR_REL_BACK : 510 physicsObj.GetLocalAngles( ang ); 511 vec = ang.ToForward() * -1; 512 break; 513 514 default: 515 ang.Set( 0, angle, 0 ); 516 vec = GetWorldVector( ang.ToForward() ); 517 break; 518 } 519 } 520 521 /* 522 ================ 523 idMover::FindGuiTargets 524 ================ 525 */ 526 void idMover::FindGuiTargets() { 527 gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" ); 528 } 529 530 /* 531 ============================== 532 idMover::ClientThink 533 ============================== 534 */ 535 void idMover::ClientThink( const int curTime, const float fraction, const bool predict ) { 536 537 // HACK. because I'm not sure all the other stuff this will screw up. 538 // There was a reason we weren't fully interpolating movers ( Which would evaluate bound objects ). 539 // I just cant remember what it was. 540 541 // Evaluating the Team will update the parts that bound to the entity. 542 // but because we interpolate the master, we don't want to run evaluate on the mover itself. 543 // sending in true to the interpolatePhysicsOnly will run the TeamChain Evaluate, but only on 544 // Objects bound to the entity. 545 if( this->name == "blueshotty_door" || this->name == "redshotty_door" || 546 this->name == "Red_blastshield_mover" || this->name == "Blue_blastshield_mover" ) { 547 InterpolatePhysicsOnly( fraction, true ); 548 } else { 549 InterpolatePhysicsOnly( fraction ); 550 } 551 552 Present(); 553 } 554 555 /* 556 ============================== 557 idMover::SetGuiState 558 559 key/val will be set to any renderEntity->gui's on the list 560 ============================== 561 */ 562 void idMover::SetGuiState( const char *key, const char *val ) const { 563 gameLocal.Printf( "Setting %s to %s\n", key, val ); 564 for( int i = 0; i < guiTargets.Num(); i++ ) { 565 idEntity *ent = guiTargets[ i ].GetEntity(); 566 if ( ent ) { 567 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) { 568 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) { 569 ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val ); 570 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true ); 571 } 572 } 573 ent->UpdateVisuals(); 574 } 575 } 576 } 577 578 /* 579 ================ 580 idMover::Event_InitGuiTargets 581 ================ 582 */ 583 void idMover::Event_FindGuiTargets() { 584 FindGuiTargets(); 585 } 586 587 /* 588 ================ 589 idMover::SetGuiStates 590 ================ 591 */ 592 void idMover::SetGuiStates( const char *state ) { 593 int i; 594 if ( guiTargets.Num() ) { 595 SetGuiState( "movestate", state ); 596 } 597 for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) { 598 if ( renderEntity.gui[ i ] ) { 599 renderEntity.gui[ i ]->SetStateString( "movestate", state ); 600 renderEntity.gui[ i ]->StateChanged( gameLocal.slow.time, true ); 601 } 602 } 603 } 604 605 /* 606 ================ 607 idMover::Event_InitGuiTargets 608 ================ 609 */ 610 void idMover::Event_InitGuiTargets() { 611 SetGuiStates( guiBinaryMoverStates[MOVER_POS1] ); 612 } 613 614 /*********************************************************************** 615 616 Translation control functions 617 618 ***********************************************************************/ 619 620 /* 621 ================ 622 idMover::Event_StopMoving 623 ================ 624 */ 625 void idMover::Event_StopMoving() { 626 physicsObj.GetLocalOrigin( dest_position ); 627 DoneMoving(); 628 } 629 630 /* 631 ================ 632 idMover::DoneMoving 633 ================ 634 */ 635 void idMover::DoneMoving() { 636 637 if ( lastCommand != MOVER_SPLINE ) { 638 // set our final position so that we get rid of any numerical inaccuracy 639 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin ); 640 } 641 642 lastCommand = MOVER_NONE; 643 idThread::ObjectMoveDone( move_thread, this ); 644 move_thread = 0; 645 646 StopSound( SND_CHANNEL_BODY, false ); 647 } 648 649 /* 650 ================ 651 idMover::UpdateMoveSound 652 ================ 653 */ 654 void idMover::UpdateMoveSound( moveStage_t stage ) { 655 switch( stage ) { 656 case ACCELERATION_STAGE: { 657 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL ); 658 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL ); 659 break; 660 } 661 case LINEAR_STAGE: { 662 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL ); 663 break; 664 } 665 case DECELERATION_STAGE: { 666 StopSound( SND_CHANNEL_BODY, false ); 667 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL ); 668 break; 669 } 670 case FINISHED_STAGE: { 671 StopSound( SND_CHANNEL_BODY, false ); 672 break; 673 } 674 } 675 } 676 677 /* 678 ================ 679 idMover::Event_UpdateMove 680 ================ 681 */ 682 void idMover::Event_UpdateMove() { 683 idVec3 org; 684 685 physicsObj.GetLocalOrigin( org ); 686 687 UpdateMoveSound( move.stage ); 688 689 switch( move.stage ) { 690 case ACCELERATION_STAGE: { 691 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, move.acceleration, org, move.dir, vec3_origin ); 692 if ( move.movetime > 0 ) { 693 move.stage = LINEAR_STAGE; 694 } else if ( move.deceleration > 0 ) { 695 move.stage = DECELERATION_STAGE; 696 } else { 697 move.stage = FINISHED_STAGE; 698 } 699 break; 700 } 701 case LINEAR_STAGE: { 702 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, move.movetime, org, move.dir, vec3_origin ); 703 if ( move.deceleration ) { 704 move.stage = DECELERATION_STAGE; 705 } else { 706 move.stage = FINISHED_STAGE; 707 } 708 break; 709 } 710 case DECELERATION_STAGE: { 711 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, move.deceleration, org, move.dir, vec3_origin ); 712 move.stage = FINISHED_STAGE; 713 break; 714 } 715 case FINISHED_STAGE: { 716 if ( g_debugMover.GetBool() ) { 717 gameLocal.Printf( "%d: '%s' move done\n", gameLocal.slow.time, name.c_str() ); 718 } 719 DoneMoving(); 720 break; 721 } 722 } 723 } 724 725 /* 726 ================ 727 idMover::BeginMove 728 ================ 729 */ 730 void idMover::BeginMove( idThread *thread ) { 731 moveStage_t stage; 732 idVec3 org; 733 float dist; 734 float acceldist; 735 int totalacceltime; 736 int at; 737 int dt; 738 739 lastCommand = MOVER_MOVING; 740 move_thread = 0; 741 742 physicsObj.GetLocalOrigin( org ); 743 744 move_delta = dest_position - org; 745 if ( move_delta.Compare( vec3_zero ) ) { 746 DoneMoving(); 747 return; 748 } 749 750 // scale times up to whole physics frames 751 at = idPhysics::SnapTimeToPhysicsFrame( acceltime ); 752 move_time += at - acceltime; 753 acceltime = at; 754 dt = idPhysics::SnapTimeToPhysicsFrame( deceltime ); 755 move_time += dt - deceltime; 756 deceltime = dt; 757 758 // if we're moving at a specific speed, we need to calculate the move time 759 if ( move_speed ) { 760 dist = move_delta.Length(); 761 762 totalacceltime = acceltime + deceltime; 763 764 // calculate the distance we'll move during acceleration and deceleration 765 acceldist = totalacceltime * 0.5f * 0.001f * move_speed; 766 if ( acceldist >= dist ) { 767 // going too slow for this distance to move at a constant speed 768 move_time = totalacceltime; 769 } else { 770 // calculate move time taking acceleration into account 771 move_time = totalacceltime + 1000.0f * ( dist - acceldist ) / move_speed; 772 } 773 } 774 775 // scale time up to a whole physics frames 776 move_time = idPhysics::SnapTimeToPhysicsFrame( move_time ); 777 778 if ( acceltime ) { 779 stage = ACCELERATION_STAGE; 780 } else if ( move_time <= deceltime ) { 781 stage = DECELERATION_STAGE; 782 } else { 783 stage = LINEAR_STAGE; 784 } 785 786 at = acceltime; 787 dt = deceltime; 788 789 if ( at + dt > move_time ) { 790 // there's no real correct way to handle this, so we just scale 791 // the times to fit into the move time in the same proportions 792 at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) ); 793 dt = move_time - at; 794 } 795 796 move_delta = move_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) ); 797 798 move.stage = stage; 799 move.acceleration = at; 800 move.movetime = move_time - at - dt; 801 move.deceleration = dt; 802 move.dir = move_delta; 803 804 ProcessEvent( &EV_ReachedPos ); 805 } 806 807 /*********************************************************************** 808 809 Rotation control functions 810 811 ***********************************************************************/ 812 813 /* 814 ================ 815 idMover::Event_StopRotating 816 ================ 817 */ 818 void idMover::Event_StopRotating() { 819 physicsObj.GetLocalAngles( dest_angles ); 820 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero ); 821 DoneRotating(); 822 } 823 824 /* 825 ================ 826 idMover::DoneRotating 827 ================ 828 */ 829 void idMover::DoneRotating() { 830 lastCommand = MOVER_NONE; 831 idThread::ObjectMoveDone( rotate_thread, this ); 832 rotate_thread = 0; 833 834 StopSound( SND_CHANNEL_BODY, false ); 835 } 836 837 /* 838 ================ 839 idMover::UpdateRotationSound 840 ================ 841 */ 842 void idMover::UpdateRotationSound( moveStage_t stage ) { 843 switch( stage ) { 844 case ACCELERATION_STAGE: { 845 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL ); 846 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL ); 847 break; 848 } 849 case LINEAR_STAGE: { 850 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL ); 851 break; 852 } 853 case DECELERATION_STAGE: { 854 StopSound( SND_CHANNEL_BODY, false ); 855 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL ); 856 break; 857 } 858 case FINISHED_STAGE: { 859 StopSound( SND_CHANNEL_BODY, false ); 860 break; 861 } 862 } 863 } 864 865 /* 866 ================ 867 idMover::Event_UpdateRotation 868 ================ 869 */ 870 void idMover::Event_UpdateRotation() { 871 idAngles ang; 872 873 physicsObj.GetLocalAngles( ang ); 874 875 UpdateRotationSound( rot.stage ); 876 877 switch( rot.stage ) { 878 case ACCELERATION_STAGE: { 879 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, rot.acceleration, ang, rot.rot, ang_zero ); 880 if ( rot.movetime > 0 ) { 881 rot.stage = LINEAR_STAGE; 882 } else if ( rot.deceleration > 0 ) { 883 rot.stage = DECELERATION_STAGE; 884 } else { 885 rot.stage = FINISHED_STAGE; 886 } 887 break; 888 } 889 case LINEAR_STAGE: { 890 if ( !stopRotation && !rot.deceleration ) { 891 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, rot.movetime, ang, rot.rot, ang_zero ); 892 } else { 893 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, rot.movetime, ang, rot.rot, ang_zero ); 894 } 895 896 if ( rot.deceleration ) { 897 rot.stage = DECELERATION_STAGE; 898 } else { 899 rot.stage = FINISHED_STAGE; 900 } 901 break; 902 } 903 case DECELERATION_STAGE: { 904 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, rot.deceleration, ang, rot.rot, ang_zero ); 905 rot.stage = FINISHED_STAGE; 906 break; 907 } 908 case FINISHED_STAGE: { 909 lastCommand = MOVER_NONE; 910 if ( stopRotation ) { 911 // set our final angles so that we get rid of any numerical inaccuracy 912 dest_angles.Normalize360(); 913 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero ); 914 stopRotation = false; 915 } else if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_ACCELLINEAR ) { 916 // keep our angular velocity constant 917 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, ang, rot.rot, ang_zero ); 918 } 919 920 if ( g_debugMover.GetBool() ) { 921 gameLocal.Printf( "%d: '%s' rotation done\n", gameLocal.slow.time, name.c_str() ); 922 } 923 924 DoneRotating(); 925 break; 926 } 927 } 928 } 929 930 /* 931 ================ 932 idMover::BeginRotation 933 ================ 934 */ 935 void idMover::BeginRotation( idThread *thread, bool stopwhendone ) { 936 moveStage_t stage; 937 idAngles ang; 938 int at; 939 int dt; 940 941 lastCommand = MOVER_ROTATING; 942 rotate_thread = 0; 943 944 // rotation always uses move_time so that if a move was started before the rotation, 945 // the rotation will take the same amount of time as the move. If no move has been 946 // started and no time is set, the rotation takes 1 second. 947 if ( !move_time ) { 948 move_time = 1; 949 } 950 951 physicsObj.GetLocalAngles( ang ); 952 angle_delta = dest_angles - ang; 953 if ( angle_delta == ang_zero ) { 954 // set our final angles so that we get rid of any numerical inaccuracy 955 dest_angles.Normalize360(); 956 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero ); 957 stopRotation = false; 958 DoneRotating(); 959 return; 960 } 961 962 // scale times up to whole physics frames 963 at = idPhysics::SnapTimeToPhysicsFrame( acceltime ); 964 move_time += at - acceltime; 965 acceltime = at; 966 dt = idPhysics::SnapTimeToPhysicsFrame( deceltime ); 967 move_time += dt - deceltime; 968 deceltime = dt; 969 move_time = idPhysics::SnapTimeToPhysicsFrame( move_time ); 970 971 if ( acceltime ) { 972 stage = ACCELERATION_STAGE; 973 } else if ( move_time <= deceltime ) { 974 stage = DECELERATION_STAGE; 975 } else { 976 stage = LINEAR_STAGE; 977 } 978 979 at = acceltime; 980 dt = deceltime; 981 982 if ( at + dt > move_time ) { 983 // there's no real correct way to handle this, so we just scale 984 // the times to fit into the move time in the same proportions 985 at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) ); 986 dt = move_time - at; 987 } 988 989 angle_delta = angle_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) ); 990 991 stopRotation = stopwhendone || ( dt != 0 ); 992 993 rot.stage = stage; 994 rot.acceleration = at; 995 rot.movetime = move_time - at - dt; 996 rot.deceleration = dt; 997 rot.rot = angle_delta; 998 999 ProcessEvent( &EV_ReachedAng ); 1000 } 1001 1002 1003 /*********************************************************************** 1004 1005 Script callable routines 1006 1007 ***********************************************************************/ 1008 1009 /* 1010 =============== 1011 idMover::Event_TeamBlocked 1012 =============== 1013 */ 1014 void idMover::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) { 1015 if ( g_debugMover.GetBool() ) { 1016 gameLocal.Printf( "%d: '%s' stopped due to team member '%s' blocked by '%s'\n", gameLocal.slow.time, name.c_str(), blockedEntity->name.c_str(), blockingEntity->name.c_str() ); 1017 } 1018 } 1019 1020 /* 1021 =============== 1022 idMover::Event_PartBlocked 1023 =============== 1024 */ 1025 void idMover::Event_PartBlocked( idEntity *blockingEntity ) { 1026 if ( damage > 0.0f ) { 1027 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); 1028 } 1029 if ( g_debugMover.GetBool() ) { 1030 gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.slow.time, name.c_str(), blockingEntity->name.c_str() ); 1031 } 1032 } 1033 1034 /* 1035 ================ 1036 idMover::Event_SetMoveSpeed 1037 ================ 1038 */ 1039 void idMover::Event_SetMoveSpeed( float speed ) { 1040 if ( speed <= 0 ) { 1041 gameLocal.Error( "Cannot set speed less than or equal to 0." ); 1042 } 1043 1044 move_speed = speed; 1045 move_time = 0; // move_time is calculated for each move when move_speed is non-0 1046 } 1047 1048 /* 1049 ================ 1050 idMover::Event_SetMoveTime 1051 ================ 1052 */ 1053 void idMover::Event_SetMoveTime( float time ) { 1054 if ( time <= 0 ) { 1055 gameLocal.Error( "Cannot set time less than or equal to 0." ); 1056 } 1057 1058 move_speed = 0; 1059 move_time = SEC2MS( time ); 1060 } 1061 1062 /* 1063 ================ 1064 idMover::Event_SetAccellerationTime 1065 ================ 1066 */ 1067 void idMover::Event_SetAccellerationTime( float time ) { 1068 if ( time < 0 ) { 1069 gameLocal.Error( "Cannot set acceleration time less than 0." ); 1070 } 1071 1072 acceltime = SEC2MS( time ); 1073 } 1074 1075 /* 1076 ================ 1077 idMover::Event_SetDecelerationTime 1078 ================ 1079 */ 1080 void idMover::Event_SetDecelerationTime( float time ) { 1081 if ( time < 0 ) { 1082 gameLocal.Error( "Cannot set deceleration time less than 0." ); 1083 } 1084 1085 deceltime = SEC2MS( time ); 1086 } 1087 1088 /* 1089 ================ 1090 idMover::Event_MoveTo 1091 ================ 1092 */ 1093 void idMover::Event_MoveTo( idEntity *ent ) { 1094 if ( ent == NULL ) { 1095 gameLocal.Warning( "Entity not found" ); 1096 return; 1097 } 1098 1099 dest_position = GetLocalCoordinates( ent->GetPhysics()->GetOrigin() ); 1100 BeginMove( idThread::CurrentThread() ); 1101 } 1102 1103 /* 1104 ================ 1105 idMover::MoveToPos 1106 ================ 1107 */ 1108 void idMover::MoveToPos( const idVec3 &pos ) { 1109 dest_position = GetLocalCoordinates( pos ); 1110 BeginMove( NULL ); 1111 } 1112 1113 /* 1114 ================ 1115 idMover::Event_MoveToPos 1116 ================ 1117 */ 1118 void idMover::Event_MoveToPos( idVec3 &pos ) { 1119 MoveToPos( pos ); 1120 } 1121 1122 /* 1123 ================ 1124 idMover::Event_MoveDir 1125 ================ 1126 */ 1127 void idMover::Event_MoveDir( float angle, float distance ) { 1128 idVec3 dir; 1129 idVec3 org; 1130 1131 physicsObj.GetLocalOrigin( org ); 1132 VectorForDir( angle, dir ); 1133 dest_position = org + dir * distance; 1134 1135 BeginMove( idThread::CurrentThread() ); 1136 } 1137 1138 /* 1139 ================ 1140 idMover::Event_MoveAccelerateTo 1141 ================ 1142 */ 1143 void idMover::Event_MoveAccelerateTo( float speed, float time ) { 1144 float v; 1145 idVec3 org, dir; 1146 int at; 1147 1148 if ( time < 0 ) { 1149 gameLocal.Error( "idMover::Event_MoveAccelerateTo: cannot set acceleration time less than 0." ); 1150 } 1151 1152 dir = physicsObj.GetLinearVelocity(); 1153 v = dir.Normalize(); 1154 1155 // if not moving already 1156 if ( v == 0.0f ) { 1157 gameLocal.Error( "idMover::Event_MoveAccelerateTo: not moving." ); 1158 } 1159 1160 // if already moving faster than the desired speed 1161 if ( v >= speed ) { 1162 return; 1163 } 1164 1165 at = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) ); 1166 1167 lastCommand = MOVER_MOVING; 1168 1169 physicsObj.GetLocalOrigin( org ); 1170 1171 move.stage = ACCELERATION_STAGE; 1172 move.acceleration = at; 1173 move.movetime = 0; 1174 move.deceleration = 0; 1175 1176 StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL ); 1177 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL ); 1178 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, move.acceleration, org, dir * ( speed - v ), dir * v ); 1179 } 1180 1181 /* 1182 ================ 1183 idMover::Event_MoveDecelerateTo 1184 ================ 1185 */ 1186 void idMover::Event_MoveDecelerateTo( float speed, float time ) { 1187 float v; 1188 idVec3 org, dir; 1189 int dt; 1190 1191 if ( time < 0 ) { 1192 gameLocal.Error( "idMover::Event_MoveDecelerateTo: cannot set deceleration time less than 0." ); 1193 } 1194 1195 dir = physicsObj.GetLinearVelocity(); 1196 v = dir.Normalize(); 1197 1198 // if not moving already 1199 if ( v == 0.0f ) { 1200 gameLocal.Error( "idMover::Event_MoveDecelerateTo: not moving." ); 1201 } 1202 1203 // if already moving slower than the desired speed 1204 if ( v <= speed ) { 1205 return; 1206 } 1207 1208 dt = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) ); 1209 1210 lastCommand = MOVER_MOVING; 1211 1212 physicsObj.GetLocalOrigin( org ); 1213 1214 move.stage = DECELERATION_STAGE; 1215 move.acceleration = 0; 1216 move.movetime = 0; 1217 move.deceleration = dt; 1218 1219 StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL ); 1220 StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL ); 1221 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, move.deceleration, org, dir * ( v - speed ), dir * speed ); 1222 } 1223 1224 /* 1225 ================ 1226 idMover::Event_RotateDownTo 1227 ================ 1228 */ 1229 void idMover::Event_RotateDownTo( int axis, float angle ) { 1230 idAngles ang; 1231 1232 if ( ( axis < 0 ) || ( axis > 2 ) ) { 1233 gameLocal.Error( "Invalid axis" ); 1234 } 1235 1236 physicsObj.GetLocalAngles( ang ); 1237 1238 dest_angles[ axis ] = angle; 1239 if ( dest_angles[ axis ] > ang[ axis ] ) { 1240 dest_angles[ axis ] -= 360; 1241 } 1242 1243 BeginRotation( idThread::CurrentThread(), true ); 1244 } 1245 1246 /* 1247 ================ 1248 idMover::Event_RotateUpTo 1249 ================ 1250 */ 1251 void idMover::Event_RotateUpTo( int axis, float angle ) { 1252 idAngles ang; 1253 1254 if ( ( axis < 0 ) || ( axis > 2 ) ) { 1255 gameLocal.Error( "Invalid axis" ); 1256 } 1257 1258 physicsObj.GetLocalAngles( ang ); 1259 1260 dest_angles[ axis ] = angle; 1261 if ( dest_angles[ axis ] < ang[ axis ] ) { 1262 dest_angles[ axis ] += 360; 1263 } 1264 1265 BeginRotation( idThread::CurrentThread(), true ); 1266 } 1267 1268 /* 1269 ================ 1270 idMover::Event_RotateTo 1271 ================ 1272 */ 1273 void idMover::Event_RotateTo( idAngles &angles ) { 1274 dest_angles = angles; 1275 BeginRotation( idThread::CurrentThread(), true ); 1276 } 1277 1278 /* 1279 ================ 1280 idMover::Event_Rotate 1281 ================ 1282 */ 1283 void idMover::Event_Rotate( idAngles &angles ) { 1284 idAngles ang; 1285 1286 if ( rotate_thread ) { 1287 DoneRotating(); 1288 } 1289 1290 physicsObj.GetLocalAngles( ang ); 1291 dest_angles = ang + angles * ( move_time - ( acceltime + deceltime ) / 2 ) * 0.001f; 1292 1293 BeginRotation( idThread::CurrentThread(), false ); 1294 } 1295 1296 /* 1297 ================ 1298 idMover::Event_RotateOnce 1299 ================ 1300 */ 1301 void idMover::Event_RotateOnce( idAngles &angles ) { 1302 idAngles ang; 1303 1304 if ( rotate_thread ) { 1305 DoneRotating(); 1306 } 1307 1308 physicsObj.GetLocalAngles( ang ); 1309 dest_angles = ang + angles; 1310 1311 BeginRotation( idThread::CurrentThread(), true ); 1312 } 1313 1314 /* 1315 ================ 1316 idMover::Event_Bob 1317 ================ 1318 */ 1319 void idMover::Event_Bob( float speed, float phase, idVec3 &depth ) { 1320 idVec3 org; 1321 1322 physicsObj.GetLocalOrigin( org ); 1323 physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), speed * 1000 * phase, speed * 500, org, depth * 2.0f, vec3_origin ); 1324 } 1325 1326 /* 1327 ================ 1328 idMover::Event_Sway 1329 ================ 1330 */ 1331 void idMover::Event_Sway( float speed, float phase, idAngles &depth ) { 1332 idAngles ang, angSpeed; 1333 float duration; 1334 1335 physicsObj.GetLocalAngles( ang ); 1336 assert ( speed > 0.0f ); 1337 duration = idMath::Sqrt( depth[0] * depth[0] + depth[1] * depth[1] + depth[2] * depth[2] ) / speed; 1338 angSpeed = depth / ( duration * idMath::SQRT_1OVER2 ); 1339 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), duration * 1000.0f * phase, duration * 1000.0f, ang, angSpeed, ang_zero ); 1340 } 1341 1342 /* 1343 ================ 1344 idMover::Event_OpenPortal 1345 1346 Sets the portal associtated with this mover to be open 1347 ================ 1348 */ 1349 void idMover::Event_OpenPortal() { 1350 if ( areaPortal ) { 1351 SetPortalState( true ); 1352 } 1353 } 1354 1355 /* 1356 ================ 1357 idMover::Event_ClosePortal 1358 1359 Sets the portal associtated with this mover to be closed 1360 ================ 1361 */ 1362 void idMover::Event_ClosePortal() { 1363 if ( areaPortal ) { 1364 SetPortalState( false ); 1365 } 1366 } 1367 1368 /* 1369 ================ 1370 idMover::Event_SetAccelSound 1371 ================ 1372 */ 1373 void idMover::Event_SetAccelSound( const char *sound ) { 1374 // refSound.SetSound( "accel", sound ); 1375 } 1376 1377 /* 1378 ================ 1379 idMover::Event_SetDecelSound 1380 ================ 1381 */ 1382 void idMover::Event_SetDecelSound( const char *sound ) { 1383 // refSound.SetSound( "decel", sound ); 1384 } 1385 1386 /* 1387 ================ 1388 idMover::Event_SetMoveSound 1389 ================ 1390 */ 1391 void idMover::Event_SetMoveSound( const char *sound ) { 1392 // refSound.SetSound( "move", sound ); 1393 } 1394 1395 /* 1396 ================ 1397 idMover::Event_EnableSplineAngles 1398 ================ 1399 */ 1400 void idMover::Event_EnableSplineAngles() { 1401 useSplineAngles = true; 1402 } 1403 1404 /* 1405 ================ 1406 idMover::Event_DisableSplineAngles 1407 ================ 1408 */ 1409 void idMover::Event_DisableSplineAngles() { 1410 useSplineAngles = false; 1411 } 1412 1413 /* 1414 ================ 1415 idMover::Event_RemoveInitialSplineAngles 1416 ================ 1417 */ 1418 void idMover::Event_RemoveInitialSplineAngles() { 1419 idCurve_Spline<idVec3> *spline; 1420 idAngles ang; 1421 1422 spline = physicsObj.GetSpline(); 1423 if ( !spline ) { 1424 return; 1425 } 1426 ang = spline->GetCurrentFirstDerivative( 0 ).ToAngles(); 1427 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, -ang, ang_zero, ang_zero ); 1428 } 1429 1430 /* 1431 ================ 1432 idMover::Event_StartSpline 1433 ================ 1434 */ 1435 void idMover::Event_StartSpline( idEntity *splineEntity ) { 1436 idCurve_Spline<idVec3> *spline; 1437 1438 if ( !splineEntity ) { 1439 return; 1440 } 1441 1442 // Needed for savegames 1443 splineEnt = splineEntity; 1444 1445 spline = splineEntity->GetSpline(); 1446 if ( !spline ) { 1447 return; 1448 } 1449 1450 lastCommand = MOVER_SPLINE; 1451 move_thread = 0; 1452 1453 if ( acceltime + deceltime > move_time ) { 1454 acceltime = move_time / 2; 1455 deceltime = move_time - acceltime; 1456 } 1457 move.stage = FINISHED_STAGE; 1458 move.acceleration = acceltime; 1459 move.movetime = move_time; 1460 move.deceleration = deceltime; 1461 1462 spline->MakeUniform( move_time ); 1463 spline->ShiftTime( gameLocal.slow.time - spline->GetTime( 0 ) ); 1464 1465 physicsObj.SetSpline( spline, move.acceleration, move.deceleration, useSplineAngles ); 1466 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin ); 1467 } 1468 1469 /* 1470 ================ 1471 idMover::Event_StopSpline 1472 ================ 1473 */ 1474 void idMover::Event_StopSpline() { 1475 physicsObj.SetSpline( NULL, 0, 0, useSplineAngles ); 1476 splineEnt = NULL; 1477 } 1478 1479 /* 1480 ================ 1481 idMover::Event_Activate 1482 ================ 1483 */ 1484 void idMover::Event_Activate( idEntity *activator ) { 1485 Show(); 1486 Event_StartSpline( this ); 1487 } 1488 1489 /* 1490 ================ 1491 idMover::Event_IsMoving 1492 ================ 1493 */ 1494 void idMover::Event_IsMoving() { 1495 if ( physicsObj.GetLinearExtrapolationType() == EXTRAPOLATION_NONE ) { 1496 idThread::ReturnInt( false ); 1497 } else { 1498 idThread::ReturnInt( true ); 1499 } 1500 } 1501 1502 /* 1503 ================ 1504 idMover::Event_IsRotating 1505 ================ 1506 */ 1507 void idMover::Event_IsRotating() { 1508 if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_NONE ) { 1509 idThread::ReturnInt( false ); 1510 } else { 1511 idThread::ReturnInt( true ); 1512 } 1513 } 1514 1515 /* 1516 ================ 1517 idMover::WriteToSnapshot 1518 ================ 1519 */ 1520 void idMover::WriteToSnapshot( idBitMsg &msg ) const { 1521 physicsObj.WriteToSnapshot( msg ); 1522 msg.WriteBits( move.stage, 3 ); 1523 msg.WriteBits( rot.stage, 3 ); 1524 WriteBindToSnapshot( msg ); 1525 WriteGUIToSnapshot( msg ); 1526 } 1527 1528 /* 1529 ================ 1530 idMover::ReadFromSnapshot 1531 ================ 1532 */ 1533 void idMover::ReadFromSnapshot( const idBitMsg &msg ) { 1534 moveStage_t oldMoveStage = move.stage; 1535 moveStage_t oldRotStage = rot.stage; 1536 1537 physicsObj.ReadFromSnapshot( msg ); 1538 move.stage = (moveStage_t) msg.ReadBits( 3 ); 1539 rot.stage = (moveStage_t) msg.ReadBits( 3 ); 1540 ReadBindFromSnapshot( msg ); 1541 ReadGUIFromSnapshot( msg ); 1542 1543 if ( msg.HasChanged() ) { 1544 if ( move.stage != oldMoveStage ) { 1545 UpdateMoveSound( oldMoveStage ); 1546 } 1547 if ( rot.stage != oldRotStage ) { 1548 UpdateRotationSound( oldRotStage ); 1549 } 1550 UpdateVisuals(); 1551 } 1552 } 1553 1554 /* 1555 ================ 1556 idMover::SetPortalState 1557 ================ 1558 */ 1559 void idMover::SetPortalState( bool open ) { 1560 assert( areaPortal ); 1561 gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL ); 1562 } 1563 1564 /* 1565 =============================================================================== 1566 1567 idSplinePath, holds a spline path to be used by an idMover 1568 1569 =============================================================================== 1570 */ 1571 1572 CLASS_DECLARATION( idEntity, idSplinePath ) 1573 END_CLASS 1574 1575 /* 1576 ================ 1577 idSplinePath::idSplinePath 1578 ================ 1579 */ 1580 idSplinePath::idSplinePath() { 1581 } 1582 1583 /* 1584 ================ 1585 idSplinePath::Spawn 1586 ================ 1587 */ 1588 void idSplinePath::Spawn() { 1589 } 1590 1591 1592 /* 1593 =============================================================================== 1594 1595 idElevator 1596 1597 =============================================================================== 1598 */ 1599 const idEventDef EV_PostArrival( "postArrival", NULL ); 1600 const idEventDef EV_GotoFloor( "gotoFloor", "d" ); 1601 const idEventDef EV_SetGuiStates( "setGuiStates" ); 1602 1603 CLASS_DECLARATION( idMover, idElevator ) 1604 EVENT( EV_Activate, idElevator::Event_Activate ) 1605 EVENT( EV_TeamBlocked, idElevator::Event_TeamBlocked ) 1606 EVENT( EV_PartBlocked, idElevator::Event_PartBlocked ) 1607 EVENT( EV_PostArrival, idElevator::Event_PostFloorArrival ) 1608 EVENT( EV_GotoFloor, idElevator::Event_GotoFloor ) 1609 EVENT( EV_Touch, idElevator::Event_Touch ) 1610 EVENT( EV_SetGuiStates, idElevator::Event_SetGuiStates ) 1611 END_CLASS 1612 1613 /* 1614 ================ 1615 idElevator::idElevator 1616 ================ 1617 */ 1618 idElevator::idElevator() { 1619 state = INIT; 1620 floorInfo.Clear(); 1621 currentFloor = 0; 1622 pendingFloor = 0; 1623 lastFloor = 0; 1624 controlsDisabled = false; 1625 lastTouchTime = 0; 1626 returnFloor = 0; 1627 returnTime = 0; 1628 } 1629 1630 /* 1631 ================ 1632 idElevator::Save 1633 ================ 1634 */ 1635 void idElevator::Save( idSaveGame *savefile ) const { 1636 int i; 1637 1638 savefile->WriteInt( (int)state ); 1639 1640 savefile->WriteInt( floorInfo.Num() ); 1641 for ( i = 0; i < floorInfo.Num(); i++ ) { 1642 savefile->WriteVec3( floorInfo[ i ].pos ); 1643 savefile->WriteString( floorInfo[ i ].door ); 1644 savefile->WriteInt( floorInfo[ i ].floor ); 1645 } 1646 1647 savefile->WriteInt( currentFloor ); 1648 savefile->WriteInt( pendingFloor ); 1649 savefile->WriteInt( lastFloor ); 1650 savefile->WriteBool( controlsDisabled ); 1651 savefile->WriteFloat( returnTime ); 1652 savefile->WriteInt( returnFloor ); 1653 savefile->WriteInt( lastTouchTime ); 1654 } 1655 1656 /* 1657 ================ 1658 idElevator::Restore 1659 ================ 1660 */ 1661 void idElevator::Restore( idRestoreGame *savefile ) { 1662 int i, num; 1663 1664 savefile->ReadInt( (int &)state ); 1665 1666 savefile->ReadInt( num ); 1667 for ( i = 0; i < num; i++ ) { 1668 floorInfo_s floor; 1669 1670 savefile->ReadVec3( floor.pos ); 1671 savefile->ReadString( floor.door ); 1672 savefile->ReadInt( floor.floor ); 1673 1674 floorInfo.Append( floor ); 1675 } 1676 1677 savefile->ReadInt( currentFloor ); 1678 savefile->ReadInt( pendingFloor ); 1679 savefile->ReadInt( lastFloor ); 1680 savefile->ReadBool( controlsDisabled ); 1681 savefile->ReadFloat( returnTime ); 1682 savefile->ReadInt( returnFloor ); 1683 savefile->ReadInt( lastTouchTime ); 1684 } 1685 1686 /* 1687 ================ 1688 idElevator::Spawn 1689 ================ 1690 */ 1691 void idElevator::Spawn() { 1692 idStr str; 1693 int len1; 1694 1695 lastFloor = 0; 1696 currentFloor = 0; 1697 pendingFloor = spawnArgs.GetInt( "floor", "1" ); 1698 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1]); 1699 1700 returnTime = spawnArgs.GetFloat( "returnTime" ); 1701 returnFloor = spawnArgs.GetInt( "returnFloor" ); 1702 1703 len1 = strlen( "floorPos_" ); 1704 const idKeyValue *kv = spawnArgs.MatchPrefix( "floorPos_", NULL ); 1705 while( kv ) { 1706 str = kv->GetKey().Right( kv->GetKey().Length() - len1 ); 1707 floorInfo_s fi; 1708 fi.floor = atoi( str ); 1709 fi.door = spawnArgs.GetString( va( "floorDoor_%i", fi.floor ) ); 1710 fi.pos = spawnArgs.GetVector( kv->GetKey() ); 1711 floorInfo.Append( fi ); 1712 kv = spawnArgs.MatchPrefix( "floorPos_", kv ); 1713 } 1714 lastTouchTime = 0; 1715 state = INIT; 1716 BecomeActive( TH_THINK | TH_PHYSICS ); 1717 PostEventMS( &EV_Mover_InitGuiTargets, 0 ); 1718 controlsDisabled = false; 1719 } 1720 1721 /* 1722 ============== 1723 idElevator::Event_Touch 1724 =============== 1725 */ 1726 void idElevator::Event_Touch( idEntity *other, trace_t *trace ) { 1727 1728 if ( common->IsClient() ) { 1729 return; 1730 } 1731 1732 if ( gameLocal.slow.time < lastTouchTime + 2000 ) { 1733 return; 1734 } 1735 1736 if ( !other->IsType( idPlayer::Type ) ) { 1737 return; 1738 } 1739 1740 lastTouchTime = gameLocal.slow.time; 1741 1742 if ( thinkFlags & TH_PHYSICS ) { 1743 return; 1744 } 1745 1746 int triggerFloor = spawnArgs.GetInt( "triggerFloor" ); 1747 if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) { 1748 PostEventSec( &EV_GotoFloor, 0.25f, triggerFloor ); 1749 } 1750 } 1751 1752 /* 1753 ================ 1754 idElevator::Think 1755 ================ 1756 */ 1757 void idElevator::Think() { 1758 idVec3 masterOrigin; 1759 idMat3 masterAxis; 1760 idDoor *doorent = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 1761 if ( state == INIT ) { 1762 state = IDLE; 1763 if ( doorent ) { 1764 doorent->BindTeam( this ); 1765 doorent->spawnArgs.Set( "snd_open", "" ); 1766 doorent->spawnArgs.Set( "snd_close", "" ); 1767 doorent->spawnArgs.Set( "snd_opened", "" ); 1768 } 1769 for ( int i = 0; i < floorInfo.Num(); i++ ) { 1770 idDoor *door = GetDoor( floorInfo[i].door ); 1771 if ( door ) { 1772 door->SetCompanion( doorent ); 1773 } 1774 } 1775 1776 Event_GotoFloor( pendingFloor ); 1777 DisableAllDoors(); 1778 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] ); 1779 } else if ( state == WAITING_ON_DOORS ) { 1780 state = IDLE; 1781 if ( doorent != NULL && doorent->IsOpen() ) { 1782 state = WAITING_ON_DOORS; 1783 } else { 1784 for ( int i = 0; i < floorInfo.Num(); i++ ) { 1785 idDoor *door = GetDoor( floorInfo[i].door ); 1786 if ( door != NULL && door->IsOpen() ) { 1787 state = WAITING_ON_DOORS; 1788 break; 1789 } 1790 } 1791 } 1792 if ( state == IDLE ) { 1793 lastFloor = currentFloor; 1794 currentFloor = pendingFloor; 1795 floorInfo_s *fi = GetFloorInfo( currentFloor ); 1796 if ( fi ) { 1797 MoveToPos( fi->pos ); 1798 } 1799 } 1800 } 1801 RunPhysics(); 1802 Present(); 1803 } 1804 1805 /* 1806 ================ 1807 idElevator::Event_Activate 1808 ================ 1809 */ 1810 void idElevator::Event_Activate( idEntity *activator ) { 1811 int triggerFloor = spawnArgs.GetInt( "triggerFloor" ); 1812 if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) { 1813 Event_GotoFloor( triggerFloor ); 1814 } 1815 } 1816 1817 /* 1818 ================ 1819 idElevator::Event_TeamBlocked 1820 ================ 1821 */ 1822 void idElevator::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) { 1823 if ( blockedEntity == this ) { 1824 Event_GotoFloor( lastFloor ); 1825 } else if ( blockedEntity && blockedEntity->IsType( idDoor::Type ) ) { 1826 // open the inner doors if one is blocked 1827 idDoor *blocked = static_cast<idDoor *>( blockedEntity ); 1828 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 1829 if ( door != NULL && blocked->GetMoveMaster() == door->GetMoveMaster() ) { 1830 door->SetBlocked(true); 1831 OpenInnerDoor(); 1832 OpenFloorDoor( currentFloor ); 1833 } 1834 } 1835 } 1836 1837 /* 1838 =============== 1839 idElevator::HandleSingleGuiCommand 1840 =============== 1841 */ 1842 bool idElevator::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) { 1843 idToken token; 1844 1845 if ( controlsDisabled ) { 1846 return false; 1847 } 1848 1849 if ( !src->ReadToken( &token ) ) { 1850 return false; 1851 } 1852 1853 if ( token == ";" ) { 1854 return false; 1855 } 1856 1857 if ( token.Icmp( "changefloor" ) == 0 ) { 1858 if ( src->ReadToken( &token ) ) { 1859 int newFloor = atoi( token ); 1860 if ( newFloor == currentFloor ) { 1861 // open currentFloor and interior doors 1862 OpenInnerDoor(); 1863 OpenFloorDoor( currentFloor ); 1864 } else { 1865 ProcessEvent( &EV_GotoFloor, newFloor ); 1866 } 1867 return true; 1868 } 1869 } 1870 1871 src->UnreadToken( &token ); 1872 return false; 1873 } 1874 1875 /* 1876 ================ 1877 idElevator::OpenFloorDoor 1878 ================ 1879 */ 1880 void idElevator::OpenFloorDoor( int floor ) { 1881 floorInfo_s *fi = GetFloorInfo( floor ); 1882 if ( fi ) { 1883 idDoor *door = GetDoor( fi->door ); 1884 if ( door ) { 1885 door->Open(); 1886 } 1887 } 1888 } 1889 1890 /* 1891 ================ 1892 idElevator::OpenInnerDoor 1893 ================ 1894 */ 1895 void idElevator::OpenInnerDoor() { 1896 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 1897 if ( door ) { 1898 door->Open(); 1899 } 1900 } 1901 1902 /* 1903 ================ 1904 idElevator::GetFloorInfo 1905 ================ 1906 */ 1907 floorInfo_s *idElevator::GetFloorInfo( int floor ) { 1908 for ( int i = 0; i < floorInfo.Num(); i++ ) { 1909 if ( floorInfo[i].floor == floor ) { 1910 return &floorInfo[i]; 1911 } 1912 } 1913 return NULL; 1914 } 1915 1916 /* 1917 ================ 1918 idElevator::Event_GotoFloor 1919 ================ 1920 */ 1921 void idElevator::Event_GotoFloor( int floor ) { 1922 floorInfo_s *fi = GetFloorInfo( floor ); 1923 if ( fi ) { 1924 DisableAllDoors(); 1925 CloseAllDoors(); 1926 state = WAITING_ON_DOORS; 1927 pendingFloor = floor; 1928 } 1929 // If the inner door is blocked, repost this event 1930 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 1931 if ( door ) { 1932 if ( door->IsBlocked() || door->IsOpen() ) { 1933 PostEventSec( &EV_GotoFloor, 0.5f, floor ); 1934 } 1935 } 1936 } 1937 1938 /* 1939 ================ 1940 idElevator::BeginMove 1941 ================ 1942 */ 1943 void idElevator::BeginMove( idThread *thread ) { 1944 controlsDisabled = true; 1945 CloseAllDoors(); 1946 DisableAllDoors(); 1947 const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" ); 1948 while( kv ) { 1949 idEntity *ent = gameLocal.FindEntity( kv->GetValue() ); 1950 if ( ent ) { 1951 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) { 1952 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) { 1953 ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", "" ); 1954 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true ); 1955 } 1956 } 1957 ent->UpdateVisuals(); 1958 } 1959 kv = spawnArgs.MatchPrefix( "statusGui", kv ); 1960 } 1961 SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[3] : guiBinaryMoverStates[2] ); 1962 idMover::BeginMove( thread ); 1963 } 1964 1965 /* 1966 ================ 1967 idElevator::GetDoor 1968 ================ 1969 */ 1970 idDoor *idElevator::GetDoor( const char *name ) { 1971 idEntity *ent; 1972 idEntity *master; 1973 idDoor *doorEnt; 1974 1975 doorEnt = NULL; 1976 if ( name && *name ) { 1977 ent = gameLocal.FindEntity( name ); 1978 if ( ent && ent->IsType( idDoor::Type ) ) { 1979 doorEnt = static_cast<idDoor*>( ent ); 1980 master = doorEnt->GetMoveMaster(); 1981 if ( master != doorEnt ) { 1982 if ( master->IsType( idDoor::Type ) ) { 1983 doorEnt = static_cast<idDoor*>( master ); 1984 } else { 1985 doorEnt = NULL; 1986 } 1987 } 1988 } 1989 } 1990 1991 return doorEnt; 1992 } 1993 1994 /* 1995 ================ 1996 idElevator::Event_PostFloorArrival 1997 ================ 1998 */ 1999 void idElevator::Event_PostFloorArrival() { 2000 OpenFloorDoor( currentFloor ); 2001 OpenInnerDoor(); 2002 SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] ); 2003 controlsDisabled = false; 2004 if ( returnTime > 0.0f && returnFloor != currentFloor ) { 2005 PostEventSec( &EV_GotoFloor, returnTime, returnFloor ); 2006 } 2007 } 2008 2009 void idElevator::Event_SetGuiStates() { 2010 SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] ); 2011 } 2012 2013 /* 2014 ================ 2015 idElevator::DoneMoving 2016 ================ 2017 */ 2018 void idElevator::DoneMoving() { 2019 idMover::DoneMoving(); 2020 EnableProperDoors(); 2021 const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" ); 2022 while( kv ) { 2023 idEntity *ent = gameLocal.FindEntity( kv->GetValue() ); 2024 if ( ent ) { 2025 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) { 2026 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) { 2027 ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", va( "%i", currentFloor ) ); 2028 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true ); 2029 } 2030 } 2031 ent->UpdateVisuals(); 2032 } 2033 kv = spawnArgs.MatchPrefix( "statusGui", kv ); 2034 } 2035 if ( spawnArgs.GetInt( "pauseOnFloor", "-1" ) == currentFloor ) { 2036 PostEventSec( &EV_PostArrival, spawnArgs.GetFloat( "pauseTime" ) ); 2037 } else { 2038 Event_PostFloorArrival(); 2039 } 2040 } 2041 2042 /* 2043 ================ 2044 idElevator::CloseAllDoors 2045 ================ 2046 */ 2047 void idElevator::CloseAllDoors() { 2048 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 2049 if ( door ) { 2050 door->Close(); 2051 } 2052 for ( int i = 0; i < floorInfo.Num(); i++ ) { 2053 door = GetDoor( floorInfo[i].door ); 2054 if ( door ) { 2055 door->Close(); 2056 } 2057 } 2058 } 2059 2060 /* 2061 ================ 2062 idElevator::DisableAllDoors 2063 ================ 2064 */ 2065 void idElevator::DisableAllDoors() { 2066 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 2067 if ( door ) { 2068 door->Enable( false ); 2069 } 2070 for ( int i = 0; i < floorInfo.Num(); i++ ) { 2071 door = GetDoor( floorInfo[i].door ); 2072 if ( door ) { 2073 door->Enable( false ); 2074 } 2075 } 2076 } 2077 2078 /* 2079 ================ 2080 idElevator::EnableProperDoors 2081 ================ 2082 */ 2083 void idElevator::EnableProperDoors() { 2084 idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) ); 2085 if ( door ) { 2086 door->Enable( true ); 2087 } 2088 for ( int i = 0; i < floorInfo.Num(); i++ ) { 2089 if ( floorInfo[i].floor == currentFloor ) { 2090 door = GetDoor( floorInfo[i].door ); 2091 if ( door ) { 2092 door->Enable( true ); 2093 break; 2094 } 2095 } 2096 } 2097 } 2098 2099 2100 /* 2101 =============================================================================== 2102 2103 idMover_Binary 2104 2105 Doors, plats, and buttons are all binary (two position) movers 2106 Pos1 is "at rest", pos2 is "activated" 2107 2108 =============================================================================== 2109 */ 2110 2111 const idEventDef EV_Mover_ReturnToPos1( "<returntopos1>", NULL ); 2112 const idEventDef EV_Mover_MatchTeam( "<matchteam>", "dd" ); 2113 const idEventDef EV_Mover_Enable( "enable", NULL ); 2114 const idEventDef EV_Mover_Disable( "disable", NULL ); 2115 2116 CLASS_DECLARATION( idEntity, idMover_Binary ) 2117 EVENT( EV_FindGuiTargets, idMover_Binary::Event_FindGuiTargets ) 2118 EVENT( EV_Thread_SetCallback, idMover_Binary::Event_SetCallback ) 2119 EVENT( EV_Mover_ReturnToPos1, idMover_Binary::Event_ReturnToPos1 ) 2120 EVENT( EV_Activate, idMover_Binary::Event_Use_BinaryMover ) 2121 EVENT( EV_ReachedPos, idMover_Binary::Event_Reached_BinaryMover ) 2122 EVENT( EV_Mover_MatchTeam, idMover_Binary::Event_MatchActivateTeam ) 2123 EVENT( EV_Mover_Enable, idMover_Binary::Event_Enable ) 2124 EVENT( EV_Mover_Disable, idMover_Binary::Event_Disable ) 2125 EVENT( EV_Mover_OpenPortal, idMover_Binary::Event_OpenPortal ) 2126 EVENT( EV_Mover_ClosePortal, idMover_Binary::Event_ClosePortal ) 2127 EVENT( EV_Mover_InitGuiTargets, idMover_Binary::Event_InitGuiTargets ) 2128 END_CLASS 2129 2130 /* 2131 ================ 2132 idMover_Binary::idMover_Binary() 2133 ================ 2134 */ 2135 idMover_Binary::idMover_Binary() { 2136 pos1.Zero(); 2137 pos2.Zero(); 2138 moverState = MOVER_POS1; 2139 moveMaster = NULL; 2140 activateChain = NULL; 2141 soundPos1 = 0; 2142 sound1to2 = 0; 2143 sound2to1 = 0; 2144 soundPos2 = 0; 2145 soundLoop = 0; 2146 wait = 0.0f; 2147 damage = 0.0f; 2148 duration = 0; 2149 accelTime = 0; 2150 decelTime = 0; 2151 activatedBy = this; 2152 stateStartTime = 0; 2153 team.Clear(); 2154 enabled = false; 2155 move_thread = 0; 2156 updateStatus = 0; 2157 areaPortal = 0; 2158 blocked = false; 2159 playerOnly = false; 2160 fl.networkSync = true; 2161 } 2162 2163 /* 2164 ================ 2165 idMover_Binary::~idMover_Binary 2166 ================ 2167 */ 2168 idMover_Binary::~idMover_Binary() { 2169 idMover_Binary *mover; 2170 2171 // if this is the mover master 2172 if ( this == moveMaster ) { 2173 // make the next mover in the chain the move master 2174 for ( mover = moveMaster; mover; mover = mover->activateChain ) { 2175 mover->moveMaster = this->activateChain; 2176 } 2177 } 2178 else { 2179 // remove mover from the activate chain 2180 for ( mover = moveMaster; mover; mover = mover->activateChain ) { 2181 if ( mover->activateChain == this ) { 2182 mover->activateChain = this->activateChain; 2183 break; 2184 } 2185 } 2186 } 2187 } 2188 2189 /* 2190 ================ 2191 idMover_Binary::Save 2192 ================ 2193 */ 2194 void idMover_Binary::Save( idSaveGame *savefile ) const { 2195 int i; 2196 2197 savefile->WriteVec3( pos1 ); 2198 savefile->WriteVec3( pos2 ); 2199 savefile->WriteInt( (moverState_t)moverState ); 2200 2201 savefile->WriteObject( moveMaster ); 2202 savefile->WriteObject( activateChain ); 2203 2204 savefile->WriteInt( soundPos1 ); 2205 savefile->WriteInt( sound1to2 ); 2206 savefile->WriteInt( sound2to1 ); 2207 savefile->WriteInt( soundPos2 ); 2208 savefile->WriteInt( soundLoop ); 2209 2210 savefile->WriteFloat( wait ); 2211 savefile->WriteFloat( damage ); 2212 2213 savefile->WriteInt( duration ); 2214 savefile->WriteInt( accelTime ); 2215 savefile->WriteInt( decelTime ); 2216 2217 activatedBy.Save( savefile ); 2218 2219 savefile->WriteInt( stateStartTime ); 2220 savefile->WriteString( team ); 2221 savefile->WriteBool( enabled ); 2222 2223 savefile->WriteInt( move_thread ); 2224 savefile->WriteInt( updateStatus ); 2225 2226 savefile->WriteInt( buddies.Num() ); 2227 for ( i = 0; i < buddies.Num(); i++ ) { 2228 savefile->WriteString( buddies[ i ] ); 2229 } 2230 2231 savefile->WriteStaticObject( physicsObj ); 2232 2233 savefile->WriteInt( areaPortal ); 2234 if ( areaPortal ) { 2235 savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) ); 2236 } 2237 savefile->WriteBool( blocked ); 2238 savefile->WriteBool( playerOnly ); 2239 2240 savefile->WriteInt( guiTargets.Num() ); 2241 for( i = 0; i < guiTargets.Num(); i++ ) { 2242 guiTargets[ i ].Save( savefile ); 2243 } 2244 } 2245 2246 /* 2247 ================ 2248 idMover_Binary::Restore 2249 ================ 2250 */ 2251 void idMover_Binary::Restore( idRestoreGame *savefile ) { 2252 int i, num, portalState; 2253 idStr temp; 2254 2255 savefile->ReadVec3( pos1 ); 2256 savefile->ReadVec3( pos2 ); 2257 savefile->ReadInt( (int &)moverState ); 2258 2259 savefile->ReadObject( reinterpret_cast<idClass *&>( moveMaster ) ); 2260 savefile->ReadObject( reinterpret_cast<idClass *&>( activateChain ) ); 2261 2262 savefile->ReadInt( soundPos1 ); 2263 savefile->ReadInt( sound1to2 ); 2264 savefile->ReadInt( sound2to1 ); 2265 savefile->ReadInt( soundPos2 ); 2266 savefile->ReadInt( soundLoop ); 2267 2268 savefile->ReadFloat( wait ); 2269 savefile->ReadFloat( damage ); 2270 2271 savefile->ReadInt( duration ); 2272 savefile->ReadInt( accelTime ); 2273 savefile->ReadInt( decelTime ); 2274 2275 activatedBy.Restore( savefile ); 2276 2277 savefile->ReadInt( stateStartTime ); 2278 2279 savefile->ReadString( team ); 2280 savefile->ReadBool( enabled ); 2281 2282 savefile->ReadInt( move_thread ); 2283 savefile->ReadInt( updateStatus ); 2284 2285 savefile->ReadInt( num ); 2286 for ( i = 0; i < num; i++ ) { 2287 savefile->ReadString( temp ); 2288 buddies.Append( temp ); 2289 } 2290 2291 savefile->ReadStaticObject( physicsObj ); 2292 RestorePhysics( &physicsObj ); 2293 2294 savefile->ReadInt( areaPortal ); 2295 if ( areaPortal ) { 2296 savefile->ReadInt( portalState ); 2297 gameLocal.SetPortalState( areaPortal, portalState ); 2298 } 2299 savefile->ReadBool( blocked ); 2300 savefile->ReadBool( playerOnly ); 2301 2302 guiTargets.Clear(); 2303 savefile->ReadInt( num ); 2304 guiTargets.SetNum( num ); 2305 for( i = 0; i < num; i++ ) { 2306 guiTargets[ i ].Restore( savefile ); 2307 } 2308 } 2309 2310 /* 2311 ================ 2312 idMover_Binary::Spawn 2313 2314 Base class for all movers. 2315 2316 "wait" wait before returning (3 default, -1 = never return) 2317 "speed" movement speed 2318 ================ 2319 */ 2320 void idMover_Binary::Spawn() { 2321 idEntity *ent; 2322 const char *temp; 2323 2324 move_thread = 0; 2325 enabled = true; 2326 areaPortal = 0; 2327 2328 activateChain = NULL; 2329 2330 spawnArgs.GetFloat( "wait", "0", wait ); 2331 2332 spawnArgs.GetInt( "updateStatus", "0", updateStatus ); 2333 2334 const idKeyValue *kv = spawnArgs.MatchPrefix( "buddy", NULL ); 2335 while( kv ) { 2336 buddies.Append( kv->GetValue() ); 2337 kv = spawnArgs.MatchPrefix( "buddy", kv ); 2338 } 2339 2340 spawnArgs.GetString( "team", "", &temp ); 2341 team = temp; 2342 2343 if ( !team.Length() ) { 2344 ent = this; 2345 } else { 2346 // find the first entity spawned on this team (which could be us) 2347 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { 2348 if ( ent->IsType( idMover_Binary::Type ) && !idStr::Icmp( static_cast<idMover_Binary *>(ent)->team.c_str(), temp ) ) { 2349 break; 2350 } 2351 } 2352 if ( !ent ) { 2353 ent = this; 2354 } 2355 } 2356 moveMaster = static_cast<idMover_Binary *>(ent); 2357 2358 // create a physics team for the binary mover parts 2359 if ( ent != this ) { 2360 JoinTeam( ent ); 2361 } 2362 2363 physicsObj.SetSelf( this ); 2364 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_MOVER) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 2365 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 2366 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 2367 physicsObj.SetClipMask( MASK_SOLID ); 2368 if ( !spawnArgs.GetBool( "solid", "1" ) ) { 2369 physicsObj.SetContents( 0 ); 2370 } 2371 if ( !spawnArgs.GetBool( "nopush" ) ) { 2372 physicsObj.SetPusher( 0 ); 2373 } 2374 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin ); 2375 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero ); 2376 SetPhysics( &physicsObj ); 2377 2378 if ( moveMaster != this ) { 2379 JoinActivateTeam( moveMaster ); 2380 } 2381 2382 idBounds soundOrigin; 2383 idMover_Binary *slave; 2384 2385 soundOrigin.Clear(); 2386 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) { 2387 soundOrigin += slave->GetPhysics()->GetAbsBounds(); 2388 } 2389 moveMaster->refSound.origin = soundOrigin.GetCenter(); 2390 2391 if ( spawnArgs.MatchPrefix( "guiTarget" ) ) { 2392 if ( gameLocal.GameState() == GAMESTATE_STARTUP ) { 2393 PostEventMS( &EV_FindGuiTargets, 0 ); 2394 } else { 2395 // not during spawn, so it's ok to get the targets 2396 FindGuiTargets(); 2397 } 2398 } 2399 } 2400 2401 /* 2402 =============== 2403 idMover_Binary::GetMovedir 2404 2405 The editor only specifies a single value for angles (yaw), 2406 but we have special constants to generate an up or down direction. 2407 Angles will be cleared, because it is being used to represent a direction 2408 instead of an orientation. 2409 =============== 2410 */ 2411 void idMover_Binary::GetMovedir( float angle, idVec3 &movedir ) { 2412 if ( angle == -1 ) { 2413 movedir.Set( 0, 0, 1 ); 2414 } else if ( angle == -2 ) { 2415 movedir.Set( 0, 0, -1 ); 2416 } else { 2417 movedir = idAngles( 0, angle, 0 ).ToForward(); 2418 } 2419 } 2420 2421 /* 2422 ================ 2423 idMover_Binary::Event_SetCallback 2424 ================ 2425 */ 2426 void idMover_Binary::Event_SetCallback() { 2427 if ( ( moverState == MOVER_1TO2 ) || ( moverState == MOVER_2TO1 ) ) { 2428 move_thread = idThread::CurrentThreadNum(); 2429 idThread::ReturnInt( true ); 2430 } else { 2431 idThread::ReturnInt( false ); 2432 } 2433 } 2434 2435 /* 2436 =============== 2437 idMover_Binary::UpdateMoverSound 2438 =============== 2439 */ 2440 void idMover_Binary::UpdateMoverSound( moverState_t state ) { 2441 if ( moveMaster == this ) { 2442 switch( state ) { 2443 case MOVER_POS1: 2444 break; 2445 case MOVER_POS2: 2446 break; 2447 case MOVER_1TO2: 2448 StartSound( "snd_open", SND_CHANNEL_ANY, 0, false, NULL ); 2449 break; 2450 case MOVER_2TO1: 2451 StartSound( "snd_close", SND_CHANNEL_ANY, 0, false, NULL ); 2452 break; 2453 } 2454 } 2455 } 2456 2457 /* 2458 =============== 2459 idMover_Binary::SetMoverState 2460 =============== 2461 */ 2462 void idMover_Binary::SetMoverState( moverState_t newstate, int time ) { 2463 idVec3 delta; 2464 2465 moverState = newstate; 2466 move_thread = 0; 2467 2468 UpdateMoverSound( newstate ); 2469 2470 stateStartTime = time; 2471 switch( moverState ) { 2472 case MOVER_POS1: { 2473 Signal( SIG_MOVER_POS1 ); 2474 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos1, vec3_origin, vec3_origin ); 2475 break; 2476 } 2477 case MOVER_POS2: { 2478 Signal( SIG_MOVER_POS2 ); 2479 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos2, vec3_origin, vec3_origin ); 2480 break; 2481 } 2482 case MOVER_1TO2: { 2483 Signal( SIG_MOVER_1TO2 ); 2484 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos1, ( pos2 - pos1 ) * 1000.0f / duration, vec3_origin ); 2485 if ( accelTime != 0 || decelTime != 0 ) { 2486 physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos1, pos2 ); 2487 } else { 2488 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 ); 2489 } 2490 break; 2491 } 2492 case MOVER_2TO1: { 2493 Signal( SIG_MOVER_2TO1 ); 2494 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos2, ( pos1 - pos2 ) * 1000.0f / duration, vec3_origin ); 2495 if ( accelTime != 0 || decelTime != 0 ) { 2496 physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos2, pos1 ); 2497 } else { 2498 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 ); 2499 } 2500 break; 2501 } 2502 } 2503 } 2504 2505 /* 2506 ================ 2507 idMover_Binary::MatchActivateTeam 2508 2509 All entities in a mover team will move from pos1 to pos2 2510 in the same amount of time 2511 ================ 2512 */ 2513 void idMover_Binary::MatchActivateTeam( moverState_t newstate, int time ) { 2514 idMover_Binary *slave; 2515 2516 for ( slave = this; slave != NULL; slave = slave->activateChain ) { 2517 slave->SetMoverState( newstate, time ); 2518 } 2519 } 2520 2521 /* 2522 ================ 2523 idMover_Binary::Enable 2524 ================ 2525 */ 2526 void idMover_Binary::Enable( bool b ) { 2527 enabled = b; 2528 } 2529 2530 /* 2531 ================ 2532 idMover_Binary::Event_MatchActivateTeam 2533 ================ 2534 */ 2535 void idMover_Binary::Event_MatchActivateTeam( moverState_t newstate, int time ) { 2536 MatchActivateTeam( newstate, time ); 2537 } 2538 2539 /* 2540 ================ 2541 idMover_Binary::BindTeam 2542 2543 All entities in a mover team will be bound 2544 ================ 2545 */ 2546 void idMover_Binary::BindTeam( idEntity *bindTo ) { 2547 idMover_Binary *slave; 2548 2549 for ( slave = this; slave != NULL; slave = slave->activateChain ) { 2550 slave->Bind( bindTo, true ); 2551 } 2552 } 2553 2554 /* 2555 ================ 2556 idMover_Binary::JoinActivateTeam 2557 2558 Set all entities in a mover team to be enabled 2559 ================ 2560 */ 2561 void idMover_Binary::JoinActivateTeam( idMover_Binary *master ) { 2562 this->activateChain = master->activateChain; 2563 master->activateChain = this; 2564 } 2565 2566 /* 2567 ================ 2568 idMover_Binary::Event_Enable 2569 2570 Set all entities in a mover team to be enabled 2571 ================ 2572 */ 2573 void idMover_Binary::Event_Enable() { 2574 idMover_Binary *slave; 2575 2576 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) { 2577 slave->Enable( false ); 2578 } 2579 } 2580 2581 /* 2582 ================ 2583 idMover_Binary::Event_Disable 2584 2585 Set all entities in a mover team to be disabled 2586 ================ 2587 */ 2588 void idMover_Binary::Event_Disable() { 2589 idMover_Binary *slave; 2590 2591 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) { 2592 slave->Enable( false ); 2593 } 2594 } 2595 2596 /* 2597 ================ 2598 idMover_Binary::Event_OpenPortal 2599 2600 Sets the portal associtated with this mover to be open 2601 ================ 2602 */ 2603 void idMover_Binary::Event_OpenPortal() { 2604 idMover_Binary *slave; 2605 2606 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) { 2607 if ( slave->areaPortal ) { 2608 slave->SetPortalState( true ); 2609 } 2610 if ( slave->playerOnly ) { 2611 gameLocal.SetAASAreaState( slave->GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, false ); 2612 } 2613 } 2614 } 2615 2616 /* 2617 ================ 2618 idMover_Binary::Event_ClosePortal 2619 2620 Sets the portal associtated with this mover to be closed 2621 ================ 2622 */ 2623 void idMover_Binary::Event_ClosePortal() { 2624 idMover_Binary *slave; 2625 2626 for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) { 2627 if ( !slave->IsHidden() ) { 2628 if ( slave->areaPortal ) { 2629 slave->SetPortalState( false ); 2630 } 2631 if ( slave->playerOnly ) { 2632 gameLocal.SetAASAreaState( slave->GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, true ); 2633 } 2634 } 2635 } 2636 } 2637 2638 /* 2639 ================ 2640 idMover_Binary::Event_ReturnToPos1 2641 ================ 2642 */ 2643 void idMover_Binary::Event_ReturnToPos1() { 2644 MatchActivateTeam( MOVER_2TO1, gameLocal.slow.time ); 2645 } 2646 2647 /* 2648 ================ 2649 idMover_Binary::Event_Reached_BinaryMover 2650 ================ 2651 */ 2652 void idMover_Binary::Event_Reached_BinaryMover() { 2653 2654 if ( moverState == MOVER_1TO2 ) { 2655 // reached pos2 2656 idThread::ObjectMoveDone( move_thread, this ); 2657 move_thread = 0; 2658 2659 if ( moveMaster == this ) { 2660 StartSound( "snd_opened", SND_CHANNEL_ANY, 0, false, NULL ); 2661 } 2662 2663 SetMoverState( MOVER_POS2, gameLocal.slow.time ); 2664 2665 SetGuiStates( guiBinaryMoverStates[MOVER_POS2] ); 2666 2667 UpdateBuddies( 1 ); 2668 2669 if ( enabled && wait >= 0 && !spawnArgs.GetBool( "toggle" ) ) { 2670 // return to pos1 after a delay 2671 PostEventSec( &EV_Mover_ReturnToPos1, wait ); 2672 } 2673 2674 // fire targets 2675 ActivateTargets( moveMaster->GetActivator() ); 2676 2677 SetBlocked(false); 2678 } else if ( moverState == MOVER_2TO1 ) { 2679 // reached pos1 2680 idThread::ObjectMoveDone( move_thread, this ); 2681 move_thread = 0; 2682 2683 SetMoverState( MOVER_POS1, gameLocal.slow.time ); 2684 2685 SetGuiStates( guiBinaryMoverStates[MOVER_POS1] ); 2686 2687 UpdateBuddies( 0 ); 2688 2689 // close areaportals 2690 if ( moveMaster == this ) { 2691 ProcessEvent( &EV_Mover_ClosePortal ); 2692 } 2693 2694 if ( enabled && wait >= 0 && spawnArgs.GetBool( "continuous" ) ) { 2695 PostEventSec( &EV_Activate, wait, this ); 2696 } 2697 SetBlocked(false); 2698 } else { 2699 gameLocal.Error( "Event_Reached_BinaryMover: bad moverState" ); 2700 } 2701 } 2702 2703 /* 2704 ================ 2705 idMover_Binary::GotoPosition1 2706 ================ 2707 */ 2708 void idMover_Binary::GotoPosition1() { 2709 idMover_Binary *slave; 2710 int partial; 2711 2712 // only the master should control this 2713 if ( moveMaster != this ) { 2714 moveMaster->GotoPosition1(); 2715 return; 2716 } 2717 2718 SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] ); 2719 2720 if ( ( moverState == MOVER_POS1 ) || ( moverState == MOVER_2TO1 ) ) { 2721 // already there, or on the way 2722 return; 2723 } 2724 2725 if ( moverState == MOVER_POS2 ) { 2726 for ( slave = this; slave != NULL; slave = slave->activateChain ) { 2727 slave->CancelEvents( &EV_Mover_ReturnToPos1 ); 2728 } 2729 ProcessEvent( &EV_Mover_ReturnToPos1 ); 2730 return; 2731 } 2732 2733 // only partway up before reversing 2734 if ( moverState == MOVER_1TO2 ) { 2735 // use the physics times because this might be executed during the physics simulation 2736 partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime(); 2737 assert( partial >= 0 ); 2738 if ( partial < 0 ) { 2739 partial = 0; 2740 } 2741 MatchActivateTeam( MOVER_2TO1, physicsObj.GetTime() - partial ); 2742 // if already at at position 1 (partial == duration) execute the reached event 2743 if ( partial >= duration ) { 2744 Event_Reached_BinaryMover(); 2745 } 2746 } 2747 } 2748 2749 /* 2750 ================ 2751 idMover_Binary::GotoPosition2 2752 ================ 2753 */ 2754 void idMover_Binary::GotoPosition2() { 2755 int partial; 2756 2757 // only the master should control this 2758 if ( moveMaster != this ) { 2759 moveMaster->GotoPosition2(); 2760 return; 2761 } 2762 2763 SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] ); 2764 2765 if ( ( moverState == MOVER_POS2 ) || ( moverState == MOVER_1TO2 ) ) { 2766 // already there, or on the way 2767 return; 2768 } 2769 2770 if ( moverState == MOVER_POS1 ) { 2771 MatchActivateTeam( MOVER_1TO2, gameLocal.slow.time ); 2772 2773 // open areaportal 2774 ProcessEvent( &EV_Mover_OpenPortal ); 2775 return; 2776 } 2777 2778 2779 // only partway up before reversing 2780 if ( moverState == MOVER_2TO1 ) { 2781 // use the physics times because this might be executed during the physics simulation 2782 partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime(); 2783 assert( partial >= 0 ); 2784 if ( partial < 0 ) { 2785 partial = 0; 2786 } 2787 MatchActivateTeam( MOVER_1TO2, physicsObj.GetTime() - partial ); 2788 // if already at at position 2 (partial == duration) execute the reached event 2789 if ( partial >= duration ) { 2790 Event_Reached_BinaryMover(); 2791 } 2792 } 2793 } 2794 2795 /* 2796 ================ 2797 idMover_Binary::UpdateBuddies 2798 ================ 2799 */ 2800 void idMover_Binary::UpdateBuddies( int val ) { 2801 int i, c; 2802 2803 if ( updateStatus == 2 ) { 2804 c = buddies.Num(); 2805 for ( i = 0; i < c; i++ ) { 2806 idEntity *buddy = gameLocal.FindEntity( buddies[i] ); 2807 if ( buddy ) { 2808 buddy->SetShaderParm( SHADERPARM_MODE, val ); 2809 buddy->UpdateVisuals(); 2810 } 2811 } 2812 } 2813 } 2814 2815 /* 2816 ================ 2817 idMover_Binary::SetGuiStates 2818 ================ 2819 */ 2820 void idMover_Binary::SetGuiStates( const char *state ) { 2821 if ( guiTargets.Num() ) { 2822 SetGuiState( "movestate", state ); 2823 } 2824 2825 idMover_Binary *mb = activateChain; 2826 while( mb ) { 2827 if ( mb->guiTargets.Num() ) { 2828 mb->SetGuiState( "movestate", state ); 2829 } 2830 mb = mb->activateChain; 2831 } 2832 } 2833 2834 /* 2835 ================ 2836 idMover_Binary::Use_BinaryMover 2837 ================ 2838 */ 2839 void idMover_Binary::Use_BinaryMover( idEntity *activator ) { 2840 // only the master should be used 2841 if ( moveMaster != this ) { 2842 moveMaster->Use_BinaryMover( activator ); 2843 return; 2844 } 2845 2846 if ( !enabled ) { 2847 return; 2848 } 2849 2850 activatedBy = activator; 2851 2852 if ( moverState == MOVER_POS1 ) { 2853 // FIXME: start moving 1 ms later, because if this was player 2854 // triggered, gameLocal.time hasn't been advanced yet 2855 MatchActivateTeam( MOVER_1TO2, gameLocal.slow.time + 1 ); 2856 2857 SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] ); 2858 // open areaportal 2859 ProcessEvent( &EV_Mover_OpenPortal ); 2860 return; 2861 } 2862 2863 // if all the way up, just delay before coming down 2864 if ( moverState == MOVER_POS2 ) { 2865 idMover_Binary *slave; 2866 2867 if ( wait == -1 ) { 2868 return; 2869 } 2870 2871 SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] ); 2872 2873 for ( slave = this; slave != NULL; slave = slave->activateChain ) { 2874 slave->CancelEvents( &EV_Mover_ReturnToPos1 ); 2875 slave->PostEventSec( &EV_Mover_ReturnToPos1, spawnArgs.GetBool( "toggle" ) ? 0 : wait ); 2876 } 2877 return; 2878 } 2879 2880 // only partway down before reversing 2881 if ( moverState == MOVER_2TO1 ) { 2882 GotoPosition2(); 2883 return; 2884 } 2885 2886 // only partway up before reversing 2887 if ( moverState == MOVER_1TO2 ) { 2888 GotoPosition1(); 2889 return; 2890 } 2891 } 2892 2893 /* 2894 ================ 2895 idMover_Binary::Event_Use_BinaryMover 2896 ================ 2897 */ 2898 void idMover_Binary::Event_Use_BinaryMover( idEntity *activator ) { 2899 Use_BinaryMover( activator ); 2900 } 2901 2902 /* 2903 ================ 2904 idMover_Binary::PreBind 2905 ================ 2906 */ 2907 void idMover_Binary::PreBind() { 2908 pos1 = GetWorldCoordinates( pos1 ); 2909 pos2 = GetWorldCoordinates( pos2 ); 2910 } 2911 2912 /* 2913 ================ 2914 idMover_Binary::PostBind 2915 ================ 2916 */ 2917 void idMover_Binary::PostBind() { 2918 pos1 = GetLocalCoordinates( pos1 ); 2919 pos2 = GetLocalCoordinates( pos2 ); 2920 } 2921 2922 /* 2923 ================ 2924 idMover_Binary::FindGuiTargets 2925 ================ 2926 */ 2927 void idMover_Binary::FindGuiTargets() { 2928 gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" ); 2929 } 2930 2931 /* 2932 ============================== 2933 idMover_Binary::SetGuiState 2934 2935 key/val will be set to any renderEntity->gui's on the list 2936 ============================== 2937 */ 2938 void idMover_Binary::SetGuiState( const char *key, const char *val ) const { 2939 int i; 2940 2941 for( i = 0; i < guiTargets.Num(); i++ ) { 2942 idEntity *ent = guiTargets[ i ].GetEntity(); 2943 if ( ent ) { 2944 for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) { 2945 if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) { 2946 ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val ); 2947 ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true ); 2948 } 2949 } 2950 ent->UpdateVisuals(); 2951 } 2952 } 2953 } 2954 2955 /* 2956 ================ 2957 idMover_Binary::Event_InitGuiTargets 2958 ================ 2959 */ 2960 void idMover_Binary::Event_FindGuiTargets() { 2961 FindGuiTargets(); 2962 } 2963 2964 /* 2965 ================ 2966 idMover_Binary::Event_InitGuiTargets 2967 ================ 2968 */ 2969 void idMover_Binary::Event_InitGuiTargets() { 2970 if ( guiTargets.Num() ) { 2971 SetGuiState( "movestate", guiBinaryMoverStates[MOVER_POS1] ); 2972 } 2973 } 2974 2975 /* 2976 ================ 2977 idMover_Binary::InitSpeed 2978 2979 pos1, pos2, and speed are passed in so the movement delta can be calculated 2980 ================ 2981 */ 2982 void idMover_Binary::InitSpeed( idVec3 &mpos1, idVec3 &mpos2, float mspeed, float maccelTime, float mdecelTime ) { 2983 idVec3 move; 2984 float distance; 2985 float speed; 2986 2987 pos1 = mpos1; 2988 pos2 = mpos2; 2989 2990 accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) ); 2991 decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) ); 2992 2993 speed = mspeed ? mspeed : 100; 2994 2995 // calculate time to reach second position from speed 2996 move = pos2 - pos1; 2997 distance = move.Length(); 2998 duration = idPhysics::SnapTimeToPhysicsFrame( distance * 1000 / speed ); 2999 if ( duration <= 0 ) { 3000 duration = 1; 3001 } 3002 3003 moverState = MOVER_POS1; 3004 3005 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin ); 3006 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin ); 3007 SetOrigin( pos1 ); 3008 3009 PostEventMS( &EV_Mover_InitGuiTargets, 0 ); 3010 } 3011 3012 /* 3013 ================ 3014 idMover_Binary::InitTime 3015 3016 pos1, pos2, and time are passed in so the movement delta can be calculated 3017 ================ 3018 */ 3019 void idMover_Binary::InitTime( idVec3 &mpos1, idVec3 &mpos2, float mtime, float maccelTime, float mdecelTime ) { 3020 3021 pos1 = mpos1; 3022 pos2 = mpos2; 3023 3024 accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) ); 3025 decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) ); 3026 3027 duration = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mtime ) ); 3028 if ( duration <= 0 ) { 3029 duration = 1; 3030 } 3031 3032 moverState = MOVER_POS1; 3033 3034 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin ); 3035 physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin ); 3036 SetOrigin( pos1 ); 3037 3038 PostEventMS( &EV_Mover_InitGuiTargets, 0 ); 3039 } 3040 3041 /* 3042 ================ 3043 idMover_Binary::SetBlocked 3044 ================ 3045 */ 3046 void idMover_Binary::SetBlocked( bool b ) { 3047 for ( idMover_Binary *slave = moveMaster; slave != NULL; slave = slave->activateChain ) { 3048 slave->blocked = b; 3049 if ( b ) { 3050 const idKeyValue *kv = slave->spawnArgs.MatchPrefix( "triggerBlocked" ); 3051 while( kv ) { 3052 idEntity *ent = gameLocal.FindEntity( kv->GetValue() ); 3053 if ( ent ) { 3054 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() ); 3055 } 3056 kv = slave->spawnArgs.MatchPrefix( "triggerBlocked", kv ); 3057 } 3058 } 3059 } 3060 } 3061 3062 /* 3063 ================ 3064 idMover_Binary::IsBlocked 3065 ================ 3066 */ 3067 bool idMover_Binary::IsBlocked() { 3068 return blocked; 3069 } 3070 3071 /* 3072 ================ 3073 idMover_Binary::GetActivator 3074 ================ 3075 */ 3076 idEntity *idMover_Binary::GetActivator() const { 3077 return activatedBy.GetEntity(); 3078 } 3079 3080 /* 3081 ================ 3082 idMover_Binary::WriteToSnapshot 3083 ================ 3084 */ 3085 void idMover_Binary::WriteToSnapshot( idBitMsg &msg ) const { 3086 physicsObj.WriteToSnapshot( msg ); 3087 msg.WriteBits( moverState, 3 ); 3088 WriteBindToSnapshot( msg ); 3089 } 3090 3091 /* 3092 ================ 3093 idMover_Binary::ReadFromSnapshot 3094 ================ 3095 */ 3096 void idMover_Binary::ReadFromSnapshot( const idBitMsg &msg ) { 3097 moverState_t oldMoverState = moverState; 3098 3099 physicsObj.ReadFromSnapshot( msg ); 3100 moverState = (moverState_t) msg.ReadBits( 3 ); 3101 ReadBindFromSnapshot( msg ); 3102 3103 if ( msg.HasChanged() ) { 3104 if ( moverState != oldMoverState ) { 3105 UpdateMoverSound( moverState ); 3106 MatchActivateTeam( moverState, gameLocal.slow.time ); 3107 } 3108 UpdateVisuals(); 3109 } 3110 } 3111 3112 /* 3113 ================ 3114 idMover_Binary::SetPortalState 3115 ================ 3116 */ 3117 void idMover_Binary::SetPortalState( bool open ) { 3118 assert( areaPortal ); 3119 gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL ); 3120 } 3121 3122 /* 3123 =============================================================================== 3124 3125 idDoor 3126 3127 A use can be triggered either by a touch function, by being shot, or by being 3128 targeted by another entity. 3129 3130 =============================================================================== 3131 */ 3132 3133 const idEventDef EV_Door_StartOpen( "<startOpen>", NULL ); 3134 const idEventDef EV_Door_SpawnDoorTrigger( "<spawnDoorTrigger>", NULL ); 3135 const idEventDef EV_Door_SpawnSoundTrigger( "<spawnSoundTrigger>", NULL ); 3136 const idEventDef EV_Door_Open( "open", NULL ); 3137 const idEventDef EV_Door_Close( "close", NULL ); 3138 const idEventDef EV_Door_Lock( "lock", "d" ); 3139 const idEventDef EV_Door_IsOpen( "isOpen", NULL, 'f' ); 3140 const idEventDef EV_Door_IsLocked( "isLocked", NULL, 'f' ); 3141 3142 CLASS_DECLARATION( idMover_Binary, idDoor ) 3143 EVENT( EV_TeamBlocked, idDoor::Event_TeamBlocked ) 3144 EVENT( EV_PartBlocked, idDoor::Event_PartBlocked ) 3145 EVENT( EV_Touch, idDoor::Event_Touch ) 3146 EVENT( EV_Activate, idDoor::Event_Activate ) 3147 EVENT( EV_Door_StartOpen, idDoor::Event_StartOpen ) 3148 EVENT( EV_Door_SpawnDoorTrigger, idDoor::Event_SpawnDoorTrigger ) 3149 EVENT( EV_Door_SpawnSoundTrigger, idDoor::Event_SpawnSoundTrigger ) 3150 EVENT( EV_Door_Open, idDoor::Event_Open ) 3151 EVENT( EV_Door_Close, idDoor::Event_Close ) 3152 EVENT( EV_Door_Lock, idDoor::Event_Lock ) 3153 EVENT( EV_Door_IsOpen, idDoor::Event_IsOpen ) 3154 EVENT( EV_Door_IsLocked, idDoor::Event_Locked ) 3155 EVENT( EV_ReachedPos, idDoor::Event_Reached_BinaryMover ) 3156 EVENT( EV_SpectatorTouch, idDoor::Event_SpectatorTouch ) 3157 EVENT( EV_Mover_OpenPortal, idDoor::Event_OpenPortal ) 3158 EVENT( EV_Mover_ClosePortal, idDoor::Event_ClosePortal ) 3159 END_CLASS 3160 3161 /* 3162 ================ 3163 idDoor::idDoor 3164 ================ 3165 */ 3166 idDoor::idDoor() { 3167 triggersize = 1.0f; 3168 crusher = false; 3169 noTouch = false; 3170 aas_area_closed = false; 3171 buddyStr.Clear(); 3172 trigger = NULL; 3173 sndTrigger = NULL; 3174 nextSndTriggerTime = 0; 3175 localTriggerOrigin.Zero(); 3176 localTriggerAxis.Identity(); 3177 requires.Clear(); 3178 removeItem = 0; 3179 syncLock.Clear(); 3180 companionDoor = NULL; 3181 normalAxisIndex = 0; 3182 } 3183 3184 /* 3185 ================ 3186 idDoor::~idDoor 3187 ================ 3188 */ 3189 idDoor::~idDoor() { 3190 if ( trigger ) { 3191 delete trigger; 3192 } 3193 if ( sndTrigger ) { 3194 delete sndTrigger; 3195 } 3196 } 3197 3198 /* 3199 ================ 3200 idDoor::Save 3201 ================ 3202 */ 3203 void idDoor::Save( idSaveGame *savefile ) const { 3204 3205 savefile->WriteFloat( triggersize ); 3206 savefile->WriteBool( crusher ); 3207 savefile->WriteBool( noTouch ); 3208 savefile->WriteBool( aas_area_closed ); 3209 savefile->WriteString( buddyStr ); 3210 savefile->WriteInt( nextSndTriggerTime ); 3211 3212 savefile->WriteVec3( localTriggerOrigin ); 3213 savefile->WriteMat3( localTriggerAxis ); 3214 3215 savefile->WriteString( requires ); 3216 savefile->WriteInt( removeItem ); 3217 savefile->WriteString( syncLock ); 3218 savefile->WriteInt( normalAxisIndex ); 3219 3220 savefile->WriteClipModel( trigger ); 3221 savefile->WriteClipModel( sndTrigger ); 3222 3223 savefile->WriteObject( companionDoor ); 3224 } 3225 3226 /* 3227 ================ 3228 idDoor::Restore 3229 ================ 3230 */ 3231 void idDoor::Restore( idRestoreGame *savefile ) { 3232 3233 savefile->ReadFloat( triggersize ); 3234 savefile->ReadBool( crusher ); 3235 savefile->ReadBool( noTouch ); 3236 savefile->ReadBool( aas_area_closed ); 3237 SetAASAreaState( aas_area_closed ); 3238 savefile->ReadString( buddyStr ); 3239 savefile->ReadInt( nextSndTriggerTime ); 3240 3241 savefile->ReadVec3( localTriggerOrigin ); 3242 savefile->ReadMat3( localTriggerAxis ); 3243 3244 savefile->ReadString( requires ); 3245 savefile->ReadInt( removeItem ); 3246 savefile->ReadString( syncLock ); 3247 savefile->ReadInt( normalAxisIndex ); 3248 3249 savefile->ReadClipModel( trigger ); 3250 savefile->ReadClipModel( sndTrigger ); 3251 3252 savefile->ReadObject( reinterpret_cast<idClass *&>( companionDoor ) ); 3253 } 3254 3255 /* 3256 ================ 3257 idDoor::Spawn 3258 ================ 3259 */ 3260 void idDoor::Spawn() { 3261 idVec3 abs_movedir; 3262 float distance; 3263 idVec3 size; 3264 idVec3 movedir; 3265 float dir; 3266 float lip; 3267 bool start_open; 3268 float time; 3269 float speed; 3270 3271 // get the direction to move 3272 if ( !spawnArgs.GetFloat( "movedir", "0", dir ) ) { 3273 // no movedir, so angle defines movement direction and not orientation, 3274 // a la oldschool Quake 3275 SetAngles( ang_zero ); 3276 spawnArgs.GetFloat( "angle", "0", dir ); 3277 } 3278 GetMovedir( dir, movedir ); 3279 3280 // default speed of 400 3281 spawnArgs.GetFloat( "speed", "400", speed ); 3282 3283 // default wait of 2 seconds 3284 spawnArgs.GetFloat( "wait", "3", wait ); 3285 3286 // default lip of 8 units 3287 spawnArgs.GetFloat( "lip", "8", lip ); 3288 3289 // by default no damage 3290 spawnArgs.GetFloat( "damage", "0", damage ); 3291 3292 // trigger size 3293 spawnArgs.GetFloat( "triggersize", "120", triggersize ); 3294 3295 spawnArgs.GetBool( "crusher", "0", crusher ); 3296 spawnArgs.GetBool( "start_open", "0", start_open ); 3297 spawnArgs.GetBool( "no_touch", "0", noTouch ); 3298 spawnArgs.GetBool( "player_only", "0", playerOnly ); 3299 3300 // expects syncLock to be a door that must be closed before this door will open 3301 spawnArgs.GetString( "syncLock", "", syncLock ); 3302 3303 spawnArgs.GetString( "buddy", "", buddyStr ); 3304 3305 spawnArgs.GetString( "requires", "", requires ); 3306 spawnArgs.GetInt( "removeItem", "0", removeItem ); 3307 3308 // ever separate piece of a door is considered solid when other team mates push entities 3309 fl.solidForTeam = true; 3310 3311 // first position at start 3312 pos1 = GetPhysics()->GetOrigin(); 3313 3314 // calculate second position 3315 abs_movedir[0] = idMath::Fabs( movedir[ 0 ] ); 3316 abs_movedir[1] = idMath::Fabs( movedir[ 1 ] ); 3317 abs_movedir[2] = idMath::Fabs( movedir[ 2 ] ); 3318 size = GetPhysics()->GetAbsBounds()[1] - GetPhysics()->GetAbsBounds()[0]; 3319 distance = ( abs_movedir * size ) - lip; 3320 pos2 = pos1 + distance * movedir; 3321 3322 // if "start_open", reverse position 1 and 2 3323 if ( start_open ) { 3324 // post it after EV_SpawnBind 3325 PostEventMS( &EV_Door_StartOpen, 1 ); 3326 } 3327 3328 if ( spawnArgs.GetFloat( "time", "1", time ) ) { 3329 InitTime( pos1, pos2, time, 0, 0 ); 3330 } else { 3331 InitSpeed( pos1, pos2, speed, 0, 0 ); 3332 } 3333 3334 if ( moveMaster == this ) { 3335 if ( health ) { 3336 fl.takedamage = true; 3337 } 3338 if ( noTouch || health ) { 3339 // non touch/shoot doors 3340 PostEventMS( &EV_Mover_MatchTeam, 0, moverState, gameLocal.slow.time ); 3341 3342 const char *sndtemp = spawnArgs.GetString( "snd_locked" ); 3343 if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) { 3344 PostEventMS( &EV_Door_SpawnSoundTrigger, 0 ); 3345 } 3346 } else { 3347 // spawn trigger 3348 PostEventMS( &EV_Door_SpawnDoorTrigger, 0 ); 3349 } 3350 } 3351 3352 // see if we are on an areaportal 3353 areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() ); 3354 if ( !start_open ) { 3355 // start closed 3356 ProcessEvent( &EV_Mover_ClosePortal ); 3357 3358 if ( playerOnly ) { 3359 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, true ); 3360 } 3361 } 3362 3363 int locked = spawnArgs.GetInt( "locked" ); 3364 if ( locked ) { 3365 // make sure all members of the team get locked 3366 PostEventMS( &EV_Door_Lock, 0, locked ); 3367 } 3368 3369 if ( spawnArgs.GetBool( "continuous" ) ) { 3370 PostEventSec( &EV_Activate, spawnArgs.GetFloat( "delay" ), this ); 3371 } 3372 3373 // sounds have a habit of stuttering when portals close, so make them unoccluded 3374 refSound.parms.soundShaderFlags |= SSF_NO_OCCLUSION; 3375 3376 companionDoor = NULL; 3377 3378 enabled = true; 3379 blocked = false; 3380 } 3381 3382 /* 3383 ================ 3384 idDoor::Think 3385 ================ 3386 */ 3387 void idDoor::ClientThink( const int curTime, const float fraction, const bool predict ) { 3388 idVec3 masterOrigin; 3389 idMat3 masterAxis; 3390 3391 idMover_Binary::ClientThink( curTime, fraction, predict ); 3392 3393 if ( thinkFlags & TH_PHYSICS ) { 3394 // update trigger position 3395 if ( GetMasterPosition( masterOrigin, masterAxis ) ) { 3396 if ( trigger ) { 3397 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis ); 3398 } 3399 if ( sndTrigger ) { 3400 sndTrigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis ); 3401 } 3402 } 3403 } 3404 } 3405 3406 /* 3407 ================ 3408 idDoor::Think 3409 ================ 3410 */ 3411 void idDoor::Think() { 3412 idVec3 masterOrigin; 3413 idMat3 masterAxis; 3414 3415 idMover_Binary::Think(); 3416 3417 if ( thinkFlags & TH_PHYSICS ) { 3418 // update trigger position 3419 if ( GetMasterPosition( masterOrigin, masterAxis ) ) { 3420 if ( trigger ) { 3421 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis ); 3422 } 3423 if ( sndTrigger ) { 3424 sndTrigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis ); 3425 } 3426 } 3427 } 3428 } 3429 3430 /* 3431 ================ 3432 idDoor::PreBind 3433 ================ 3434 */ 3435 void idDoor::PreBind() { 3436 idMover_Binary::PreBind(); 3437 } 3438 3439 /* 3440 ================ 3441 idDoor::PostBind 3442 ================ 3443 */ 3444 void idDoor::PostBind() { 3445 idMover_Binary::PostBind(); 3446 GetLocalTriggerPosition( trigger ? trigger : sndTrigger ); 3447 } 3448 3449 /* 3450 ================ 3451 idDoor::SetAASAreaState 3452 ================ 3453 */ 3454 void idDoor::SetAASAreaState( bool closed ) { 3455 aas_area_closed = closed; 3456 gameLocal.SetAASAreaState( physicsObj.GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL|AREACONTENTS_OBSTACLE, closed ); 3457 } 3458 3459 /* 3460 ================ 3461 idDoor::Hide 3462 ================ 3463 */ 3464 void idDoor::Hide() { 3465 idMover_Binary *slave; 3466 idMover_Binary *master; 3467 idDoor *slaveDoor; 3468 idDoor *companion; 3469 3470 master = GetMoveMaster(); 3471 if ( this != master ) { 3472 master->Hide(); 3473 } else { 3474 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) { 3475 if ( slave->IsType( idDoor::Type ) ) { 3476 slaveDoor = static_cast<idDoor *>( slave ); 3477 companion = slaveDoor->companionDoor; 3478 if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) { 3479 companion->Hide(); 3480 } 3481 if ( slaveDoor->trigger ) { 3482 slaveDoor->trigger->Disable(); 3483 } 3484 if ( slaveDoor->sndTrigger ) { 3485 slaveDoor->sndTrigger->Disable(); 3486 } 3487 if ( slaveDoor->areaPortal ) { 3488 slaveDoor->SetPortalState( true ); 3489 } 3490 slaveDoor->SetAASAreaState( false ); 3491 } 3492 slave->GetPhysics()->GetClipModel()->Disable(); 3493 slave->idMover_Binary::Hide(); 3494 } 3495 } 3496 } 3497 3498 /* 3499 ================ 3500 idDoor::Show 3501 ================ 3502 */ 3503 void idDoor::Show() { 3504 idMover_Binary *slave; 3505 idMover_Binary *master; 3506 idDoor *slaveDoor; 3507 idDoor *companion; 3508 3509 master = GetMoveMaster(); 3510 if ( this != master ) { 3511 master->Show(); 3512 } else { 3513 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) { 3514 if ( slave->IsType( idDoor::Type ) ) { 3515 slaveDoor = static_cast<idDoor *>( slave ); 3516 companion = slaveDoor->companionDoor; 3517 if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) { 3518 companion->Show(); 3519 } 3520 if ( slaveDoor->trigger ) { 3521 slaveDoor->trigger->Enable(); 3522 } 3523 if ( slaveDoor->sndTrigger ) { 3524 slaveDoor->sndTrigger->Enable(); 3525 } 3526 if ( slaveDoor->areaPortal && ( slaveDoor->moverState == MOVER_POS1 ) ) { 3527 slaveDoor->SetPortalState( false ); 3528 } 3529 slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() ); 3530 } 3531 slave->GetPhysics()->GetClipModel()->Enable(); 3532 slave->idMover_Binary::Show(); 3533 } 3534 } 3535 } 3536 3537 /* 3538 ================ 3539 idDoor::GetLocalTriggerPosition 3540 ================ 3541 */ 3542 void idDoor::GetLocalTriggerPosition( const idClipModel *trigger ) { 3543 idVec3 origin; 3544 idMat3 axis; 3545 3546 if ( !trigger ) { 3547 return; 3548 } 3549 3550 GetMasterPosition( origin, axis ); 3551 localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose(); 3552 localTriggerAxis = trigger->GetAxis() * axis.Transpose(); 3553 } 3554 3555 /* 3556 ================ 3557 idDoor::Use 3558 ================ 3559 */ 3560 void idDoor::Use( idEntity *other, idEntity *activator ) { 3561 if ( gameLocal.RequirementMet( activator, requires, removeItem ) ) { 3562 if ( syncLock.Length() ) { 3563 idEntity *sync = gameLocal.FindEntity( syncLock ); 3564 if ( sync != NULL && sync->IsType( idDoor::Type ) ) { 3565 if ( static_cast<idDoor *>( sync )->IsOpen() ) { 3566 return; 3567 } 3568 } 3569 } 3570 ActivateTargets( activator ); 3571 Use_BinaryMover( activator ); 3572 } 3573 } 3574 3575 /* 3576 ================ 3577 idDoor::Open 3578 ================ 3579 */ 3580 void idDoor::Open() { 3581 GotoPosition2(); 3582 } 3583 3584 /* 3585 ================ 3586 idDoor::Close 3587 ================ 3588 */ 3589 void idDoor::Close() { 3590 GotoPosition1(); 3591 } 3592 3593 /* 3594 ================ 3595 idDoor::Lock 3596 ================ 3597 */ 3598 void idDoor::Lock( int f ) { 3599 idMover_Binary *other; 3600 3601 // lock all the doors on the team 3602 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) { 3603 if ( other->IsType( idDoor::Type ) ) { 3604 idDoor *door = static_cast<idDoor *>( other ); 3605 if ( other == moveMaster ) { 3606 if ( door->sndTrigger == NULL ) { 3607 // in this case the sound trigger never got spawned 3608 const char *sndtemp = door->spawnArgs.GetString( "snd_locked" ); 3609 if ( sndtemp != NULL && *sndtemp != NULL ) { 3610 door->PostEventMS( &EV_Door_SpawnSoundTrigger, 0 ); 3611 } 3612 } 3613 if ( !f && ( door->spawnArgs.GetInt( "locked" ) != 0 ) ) { 3614 door->StartSound( "snd_unlocked", SND_CHANNEL_ANY, 0, false, NULL ); 3615 } 3616 } 3617 door->spawnArgs.SetInt( "locked", f ); 3618 if ( ( f == 0 ) || ( !IsHidden() && ( door->moverState == MOVER_POS1 ) ) ) { 3619 door->SetAASAreaState( f != 0 ); 3620 } 3621 } 3622 } 3623 3624 if ( f ) { 3625 Close(); 3626 } 3627 } 3628 3629 /* 3630 ================ 3631 idDoor::IsLocked 3632 ================ 3633 */ 3634 int idDoor::IsLocked() { 3635 return spawnArgs.GetInt( "locked" ); 3636 } 3637 3638 /* 3639 ================ 3640 idDoor::IsOpen 3641 ================ 3642 */ 3643 bool idDoor::IsOpen() { 3644 return ( moverState != MOVER_POS1 ); 3645 } 3646 3647 /* 3648 ================ 3649 idDoor::IsNoTouch 3650 ================ 3651 */ 3652 bool idDoor::IsNoTouch() { 3653 return noTouch; 3654 } 3655 3656 /* 3657 ================ 3658 idDoor::AllowPlayerOnly 3659 ================ 3660 */ 3661 bool idDoor::AllowPlayerOnly( idEntity *ent ) { 3662 if ( playerOnly && !ent->IsType(idPlayer::Type) ) { 3663 return false; 3664 } 3665 3666 return true; 3667 } 3668 3669 /* 3670 ====================== 3671 idDoor::CalcTriggerBounds 3672 3673 Calcs bounds for a trigger. 3674 ====================== 3675 */ 3676 void idDoor::CalcTriggerBounds( float size, idBounds &bounds ) { 3677 idMover_Binary *other; 3678 int i; 3679 int best; 3680 3681 // find the bounds of everything on the team 3682 bounds = GetPhysics()->GetAbsBounds(); 3683 3684 fl.takedamage = true; 3685 for( other = activateChain; other != NULL; other = other->GetActivateChain() ) { 3686 if ( other->IsType( idDoor::Type ) ) { 3687 // find the bounds of everything on the team 3688 bounds.AddBounds( other->GetPhysics()->GetAbsBounds() ); 3689 3690 // set all of the slaves as shootable 3691 other->fl.takedamage = true; 3692 } 3693 } 3694 3695 // find the thinnest axis, which will be the one we expand 3696 best = 0; 3697 for ( i = 1 ; i < 3 ; i++ ) { 3698 if ( bounds[1][ i ] - bounds[0][ i ] < bounds[1][ best ] - bounds[0][ best ] ) { 3699 best = i; 3700 } 3701 } 3702 normalAxisIndex = best; 3703 bounds[0][ best ] -= size; 3704 bounds[1][ best ] += size; 3705 bounds[0] -= GetPhysics()->GetOrigin(); 3706 bounds[1] -= GetPhysics()->GetOrigin(); 3707 } 3708 3709 /* 3710 ====================== 3711 idDoor::Event_StartOpen 3712 3713 if "start_open", reverse position 1 and 2 3714 ====================== 3715 */ 3716 void idDoor::Event_StartOpen() { 3717 float time; 3718 float speed; 3719 3720 // if "start_open", reverse position 1 and 2 3721 pos1 = pos2; 3722 pos2 = GetPhysics()->GetOrigin(); 3723 3724 spawnArgs.GetFloat( "speed", "400", speed ); 3725 3726 if ( spawnArgs.GetFloat( "time", "1", time ) ) { 3727 InitTime( pos1, pos2, time, 0, 0 ); 3728 } else { 3729 InitSpeed( pos1, pos2, speed, 0, 0 ); 3730 } 3731 } 3732 3733 /* 3734 ====================== 3735 idDoor::Event_SpawnDoorTrigger 3736 3737 All of the parts of a door have been spawned, so create 3738 a trigger that encloses all of them. 3739 ====================== 3740 */ 3741 void idDoor::Event_SpawnDoorTrigger() { 3742 idBounds bounds; 3743 idMover_Binary *other; 3744 bool toggle; 3745 3746 if ( trigger ) { 3747 // already have a trigger, so don't spawn a new one. 3748 return; 3749 } 3750 3751 // check if any of the doors are marked as toggled 3752 toggle = false; 3753 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) { 3754 if ( other->IsType( idDoor::Type ) && other->spawnArgs.GetBool( "toggle" ) ) { 3755 toggle = true; 3756 break; 3757 } 3758 } 3759 3760 if ( toggle ) { 3761 // mark them all as toggled 3762 for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) { 3763 if ( other->IsType( idDoor::Type ) ) { 3764 other->spawnArgs.Set( "toggle", "1" ); 3765 } 3766 } 3767 // don't spawn trigger 3768 return; 3769 } 3770 3771 const char *sndtemp = spawnArgs.GetString( "snd_locked" ); 3772 if ( spawnArgs.GetInt( "locked" ) && sndtemp != NULL && *sndtemp != NULL ) { 3773 PostEventMS( &EV_Door_SpawnSoundTrigger, 0 ); 3774 } 3775 3776 CalcTriggerBounds( triggersize, bounds ); 3777 3778 // create a trigger clip model 3779 trigger = new (TAG_PHYSICS_CLIP_MOVER) idClipModel( idTraceModel( bounds ) ); 3780 trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity ); 3781 trigger->SetContents( CONTENTS_TRIGGER ); 3782 3783 GetLocalTriggerPosition( trigger ); 3784 3785 MatchActivateTeam( moverState, gameLocal.slow.time ); 3786 } 3787 3788 /* 3789 ====================== 3790 idDoor::Event_SpawnSoundTrigger 3791 3792 Spawn a sound trigger to activate locked sound if it exists. 3793 ====================== 3794 */ 3795 void idDoor::Event_SpawnSoundTrigger() { 3796 idBounds bounds; 3797 3798 if ( sndTrigger ) { 3799 return; 3800 } 3801 3802 CalcTriggerBounds( triggersize * 0.5f, bounds ); 3803 3804 // create a trigger clip model 3805 sndTrigger = new (TAG_PHYSICS_CLIP_MOVER) idClipModel( idTraceModel( bounds ) ); 3806 sndTrigger->Link( gameLocal.clip, this, 254, GetPhysics()->GetOrigin(), mat3_identity ); 3807 sndTrigger->SetContents( CONTENTS_TRIGGER ); 3808 3809 GetLocalTriggerPosition( sndTrigger ); 3810 } 3811 3812 /* 3813 ================ 3814 idDoor::Event_Reached_BinaryMover 3815 ================ 3816 */ 3817 void idDoor::Event_Reached_BinaryMover() { 3818 if ( moverState == MOVER_2TO1 ) { 3819 SetBlocked( false ); 3820 const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerClosed" ); 3821 while( kv ) { 3822 idEntity *ent = gameLocal.FindEntity( kv->GetValue() ); 3823 if ( ent ) { 3824 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() ); 3825 } 3826 kv = spawnArgs.MatchPrefix( "triggerClosed", kv ); 3827 } 3828 } else if ( moverState == MOVER_1TO2 ) { 3829 const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerOpened" ); 3830 while( kv ) { 3831 idEntity *ent = gameLocal.FindEntity( kv->GetValue() ); 3832 if ( ent ) { 3833 ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() ); 3834 } 3835 kv = spawnArgs.MatchPrefix( "triggerOpened", kv ); 3836 } 3837 } 3838 idMover_Binary::Event_Reached_BinaryMover(); 3839 } 3840 3841 /* 3842 ================ 3843 idDoor::Blocked_Door 3844 ================ 3845 */ 3846 void idDoor::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) { 3847 SetBlocked( true ); 3848 3849 if ( crusher ) { 3850 return; // crushers don't reverse 3851 } 3852 3853 // reverse direction 3854 Use_BinaryMover( moveMaster->GetActivator() ); 3855 3856 if ( companionDoor ) { 3857 companionDoor->ProcessEvent( &EV_TeamBlocked, blockedEntity, blockingEntity ); 3858 } 3859 } 3860 3861 /* 3862 =============== 3863 idDoor::SetCompanion 3864 =============== 3865 */ 3866 void idDoor::SetCompanion( idDoor *door ) { 3867 companionDoor = door; 3868 } 3869 3870 /* 3871 =============== 3872 idDoor::Event_PartBlocked 3873 =============== 3874 */ 3875 void idDoor::Event_PartBlocked( idEntity *blockingEntity ) { 3876 if ( damage > 0.0f ) { 3877 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); 3878 } 3879 } 3880 3881 /* 3882 ================ 3883 idDoor::Event_Touch 3884 ================ 3885 */ 3886 void idDoor::Event_Touch( idEntity *other, trace_t *trace ) { 3887 idVec3 contact, translate; 3888 idVec3 planeaxis1, planeaxis2, normal; 3889 idBounds bounds; 3890 3891 if ( common->IsClient() ) { 3892 return; 3893 } 3894 3895 if ( !enabled ) { 3896 return; 3897 } 3898 3899 if ( trigger && trace->c.id == trigger->GetId() ) { 3900 if ( !IsNoTouch() && !IsLocked() && GetMoverState() != MOVER_1TO2 ) { 3901 if ( AllowPlayerOnly( other ) ) { 3902 Use( this, other ); 3903 } 3904 } 3905 } else if ( sndTrigger && trace->c.id == sndTrigger->GetId() ) { 3906 if ( other && other->IsType( idPlayer::Type ) && IsLocked() && gameLocal.slow.time > nextSndTriggerTime ) { 3907 StartSound( "snd_locked", SND_CHANNEL_ANY, 0, false, NULL ); 3908 nextSndTriggerTime = gameLocal.slow.time + 10000; 3909 } 3910 } 3911 } 3912 3913 /* 3914 ================ 3915 idDoor::Event_SpectatorTouch 3916 ================ 3917 */ 3918 void idDoor::Event_SpectatorTouch( idEntity *other, trace_t *trace ) { 3919 idVec3 contact, translate, normal; 3920 idBounds bounds; 3921 idPlayer *p; 3922 3923 assert( other && other->IsType( idPlayer::Type ) && static_cast< idPlayer * >( other )->spectating ); 3924 3925 p = static_cast< idPlayer * >( other ); 3926 // avoid flicker when stopping right at clip box boundaries 3927 if ( p->lastSpectateTeleport > gameLocal.slow.time - 1000 ) { 3928 return; 3929 } 3930 if ( trigger && !IsOpen() ) { 3931 // teleport to the other side, center to the middle of the trigger brush 3932 bounds = trigger->GetAbsBounds(); 3933 contact = trace->endpos - bounds.GetCenter(); 3934 translate = bounds.GetCenter(); 3935 normal.Zero(); 3936 normal[ normalAxisIndex ] = 1.0f; 3937 if ( normal * contact > 0 ) { 3938 translate[ normalAxisIndex ] += ( bounds[ 0 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f; 3939 } else { 3940 translate[ normalAxisIndex ] += ( bounds[ 1 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f; 3941 } 3942 p->SetOrigin( translate ); 3943 p->lastSpectateTeleport = gameLocal.slow.time; 3944 } 3945 } 3946 3947 /* 3948 ================ 3949 idDoor::Event_Activate 3950 ================ 3951 */ 3952 void idDoor::Event_Activate( idEntity *activator ) { 3953 int old_lock; 3954 3955 if ( spawnArgs.GetInt( "locked" ) ) { 3956 if ( !trigger ) { 3957 PostEventMS( &EV_Door_SpawnDoorTrigger, 0 ); 3958 } 3959 if ( buddyStr.Length() ) { 3960 idEntity *buddy = gameLocal.FindEntity( buddyStr ); 3961 if ( buddy ) { 3962 buddy->SetShaderParm( SHADERPARM_MODE, 1 ); 3963 buddy->UpdateVisuals(); 3964 } 3965 } 3966 3967 old_lock = spawnArgs.GetInt( "locked" ); 3968 Lock( 0 ); 3969 if ( old_lock == 2 ) { 3970 return; 3971 } 3972 } 3973 3974 if ( syncLock.Length() ) { 3975 idEntity *sync = gameLocal.FindEntity( syncLock ); 3976 if ( sync != NULL && sync->IsType( idDoor::Type ) ) { 3977 if ( static_cast<idDoor *>( sync )->IsOpen() ) { 3978 return; 3979 } 3980 } 3981 } 3982 3983 ActivateTargets( activator ); 3984 3985 renderEntity.shaderParms[ SHADERPARM_MODE ] = 1; 3986 UpdateVisuals(); 3987 3988 Use_BinaryMover( activator ); 3989 } 3990 3991 /* 3992 ================ 3993 idDoor::Event_Open 3994 ================ 3995 */ 3996 void idDoor::Event_Open() { 3997 Open(); 3998 } 3999 4000 /* 4001 ================ 4002 idDoor::Event_Close 4003 ================ 4004 */ 4005 void idDoor::Event_Close() { 4006 Close(); 4007 } 4008 4009 /* 4010 ================ 4011 idDoor::Event_Lock 4012 ================ 4013 */ 4014 void idDoor::Event_Lock( int f ) { 4015 Lock( f ); 4016 } 4017 4018 /* 4019 ================ 4020 idDoor::Event_IsOpen 4021 ================ 4022 */ 4023 void idDoor::Event_IsOpen() { 4024 bool state; 4025 4026 state = IsOpen(); 4027 idThread::ReturnFloat( state ); 4028 } 4029 4030 /* 4031 ================ 4032 idDoor::Event_Locked 4033 ================ 4034 */ 4035 void idDoor::Event_Locked() { 4036 idThread::ReturnFloat( spawnArgs.GetInt("locked") ); 4037 } 4038 4039 /* 4040 ================ 4041 idDoor::Event_OpenPortal 4042 4043 Sets the portal associtated with this door to be open 4044 ================ 4045 */ 4046 void idDoor::Event_OpenPortal() { 4047 idMover_Binary *slave; 4048 idDoor *slaveDoor; 4049 4050 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) { 4051 if ( slave->IsType( idDoor::Type ) ) { 4052 slaveDoor = static_cast<idDoor *>( slave ); 4053 if ( slaveDoor->areaPortal ) { 4054 slaveDoor->SetPortalState( true ); 4055 } 4056 slaveDoor->SetAASAreaState( false ); 4057 } 4058 } 4059 } 4060 4061 /* 4062 ================ 4063 idDoor::Event_ClosePortal 4064 4065 Sets the portal associtated with this door to be closed 4066 ================ 4067 */ 4068 void idDoor::Event_ClosePortal() { 4069 idMover_Binary *slave; 4070 idDoor *slaveDoor; 4071 4072 for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) { 4073 if ( !slave->IsHidden() ) { 4074 if ( slave->IsType( idDoor::Type ) ) { 4075 slaveDoor = static_cast<idDoor *>( slave ); 4076 if ( slaveDoor->areaPortal ) { 4077 slaveDoor->SetPortalState( false ); 4078 } 4079 slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() ); 4080 } 4081 } 4082 } 4083 } 4084 4085 4086 /* 4087 =============================================================================== 4088 4089 idPlat 4090 4091 =============================================================================== 4092 */ 4093 4094 CLASS_DECLARATION( idMover_Binary, idPlat ) 4095 EVENT( EV_Touch, idPlat::Event_Touch ) 4096 EVENT( EV_TeamBlocked, idPlat::Event_TeamBlocked ) 4097 EVENT( EV_PartBlocked, idPlat::Event_PartBlocked ) 4098 END_CLASS 4099 4100 /* 4101 =============== 4102 idPlat::idPlat 4103 =============== 4104 */ 4105 idPlat::idPlat() { 4106 trigger = NULL; 4107 localTriggerOrigin.Zero(); 4108 localTriggerAxis.Identity(); 4109 } 4110 4111 /* 4112 =============== 4113 idPlat::~idPlat 4114 =============== 4115 */ 4116 idPlat::~idPlat() { 4117 if ( trigger ) { 4118 delete trigger; 4119 } 4120 } 4121 4122 /* 4123 =============== 4124 idPlat::Save 4125 =============== 4126 */ 4127 void idPlat::Save( idSaveGame *savefile ) const { 4128 savefile->WriteClipModel( trigger ); 4129 savefile->WriteVec3( localTriggerOrigin ); 4130 savefile->WriteMat3( localTriggerAxis ); 4131 } 4132 4133 /* 4134 =============== 4135 idPlat::Restore 4136 =============== 4137 */ 4138 void idPlat::Restore( idRestoreGame *savefile ) { 4139 savefile->ReadClipModel( trigger ); 4140 savefile->ReadVec3( localTriggerOrigin ); 4141 savefile->ReadMat3( localTriggerAxis ); 4142 } 4143 4144 /* 4145 =============== 4146 idPlat::Spawn 4147 =============== 4148 */ 4149 void idPlat::Spawn() { 4150 float lip; 4151 float height; 4152 float time; 4153 float speed; 4154 float accel; 4155 float decel; 4156 bool noTouch; 4157 4158 spawnArgs.GetFloat( "speed", "100", speed ); 4159 spawnArgs.GetFloat( "damage", "0", damage ); 4160 spawnArgs.GetFloat( "wait", "1", wait ); 4161 spawnArgs.GetFloat( "lip", "8", lip ); 4162 spawnArgs.GetFloat( "accel_time", "0.25", accel ); 4163 spawnArgs.GetFloat( "decel_time", "0.25", decel ); 4164 4165 // create second position 4166 if ( !spawnArgs.GetFloat( "height", "0", height ) ) { 4167 height = ( GetPhysics()->GetBounds()[1][2] - GetPhysics()->GetBounds()[0][2] ) - lip; 4168 } 4169 4170 spawnArgs.GetBool( "no_touch", "0", noTouch ); 4171 4172 // pos1 is the rest (bottom) position, pos2 is the top 4173 pos2 = GetPhysics()->GetOrigin(); 4174 pos1 = pos2; 4175 pos1[2] -= height; 4176 4177 if ( spawnArgs.GetFloat( "time", "1", time ) ) { 4178 InitTime( pos1, pos2, time, accel, decel ); 4179 } else { 4180 InitSpeed( pos1, pos2, speed, accel, decel ); 4181 } 4182 4183 SetMoverState( MOVER_POS1, gameLocal.slow.time ); 4184 UpdateVisuals(); 4185 4186 // spawn the trigger if one hasn't been custom made 4187 if ( !noTouch ) { 4188 // spawn trigger 4189 SpawnPlatTrigger( pos1 ); 4190 } 4191 } 4192 4193 /* 4194 ================ 4195 idPlat::RunPhysics_NoBlocking 4196 ================ 4197 */ 4198 void idPlat::RunPhysics_NoBlocking() { 4199 int i, startTime, endTime; 4200 idEntity * part = NULL, *blockedPart = NULL, *blockingEntity = NULL; 4201 trace_t results; 4202 bool moved; 4203 4204 // don't run physics if not enabled 4205 if ( !( thinkFlags & TH_PHYSICS ) ) { 4206 // however do update any animation controllers 4207 if ( UpdateAnimationControllers() ) { 4208 BecomeActive( TH_ANIMATE ); 4209 } 4210 return; 4211 } 4212 4213 /* 4214 // if this entity is a team slave don't do anything because the team master will handle everything 4215 if ( teamMaster && teamMaster != this ) { 4216 return false; 4217 } 4218 */ 4219 startTime = gameLocal.previousTime; 4220 endTime = gameLocal.time; 4221 4222 gameLocal.push.InitSavingPushedEntityPositions(); 4223 blockedPart = NULL; 4224 4225 // save the physics state of the whole team and disable the team for collision detection 4226 for ( part = this; part != NULL; part = part->GetTeamChain() ) { 4227 if ( part->GetPhysics() ) { 4228 if ( !part->fl.solidForTeam ) { 4229 part->GetPhysics()->DisableClip(); 4230 } 4231 part->GetPhysics()->SaveState(); 4232 } 4233 } 4234 4235 4236 // move the whole team 4237 for ( part = this; part != NULL; part = part->GetTeamChain() ) { 4238 4239 if ( part->GetPhysics() ) { 4240 4241 // run physics 4242 moved = part->GetPhysics()->Evaluate( endTime - startTime, endTime ); 4243 4244 // check if the object is blocked 4245 blockingEntity = part->GetPhysics()->GetBlockingEntity(); 4246 if ( blockingEntity ) { 4247 blockedPart = part; 4248 break; 4249 } 4250 4251 // if moved or forced to update the visual position and orientation from the physics 4252 if ( moved || part->fl.forcePhysicsUpdate ) { 4253 part->UpdateVisuals(); 4254 } 4255 4256 // update any animation controllers here so an entity bound 4257 // to a joint of this entity gets the correct position 4258 if ( part->UpdateAnimationControllers() ) { 4259 part->BecomeActive( TH_ANIMATE ); 4260 } 4261 } 4262 } 4263 4264 // enable the whole team for collision detection 4265 for ( part = this; part != NULL; part = part->GetTeamChain() ) { 4266 if ( part->GetPhysics() ) { 4267 if ( !part->fl.solidForTeam ) { 4268 part->GetPhysics()->EnableClip(); 4269 } 4270 } 4271 } 4272 4273 // set pushed 4274 for ( i = 0; i < gameLocal.push.GetNumPushedEntities(); i++ ) { 4275 idEntity *ent = gameLocal.push.GetPushedEntity( i ); 4276 ent->GetPhysics()->SetPushed( endTime - startTime ); 4277 } 4278 } 4279 4280 /* 4281 ================ 4282 idPlat::ClientThink 4283 ================ 4284 */ 4285 void idPlat::ClientThink( const int curTime, const float fraction, const bool predict ) { 4286 InterpolatePhysicsOnly( fraction ); 4287 4288 Present(); 4289 4290 4291 4292 //idMover_Binary::ClientThink( curTime, fraction, predict ); 4293 4294 /* 4295 idVec3 masterOrigin; 4296 idMat3 masterAxis; 4297 4298 // Dont bother with blocking entities on clients.. host tells us our move state. 4299 RunPhysics_NoBlocking(); 4300 4301 Present(); 4302 4303 if ( thinkFlags & TH_PHYSICS ) { 4304 // update trigger position 4305 if ( GetMasterPosition( masterOrigin, masterAxis ) ) { 4306 if ( trigger ) { 4307 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis ); 4308 } 4309 } 4310 } 4311 */ 4312 } 4313 4314 /* 4315 ================ 4316 idPlat::Think 4317 ================ 4318 */ 4319 void idPlat::Think() { 4320 idVec3 masterOrigin; 4321 idMat3 masterAxis; 4322 4323 idMover_Binary::Think(); 4324 4325 if ( thinkFlags & TH_PHYSICS ) { 4326 // update trigger position 4327 if ( GetMasterPosition( masterOrigin, masterAxis ) ) { 4328 if ( trigger ) { 4329 trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis ); 4330 } 4331 } 4332 } 4333 } 4334 4335 /* 4336 ================ 4337 idPlat::PreBind 4338 ================ 4339 */ 4340 void idPlat::PreBind() { 4341 idMover_Binary::PreBind(); 4342 } 4343 4344 /* 4345 ================ 4346 idPlat::PostBind 4347 ================ 4348 */ 4349 void idPlat::PostBind() { 4350 idMover_Binary::PostBind(); 4351 GetLocalTriggerPosition( trigger ); 4352 } 4353 4354 /* 4355 ================ 4356 idPlat::GetLocalTriggerPosition 4357 ================ 4358 */ 4359 void idPlat::GetLocalTriggerPosition( const idClipModel *trigger ) { 4360 idVec3 origin; 4361 idMat3 axis; 4362 4363 if ( !trigger ) { 4364 return; 4365 } 4366 4367 GetMasterPosition( origin, axis ); 4368 localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose(); 4369 localTriggerAxis = trigger->GetAxis() * axis.Transpose(); 4370 } 4371 4372 /* 4373 ============== 4374 idPlat::SpawnPlatTrigger 4375 =============== 4376 */ 4377 void idPlat::SpawnPlatTrigger( idVec3 &pos ) { 4378 idBounds bounds; 4379 idVec3 tmin; 4380 idVec3 tmax; 4381 4382 // the middle trigger will be a thin trigger just 4383 // above the starting position 4384 4385 bounds = GetPhysics()->GetBounds(); 4386 4387 tmin[0] = bounds[0][0] + 33; 4388 tmin[1] = bounds[0][1] + 33; 4389 tmin[2] = bounds[0][2]; 4390 4391 tmax[0] = bounds[1][0] - 33; 4392 tmax[1] = bounds[1][1] - 33; 4393 tmax[2] = bounds[1][2] + 8; 4394 4395 if ( tmax[0] <= tmin[0] ) { 4396 tmin[0] = ( bounds[0][0] + bounds[1][0] ) * 0.5f; 4397 tmax[0] = tmin[0] + 1; 4398 } 4399 if ( tmax[1] <= tmin[1] ) { 4400 tmin[1] = ( bounds[0][1] + bounds[1][1] ) * 0.5f; 4401 tmax[1] = tmin[1] + 1; 4402 } 4403 4404 trigger = new (TAG_PHYSICS_CLIP_MOVER) idClipModel( idTraceModel( idBounds( tmin, tmax ) ) ); 4405 trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity ); 4406 trigger->SetContents( CONTENTS_TRIGGER ); 4407 } 4408 4409 /* 4410 ============== 4411 idPlat::Event_Touch 4412 =============== 4413 */ 4414 void idPlat::Event_Touch( idEntity *other, trace_t *trace ) { 4415 if ( common->IsClient() ) { 4416 return; 4417 } 4418 4419 if ( !other->IsType( idPlayer::Type ) ) { 4420 return; 4421 } 4422 4423 if ( ( GetMoverState() == MOVER_POS1 ) && trigger && ( trace->c.id == trigger->GetId() ) && ( other->health > 0 ) ) { 4424 Use_BinaryMover( other ); 4425 } 4426 } 4427 4428 /* 4429 ================ 4430 idPlat::Event_TeamBlocked 4431 ================ 4432 */ 4433 void idPlat::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) { 4434 // reverse direction 4435 Use_BinaryMover( activatedBy.GetEntity() ); 4436 } 4437 4438 /* 4439 =============== 4440 idPlat::Event_PartBlocked 4441 =============== 4442 */ 4443 void idPlat::Event_PartBlocked( idEntity *blockingEntity ) { 4444 if ( damage > 0.0f ) { 4445 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); 4446 } 4447 } 4448 4449 4450 /* 4451 =============================================================================== 4452 4453 idMover_Periodic 4454 4455 =============================================================================== 4456 */ 4457 4458 CLASS_DECLARATION( idEntity, idMover_Periodic ) 4459 EVENT( EV_TeamBlocked, idMover_Periodic::Event_TeamBlocked ) 4460 EVENT( EV_PartBlocked, idMover_Periodic::Event_PartBlocked ) 4461 END_CLASS 4462 4463 /* 4464 =============== 4465 idMover_Periodic::idMover_Periodic 4466 =============== 4467 */ 4468 idMover_Periodic::idMover_Periodic() { 4469 damage = 0.0f; 4470 fl.neverDormant = false; 4471 } 4472 4473 /* 4474 =============== 4475 idMover_Periodic::Spawn 4476 =============== 4477 */ 4478 void idMover_Periodic::Spawn() { 4479 spawnArgs.GetFloat( "damage", "0", damage ); 4480 if ( !spawnArgs.GetBool( "solid", "1" ) ) { 4481 GetPhysics()->SetContents( 0 ); 4482 } 4483 } 4484 4485 /* 4486 =============== 4487 idMover_Periodic::Save 4488 =============== 4489 */ 4490 void idMover_Periodic::Save( idSaveGame *savefile ) const { 4491 savefile->WriteFloat( damage ); 4492 savefile->WriteStaticObject( physicsObj ); 4493 } 4494 4495 /* 4496 =============== 4497 idMover_Periodic::Restore 4498 =============== 4499 */ 4500 void idMover_Periodic::Restore( idRestoreGame *savefile ) { 4501 savefile->ReadFloat( damage ); 4502 savefile->ReadStaticObject( physicsObj ); 4503 RestorePhysics( &physicsObj ); 4504 } 4505 4506 4507 /* 4508 ================ 4509 idMover_Periodic::Think 4510 ================ 4511 */ 4512 void idMover_Periodic::Think() { 4513 // if we are completely closed off from the player, don't do anything at all 4514 if ( CheckDormant() ) { 4515 return; 4516 } 4517 4518 RunPhysics(); 4519 Present(); 4520 } 4521 4522 /* 4523 =============== 4524 idMover_Periodic::Event_TeamBlocked 4525 =============== 4526 */ 4527 void idMover_Periodic::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) { 4528 } 4529 4530 /* 4531 =============== 4532 idMover_Periodic::Event_PartBlocked 4533 =============== 4534 */ 4535 void idMover_Periodic::Event_PartBlocked( idEntity *blockingEntity ) { 4536 if ( damage > 0.0f ) { 4537 blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); 4538 } 4539 } 4540 4541 /* 4542 ================ 4543 idMover_Periodic::WriteToSnapshot 4544 ================ 4545 */ 4546 void idMover_Periodic::WriteToSnapshot( idBitMsg &msg ) const { 4547 physicsObj.WriteToSnapshot( msg ); 4548 WriteBindToSnapshot( msg ); 4549 } 4550 4551 /* 4552 ================ 4553 idMover_Periodic::ReadFromSnapshot 4554 ================ 4555 */ 4556 void idMover_Periodic::ReadFromSnapshot( const idBitMsg &msg ) { 4557 physicsObj.ReadFromSnapshot( msg ); 4558 ReadBindFromSnapshot( msg ); 4559 4560 if ( msg.HasChanged() ) { 4561 UpdateVisuals(); 4562 } 4563 } 4564 4565 4566 /* 4567 =============================================================================== 4568 4569 idRotater 4570 4571 =============================================================================== 4572 */ 4573 4574 CLASS_DECLARATION( idMover_Periodic, idRotater ) 4575 EVENT( EV_Activate, idRotater::Event_Activate ) 4576 END_CLASS 4577 4578 /* 4579 =============== 4580 idRotater::idRotater 4581 =============== 4582 */ 4583 idRotater::idRotater() { 4584 activatedBy = this; 4585 } 4586 4587 /* 4588 =============== 4589 idRotater::Spawn 4590 =============== 4591 */ 4592 void idRotater::Spawn() { 4593 physicsObj.SetSelf( this ); 4594 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_MOVER) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 4595 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 4596 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 4597 physicsObj.SetClipMask( MASK_SOLID ); 4598 if ( !spawnArgs.GetBool( "nopush" ) ) { 4599 physicsObj.SetPusher( 0 ); 4600 } 4601 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, gameLocal.slow.time, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin ); 4602 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero ); 4603 SetPhysics( &physicsObj ); 4604 4605 if ( spawnArgs.GetBool( "start_on" ) ) { 4606 ProcessEvent( &EV_Activate, this ); 4607 } 4608 } 4609 4610 /* 4611 =============== 4612 idRotater::Save 4613 =============== 4614 */ 4615 void idRotater::Save( idSaveGame *savefile ) const { 4616 activatedBy.Save( savefile ); 4617 } 4618 4619 /* 4620 =============== 4621 idRotater::Restore 4622 =============== 4623 */ 4624 void idRotater::Restore( idRestoreGame *savefile ) { 4625 activatedBy.Restore( savefile ); 4626 } 4627 4628 /* 4629 =============== 4630 idRotater::Event_Activate 4631 =============== 4632 */ 4633 void idRotater::Event_Activate( idEntity *activator ) { 4634 float speed; 4635 bool x_axis; 4636 bool y_axis; 4637 idAngles delta; 4638 4639 activatedBy = activator; 4640 4641 delta.Zero(); 4642 4643 if ( !spawnArgs.GetBool( "rotate" ) ) { 4644 spawnArgs.Set( "rotate", "1" ); 4645 spawnArgs.GetFloat( "speed", "100", speed ); 4646 spawnArgs.GetBool( "x_axis", "0", x_axis ); 4647 spawnArgs.GetBool( "y_axis", "0", y_axis ); 4648 4649 // set the axis of rotation 4650 if ( x_axis ) { 4651 delta[2] = speed; 4652 } else if ( y_axis ) { 4653 delta[0] = speed; 4654 } else { 4655 delta[1] = speed; 4656 } 4657 } else { 4658 spawnArgs.Set( "rotate", "0" ); 4659 } 4660 4661 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, physicsObj.GetAxis().ToAngles(), delta, ang_zero ); 4662 } 4663 4664 4665 /* 4666 =============================================================================== 4667 4668 idBobber 4669 4670 =============================================================================== 4671 */ 4672 4673 CLASS_DECLARATION( idMover_Periodic, idBobber ) 4674 END_CLASS 4675 4676 /* 4677 =============== 4678 idBobber::idBobber 4679 =============== 4680 */ 4681 idBobber::idBobber() { 4682 } 4683 4684 /* 4685 =============== 4686 idBobber::Spawn 4687 =============== 4688 */ 4689 void idBobber::Spawn() { 4690 float speed; 4691 float height; 4692 float phase; 4693 bool x_axis; 4694 bool y_axis; 4695 idVec3 delta; 4696 4697 spawnArgs.GetFloat( "speed", "4", speed ); 4698 spawnArgs.GetFloat( "height", "32", height ); 4699 spawnArgs.GetFloat( "phase", "0", phase ); 4700 spawnArgs.GetBool( "x_axis", "0", x_axis ); 4701 spawnArgs.GetBool( "y_axis", "0", y_axis ); 4702 4703 // set the axis of bobbing 4704 delta = vec3_origin; 4705 if ( x_axis ) { 4706 delta[ 0 ] = height; 4707 } else if ( y_axis ) { 4708 delta[ 1 ] = height; 4709 } else { 4710 delta[ 2 ] = height; 4711 } 4712 4713 physicsObj.SetSelf( this ); 4714 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_MOVER) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 4715 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 4716 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 4717 physicsObj.SetClipMask( MASK_SOLID ); 4718 if ( !spawnArgs.GetBool( "nopush" ) ) { 4719 physicsObj.SetPusher( 0 ); 4720 } 4721 physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, speed * 500, GetPhysics()->GetOrigin(), delta * 2.0f, vec3_origin ); 4722 SetPhysics( &physicsObj ); 4723 } 4724 4725 4726 /* 4727 =============================================================================== 4728 4729 idPendulum 4730 4731 =============================================================================== 4732 */ 4733 4734 CLASS_DECLARATION( idMover_Periodic, idPendulum ) 4735 END_CLASS 4736 4737 /* 4738 =============== 4739 idPendulum::idPendulum 4740 =============== 4741 */ 4742 idPendulum::idPendulum() { 4743 } 4744 4745 /* 4746 =============== 4747 idPendulum::Spawn 4748 =============== 4749 */ 4750 void idPendulum::Spawn() { 4751 float speed; 4752 float freq; 4753 float length; 4754 float phase; 4755 4756 spawnArgs.GetFloat( "speed", "30", speed ); 4757 spawnArgs.GetFloat( "phase", "0", phase ); 4758 4759 if ( spawnArgs.GetFloat( "freq", "", freq ) ) { 4760 if ( freq <= 0.0f ) { 4761 gameLocal.Error( "Invalid frequency on entity '%s'", GetName() ); 4762 } 4763 } else { 4764 // find pendulum length 4765 length = idMath::Fabs( GetPhysics()->GetBounds()[0][2] ); 4766 if ( length < 8 ) { 4767 length = 8; 4768 } 4769 4770 freq = 1 / ( idMath::TWO_PI ) * idMath::Sqrt( g_gravity.GetFloat() / ( 3 * length ) ); 4771 } 4772 4773 physicsObj.SetSelf( this ); 4774 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_MOVER) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 4775 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 4776 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 4777 physicsObj.SetClipMask( MASK_SOLID ); 4778 if ( !spawnArgs.GetBool( "nopush" ) ) { 4779 physicsObj.SetPusher( 0 ); 4780 } 4781 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin ); 4782 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, 500/freq, GetPhysics()->GetAxis().ToAngles(), idAngles( 0, 0, speed * 2.0f ), ang_zero ); 4783 SetPhysics( &physicsObj ); 4784 } 4785 4786 4787 /* 4788 =============================================================================== 4789 4790 idBobber 4791 4792 =============================================================================== 4793 */ 4794 4795 CLASS_DECLARATION( idMover_Periodic, idRiser ) 4796 EVENT( EV_Activate, idRiser::Event_Activate ) 4797 END_CLASS 4798 4799 /* 4800 =============== 4801 idRiser::idRiser 4802 =============== 4803 */ 4804 idRiser::idRiser() { 4805 } 4806 4807 /* 4808 =============== 4809 idRiser::Spawn 4810 =============== 4811 */ 4812 void idRiser::Spawn() { 4813 physicsObj.SetSelf( this ); 4814 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_MOVER) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 4815 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 4816 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 4817 4818 physicsObj.SetClipMask( MASK_SOLID ); 4819 if ( !spawnArgs.GetBool( "solid", "1" ) ) { 4820 physicsObj.SetContents( 0 ); 4821 } 4822 if ( !spawnArgs.GetBool( "nopush" ) ) { 4823 physicsObj.SetPusher( 0 ); 4824 } 4825 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin ); 4826 SetPhysics( &physicsObj ); 4827 } 4828 4829 /* 4830 ================ 4831 idRiser::Event_Activate 4832 ================ 4833 */ 4834 void idRiser::Event_Activate( idEntity *activator ) { 4835 4836 if ( !IsHidden() && spawnArgs.GetBool("hide") ) { 4837 Hide(); 4838 } else { 4839 Show(); 4840 float time; 4841 float height; 4842 idVec3 delta; 4843 4844 spawnArgs.GetFloat( "time", "4", time ); 4845 spawnArgs.GetFloat( "height", "32", height ); 4846 4847 delta = vec3_origin; 4848 delta[ 2 ] = height; 4849 4850 physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, time * 1000, physicsObj.GetOrigin(), delta, vec3_origin ); 4851 } 4852 }