mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-02 17:02:17 +00:00
2402 lines
51 KiB
C++
2402 lines
51 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
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( "<dropToFloor>" );
|
|
const idEventDef EV_RespawnItem( "respawn" );
|
|
const idEventDef EV_RespawnFx( "<respawnFx>" );
|
|
const idEventDef EV_GetPlayerPos( "<getplayerpos>" );
|
|
const idEventDef EV_HideObjective( "<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<idItem*>( 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 != NULL )
|
|
{
|
|
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<idPlayer*>( 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<idPlayer*>( 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 != NULL )
|
|
{
|
|
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( 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<idPlayer*>( 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<idPlayer*>( 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<idPhysics_RigidBody*>( 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<idPlayer*>( 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<idPlayer*>( 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<const idDeclVideo* >( 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<const idDeclPDA*>( 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<const idDeclParticle*>( 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_drop<type>Item" "item def"
|
|
"drop<type>ItemJoint" "joint name"
|
|
"drop<type>ItemRotation" "pitch yaw roll"
|
|
"drop<type>ItemOffset" "x y z"
|
|
"skin_drop<type>" "skin name"
|
|
To drop multiple items the following key/value pairs can be used:
|
|
"def_drop<type>Item<X>" "item def"
|
|
"drop<type>Item<X>Joint" "joint name"
|
|
"drop<type>Item<X>Rotation" "pitch yaw roll"
|
|
"drop<type>Item<X>Offset" "x y z"
|
|
where <X> is an aribtrary string.
|
|
================
|
|
*/
|
|
void idMoveableItem::DropItems( idAnimatedEntity* ent, const char* type, idList<idEntity*>* 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<const idDeclParticle*>( 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<const idDeclPDA*>( 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<idPlayer*>( 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 );
|
|
}
|
|
}
|
|
}
|