dhewm3-sdk/game/Item.cpp
dhewg afebd7e1e5 Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2018-08-20 01:46:28 +02:00

1358 lines
32 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
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 "sys/platform.h"
#include "renderer/RenderSystem.h"
#include "gamesys/SysCvar.h"
#include "Player.h"
#include "Fx.h"
#include "SmokeParticles.h"
#include "Item.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>" );
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() {
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 ) {
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::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::Think
================
*/
void idItem::Think( void ) {
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( 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::Spawn
================
*/
void idItem::Spawn( void ) {
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() );
}
PostEventMS( &EV_Touch, 0, ent, 0 );
}
if ( spawnArgs.GetBool( "spin" ) || gameLocal.isMultiplayer ) {
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 ) {
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 );
}
return player->GiveItem( this );
}
/*
================
idItem::Pickup
================
*/
bool idItem::Pickup( idPlayer *player ) {
if ( !GiveToPlayer( player ) ) {
return false;
}
if ( gameLocal.isServer ) {
ServerSendEvent( EVENT_PICKUP, NULL, false, -1 );
}
// play pickup sound
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();
// add the highlight shell
if ( itemShellHandle != -1 ) {
gameRenderWorld->FreeEntityDef( itemShellHandle );
itemShellHandle = -1;
}
float respawn = spawnArgs.GetFloat( "respawn" );
bool dropped = spawnArgs.GetBool( "dropped" );
bool no_respawn = spawnArgs.GetBool( "no_respawn" );
if ( gameLocal.isMultiplayer && respawn == 0.0f ) {
respawn = 20.0f;
}
if ( respawn && !dropped && !no_respawn ) {
const char *sfx = spawnArgs.GetString( "fxRespawn" );
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 );
}
}
BecomeInactive( TH_THINK );
return 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 {
msg.WriteBits( IsHidden(), 1 );
}
/*
================
idItem::ReadFromSnapshot
================
*/
void idItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
if ( msg.ReadBits( 1 ) ) {
Hide();
} else {
Show();
}
}
/*
================
idItem::ClientReceiveEvent
================
*/
bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
switch( event ) {
case EVENT_PICKUP: {
// play pickup sound
StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
// hide the model
Hide();
// remove the highlight shell
if ( itemShellHandle != -1 ) {
gameRenderWorld->FreeEntityDef( itemShellHandle );
itemShellHandle = -1;
}
return true;
}
case EVENT_RESPAWN: {
Event_Respawn();
return true;
}
case EVENT_RESPAWNFX: {
Event_RespawnFx();
return true;
}
default: {
return idEntity::ClientReceiveEvent( event, time, msg );
}
}
return false;
}
/*
================
idItem::Event_DropToFloor
================
*/
void idItem::Event_DropToFloor( void ) {
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( void ) {
if ( gameLocal.isServer ) {
ServerSendEvent( EVENT_RESPAWN, NULL, false, -1 );
}
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( void ) {
if ( gameLocal.isServer ) {
ServerSendEvent( EVENT_RESPAWNFX, NULL, false, -1 );
}
const char *sfx = spawnArgs.GetString( "fxRespawn" );
if ( sfx && *sfx ) {
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( void ) {
time = spawnArgs.GetInt( "time", "30" );
type = spawnArgs.GetInt( "type", "0" );
}
/*
================
idItemPowerup::GiveToPlayer
================
*/
bool idItemPowerup::GiveToPlayer( idPlayer *player ) {
if ( player->spectating ) {
return false;
}
player->GivePowerUp( type, time * 1000 );
return true;
}
/*
===============================================================================
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();
}
/*
================
idObjective::Save
================
*/
void idObjective::Save( idSaveGame *savefile ) const {
savefile->WriteVec3( playerPos );
}
/*
================
idObjective::Restore
================
*/
void idObjective::Restore( idRestoreGame *savefile ) {
savefile->ReadVec3( playerPos );
PostEventMS( &EV_CamShot, 250 );
}
/*
================
idObjective::Spawn
================
*/
void idObjective::Spawn( void ) {
Hide();
PostEventMS( &EV_CamShot, 250 );
}
/*
================
idObjective::Event_Screenshot
================
*/
void idObjective::Event_CamShot( ) {
const char *camName;
idStr shotName = gameLocal.GetMapName();
shotName.StripFileExtension();
shotName += "/";
shotName += spawnArgs.GetString( "screenshot" );
shotName.SetFileExtension( ".tga" );
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 );
renderSystem->CaptureRenderToFile( shotName );
renderSystem->UnCrop();
}
}
}
/*
================
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->hud ) {
idStr shotName = gameLocal.GetMapName();
shotName.StripFileExtension();
shotName += "/";
shotName += spawnArgs.GetString( "screenshot" );
shotName.SetFileExtension( ".tga" );
player->hud->SetStateString( "screenshot", shotName );
player->hud->SetStateString( "objective", "1" );
player->hud->SetStateString( "objectivetext", spawnArgs.GetString( "objectivetext" ) );
player->hud->SetStateString( "objectivetitle", spawnArgs.GetString( "objectivetitle" ) );
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++ ) {
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::Spawn
================
*/
void idVideoCDItem::Spawn( void ) {
}
/*
================
idVideoCDItem::GiveToPlayer
================
*/
bool idVideoCDItem::GiveToPlayer( idPlayer *player ) {
idStr str = spawnArgs.GetString( "video" );
if ( player && str.Length() ) {
player->GiveVideo( str, &spawnArgs );
}
return true;
}
/*
===============================================================================
idPDAItem
===============================================================================
*/
CLASS_DECLARATION( idItem, idPDAItem )
END_CLASS
/*
================
idPDAItem::GiveToPlayer
================
*/
bool idPDAItem::GiveToPlayer(idPlayer *player) {
const char *str = spawnArgs.GetString( "pda_name" );
if ( player ) {
player->GivePDA( str, &spawnArgs );
}
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;
}
/*
================
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 );
}
/*
================
idMoveableItem::Restore
================
*/
void idMoveableItem::Restore( idRestoreGame *savefile ) {
savefile->ReadStaticObject( physicsObj );
RestorePhysics( &physicsObj );
savefile->ReadClipModel( trigger );
savefile->ReadParticle( smoke );
savefile->ReadInt( smokeTime );
}
/*
================
idMoveableItem::Spawn
================
*/
void idMoveableItem::Spawn( void ) {
idTraceModel trm;
float density, friction, bouncyness, tsize;
idStr clipModelName;
idBounds bounds;
// create a trigger for item pickup
spawnArgs.GetFloat( "triggersize", "16.0", tsize );
trigger = new 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 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 );
smoke = NULL;
smokeTime = 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 );
}
}
/*
================
idMoveableItem::Think
================
*/
void idMoveableItem::Think( void ) {
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() ) ) {
smokeTime = 0;
BecomeInactive( TH_UPDATEPARTICLES );
}
}
Present();
}
/*
================
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( 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 ) {
// 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 );
}
// remove the entity
PostEventMS( &EV_Remove, 0 );
}
/*
================
idMoveableItem::Event_DropToFloor
================
*/
void idMoveableItem::Event_DropToFloor( void ) {
// 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) {
const char *str = spawnArgs.GetString( "pda_name" );
if ( player ) {
player->GivePDA( str, &spawnArgs );
}
return true;
}
/*
===============================================================================
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 ) {
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( 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 ) ) {
if ( player->hud ) {
player->hud->SetStateString( "objective", "2" );
player->hud->SetStateString( "objectivetext", spawnArgs.GetString( "objectivetext" ) );
player->hud->SetStateString( "objectivetitle", spawnArgs.GetString( "objectivetitle" ) );
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->hud->HandleNamedEvent( "closeObjective" );
PostEventMS( &EV_Remove, 0 );
} else {
PostEventMS( &EV_HideObjective, 100, player );
}
}
}