]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/game/Item.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / game / Item.cpp
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 "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Game_local.h"
33
34
35 /*
36 ===============================================================================
37
38   idItem
39
40 ===============================================================================
41 */
42
43 const idEventDef EV_DropToFloor( "<dropToFloor>" );
44 const idEventDef EV_RespawnItem( "respawn" );
45 const idEventDef EV_RespawnFx( "<respawnFx>" );
46 const idEventDef EV_GetPlayerPos( "<getplayerpos>" );
47 const idEventDef EV_HideObjective( "<hideobjective>", "e" );
48 const idEventDef EV_CamShot( "<camshot>" );
49
50 CLASS_DECLARATION( idEntity, idItem )
51         EVENT( EV_DropToFloor,          idItem::Event_DropToFloor )
52         EVENT( EV_Touch,                        idItem::Event_Touch )
53         EVENT( EV_Activate,                     idItem::Event_Trigger )
54         EVENT( EV_RespawnItem,          idItem::Event_Respawn )
55         EVENT( EV_RespawnFx,            idItem::Event_RespawnFx )
56 END_CLASS
57
58
59 /*
60 ================
61 idItem::idItem
62 ================
63 */
64 idItem::idItem() {
65         spin = false;
66         inView = false;
67         inViewTime = 0;
68         lastCycle = 0;
69         lastRenderViewTime = -1;
70         itemShellHandle = -1;
71         shellMaterial = NULL;
72         orgOrigin.Zero();
73         canPickUp = true;
74         fl.networkSync = true;
75 }
76
77 /*
78 ================
79 idItem::~idItem
80 ================
81 */
82 idItem::~idItem() {
83         // remove the highlight shell
84         if ( itemShellHandle != -1 ) {
85                 gameRenderWorld->FreeEntityDef( itemShellHandle );
86         }
87 }
88
89 /*
90 ================
91 idItem::Save
92 ================
93 */
94 void idItem::Save( idSaveGame *savefile ) const {
95
96         savefile->WriteVec3( orgOrigin );
97         savefile->WriteBool( spin );
98         savefile->WriteBool( pulse );
99         savefile->WriteBool( canPickUp );
100
101         savefile->WriteMaterial( shellMaterial );
102
103         savefile->WriteBool( inView );
104         savefile->WriteInt( inViewTime );
105         savefile->WriteInt( lastCycle );
106         savefile->WriteInt( lastRenderViewTime );
107 }
108
109 /*
110 ================
111 idItem::Restore
112 ================
113 */
114 void idItem::Restore( idRestoreGame *savefile ) {
115
116         savefile->ReadVec3( orgOrigin );
117         savefile->ReadBool( spin );
118         savefile->ReadBool( pulse );
119         savefile->ReadBool( canPickUp );
120
121         savefile->ReadMaterial( shellMaterial );
122
123         savefile->ReadBool( inView );
124         savefile->ReadInt( inViewTime );
125         savefile->ReadInt( lastCycle );
126         savefile->ReadInt( lastRenderViewTime );
127
128         itemShellHandle = -1;
129 }
130
131 /*
132 ================
133 idItem::UpdateRenderEntity
134 ================
135 */
136 bool idItem::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
137
138         if ( lastRenderViewTime == renderView->time ) {
139                 return false;
140         }
141
142         lastRenderViewTime = renderView->time;
143
144         // check for glow highlighting if near the center of the view
145         idVec3 dir = renderEntity->origin - renderView->vieworg;
146         dir.Normalize();
147         float d = dir * renderView->viewaxis[0];
148
149         // two second pulse cycle
150         float cycle = ( renderView->time - inViewTime ) / 2000.0f;
151
152         if ( d > 0.94f ) {
153                 if ( !inView ) {
154                         inView = true;
155                         if ( cycle > lastCycle ) {
156                                 // restart at the beginning
157                                 inViewTime = renderView->time;
158                                 cycle = 0.0f;
159                         }
160                 }
161         } else {
162                 if ( inView ) {
163                         inView = false;
164                         lastCycle = ceil( cycle );
165                 }
166         }
167
168         // fade down after the last pulse finishes 
169         if ( !inView && cycle > lastCycle ) {
170                 renderEntity->shaderParms[4] = 0.0f;
171         } else {
172                 // pulse up in 1/4 second
173                 cycle -= (int)cycle;
174                 if ( cycle < 0.1f ) {
175                         renderEntity->shaderParms[4] = cycle * 10.0f;
176                 } else if ( cycle < 0.2f ) {
177                         renderEntity->shaderParms[4] = 1.0f;
178                 } else if ( cycle < 0.3f ) {
179                         renderEntity->shaderParms[4] = 1.0f - ( cycle - 0.2f ) * 10.0f;
180                 } else {
181                         // stay off between pulses
182                         renderEntity->shaderParms[4] = 0.0f;
183                 }
184         }
185
186         // update every single time this is in view
187         return true;
188 }
189
190 /*
191 ================
192 idItem::ModelCallback
193 ================
194 */
195 bool idItem::ModelCallback( renderEntity_t *renderEntity, const renderView_t *renderView ) {
196         const idItem *ent;
197
198         // this may be triggered by a model trace or other non-view related source
199         if ( !renderView ) {
200                 return false;
201         }
202
203         ent = static_cast<idItem *>(gameLocal.entities[ renderEntity->entityNum ]);
204         if ( !ent ) {
205                 gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" );
206         }
207
208         return ent->UpdateRenderEntity( renderEntity, renderView );
209 }
210
211 /*
212 ================
213 idItem::Think
214 ================
215 */
216 void idItem::Think( void ) {
217         if ( thinkFlags & TH_THINK ) {
218                 if ( spin ) {
219                         idAngles        ang;
220                         idVec3          org;
221
222                         ang.pitch = ang.roll = 0.0f;
223                         ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f;
224                         SetAngles( ang );
225
226                         float scale = 0.005f + entityNumber * 0.00001f;
227                         
228                         org = orgOrigin;
229                         org.z += 4.0f + cos( ( gameLocal.time + 2000 ) * scale ) * 4.0f;
230                         SetOrigin( org );
231                 }
232         }
233
234         Present();
235 }
236
237 /*
238 ================
239 idItem::Present
240 ================
241 */
242 void idItem::Present( void ) {
243         idEntity::Present();
244
245         if ( !fl.hidden && pulse ) {
246                 // also add a highlight shell model
247                 renderEntity_t  shell;
248
249                 shell = renderEntity;
250
251                 // we will mess with shader parms when the item is in view
252                 // to give the "item pulse" effect
253                 shell.callback = idItem::ModelCallback;
254                 shell.entityNum = entityNumber;
255                 shell.customShader = shellMaterial;
256                 if ( itemShellHandle == -1 ) {
257                         itemShellHandle = gameRenderWorld->AddEntityDef( &shell );
258                 } else {
259                         gameRenderWorld->UpdateEntityDef( itemShellHandle, &shell );
260                 }
261
262         }
263 }
264
265 /*
266 ================
267 idItem::Spawn
268 ================
269 */
270 void idItem::Spawn( void ) {
271         idStr           giveTo;
272         idEntity *      ent;
273         float           tsize;
274
275         if ( spawnArgs.GetBool( "dropToFloor" ) ) {
276                 PostEventMS( &EV_DropToFloor, 0 );
277         }
278
279         if ( spawnArgs.GetFloat( "triggersize", "0", tsize ) ) {
280                 GetPhysics()->GetClipModel()->LoadModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) );
281                 GetPhysics()->GetClipModel()->Link( gameLocal.clip );
282         }
283
284         if ( spawnArgs.GetBool( "start_off" ) ) {
285                 GetPhysics()->SetContents( 0 );
286                 Hide();
287         } else {
288                 GetPhysics()->SetContents( CONTENTS_TRIGGER );
289         }
290
291         giveTo = spawnArgs.GetString( "owner" );
292         if ( giveTo.Length() ) {
293                 ent = gameLocal.FindEntity( giveTo );
294                 if ( !ent ) {
295                         gameLocal.Error( "Item couldn't find owner '%s'", giveTo.c_str() );
296                 }
297                 PostEventMS( &EV_Touch, 0, ent, NULL );
298         }
299
300         if ( spawnArgs.GetBool( "spin" ) || gameLocal.isMultiplayer ) {
301                 spin = true;
302                 BecomeActive( TH_THINK );
303         }
304
305         //pulse = !spawnArgs.GetBool( "nopulse" );
306         //temp hack for tim
307         pulse = false;
308         orgOrigin = GetPhysics()->GetOrigin();
309
310         canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) );
311
312         inViewTime = -1000;
313         lastCycle = -1;
314         itemShellHandle = -1;
315         shellMaterial = declManager->FindMaterial( "itemHighlightShell" );
316 }
317
318 /*
319 ================
320 idItem::GetAttributes
321 ================
322 */
323 void idItem::GetAttributes( idDict &attributes ) {
324         int                                     i;
325         const idKeyValue        *arg;
326
327         for( i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) {
328                 arg = spawnArgs.GetKeyVal( i );
329                 if ( arg->GetKey().Left( 4 ) == "inv_" ) {
330                         attributes.Set( arg->GetKey().Right( arg->GetKey().Length() - 4 ), arg->GetValue() );
331                 }
332         }
333 }
334
335 /*
336 ================
337 idItem::GiveToPlayer
338 ================
339 */
340 bool idItem::GiveToPlayer( idPlayer *player ) {
341         if ( player == NULL ) {
342                 return false;
343         }
344
345         if ( spawnArgs.GetBool( "inv_carry" ) ) {
346                 return player->GiveInventoryItem( &spawnArgs );
347         } 
348         
349         return player->GiveItem( this );
350 }
351
352 /*
353 ================
354 idItem::Pickup
355 ================
356 */
357 bool idItem::Pickup( idPlayer *player ) {
358         
359         if ( !GiveToPlayer( player ) ) {
360                 return false;
361         }
362
363         if ( gameLocal.isServer ) {
364                 ServerSendEvent( EVENT_PICKUP, NULL, false, -1 );
365         }
366
367         // play pickup sound
368         StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
369
370         // trigger our targets
371         ActivateTargets( player );
372
373         // clear our contents so the object isn't picked up twice
374         GetPhysics()->SetContents( 0 );
375
376         // hide the model
377         Hide();
378
379         // add the highlight shell
380         if ( itemShellHandle != -1 ) {
381                 gameRenderWorld->FreeEntityDef( itemShellHandle );
382                 itemShellHandle = -1;
383         }
384
385         float respawn = spawnArgs.GetFloat( "respawn" );
386         bool dropped = spawnArgs.GetBool( "dropped" );
387         bool no_respawn = spawnArgs.GetBool( "no_respawn" );
388
389         if ( gameLocal.isMultiplayer && respawn == 0.0f ) {
390                 respawn = 20.0f;
391         }
392
393         if ( respawn && !dropped && !no_respawn ) {
394                 const char *sfx = spawnArgs.GetString( "fxRespawn" );
395                 if ( sfx && *sfx ) {
396                         PostEventSec( &EV_RespawnFx, respawn - 0.5f );
397                 } 
398                 PostEventSec( &EV_RespawnItem, respawn );
399         } else if ( !spawnArgs.GetBool( "inv_objective" ) && !no_respawn ) {
400                 // give some time for the pickup sound to play
401                 // FIXME: Play on the owner
402                 if ( !spawnArgs.GetBool( "inv_carry" ) ) {
403                         PostEventMS( &EV_Remove, 5000 );
404                 }
405         }
406
407         BecomeInactive( TH_THINK );
408         return true;
409 }
410
411 /*
412 ================
413 idItem::ClientPredictionThink
414 ================
415 */
416 void idItem::ClientPredictionThink( void ) {
417         // only think forward because the state is not synced through snapshots
418         if ( !gameLocal.isNewFrame ) {
419                 return;
420         }
421         Think();
422 }
423
424 /*
425 ================
426 idItem::WriteFromSnapshot
427 ================
428 */
429 void idItem::WriteToSnapshot( idBitMsgDelta &msg ) const {
430         msg.WriteBits( IsHidden(), 1 );
431 }
432
433 /*
434 ================
435 idItem::ReadFromSnapshot
436 ================
437 */
438 void idItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
439         if ( msg.ReadBits( 1 ) ) {
440                 Hide();
441         } else {
442                 Show();
443         }
444 }
445
446 /*
447 ================
448 idItem::ClientReceiveEvent
449 ================
450 */
451 bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
452
453         switch( event ) {
454                 case EVENT_PICKUP: {
455
456                         // play pickup sound
457                         StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
458
459                         // hide the model
460                         Hide();
461
462                         // remove the highlight shell
463                         if ( itemShellHandle != -1 ) {
464                                 gameRenderWorld->FreeEntityDef( itemShellHandle );
465                                 itemShellHandle = -1;
466                         }
467                         return true;
468                 }
469                 case EVENT_RESPAWN: {
470                         Event_Respawn();
471                         return true;
472                 }
473                 case EVENT_RESPAWNFX: {
474                         Event_RespawnFx();
475                         return true;
476                 }
477                 default: {
478                         return idEntity::ClientReceiveEvent( event, time, msg );
479                 }
480         }
481         return false;
482 }
483
484 /*
485 ================
486 idItem::Event_DropToFloor
487 ================
488 */
489 void idItem::Event_DropToFloor( void ) {
490         trace_t trace;
491
492         // don't drop the floor if bound to another entity
493         if ( GetBindMaster() != NULL && GetBindMaster() != this ) {
494                 return;
495         }
496
497         gameLocal.clip.TraceBounds( trace, renderEntity.origin, renderEntity.origin - idVec3( 0, 0, 64 ), renderEntity.bounds, MASK_SOLID | CONTENTS_CORPSE, this );
498         SetOrigin( trace.endpos );
499 }
500
501 /*
502 ================
503 idItem::Event_Touch
504 ================
505 */
506 void idItem::Event_Touch( idEntity *other, trace_t *trace ) {
507         if ( !other->IsType( idPlayer::Type ) ) {
508                 return;
509         }
510
511         if ( !canPickUp ) {
512                 return;
513         }
514
515         Pickup( static_cast<idPlayer *>(other) );
516 }
517
518 /*
519 ================
520 idItem::Event_Trigger
521 ================
522 */
523 void idItem::Event_Trigger( idEntity *activator ) {
524
525         if ( !canPickUp && spawnArgs.GetBool( "triggerFirst" ) ) {
526                 canPickUp = true;
527                 return;
528         }
529
530         if ( activator && activator->IsType( idPlayer::Type ) ) {
531                 Pickup( static_cast<idPlayer *>( activator ) );
532         }
533 }
534
535 /*
536 ================
537 idItem::Event_Respawn
538 ================
539 */
540 void idItem::Event_Respawn( void ) {
541         if ( gameLocal.isServer ) {
542                 ServerSendEvent( EVENT_RESPAWN, NULL, false, -1 );
543         }
544         BecomeActive( TH_THINK );
545         Show();
546         inViewTime = -1000;
547         lastCycle = -1;
548         GetPhysics()->SetContents( CONTENTS_TRIGGER );
549         SetOrigin( orgOrigin );
550         StartSound( "snd_respawn", SND_CHANNEL_ITEM, 0, false, NULL );
551         CancelEvents( &EV_RespawnItem ); // don't double respawn
552 }
553
554 /*
555 ================
556 idItem::Event_RespawnFx
557 ================
558 */
559 void idItem::Event_RespawnFx( void ) {
560         if ( gameLocal.isServer ) {
561                 ServerSendEvent( EVENT_RESPAWNFX, NULL, false, -1 );
562         }
563         const char *sfx = spawnArgs.GetString( "fxRespawn" );
564         if ( sfx && *sfx ) {
565                 idEntityFx::StartFx( sfx, NULL, NULL, this, true );
566         }
567 }
568
569 /*
570 ===============================================================================
571
572   idItemPowerup
573
574 ===============================================================================
575 */
576
577 /*
578 ===============
579 idItemPowerup
580 ===============
581 */
582
583 CLASS_DECLARATION( idItem, idItemPowerup )
584 END_CLASS
585
586 /*
587 ================
588 idItemPowerup::idItemPowerup
589 ================
590 */
591 idItemPowerup::idItemPowerup() {
592         time = 0;
593         type = 0;
594 }
595
596 /*
597 ================
598 idItemPowerup::Save
599 ================
600 */
601 void idItemPowerup::Save( idSaveGame *savefile ) const {
602         savefile->WriteInt( time );
603         savefile->WriteInt( type );
604 }
605
606 /*
607 ================
608 idItemPowerup::Restore
609 ================
610 */
611 void idItemPowerup::Restore( idRestoreGame *savefile ) {
612         savefile->ReadInt( time );
613         savefile->ReadInt( type );
614 }
615
616 /*
617 ================
618 idItemPowerup::Spawn
619 ================
620 */
621 void idItemPowerup::Spawn( void ) {
622         time = spawnArgs.GetInt( "time", "30" );
623         type = spawnArgs.GetInt( "type", "0" );
624 }
625
626 /*
627 ================
628 idItemPowerup::GiveToPlayer
629 ================
630 */
631 bool idItemPowerup::GiveToPlayer( idPlayer *player ) {
632         if ( player->spectating ) {
633                 return false;
634         }
635         player->GivePowerUp( type, time * 1000 );
636         return true;
637 }
638
639 /*
640 ===============================================================================
641
642   idObjective
643
644 ===============================================================================
645 */
646
647 CLASS_DECLARATION( idItem, idObjective )
648         EVENT( EV_Activate,                     idObjective::Event_Trigger )
649         EVENT( EV_HideObjective,        idObjective::Event_HideObjective )
650         EVENT( EV_GetPlayerPos,         idObjective::Event_GetPlayerPos )
651         EVENT( EV_CamShot,                      idObjective::Event_CamShot )
652 END_CLASS
653
654 /*
655 ================
656 idObjective::idObjective
657 ================
658 */
659 idObjective::idObjective() {
660         playerPos.Zero();
661 }
662
663 /*
664 ================
665 idObjective::Save
666 ================
667 */
668 void idObjective::Save( idSaveGame *savefile ) const {
669         savefile->WriteVec3( playerPos );
670 }
671
672 /*
673 ================
674 idObjective::Restore
675 ================
676 */
677 void idObjective::Restore( idRestoreGame *savefile ) {
678         savefile->ReadVec3( playerPos );
679         PostEventMS( &EV_CamShot, 250 );
680 }
681
682 /*
683 ================
684 idObjective::Spawn
685 ================
686 */
687 void idObjective::Spawn( void ) {
688         Hide();
689         PostEventMS( &EV_CamShot, 250 );
690 }
691
692 /*
693 ================
694 idObjective::Event_Screenshot
695 ================
696 */
697 void idObjective::Event_CamShot( ) {
698         const char *camName;
699         idStr shotName = gameLocal.GetMapName();
700         shotName.StripFileExtension();
701         shotName += "/";
702         shotName += spawnArgs.GetString( "screenshot" );
703         shotName.SetFileExtension( ".tga" );
704         if ( spawnArgs.GetString( "camShot", "", &camName ) ) {
705                 idEntity *ent = gameLocal.FindEntity( camName );
706                 if ( ent && ent->cameraTarget ) {
707                         const renderView_t *view = ent->cameraTarget->GetRenderView();
708                         renderView_t fullView = *view;
709                         fullView.width = SCREEN_WIDTH;
710                         fullView.height = SCREEN_HEIGHT;
711                         // draw a view to a texture
712                         renderSystem->CropRenderSize( 256, 256, true );
713                         gameRenderWorld->RenderScene( &fullView );
714                         renderSystem->CaptureRenderToFile( shotName );
715                         renderSystem->UnCrop();
716                 }
717         }
718 }
719
720 /*
721 ================
722 idObjective::Event_Trigger
723 ================
724 */
725 void idObjective::Event_Trigger( idEntity *activator ) {
726         idPlayer *player = gameLocal.GetLocalPlayer();
727         if ( player ) {
728
729                 //Pickup( player );
730
731                 if ( spawnArgs.GetString( "inv_objective", NULL ) ) {
732                         if ( player && player->hud ) {
733                                 idStr shotName = gameLocal.GetMapName();
734                                 shotName.StripFileExtension();
735                                 shotName += "/";
736                                 shotName += spawnArgs.GetString( "screenshot" );
737                                 shotName.SetFileExtension( ".tga" );
738                                 player->hud->SetStateString( "screenshot", shotName );
739                                 player->hud->SetStateString( "objective", "1" );
740                                 player->hud->SetStateString( "objectivetext", spawnArgs.GetString( "objectivetext" ) );
741                                 player->hud->SetStateString( "objectivetitle", spawnArgs.GetString( "objectivetitle" ) );
742                                 player->GiveObjective( spawnArgs.GetString( "objectivetitle" ), spawnArgs.GetString( "objectivetext" ), shotName );
743
744                                 // a tad slow but keeps from having to update all objectives in all maps with a name ptr
745                                 for( int i = 0; i < gameLocal.num_entities; i++ ) {
746                                         if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idObjectiveComplete::Type ) ) {
747                                                 if ( idStr::Icmp( spawnArgs.GetString( "objectivetitle" ), gameLocal.entities[ i ]->spawnArgs.GetString( "objectivetitle" ) ) == 0 ){
748                                                         gameLocal.entities[ i ]->spawnArgs.SetBool( "objEnabled", true );
749                                                         break;
750                                                 }
751                                         }
752                                 }
753
754                                 PostEventMS( &EV_GetPlayerPos, 2000 );
755                         }
756                 }
757         }
758 }
759
760 /*
761 ================
762 idObjective::Event_GetPlayerPos
763 ================
764 */
765 void idObjective::Event_GetPlayerPos() {
766         idPlayer *player = gameLocal.GetLocalPlayer();
767         if ( player ) {
768                 playerPos = player->GetPhysics()->GetOrigin();
769                 PostEventMS( &EV_HideObjective, 100, player );
770         }
771 }
772
773 /*
774 ================
775 idObjective::Event_HideObjective
776 ================
777 */
778 void idObjective::Event_HideObjective(idEntity *e) {
779         idPlayer *player = gameLocal.GetLocalPlayer();
780         if ( player ) {
781                 idVec3 v = player->GetPhysics()->GetOrigin() - playerPos;
782                 if ( v.Length() > 64.0f ) {
783                         player->HideObjective();
784                         PostEventMS( &EV_Remove, 0 );
785                 } else {
786                         PostEventMS( &EV_HideObjective, 100, player );
787                 }
788         }
789 }
790
791 /*
792 ===============================================================================
793
794   idVideoCDItem
795
796 ===============================================================================
797 */
798
799 CLASS_DECLARATION( idItem, idVideoCDItem )
800 END_CLASS
801
802 /*
803 ================
804 idVideoCDItem::Spawn
805 ================
806 */
807 void idVideoCDItem::Spawn( void ) {
808 }
809
810 /*
811 ================
812 idVideoCDItem::GiveToPlayer
813 ================
814 */
815 bool idVideoCDItem::GiveToPlayer( idPlayer *player ) {
816         idStr str = spawnArgs.GetString( "video" );
817         if ( player && str.Length() ) {
818                 player->GiveVideo( str, &spawnArgs );
819         }
820         return true;
821 }
822
823 /*
824 ===============================================================================
825
826   idPDAItem
827
828 ===============================================================================
829 */
830
831 CLASS_DECLARATION( idItem, idPDAItem )
832 END_CLASS
833
834 /*
835 ================
836 idPDAItem::GiveToPlayer
837 ================
838 */
839 bool idPDAItem::GiveToPlayer(idPlayer *player) {
840         const char *str = spawnArgs.GetString( "pda_name" );
841         if ( player ) {
842                 player->GivePDA( str, &spawnArgs );
843         }
844         return true;
845 }
846
847 /*
848 ===============================================================================
849
850   idMoveableItem
851         
852 ===============================================================================
853 */
854
855 CLASS_DECLARATION( idItem, idMoveableItem )
856         EVENT( EV_DropToFloor,  idMoveableItem::Event_DropToFloor )
857         EVENT( EV_Gib,                  idMoveableItem::Event_Gib )
858 END_CLASS
859
860 /*
861 ================
862 idMoveableItem::idMoveableItem
863 ================
864 */
865 idMoveableItem::idMoveableItem() {
866         trigger = NULL;
867         smoke = NULL;
868         smokeTime = 0;
869 }
870
871 /*
872 ================
873 idMoveableItem::~idMoveableItem
874 ================
875 */
876 idMoveableItem::~idMoveableItem() {
877         if ( trigger ) {
878                 delete trigger;
879         }
880 }
881
882 /*
883 ================
884 idMoveableItem::Save
885 ================
886 */
887 void idMoveableItem::Save( idSaveGame *savefile ) const {
888         savefile->WriteStaticObject( physicsObj );
889
890         savefile->WriteClipModel( trigger );
891
892         savefile->WriteParticle( smoke );
893         savefile->WriteInt( smokeTime );
894 }
895
896 /*
897 ================
898 idMoveableItem::Restore
899 ================
900 */
901 void idMoveableItem::Restore( idRestoreGame *savefile ) {
902         savefile->ReadStaticObject( physicsObj );
903         RestorePhysics( &physicsObj );
904
905         savefile->ReadClipModel( trigger );
906
907         savefile->ReadParticle( smoke );
908         savefile->ReadInt( smokeTime );
909 }
910
911 /*
912 ================
913 idMoveableItem::Spawn
914 ================
915 */
916 void idMoveableItem::Spawn( void ) {
917         idTraceModel trm;
918         float density, friction, bouncyness, tsize;
919         idStr clipModelName;
920         idBounds bounds;
921
922         // create a trigger for item pickup
923         spawnArgs.GetFloat( "triggersize", "16.0", tsize );
924         trigger = new idClipModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) );
925         trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
926         trigger->SetContents( CONTENTS_TRIGGER );
927
928         // check if a clip model is set
929         spawnArgs.GetString( "clipmodel", "", clipModelName );
930         if ( !clipModelName[0] ) {
931                 clipModelName = spawnArgs.GetString( "model" );         // use the visual model
932         }
933
934         // load the trace model
935         if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) {
936                 gameLocal.Error( "idMoveableItem '%s': cannot load collision model %s", name.c_str(), clipModelName.c_str() );
937                 return;
938         }
939
940         // if the model should be shrinked
941         if ( spawnArgs.GetBool( "clipshrink" ) ) {
942                 trm.Shrink( CM_CLIP_EPSILON );
943         }
944
945         // get rigid body properties
946         spawnArgs.GetFloat( "density", "0.5", density );
947         density = idMath::ClampFloat( 0.001f, 1000.0f, density );
948         spawnArgs.GetFloat( "friction", "0.05", friction );
949         friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
950         spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness );
951         bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
952
953         // setup the physics
954         physicsObj.SetSelf( this );
955         physicsObj.SetClipModel( new idClipModel( trm ), density );
956         physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
957         physicsObj.SetAxis( GetPhysics()->GetAxis() );
958         physicsObj.SetBouncyness( bouncyness );
959         physicsObj.SetFriction( 0.6f, 0.6f, friction );
960         physicsObj.SetGravity( gameLocal.GetGravity() );
961         physicsObj.SetContents( CONTENTS_RENDERMODEL );
962         physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
963         SetPhysics( &physicsObj );
964
965         smoke = NULL;
966         smokeTime = 0;
967         const char *smokeName = spawnArgs.GetString( "smoke_trail" );
968         if ( *smokeName != '\0' ) {
969                 smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
970                 smokeTime = gameLocal.time;
971                 BecomeActive( TH_UPDATEPARTICLES );
972         }
973 }
974
975 /*
976 ================
977 idMoveableItem::Think
978 ================
979 */
980 void idMoveableItem::Think( void ) {
981
982         RunPhysics();
983
984         if ( thinkFlags & TH_PHYSICS ) {
985                 // update trigger position
986                 trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity );
987         }
988         
989         if ( thinkFlags & TH_UPDATEPARTICLES ) {
990                 if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
991                         smokeTime = 0;
992                         BecomeInactive( TH_UPDATEPARTICLES );
993                 }
994         }
995
996         Present();
997 }
998
999 /*
1000 ================
1001 idMoveableItem::Pickup
1002 ================
1003 */
1004 bool idMoveableItem::Pickup( idPlayer *player ) {
1005         bool ret = idItem::Pickup( player );
1006         if ( ret ) {
1007                 trigger->SetContents( 0 );
1008         } 
1009         return ret;
1010 }
1011
1012 /*
1013 ================
1014 idMoveableItem::DropItem
1015 ================
1016 */
1017 idEntity *idMoveableItem::DropItem( const char *classname, const idVec3 &origin, const idMat3 &axis, const idVec3 &velocity, int activateDelay, int removeDelay ) {
1018         idDict args;
1019         idEntity *item;
1020
1021         args.Set( "classname", classname );
1022         args.Set( "dropped", "1" );
1023
1024         // we sometimes drop idMoveables here, so set 'nodrop' to 1 so that it doesn't get put on the floor
1025         args.Set( "nodrop", "1" );
1026
1027         if ( activateDelay ) {
1028                 args.SetBool( "triggerFirst", true );
1029         }
1030
1031         gameLocal.SpawnEntityDef( args, &item );
1032         if ( item ) {
1033                 // set item position
1034                 item->GetPhysics()->SetOrigin( origin );
1035                 item->GetPhysics()->SetAxis( axis );
1036                 item->GetPhysics()->SetLinearVelocity( velocity );
1037                 item->UpdateVisuals();
1038                 if ( activateDelay ) {
1039                         item->PostEventMS( &EV_Activate, activateDelay, item );
1040                 }
1041                 if ( !removeDelay ) {
1042                         removeDelay = 5 * 60 * 1000;
1043                 }
1044                 // always remove a dropped item after 5 minutes in case it dropped to an unreachable location
1045                 item->PostEventMS( &EV_Remove, removeDelay );
1046         }
1047         return item;
1048 }
1049
1050 /*
1051 ================
1052 idMoveableItem::DropItems
1053
1054   The entity should have the following key/value pairs set:
1055         "def_drop<type>Item"                    "item def"
1056         "drop<type>ItemJoint"                   "joint name"
1057         "drop<type>ItemRotation"                "pitch yaw roll"
1058         "drop<type>ItemOffset"                  "x y z"
1059         "skin_drop<type>"                               "skin name"
1060   To drop multiple items the following key/value pairs can be used:
1061         "def_drop<type>Item<X>"                 "item def"
1062         "drop<type>Item<X>Joint"                "joint name"
1063         "drop<type>Item<X>Rotation"             "pitch yaw roll"
1064         "drop<type>Item<X>Offset"               "x y z"
1065   where <X> is an aribtrary string.
1066 ================
1067 */
1068 void idMoveableItem::DropItems( idAnimatedEntity  *ent, const char *type, idList<idEntity *> *list ) {
1069         const idKeyValue *kv;
1070         const char *skinName, *c, *jointName;
1071         idStr key, key2;
1072         idVec3 origin;
1073         idMat3 axis;
1074         idAngles angles;
1075         const idDeclSkin *skin;
1076         jointHandle_t joint;
1077         idEntity *item;
1078
1079         // drop all items
1080         kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), NULL );
1081         while ( kv ) {
1082
1083                 c = kv->GetKey().c_str() + kv->GetKey().Length();
1084                 if ( idStr::Icmp( c - 5, "Joint" ) != 0 && idStr::Icmp( c - 8, "Rotation" ) != 0 ) {
1085
1086                         key = kv->GetKey().c_str() + 4;
1087                         key2 = key;
1088                         key += "Joint";
1089                         key2 += "Offset";
1090                         jointName = ent->spawnArgs.GetString( key );
1091                         joint = ent->GetAnimator()->GetJointHandle( jointName );
1092                         if ( !ent->GetJointWorldTransform( joint, gameLocal.time, origin, axis ) ) {
1093                                 gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", key.c_str(), jointName, ent->name.c_str() );
1094                                 origin = ent->GetPhysics()->GetOrigin();
1095                                 axis = ent->GetPhysics()->GetAxis();
1096                         }
1097                         if ( g_dropItemRotation.GetString()[0] ) {
1098                                 angles.Zero();
1099                                 sscanf( g_dropItemRotation.GetString(), "%f %f %f", &angles.pitch, &angles.yaw, &angles.roll );
1100                         } else {
1101                                 key = kv->GetKey().c_str() + 4;
1102                                 key += "Rotation";
1103                                 ent->spawnArgs.GetAngles( key, "0 0 0", angles );
1104                         }
1105                         axis = angles.ToMat3() * axis;
1106
1107                         origin += ent->spawnArgs.GetVector( key2, "0 0 0" );
1108
1109                         item = DropItem( kv->GetValue(), origin, axis, vec3_origin, 0, 0 );
1110                         if ( list && item ) {
1111                                 list->Append( item );
1112                         }
1113                 }
1114
1115                 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv );
1116         }
1117
1118         // change the skin to hide all items
1119         skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
1120         if ( skinName[0] ) {
1121                 skin = declManager->FindSkin( skinName );
1122                 ent->SetSkin( skin );
1123         }
1124 }
1125
1126 /*
1127 ======================
1128 idMoveableItem::WriteToSnapshot
1129 ======================
1130 */
1131 void idMoveableItem::WriteToSnapshot( idBitMsgDelta &msg ) const {
1132         physicsObj.WriteToSnapshot( msg );
1133 }
1134
1135 /*
1136 ======================
1137 idMoveableItem::ReadFromSnapshot
1138 ======================
1139 */
1140 void idMoveableItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1141         physicsObj.ReadFromSnapshot( msg );
1142         if ( msg.HasChanged() ) {
1143                 UpdateVisuals();
1144         }
1145 }
1146
1147 /*
1148 ============
1149 idMoveableItem::Gib
1150 ============
1151 */
1152 void idMoveableItem::Gib( const idVec3 &dir, const char *damageDefName ) {
1153         // spawn smoke puff
1154         const char *smokeName = spawnArgs.GetString( "smoke_gib" );
1155         if ( *smokeName != '\0' ) {
1156                 const idDeclParticle *smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1157                 gameLocal.smokeParticles->EmitSmoke( smoke, gameLocal.time, gameLocal.random.CRandomFloat(), renderEntity.origin, renderEntity.axis );
1158         }
1159         // remove the entity
1160         PostEventMS( &EV_Remove, 0 );
1161 }
1162
1163 /*
1164 ================
1165 idMoveableItem::Event_DropToFloor
1166 ================
1167 */
1168 void idMoveableItem::Event_DropToFloor( void ) {
1169         // the physics will drop the moveable to the floor
1170 }
1171
1172 /*
1173 ============
1174 idMoveableItem::Event_Gib
1175 ============
1176 */
1177 void idMoveableItem::Event_Gib( const char *damageDefName ) {
1178         Gib( idVec3( 0, 0, 1 ), damageDefName );
1179 }
1180
1181 /*
1182 ===============================================================================
1183
1184   idMoveablePDAItem
1185
1186 ===============================================================================
1187 */
1188
1189 CLASS_DECLARATION( idMoveableItem, idMoveablePDAItem )
1190 END_CLASS
1191
1192 /*
1193 ================
1194 idMoveablePDAItem::GiveToPlayer
1195 ================
1196 */
1197 bool idMoveablePDAItem::GiveToPlayer(idPlayer *player) {
1198         const char *str = spawnArgs.GetString( "pda_name" );
1199         if ( player ) {
1200                 player->GivePDA( str, &spawnArgs );
1201         }
1202         return true;
1203 }
1204
1205 /*
1206 ===============================================================================
1207
1208   idItemRemover
1209
1210 ===============================================================================
1211 */
1212
1213 CLASS_DECLARATION( idEntity, idItemRemover )
1214         EVENT( EV_Activate,             idItemRemover::Event_Trigger )
1215 END_CLASS
1216
1217 /*
1218 ================
1219 idItemRemover::Spawn
1220 ================
1221 */
1222 void idItemRemover::Spawn( void ) {
1223 }
1224
1225 /*
1226 ================
1227 idItemRemover::RemoveItem
1228 ================
1229 */
1230 void idItemRemover::RemoveItem( idPlayer *player ) {
1231         const char *remove;
1232         
1233         remove = spawnArgs.GetString( "remove" );
1234         player->RemoveInventoryItem( remove );
1235 }
1236
1237 /*
1238 ================
1239 idItemRemover::Event_Trigger
1240 ================
1241 */
1242 void idItemRemover::Event_Trigger( idEntity *activator ) {
1243         if ( activator->IsType( idPlayer::Type ) ) {
1244                 RemoveItem( static_cast<idPlayer *>(activator) );
1245         }
1246 }
1247
1248 /*
1249 ===============================================================================
1250
1251   idObjectiveComplete
1252
1253 ===============================================================================
1254 */
1255
1256 CLASS_DECLARATION( idItemRemover, idObjectiveComplete )
1257         EVENT( EV_Activate,                     idObjectiveComplete::Event_Trigger )
1258         EVENT( EV_HideObjective,        idObjectiveComplete::Event_HideObjective )
1259         EVENT( EV_GetPlayerPos,         idObjectiveComplete::Event_GetPlayerPos )
1260 END_CLASS
1261
1262 /*
1263 ================
1264 idObjectiveComplete::idObjectiveComplete
1265 ================
1266 */
1267 idObjectiveComplete::idObjectiveComplete() {
1268         playerPos.Zero();
1269 }
1270
1271 /*
1272 ================
1273 idObjectiveComplete::Save
1274 ================
1275 */
1276 void idObjectiveComplete::Save( idSaveGame *savefile ) const {
1277         savefile->WriteVec3( playerPos );
1278 }
1279
1280 /*
1281 ================
1282 idObjectiveComplete::Restore
1283 ================
1284 */
1285 void idObjectiveComplete::Restore( idRestoreGame *savefile ) {
1286         savefile->ReadVec3( playerPos );
1287 }
1288
1289 /*
1290 ================
1291 idObjectiveComplete::Spawn
1292 ================
1293 */
1294 void idObjectiveComplete::Spawn( void ) {
1295         spawnArgs.SetBool( "objEnabled", false );
1296         Hide();
1297 }
1298
1299 /*
1300 ================
1301 idObjectiveComplete::Event_Trigger
1302 ================
1303 */
1304 void idObjectiveComplete::Event_Trigger( idEntity *activator ) {
1305         if ( !spawnArgs.GetBool( "objEnabled" ) ) {
1306                 return;
1307         }
1308         idPlayer *player = gameLocal.GetLocalPlayer();
1309         if ( player ) {
1310                 RemoveItem( player );
1311
1312                 if ( spawnArgs.GetString( "inv_objective", NULL ) ) {
1313                         if ( player->hud ) {
1314                                 player->hud->SetStateString( "objective", "2" );
1315                                 player->hud->SetStateString( "objectivetext", spawnArgs.GetString( "objectivetext" ) );
1316                                 player->hud->SetStateString( "objectivetitle", spawnArgs.GetString( "objectivetitle" ) );
1317                                 player->CompleteObjective( spawnArgs.GetString( "objectivetitle" ) );
1318                                 PostEventMS( &EV_GetPlayerPos, 2000 );
1319                         }
1320                 }
1321         }
1322 }
1323
1324 /*
1325 ================
1326 idObjectiveComplete::Event_GetPlayerPos
1327 ================
1328 */
1329 void idObjectiveComplete::Event_GetPlayerPos() {
1330         idPlayer *player = gameLocal.GetLocalPlayer();
1331         if ( player ) {
1332                 playerPos = player->GetPhysics()->GetOrigin();
1333                 PostEventMS( &EV_HideObjective, 100, player );
1334         }
1335 }
1336
1337 /*
1338 ================
1339 idObjectiveComplete::Event_HideObjective
1340 ================
1341 */
1342 void idObjectiveComplete::Event_HideObjective( idEntity *e ) {
1343         idPlayer *player = gameLocal.GetLocalPlayer();
1344         if ( player ) {
1345                 idVec3 v = player->GetPhysics()->GetOrigin();
1346                 v -= playerPos;
1347                 if ( v.Length() > 64.0f ) {
1348                         player->hud->HandleNamedEvent( "closeObjective" );
1349                         PostEventMS( &EV_Remove, 0 );
1350                 } else {
1351                         PostEventMS( &EV_HideObjective, 100, player );
1352                 }
1353         }
1354 }