mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-16 00:01:53 +02:00
1065 lines
25 KiB
C++
1065 lines
25 KiB
C++
// Copyright (C) 2004 Id Software, Inc.
|
|
//
|
|
|
|
#include "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>" );
|
|
|
|
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 ) {
|
|
// HUMANHEAD pdm: changed and moved to GetPhysicsToVisualTransform
|
|
if ( thinkFlags & TH_THINK ) {
|
|
if ( spin ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idItem::GetPhysicsToVisualTransform
|
|
HUMANHEAD pdm: moved here so items can be bound to things
|
|
================
|
|
*/
|
|
bool idItem::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
|
|
if ( spin ) {
|
|
idAngles ang;
|
|
idVec3 org;
|
|
|
|
ang.pitch = ang.roll = 0.0f;
|
|
ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f;
|
|
axis = ang.ToMat3();
|
|
|
|
const float scale = 0.0055f;
|
|
origin = idVec3(0.0f, 0.0f, 1.0f);
|
|
origin *= (4.0f + idMath::Cos( ( gameLocal.time + entityNumber ) * scale ) * 4.0f);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
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, NULL );
|
|
}
|
|
|
|
if ( spawnArgs.GetBool( "spin" ) || gameLocal.isMultiplayer ) {
|
|
if (!IsType(idMoveableItem::Type)) { //HUMANHEAD rww
|
|
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 && !IsType(idMoveableItem::Type) ) { //HUMANHEAD rww - not for moveable items
|
|
respawn = ITEM_SPAWNRATE_DEFAULT; //HUMANHEAD rww - changed to a define
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
/*
|
|
================
|
|
idItem::GetNonRespawnSkin
|
|
================
|
|
*/
|
|
const idDeclSkin *idItem::GetNonRespawnSkin(void) const {
|
|
const char *customSkin = spawnArgs.GetString("skin", "");
|
|
if (!customSkin || !customSkin[0])
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return declManager->FindSkin(customSkin);
|
|
}
|
|
//HUMANHEAD END
|
|
|
|
/*
|
|
================
|
|
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 );
|
|
|
|
//HUMANHEAD rww
|
|
msg.WriteBits(renderEntity.customSkin != GetNonRespawnSkin(), 1);
|
|
//HUMANHEAD END
|
|
}
|
|
|
|
/*
|
|
================
|
|
idItem::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idItem::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
if ( msg.ReadBits( 1 ) ) {
|
|
Hide();
|
|
} else {
|
|
Show();
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
bool usingRespawnSkin = !!msg.ReadBits(1);
|
|
if (usingRespawnSkin != (renderEntity.customSkin != GetNonRespawnSkin())) {
|
|
if (usingRespawnSkin) { //apply the skin
|
|
idStr respawningSkin;
|
|
if (spawnArgs.GetString("skin_itemRespawning", "", respawningSkin)) {
|
|
SetSkin(declManager->FindSkin(respawningSkin.c_str()));
|
|
}
|
|
}
|
|
else { //get rid of it
|
|
SetSkin(GetNonRespawnSkin());
|
|
}
|
|
}
|
|
//HUMANHEAD END
|
|
}
|
|
|
|
/*
|
|
================
|
|
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 (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idObjective (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idVideoCDItem (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idPDAItem (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idMoveableItem
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
//HUMANHEAD pdm: Now inherits from our hhItem
|
|
//CLASS_DECLARATION( idItem, idMoveableItem )
|
|
CLASS_DECLARATION( hhItem, 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_SHOOTABLE );
|
|
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 );
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
fl.takedamage = spawnArgs.GetBool("takedamage");
|
|
//HUMANHEAD END
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMoveableItem::Think
|
|
================
|
|
*/
|
|
void idMoveableItem::Think( void ) {
|
|
|
|
// HUMANHEAD mdl: Moved this if block up above run physics to fix the trigger not updating on movers
|
|
if ( thinkFlags & TH_PHYSICS ) {
|
|
// update trigger position
|
|
trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity );
|
|
}
|
|
|
|
RunPhysics();
|
|
|
|
if ( thinkFlags & TH_UPDATEPARTICLES ) {
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
|
|
smokeTime = 0;
|
|
BecomeInactive( TH_UPDATEPARTICLES );
|
|
}
|
|
}
|
|
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMoveableItem::SquishedByDoor
|
|
HUMANHEAD pdm
|
|
================
|
|
*/
|
|
void idMoveableItem::SquishedByDoor(idEntity *door) {
|
|
Killed(door, door, 0, vec3_origin, 0);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMoveableItem::Pickup
|
|
================
|
|
*/
|
|
bool idMoveableItem::Pickup( idPlayer *player ) {
|
|
// HUMANHEAD pdm: this now inherits from hhItem
|
|
hhItem::Pickup( player );
|
|
//HUMANHEAD rww - er, hhItem::Pickup always returns true! i'll just check if it's been hidden instead.
|
|
if ( IsHidden() ) {
|
|
trigger->SetContents( 0 );
|
|
}
|
|
return IsHidden();
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
/*
|
|
================
|
|
idMoveableItem::Damage
|
|
================
|
|
*/
|
|
void idMoveableItem::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
|
|
hhItem::Damage(inflictor, attacker, dir, damageDefName, damageScale, location);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMoveableItem::Killed
|
|
================
|
|
*/
|
|
void idMoveableItem::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
fl.takedamage = false;
|
|
PostEventMS(&EV_Remove, 0);
|
|
}
|
|
//HUMANHEAD END
|
|
|
|
/*
|
|
================
|
|
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 ) {
|
|
//HUMANHEAD jsh
|
|
if ( !kv->GetValue()[0] ) {
|
|
kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv );
|
|
continue;
|
|
}
|
|
//END HUMANHEAD
|
|
|
|
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 (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
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 (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|