/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 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. =========================================================================== */ #include "../idlib/precompiled.h" #pragma hdrstop #include "Game_local.h" /* =============================================================================== idItem =============================================================================== */ const idEventDef EV_DropToFloor( "" ); const idEventDef EV_RespawnItem( "respawn" ); const idEventDef EV_RespawnFx( "" ); const idEventDef EV_GetPlayerPos( "" ); const idEventDef EV_HideObjective( "", "e" ); CLASS_DECLARATION( idEntity, idItem ) EVENT( EV_DropToFloor, idItem::Event_DropToFloor ) EVENT( EV_Touch, idItem::Event_Touch ) EVENT( EV_Activate, idItem::Event_Trigger ) EVENT( EV_RespawnItem, idItem::Event_Respawn ) EVENT( EV_RespawnFx, idItem::Event_RespawnFx ) END_CLASS /* ================ idItem::idItem ================ */ idItem::idItem() : clientPredictPickupMilliseconds( 0 ) { spin = false; inView = false; inViewTime = 0; lastCycle = 0; lastRenderViewTime = -1; itemShellHandle = -1; shellMaterial = NULL; orgOrigin.Zero(); canPickUp = true; fl.networkSync = true; } /* ================ idItem::~idItem ================ */ idItem::~idItem() { // remove the highlight shell if( itemShellHandle != -1 ) { gameRenderWorld->FreeEntityDef( itemShellHandle ); } } /* ================ idItem::Save ================ */ void idItem::Save( idSaveGame* savefile ) const { savefile->WriteVec3( orgOrigin ); savefile->WriteBool( spin ); savefile->WriteBool( pulse ); savefile->WriteBool( canPickUp ); savefile->WriteMaterial( shellMaterial ); savefile->WriteBool( inView ); savefile->WriteInt( inViewTime ); savefile->WriteInt( lastCycle ); savefile->WriteInt( lastRenderViewTime ); } /* ================ idItem::Restore ================ */ void idItem::Restore( idRestoreGame* savefile ) { savefile->ReadVec3( orgOrigin ); savefile->ReadBool( spin ); savefile->ReadBool( pulse ); savefile->ReadBool( canPickUp ); savefile->ReadMaterial( shellMaterial ); savefile->ReadBool( inView ); savefile->ReadInt( inViewTime ); savefile->ReadInt( lastCycle ); savefile->ReadInt( lastRenderViewTime ); itemShellHandle = -1; } /* ================ idItem::UpdateRenderEntity ================ */ bool idItem::UpdateRenderEntity( renderEntity_s* renderEntity, const renderView_t* renderView ) const { if( lastRenderViewTime == renderView->time[timeGroup] ) { return false; } lastRenderViewTime = renderView->time[timeGroup]; // check for glow highlighting if near the center of the view idVec3 dir = renderEntity->origin - renderView->vieworg; dir.Normalize(); float d = dir * renderView->viewaxis[0]; // two second pulse cycle float cycle = ( renderView->time[timeGroup] - inViewTime ) / 2000.0f; if( d > 0.94f ) { if( !inView ) { inView = true; if( cycle > lastCycle ) { // restart at the beginning inViewTime = renderView->time[timeGroup]; cycle = 0.0f; } } } else { if( inView ) { inView = false; lastCycle = ceil( cycle ); } } // fade down after the last pulse finishes if( !inView && cycle > lastCycle ) { renderEntity->shaderParms[4] = 0.0f; } else { // pulse up in 1/4 second cycle -= ( int )cycle; if( cycle < 0.1f ) { renderEntity->shaderParms[4] = cycle * 10.0f; } else if( cycle < 0.2f ) { renderEntity->shaderParms[4] = 1.0f; } else if( cycle < 0.3f ) { renderEntity->shaderParms[4] = 1.0f - ( cycle - 0.2f ) * 10.0f; } else { // stay off between pulses renderEntity->shaderParms[4] = 0.0f; } } // update every single time this is in view return true; } /* ================ idItem::ModelCallback ================ */ bool idItem::ModelCallback( renderEntity_t* renderEntity, const renderView_t* renderView ) { const idItem* ent; // this may be triggered by a model trace or other non-view related source if( !renderView ) { return false; } ent = static_cast( gameLocal.entities[ renderEntity->entityNum ] ); if( ent == NULL ) { gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" ); return false; } return ent->UpdateRenderEntity( renderEntity, renderView ); } /* ================ idItem::Think ================ */ void idItem::Think() { if( thinkFlags & TH_THINK ) { if( spin ) { idAngles ang; idVec3 org; ang.pitch = ang.roll = 0.0f; ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f; SetAngles( ang ); float scale = 0.005f + entityNumber * 0.00001f; org = orgOrigin; org.z += 4.0f + cos( ( gameLocal.time + 2000 ) * scale ) * 4.0f; SetOrigin( org ); } } Present(); } /* ================ idItem::Present ================ */ void idItem::Present() { idEntity::Present(); if( !fl.hidden && pulse ) { // also add a highlight shell model renderEntity_t shell; shell = renderEntity; // we will mess with shader parms when the item is in view // to give the "item pulse" effect shell.callback = idItem::ModelCallback; shell.entityNum = entityNumber; shell.customShader = shellMaterial; if( itemShellHandle == -1 ) { itemShellHandle = gameRenderWorld->AddEntityDef( &shell ); } else { gameRenderWorld->UpdateEntityDef( itemShellHandle, &shell ); } } } /* ================ idItem::Spawn ================ */ void idItem::Spawn() { idStr giveTo; idEntity* ent; float tsize; if( spawnArgs.GetBool( "dropToFloor" ) ) { PostEventMS( &EV_DropToFloor, 0 ); } if( spawnArgs.GetFloat( "triggersize", "0", tsize ) ) { GetPhysics()->GetClipModel()->LoadModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) ); GetPhysics()->GetClipModel()->Link( gameLocal.clip ); } if( spawnArgs.GetBool( "start_off" ) ) { GetPhysics()->SetContents( 0 ); Hide(); } else { GetPhysics()->SetContents( CONTENTS_TRIGGER ); } giveTo = spawnArgs.GetString( "owner" ); if( giveTo.Length() ) { ent = gameLocal.FindEntity( giveTo ); if( !ent ) { gameLocal.Error( "Item couldn't find owner '%s'", giveTo.c_str() ); } // RB: 64 bit fixes, changed NULL to 0 PostEventMS( &EV_Touch, 0, ent, 0 ); // RB end } // idItemTeam does not rotate and bob if( spawnArgs.GetBool( "spin" ) || ( common->IsMultiplayer() && !this->IsType( idItemTeam::Type ) ) ) { spin = true; BecomeActive( TH_THINK ); } //pulse = !spawnArgs.GetBool( "nopulse" ); //temp hack for tim pulse = false; orgOrigin = GetPhysics()->GetOrigin(); canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) ); inViewTime = -1000; lastCycle = -1; itemShellHandle = -1; shellMaterial = declManager->FindMaterial( "itemHighlightShell" ); } /* ================ idItem::GetAttributes ================ */ void idItem::GetAttributes( idDict& attributes ) const { int i; const idKeyValue* arg; for( i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) { arg = spawnArgs.GetKeyVal( i ); if( arg->GetKey().Left( 4 ) == "inv_" ) { attributes.Set( arg->GetKey().Right( arg->GetKey().Length() - 4 ), arg->GetValue() ); } } } /* ================ idItem::GiveToPlayer ================ */ bool idItem::GiveToPlayer( idPlayer* player, unsigned int giveFlags ) { if( player == NULL ) { return false; } if( spawnArgs.GetBool( "inv_carry" ) ) { return player->GiveInventoryItem( &spawnArgs, giveFlags ); } return player->GiveItem( this, giveFlags ); } /* ================ idItem::Pickup ================ */ bool idItem::Pickup( idPlayer* player ) { const bool didGiveSucceed = GiveToPlayer( player, ITEM_GIVE_FEEDBACK ); if( !didGiveSucceed ) { return false; } // Store the time so clients know when to stop predicting and let snapshots overwrite. if( player->IsLocallyControlled() ) { clientPredictPickupMilliseconds = gameLocal.time; } else { clientPredictPickupMilliseconds = 0; } // play pickup sound StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); // clear our contents so the object isn't picked up twice GetPhysics()->SetContents( 0 ); // hide the model Hide(); // remove the highlight shell if( itemShellHandle != -1 ) { gameRenderWorld->FreeEntityDef( itemShellHandle ); itemShellHandle = -1; } // Clients need to bail out after some feedback, but // before actually changing any values. The values // will be updated in the next snapshot. if( common->IsClient() ) { return didGiveSucceed; } if( !GiveToPlayer( player, ITEM_GIVE_UPDATE_STATE ) ) { return false; } // trigger our targets ActivateTargets( player ); float respawn = spawnArgs.GetFloat( "respawn" ); bool dropped = spawnArgs.GetBool( "dropped" ); bool no_respawn = spawnArgs.GetBool( "no_respawn" ); if( common->IsMultiplayer() && respawn == 0.0f ) { respawn = 20.0f; } if( respawn && !dropped && !no_respawn ) { const char* sfx = spawnArgs.GetString( "fxRespawn" ); if( sfx != NULL && *sfx != '\0' ) { PostEventSec( &EV_RespawnFx, respawn - 0.5f ); } PostEventSec( &EV_RespawnItem, respawn ); } else if( !spawnArgs.GetBool( "inv_objective" ) && !no_respawn ) { // give some time for the pickup sound to play // FIXME: Play on the owner if( !spawnArgs.GetBool( "inv_carry" ) ) { PostEventMS( &EV_Remove, 5000 ); } } BecomeInactive( TH_THINK ); return true; } /* ================ idItem::ClientThink ================ */ void idItem::ClientThink( const int curTime, const float fraction, const bool predict ) { // only think forward because the state is not synced through snapshots if( !gameLocal.isNewFrame ) { return; } Think(); } /* ================ idItem::ClientPredictionThink ================ */ void idItem::ClientPredictionThink() { // only think forward because the state is not synced through snapshots if( !gameLocal.isNewFrame ) { return; } Think(); } /* ================ idItem::WriteFromSnapshot ================ */ void idItem::WriteToSnapshot( idBitMsg& msg ) const { msg.WriteBits( IsHidden(), 1 ); } /* ================ idItem::ReadFromSnapshot ================ */ void idItem::ReadFromSnapshot( const idBitMsg& msg ) { if( msg.ReadBits( 1 ) ) { Hide(); } else if( clientPredictPickupMilliseconds != 0 ) { // Fix mispredictions if( gameLocal.GetLastClientUsercmdMilliseconds( gameLocal.GetLocalClientNum() ) >= clientPredictPickupMilliseconds ) { if( GetPhysics()->GetContents() == 0 ) { GetPhysics()->SetContents( CONTENTS_TRIGGER ); } Show(); } } } /* ================ idItem::ClientReceiveEvent ================ */ bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg& msg ) { switch( event ) { case EVENT_RESPAWN: { Event_Respawn(); return true; } case EVENT_RESPAWNFX: { Event_RespawnFx(); return true; } default: { return idEntity::ClientReceiveEvent( event, time, msg ); } } } /* ================ idItem::Event_DropToFloor ================ */ void idItem::Event_DropToFloor() { trace_t trace; // don't drop the floor if bound to another entity if( GetBindMaster() != NULL && GetBindMaster() != this ) { return; } gameLocal.clip.TraceBounds( trace, renderEntity.origin, renderEntity.origin - idVec3( 0, 0, 64 ), renderEntity.bounds, MASK_SOLID | CONTENTS_CORPSE, this ); SetOrigin( trace.endpos ); } /* ================ idItem::Event_Touch ================ */ void idItem::Event_Touch( idEntity* other, trace_t* trace ) { if( !other->IsType( idPlayer::Type ) ) { return; } if( !canPickUp ) { return; } Pickup( static_cast( other ) ); } /* ================ idItem::Event_Trigger ================ */ void idItem::Event_Trigger( idEntity* activator ) { if( !canPickUp && spawnArgs.GetBool( "triggerFirst" ) ) { canPickUp = true; return; } if( activator && activator->IsType( idPlayer::Type ) ) { Pickup( static_cast( activator ) ); } } /* ================ idItem::Event_Respawn ================ */ void idItem::Event_Respawn() { if( common->IsServer() ) { ServerSendEvent( EVENT_RESPAWN, NULL, false ); } BecomeActive( TH_THINK ); Show(); inViewTime = -1000; lastCycle = -1; GetPhysics()->SetContents( CONTENTS_TRIGGER ); SetOrigin( orgOrigin ); StartSound( "snd_respawn", SND_CHANNEL_ITEM, 0, false, NULL ); CancelEvents( &EV_RespawnItem ); // don't double respawn } /* ================ idItem::Event_RespawnFx ================ */ void idItem::Event_RespawnFx() { if( common->IsServer() ) { ServerSendEvent( EVENT_RESPAWNFX, NULL, false ); } const char* sfx = spawnArgs.GetString( "fxRespawn" ); if( sfx != NULL && *sfx != '\0' ) { idEntityFx::StartFx( sfx, NULL, NULL, this, true ); } } /* =============================================================================== idItemPowerup =============================================================================== */ /* =============== idItemPowerup =============== */ CLASS_DECLARATION( idItem, idItemPowerup ) END_CLASS /* ================ idItemPowerup::idItemPowerup ================ */ idItemPowerup::idItemPowerup() { time = 0; type = 0; } /* ================ idItemPowerup::Save ================ */ void idItemPowerup::Save( idSaveGame* savefile ) const { savefile->WriteInt( time ); savefile->WriteInt( type ); } /* ================ idItemPowerup::Restore ================ */ void idItemPowerup::Restore( idRestoreGame* savefile ) { savefile->ReadInt( time ); savefile->ReadInt( type ); } /* ================ idItemPowerup::Spawn ================ */ void idItemPowerup::Spawn() { time = spawnArgs.GetInt( "time", "30" ); type = spawnArgs.GetInt( "type", "0" ); } /* ================ idItemPowerup::GiveToPlayer ================ */ bool idItemPowerup::GiveToPlayer( idPlayer* player, unsigned int giveFlags ) { if( player->spectating ) { return false; } player->GivePowerUp( type, time * 1000, giveFlags ); return true; } /* =============================================================================== idItemTeam Used for flags in Capture the Flag =============================================================================== */ // temporarely removed these events const idEventDef EV_FlagReturn( "flagreturn", "e" ); const idEventDef EV_TakeFlag( "takeflag", "e" ); const idEventDef EV_DropFlag( "dropflag", "d" ); const idEventDef EV_FlagCapture( "flagcapture" ); CLASS_DECLARATION( idItem, idItemTeam ) EVENT( EV_FlagReturn, idItemTeam::Event_FlagReturn ) EVENT( EV_TakeFlag, idItemTeam::Event_TakeFlag ) EVENT( EV_DropFlag, idItemTeam::Event_DropFlag ) EVENT( EV_FlagCapture, idItemTeam::Event_FlagCapture ) END_CLASS /* =============== idItemTeam::idItemTeam =============== */ idItemTeam::idItemTeam() { team = -1; carried = false; dropped = false; lastDrop = 0; itemGlowHandle = -1; skinDefault = NULL; skinCarried = NULL; scriptTaken = NULL; scriptDropped = NULL; scriptReturned = NULL; scriptCaptured = NULL; lastNuggetDrop = 0; nuggetName = 0; } /* =============== idItemTeam::~idItemTeam =============== */ idItemTeam::~idItemTeam() { FreeLightDef(); } /* =============== idItemTeam::Spawn =============== */ void idItemTeam::Spawn() { team = spawnArgs.GetInt( "team" ); returnOrigin = GetPhysics()->GetOrigin() + idVec3( 0, 0, 20 ); returnAxis = GetPhysics()->GetAxis(); BecomeActive( TH_THINK ); const char* skinName; skinName = spawnArgs.GetString( "skin", "" ); if( skinName[0] ) skinDefault = declManager->FindSkin( skinName ); skinName = spawnArgs.GetString( "skin_carried", "" ); if( skinName[0] ) skinCarried = declManager->FindSkin( skinName ); nuggetName = spawnArgs.GetString( "nugget_name", "" ); if( !nuggetName[0] ) { nuggetName = NULL; } scriptTaken = LoadScript( "script_taken" ); scriptDropped = LoadScript( "script_dropped" ); scriptReturned = LoadScript( "script_returned" ); scriptCaptured = LoadScript( "script_captured" ); /* Spawn attached dlight */ /* idDict args; idVec3 lightOffset( 0.0f, 20.0f, 0.0f ); // Set up the flag's dynamic light memset( &itemGlow, 0, sizeof( itemGlow ) ); itemGlow.axis = mat3_identity; itemGlow.lightRadius.x = 128.0f; itemGlow.lightRadius.y = itemGlow.lightRadius.z = itemGlow.lightRadius.x; itemGlow.noShadows = true; itemGlow.pointLight = true; itemGlow.shaderParms[ SHADERPARM_RED ] = 0.0f; itemGlow.shaderParms[ SHADERPARM_GREEN ] = 0.0f; itemGlow.shaderParms[ SHADERPARM_BLUE ] = 0.0f; itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 0.0f; // Select a shader based on the team if ( team == 0 ) itemGlow.shader = declManager->FindMaterial( "lights/redflag" ); else itemGlow.shader = declManager->FindMaterial( "lights/blueflag" ); */ idMoveableItem::Spawn(); physicsObj.SetContents( 0 ); physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); physicsObj.SetGravity( idVec3( 0, 0, spawnArgs.GetInt( "gravity", "-30" ) ) ); } /* =============== idItemTeam::LoadScript =============== */ function_t* idItemTeam::LoadScript( const char* script ) { function_t* function = NULL; idStr funcname = spawnArgs.GetString( script, "" ); if( funcname.Length() ) { function = gameLocal.program.FindFunction( funcname ); if( function == NULL ) { #ifdef _DEBUG gameLocal.Warning( "idItemTeam '%s' at (%s) calls unknown function '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString( 0 ), funcname.c_str() ); #endif } } return function; } /* =============== idItemTeam::Think =============== */ void idItemTeam::Think() { idMoveableItem::Think(); TouchTriggers(); // TODO : only update on updatevisuals /*idVec3 offset( 0.0f, 0.0f, 20.0f ); itemGlow.origin = GetPhysics()->GetOrigin() + offset; if ( itemGlowHandle == -1 ) { itemGlowHandle = gameRenderWorld->AddLightDef( &itemGlow ); } else { gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow ); }*/ #if 1 // should only the server do this? if( common->IsServer() && nuggetName && carried && ( !lastNuggetDrop || ( gameLocal.time - lastNuggetDrop ) > spawnArgs.GetInt( "nugget_frequency" ) ) ) { SpawnNugget( GetPhysics()->GetOrigin() ); lastNuggetDrop = gameLocal.time; } #endif // return dropped flag after si_flagDropTimeLimit seconds if( dropped && !carried && lastDrop != 0 && ( gameLocal.time - lastDrop ) > ( si_flagDropTimeLimit.GetInteger() * 1000 ) ) { Return(); // return flag after 30 seconds on ground return; } } /* =============== idItemTeam::Pickup =============== */ bool idItemTeam::Pickup( idPlayer* player ) { if( !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */ return false; if( gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP || gameLocal.mpGame.GetGameState() == idMultiplayerGame::COUNTDOWN ) return false; // wait 2 seconds after drop before beeing picked up again if( lastDrop != 0 && ( gameLocal.time - lastDrop ) < spawnArgs.GetInt( "pickupDelay", "500" ) ) return false; if( carried == false && player->team != this->team ) { PostEventMS( &EV_TakeFlag, 0, player ); return true; } else if( carried == false && dropped == true && player->team == this->team ) { gameLocal.mpGame.PlayerScoreCTF( player->entityNumber, 5 ); // return flag PostEventMS( &EV_FlagReturn, 0, player ); return false; } return false; } /* =============== idItemTeam::ClientReceiveEvent =============== */ bool idItemTeam::ClientReceiveEvent( int event, int time, const idBitMsg& msg ) { gameLocal.DPrintf( "ClientRecieveEvent: %i\n", event ); switch( event ) { case EVENT_TAKEFLAG: { idPlayer* player = static_cast( gameLocal.entities[ msg.ReadBits( GENTITYNUM_BITS ) ] ); if( player == NULL ) { gameLocal.Warning( "NULL player takes flag?\n" ); return false; } Event_TakeFlag( player ); } return true; case EVENT_DROPFLAG : { bool death = bool( msg.ReadBits( 1 ) == 1 ); Event_DropFlag( death ); } return true; case EVENT_FLAGRETURN : { Hide(); FreeModelDef(); FreeLightDef(); Event_FlagReturn(); } return true; case EVENT_FLAGCAPTURE : { Hide(); FreeModelDef(); FreeLightDef(); Event_FlagCapture(); } return true; }; return false; } /* ================ idItemTeam::Drop ================ */ void idItemTeam::Drop( bool death ) { // PostEventMS( &EV_DropFlag, 0, int(death == true) ); // had to remove the delayed drop because of drop flag on disconnect Event_DropFlag( death ); } /* ================ idItemTeam::Return ================ */ void idItemTeam::Return( idPlayer* player ) { if( team != 0 && team != 1 ) return; // PostEventMS( &EV_FlagReturn, 0 ); Event_FlagReturn(); } /* ================ idItemTeam::Capture ================ */ void idItemTeam::Capture() { if( team != 0 && team != 1 ) return; PostEventMS( &EV_FlagCapture, 0 ); } /* ================ idItemTeam::PrivateReturn ================ */ void idItemTeam::PrivateReturn() { Unbind(); if( common->IsServer() && carried && !dropped ) { int playerIdx = gameLocal.mpGame.GetFlagCarrier( 1 - team ); if( playerIdx != -1 ) { idPlayer* player = static_cast( gameLocal.entities[ playerIdx ] ); player->carryingFlag = false; } else { gameLocal.Warning( "BUG: carried flag has no carrier before return" ); } } dropped = false; carried = false; SetOrigin( returnOrigin ); SetAxis( returnAxis ); trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity ); SetSkin( skinDefault ); // Turn off the light /*itemGlow.shaderParms[ SHADERPARM_RED ] = 0.0f; itemGlow.shaderParms[ SHADERPARM_GREEN ] = 0.0f; itemGlow.shaderParms[ SHADERPARM_BLUE ] = 0.0f; itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 0.0f; if ( itemGlowHandle != -1 ) gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );*/ GetPhysics()->SetLinearVelocity( idVec3( 0, 0, 0 ) ); GetPhysics()->SetAngularVelocity( idVec3( 0, 0, 0 ) ); } /* ================ idItemTeam::Event_TakeFlag ================ */ void idItemTeam::Event_TakeFlag( idPlayer* player ) { gameLocal.DPrintf( "Event_TakeFlag()!\n" ); assert( player != NULL ); if( player->carryingFlag ) { // Don't do anything if the player is already carrying the flag. // Prevents duplicate messages. return; } if( common->IsServer() ) { idBitMsg msg; byte msgBuf[MAX_EVENT_PARAM_SIZE]; // Send the event msg.InitWrite( msgBuf, sizeof( msgBuf ) ); msg.BeginWriting(); msg.WriteBits( player->entityNumber, GENTITYNUM_BITS ); ServerSendEvent( EVENT_TAKEFLAG, &msg, false ); gameLocal.mpGame.PlayTeamSound( player->team, SND_FLAG_TAKEN_THEIRS ); gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_TAKEN_YOURS ); gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGTAKEN, team, player->entityNumber ); // dont drop a nugget RIGHT away lastNuggetDrop = gameLocal.time - gameLocal.random.RandomInt( 1000 ); } BindToJoint( player, g_flagAttachJoint.GetString(), true ); idVec3 origin( g_flagAttachOffsetX.GetFloat(), g_flagAttachOffsetY.GetFloat(), g_flagAttachOffsetZ.GetFloat() ); idAngles angle( g_flagAttachAngleX.GetFloat(), g_flagAttachAngleY.GetFloat(), g_flagAttachAngleZ.GetFloat() ); SetAngles( angle ); SetOrigin( origin ); // Turn the light on /*itemGlow.shaderParms[ SHADERPARM_RED ] = 1.0f; itemGlow.shaderParms[ SHADERPARM_GREEN ] = 1.0f; itemGlow.shaderParms[ SHADERPARM_BLUE ] = 1.0f; itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 1.0f; if ( itemGlowHandle != -1 ) gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );*/ if( scriptTaken ) { idThread* thread = new idThread(); thread->CallFunction( scriptTaken, false ); thread->DelayedStart( 0 ); } dropped = false; carried = true; player->carryingFlag = true; SetSkin( skinCarried ); UpdateVisuals(); UpdateGuis(); if( common->IsServer() ) { if( team == 0 ) gameLocal.mpGame.player_red_flag = player->entityNumber; else gameLocal.mpGame.player_blue_flag = player->entityNumber; } } /* ================ idItemTeam::Event_DropFlag ================ */ void idItemTeam::Event_DropFlag( bool death ) { gameLocal.DPrintf( "Event_DropFlag()!\n" ); if( common->IsServer() ) { idBitMsg msg; byte msgBuf[MAX_EVENT_PARAM_SIZE]; // Send the event msg.InitWrite( msgBuf, sizeof( msgBuf ) ); msg.BeginWriting(); msg.WriteBits( death, 1 ); ServerSendEvent( EVENT_DROPFLAG, &msg, false ); if( gameLocal.mpGame.IsFlagMsgOn() ) { gameLocal.mpGame.PlayTeamSound( 1 - team, SND_FLAG_DROPPED_THEIRS ); gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_DROPPED_YOURS ); gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGDROP, team ); } } lastDrop = gameLocal.time; BecomeActive( TH_THINK ); Show(); if( death ) GetPhysics()->SetLinearVelocity( idVec3( 0, 0, 0 ) ); else GetPhysics()->SetLinearVelocity( idVec3( 0, 0, 20 ) ); GetPhysics()->SetAngularVelocity( idVec3( 0, 0, 0 ) ); // GetPhysics()->SetLinearVelocity( ( GetPhysics()->GetLinearVelocity() * GetBindMaster()->GetPhysics()->GetAxis() ) + GetBindMaster()->GetPhysics()->GetLinearVelocity() ); if( GetBindMaster() ) { const idBounds bounds = GetPhysics()->GetBounds(); idVec3 origin = GetBindMaster()->GetPhysics()->GetOrigin() + idVec3( 0, 0, ( bounds[1].z - bounds[0].z ) * 0.6f ); Unbind(); SetOrigin( origin ); } idAngles angle = GetPhysics()->GetAxis().ToAngles(); angle.roll = 0; angle.pitch = 0; SetAxis( angle.ToMat3() ); dropped = true; carried = false; if( scriptDropped ) { idThread* thread = new idThread(); thread->CallFunction( scriptDropped, false ); thread->DelayedStart( 0 ); } SetSkin( skinDefault ); UpdateVisuals(); UpdateGuis(); if( common->IsServer() ) { if( team == 0 ) gameLocal.mpGame.player_red_flag = -1; else gameLocal.mpGame.player_blue_flag = -1; } } /* ================ idItemTeam::Event_FlagReturn ================ */ void idItemTeam::Event_FlagReturn( idPlayer* player ) { gameLocal.DPrintf( "Event_FlagReturn()!\n" ); if( common->IsServer() ) { ServerSendEvent( EVENT_FLAGRETURN, NULL, false ); if( gameLocal.mpGame.IsFlagMsgOn() ) { gameLocal.mpGame.PlayTeamSound( 1 - team, SND_FLAG_RETURN ); gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_RETURN ); int entitynum = 255; if( player ) { entitynum = player->entityNumber; } gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGRETURN, team, entitynum ); } } BecomeActive( TH_THINK ); Show(); PrivateReturn(); if( scriptReturned ) { idThread* thread = new idThread(); thread->CallFunction( scriptReturned, false ); thread->DelayedStart( 0 ); } UpdateVisuals(); UpdateGuis(); // Present(); if( common->IsServer() ) { if( team == 0 ) gameLocal.mpGame.player_red_flag = -1; else gameLocal.mpGame.player_blue_flag = -1; } } /* ================ idItemTeam::Event_FlagCapture ================ */ void idItemTeam::Event_FlagCapture() { gameLocal.DPrintf( "Event_FlagCapture()!\n" ); if( common->IsServer() ) { int playerIdx = gameLocal.mpGame.GetFlagCarrier( 1 - team ); if( playerIdx != -1 ) { ServerSendEvent( EVENT_FLAGCAPTURE, NULL, false ); gameLocal.mpGame.PlayTeamSound( 1 - team, SND_FLAG_CAPTURED_THEIRS ); gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_CAPTURED_YOURS ); gameLocal.mpGame.TeamScoreCTF( 1 - team, 1 ); gameLocal.mpGame.PlayerScoreCTF( playerIdx, 10 ); gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGCAPTURE, team, playerIdx ); } else { playerIdx = 255; } } BecomeActive( TH_THINK ); Show(); PrivateReturn(); if( scriptCaptured ) { idThread* thread = new idThread(); thread->CallFunction( scriptCaptured, false ); thread->DelayedStart( 0 ); } UpdateVisuals(); UpdateGuis(); if( common->IsServer() ) { if( team == 0 ) gameLocal.mpGame.player_red_flag = -1; else gameLocal.mpGame.player_blue_flag = -1; } } /* ================ idItemTeam::FreeLightDef ================ */ void idItemTeam::FreeLightDef() { if( itemGlowHandle != -1 ) { gameRenderWorld->FreeLightDef( itemGlowHandle ); itemGlowHandle = -1; } } /* ================ idItemTeam::SpawnNugget ================ */ void idItemTeam::SpawnNugget( idVec3 pos ) { idAngles angle( gameLocal.random.RandomInt( spawnArgs.GetInt( "nugget_pitch", "30" ) ), gameLocal.random.RandomInt( spawnArgs.GetInt( "nugget_yaw", "360" ) ), 0 ); float velocity = float( gameLocal.random.RandomInt( 40 ) + 15 ); velocity *= spawnArgs.GetFloat( "nugget_velocity", "1" ); idEntity* ent = idMoveableItem::DropItem( nuggetName, pos, GetPhysics()->GetAxis(), angle.ToMat3() * idVec3( velocity, velocity, velocity ), 0, spawnArgs.GetInt( "nugget_removedelay" ) ); idPhysics_RigidBody* physics = static_cast( ent->GetPhysics() ); if( physics != NULL && physics->IsType( idPhysics_RigidBody::Type ) ) { physics->DisableImpact(); } } /* ================ idItemTeam::Event_FlagCapture ================ */ void idItemTeam::WriteToSnapshot( idBitMsg& msg ) const { msg.WriteBits( carried, 1 ); msg.WriteBits( dropped, 1 ); WriteBindToSnapshot( msg ); idMoveableItem::WriteToSnapshot( msg ); } /* ================ idItemTeam::ReadFromSnapshot ================ */ void idItemTeam::ReadFromSnapshot( const idBitMsg& msg ) { carried = msg.ReadBits( 1 ) == 1; dropped = msg.ReadBits( 1 ) == 1; ReadBindFromSnapshot( msg ); if( msg.HasChanged() ) { UpdateGuis(); if( carried == true ) SetSkin( skinCarried ); else SetSkin( skinDefault ); } idMoveableItem::ReadFromSnapshot( msg ); } /* ================ idItemTeam::UpdateGuis Update all client's huds wrt the flag status. ================ */ void idItemTeam::UpdateGuis() { idPlayer* player; for( int i = 0; i < gameLocal.numClients; i++ ) { player = static_cast( gameLocal.entities[ i ] ); if( player && player->hud ) { player->hud->SetFlagState( 0, gameLocal.mpGame.GetFlagStatus( 0 ) ); player->hud->SetFlagState( 1, gameLocal.mpGame.GetFlagStatus( 1 ) ); player->hud->SetTeamScore( 0, gameLocal.mpGame.GetFlagPoints( 0 ) ); player->hud->SetTeamScore( 1, gameLocal.mpGame.GetFlagPoints( 1 ) ); } } } /* ================ idItemTeam::Present ================ */ void idItemTeam::Present() { // hide the flag for localplayer if in first person if( carried && GetBindMaster() ) { idPlayer* player = static_cast( GetBindMaster() ); if( player == gameLocal.GetLocalPlayer() && !pm_thirdPerson.GetBool() ) { FreeModelDef(); BecomeActive( TH_UPDATEVISUALS ); return; } } idEntity::Present(); } /* =============================================================================== idObjective =============================================================================== */ CLASS_DECLARATION( idItem, idObjective ) EVENT( EV_Activate, idObjective::Event_Trigger ) EVENT( EV_HideObjective, idObjective::Event_HideObjective ) EVENT( EV_GetPlayerPos, idObjective::Event_GetPlayerPos ) END_CLASS /* ================ idObjective::idObjective ================ */ idObjective::idObjective() { playerPos.Zero(); } /* ================ idObjective::Save ================ */ void idObjective::Save( idSaveGame* savefile ) const { savefile->WriteVec3( playerPos ); savefile->WriteMaterial( screenshot ); } /* ================ idObjective::Restore ================ */ void idObjective::Restore( idRestoreGame* savefile ) { savefile->ReadVec3( playerPos ); savefile->ReadMaterial( screenshot ); } /* ================ idObjective::Spawn ================ */ void idObjective::Spawn() { Hide(); idStr shotName; shotName = gameLocal.GetMapName(); shotName.StripFileExtension(); shotName += "/"; shotName += spawnArgs.GetString( "screenshot" ); shotName.SetFileExtension( ".tga" ); screenshot = declManager->FindMaterial( shotName ); } /* ================ idObjective::Event_Trigger ================ */ void idObjective::Event_Trigger( idEntity* activator ) { idPlayer* player = gameLocal.GetLocalPlayer(); if( player ) { //Pickup( player ); if( spawnArgs.GetString( "inv_objective", NULL ) ) { if( player ) { player->GiveObjective( spawnArgs.GetString( "objectivetitle" ), spawnArgs.GetString( "objectivetext" ), screenshot ); // a tad slow but keeps from having to update all objectives in all maps with a name ptr for( int i = 0; i < gameLocal.num_entities; i++ ) { if( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idObjectiveComplete::Type ) ) { if( idStr::Icmp( spawnArgs.GetString( "objectivetitle" ), gameLocal.entities[ i ]->spawnArgs.GetString( "objectivetitle" ) ) == 0 ) { gameLocal.entities[ i ]->spawnArgs.SetBool( "objEnabled", true ); break; } } } PostEventMS( &EV_GetPlayerPos, 2000 ); } } } } /* ================ idObjective::Event_GetPlayerPos ================ */ void idObjective::Event_GetPlayerPos() { idPlayer* player = gameLocal.GetLocalPlayer(); if( player ) { playerPos = player->GetPhysics()->GetOrigin(); PostEventMS( &EV_HideObjective, 100, player ); } } /* ================ idObjective::Event_HideObjective ================ */ void idObjective::Event_HideObjective( idEntity* e ) { idPlayer* player = gameLocal.GetLocalPlayer(); if( player ) { idVec3 v = player->GetPhysics()->GetOrigin() - playerPos; if( v.Length() > 64.0f ) { player->HideObjective(); PostEventMS( &EV_Remove, 0 ); } else { PostEventMS( &EV_HideObjective, 100, player ); } } } /* =============================================================================== idVideoCDItem =============================================================================== */ CLASS_DECLARATION( idItem, idVideoCDItem ) END_CLASS /* ================ idVideoCDItem::GiveToPlayer ================ */ bool idVideoCDItem::GiveToPlayer( idPlayer* player, unsigned int giveFlags ) { if( player == NULL ) { return false; } const idDeclVideo* video = static_cast( declManager->FindType( DECL_VIDEO, spawnArgs.GetString( "video" ), false ) ); if( video == NULL ) { return false; } if( giveFlags & ITEM_GIVE_UPDATE_STATE ) { player->GiveVideo( video, spawnArgs.GetString( "inv_name" ) ); } return true; } /* =============================================================================== idPDAItem =============================================================================== */ CLASS_DECLARATION( idItem, idPDAItem ) END_CLASS /* ================ idPDAItem::GiveToPlayer ================ */ bool idPDAItem::GiveToPlayer( idPlayer* player, unsigned int giveFlags ) { if( player == NULL ) { return false; } const char* pdaName = spawnArgs.GetString( "pda_name" ); const char* invName = spawnArgs.GetString( "inv_name" ); const idDeclPDA* pda = NULL; if( pdaName != NULL && pdaName[0] != 0 ) { // An empty PDA name is legitimate, it means the personal PDA // But if the PDA name is not empty, it should be valid pda = static_cast( declManager->FindType( DECL_PDA, pdaName, false ) ); if( pda == NULL ) { idLib::Warning( "PDA Item '%s' references unknown PDA %s", GetName(), pdaName ); return false; } } if( giveFlags & ITEM_GIVE_UPDATE_STATE ) { player->GivePDA( pda, invName ); } return true; } /* =============================================================================== idMoveableItem =============================================================================== */ CLASS_DECLARATION( idItem, idMoveableItem ) EVENT( EV_DropToFloor, idMoveableItem::Event_DropToFloor ) EVENT( EV_Gib, idMoveableItem::Event_Gib ) END_CLASS /* ================ idMoveableItem::idMoveableItem ================ */ idMoveableItem::idMoveableItem() { trigger = NULL; smoke = NULL; smokeTime = 0; nextSoundTime = 0; repeatSmoke = false; } /* ================ idMoveableItem::~idMoveableItem ================ */ idMoveableItem::~idMoveableItem() { if( trigger ) { delete trigger; } } /* ================ idMoveableItem::Save ================ */ void idMoveableItem::Save( idSaveGame* savefile ) const { savefile->WriteStaticObject( physicsObj ); savefile->WriteClipModel( trigger ); savefile->WriteParticle( smoke ); savefile->WriteInt( smokeTime ); savefile->WriteInt( nextSoundTime ); } /* ================ idMoveableItem::Restore ================ */ void idMoveableItem::Restore( idRestoreGame* savefile ) { savefile->ReadStaticObject( physicsObj ); RestorePhysics( &physicsObj ); savefile->ReadClipModel( trigger ); savefile->ReadParticle( smoke ); savefile->ReadInt( smokeTime ); savefile->ReadInt( nextSoundTime ); } /* ================ idMoveableItem::Spawn ================ */ void idMoveableItem::Spawn() { idTraceModel trm; float density, friction, bouncyness, tsize; idStr clipModelName; idBounds bounds; SetTimeState ts( timeGroup ); // create a trigger for item pickup spawnArgs.GetFloat( "triggersize", "16.0", tsize ); trigger = new( TAG_PHYSICS_CLIP_ENTITY ) idClipModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) ); trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ); trigger->SetContents( CONTENTS_TRIGGER ); // check if a clip model is set spawnArgs.GetString( "clipmodel", "", clipModelName ); if( !clipModelName[0] ) { clipModelName = spawnArgs.GetString( "model" ); // use the visual model } // load the trace model if( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) { gameLocal.Error( "idMoveableItem '%s': cannot load collision model %s", name.c_str(), clipModelName.c_str() ); return; } // if the model should be shrinked if( spawnArgs.GetBool( "clipshrink" ) ) { trm.Shrink( CM_CLIP_EPSILON ); } // get rigid body properties spawnArgs.GetFloat( "density", "0.5", density ); density = idMath::ClampFloat( 0.001f, 1000.0f, density ); spawnArgs.GetFloat( "friction", "0.05", friction ); friction = idMath::ClampFloat( 0.0f, 1.0f, friction ); spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness ); bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness ); // setup the physics physicsObj.SetSelf( this ); physicsObj.SetClipModel( new( TAG_PHYSICS_CLIP_ENTITY ) idClipModel( trm ), density ); physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); physicsObj.SetAxis( GetPhysics()->GetAxis() ); physicsObj.SetBouncyness( bouncyness ); physicsObj.SetFriction( 0.6f, 0.6f, friction ); physicsObj.SetGravity( gameLocal.GetGravity() ); physicsObj.SetContents( CONTENTS_RENDERMODEL ); physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); SetPhysics( &physicsObj ); if( spawnArgs.GetBool( "nodrop" ) ) { physicsObj.PutToRest(); } smoke = NULL; smokeTime = 0; nextSoundTime = 0; const char* smokeName = spawnArgs.GetString( "smoke_trail" ); if( *smokeName != '\0' ) { smoke = static_cast( declManager->FindType( DECL_PARTICLE, smokeName ) ); smokeTime = gameLocal.time; BecomeActive( TH_UPDATEPARTICLES ); } repeatSmoke = spawnArgs.GetBool( "repeatSmoke", "0" ); } /* ================ idItem::ClientThink ================ */ void idMoveableItem::ClientThink( const int curTime, const float fraction, const bool predict ) { InterpolatePhysicsOnly( fraction ); if( thinkFlags & TH_PHYSICS ) { // update trigger position trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity ); } Present(); } /* ================ idMoveableItem::Think ================ */ void idMoveableItem::Think() { RunPhysics(); if( thinkFlags & TH_PHYSICS ) { // update trigger position trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity ); } if( thinkFlags & TH_UPDATEPARTICLES ) { if( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) { if( !repeatSmoke ) { smokeTime = 0; BecomeInactive( TH_UPDATEPARTICLES ); } else { smokeTime = gameLocal.time; } } } Present(); } /* ================= idMoveableItem::Collide ================= */ bool idMoveableItem::Collide( const trace_t& collision, const idVec3& velocity ) { float v, f; v = -( velocity * collision.c.normal ); if( v > 80 && gameLocal.time > nextSoundTime ) { f = v > 200 ? 1.0f : idMath::Sqrt( v - 80 ) * 0.091f; if( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) { // don't set the volume unless there is a bounce sound as it overrides the entire channel // which causes footsteps on ai's to not honor their shader parms SetSoundVolume( f ); } nextSoundTime = gameLocal.time + 500; } return false; } /* ================ idMoveableItem::Pickup ================ */ bool idMoveableItem::Pickup( idPlayer* player ) { bool ret = idItem::Pickup( player ); if( ret ) { trigger->SetContents( 0 ); } return ret; } /* ================ idMoveableItem::DropItem ================ */ idEntity* idMoveableItem::DropItem( const char* classname, const idVec3& origin, const idMat3& axis, const idVec3& velocity, int activateDelay, int removeDelay ) { idDict args; idEntity* item; args.Set( "classname", classname ); args.Set( "dropped", "1" ); // we sometimes drop idMoveables here, so set 'nodrop' to 1 so that it doesn't get put on the floor args.Set( "nodrop", "1" ); if( activateDelay ) { args.SetBool( "triggerFirst", true ); } gameLocal.SpawnEntityDef( args, &item ); if( item ) { // set item position item->GetPhysics()->SetOrigin( origin ); item->GetPhysics()->SetAxis( axis ); item->GetPhysics()->SetLinearVelocity( velocity ); item->UpdateVisuals(); if( activateDelay ) { item->PostEventMS( &EV_Activate, activateDelay, item ); } if( !removeDelay ) { removeDelay = 5 * 60 * 1000; } // always remove a dropped item after 5 minutes in case it dropped to an unreachable location item->PostEventMS( &EV_Remove, removeDelay ); } return item; } /* ================ idMoveableItem::DropItems The entity should have the following key/value pairs set: "def_dropItem" "item def" "dropItemJoint" "joint name" "dropItemRotation" "pitch yaw roll" "dropItemOffset" "x y z" "skin_drop" "skin name" To drop multiple items the following key/value pairs can be used: "def_dropItem" "item def" "dropItemJoint" "joint name" "dropItemRotation" "pitch yaw roll" "dropItemOffset" "x y z" where is an aribtrary string. ================ */ void idMoveableItem::DropItems( idAnimatedEntity* ent, const char* type, idList* list ) { const idKeyValue* kv; const char* skinName, *c, *jointName; idStr key, key2; idVec3 origin; idMat3 axis; idAngles angles; const idDeclSkin* skin; jointHandle_t joint; idEntity* item; // drop all items kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), NULL ); while( kv ) { c = kv->GetKey().c_str() + kv->GetKey().Length(); if( idStr::Icmp( c - 5, "Joint" ) != 0 && idStr::Icmp( c - 8, "Rotation" ) != 0 ) { key = kv->GetKey().c_str() + 4; key2 = key; key += "Joint"; key2 += "Offset"; jointName = ent->spawnArgs.GetString( key ); joint = ent->GetAnimator()->GetJointHandle( jointName ); if( !ent->GetJointWorldTransform( joint, gameLocal.time, origin, axis ) ) { gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", key.c_str(), jointName, ent->name.c_str() ); origin = ent->GetPhysics()->GetOrigin(); axis = ent->GetPhysics()->GetAxis(); } if( g_dropItemRotation.GetString()[0] ) { angles.Zero(); sscanf( g_dropItemRotation.GetString(), "%f %f %f", &angles.pitch, &angles.yaw, &angles.roll ); } else { key = kv->GetKey().c_str() + 4; key += "Rotation"; ent->spawnArgs.GetAngles( key, "0 0 0", angles ); } axis = angles.ToMat3() * axis; origin += ent->spawnArgs.GetVector( key2, "0 0 0" ); item = DropItem( kv->GetValue(), origin, axis, vec3_origin, 0, 0 ); if( list && item ) { list->Append( item ); } } kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv ); } // change the skin to hide all items skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) ); if( skinName[0] ) { skin = declManager->FindSkin( skinName ); ent->SetSkin( skin ); } } /* ====================== idMoveableItem::WriteToSnapshot ====================== */ void idMoveableItem::WriteToSnapshot( idBitMsg& msg ) const { physicsObj.WriteToSnapshot( msg ); msg.WriteBool( IsHidden() ); } /* ====================== idMoveableItem::ReadFromSnapshot ====================== */ void idMoveableItem::ReadFromSnapshot( const idBitMsg& msg ) { physicsObj.ReadFromSnapshot( msg ); const bool snapshotHidden = msg.ReadBool(); if( snapshotHidden ) { Hide(); } else if( GetPredictPickupMilliseconds() != 0 ) { if( gameLocal.GetLastClientUsercmdMilliseconds( gameLocal.GetLocalClientNum() ) >= GetPredictPickupMilliseconds() ) { if( trigger->GetContents() == 0 ) { trigger->SetContents( CONTENTS_TRIGGER ); } Show(); } } if( msg.HasChanged() ) { UpdateVisuals(); } } /* ============ idMoveableItem::Gib ============ */ void idMoveableItem::Gib( const idVec3& dir, const char* damageDefName ) { // spawn smoke puff const char* smokeName = spawnArgs.GetString( "smoke_gib" ); if( *smokeName != '\0' ) { const idDeclParticle* smoke = static_cast( declManager->FindType( DECL_PARTICLE, smokeName ) ); gameLocal.smokeParticles->EmitSmoke( smoke, gameLocal.time, gameLocal.random.CRandomFloat(), renderEntity.origin, renderEntity.axis, timeGroup /*_D3XP*/ ); } // remove the entity PostEventMS( &EV_Remove, 0 ); } /* ================ idMoveableItem::Event_DropToFloor ================ */ void idMoveableItem::Event_DropToFloor() { // the physics will drop the moveable to the floor } /* ============ idMoveableItem::Event_Gib ============ */ void idMoveableItem::Event_Gib( const char* damageDefName ) { Gib( idVec3( 0, 0, 1 ), damageDefName ); } /* =============================================================================== idMoveablePDAItem =============================================================================== */ CLASS_DECLARATION( idMoveableItem, idMoveablePDAItem ) END_CLASS /* ================ idMoveablePDAItem::GiveToPlayer ================ */ bool idMoveablePDAItem::GiveToPlayer( idPlayer* player, unsigned int giveFlags ) { if( player == NULL ) { return false; } const char* pdaName = spawnArgs.GetString( "pda_name" ); const char* invName = spawnArgs.GetString( "inv_name" ); const idDeclPDA* pda = NULL; if( pdaName != NULL && pdaName[0] != 0 ) { // An empty PDA name is legitimate, it means the personal PDA // But if the PDA name is not empty, it should be valid pda = static_cast( declManager->FindType( DECL_PDA, pdaName, false ) ); if( pda == NULL ) { idLib::Warning( "PDA Item '%s' references unknown PDA %s", GetName(), pdaName ); return false; } } if( giveFlags & ITEM_GIVE_UPDATE_STATE ) { player->GivePDA( pda, invName ); } return true; } /* =============================================================================== idItemRemover =============================================================================== */ CLASS_DECLARATION( idEntity, idItemRemover ) EVENT( EV_Activate, idItemRemover::Event_Trigger ) END_CLASS /* ================ idItemRemover::Spawn ================ */ void idItemRemover::Spawn() { } /* ================ idItemRemover::RemoveItem ================ */ void idItemRemover::RemoveItem( idPlayer* player ) { const char* remove; remove = spawnArgs.GetString( "remove" ); player->RemoveInventoryItem( remove ); } /* ================ idItemRemover::Event_Trigger ================ */ void idItemRemover::Event_Trigger( idEntity* activator ) { if( activator->IsType( idPlayer::Type ) ) { RemoveItem( static_cast( activator ) ); } } /* =============================================================================== idObjectiveComplete =============================================================================== */ CLASS_DECLARATION( idItemRemover, idObjectiveComplete ) EVENT( EV_Activate, idObjectiveComplete::Event_Trigger ) EVENT( EV_HideObjective, idObjectiveComplete::Event_HideObjective ) EVENT( EV_GetPlayerPos, idObjectiveComplete::Event_GetPlayerPos ) END_CLASS /* ================ idObjectiveComplete::idObjectiveComplete ================ */ idObjectiveComplete::idObjectiveComplete() { playerPos.Zero(); } /* ================ idObjectiveComplete::Save ================ */ void idObjectiveComplete::Save( idSaveGame* savefile ) const { savefile->WriteVec3( playerPos ); } /* ================ idObjectiveComplete::Restore ================ */ void idObjectiveComplete::Restore( idRestoreGame* savefile ) { savefile->ReadVec3( playerPos ); } /* ================ idObjectiveComplete::Spawn ================ */ void idObjectiveComplete::Spawn() { spawnArgs.SetBool( "objEnabled", false ); Hide(); } /* ================ idObjectiveComplete::Event_Trigger ================ */ void idObjectiveComplete::Event_Trigger( idEntity* activator ) { if( !spawnArgs.GetBool( "objEnabled" ) ) { return; } idPlayer* player = gameLocal.GetLocalPlayer(); if( player ) { RemoveItem( player ); if( spawnArgs.GetString( "inv_objective", NULL ) ) { player->CompleteObjective( spawnArgs.GetString( "objectivetitle" ) ); PostEventMS( &EV_GetPlayerPos, 2000 ); } } } /* ================ idObjectiveComplete::Event_GetPlayerPos ================ */ void idObjectiveComplete::Event_GetPlayerPos() { idPlayer* player = gameLocal.GetLocalPlayer(); if( player ) { playerPos = player->GetPhysics()->GetOrigin(); PostEventMS( &EV_HideObjective, 100, player ); } } /* ================ idObjectiveComplete::Event_HideObjective ================ */ void idObjectiveComplete::Event_HideObjective( idEntity* e ) { idPlayer* player = gameLocal.GetLocalPlayer(); if( player ) { idVec3 v = player->GetPhysics()->GetOrigin(); v -= playerPos; if( v.Length() > 64.0f ) { player->HideObjective(); PostEventMS( &EV_Remove, 0 ); } else { PostEventMS( &EV_HideObjective, 100, player ); } } }