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