quake4-sdk/source/game/Item.cpp

2376 lines
57 KiB
C++

// RAVEN BEGIN
// bdube: note that this file is no longer merged with Doom3 updates
//
// MERGE_DATE 09/30/2004
#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" );
const idEventDef EV_CamShot( "<camshot>" );
// RAVEN BEGIN
// abahr:
const idEventDef EV_SetGravity( "<setGravity>" );
// RAVEN END
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 )
// RAVEN BEGIN
// abahr
EVENT( EV_SetGravity, idItem::Event_SetGravity )
// RAVEN END
END_CLASS
/*
================
idItem::idItem
================
*/
idItem::idItem() {
spin = false;
inView = false;
skin = NULL;
pickupSkin = NULL;
inViewTime = 0;
lastCycle = 0;
lastRenderViewTime = -1;
itemShellHandle = -1;
shellMaterial = NULL;
orgOrigin.Zero();
canPickUp = true;
fl.networkSync = true;
trigger = NULL;
syncPhysics = false;
srvReady = -1;
clReady = -1;
effectIdle = NULL;
itemPVSArea = 0;
effectIdle = NULL;
simpleItem = false;
pickedUp = false;
}
/*
================
idItem::~idItem
================
*/
idItem::~idItem() {
// remove the highlight shell
if ( itemShellHandle != -1 ) {
gameRenderWorld->FreeEntityDef( itemShellHandle );
}
if ( trigger ) {
delete trigger;
}
SetPhysics( NULL );
}
/*
================
idItem::Save
================
*/
void idItem::Save( idSaveGame *savefile ) const {
savefile->WriteClipModel( trigger );
savefile->WriteBool( spin );
savefile->WriteSkin( skin );
savefile->WriteSkin( pickupSkin );
savefile->WriteVec3( orgOrigin );
savefile->WriteBool( pulse );
savefile->WriteBool( canPickUp );
savefile->WriteStaticObject( physicsObj );
// savefile->WriteInt(itemShellHandle); // cnicholson: Set at end of Restore, do not save
savefile->WriteMaterial( shellMaterial );
savefile->WriteBool( inView );
savefile->WriteInt( inViewTime );
savefile->WriteInt( lastCycle );
savefile->WriteInt( lastRenderViewTime );
}
/*
================
idItem::Restore
================
*/
void idItem::Restore( idRestoreGame *savefile ) {
savefile->ReadClipModel( trigger );
savefile->ReadBool( spin );
savefile->ReadSkin( skin );
savefile->ReadSkin( pickupSkin );
savefile->ReadVec3( orgOrigin );
savefile->ReadBool( pulse );
savefile->ReadBool( canPickUp );
savefile->ReadStaticObject ( physicsObj );
// savefile->ReadInt(itemShellHandle); // cnicholson: Set at end of function, do not restore
savefile->ReadMaterial( shellMaterial );
savefile->ReadBool( inView );
savefile->ReadInt( inViewTime );
savefile->ReadInt( lastCycle );
savefile->ReadInt( lastRenderViewTime );
RestorePhysics( &physicsObj );
physicsObj.SetSelf( this );
itemShellHandle = -1;
}
/*
================
idItem::UpdateRenderEntity
================
*/
bool idItem::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
if( simpleItem ) {
return false;
}
if ( lastRenderViewTime == renderView->time ) {
return false;
}
lastRenderViewTime = renderView->time;
// 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 - inViewTime ) / 2000.0f;
if ( d > 0.94f ) {
if ( !inView ) {
inView = true;
if ( cycle > lastCycle ) {
// restart at the beginning
inViewTime = renderView->time;
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::UpdateTrigger
================
*/
void idItem::UpdateTrigger ( void ) {
// update trigger position
// RAVEN BEGIN
// ddynerman: multiple clip worlds
trigger->Link( this, 0, GetPhysics()->GetOrigin(), mat3_identity );
// RAVEN END
}
/*
================
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 ) {
gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" );
}
return ent->UpdateRenderEntity( renderEntity, renderView );
}
/*
================
idItem::GetPhysicsToVisualTransform
================
*/
bool idItem::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
if( simpleItem ) {
if ( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetRenderView() ) {
if( gameLocal.GetLocalPlayer()->spectating ) {
idPlayer* spec = (idPlayer*)gameLocal.entities[ gameLocal.GetLocalPlayer()->spectator ];
if( spec && spec->GetRenderView() ) {
axis = spec->GetRenderView()->viewaxis;
}
} else {
axis = gameLocal.GetLocalPlayer()->GetRenderView()->viewaxis;
}
} else {
// dedicated server for instance
axis = mat3_identity;
}
origin = idVec3( 0.0f, 0.0f, 32.0f );
return true;
}
if( !spin || (gameLocal.isServer && !gameLocal.isListenServer) ) {
return false;
}
idAngles ang;
ang.pitch = ang.roll = 0.0f;
ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f;
axis = ang.ToMat3() * GetPhysics()->GetAxis();
float scale = 0.005f;
float offset = entityNumber * 0.685145f; // rjohnson: just a random number here to shift the cos curve
origin.Zero();
origin += GetPhysics()->GetAxis()[2] * (4.0f + idMath::Cos( ( ( gameLocal.time + 1000 ) * scale ) + offset ) * 4.0f);
return true;
}
// RAVEN BEGIN
// mekberg: added
/*
================
idItem::Collide
================
*/
bool idItem::Collide( const trace_t &collision, const idVec3 &velocity ) {
idEntity* lol = gameLocal.entities[ collision.c.entityNum ];
if ( gameLocal.isMultiplayer && collision.c.contents & CONTENTS_ITEMCLIP && lol && !lol->IsType( idItem::GetClassType() ) ) {
PostEventMS( &EV_Remove, 0 );
}
return false;
}
// RAVEN END
/*
================
idItem::Think
================
*/
void idItem::Think( void ) {
if ( thinkFlags & TH_PHYSICS ) {
RunPhysics();
UpdateTrigger();
}
if ( gameLocal.IsMultiplayer() && g_skipItemShadowsMP.GetBool() ) {
renderEntity.suppressShadowInViewID = gameLocal.localClientNum + 1;
} else {
renderEntity.suppressShadowInViewID = 0;
}
if( !(simpleItem && pickedUp) ) {
UpdateVisuals();
Present();
}
}
/*
================
idItem::Present
================
*/
void idItem::Present( void ) {
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::InstanceJoin
================
*/
void idItem::InstanceJoin( void ) {
idEntity::InstanceJoin();
UpdateModelTransform();
if ( !simpleItem && spawnArgs.GetString( "fx_idle" ) ) {
PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true );
}
}
/*
================
idItem::InstanceLeave
================
*/
void idItem::InstanceLeave( void ) {
idEntity::InstanceLeave();
StopEffect( "fx_idle", true );
}
/*
================
idItem::Spawn
================
*/
void idItem::Spawn( void ) {
idStr giveTo;
idEntity * ent;
idVec3 vSize;
idBounds bounds(vec3_origin);
// check for triggerbounds, which allows for non-square triggers (useful for, say, a CTF flag)
if ( spawnArgs.GetVector( "triggerbounds", "16 16 16", vSize )) {
bounds.AddPoint(idVec3( vSize.x*0.5f, vSize.y*0.5f, 0.0f));
bounds.AddPoint(idVec3(-vSize.x*0.5f, -vSize.y*0.5f, vSize.z));
}
else {
// create a square trigger for item pickup
float tsize;
spawnArgs.GetFloat( "triggersize", "16.0", tsize );
bounds.ExpandSelf( tsize );
}
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
trigger = new idClipModel( idTraceModel( bounds ));
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
// RAVEN BEGIN
// ddynerman: multiple clip worlds
trigger->Link( this, 0, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
// RAVEN END
physicsObj.SetSelf ( this );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
physicsObj.SetAxis( GetPhysics()->GetAxis() );
physicsObj.SetGravity( gameLocal.GetGravity() );
physicsObj.SetContents( 0 );
physicsObj.SetClipMask( MASK_SOLID );
physicsObj.SetFriction( 0.0f, 0.0f, 6.0f );
SetPhysics( &physicsObj );
if ( spawnArgs.GetBool( "start_off" ) ) {
trigger->SetContents( 0 );
Hide();
} else {
trigger->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() );
}
PostEventMS( &EV_Touch, 0, ent, NULL );
}
if ( spawnArgs.GetBool( "spin" ) || gameLocal.isMultiplayer ) {
spin = true;
BecomeActive( TH_THINK );
}
// pulse ( and therefore itemShellHandle ) was taken out and shot. do not sync
//pulse = !spawnArgs.GetBool( "nopulse" );
pulse = false;
orgOrigin = GetPhysics()->GetOrigin();
canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) );
inViewTime = -1000;
lastCycle = -1;
itemShellHandle = -1;
// RAVEN BEGIN
// abahr: move texture to def file for precaching
shellMaterial = declManager->FindMaterial( spawnArgs.GetString("mtr_highlight", "_default") );
PostEventMS( &EV_SetGravity, 0 );
// RAVEN END
if ( spawnArgs.GetString( "skin", NULL ) ) {
skin = declManager->FindSkin( spawnArgs.GetString( "skin" ), false );
if( skin ) {
SetSkin( skin );
srvReady = 1;
}
} else {
skin = NULL;
}
if ( spawnArgs.GetString( "skin_pickup", NULL ) ) {
pickupSkin = declManager->FindSkin( spawnArgs.GetString( "skin_pickup" ), false );
} else {
pickupSkin = NULL;
}
syncPhysics = spawnArgs.GetBool( "net_syncPhysics", "0" );
if ( srvReady == -1 ) {
srvReady = IsHidden() ? 0 : 1;
}
// RAVEN BEGIN
// mekberg: added for removing pickups in mp in pits
if ( gameLocal.isMultiplayer ) {
trigger->SetContents( trigger->GetContents() | CONTENTS_ITEMCLIP );
}
// RAVEN END
if( gameLocal.isMultiplayer ) {
itemPVSArea = gameLocal.pvs.GetPVSArea( GetPhysics()->GetOrigin() );
} else {
itemPVSArea = 0;
}
simpleItem = g_simpleItems.GetBool() && gameLocal.isMultiplayer && !IsType( rvItemCTFFlag::GetClassType() );
if( simpleItem ) {
memset( &renderEntity, 0, sizeof( renderEntity ) );
renderEntity.axis = mat3_identity;
renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
renderEntity.shaderParms[ SHADERPARM_SPRITE_WIDTH ] = 32.0f;
renderEntity.shaderParms[ SHADERPARM_SPRITE_HEIGHT ] = 32.0f;
renderEntity.hModel = renderModelManager->FindModel( "_sprite" );
renderEntity.callback = NULL;
renderEntity.numJoints = 0;
renderEntity.joints = NULL;
renderEntity.customSkin = 0;
renderEntity.noShadow = true;
renderEntity.noSelfShadow = true;
renderEntity.customShader = declManager->FindMaterial( spawnArgs.GetString( "mtr_simple_icon" ) );
renderEntity.referenceShader = 0;
renderEntity.bounds = renderEntity.hModel->Bounds( &renderEntity );
SetAxis( mat3_identity );
} else {
if ( spawnArgs.GetString( "fx_idle" ) ) {
UpdateModelTransform();
effectIdle = PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true );
}
}
GetPhysics( )->SetClipMask( GetPhysics( )->GetClipMask( ) | CONTENTS_ITEMCLIP );
pickedUp = false;
}
/*
================
idItem::Event_SetGravity
================
*/
void idItem::Event_SetGravity() {
// If the item isnt a dropped item then see if it should settle itself
// to the floor or not
if ( !spawnArgs.GetBool( "dropped" ) ) {
if ( spawnArgs.GetBool( "nodrop" ) ) {
physicsObj.PutToRest();
} else {
PostEventMS( &EV_DropToFloor, 0 );
}
}
}
// RAVEN END
/*
================
idItem::GetAttributes
================
*/
void idItem::GetAttributes( idDict &attributes ) {
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 ) {
if ( player == NULL ) {
return false;
}
if ( spawnArgs.GetBool( "inv_carry" ) ) {
return player->GiveInventoryItem( &spawnArgs );
}
// Handle the special ammo pickup that gives ammo for the weapon the player currently has
if ( spawnArgs.GetBool( "item_currentWeaponAmmo" ) ) {
const char *ammoName = player->weapon->GetAmmoNameForIndex(player->weapon->GetAmmoType());
if ( player->weapon->TotalAmmoCount() != player->weapon->maxAmmo && player->weapon->AmmoRequired() ) {
player->GiveItem(ammoName);
player->GiveInventoryItem( &spawnArgs );
return true;
}
return false;
}
return player->GiveItem( this );
}
/*
===============
idItem::SendPickupMsg
===============
*/
void idItem::SendPickupMsg( int clientNum ) {
idBitMsg msg;
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteByte( GAME_UNRELIABLE_MESSAGE_EVENT );
msg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
msg.WriteByte( EVENT_PICKUP );
msg.WriteByte( clientNum );
// send as unreliable to client picking up the item, so it can play HUD things
gameLocal.SendUnreliableMessagePVS( msg, this, itemPVSArea );
}
/*
================
idItem::Pickup
================
*/
bool idItem::Pickup( idPlayer *player ) {
//dropped weapon?
bool dropped = spawnArgs.GetBool( "dropped" );
if ( gameLocal.isMultiplayer && !dropped && spawnArgs.FindKey( "weaponclass" )
&& gameLocal.IsWeaponsStayOn() && gameLocal.time > player->lastPickupTime + 1000 ) {
idDict attr;
GetAttributes( attr );
const idKeyValue* arg = attr.FindKey( "weapon" );
if ( arg ) {
if ( !player->inventory.Give( player, player->spawnArgs, arg->GetKey(), arg->GetValue(), NULL, false, dropped, true ) ) {
StartSound( "snd_noacquire", SND_CHANNEL_ITEM, 0, false, NULL );
}
}
}
// only predict noacquire on client
if ( gameLocal.isClient ) {
return false;
}
int givenToPlayer = spawnArgs.GetInt( "givenToPlayer", "-1" );
if ( player == NULL || ( givenToPlayer != -1 && givenToPlayer != player->entityNumber ) ) {
// idPlayer::GiveItem spawns an idItem for pickup, which appears at the origin before being picked
// and could sometimes be picked by someone else, particularly in buy mode when there is a play spawn sitting on the origin ;-)
return false;
}
if ( !GiveToPlayer( player ) ) {
return false;
}
if ( gameLocal.isServer ) {
SendPickupMsg( player->entityNumber );
}
// Check for global acquire sounds in multiplayer
if ( gameLocal.isMultiplayer && spawnArgs.GetBool( "globalAcquireSound" ) ) {
gameLocal.mpGame.PlayGlobalItemAcquireSound( entityDefNumber );
} else {
StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
}
// trigger our targets
ActivateTargets( player );
player->lastPickupTime = gameLocal.time;
//if a placed item and si_weaponStay is on and we're a weapon, don't remove and respawn
if ( gameLocal.IsMultiplayer() ) {
if ( !dropped ) {
if ( spawnArgs.FindKey( "weaponclass" ) ) {
if ( gameLocal.IsWeaponsStayOn() ) {
return true;
}
}
}
}
// clear our contents so the object isn't picked up twice
GetPhysics()->SetContents( 0 );
// hide the model, or switch to the pickup skin
if ( pickupSkin ) {
SetSkin( pickupSkin );
srvReady = 0;
} else {
Hide();
BecomeInactive( TH_THINK );
}
pickedUp = true;
// allow SetSkin or Hide() to get called regardless of simpleitem mode
if( simpleItem ) {
FreeModelDef();
UpdateVisuals();
}
// remove the highlight shell
if ( itemShellHandle != -1 ) {
gameRenderWorld->FreeEntityDef( itemShellHandle );
itemShellHandle = -1;
}
// asalmon: Added option for a differnt respawn rate based on gametype.
float respawn = spawnArgs.GetFloat(va("respawn_%s",gameLocal.serverInfo.GetString( "si_gameType" )), "-1.0");
if( respawn == -1.0f ) {
respawn = spawnArgs.GetFloat( "respawn", "5.0" );
}
bool no_respawn = spawnArgs.GetBool( "no_respawn" );
if ( !gameLocal.isMultiplayer ) {
respawn = 0.0f;
} else if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() ) {
if ( givenToPlayer != -1 ) {
respawn = 0.0f;
}
}
if ( respawn && !dropped && !no_respawn ) {
const char *sfx = spawnArgs.GetString( "fx_Respawn" );
if ( sfx && *sfx ) {
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 );
}
}
trigger->SetContents( 0 );
StopEffect( "fx_idle" );
return true;
}
/*
================
idItem::Hide
================
*/
void idItem::Hide( void ) {
srvReady = 0;
idEntity::Hide( );
trigger->SetContents( 0 );
}
/*
================
idItem::Show
================
*/
void idItem::Show( void ) {
srvReady = 1;
idEntity::Show( );
trigger->SetContents( CONTENTS_TRIGGER );
}
/*
================
idItem::ClientStale
================
*/
bool idItem::ClientStale( void ) {
idEntity::ClientStale();
StopEffect( "fx_idle" );
return false;
}
/*
================
idItem::ClientUnstale
================
*/
void idItem::ClientUnstale( void ) {
idEntity::ClientUnstale();
UpdateModelTransform();
if ( !simpleItem && spawnArgs.GetString( "fx_idle" ) ) {
PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true );
}
}
/*
================
idItem::ClientPredictionThink
================
*/
void idItem::ClientPredictionThink( void ) {
// only think forward because the state is not synced through snapshots
if ( !gameLocal.isNewFrame ) {
return;
}
Think();
}
/*
================
idItem::WriteFromSnapshot
================
*/
void idItem::WriteToSnapshot( idBitMsgDelta &msg ) const {
if ( syncPhysics ) {
physicsObj.WriteToSnapshot( msg );
}
assert( srvReady != -1 );
msg.WriteBits( ( srvReady == 1 ), 1 );
}
/*
================
idItem::ReadFromSnapshot
================
*/
void idItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
if ( syncPhysics ) {
physicsObj.ReadFromSnapshot( msg );
}
int newReady = ( msg.ReadBits( 1 ) != 0 );
idVec3 resetOrigin( 0, 0, 0 );
// client spawns the ent with ready == -1 so the state set happens at least once
if ( newReady != clReady ) {
if ( newReady ) {
// g_simpleItems might force a hide even with a pickup skin
if ( pickupSkin ) {
SetSkin( skin );
} else {
SetSkin( skin );
Show();
}
if( simpleItem ) {
UpdateVisuals();
}
pickedUp = false;
if ( effectIdle.GetEntity( ) ) {
UpdateModelTransform();
effectIdle->SetOrigin( resetOrigin );
} else if ( spawnArgs.GetString( "fx_idle" ) && !simpleItem ) {
UpdateModelTransform();
effectIdle = PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true );
}
} else {
if ( pickupSkin ) {
SetSkin( pickupSkin );
} else {
Hide();
}
if( simpleItem ) {
FreeModelDef();
UpdateVisuals();
}
pickedUp = true;
StopEffect( "fx_idle" );
effectIdle = NULL;
}
}
clReady = newReady;
}
/*
================
idItem::Event_Pickup
================
*/
void idItem::Event_Pickup( int clientNum ) {
idPlayer *player;
assert( gameLocal.isClient );
// play pickup sound
if ( !spawnArgs.GetBool( "globalAcquireSound" ) ) {
StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
}
if( clientNum == gameLocal.localClientNum ) {
player = (idPlayer*)gameLocal.entities[ clientNum ];
player->lastPickupTime = gameLocal.time;
if ( player ) {
player->GiveItem( this );
}
}
}
/*
================
idItem::ClientReceiveEvent
================
*/
bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
switch( event ) {
case EVENT_PICKUP: {
int clientNum = msg.ReadByte();
if( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
Event_Pickup( clientNum );
}
return true;
}
case EVENT_RESPAWNFX: {
Event_RespawnFx();
return true;
}
default: {
return idEntity::ClientReceiveEvent( event, time, msg );
}
}
//unreachable
// return false;
}
/*
================
idItem::Event_DropToFloor
================
*/
void idItem::Event_DropToFloor( void ) {
// don't drop the floor if bound to another entity
if ( GetBindMaster() != NULL && GetBindMaster() != this ) {
return;
}
physicsObj.DropToFloor( );
}
/*
================
idItem::Event_Touch
================
*/
void idItem::Event_Touch( idEntity *other, trace_t *trace ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( !other->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
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;
}
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( activator && activator->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
Pickup( static_cast<idPlayer *>( activator ) );
}
}
/*
================
idItem::Event_Respawn
================
*/
void idItem::Event_Respawn( void ) {
// with simple items, re-show the item, but still set srvReady to true to let clients
// know how to deal
if ( pickupSkin ) {
srvReady = true;
if( !simpleItem ) {
SetSkin( skin );
}
}
if( !pickupSkin ) {
BecomeActive( TH_THINK );
Show();
}
if( simpleItem ) {
Show();
}
pickedUp = false;
inViewTime = -1000;
lastCycle = -1;
trigger->SetContents ( CONTENTS_TRIGGER );
SetOrigin( orgOrigin );
StartSound( "snd_respawn", SND_CHANNEL_ITEM, 0, false, NULL );
PostEventMS( &EV_SetGravity, 0 );
CancelEvents( &EV_RespawnItem ); // don't double respawn
if ( !simpleItem && spawnArgs.GetString( "fx_idle" ) ) {
UpdateModelTransform();
PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true );
}
}
/*
================
idItem::Event_RespawnFx
================
*/
void idItem::Event_RespawnFx( void ) {
idBitMsg msg;
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
if ( gameLocal.isServer ) {
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteByte( GAME_UNRELIABLE_MESSAGE_EVENT );
msg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
msg.WriteByte( EVENT_RESPAWNFX );
// send unreliable PVS-sensitive
gameLocal.SendUnreliableMessagePVS( msg, this, gameLocal.pvs.GetPVSArea( GetPhysics()->GetOrigin() ) );
}
if( !simpleItem ) {
gameLocal.PlayEffect( spawnArgs, "fx_respawn", GetPhysics()->GetOrigin(), idVec3(0,0,1).ToMat3(), false, vec3_origin );
}
}
/*
===============================================================================
idItemPowerup
===============================================================================
*/
/*
===============
idItemPowerup
===============
*/
CLASS_DECLARATION( idItem, idItemPowerup )
END_CLASS
/*
================
idItemPowerup::idItemPowerup
================
*/
idItemPowerup::idItemPowerup() {
time = 0;
type = 0;
droppedTime = 0;
unique = false;
}
/*
================
idItemPowerup::Save
================
*/
void idItemPowerup::Save( idSaveGame *savefile ) const {
savefile->WriteInt( time );
savefile->WriteInt( type );
savefile->WriteInt( droppedTime ); // cnicholson: Added unsaved var
}
/*
================
idItemPowerup::Restore
================
*/
void idItemPowerup::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( time );
savefile->ReadInt( type );
savefile->ReadInt( droppedTime ); // cnicholson: Added unrestored var
}
/*
================
idItemPowerup::Spawn
================
*/
void idItemPowerup::Spawn( void ) {
time = SEC2MS( spawnArgs.GetInt( "time", "30" ) );
// SEC2MS screws up when we want -1 time (no expiration)
if( spawnArgs.GetInt( "time" ) == -1 ) {
time = -1;
}
type = spawnArgs.GetInt( "type", "0" );
// If the powerup was dropped then make it dissapear using its remaining time.
if ( spawnArgs.GetBool( "dropped" ) && time != -1 ) {
droppedTime = gameLocal.time + time;
PostEventMS( &EV_Remove, time );
}
// unique powerpus won't respawn while a player has them
unique = spawnArgs.GetBool( "unique", "0" );
if( unique ) {
spawnArgs.SetBool( "no_respawn", true );
}
if ( !idStr::Icmp( spawnArgs.GetString( "team" ), "strogg" ) ) {
team = TEAM_STROGG;
} else if( !idStr::Icmp( spawnArgs.GetString( "team" ), "marine" ) ) {
team = TEAM_MARINE;
} else {
team = -1;
}
}
/*
================
idItemPowerup::GiveToPlayer
================
*/
bool idItemPowerup::GiveToPlayer( idPlayer *player ) {
if ( player == NULL || player->spectating ) {
return false;
}
// only one arena CTF powerup at a time
if ( type >= POWERUP_AMMOREGEN && type <= POWERUP_SCOUT ) {
if ( ( player->inventory.powerups & ARENA_POWERUP_MASK ) != 0 ) {
return false;
}
}
// in flavours of arena CTF (or are idItemPowerups only used in Arena? or even, are idItemPowerups MP only?),
// ensure that items with a team can only be picked up by members of that team
if ( gameLocal.IsMultiplayer() && gameLocal.IsTeamGame() && team >= 0 ) {
if( team != player->team ) {
return false;
}
}
if ( droppedTime > 0 ) {
player->GivePowerUp( type, droppedTime - gameLocal.time );
} else {
player->GivePowerUp( type, time );
}
// also call idItem::GiveToPlayer so any inv_* keywords get applied
idItem::GiveToPlayer( player );
return true;
}
/*
================
idItemPowerup::Think
================
*/
void idItemPowerup::Think( void ) {
int i;
// idItem::Think() only needs to be called if we're spawned in
if( !IsHidden() || (pickupSkin && GetSkin() != pickupSkin ) ) {
// only get here if spawned in
idItem::Think();
return;
}
if( !unique ) {
// non-unique despawned powerups don't need to think
return;
}
for( i = 0; i < gameLocal.numClients; i++ ) {
idPlayer* p = (idPlayer*)gameLocal.entities[ i ];
if( p == NULL ) {
continue;
}
// only spawn back in if noone on your team has the powerup
if( p->PowerUpActive( type ) && p->team == team ) {
break;
}
}
if( i >= gameLocal.numClients ) {
PostEventMS( &EV_RespawnItem, 0 );
}
}
/*
================
idItemPowerup::Pickup
================
*/
bool idItemPowerup::Pickup( idPlayer* player ) {
// regular pickup routine, but unique items need to think to know when to respawn
bool pickup;
if( gameLocal.isClient ) {
// no client-side powerup prediction
return false;
}
pickup = idItem::Pickup( player );
if( unique ) {
BecomeActive( TH_THINK );
}
return pickup;
}
/*
===============================================================================
idObjective
===============================================================================
*/
CLASS_DECLARATION( idItem, idObjective )
EVENT( EV_Activate, idObjective::Event_Trigger )
EVENT( EV_HideObjective, idObjective::Event_HideObjective )
EVENT( EV_GetPlayerPos, idObjective::Event_GetPlayerPos )
EVENT( EV_CamShot, idObjective::Event_CamShot )
END_CLASS
/*
================
idObjective::idObjective
================
*/
idObjective::idObjective() {
playerPos.Zero();
// RAVEN BEGIN
// mekberg: store triggered time for timed removal.
triggerTime = 0;
// RAVEN END
}
/*
================
idObjective::Save
================
*/
void idObjective::Save( idSaveGame *savefile ) const {
savefile->WriteVec3( playerPos );
}
/*
================
idObjective::Restore
================
*/
void idObjective::Restore( idRestoreGame *savefile ) {
savefile->ReadVec3( playerPos );
// RAVEN BEGIN
// jnewquist: Don't do this on Xenon, we want prebuilt textures
#ifndef _XENON
PostEventMS( &EV_CamShot, 250 );
#endif
// RAVEN END
}
/*
================
idObjective::Spawn
================
*/
void idObjective::Spawn( void ) {
Hide();
// RAVEN BEGIN
// jnewquist: Only post a camshot event if the spawn args request it
#ifndef _XENON
const char *camName;
if ( spawnArgs.GetString( "camShot", "", &camName ) ) {
common->Warning( "SpawnArg camShot on %s is not recommended.", spawnArgs.GetString( "name" ) );
PostEventMS( &EV_CamShot, 250 );
}
#endif
// RAVEN END
}
/*
================
idObjective::Event_Screenshot
================
*/
void idObjective::Event_CamShot( ) {
const char *camName;
// RAVEN BEGIN
// bdube: changed screenshot location
idStr shotName = "gfx/objectives/";
shotName += spawnArgs.GetString( "screenshot" );
shotName.SetFileExtension( ".tga" );
// RAVEN END
if ( spawnArgs.GetString( "camShot", "", &camName ) ) {
idEntity *ent = gameLocal.FindEntity( camName );
if ( ent && ent->cameraTarget ) {
const renderView_t *view = ent->cameraTarget->GetRenderView();
renderView_t fullView = *view;
fullView.width = SCREEN_WIDTH;
fullView.height = SCREEN_HEIGHT;
// draw a view to a texture
renderSystem->CropRenderSize( 256, 256, true );
gameRenderWorld->RenderScene( &fullView );
// TTimo: I don't think this jives with SMP code, but I grepped through the final maps and didn't see any using it
renderSystem->CaptureRenderToFile( shotName );
renderSystem->UnCrop();
}
}
}
/*
================
idObjective::Event_Trigger
================
*/
void idObjective::Event_Trigger( idEntity *activator ) {
idPlayer *player = gameLocal.GetLocalPlayer();
if ( player ) {
if ( spawnArgs.GetString( "inv_objective", NULL ) ) {
// RAVEN BEGIN
// abahr: changed player->hud to player->GetHud so when in a vehicle we update the vehicle hud
// twhitaker: changed player->hud to player->GetObjectiveHud(), to resolve all issues
if ( player && player->GetObjectiveHud() ) {
// bdube: changed screenshot location
idStr shotName = spawnArgs.GetString( "screenshot" );
player->GetObjectiveHud()->SetStateString( "screenshot", shotName );
player->GetObjectiveHud()->SetStateString( "objective", "1" );
player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext" ) ) );
player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle" ) ) );
// RAVEN END
player->GiveObjective( spawnArgs.GetString( "objectivetitle" ), spawnArgs.GetString( "objectivetext" ), shotName );
// 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++ ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idObjectiveComplete::GetClassType() ) ) {
// RAVEN END
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 );
// RAVEN BEGIN
// mekberg: store triggered time for timed removal.
triggerTime = gameLocal.time;
// RAVEN END
}
}
}
}
/*
================
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;
// RAVEN BEGIN
// mekberg: hide time done internally now
if ( v.Length() > 64.0f || gameLocal.time > triggerTime + 5000 ) {
// RAVEN END
player->HideObjective ( );
PostEventMS( &EV_Remove, 0 );
} else {
PostEventMS( &EV_HideObjective, 100, player );
}
}
}
/*
===============================================================================
idMoveableItem
===============================================================================
*/
CLASS_DECLARATION( idItem, idMoveableItem )
EVENT( EV_Gib, idMoveableItem::Event_Gib )
END_CLASS
/*
================
idMoveableItem::idMoveableItem
================
*/
idMoveableItem::idMoveableItem() {
}
/*
================
idMoveableItem::~idMoveableItem
================
*/
idMoveableItem::~idMoveableItem() {
// If this entity has been allocated, but not spawned, the physics object will
// not have a self pointer, and will not unregister itself from the entity.
SetPhysics( NULL );
}
/*
================
idMoveableItem::Save
================
*/
void idMoveableItem::Save( idSaveGame *savefile ) const {
savefile->WriteStaticObject( physicsObj );
}
/*
================
idMoveableItem::Restore
================
*/
void idMoveableItem::Restore( idRestoreGame *savefile ) {
savefile->ReadStaticObject ( physicsObj );
RestorePhysics ( &physicsObj );
}
/*
================
idMoveableItem::Spawn
================
*/
void idMoveableItem::Spawn( void ) {
idTraceModel trm;
float density, friction, bouncyness;
idStr clipModelName;
idBounds bounds;
// 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 );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// check if a clip model is set
spawnArgs.GetString( "itemclipmodel", "", clipModelName );
if ( clipModelName[0] ) {
if ( collisionModelManager->TrmFromModel( gameLocal.GetMapName(), clipModelName, trm ) ) {
physicsObj.SetClipModel( new idClipModel( trm ), density );
} else {
// fallback
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), density );
}
} else {
// fallback
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), density );
}
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
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 );
// RAVEN BEGIN
// mekberg: added
if ( spawnArgs.GetBool( "noimpact" ) || spawnArgs.GetBool( "notPushable" ) ) {
physicsObj.DisableImpact();
}
// RAVEN END
}
/*
================
idMoveableItem::Think
================
*/
void idMoveableItem::Think( void ) {
RunPhysics();
UpdateTrigger( );
Present();
}
/*
================
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( idBitMsgDelta &msg ) const {
physicsObj.WriteToSnapshot( msg );
}
/*
======================
idMoveableItem::ReadFromSnapshot
======================
*/
void idMoveableItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
physicsObj.ReadFromSnapshot( msg );
if ( msg.HasChanged() ) {
UpdateVisuals();
}
}
/*
============
idMoveableItem::Gib
============
*/
void idMoveableItem::Gib( const idVec3 &dir, const char *damageDefName ) {
// remove the entity
PostEventMS( &EV_Remove, 0 );
}
/*
============
idMoveableItem::Event_Gib
============
*/
void idMoveableItem::Event_Gib( const char *damageDefName ) {
Gib( idVec3( 0, 0, 1 ), damageDefName );
}
/*
===============================================================================
idItemRemover
===============================================================================
*/
CLASS_DECLARATION( idEntity, idItemRemover )
EVENT( EV_Activate, idItemRemover::Event_Trigger )
END_CLASS
/*
================
idItemRemover::Spawn
================
*/
void idItemRemover::Spawn( void ) {
}
/*
================
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 ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( activator->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
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();
// RAVEN BEGIN
// mekberg: store triggered time for timed removal.
triggerTime = 0;
// RAVEN END
}
/*
================
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( void ) {
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 ) ) {
// RAVEN BEGIN
// abahr: changed player->hud to player->GetHud so if in a vehicle we update that hud
// twhitaker: moved objective system to it's own hud, to solve vehicle hud issues.
if ( player->GetObjectiveHud() ) {
player->GetObjectiveHud()->SetStateString( "objective", "2" );
player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext" ) ) );
player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle" ) ) );
// RAVEN END
player->CompleteObjective( spawnArgs.GetString( "objectivetitle" ) );
PostEventMS( &EV_GetPlayerPos, 2000 );
// RAVEN BEGIN
// mekberg: store triggered time for timed removal.
triggerTime = gameLocal.time;
// RAVEN END
}
}
}
}
/*
================
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 && player->GetObjectiveHud() ) {
idVec3 v = player->GetPhysics()->GetOrigin();
v -= playerPos;
// RAVEN BEGIN
// mekberg: hide time done internally now
if ( v.Length() > 64.0f || gameLocal.time > triggerTime + 5000 ) {
// RAVEN END
player->HideObjective ( );
PostEventMS( &EV_Remove, 0 );
} else {
PostEventMS( &EV_HideObjective, 100, player );
}
}
}
/*
===============================================================================
rvObjectiveFailed
===============================================================================
*/
CLASS_DECLARATION( idItemRemover, rvObjectiveFailed )
EVENT( EV_Activate, rvObjectiveFailed::Event_Trigger )
END_CLASS
rvObjectiveFailed::rvObjectiveFailed ( void ) {
}
/*
================
rvObjectiveFailed::Event_Trigger
================
*/
void rvObjectiveFailed::Event_Trigger( idEntity *activator ) {
idPlayer *player = gameLocal.GetLocalPlayer();
if ( !player || !player->GetObjectiveHud() ) {
return;
}
if ( !spawnArgs.GetString( "inv_objective", NULL ) ) {
return;
}
player->GetObjectiveHud()->SetStateString( "objective", "2" );
player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext" ) ) );
player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle" ) ) );
player->FailObjective( spawnArgs.GetString( "objectivetitle" ) );
}
/*
===============================================================================
rvItemCTFFlag
===============================================================================
*/
const idEventDef EV_ResetFlag ( "<resetflag>" );
const idEventDef EV_LinkTrigger( "<linktrigger>" );
CLASS_DECLARATION( idItem, rvItemCTFFlag )
EVENT( EV_ResetFlag, rvItemCTFFlag::Event_ResetFlag )
EVENT( EV_LinkTrigger, rvItemCTFFlag::Event_LinkTrigger )
END_CLASS
/*
================
rvItemCTFFlag::rvItemCTFFlag
================
*/
rvItemCTFFlag::rvItemCTFFlag() {
}
/*
================
rvItemCTFFlag::Spawn
================
*/
void rvItemCTFFlag::Spawn () {
/* rjohnson: I fixed the crash so we don't need to remove them...
//don't spawn outside of CTF games
if( (gameLocal.gameType != GAME_CTF) && (gameLocal.gameType != GAME_1F_CTF) &&
(gameLocal.gameType != GAME_ARENA_CTF) && (gameLocal.gameType != GAME_ARENA_1F_CTF) ){
//don't spawn!
gameLocal.Error("Flag spawn on in non-CTF gametype.");
PostEventMS ( &EV_Remove, 0 );
return;
}
*/
spawnArgs.GetBool ( "dropped", "0", dropped );
spawnArgs.GetInt ( "team", "0", team );
bool reset = false;
spawnArgs.GetBool ( "reset", "0", reset );
switch ( team ) {
case TEAM_MARINE: {
powerup = POWERUP_CTF_MARINEFLAG;
gameLocal.mpGame.SetFlagEntity( this, TEAM_MARINE );
break;
}
case TEAM_STROGG: {
powerup = POWERUP_CTF_STROGGFLAG;
gameLocal.mpGame.SetFlagEntity( this, TEAM_STROGG );
break;
}
case TEAM_MAX: {
powerup = POWERUP_CTF_ONEFLAG;
break;
}
default:
gameLocal.Warning ( "Unknown ctf flag team '%d' on entity '%s'", team, name.c_str() );
PostEventMS ( &EV_Remove, 0 );
return;
}
if ( dropped ) {
if ( !gameLocal.isClient ) {
((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_DROPPED );
}
if ( reset ) {
//PostEventSec( &EV_ResetFlag, 1 );
} else {
PostEventSec( &EV_ResetFlag, 30 );
}
// Let powerups settle for a frame before allowing them to be picked up - we need to
// make sure we hit the dropped state for VO, etc to work properly
trigger->SetContents( 0 );
PostEventSec( &EV_LinkTrigger, 0 );
} else {
if ( !gameLocal.isClient ) {
((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_AT_BASE );
}
}
GetPhysics( )->SetClipMask( GetPhysics( )->GetClipMask( ) | CONTENTS_ITEMCLIP );
spin = false;
}
/*
================
rvItemCTFFlag::Event_LinkTrigger
================
*/
void rvItemCTFFlag::Event_LinkTrigger( void ) {
trigger->SetContents( CONTENTS_TRIGGER | CONTENTS_ITEMCLIP );
}
/*
================
rvItemCTFFlag::GiveToPlayer
================
*/
bool rvItemCTFFlag::GiveToPlayer( idPlayer* player ) {
if ( !gameLocal.IsMultiplayer() ) {
return false;
}
if ( player->spectating || ((gameLocal.mpGame.GetGameState())->GetMPGameState() != GAMEON && (gameLocal.mpGame.GetGameState())->GetMPGameState() != SUDDENDEATH && !cvarSystem->GetCVarBool( "g_testCTF" )) ) {
return false;
}
int teamPowerup;
int enemyPowerup;
bool canPickup = ( team == player->team );
switch ( player->team ) {
default:
case TEAM_MARINE:
teamPowerup = POWERUP_CTF_MARINEFLAG;
enemyPowerup = POWERUP_CTF_STROGGFLAG;
break;
case TEAM_STROGG:
teamPowerup = POWERUP_CTF_STROGGFLAG;
enemyPowerup = POWERUP_CTF_MARINEFLAG;
break;
}
if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) {
// in one flag CTF, we score touching the enemy's flag
canPickup = ( team == gameLocal.mpGame.OpposingTeam( player->team ) );
enemyPowerup = POWERUP_CTF_ONEFLAG;
}
// If the player runs over their own flag the only thing they
// can do is score or return it.
// when the player touches the one flag, he always gets it
if ( canPickup ) {
// If the flag was dropped, return it
if ( dropped ) {
gameLocal.mpGame.AddPlayerTeamScore( player, 2 );
statManager->FlagReturned( player );
ResetFlag ( teamPowerup );
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
player->GiveCash( (float)gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_flagReturned", 0 ) );
// RITUAL END
} else if ( player->PowerUpActive ( enemyPowerup ) ) {
// If they have the enemy flag then they score
if ( !gameLocal.mpGame.CanCapture ( player->team ) ) {
return false;
}
ResetFlag( enemyPowerup );
gameLocal.mpGame.FlagCaptured( player );
}
return false;
}
// only pickup one flag in arena CTF
if( ( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) && team != TEAM_MAX ) {
return false;
}
player->GivePowerUp( enemyPowerup, -1 );
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
player->GiveCash( (float)gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_flagStolen", 0 ) );
// RITUAL END
return true;
}
/*
================
rvItemCTFFlag::Pickup
================
*/
bool rvItemCTFFlag::Pickup( idPlayer *player ) {
if( gameLocal.isClient ) {
// no client-side CTF flag prediction
return false;
}
if ( !GiveToPlayer( player ) ) {
return false;
}
// ServerSendEvent( EVENT_PICKUP, NULL, false, -1 );
if ( gameLocal.isServer ) {
SendPickupMsg( player->entityNumber );
}
// Check for global acquire sounds in multiplayer
if ( gameLocal.isMultiplayer && spawnArgs.GetBool( "globalAcquireSound" ) ) {
gameLocal.mpGame.PlayGlobalItemAcquireSound( entityDefNumber );
} else {
StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
}
// trigger our targets
ActivateTargets( player );
// clear our contents so the object isn't picked up twice
GetPhysics()->SetContents( 0 );
// hide the model
Hide();
gameLocal.mpGame.SetFlagEntity( NULL, team );
gameLocal.mpGame.AddPlayerTeamScore( player, 1 );
if( gameLocal.gameType == GAME_CTF || gameLocal.gameType == GAME_ARENA_CTF ) {
((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_TAKEN );
} else if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) {
((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, (player->team == TEAM_MARINE ? FS_TAKEN_MARINE : FS_TAKEN_STROGG) );
}
((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagCarrier( team, player->entityNumber );
if ( spawnArgs.GetBool( "dropped" ) ) {
PostEventMS( &EV_Remove, 0 );
}
BecomeInactive( TH_THINK );
return true;
}
/*
================
rvItemCTFFlag::ResetFlag
================
*/
void rvItemCTFFlag::ResetFlag( int powerup ) {
idEntity* ent;
for ( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
// Make sure no players have the flag anymore
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( ent->IsType ( idPlayer::GetClassType() ) ) {
// RAVEN END
static_cast<idPlayer*>(ent)->ClearPowerup ( powerup );
continue;
}
// If its not a CTF flag item then skip it
if ( !ent->IsType( rvItemCTFFlag::Type ) ) {
continue;
}
// Make sure its the right type first
rvItemCTFFlag* flag;
flag = static_cast<rvItemCTFFlag*>(ent);
if ( flag->powerup != powerup ) {
continue;
}
if ( flag->dropped ) {
flag->PostEventMS( &EV_Remove, 0 );
} else {
flag->PostEventMS( &EV_RespawnItem, 0 );
gameLocal.mpGame.SetFlagEntity( flag, flag->team );
}
}
if ( !gameLocal.isClient ) {
int team = -1;
if ( powerup == POWERUP_CTF_MARINEFLAG ) {
team = TEAM_MARINE;
} else if ( powerup == POWERUP_CTF_STROGGFLAG ) {
team = TEAM_STROGG;
} else if ( powerup == POWERUP_CTF_ONEFLAG ) {
team = TEAM_MAX;
}
((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_AT_BASE );
}
}
/*
================
rvItemCTFFlag::Event_ResetFlag
================
*/
void rvItemCTFFlag::Event_ResetFlag( void ) {
ResetFlag( powerup );
}
void rvItemCTFFlag::Think( void ) {
idItem::Think();
}
/*
================
rvItemCTFFlag::Collide
================
*/
bool rvItemCTFFlag::Collide( const trace_t &collision, const idVec3 &velocity ) {
idEntity* lol = gameLocal.entities[ collision.c.entityNum ];
if ( collision.c.contents & CONTENTS_ITEMCLIP && lol && !lol->IsType( idItem::GetClassType() ) ) {
ResetFlag( powerup );
}
return false;
}
// RAVEN END
const idEventDef EV_ResetSpawn ( "<resetspawn>" );
CLASS_DECLARATION( idItemPowerup, riDeadZonePowerup )
EVENT( EV_ResetSpawn, riDeadZonePowerup::Event_ResetSpawn )
END_CLASS
/*
================
riDeadZonePowerup::idItemPowerup
================
*/
riDeadZonePowerup::riDeadZonePowerup() {
}
/*
================
riDeadZonePowerup::Save
================
*/
void riDeadZonePowerup::Save( idSaveGame *savefile ) const {
}
/*
================
riDeadZonePowerup::Restore
================
*/
void riDeadZonePowerup::Restore( idRestoreGame *savefile ) {
}
/*
================
riDeadZonePowerup::Show
================
*/
void riDeadZonePowerup::Show()
{
idItem::Show();
}
/*
================
riDeadZonePowerup::Spawn
================
*/
void riDeadZonePowerup::Spawn( void ) {
powerup = POWERUP_DEADZONE;
time = SEC2MS( gameLocal.serverInfo.GetInt("si_deadZonePowerupTime") );
if ( spawnArgs.GetBool( "dropped" ) ) {
time = SEC2MS( spawnArgs.GetInt( "time", "10" ) );
if ( time > SEC2MS(10) )
time = SEC2MS(10);
Show();
CancelEvents(&EV_Remove);
PostEventSec( &EV_ResetSpawn, MS2SEC(time) );
}
else
Hide();
}
/*
================
riDeadZonePowerup::Pickup
================
*/
bool riDeadZonePowerup::Pickup( idPlayer* player ) {
// regular pickup routine, but unique items need to think to know when to respawn
bool pickup;
if ( player->PowerUpActive(POWERUP_DEADZONE) )
return false;
pickup = idItemPowerup::Pickup( player );
if ( spawnArgs.GetBool( "dropped" ) ) {
// Cancel the respawn so the powerup doesn't get removed
// from the player that just picked it up.
PostEventMS( &EV_Remove, 0 );
CancelEvents( &EV_ResetSpawn );
}
return pickup;
}
/*
================
riDeadZonePowerup::ResetSpawn
================
*/
void riDeadZonePowerup::ResetSpawn( int powerup ) {
int count = 1;
idEntity* ent;
riDeadZonePowerup* spawnSpot = 0;
for ( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
// If its not a DeadZone powerup then skip it
if ( !ent->IsType( riDeadZonePowerup::Type ) ) {
continue;
}
// Make sure its the right type first
riDeadZonePowerup* flag;
flag = static_cast<riDeadZonePowerup*>(ent);
if ( flag->powerup != powerup ) {
continue;
}
if ( flag->spawnArgs.GetBool("dropped", "0") && flag == this ) {
flag->PostEventMS( &EV_Remove, 0 );
} else {
if ( !flag->IsVisible() ) {
if ( !(rand()%count) ) {
spawnSpot = flag;
if ( flag->spawnArgs.GetBool("dropped", "0") )
gameLocal.DPrintf("WARNING: Trying to spawn a powerup at a DROPPED location!");
}
count++;
}
}
}
if ( spawnSpot ) {
spawnSpot->Show();
spawnSpot->PostEventMS( &EV_RespawnItem, 0 );
}
else {
gameLocal.DPrintf("WARNING: Failed to find a valid spawn spot!");
}
}
/*
================
riDeadZonePowerup::Collide
================
*/
bool riDeadZonePowerup::Collide( const trace_t &collision, const idVec3 &velocity ) {
idEntity* lol = gameLocal.entities[ collision.c.entityNum ];
if ( gameLocal.isMultiplayer && collision.c.contents & CONTENTS_ITEMCLIP && lol && !lol->IsType( idItem::GetClassType() ) ) {
PostEventMS( &EV_ResetSpawn, 0 ); // Just respawn it.
}
return false;
}
/*
================
riDeadZonePowerup::Event_ResetFlag
================
*/
void riDeadZonePowerup::Event_ResetSpawn( void ) {
ResetSpawn( POWERUP_DEADZONE );
}