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