Item.cpp (52175B)
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 35 /* 36 =============================================================================== 37 38 idItem 39 40 =============================================================================== 41 */ 42 43 const idEventDef EV_DropToFloor( "<dropToFloor>" ); 44 const idEventDef EV_RespawnItem( "respawn" ); 45 const idEventDef EV_RespawnFx( "<respawnFx>" ); 46 const idEventDef EV_GetPlayerPos( "<getplayerpos>" ); 47 const idEventDef EV_HideObjective( "<hideobjective>", "e" ); 48 49 CLASS_DECLARATION( idEntity, idItem ) 50 EVENT( EV_DropToFloor, idItem::Event_DropToFloor ) 51 EVENT( EV_Touch, idItem::Event_Touch ) 52 EVENT( EV_Activate, idItem::Event_Trigger ) 53 EVENT( EV_RespawnItem, idItem::Event_Respawn ) 54 EVENT( EV_RespawnFx, idItem::Event_RespawnFx ) 55 END_CLASS 56 57 58 /* 59 ================ 60 idItem::idItem 61 ================ 62 */ 63 idItem::idItem() : 64 clientPredictPickupMilliseconds( 0 ) { 65 spin = false; 66 inView = false; 67 inViewTime = 0; 68 lastCycle = 0; 69 lastRenderViewTime = -1; 70 itemShellHandle = -1; 71 shellMaterial = NULL; 72 orgOrigin.Zero(); 73 canPickUp = true; 74 fl.networkSync = true; 75 } 76 77 /* 78 ================ 79 idItem::~idItem 80 ================ 81 */ 82 idItem::~idItem() { 83 // remove the highlight shell 84 if ( itemShellHandle != -1 ) { 85 gameRenderWorld->FreeEntityDef( itemShellHandle ); 86 } 87 } 88 89 /* 90 ================ 91 idItem::Save 92 ================ 93 */ 94 void idItem::Save( idSaveGame *savefile ) const { 95 96 savefile->WriteVec3( orgOrigin ); 97 savefile->WriteBool( spin ); 98 savefile->WriteBool( pulse ); 99 savefile->WriteBool( canPickUp ); 100 101 savefile->WriteMaterial( shellMaterial ); 102 103 savefile->WriteBool( inView ); 104 savefile->WriteInt( inViewTime ); 105 savefile->WriteInt( lastCycle ); 106 savefile->WriteInt( lastRenderViewTime ); 107 } 108 109 /* 110 ================ 111 idItem::Restore 112 ================ 113 */ 114 void idItem::Restore( idRestoreGame *savefile ) { 115 116 savefile->ReadVec3( orgOrigin ); 117 savefile->ReadBool( spin ); 118 savefile->ReadBool( pulse ); 119 savefile->ReadBool( canPickUp ); 120 121 savefile->ReadMaterial( shellMaterial ); 122 123 savefile->ReadBool( inView ); 124 savefile->ReadInt( inViewTime ); 125 savefile->ReadInt( lastCycle ); 126 savefile->ReadInt( lastRenderViewTime ); 127 128 itemShellHandle = -1; 129 } 130 131 /* 132 ================ 133 idItem::UpdateRenderEntity 134 ================ 135 */ 136 bool idItem::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const { 137 138 if ( lastRenderViewTime == renderView->time[timeGroup] ) { 139 return false; 140 } 141 142 lastRenderViewTime = renderView->time[timeGroup]; 143 144 // check for glow highlighting if near the center of the view 145 idVec3 dir = renderEntity->origin - renderView->vieworg; 146 dir.Normalize(); 147 float d = dir * renderView->viewaxis[0]; 148 149 // two second pulse cycle 150 float cycle = ( renderView->time[timeGroup] - inViewTime ) / 2000.0f; 151 152 if ( d > 0.94f ) { 153 if ( !inView ) { 154 inView = true; 155 if ( cycle > lastCycle ) { 156 // restart at the beginning 157 inViewTime = renderView->time[timeGroup]; 158 cycle = 0.0f; 159 } 160 } 161 } else { 162 if ( inView ) { 163 inView = false; 164 lastCycle = ceil( cycle ); 165 } 166 } 167 168 // fade down after the last pulse finishes 169 if ( !inView && cycle > lastCycle ) { 170 renderEntity->shaderParms[4] = 0.0f; 171 } else { 172 // pulse up in 1/4 second 173 cycle -= (int)cycle; 174 if ( cycle < 0.1f ) { 175 renderEntity->shaderParms[4] = cycle * 10.0f; 176 } else if ( cycle < 0.2f ) { 177 renderEntity->shaderParms[4] = 1.0f; 178 } else if ( cycle < 0.3f ) { 179 renderEntity->shaderParms[4] = 1.0f - ( cycle - 0.2f ) * 10.0f; 180 } else { 181 // stay off between pulses 182 renderEntity->shaderParms[4] = 0.0f; 183 } 184 } 185 186 // update every single time this is in view 187 return true; 188 } 189 190 /* 191 ================ 192 idItem::ModelCallback 193 ================ 194 */ 195 bool idItem::ModelCallback( renderEntity_t *renderEntity, const renderView_t *renderView ) { 196 const idItem *ent; 197 198 // this may be triggered by a model trace or other non-view related source 199 if ( !renderView ) { 200 return false; 201 } 202 203 ent = static_cast<idItem *>(gameLocal.entities[ renderEntity->entityNum ]); 204 if ( ent == NULL ) { 205 gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" ); 206 return false; 207 } 208 209 return ent->UpdateRenderEntity( renderEntity, renderView ); 210 } 211 212 /* 213 ================ 214 idItem::Think 215 ================ 216 */ 217 void idItem::Think() { 218 if ( thinkFlags & TH_THINK ) { 219 if ( spin ) { 220 idAngles ang; 221 idVec3 org; 222 223 ang.pitch = ang.roll = 0.0f; 224 ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f; 225 SetAngles( ang ); 226 227 float scale = 0.005f + entityNumber * 0.00001f; 228 229 org = orgOrigin; 230 org.z += 4.0f + cos( ( gameLocal.time + 2000 ) * scale ) * 4.0f; 231 SetOrigin( org ); 232 } 233 } 234 235 Present(); 236 } 237 238 /* 239 ================ 240 idItem::Present 241 ================ 242 */ 243 void idItem::Present() { 244 idEntity::Present(); 245 246 if ( !fl.hidden && pulse ) { 247 // also add a highlight shell model 248 renderEntity_t shell; 249 250 shell = renderEntity; 251 252 // we will mess with shader parms when the item is in view 253 // to give the "item pulse" effect 254 shell.callback = idItem::ModelCallback; 255 shell.entityNum = entityNumber; 256 shell.customShader = shellMaterial; 257 if ( itemShellHandle == -1 ) { 258 itemShellHandle = gameRenderWorld->AddEntityDef( &shell ); 259 } else { 260 gameRenderWorld->UpdateEntityDef( itemShellHandle, &shell ); 261 } 262 263 } 264 } 265 266 /* 267 ================ 268 idItem::Spawn 269 ================ 270 */ 271 void idItem::Spawn() { 272 idStr giveTo; 273 idEntity * ent; 274 float tsize; 275 276 if ( spawnArgs.GetBool( "dropToFloor" ) ) { 277 PostEventMS( &EV_DropToFloor, 0 ); 278 } 279 280 if ( spawnArgs.GetFloat( "triggersize", "0", tsize ) ) { 281 GetPhysics()->GetClipModel()->LoadModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) ); 282 GetPhysics()->GetClipModel()->Link( gameLocal.clip ); 283 } 284 285 if ( spawnArgs.GetBool( "start_off" ) ) { 286 GetPhysics()->SetContents( 0 ); 287 Hide(); 288 } else { 289 GetPhysics()->SetContents( CONTENTS_TRIGGER ); 290 } 291 292 giveTo = spawnArgs.GetString( "owner" ); 293 if ( giveTo.Length() ) { 294 ent = gameLocal.FindEntity( giveTo ); 295 if ( !ent ) { 296 gameLocal.Error( "Item couldn't find owner '%s'", giveTo.c_str() ); 297 } 298 PostEventMS( &EV_Touch, 0, ent, NULL ); 299 } 300 301 // idItemTeam does not rotate and bob 302 if ( spawnArgs.GetBool( "spin" ) || (common->IsMultiplayer() && !this->IsType( idItemTeam::Type ) ) ) { 303 spin = true; 304 BecomeActive( TH_THINK ); 305 } 306 307 //pulse = !spawnArgs.GetBool( "nopulse" ); 308 //temp hack for tim 309 pulse = false; 310 orgOrigin = GetPhysics()->GetOrigin(); 311 312 canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) ); 313 314 inViewTime = -1000; 315 lastCycle = -1; 316 itemShellHandle = -1; 317 shellMaterial = declManager->FindMaterial( "itemHighlightShell" ); 318 } 319 320 /* 321 ================ 322 idItem::GetAttributes 323 ================ 324 */ 325 void idItem::GetAttributes( idDict &attributes ) const { 326 int i; 327 const idKeyValue *arg; 328 329 for( i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) { 330 arg = spawnArgs.GetKeyVal( i ); 331 if ( arg->GetKey().Left( 4 ) == "inv_" ) { 332 attributes.Set( arg->GetKey().Right( arg->GetKey().Length() - 4 ), arg->GetValue() ); 333 } 334 } 335 } 336 337 /* 338 ================ 339 idItem::GiveToPlayer 340 ================ 341 */ 342 bool idItem::GiveToPlayer( idPlayer *player, unsigned int giveFlags ) { 343 if ( player == NULL ) { 344 return false; 345 } 346 347 if ( spawnArgs.GetBool( "inv_carry" ) ) { 348 return player->GiveInventoryItem( &spawnArgs, giveFlags ); 349 } 350 351 return player->GiveItem( this, giveFlags ); 352 } 353 354 /* 355 ================ 356 idItem::Pickup 357 ================ 358 */ 359 bool idItem::Pickup( idPlayer *player ) { 360 361 const bool didGiveSucceed = GiveToPlayer( player, ITEM_GIVE_FEEDBACK ); 362 if ( !didGiveSucceed ) { 363 return false; 364 } 365 366 // Store the time so clients know when to stop predicting and let snapshots overwrite. 367 if ( player->IsLocallyControlled() ) { 368 clientPredictPickupMilliseconds = gameLocal.time; 369 } else { 370 clientPredictPickupMilliseconds = 0; 371 } 372 373 // play pickup sound 374 StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); 375 376 // clear our contents so the object isn't picked up twice 377 GetPhysics()->SetContents( 0 ); 378 379 // hide the model 380 Hide(); 381 382 // remove the highlight shell 383 if ( itemShellHandle != -1 ) { 384 gameRenderWorld->FreeEntityDef( itemShellHandle ); 385 itemShellHandle = -1; 386 } 387 388 // Clients need to bail out after some feedback, but 389 // before actually changing any values. The values 390 // will be updated in the next snapshot. 391 if ( common->IsClient() ) { 392 return didGiveSucceed; 393 } 394 395 if ( !GiveToPlayer( player, ITEM_GIVE_UPDATE_STATE ) ) { 396 return false; 397 } 398 399 // trigger our targets 400 ActivateTargets( player ); 401 402 float respawn = spawnArgs.GetFloat( "respawn" ); 403 bool dropped = spawnArgs.GetBool( "dropped" ); 404 bool no_respawn = spawnArgs.GetBool( "no_respawn" ); 405 406 if ( common->IsMultiplayer() && respawn == 0.0f ) { 407 respawn = 20.0f; 408 } 409 410 if ( respawn && !dropped && !no_respawn ) { 411 const char *sfx = spawnArgs.GetString( "fxRespawn" ); 412 if ( sfx != NULL && *sfx != NULL ) { 413 PostEventSec( &EV_RespawnFx, respawn - 0.5f ); 414 } 415 PostEventSec( &EV_RespawnItem, respawn ); 416 } else if ( !spawnArgs.GetBool( "inv_objective" ) && !no_respawn ) { 417 // give some time for the pickup sound to play 418 // FIXME: Play on the owner 419 if ( !spawnArgs.GetBool( "inv_carry" ) ) { 420 PostEventMS( &EV_Remove, 5000 ); 421 } 422 } 423 424 BecomeInactive( TH_THINK ); 425 return true; 426 } 427 428 /* 429 ================ 430 idItem::ClientThink 431 ================ 432 */ 433 void idItem::ClientThink( const int curTime, const float fraction, const bool predict ) { 434 435 // only think forward because the state is not synced through snapshots 436 if ( !gameLocal.isNewFrame ) { 437 return; 438 } 439 Think(); 440 } 441 442 443 /* 444 ================ 445 idItem::ClientPredictionThink 446 ================ 447 */ 448 void idItem::ClientPredictionThink() { 449 // only think forward because the state is not synced through snapshots 450 if ( !gameLocal.isNewFrame ) { 451 return; 452 } 453 Think(); 454 } 455 456 /* 457 ================ 458 idItem::WriteFromSnapshot 459 ================ 460 */ 461 void idItem::WriteToSnapshot( idBitMsg &msg ) const { 462 msg.WriteBits( IsHidden(), 1 ); 463 } 464 465 /* 466 ================ 467 idItem::ReadFromSnapshot 468 ================ 469 */ 470 void idItem::ReadFromSnapshot( const idBitMsg &msg ) { 471 if ( msg.ReadBits( 1 ) ) { 472 Hide(); 473 } else if ( clientPredictPickupMilliseconds != 0 ) { 474 // Fix mispredictions 475 if ( gameLocal.GetLastClientUsercmdMilliseconds( gameLocal.GetLocalClientNum() ) >= clientPredictPickupMilliseconds ) { 476 if ( GetPhysics()->GetContents() == 0 ) { 477 GetPhysics()->SetContents( CONTENTS_TRIGGER ); 478 } 479 Show(); 480 } 481 } 482 } 483 484 /* 485 ================ 486 idItem::ClientReceiveEvent 487 ================ 488 */ 489 bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { 490 491 switch( event ) { 492 case EVENT_RESPAWN: { 493 Event_Respawn(); 494 return true; 495 } 496 case EVENT_RESPAWNFX: { 497 Event_RespawnFx(); 498 return true; 499 } 500 default: { 501 return idEntity::ClientReceiveEvent( event, time, msg ); 502 } 503 } 504 } 505 506 /* 507 ================ 508 idItem::Event_DropToFloor 509 ================ 510 */ 511 void idItem::Event_DropToFloor() { 512 trace_t trace; 513 514 // don't drop the floor if bound to another entity 515 if ( GetBindMaster() != NULL && GetBindMaster() != this ) { 516 return; 517 } 518 519 gameLocal.clip.TraceBounds( trace, renderEntity.origin, renderEntity.origin - idVec3( 0, 0, 64 ), renderEntity.bounds, MASK_SOLID | CONTENTS_CORPSE, this ); 520 SetOrigin( trace.endpos ); 521 } 522 523 /* 524 ================ 525 idItem::Event_Touch 526 ================ 527 */ 528 void idItem::Event_Touch( idEntity *other, trace_t *trace ) { 529 if ( !other->IsType( idPlayer::Type ) ) { 530 return; 531 } 532 533 if ( !canPickUp ) { 534 return; 535 } 536 537 Pickup( static_cast<idPlayer *>(other) ); 538 } 539 540 /* 541 ================ 542 idItem::Event_Trigger 543 ================ 544 */ 545 void idItem::Event_Trigger( idEntity *activator ) { 546 547 if ( !canPickUp && spawnArgs.GetBool( "triggerFirst" ) ) { 548 canPickUp = true; 549 return; 550 } 551 552 if ( activator && activator->IsType( idPlayer::Type ) ) { 553 Pickup( static_cast<idPlayer *>( activator ) ); 554 } 555 } 556 557 /* 558 ================ 559 idItem::Event_Respawn 560 ================ 561 */ 562 void idItem::Event_Respawn() { 563 if ( common->IsServer() ) { 564 ServerSendEvent( EVENT_RESPAWN, NULL, false ); 565 } 566 BecomeActive( TH_THINK ); 567 Show(); 568 inViewTime = -1000; 569 lastCycle = -1; 570 GetPhysics()->SetContents( CONTENTS_TRIGGER ); 571 SetOrigin( orgOrigin ); 572 StartSound( "snd_respawn", SND_CHANNEL_ITEM, 0, false, NULL ); 573 CancelEvents( &EV_RespawnItem ); // don't double respawn 574 } 575 576 /* 577 ================ 578 idItem::Event_RespawnFx 579 ================ 580 */ 581 void idItem::Event_RespawnFx() { 582 if ( common->IsServer() ) { 583 ServerSendEvent( EVENT_RESPAWNFX, NULL, false ); 584 } 585 const char *sfx = spawnArgs.GetString( "fxRespawn" ); 586 if ( sfx != NULL && *sfx != NULL ) { 587 idEntityFx::StartFx( sfx, NULL, NULL, this, true ); 588 } 589 } 590 591 /* 592 =============================================================================== 593 594 idItemPowerup 595 596 =============================================================================== 597 */ 598 599 /* 600 =============== 601 idItemPowerup 602 =============== 603 */ 604 605 CLASS_DECLARATION( idItem, idItemPowerup ) 606 END_CLASS 607 608 /* 609 ================ 610 idItemPowerup::idItemPowerup 611 ================ 612 */ 613 idItemPowerup::idItemPowerup() { 614 time = 0; 615 type = 0; 616 } 617 618 /* 619 ================ 620 idItemPowerup::Save 621 ================ 622 */ 623 void idItemPowerup::Save( idSaveGame *savefile ) const { 624 savefile->WriteInt( time ); 625 savefile->WriteInt( type ); 626 } 627 628 /* 629 ================ 630 idItemPowerup::Restore 631 ================ 632 */ 633 void idItemPowerup::Restore( idRestoreGame *savefile ) { 634 savefile->ReadInt( time ); 635 savefile->ReadInt( type ); 636 } 637 638 /* 639 ================ 640 idItemPowerup::Spawn 641 ================ 642 */ 643 void idItemPowerup::Spawn() { 644 time = spawnArgs.GetInt( "time", "30" ); 645 type = spawnArgs.GetInt( "type", "0" ); 646 } 647 648 /* 649 ================ 650 idItemPowerup::GiveToPlayer 651 ================ 652 */ 653 bool idItemPowerup::GiveToPlayer( idPlayer *player, unsigned int giveFlags ) { 654 if ( player->spectating ) { 655 return false; 656 } 657 player->GivePowerUp( type, time * 1000, giveFlags ); 658 return true; 659 } 660 661 /* 662 =============================================================================== 663 664 idItemTeam 665 666 Used for flags in Capture the Flag 667 668 =============================================================================== 669 */ 670 671 // temporarely removed these events 672 673 const idEventDef EV_FlagReturn( "flagreturn", "e" ); 674 const idEventDef EV_TakeFlag( "takeflag", "e" ); 675 const idEventDef EV_DropFlag( "dropflag", "d" ); 676 const idEventDef EV_FlagCapture( "flagcapture" ); 677 678 CLASS_DECLARATION( idItem, idItemTeam ) 679 EVENT( EV_FlagReturn, idItemTeam::Event_FlagReturn ) 680 EVENT( EV_TakeFlag, idItemTeam::Event_TakeFlag ) 681 EVENT( EV_DropFlag, idItemTeam::Event_DropFlag ) 682 EVENT( EV_FlagCapture, idItemTeam::Event_FlagCapture ) 683 END_CLASS 684 685 /* 686 =============== 687 idItemTeam::idItemTeam 688 =============== 689 */ 690 idItemTeam::idItemTeam() { 691 team = -1; 692 carried = false; 693 dropped = false; 694 lastDrop = 0; 695 696 itemGlowHandle = -1; 697 698 skinDefault = NULL; 699 skinCarried = NULL; 700 701 scriptTaken = NULL; 702 scriptDropped = NULL; 703 scriptReturned = NULL; 704 scriptCaptured = NULL; 705 706 lastNuggetDrop = 0; 707 nuggetName = 0; 708 } 709 710 /* 711 =============== 712 idItemTeam::~idItemTeam 713 =============== 714 */ 715 idItemTeam::~idItemTeam() { 716 FreeLightDef(); 717 } 718 /* 719 =============== 720 idItemTeam::Spawn 721 =============== 722 */ 723 void idItemTeam::Spawn() { 724 team = spawnArgs.GetInt( "team" ); 725 returnOrigin = GetPhysics()->GetOrigin() + idVec3( 0, 0, 20 ); 726 returnAxis = GetPhysics()->GetAxis(); 727 728 BecomeActive( TH_THINK ); 729 730 const char * skinName; 731 skinName = spawnArgs.GetString( "skin", "" ); 732 if ( skinName[0] ) 733 skinDefault = declManager->FindSkin( skinName ); 734 735 skinName = spawnArgs.GetString( "skin_carried", "" ); 736 if ( skinName[0] ) 737 skinCarried = declManager->FindSkin( skinName ); 738 739 nuggetName = spawnArgs.GetString( "nugget_name", "" ); 740 if ( !nuggetName[0] ) { 741 nuggetName = NULL; 742 } 743 744 scriptTaken = LoadScript( "script_taken" ); 745 scriptDropped = LoadScript( "script_dropped" ); 746 scriptReturned = LoadScript( "script_returned" ); 747 scriptCaptured = LoadScript( "script_captured" ); 748 749 /* Spawn attached dlight */ 750 /* 751 idDict args; 752 idVec3 lightOffset( 0.0f, 20.0f, 0.0f ); 753 754 // Set up the flag's dynamic light 755 memset( &itemGlow, 0, sizeof( itemGlow ) ); 756 itemGlow.axis = mat3_identity; 757 itemGlow.lightRadius.x = 128.0f; 758 itemGlow.lightRadius.y = itemGlow.lightRadius.z = itemGlow.lightRadius.x; 759 itemGlow.noShadows = true; 760 itemGlow.pointLight = true; 761 itemGlow.shaderParms[ SHADERPARM_RED ] = 0.0f; 762 itemGlow.shaderParms[ SHADERPARM_GREEN ] = 0.0f; 763 itemGlow.shaderParms[ SHADERPARM_BLUE ] = 0.0f; 764 itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 0.0f; 765 766 // Select a shader based on the team 767 if ( team == 0 ) 768 itemGlow.shader = declManager->FindMaterial( "lights/redflag" ); 769 else 770 itemGlow.shader = declManager->FindMaterial( "lights/blueflag" ); 771 */ 772 773 idMoveableItem::Spawn(); 774 775 physicsObj.SetContents( 0 ); 776 physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); 777 physicsObj.SetGravity( idVec3( 0, 0, spawnArgs.GetInt("gravity", "-30" ) ) ); 778 } 779 780 781 /* 782 =============== 783 idItemTeam::LoadScript 784 =============== 785 */ 786 function_t * idItemTeam::LoadScript( char * script ) { 787 function_t * function = NULL; 788 idStr funcname = spawnArgs.GetString( script, "" ); 789 if ( funcname.Length() ) { 790 function = gameLocal.program.FindFunction( funcname ); 791 if ( function == NULL ) { 792 #ifdef _DEBUG 793 gameLocal.Warning( "idItemTeam '%s' at (%s) calls unknown function '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcname.c_str() ); 794 #endif 795 } 796 } 797 return function; 798 } 799 800 801 /* 802 =============== 803 idItemTeam::Think 804 =============== 805 */ 806 void idItemTeam::Think() { 807 idMoveableItem::Think(); 808 809 TouchTriggers(); 810 811 // TODO : only update on updatevisuals 812 /*idVec3 offset( 0.0f, 0.0f, 20.0f ); 813 itemGlow.origin = GetPhysics()->GetOrigin() + offset; 814 if ( itemGlowHandle == -1 ) { 815 itemGlowHandle = gameRenderWorld->AddLightDef( &itemGlow ); 816 } else { 817 gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow ); 818 }*/ 819 820 #if 1 821 // should only the server do this? 822 if ( common->IsServer() && nuggetName && carried && ( !lastNuggetDrop || (gameLocal.time - lastNuggetDrop) > spawnArgs.GetInt("nugget_frequency") ) ) { 823 824 SpawnNugget( GetPhysics()->GetOrigin() ); 825 lastNuggetDrop = gameLocal.time; 826 } 827 #endif 828 829 // return dropped flag after si_flagDropTimeLimit seconds 830 if ( dropped && !carried && lastDrop != 0 && (gameLocal.time - lastDrop) > ( si_flagDropTimeLimit.GetInteger()*1000 ) ) { 831 832 Return(); // return flag after 30 seconds on ground 833 return; 834 } 835 } 836 837 /* 838 =============== 839 idItemTeam::Pickup 840 =============== 841 */ 842 bool idItemTeam::Pickup( idPlayer *player ) { 843 if ( !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */ 844 return false; 845 846 if ( gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP || 847 gameLocal.mpGame.GetGameState() == idMultiplayerGame::COUNTDOWN ) 848 return false; 849 850 // wait 2 seconds after drop before beeing picked up again 851 if ( lastDrop != 0 && (gameLocal.time - lastDrop) < spawnArgs.GetInt("pickupDelay", "500") ) 852 return false; 853 854 if ( carried == false && player->team != this->team ) { 855 856 PostEventMS( &EV_TakeFlag, 0, player ); 857 858 return true; 859 } else if ( carried == false && dropped == true && player->team == this->team ) { 860 861 gameLocal.mpGame.PlayerScoreCTF( player->entityNumber, 5 ); 862 863 // return flag 864 PostEventMS( &EV_FlagReturn, 0, player ); 865 866 return false; 867 } 868 869 return false; 870 } 871 872 /* 873 =============== 874 idItemTeam::ClientReceiveEvent 875 =============== 876 */ 877 bool idItemTeam::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { 878 gameLocal.DPrintf("ClientRecieveEvent: %i\n", event ); 879 880 switch ( event ) { 881 case EVENT_TAKEFLAG: { 882 idPlayer * player = static_cast<idPlayer *>(gameLocal.entities[ msg.ReadBits( GENTITYNUM_BITS ) ]); 883 if ( player == NULL ) { 884 gameLocal.Warning( "NULL player takes flag?\n" ); 885 return false; 886 } 887 888 Event_TakeFlag( player ); 889 } 890 return true; 891 892 case EVENT_DROPFLAG : { 893 bool death = bool( msg.ReadBits( 1 ) == 1 ); 894 Event_DropFlag( death ); 895 } 896 return true; 897 898 case EVENT_FLAGRETURN : { 899 Hide(); 900 901 FreeModelDef(); 902 FreeLightDef(); 903 904 Event_FlagReturn(); 905 } 906 return true; 907 908 case EVENT_FLAGCAPTURE : { 909 Hide(); 910 911 FreeModelDef(); 912 FreeLightDef(); 913 914 Event_FlagCapture(); 915 } 916 return true; 917 }; 918 919 return false; 920 } 921 922 /* 923 ================ 924 idItemTeam::Drop 925 ================ 926 */ 927 void idItemTeam::Drop( bool death ) 928 { 929 // PostEventMS( &EV_DropFlag, 0, int(death == true) ); 930 // had to remove the delayed drop because of drop flag on disconnect 931 Event_DropFlag( death ); 932 } 933 934 /* 935 ================ 936 idItemTeam::Return 937 ================ 938 */ 939 void idItemTeam::Return( idPlayer * player ) 940 { 941 if ( team != 0 && team != 1 ) 942 return; 943 944 // PostEventMS( &EV_FlagReturn, 0 ); 945 Event_FlagReturn(); 946 } 947 948 /* 949 ================ 950 idItemTeam::Capture 951 ================ 952 */ 953 void idItemTeam::Capture() 954 { 955 if ( team != 0 && team != 1 ) 956 return; 957 958 PostEventMS( &EV_FlagCapture, 0 ); 959 } 960 961 /* 962 ================ 963 idItemTeam::PrivateReturn 964 ================ 965 */ 966 void idItemTeam::PrivateReturn() 967 { 968 Unbind(); 969 970 if ( common->IsServer() && carried && !dropped ) { 971 int playerIdx = gameLocal.mpGame.GetFlagCarrier( 1-team ); 972 if ( playerIdx != -1 ) { 973 idPlayer * player = static_cast<idPlayer*>( gameLocal.entities[ playerIdx ] ); 974 player->carryingFlag = false; 975 } else { 976 gameLocal.Warning( "BUG: carried flag has no carrier before return" ); 977 } 978 } 979 980 dropped = false; 981 carried = false; 982 983 SetOrigin( returnOrigin ); 984 SetAxis( returnAxis ); 985 986 trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity ); 987 988 SetSkin( skinDefault ); 989 990 // Turn off the light 991 /*itemGlow.shaderParms[ SHADERPARM_RED ] = 0.0f; 992 itemGlow.shaderParms[ SHADERPARM_GREEN ] = 0.0f; 993 itemGlow.shaderParms[ SHADERPARM_BLUE ] = 0.0f; 994 itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 0.0f; 995 996 if ( itemGlowHandle != -1 ) 997 gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );*/ 998 999 GetPhysics()->SetLinearVelocity( idVec3(0, 0, 0) ); 1000 GetPhysics()->SetAngularVelocity( idVec3(0, 0, 0) ); 1001 } 1002 1003 /* 1004 ================ 1005 idItemTeam::Event_TakeFlag 1006 ================ 1007 */ 1008 void idItemTeam::Event_TakeFlag( idPlayer * player ) { 1009 gameLocal.DPrintf("Event_TakeFlag()!\n"); 1010 1011 assert( player != NULL ); 1012 1013 if ( player->carryingFlag ) { 1014 // Don't do anything if the player is already carrying the flag. 1015 // Prevents duplicate messages. 1016 return; 1017 } 1018 1019 if ( common->IsServer() ) { 1020 idBitMsg msg; 1021 byte msgBuf[MAX_EVENT_PARAM_SIZE]; 1022 // Send the event 1023 msg.InitWrite( msgBuf, sizeof( msgBuf ) ); 1024 msg.BeginWriting(); 1025 msg.WriteBits( player->entityNumber, GENTITYNUM_BITS ); 1026 ServerSendEvent( EVENT_TAKEFLAG, &msg, false ); 1027 1028 gameLocal.mpGame.PlayTeamSound( player->team, SND_FLAG_TAKEN_THEIRS ); 1029 gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_TAKEN_YOURS ); 1030 1031 gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGTAKEN, team, player->entityNumber ); 1032 1033 // dont drop a nugget RIGHT away 1034 lastNuggetDrop = gameLocal.time - gameLocal.random.RandomInt( 1000 ); 1035 1036 } 1037 1038 BindToJoint( player, g_flagAttachJoint.GetString(), true ); 1039 idVec3 origin( g_flagAttachOffsetX.GetFloat(), g_flagAttachOffsetY.GetFloat(), g_flagAttachOffsetZ.GetFloat() ); 1040 idAngles angle( g_flagAttachAngleX.GetFloat(), g_flagAttachAngleY.GetFloat(), g_flagAttachAngleZ.GetFloat() ); 1041 SetAngles( angle ); 1042 SetOrigin( origin ); 1043 1044 // Turn the light on 1045 /*itemGlow.shaderParms[ SHADERPARM_RED ] = 1.0f; 1046 itemGlow.shaderParms[ SHADERPARM_GREEN ] = 1.0f; 1047 itemGlow.shaderParms[ SHADERPARM_BLUE ] = 1.0f; 1048 itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 1.0f; 1049 1050 if ( itemGlowHandle != -1 ) 1051 gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );*/ 1052 1053 if ( scriptTaken ) { 1054 idThread *thread = new idThread(); 1055 thread->CallFunction( scriptTaken, false ); 1056 thread->DelayedStart( 0 ); 1057 } 1058 1059 dropped = false; 1060 carried = true; 1061 player->carryingFlag = true; 1062 1063 SetSkin( skinCarried ); 1064 1065 UpdateVisuals(); 1066 UpdateGuis(); 1067 1068 if ( common->IsServer() ) { 1069 if ( team == 0 ) 1070 gameLocal.mpGame.player_red_flag = player->entityNumber; 1071 else 1072 gameLocal.mpGame.player_blue_flag = player->entityNumber; 1073 } 1074 } 1075 1076 /* 1077 ================ 1078 idItemTeam::Event_DropFlag 1079 ================ 1080 */ 1081 void idItemTeam::Event_DropFlag( bool death ) { 1082 gameLocal.DPrintf("Event_DropFlag()!\n"); 1083 1084 if ( common->IsServer() ) { 1085 idBitMsg msg; 1086 byte msgBuf[MAX_EVENT_PARAM_SIZE]; 1087 // Send the event 1088 msg.InitWrite( msgBuf, sizeof( msgBuf ) ); 1089 msg.BeginWriting(); 1090 msg.WriteBits( death, 1 ); 1091 ServerSendEvent( EVENT_DROPFLAG, &msg, false ); 1092 1093 if ( gameLocal.mpGame.IsFlagMsgOn() ) { 1094 gameLocal.mpGame.PlayTeamSound( 1-team, SND_FLAG_DROPPED_THEIRS ); 1095 gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_DROPPED_YOURS ); 1096 1097 gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGDROP, team ); 1098 } 1099 } 1100 1101 lastDrop = gameLocal.time; 1102 1103 BecomeActive( TH_THINK ); 1104 Show(); 1105 1106 if ( death ) 1107 GetPhysics()->SetLinearVelocity( idVec3(0, 0, 0) ); 1108 else 1109 GetPhysics()->SetLinearVelocity( idVec3(0, 0, 20) ); 1110 1111 GetPhysics()->SetAngularVelocity( idVec3(0, 0, 0) ); 1112 1113 // GetPhysics()->SetLinearVelocity( ( GetPhysics()->GetLinearVelocity() * GetBindMaster()->GetPhysics()->GetAxis() ) + GetBindMaster()->GetPhysics()->GetLinearVelocity() ); 1114 1115 if ( GetBindMaster() ) { 1116 const idBounds bounds = GetPhysics()->GetBounds(); 1117 idVec3 origin = GetBindMaster()->GetPhysics()->GetOrigin() + idVec3(0, 0, ( bounds[1].z-bounds[0].z )*0.6f ); 1118 1119 Unbind(); 1120 1121 SetOrigin( origin ); 1122 } 1123 1124 idAngles angle = GetPhysics()->GetAxis().ToAngles(); 1125 angle.roll = 0; 1126 angle.pitch = 0; 1127 SetAxis( angle.ToMat3() ); 1128 1129 dropped = true; 1130 carried = false; 1131 1132 if ( scriptDropped ) { 1133 idThread *thread = new idThread(); 1134 thread->CallFunction( scriptDropped, false ); 1135 thread->DelayedStart( 0 ); 1136 } 1137 1138 SetSkin( skinDefault ); 1139 UpdateVisuals(); 1140 UpdateGuis(); 1141 1142 1143 if ( common->IsServer() ) { 1144 if ( team == 0 ) 1145 gameLocal.mpGame.player_red_flag = -1; 1146 else 1147 gameLocal.mpGame.player_blue_flag = -1; 1148 1149 } 1150 } 1151 1152 /* 1153 ================ 1154 idItemTeam::Event_FlagReturn 1155 ================ 1156 */ 1157 void idItemTeam::Event_FlagReturn( idPlayer * player ) { 1158 gameLocal.DPrintf("Event_FlagReturn()!\n"); 1159 1160 if ( common->IsServer() ) { 1161 ServerSendEvent( EVENT_FLAGRETURN, NULL, false ); 1162 1163 if ( gameLocal.mpGame.IsFlagMsgOn() ) { 1164 gameLocal.mpGame.PlayTeamSound( 1-team, SND_FLAG_RETURN ); 1165 gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_RETURN ); 1166 1167 int entitynum = 255; 1168 if ( player ) { 1169 entitynum = player->entityNumber; 1170 } 1171 1172 gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGRETURN, team, entitynum ); 1173 } 1174 } 1175 1176 BecomeActive( TH_THINK ); 1177 Show(); 1178 1179 PrivateReturn(); 1180 1181 if ( scriptReturned ) { 1182 idThread *thread = new idThread(); 1183 thread->CallFunction( scriptReturned, false ); 1184 thread->DelayedStart( 0 ); 1185 } 1186 1187 UpdateVisuals(); 1188 UpdateGuis(); 1189 // Present(); 1190 1191 if ( common->IsServer() ) { 1192 if ( team == 0 ) 1193 gameLocal.mpGame.player_red_flag = -1; 1194 else 1195 gameLocal.mpGame.player_blue_flag = -1; 1196 } 1197 } 1198 1199 /* 1200 ================ 1201 idItemTeam::Event_FlagCapture 1202 ================ 1203 */ 1204 void idItemTeam::Event_FlagCapture() { 1205 gameLocal.DPrintf("Event_FlagCapture()!\n"); 1206 1207 if ( common->IsServer() ) { 1208 int playerIdx = gameLocal.mpGame.GetFlagCarrier( 1-team ); 1209 if ( playerIdx != -1 ) { 1210 ServerSendEvent( EVENT_FLAGCAPTURE, NULL, false ); 1211 1212 gameLocal.mpGame.PlayTeamSound( 1-team, SND_FLAG_CAPTURED_THEIRS ); 1213 gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_CAPTURED_YOURS ); 1214 1215 gameLocal.mpGame.TeamScoreCTF( 1-team, 1 ); 1216 1217 gameLocal.mpGame.PlayerScoreCTF( playerIdx, 10 ); 1218 1219 gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGCAPTURE, team, playerIdx ); 1220 } else { 1221 playerIdx = 255; 1222 } 1223 } 1224 1225 BecomeActive( TH_THINK ); 1226 Show(); 1227 1228 PrivateReturn(); 1229 1230 if ( scriptCaptured ) { 1231 idThread *thread = new idThread(); 1232 thread->CallFunction( scriptCaptured, false ); 1233 thread->DelayedStart( 0 ); 1234 } 1235 1236 UpdateVisuals(); 1237 UpdateGuis(); 1238 1239 1240 if ( common->IsServer() ) { 1241 if ( team == 0 ) 1242 gameLocal.mpGame.player_red_flag = -1; 1243 else 1244 gameLocal.mpGame.player_blue_flag = -1; 1245 } 1246 1247 } 1248 1249 /* 1250 ================ 1251 idItemTeam::FreeLightDef 1252 ================ 1253 */ 1254 void idItemTeam::FreeLightDef() { 1255 if ( itemGlowHandle != -1 ) { 1256 gameRenderWorld->FreeLightDef( itemGlowHandle ); 1257 itemGlowHandle = -1; 1258 } 1259 } 1260 1261 /* 1262 ================ 1263 idItemTeam::SpawnNugget 1264 ================ 1265 */ 1266 void idItemTeam::SpawnNugget( idVec3 pos ) { 1267 1268 idAngles angle( gameLocal.random.RandomInt(spawnArgs.GetInt("nugget_pitch", "30")), gameLocal.random.RandomInt(spawnArgs.GetInt("nugget_yaw", "360" )), 0 ); 1269 float velocity = float(gameLocal.random.RandomInt( 40 )+15); 1270 1271 velocity *= spawnArgs.GetFloat("nugget_velocity", "1" ); 1272 1273 idEntity * ent = idMoveableItem::DropItem( nuggetName, pos, GetPhysics()->GetAxis(), angle.ToMat3()*idVec3(velocity, velocity, velocity), 0, spawnArgs.GetInt("nugget_removedelay") ); 1274 idPhysics_RigidBody * physics = static_cast<idPhysics_RigidBody *>( ent->GetPhysics() ); 1275 1276 if ( physics != NULL && physics->IsType( idPhysics_RigidBody::Type ) ) { 1277 physics->DisableImpact(); 1278 } 1279 } 1280 1281 1282 1283 /* 1284 ================ 1285 idItemTeam::Event_FlagCapture 1286 ================ 1287 */ 1288 void idItemTeam::WriteToSnapshot( idBitMsg &msg ) const { 1289 msg.WriteBits( carried, 1 ); 1290 msg.WriteBits( dropped, 1 ); 1291 1292 WriteBindToSnapshot( msg ); 1293 1294 idMoveableItem::WriteToSnapshot( msg ); 1295 } 1296 1297 1298 /* 1299 ================ 1300 idItemTeam::ReadFromSnapshot 1301 ================ 1302 */ 1303 void idItemTeam::ReadFromSnapshot( const idBitMsg &msg ) { 1304 carried = msg.ReadBits( 1 ) == 1; 1305 dropped = msg.ReadBits( 1 ) == 1; 1306 1307 ReadBindFromSnapshot( msg ); 1308 1309 if ( msg.HasChanged() ) 1310 { 1311 UpdateGuis(); 1312 1313 if ( carried == true ) 1314 SetSkin( skinCarried ); 1315 else 1316 SetSkin( skinDefault ); 1317 } 1318 1319 idMoveableItem::ReadFromSnapshot( msg ); 1320 } 1321 1322 /* 1323 ================ 1324 idItemTeam::UpdateGuis 1325 1326 Update all client's huds wrt the flag status. 1327 ================ 1328 */ 1329 void idItemTeam::UpdateGuis() { 1330 idPlayer *player; 1331 1332 for ( int i = 0; i < gameLocal.numClients; i++ ) { 1333 player = static_cast<idPlayer *>( gameLocal.entities[ i ] ); 1334 1335 if ( player && player->hud ) { 1336 1337 player->hud->SetFlagState( 0, gameLocal.mpGame.GetFlagStatus( 0 ) ); 1338 player->hud->SetFlagState( 1, gameLocal.mpGame.GetFlagStatus( 1 ) ); 1339 1340 player->hud->SetTeamScore( 0, gameLocal.mpGame.GetFlagPoints( 0 ) ); 1341 player->hud->SetTeamScore( 1, gameLocal.mpGame.GetFlagPoints( 1 ) ); 1342 } 1343 } 1344 } 1345 1346 /* 1347 ================ 1348 idItemTeam::Present 1349 ================ 1350 */ 1351 void idItemTeam::Present() { 1352 // hide the flag for localplayer if in first person 1353 if ( carried && GetBindMaster() ) { 1354 idPlayer * player = static_cast<idPlayer *>( GetBindMaster() ); 1355 if ( player == gameLocal.GetLocalPlayer() && !pm_thirdPerson.GetBool() ) { 1356 FreeModelDef(); 1357 BecomeActive( TH_UPDATEVISUALS ); 1358 return; 1359 } 1360 } 1361 1362 idEntity::Present(); 1363 } 1364 1365 /* 1366 =============================================================================== 1367 1368 idObjective 1369 1370 =============================================================================== 1371 */ 1372 1373 CLASS_DECLARATION( idItem, idObjective ) 1374 EVENT( EV_Activate, idObjective::Event_Trigger ) 1375 EVENT( EV_HideObjective, idObjective::Event_HideObjective ) 1376 EVENT( EV_GetPlayerPos, idObjective::Event_GetPlayerPos ) 1377 END_CLASS 1378 1379 /* 1380 ================ 1381 idObjective::idObjective 1382 ================ 1383 */ 1384 idObjective::idObjective() { 1385 playerPos.Zero(); 1386 } 1387 1388 /* 1389 ================ 1390 idObjective::Save 1391 ================ 1392 */ 1393 void idObjective::Save( idSaveGame *savefile ) const { 1394 savefile->WriteVec3( playerPos ); 1395 savefile->WriteMaterial( screenshot ); 1396 } 1397 1398 /* 1399 ================ 1400 idObjective::Restore 1401 ================ 1402 */ 1403 void idObjective::Restore( idRestoreGame *savefile ) { 1404 savefile->ReadVec3( playerPos ); 1405 savefile->ReadMaterial( screenshot ); 1406 } 1407 1408 /* 1409 ================ 1410 idObjective::Spawn 1411 ================ 1412 */ 1413 void idObjective::Spawn() { 1414 Hide(); 1415 idStr shotName; 1416 shotName = gameLocal.GetMapName(); 1417 shotName.StripFileExtension(); 1418 shotName += "/"; 1419 shotName += spawnArgs.GetString( "screenshot" ); 1420 shotName.SetFileExtension( ".tga" ); 1421 screenshot = declManager->FindMaterial( shotName ); 1422 } 1423 1424 /* 1425 ================ 1426 idObjective::Event_Trigger 1427 ================ 1428 */ 1429 void idObjective::Event_Trigger( idEntity *activator ) { 1430 idPlayer *player = gameLocal.GetLocalPlayer(); 1431 if ( player ) { 1432 1433 //Pickup( player ); 1434 1435 if ( spawnArgs.GetString( "inv_objective", NULL ) ) { 1436 if ( player ) { 1437 player->GiveObjective( spawnArgs.GetString( "objectivetitle" ), spawnArgs.GetString( "objectivetext" ), screenshot ); 1438 1439 // a tad slow but keeps from having to update all objectives in all maps with a name ptr 1440 for( int i = 0; i < gameLocal.num_entities; i++ ) { 1441 if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idObjectiveComplete::Type ) ) { 1442 if ( idStr::Icmp( spawnArgs.GetString( "objectivetitle" ), gameLocal.entities[ i ]->spawnArgs.GetString( "objectivetitle" ) ) == 0 ){ 1443 gameLocal.entities[ i ]->spawnArgs.SetBool( "objEnabled", true ); 1444 break; 1445 } 1446 } 1447 } 1448 1449 PostEventMS( &EV_GetPlayerPos, 2000 ); 1450 } 1451 } 1452 } 1453 } 1454 1455 /* 1456 ================ 1457 idObjective::Event_GetPlayerPos 1458 ================ 1459 */ 1460 void idObjective::Event_GetPlayerPos() { 1461 idPlayer *player = gameLocal.GetLocalPlayer(); 1462 if ( player ) { 1463 playerPos = player->GetPhysics()->GetOrigin(); 1464 PostEventMS( &EV_HideObjective, 100, player ); 1465 } 1466 } 1467 1468 /* 1469 ================ 1470 idObjective::Event_HideObjective 1471 ================ 1472 */ 1473 void idObjective::Event_HideObjective(idEntity *e) { 1474 idPlayer *player = gameLocal.GetLocalPlayer(); 1475 if ( player ) { 1476 idVec3 v = player->GetPhysics()->GetOrigin() - playerPos; 1477 if ( v.Length() > 64.0f ) { 1478 player->HideObjective(); 1479 PostEventMS( &EV_Remove, 0 ); 1480 } else { 1481 PostEventMS( &EV_HideObjective, 100, player ); 1482 } 1483 } 1484 } 1485 1486 /* 1487 =============================================================================== 1488 1489 idVideoCDItem 1490 1491 =============================================================================== 1492 */ 1493 1494 CLASS_DECLARATION( idItem, idVideoCDItem ) 1495 END_CLASS 1496 1497 /* 1498 ================ 1499 idVideoCDItem::GiveToPlayer 1500 ================ 1501 */ 1502 bool idVideoCDItem::GiveToPlayer( idPlayer * player, unsigned int giveFlags ) { 1503 if ( player == NULL ) { 1504 return false; 1505 } 1506 const idDeclVideo * video = static_cast<const idDeclVideo * >( declManager->FindType( DECL_VIDEO, spawnArgs.GetString( "video" ), false ) ); 1507 if ( video == NULL ) { 1508 return false; 1509 } 1510 if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) { 1511 player->GiveVideo( video, spawnArgs.GetString( "inv_name" ) ); 1512 } 1513 return true; 1514 } 1515 1516 /* 1517 =============================================================================== 1518 1519 idPDAItem 1520 1521 =============================================================================== 1522 */ 1523 1524 CLASS_DECLARATION( idItem, idPDAItem ) 1525 END_CLASS 1526 1527 /* 1528 ================ 1529 idPDAItem::GiveToPlayer 1530 ================ 1531 */ 1532 bool idPDAItem::GiveToPlayer( idPlayer *player, unsigned int giveFlags ) { 1533 if ( player == NULL ) { 1534 return false; 1535 } 1536 const char * pdaName = spawnArgs.GetString( "pda_name" ); 1537 const char * invName = spawnArgs.GetString( "inv_name" ); 1538 const idDeclPDA * pda = NULL; 1539 if ( pdaName != NULL && pdaName[0] != 0 ) { 1540 // An empty PDA name is legitimate, it means the personal PDA 1541 // But if the PDA name is not empty, it should be valid 1542 pda = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, pdaName, false ) ); 1543 if ( pda == NULL ) { 1544 idLib::Warning( "PDA Item '%s' references unknown PDA %s", GetName(), pdaName ); 1545 return false; 1546 } 1547 } 1548 if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) { 1549 player->GivePDA( pda, invName ); 1550 } 1551 return true; 1552 } 1553 1554 /* 1555 =============================================================================== 1556 1557 idMoveableItem 1558 1559 =============================================================================== 1560 */ 1561 1562 CLASS_DECLARATION( idItem, idMoveableItem ) 1563 EVENT( EV_DropToFloor, idMoveableItem::Event_DropToFloor ) 1564 EVENT( EV_Gib, idMoveableItem::Event_Gib ) 1565 END_CLASS 1566 1567 /* 1568 ================ 1569 idMoveableItem::idMoveableItem 1570 ================ 1571 */ 1572 idMoveableItem::idMoveableItem() { 1573 trigger = NULL; 1574 smoke = NULL; 1575 smokeTime = 0; 1576 nextSoundTime = 0; 1577 repeatSmoke = false; 1578 } 1579 1580 /* 1581 ================ 1582 idMoveableItem::~idMoveableItem 1583 ================ 1584 */ 1585 idMoveableItem::~idMoveableItem() { 1586 if ( trigger ) { 1587 delete trigger; 1588 } 1589 } 1590 1591 /* 1592 ================ 1593 idMoveableItem::Save 1594 ================ 1595 */ 1596 void idMoveableItem::Save( idSaveGame *savefile ) const { 1597 savefile->WriteStaticObject( physicsObj ); 1598 1599 savefile->WriteClipModel( trigger ); 1600 1601 savefile->WriteParticle( smoke ); 1602 savefile->WriteInt( smokeTime ); 1603 savefile->WriteInt( nextSoundTime ); 1604 } 1605 1606 /* 1607 ================ 1608 idMoveableItem::Restore 1609 ================ 1610 */ 1611 void idMoveableItem::Restore( idRestoreGame *savefile ) { 1612 savefile->ReadStaticObject( physicsObj ); 1613 RestorePhysics( &physicsObj ); 1614 1615 savefile->ReadClipModel( trigger ); 1616 1617 savefile->ReadParticle( smoke ); 1618 savefile->ReadInt( smokeTime ); 1619 savefile->ReadInt( nextSoundTime ); 1620 } 1621 1622 /* 1623 ================ 1624 idMoveableItem::Spawn 1625 ================ 1626 */ 1627 void idMoveableItem::Spawn() { 1628 idTraceModel trm; 1629 float density, friction, bouncyness, tsize; 1630 idStr clipModelName; 1631 idBounds bounds; 1632 SetTimeState ts( timeGroup ); 1633 1634 // create a trigger for item pickup 1635 spawnArgs.GetFloat( "triggersize", "16.0", tsize ); 1636 trigger = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) ); 1637 trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ); 1638 trigger->SetContents( CONTENTS_TRIGGER ); 1639 1640 // check if a clip model is set 1641 spawnArgs.GetString( "clipmodel", "", clipModelName ); 1642 if ( !clipModelName[0] ) { 1643 clipModelName = spawnArgs.GetString( "model" ); // use the visual model 1644 } 1645 1646 // load the trace model 1647 if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) { 1648 gameLocal.Error( "idMoveableItem '%s': cannot load collision model %s", name.c_str(), clipModelName.c_str() ); 1649 return; 1650 } 1651 1652 // if the model should be shrinked 1653 if ( spawnArgs.GetBool( "clipshrink" ) ) { 1654 trm.Shrink( CM_CLIP_EPSILON ); 1655 } 1656 1657 // get rigid body properties 1658 spawnArgs.GetFloat( "density", "0.5", density ); 1659 density = idMath::ClampFloat( 0.001f, 1000.0f, density ); 1660 spawnArgs.GetFloat( "friction", "0.05", friction ); 1661 friction = idMath::ClampFloat( 0.0f, 1.0f, friction ); 1662 spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness ); 1663 bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness ); 1664 1665 // setup the physics 1666 physicsObj.SetSelf( this ); 1667 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( trm ), density ); 1668 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 1669 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 1670 physicsObj.SetBouncyness( bouncyness ); 1671 physicsObj.SetFriction( 0.6f, 0.6f, friction ); 1672 physicsObj.SetGravity( gameLocal.GetGravity() ); 1673 physicsObj.SetContents( CONTENTS_RENDERMODEL ); 1674 physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); 1675 SetPhysics( &physicsObj ); 1676 1677 if ( spawnArgs.GetBool( "nodrop" ) ) { 1678 physicsObj.PutToRest(); 1679 } 1680 1681 smoke = NULL; 1682 smokeTime = 0; 1683 nextSoundTime = 0; 1684 const char *smokeName = spawnArgs.GetString( "smoke_trail" ); 1685 if ( *smokeName != '\0' ) { 1686 smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) ); 1687 smokeTime = gameLocal.time; 1688 BecomeActive( TH_UPDATEPARTICLES ); 1689 } 1690 1691 repeatSmoke = spawnArgs.GetBool( "repeatSmoke", "0" ); 1692 } 1693 1694 /* 1695 ================ 1696 idItem::ClientThink 1697 ================ 1698 */ 1699 void idMoveableItem::ClientThink( const int curTime, const float fraction, const bool predict ) { 1700 1701 InterpolatePhysicsOnly( fraction ); 1702 1703 if ( thinkFlags & TH_PHYSICS ) { 1704 // update trigger position 1705 trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity ); 1706 } 1707 1708 Present(); 1709 } 1710 1711 /* 1712 ================ 1713 idMoveableItem::Think 1714 ================ 1715 */ 1716 void idMoveableItem::Think() { 1717 1718 RunPhysics(); 1719 1720 if ( thinkFlags & TH_PHYSICS ) { 1721 // update trigger position 1722 trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity ); 1723 } 1724 1725 if ( thinkFlags & TH_UPDATEPARTICLES ) { 1726 if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) { 1727 if ( !repeatSmoke ) { 1728 smokeTime = 0; 1729 BecomeInactive( TH_UPDATEPARTICLES ); 1730 } else { 1731 smokeTime = gameLocal.time; 1732 } 1733 } 1734 } 1735 1736 Present(); 1737 } 1738 1739 /* 1740 ================= 1741 idMoveableItem::Collide 1742 ================= 1743 */ 1744 bool idMoveableItem::Collide( const trace_t &collision, const idVec3 &velocity ) { 1745 float v, f; 1746 1747 v = -( velocity * collision.c.normal ); 1748 if ( v > 80 && gameLocal.time > nextSoundTime ) { 1749 f = v > 200 ? 1.0f : idMath::Sqrt( v - 80 ) * 0.091f; 1750 if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) { 1751 // don't set the volume unless there is a bounce sound as it overrides the entire channel 1752 // which causes footsteps on ai's to not honor their shader parms 1753 SetSoundVolume( f ); 1754 } 1755 nextSoundTime = gameLocal.time + 500; 1756 } 1757 1758 return false; 1759 } 1760 1761 /* 1762 ================ 1763 idMoveableItem::Pickup 1764 ================ 1765 */ 1766 bool idMoveableItem::Pickup( idPlayer *player ) { 1767 bool ret = idItem::Pickup( player ); 1768 if ( ret ) { 1769 trigger->SetContents( 0 ); 1770 } 1771 return ret; 1772 } 1773 1774 /* 1775 ================ 1776 idMoveableItem::DropItem 1777 ================ 1778 */ 1779 idEntity *idMoveableItem::DropItem( const char *classname, const idVec3 &origin, const idMat3 &axis, const idVec3 &velocity, int activateDelay, int removeDelay ) { 1780 idDict args; 1781 idEntity *item; 1782 1783 args.Set( "classname", classname ); 1784 args.Set( "dropped", "1" ); 1785 1786 // we sometimes drop idMoveables here, so set 'nodrop' to 1 so that it doesn't get put on the floor 1787 args.Set( "nodrop", "1" ); 1788 1789 if ( activateDelay ) { 1790 args.SetBool( "triggerFirst", true ); 1791 } 1792 1793 gameLocal.SpawnEntityDef( args, &item ); 1794 if ( item ) { 1795 // set item position 1796 item->GetPhysics()->SetOrigin( origin ); 1797 item->GetPhysics()->SetAxis( axis ); 1798 item->GetPhysics()->SetLinearVelocity( velocity ); 1799 item->UpdateVisuals(); 1800 if ( activateDelay ) { 1801 item->PostEventMS( &EV_Activate, activateDelay, item ); 1802 } 1803 if ( !removeDelay ) { 1804 removeDelay = 5 * 60 * 1000; 1805 } 1806 // always remove a dropped item after 5 minutes in case it dropped to an unreachable location 1807 item->PostEventMS( &EV_Remove, removeDelay ); 1808 } 1809 return item; 1810 } 1811 1812 /* 1813 ================ 1814 idMoveableItem::DropItems 1815 1816 The entity should have the following key/value pairs set: 1817 "def_drop<type>Item" "item def" 1818 "drop<type>ItemJoint" "joint name" 1819 "drop<type>ItemRotation" "pitch yaw roll" 1820 "drop<type>ItemOffset" "x y z" 1821 "skin_drop<type>" "skin name" 1822 To drop multiple items the following key/value pairs can be used: 1823 "def_drop<type>Item<X>" "item def" 1824 "drop<type>Item<X>Joint" "joint name" 1825 "drop<type>Item<X>Rotation" "pitch yaw roll" 1826 "drop<type>Item<X>Offset" "x y z" 1827 where <X> is an aribtrary string. 1828 ================ 1829 */ 1830 void idMoveableItem::DropItems( idAnimatedEntity *ent, const char *type, idList<idEntity *> *list ) { 1831 const idKeyValue *kv; 1832 const char *skinName, *c, *jointName; 1833 idStr key, key2; 1834 idVec3 origin; 1835 idMat3 axis; 1836 idAngles angles; 1837 const idDeclSkin *skin; 1838 jointHandle_t joint; 1839 idEntity *item; 1840 1841 // drop all items 1842 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), NULL ); 1843 while ( kv ) { 1844 1845 c = kv->GetKey().c_str() + kv->GetKey().Length(); 1846 if ( idStr::Icmp( c - 5, "Joint" ) != 0 && idStr::Icmp( c - 8, "Rotation" ) != 0 ) { 1847 1848 key = kv->GetKey().c_str() + 4; 1849 key2 = key; 1850 key += "Joint"; 1851 key2 += "Offset"; 1852 jointName = ent->spawnArgs.GetString( key ); 1853 joint = ent->GetAnimator()->GetJointHandle( jointName ); 1854 if ( !ent->GetJointWorldTransform( joint, gameLocal.time, origin, axis ) ) { 1855 gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", key.c_str(), jointName, ent->name.c_str() ); 1856 origin = ent->GetPhysics()->GetOrigin(); 1857 axis = ent->GetPhysics()->GetAxis(); 1858 } 1859 if ( g_dropItemRotation.GetString()[0] ) { 1860 angles.Zero(); 1861 sscanf( g_dropItemRotation.GetString(), "%f %f %f", &angles.pitch, &angles.yaw, &angles.roll ); 1862 } else { 1863 key = kv->GetKey().c_str() + 4; 1864 key += "Rotation"; 1865 ent->spawnArgs.GetAngles( key, "0 0 0", angles ); 1866 } 1867 axis = angles.ToMat3() * axis; 1868 1869 origin += ent->spawnArgs.GetVector( key2, "0 0 0" ); 1870 1871 item = DropItem( kv->GetValue(), origin, axis, vec3_origin, 0, 0 ); 1872 if ( list && item ) { 1873 list->Append( item ); 1874 } 1875 } 1876 1877 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv ); 1878 } 1879 1880 // change the skin to hide all items 1881 skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) ); 1882 if ( skinName[0] ) { 1883 skin = declManager->FindSkin( skinName ); 1884 ent->SetSkin( skin ); 1885 } 1886 } 1887 1888 /* 1889 ====================== 1890 idMoveableItem::WriteToSnapshot 1891 ====================== 1892 */ 1893 void idMoveableItem::WriteToSnapshot( idBitMsg &msg ) const { 1894 physicsObj.WriteToSnapshot( msg ); 1895 msg.WriteBool( IsHidden() ); 1896 } 1897 1898 /* 1899 ====================== 1900 idMoveableItem::ReadFromSnapshot 1901 ====================== 1902 */ 1903 void idMoveableItem::ReadFromSnapshot( const idBitMsg &msg ) { 1904 physicsObj.ReadFromSnapshot( msg ); 1905 const bool snapshotHidden = msg.ReadBool(); 1906 1907 if ( snapshotHidden ) { 1908 Hide(); 1909 } else if ( GetPredictPickupMilliseconds() != 0 ) { 1910 if ( gameLocal.GetLastClientUsercmdMilliseconds( gameLocal.GetLocalClientNum() ) >= GetPredictPickupMilliseconds() ) { 1911 if ( trigger->GetContents() == 0 ) { 1912 trigger->SetContents( CONTENTS_TRIGGER ); 1913 } 1914 Show(); 1915 } 1916 } 1917 if ( msg.HasChanged() ) { 1918 UpdateVisuals(); 1919 } 1920 } 1921 1922 /* 1923 ============ 1924 idMoveableItem::Gib 1925 ============ 1926 */ 1927 void idMoveableItem::Gib( const idVec3 &dir, const char *damageDefName ) { 1928 // spawn smoke puff 1929 const char *smokeName = spawnArgs.GetString( "smoke_gib" ); 1930 if ( *smokeName != '\0' ) { 1931 const idDeclParticle *smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) ); 1932 gameLocal.smokeParticles->EmitSmoke( smoke, gameLocal.time, gameLocal.random.CRandomFloat(), renderEntity.origin, renderEntity.axis, timeGroup /*_D3XP*/ ); 1933 } 1934 // remove the entity 1935 PostEventMS( &EV_Remove, 0 ); 1936 } 1937 1938 /* 1939 ================ 1940 idMoveableItem::Event_DropToFloor 1941 ================ 1942 */ 1943 void idMoveableItem::Event_DropToFloor() { 1944 // the physics will drop the moveable to the floor 1945 } 1946 1947 /* 1948 ============ 1949 idMoveableItem::Event_Gib 1950 ============ 1951 */ 1952 void idMoveableItem::Event_Gib( const char *damageDefName ) { 1953 Gib( idVec3( 0, 0, 1 ), damageDefName ); 1954 } 1955 1956 /* 1957 =============================================================================== 1958 1959 idMoveablePDAItem 1960 1961 =============================================================================== 1962 */ 1963 1964 CLASS_DECLARATION( idMoveableItem, idMoveablePDAItem ) 1965 END_CLASS 1966 1967 /* 1968 ================ 1969 idMoveablePDAItem::GiveToPlayer 1970 ================ 1971 */ 1972 bool idMoveablePDAItem::GiveToPlayer( idPlayer * player, unsigned int giveFlags ) { 1973 if ( player == NULL ) { 1974 return false; 1975 } 1976 const char * pdaName = spawnArgs.GetString( "pda_name" ); 1977 const char * invName = spawnArgs.GetString( "inv_name" ); 1978 const idDeclPDA * pda = NULL; 1979 if ( pdaName != NULL && pdaName[0] != 0 ) { 1980 // An empty PDA name is legitimate, it means the personal PDA 1981 // But if the PDA name is not empty, it should be valid 1982 pda = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, pdaName, false ) ); 1983 if ( pda == NULL ) { 1984 idLib::Warning( "PDA Item '%s' references unknown PDA %s", GetName(), pdaName ); 1985 return false; 1986 } 1987 } 1988 if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) { 1989 player->GivePDA( pda, invName ); 1990 } 1991 return true; 1992 } 1993 1994 /* 1995 =============================================================================== 1996 1997 idItemRemover 1998 1999 =============================================================================== 2000 */ 2001 2002 CLASS_DECLARATION( idEntity, idItemRemover ) 2003 EVENT( EV_Activate, idItemRemover::Event_Trigger ) 2004 END_CLASS 2005 2006 /* 2007 ================ 2008 idItemRemover::Spawn 2009 ================ 2010 */ 2011 void idItemRemover::Spawn() { 2012 } 2013 2014 /* 2015 ================ 2016 idItemRemover::RemoveItem 2017 ================ 2018 */ 2019 void idItemRemover::RemoveItem( idPlayer *player ) { 2020 const char *remove; 2021 2022 remove = spawnArgs.GetString( "remove" ); 2023 player->RemoveInventoryItem( remove ); 2024 } 2025 2026 /* 2027 ================ 2028 idItemRemover::Event_Trigger 2029 ================ 2030 */ 2031 void idItemRemover::Event_Trigger( idEntity *activator ) { 2032 if ( activator->IsType( idPlayer::Type ) ) { 2033 RemoveItem( static_cast<idPlayer *>(activator) ); 2034 } 2035 } 2036 2037 /* 2038 =============================================================================== 2039 2040 idObjectiveComplete 2041 2042 =============================================================================== 2043 */ 2044 2045 CLASS_DECLARATION( idItemRemover, idObjectiveComplete ) 2046 EVENT( EV_Activate, idObjectiveComplete::Event_Trigger ) 2047 EVENT( EV_HideObjective, idObjectiveComplete::Event_HideObjective ) 2048 EVENT( EV_GetPlayerPos, idObjectiveComplete::Event_GetPlayerPos ) 2049 END_CLASS 2050 2051 /* 2052 ================ 2053 idObjectiveComplete::idObjectiveComplete 2054 ================ 2055 */ 2056 idObjectiveComplete::idObjectiveComplete() { 2057 playerPos.Zero(); 2058 } 2059 2060 /* 2061 ================ 2062 idObjectiveComplete::Save 2063 ================ 2064 */ 2065 void idObjectiveComplete::Save( idSaveGame *savefile ) const { 2066 savefile->WriteVec3( playerPos ); 2067 } 2068 2069 /* 2070 ================ 2071 idObjectiveComplete::Restore 2072 ================ 2073 */ 2074 void idObjectiveComplete::Restore( idRestoreGame *savefile ) { 2075 savefile->ReadVec3( playerPos ); 2076 } 2077 2078 /* 2079 ================ 2080 idObjectiveComplete::Spawn 2081 ================ 2082 */ 2083 void idObjectiveComplete::Spawn() { 2084 spawnArgs.SetBool( "objEnabled", false ); 2085 Hide(); 2086 } 2087 2088 /* 2089 ================ 2090 idObjectiveComplete::Event_Trigger 2091 ================ 2092 */ 2093 void idObjectiveComplete::Event_Trigger( idEntity *activator ) { 2094 if ( !spawnArgs.GetBool( "objEnabled" ) ) { 2095 return; 2096 } 2097 idPlayer *player = gameLocal.GetLocalPlayer(); 2098 if ( player ) { 2099 RemoveItem( player ); 2100 2101 if ( spawnArgs.GetString( "inv_objective", NULL ) ) { 2102 player->CompleteObjective( spawnArgs.GetString( "objectivetitle" ) ); 2103 PostEventMS( &EV_GetPlayerPos, 2000 ); 2104 } 2105 } 2106 } 2107 2108 /* 2109 ================ 2110 idObjectiveComplete::Event_GetPlayerPos 2111 ================ 2112 */ 2113 void idObjectiveComplete::Event_GetPlayerPos() { 2114 idPlayer *player = gameLocal.GetLocalPlayer(); 2115 if ( player ) { 2116 playerPos = player->GetPhysics()->GetOrigin(); 2117 PostEventMS( &EV_HideObjective, 100, player ); 2118 } 2119 } 2120 2121 /* 2122 ================ 2123 idObjectiveComplete::Event_HideObjective 2124 ================ 2125 */ 2126 void idObjectiveComplete::Event_HideObjective( idEntity *e ) { 2127 idPlayer *player = gameLocal.GetLocalPlayer(); 2128 if ( player ) { 2129 idVec3 v = player->GetPhysics()->GetOrigin(); 2130 v -= playerPos; 2131 if ( v.Length() > 64.0f ) { 2132 player->HideObjective(); 2133 PostEventMS( &EV_Remove, 0 ); 2134 } else { 2135 PostEventMS( &EV_HideObjective, 100, player ); 2136 } 2137 } 2138 }