DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }