2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
36 ===============================================================================
40 ===============================================================================
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>" );
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 )
69 lastRenderViewTime = -1;
74 fl.networkSync = true;
83 // remove the highlight shell
84 if ( itemShellHandle != -1 ) {
85 gameRenderWorld->FreeEntityDef( itemShellHandle );
94 void idItem::Save( idSaveGame *savefile ) const {
96 savefile->WriteVec3( orgOrigin );
97 savefile->WriteBool( spin );
98 savefile->WriteBool( pulse );
99 savefile->WriteBool( canPickUp );
101 savefile->WriteMaterial( shellMaterial );
103 savefile->WriteBool( inView );
104 savefile->WriteInt( inViewTime );
105 savefile->WriteInt( lastCycle );
106 savefile->WriteInt( lastRenderViewTime );
114 void idItem::Restore( idRestoreGame *savefile ) {
116 savefile->ReadVec3( orgOrigin );
117 savefile->ReadBool( spin );
118 savefile->ReadBool( pulse );
119 savefile->ReadBool( canPickUp );
121 savefile->ReadMaterial( shellMaterial );
123 savefile->ReadBool( inView );
124 savefile->ReadInt( inViewTime );
125 savefile->ReadInt( lastCycle );
126 savefile->ReadInt( lastRenderViewTime );
128 itemShellHandle = -1;
133 idItem::UpdateRenderEntity
136 bool idItem::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
138 if ( lastRenderViewTime == renderView->time ) {
142 lastRenderViewTime = renderView->time;
144 // check for glow highlighting if near the center of the view
145 idVec3 dir = renderEntity->origin - renderView->vieworg;
147 float d = dir * renderView->viewaxis[0];
149 // two second pulse cycle
150 float cycle = ( renderView->time - inViewTime ) / 2000.0f;
155 if ( cycle > lastCycle ) {
156 // restart at the beginning
157 inViewTime = renderView->time;
164 lastCycle = ceil( cycle );
168 // fade down after the last pulse finishes
169 if ( !inView && cycle > lastCycle ) {
170 renderEntity->shaderParms[4] = 0.0f;
172 // pulse up in 1/4 second
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;
181 // stay off between pulses
182 renderEntity->shaderParms[4] = 0.0f;
186 // update every single time this is in view
192 idItem::ModelCallback
195 bool idItem::ModelCallback( renderEntity_t *renderEntity, const renderView_t *renderView ) {
198 // this may be triggered by a model trace or other non-view related source
203 ent = static_cast<idItem *>(gameLocal.entities[ renderEntity->entityNum ]);
205 gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" );
208 return ent->UpdateRenderEntity( renderEntity, renderView );
216 void idItem::Think( void ) {
217 if ( thinkFlags & TH_THINK ) {
222 ang.pitch = ang.roll = 0.0f;
223 ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f;
226 float scale = 0.005f + entityNumber * 0.00001f;
229 org.z += 4.0f + cos( ( gameLocal.time + 2000 ) * scale ) * 4.0f;
242 void idItem::Present( void ) {
245 if ( !fl.hidden && pulse ) {
246 // also add a highlight shell model
247 renderEntity_t shell;
249 shell = renderEntity;
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 );
259 gameRenderWorld->UpdateEntityDef( itemShellHandle, &shell );
270 void idItem::Spawn( void ) {
275 if ( spawnArgs.GetBool( "dropToFloor" ) ) {
276 PostEventMS( &EV_DropToFloor, 0 );
279 if ( spawnArgs.GetFloat( "triggersize", "0", tsize ) ) {
280 GetPhysics()->GetClipModel()->LoadModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) );
281 GetPhysics()->GetClipModel()->Link( gameLocal.clip );
284 if ( spawnArgs.GetBool( "start_off" ) ) {
285 GetPhysics()->SetContents( 0 );
288 GetPhysics()->SetContents( CONTENTS_TRIGGER );
291 giveTo = spawnArgs.GetString( "owner" );
292 if ( giveTo.Length() ) {
293 ent = gameLocal.FindEntity( giveTo );
295 gameLocal.Error( "Item couldn't find owner '%s'", giveTo.c_str() );
297 PostEventMS( &EV_Touch, 0, ent, NULL );
300 if ( spawnArgs.GetBool( "spin" ) || gameLocal.isMultiplayer ) {
302 BecomeActive( TH_THINK );
305 //pulse = !spawnArgs.GetBool( "nopulse" );
308 orgOrigin = GetPhysics()->GetOrigin();
310 canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) );
314 itemShellHandle = -1;
315 shellMaterial = declManager->FindMaterial( "itemHighlightShell" );
320 idItem::GetAttributes
323 void idItem::GetAttributes( idDict &attributes ) {
325 const idKeyValue *arg;
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() );
340 bool idItem::GiveToPlayer( idPlayer *player ) {
341 if ( player == NULL ) {
345 if ( spawnArgs.GetBool( "inv_carry" ) ) {
346 return player->GiveInventoryItem( &spawnArgs );
349 return player->GiveItem( this );
357 bool idItem::Pickup( idPlayer *player ) {
359 if ( !GiveToPlayer( player ) ) {
363 if ( gameLocal.isServer ) {
364 ServerSendEvent( EVENT_PICKUP, NULL, false, -1 );
368 StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
370 // trigger our targets
371 ActivateTargets( player );
373 // clear our contents so the object isn't picked up twice
374 GetPhysics()->SetContents( 0 );
379 // add the highlight shell
380 if ( itemShellHandle != -1 ) {
381 gameRenderWorld->FreeEntityDef( itemShellHandle );
382 itemShellHandle = -1;
385 float respawn = spawnArgs.GetFloat( "respawn" );
386 bool dropped = spawnArgs.GetBool( "dropped" );
387 bool no_respawn = spawnArgs.GetBool( "no_respawn" );
389 if ( gameLocal.isMultiplayer && respawn == 0.0f ) {
393 if ( respawn && !dropped && !no_respawn ) {
394 const char *sfx = spawnArgs.GetString( "fxRespawn" );
396 PostEventSec( &EV_RespawnFx, respawn - 0.5f );
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 );
407 BecomeInactive( TH_THINK );
413 idItem::ClientPredictionThink
416 void idItem::ClientPredictionThink( void ) {
417 // only think forward because the state is not synced through snapshots
418 if ( !gameLocal.isNewFrame ) {
426 idItem::WriteFromSnapshot
429 void idItem::WriteToSnapshot( idBitMsgDelta &msg ) const {
430 msg.WriteBits( IsHidden(), 1 );
435 idItem::ReadFromSnapshot
438 void idItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
439 if ( msg.ReadBits( 1 ) ) {
448 idItem::ClientReceiveEvent
451 bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
457 StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
462 // remove the highlight shell
463 if ( itemShellHandle != -1 ) {
464 gameRenderWorld->FreeEntityDef( itemShellHandle );
465 itemShellHandle = -1;
469 case EVENT_RESPAWN: {
473 case EVENT_RESPAWNFX: {
478 return idEntity::ClientReceiveEvent( event, time, msg );
486 idItem::Event_DropToFloor
489 void idItem::Event_DropToFloor( void ) {
492 // don't drop the floor if bound to another entity
493 if ( GetBindMaster() != NULL && GetBindMaster() != this ) {
497 gameLocal.clip.TraceBounds( trace, renderEntity.origin, renderEntity.origin - idVec3( 0, 0, 64 ), renderEntity.bounds, MASK_SOLID | CONTENTS_CORPSE, this );
498 SetOrigin( trace.endpos );
506 void idItem::Event_Touch( idEntity *other, trace_t *trace ) {
507 if ( !other->IsType( idPlayer::Type ) ) {
515 Pickup( static_cast<idPlayer *>(other) );
520 idItem::Event_Trigger
523 void idItem::Event_Trigger( idEntity *activator ) {
525 if ( !canPickUp && spawnArgs.GetBool( "triggerFirst" ) ) {
530 if ( activator && activator->IsType( idPlayer::Type ) ) {
531 Pickup( static_cast<idPlayer *>( activator ) );
537 idItem::Event_Respawn
540 void idItem::Event_Respawn( void ) {
541 if ( gameLocal.isServer ) {
542 ServerSendEvent( EVENT_RESPAWN, NULL, false, -1 );
544 BecomeActive( TH_THINK );
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
556 idItem::Event_RespawnFx
559 void idItem::Event_RespawnFx( void ) {
560 if ( gameLocal.isServer ) {
561 ServerSendEvent( EVENT_RESPAWNFX, NULL, false, -1 );
563 const char *sfx = spawnArgs.GetString( "fxRespawn" );
565 idEntityFx::StartFx( sfx, NULL, NULL, this, true );
570 ===============================================================================
574 ===============================================================================
583 CLASS_DECLARATION( idItem, idItemPowerup )
588 idItemPowerup::idItemPowerup
591 idItemPowerup::idItemPowerup() {
601 void idItemPowerup::Save( idSaveGame *savefile ) const {
602 savefile->WriteInt( time );
603 savefile->WriteInt( type );
608 idItemPowerup::Restore
611 void idItemPowerup::Restore( idRestoreGame *savefile ) {
612 savefile->ReadInt( time );
613 savefile->ReadInt( type );
621 void idItemPowerup::Spawn( void ) {
622 time = spawnArgs.GetInt( "time", "30" );
623 type = spawnArgs.GetInt( "type", "0" );
628 idItemPowerup::GiveToPlayer
631 bool idItemPowerup::GiveToPlayer( idPlayer *player ) {
632 if ( player->spectating ) {
635 player->GivePowerUp( type, time * 1000 );
640 ===============================================================================
644 ===============================================================================
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 )
656 idObjective::idObjective
659 idObjective::idObjective() {
668 void idObjective::Save( idSaveGame *savefile ) const {
669 savefile->WriteVec3( playerPos );
677 void idObjective::Restore( idRestoreGame *savefile ) {
678 savefile->ReadVec3( playerPos );
679 PostEventMS( &EV_CamShot, 250 );
687 void idObjective::Spawn( void ) {
689 PostEventMS( &EV_CamShot, 250 );
694 idObjective::Event_Screenshot
697 void idObjective::Event_CamShot( ) {
699 idStr shotName = gameLocal.GetMapName();
700 shotName.StripFileExtension();
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();
722 idObjective::Event_Trigger
725 void idObjective::Event_Trigger( idEntity *activator ) {
726 idPlayer *player = gameLocal.GetLocalPlayer();
731 if ( spawnArgs.GetString( "inv_objective", NULL ) ) {
732 if ( player && player->hud ) {
733 idStr shotName = gameLocal.GetMapName();
734 shotName.StripFileExtension();
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 );
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 );
754 PostEventMS( &EV_GetPlayerPos, 2000 );
762 idObjective::Event_GetPlayerPos
765 void idObjective::Event_GetPlayerPos() {
766 idPlayer *player = gameLocal.GetLocalPlayer();
768 playerPos = player->GetPhysics()->GetOrigin();
769 PostEventMS( &EV_HideObjective, 100, player );
775 idObjective::Event_HideObjective
778 void idObjective::Event_HideObjective(idEntity *e) {
779 idPlayer *player = gameLocal.GetLocalPlayer();
781 idVec3 v = player->GetPhysics()->GetOrigin() - playerPos;
782 if ( v.Length() > 64.0f ) {
783 player->HideObjective();
784 PostEventMS( &EV_Remove, 0 );
786 PostEventMS( &EV_HideObjective, 100, player );
792 ===============================================================================
796 ===============================================================================
799 CLASS_DECLARATION( idItem, idVideoCDItem )
807 void idVideoCDItem::Spawn( void ) {
812 idVideoCDItem::GiveToPlayer
815 bool idVideoCDItem::GiveToPlayer( idPlayer *player ) {
816 idStr str = spawnArgs.GetString( "video" );
817 if ( player && str.Length() ) {
818 player->GiveVideo( str, &spawnArgs );
824 ===============================================================================
828 ===============================================================================
831 CLASS_DECLARATION( idItem, idPDAItem )
836 idPDAItem::GiveToPlayer
839 bool idPDAItem::GiveToPlayer(idPlayer *player) {
840 const char *str = spawnArgs.GetString( "pda_name" );
842 player->GivePDA( str, &spawnArgs );
848 ===============================================================================
852 ===============================================================================
855 CLASS_DECLARATION( idItem, idMoveableItem )
856 EVENT( EV_DropToFloor, idMoveableItem::Event_DropToFloor )
857 EVENT( EV_Gib, idMoveableItem::Event_Gib )
862 idMoveableItem::idMoveableItem
865 idMoveableItem::idMoveableItem() {
873 idMoveableItem::~idMoveableItem
876 idMoveableItem::~idMoveableItem() {
887 void idMoveableItem::Save( idSaveGame *savefile ) const {
888 savefile->WriteStaticObject( physicsObj );
890 savefile->WriteClipModel( trigger );
892 savefile->WriteParticle( smoke );
893 savefile->WriteInt( smokeTime );
898 idMoveableItem::Restore
901 void idMoveableItem::Restore( idRestoreGame *savefile ) {
902 savefile->ReadStaticObject( physicsObj );
903 RestorePhysics( &physicsObj );
905 savefile->ReadClipModel( trigger );
907 savefile->ReadParticle( smoke );
908 savefile->ReadInt( smokeTime );
913 idMoveableItem::Spawn
916 void idMoveableItem::Spawn( void ) {
918 float density, friction, bouncyness, tsize;
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 );
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
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() );
940 // if the model should be shrinked
941 if ( spawnArgs.GetBool( "clipshrink" ) ) {
942 trm.Shrink( CM_CLIP_EPSILON );
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 );
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 );
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 );
977 idMoveableItem::Think
980 void idMoveableItem::Think( void ) {
984 if ( thinkFlags & TH_PHYSICS ) {
985 // update trigger position
986 trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity );
989 if ( thinkFlags & TH_UPDATEPARTICLES ) {
990 if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
992 BecomeInactive( TH_UPDATEPARTICLES );
1001 idMoveableItem::Pickup
1004 bool idMoveableItem::Pickup( idPlayer *player ) {
1005 bool ret = idItem::Pickup( player );
1007 trigger->SetContents( 0 );
1014 idMoveableItem::DropItem
1017 idEntity *idMoveableItem::DropItem( const char *classname, const idVec3 &origin, const idMat3 &axis, const idVec3 &velocity, int activateDelay, int removeDelay ) {
1021 args.Set( "classname", classname );
1022 args.Set( "dropped", "1" );
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" );
1027 if ( activateDelay ) {
1028 args.SetBool( "triggerFirst", true );
1031 gameLocal.SpawnEntityDef( args, &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 );
1041 if ( !removeDelay ) {
1042 removeDelay = 5 * 60 * 1000;
1044 // always remove a dropped item after 5 minutes in case it dropped to an unreachable location
1045 item->PostEventMS( &EV_Remove, removeDelay );
1052 idMoveableItem::DropItems
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.
1068 void idMoveableItem::DropItems( idAnimatedEntity *ent, const char *type, idList<idEntity *> *list ) {
1069 const idKeyValue *kv;
1070 const char *skinName, *c, *jointName;
1075 const idDeclSkin *skin;
1076 jointHandle_t joint;
1080 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), NULL );
1083 c = kv->GetKey().c_str() + kv->GetKey().Length();
1084 if ( idStr::Icmp( c - 5, "Joint" ) != 0 && idStr::Icmp( c - 8, "Rotation" ) != 0 ) {
1086 key = kv->GetKey().c_str() + 4;
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();
1097 if ( g_dropItemRotation.GetString()[0] ) {
1099 sscanf( g_dropItemRotation.GetString(), "%f %f %f", &angles.pitch, &angles.yaw, &angles.roll );
1101 key = kv->GetKey().c_str() + 4;
1103 ent->spawnArgs.GetAngles( key, "0 0 0", angles );
1105 axis = angles.ToMat3() * axis;
1107 origin += ent->spawnArgs.GetVector( key2, "0 0 0" );
1109 item = DropItem( kv->GetValue(), origin, axis, vec3_origin, 0, 0 );
1110 if ( list && item ) {
1111 list->Append( item );
1115 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv );
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 );
1127 ======================
1128 idMoveableItem::WriteToSnapshot
1129 ======================
1131 void idMoveableItem::WriteToSnapshot( idBitMsgDelta &msg ) const {
1132 physicsObj.WriteToSnapshot( msg );
1136 ======================
1137 idMoveableItem::ReadFromSnapshot
1138 ======================
1140 void idMoveableItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1141 physicsObj.ReadFromSnapshot( msg );
1142 if ( msg.HasChanged() ) {
1152 void idMoveableItem::Gib( const idVec3 &dir, const char *damageDefName ) {
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 );
1159 // remove the entity
1160 PostEventMS( &EV_Remove, 0 );
1165 idMoveableItem::Event_DropToFloor
1168 void idMoveableItem::Event_DropToFloor( void ) {
1169 // the physics will drop the moveable to the floor
1174 idMoveableItem::Event_Gib
1177 void idMoveableItem::Event_Gib( const char *damageDefName ) {
1178 Gib( idVec3( 0, 0, 1 ), damageDefName );
1182 ===============================================================================
1186 ===============================================================================
1189 CLASS_DECLARATION( idMoveableItem, idMoveablePDAItem )
1194 idMoveablePDAItem::GiveToPlayer
1197 bool idMoveablePDAItem::GiveToPlayer(idPlayer *player) {
1198 const char *str = spawnArgs.GetString( "pda_name" );
1200 player->GivePDA( str, &spawnArgs );
1206 ===============================================================================
1210 ===============================================================================
1213 CLASS_DECLARATION( idEntity, idItemRemover )
1214 EVENT( EV_Activate, idItemRemover::Event_Trigger )
1219 idItemRemover::Spawn
1222 void idItemRemover::Spawn( void ) {
1227 idItemRemover::RemoveItem
1230 void idItemRemover::RemoveItem( idPlayer *player ) {
1233 remove = spawnArgs.GetString( "remove" );
1234 player->RemoveInventoryItem( remove );
1239 idItemRemover::Event_Trigger
1242 void idItemRemover::Event_Trigger( idEntity *activator ) {
1243 if ( activator->IsType( idPlayer::Type ) ) {
1244 RemoveItem( static_cast<idPlayer *>(activator) );
1249 ===============================================================================
1253 ===============================================================================
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 )
1264 idObjectiveComplete::idObjectiveComplete
1267 idObjectiveComplete::idObjectiveComplete() {
1273 idObjectiveComplete::Save
1276 void idObjectiveComplete::Save( idSaveGame *savefile ) const {
1277 savefile->WriteVec3( playerPos );
1282 idObjectiveComplete::Restore
1285 void idObjectiveComplete::Restore( idRestoreGame *savefile ) {
1286 savefile->ReadVec3( playerPos );
1291 idObjectiveComplete::Spawn
1294 void idObjectiveComplete::Spawn( void ) {
1295 spawnArgs.SetBool( "objEnabled", false );
1301 idObjectiveComplete::Event_Trigger
1304 void idObjectiveComplete::Event_Trigger( idEntity *activator ) {
1305 if ( !spawnArgs.GetBool( "objEnabled" ) ) {
1308 idPlayer *player = gameLocal.GetLocalPlayer();
1310 RemoveItem( player );
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 );
1326 idObjectiveComplete::Event_GetPlayerPos
1329 void idObjectiveComplete::Event_GetPlayerPos() {
1330 idPlayer *player = gameLocal.GetLocalPlayer();
1332 playerPos = player->GetPhysics()->GetOrigin();
1333 PostEventMS( &EV_HideObjective, 100, player );
1339 idObjectiveComplete::Event_HideObjective
1342 void idObjectiveComplete::Event_HideObjective( idEntity *e ) {
1343 idPlayer *player = gameLocal.GetLocalPlayer();
1345 idVec3 v = player->GetPhysics()->GetOrigin();
1347 if ( v.Length() > 64.0f ) {
1348 player->hud->HandleNamedEvent( "closeObjective" );
1349 PostEventMS( &EV_Remove, 0 );
1351 PostEventMS( &EV_HideObjective, 100, player );