mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-30 16:11:11 +00:00
736ec20d4d
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.
1358 lines
32 KiB
C++
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 );
|
|
}
|
|
}
|
|
}
|