etqw-sdk/source/game/misc/Door.cpp

1820 lines
39 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "Door.h"
#include "../Player.h"
#include "../script/Script_Helper.h"
#include "../script/Script_ScriptObject.h"
#include "../ContentMask.h"
idList< doorSpawnInfo_t* > idDoor::s_doorInfo;
/*
===============================================================================
sdDoorPhysicsNetworkData
===============================================================================
*/
/*
================
sdDoorPhysicsNetworkData::MakeDefault
================
*/
void sdDoorPhysicsNetworkData::MakeDefault( void ) {
currentPos = 0.f;
closing = true;
}
/*
================
sdDoorPhysicsNetworkData::Write
================
*/
void sdDoorPhysicsNetworkData::Write( idFile* file ) const {
file->WriteFloat( currentPos );
file->WriteBool( closing );
}
/*
================
sdDoorPhysicsNetworkData::Read
================
*/
void sdDoorPhysicsNetworkData::Read( idFile* file ) {
file->ReadFloat( currentPos );
file->ReadBool( closing );
}
/*
===============================================================================
sdPhysics_Door
===============================================================================
*/
/*
================
sdPhysics_Door::sdPhysics_Door
================
*/
sdPhysics_Door::sdPhysics_Door( void ) {
currentPos = 0.f;
destPos = 0.f;
speed = 0.25f;
}
/*
================
sdPhysics_Door::~sdPhysics_Door
================
*/
sdPhysics_Door::~sdPhysics_Door( void ) {
for ( int i = 0; i < bodies.Num(); i++ ) {
gameLocal.clip.DeleteClipModel( bodies[ i ].clipModel );
}
}
/*
================
sdPhysics_Door::Open
================
*/
bool sdPhysics_Door::Open( void ) {
if ( destPos == 1.f ) {
return false;
}
destPos = 1.f;
self->BecomeActive( TH_PHYSICS );
self->StartSound( "snd_open", SND_DOOR, 0, NULL );
return true;
}
/*
================
sdPhysics_Door::Close
================
*/
bool sdPhysics_Door::Close( void ) {
if ( destPos == 0.f ) {
return false;
}
destPos = 0.f;
self->BecomeActive( TH_PHYSICS );
self->StartSound( "snd_close", SND_DOOR, 0, NULL );
return true;
}
/*
================
sdPhysics_Door::SetBodyPositions
================
*/
void sdPhysics_Door::SetBodyProperties( int id, const idVec3& _pos1, const idVec3& _pos2, const idMat3& _axes, bool pusher ) {
assert( id >= 0 && id < bodies.Num() );
if ( id < 0 || id >= bodies.Num() ) {
return;
}
bodies[ id ].pos1 = _pos1;
bodies[ id ].pos2 = _pos2;
bodies[ id ].axes = _axes;
bodies[ id ].pusher = pusher;
UpdateBodyPosition( id );
}
/*
================
sdPhysics_Door::GetNumClipModels
================
*/
int sdPhysics_Door::GetNumClipModels( void ) const {
return bodies.Num();
}
/*
================
sdPhysics_Door::GetOrigin
================
*/
const idVec3& sdPhysics_Door::GetOrigin( int id ) const {
assert( id >= 0 && id < bodies.Num() );
if ( id < 0 || id >= bodies.Num() ) {
return vec3_origin;
}
return bodies[ id ].currentOrigin;
}
/*
================
sdPhysics_Door::Evaluate
================
*/
bool sdPhysics_Door::Evaluate( int timeStepMSec, int endTimeMSec ) {
if ( currentPos == destPos ) {
return false;
}
float oldPos = currentPos;
float diff = MS2SEC( timeStepMSec ) * speed;
float newPos;
if ( currentPos < destPos ) {
newPos = currentPos + diff;
if ( newPos > destPos ) {
newPos = destPos;
}
} else {
newPos = currentPos - diff;
if ( newPos < destPos ) {
newPos = destPos;
}
}
float distance = newPos - oldPos;
bool collision = false;
self->OnMoveStarted();
trace_t tr;
int collideEntityNum = 0;
for ( int i = 0; i < bodies.Num(); i++ ) {
body_t& doorBody = bodies[ i ];
idClipModel* cm = bodies[ i ].clipModel;
if ( !doorBody.pusher ) {
// not a pusher, so just try moving in that direction and stop at the end position
idVec3 end = Lerp( bodies[ i ].pos1, bodies[ i ].pos2, newPos );
if ( gameLocal.clip.TranslationEntities( CLIP_DEBUG_PARMS tr, cm->GetOrigin(), end, cm, cm->GetAxis(), bodies[ i ].clipMask, self ) ) {
distance *= tr.fraction;
newPos = oldPos + distance;
collision = true;
collideEntityNum = tr.c.entityNum;
}
} else {
// pusher, so push through the move
idVec3 oldOrigin = bodies[ i ].currentOrigin;
idVec3 newOrigin = Lerp( bodies[ i ].pos1, bodies[ i ].pos2, newPos );
idMat3 axis = GetAxis( i );
int pushFlags = PUSHFL_CLIP|PUSHFL_NOGROUNDENTITIES; //|PUSHFL_APPLYIMPULSE;
gameLocal.push.ClipPush( tr, self, pushFlags, oldOrigin, axis, newOrigin, axis, bodies[ i ].clipModel );
if ( tr.fraction < 1.0f ) {
distance *= tr.fraction;
newPos = oldPos + distance;
collision = true;
collideEntityNum = tr.c.entityNum;
} else {
newPos = ( newOrigin - bodies[ i ].pos1 ).Length();
float pathLength = ( bodies[ i ].pos2 - bodies[ i ].pos1 ).Length();
if ( pathLength > idMath::FLT_EPSILON ) {
newPos /= pathLength;
}
}
}
// re-link all the clip models that have moved so far using the new position
// otherwise they could potentially move into blocking entities
currentPos = newPos;
for ( int j = 0; j <= i; j++ ) {
UpdateBodyPosition( j );
}
}
// Gordon: as currentPos may have been modified above, reset it back so SetCurrentPos works properly
currentPos = oldPos;
self->OnMoveFinished();
SetCurrentPos( newPos );
if ( collision ) {
self->OnTeamBlocked( self, gameLocal.entities[ collideEntityNum ] );
}
return currentPos != oldPos;
}
/*
================
sdPhysics_Door::SetCurrentPos
================
*/
bool sdPhysics_Door::SetCurrentPos( float newPos ) {
if ( currentPos == newPos ) {
return false;
}
currentPos = newPos;
for ( int i = 0; i < bodies.Num(); i++ ) {
UpdateBodyPosition( i );
}
if ( currentPos == destPos ) {
self->BecomeInactive( TH_PHYSICS );
self->ReachedPosition();
} else {
self->BecomeActive( TH_PHYSICS );
}
return true;
}
/*
================
sdPhysics_Door::UpdateBodyPosition
================
*/
void sdPhysics_Door::UpdateBodyPosition( int id ) {
bodies[ id ].currentOrigin = Lerp( bodies[ id ].pos1, bodies[ id ].pos2, currentPos );
bodies[ id ].clipModel->Link( gameLocal.clip, self, id, bodies[ id ].currentOrigin, bodies[ id ].axes );
}
/*
================
sdPhysics_Door::SetClipModel
================
*/
void sdPhysics_Door::SetClipModel( idClipModel *model, float density, int id, bool freeOld ) {
assert( self );
assert( model ); // we need a clip model
assert( model->IsTraceModel() ); // and it should be a trace model
assert( density > 0.0f ); // density should be valid
assert( id >= 0 );
int max = Max( id + 1, bodies.Num() );
if ( max > bodies.Num() ) {
bodies.AssureSize( max );
}
body_t& body = bodies[ id ];
if ( freeOld ) {
if ( ( body.clipModel != NULL ) && ( body.clipModel != model ) ) {
gameLocal.clip.DeleteClipModel( body.clipModel );
}
}
body.axes = mat3_identity;
body.clipMask = 0;
body.clipModel = model;
body.currentOrigin = vec3_origin;
body.pos1 = vec3_origin;
body.pos2 = vec3_origin;
}
/*
================
sdPhysics_Door::EvaluateContacts
================
*/
bool sdPhysics_Door::EvaluateContacts( CLIP_DEBUG_PARMS_DECLARATION_ONLY ) {
return false;
}
/*
================
sdPhysics_Door::IsAtRest
================
*/
bool sdPhysics_Door::IsAtRest( void ) const {
return currentPos == destPos;
}
/*
================
sdPhysics_Door::Pushable
================
*/
bool sdPhysics_Door::IsPushable( void ) const {
return false;
}
/*
================
sdPhysics_Door::GetBounds
================
*/
const idBounds& sdPhysics_Door::GetBounds( int id ) const {
static idBounds bounds;
bounds.Clear();
if( id >= 0 && id < bodies.Num() ) {
bounds = bodies[ id ].clipModel->GetBounds();
} else {
for( int i = 0; i < bodies.Num(); i++ ) {
bounds += bodies[ i ].clipModel->GetBounds();
}
}
return bounds;
}
/*
================
sdPhysics_Door::GetAbsBounds
================
*/
const idBounds& sdPhysics_Door::GetAbsBounds( int id ) const {
static idBounds bounds;
bounds.Clear();
if( id >= 0 && id < bodies.Num() ) {
bounds = bodies[ id ].clipModel->GetAbsBounds();
} else {
for( int i = 0; i < bodies.Num(); i++ ) {
bounds += bodies[ i ].clipModel->GetAbsBounds();
}
}
return bounds;
}
/*
================
sdPhysics_Door::GetClipModel
================
*/
idClipModel* sdPhysics_Door::GetClipModel( int id ) const {
assert( id >= 0 && id < bodies.Num() );
if ( id < 0 || id >= bodies.Num() ) {
return NULL;
}
return bodies[ id ].clipModel;
}
/*
================
sdPhysics_Door::GetContents
================
*/
int sdPhysics_Door::GetContents( int id ) const {
if ( id >= 0 && id < bodies.Num() ) {
return bodies[ id ].clipModel->GetContents();
}
int contents = 0;
for ( int i = 0; i < bodies.Num(); i++ ) {
contents |= bodies[ i ].clipModel->GetContents();
}
return contents;
}
/*
================
sdPhysics_Door::SetContents
================
*/
void sdPhysics_Door::SetContents( int contents, int id ) {
assert( id >= 0 && id < bodies.Num() );
if ( id < 0 || id >= bodies.Num() ) {
return;
}
bodies[ id ].clipModel->SetContents( contents );
}
/*
================
sdPhysics_Door::GetClipMask
================
*/
int sdPhysics_Door::GetClipMask( int id ) const {
int contents = 0;
if( id >= 0 && id < bodies.Num() ) {
contents = bodies[ id ].clipMask;
} else {
for( int i = 0; i < bodies.Num(); i++ ) {
contents |= bodies[ i ].clipMask;
}
}
return contents;
}
/*
================
sdPhysics_Door::SetClipMask
================
*/
void sdPhysics_Door::SetClipMask( int mask, int id ) {
assert( id >= 0 && id < bodies.Num() );
if ( id < 0 || id >= bodies.Num() ) {
return;
}
bodies[ id ].clipMask = mask;
}
/*
================
sdPhysics_Door::GetAxis
================
*/
const idMat3& sdPhysics_Door::GetAxis( int id ) const {
assert( id >= 0 && id < bodies.Num() );
if ( id < 0 || id >= bodies.Num() ) {
return mat3_identity;
}
return bodies[ id ].axes;
}
/*
================
sdPhysics_Door::EnableClip
================
*/
void sdPhysics_Door::EnableClip( void ) {
for ( int i = 0; i < bodies.Num(); i++ ) {
bodies[ i ].clipModel->Enable();
}
}
/*
================
sdPhysics_Door::DisableClip
================
*/
void sdPhysics_Door::DisableClip( bool activateContacting ) {
for ( int i = 0; i < bodies.Num(); i++ ) {
if ( activateContacting ) {
WakeEntitiesContacting( self, bodies[ i ].clipModel );
}
bodies[ i ].clipModel->Disable();
}
}
/*
================
sdPhysics_Door::CreateNetworkStructure
================
*/
sdEntityStateNetworkData* sdPhysics_Door::CreateNetworkStructure( networkStateMode_t mode ) const {
if ( mode == NSM_VISIBLE ) {
return new sdDoorPhysicsNetworkData();
}
return NULL;
}
/*
================
sdPhysics_Door::CheckNetworkStateChanges
================
*/
bool sdPhysics_Door::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
if ( mode == NSM_VISIBLE ) {
NET_GET_BASE( sdDoorPhysicsNetworkData );
bool wantClose = destPos == 0.f;
if ( wantClose != baseData.closing ) {
return true;
}
NET_CHECK_FIELD( currentPos, currentPos );
}
return false;
}
/*
================
sdPhysics_Door::ApplyNetworkState
================
*/
void sdPhysics_Door::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
if ( mode == NSM_VISIBLE ) {
NET_GET_NEW( sdDoorPhysicsNetworkData );
if ( newData.closing ) {
Close();
} else {
Open();
}
if ( SetCurrentPos( newData.currentPos ) ) {
self->UpdateVisuals();
}
}
}
/*
================
sdPhysics_Door::ReadNetworkState
================
*/
void sdPhysics_Door::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
if ( mode == NSM_VISIBLE ) {
NET_GET_STATES( sdDoorPhysicsNetworkData );
newData.currentPos = msg.ReadDeltaFloat( baseData.currentPos );
newData.closing = msg.ReadBool();
}
}
/*
================
sdPhysics_Door::WriteNetworkState
================
*/
void sdPhysics_Door::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
if ( mode == NSM_VISIBLE ) {
NET_GET_STATES( sdDoorPhysicsNetworkData );
newData.currentPos = currentPos;
newData.closing = destPos == 0.f;
msg.WriteDeltaFloat( baseData.currentPos, newData.currentPos );
msg.WriteBool( newData.closing );
}
}
/*
===============================================================================
sdDoorNetworkData
===============================================================================
*/
/*
================
sdDoorNetworkData::MakeDefault
================
*/
void sdDoorNetworkData::MakeDefault( void ) {
closeTime = 0;
sdScriptEntityNetworkData::MakeDefault();
}
/*
================
sdDoorNetworkData::Write
================
*/
void sdDoorNetworkData::Write( idFile* file ) const {
file->WriteInt( closeTime );
sdScriptEntityNetworkData::Write( file );
}
/*
================
sdDoorNetworkData::Read
================
*/
void sdDoorNetworkData::Read( idFile* file ) {
file->ReadInt( closeTime );
sdScriptEntityNetworkData::Read( file );
}
/*
===============================================================================
idDoor
===============================================================================
*/
const idEventDef EV_Door_Open( "open", '\0', DOC_TEXT( "Opens the door, the door will naturally close again, if not in toggle mode." ), 0, "This will ignore any script checks which would normally keep the door locked." );
const idEventDef EV_Door_Close( "close", '\0', DOC_TEXT( "Closes the door." ), 0, "This will ignore any script checks which would normally keep the door locked." );
const idEventDef EV_Door_IsOpen( "isOpen", 'b', DOC_TEXT( "Returns whether the door is fully open or not." ), 0, NULL );
const idEventDef EV_Door_IsClosed( "isClosed", 'b', DOC_TEXT( "Returns whether the door is fully closed or not." ), 0, NULL );
const idEventDef EV_Door_IsOpening( "isOpening", 'b', DOC_TEXT( "Return whether the door is currently opening or not." ), 0, NULL );
const idEventDef EV_Door_IsClosing( "isClosing", 'b', DOC_TEXT( "Return whether the door is currently closing or not." ), 0, NULL );
extern const idEventDef EV_SetSkin;
extern const idEventDef EV_SetToggle;
CLASS_DECLARATION( sdScriptEntity, idDoor )
EVENT( EV_Activate, idDoor::Event_Activate )
EVENT( EV_Door_Open, idDoor::Event_Open )
EVENT( EV_Door_Close, idDoor::Event_Close )
EVENT( EV_Door_IsOpen, idDoor::Event_IsOpen )
EVENT( EV_Door_IsClosed, idDoor::Event_IsClosed )
EVENT( EV_Door_IsOpening, idDoor::Event_IsOpening )
EVENT( EV_Door_IsClosing, idDoor::Event_IsClosing )
EVENT( EV_SetSkin, idDoor::Event_SetSkin )
EVENT( EV_SetToggle, idDoor::Event_SetToggle )
END_CLASS
/*
================
idDoor::idDoor
================
*/
idDoor::idDoor( void ) {
trigger = NULL;
sndTrigger = NULL;
nextSndTriggerTime = 0;
normalAxisIndex = 0;
closeTime = 0;
moving = false;
toggle = false;
reverseOnBlock = true;
isSlave = false;
}
/*
================
idDoor::~idDoor
================
*/
idDoor::~idDoor( void ) {
parts.DeleteContents( true );
gameLocal.clip.DeleteClipModel( trigger );
gameLocal.clip.DeleteClipModel( sndTrigger );
}
/*
================
idDoor::OnTeamBlocked
================
*/
void idDoor::OnTeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
if ( crushOnBlock ) {
blockingEntity->Damage( NULL, NULL, vec3_zero, DAMAGE_FOR_NAME( "damage_mover_crush" ), 1.f, NULL );
return;
}
if ( reverseOnBlock ) {
if ( physicsObj.IsOpening() ) {
Close();
return;
}
if ( physicsObj.IsClosing() ) {
Open();
return;
}
}
}
/*
================
idDoor::VectorForDir
================
*/
void idDoor::VectorForDir( float angle, idVec3 &vec ) {
idAngles ang;
switch( ( int )angle ) {
case DIR_UP :
vec.Set( 0, 0, 1 );
break;
case DIR_DOWN :
vec.Set( 0, 0, -1 );
break;
case DIR_LEFT :
ang = physicsObj.GetAxis().ToAngles();
ang.pitch = 0;
ang.roll = 0;
ang.yaw += 90;
vec = ang.ToForward();
break;
case DIR_RIGHT :
ang = physicsObj.GetAxis().ToAngles();
ang.pitch = 0;
ang.roll = 0;
ang.yaw -= 90;
vec = ang.ToForward();
break;
case DIR_FORWARD :
ang = physicsObj.GetAxis().ToAngles();
ang.pitch = 0;
ang.roll = 0;
vec = ang.ToForward();
break;
case DIR_BACK :
ang = physicsObj.GetAxis().ToAngles();
ang.pitch = 0;
ang.roll = 0;
ang.yaw += 180;
vec = ang.ToForward();
break;
case DIR_REL_UP :
vec.Set( 0, 0, 1 );
break;
case DIR_REL_DOWN :
vec.Set( 0, 0, -1 );
break;
case DIR_REL_LEFT :
ang = physicsObj.GetAxis().ToAngles();
ang.ToVectors( NULL, &vec );
vec *= -1;
break;
case DIR_REL_RIGHT :
ang = physicsObj.GetAxis().ToAngles();
ang.ToVectors( NULL, &vec );
break;
case DIR_REL_FORWARD :
ang = physicsObj.GetAxis().ToAngles();
vec = ang.ToForward();
break;
case DIR_REL_BACK :
ang = physicsObj.GetAxis().ToAngles();
vec = ang.ToForward() * -1;
break;
default:
ang.Set( 0, angle, 0 );
vec = GetWorldVector( ang.ToForward() );
break;
}
}
/*
================
idDoor::Spawn
================
*/
void idDoor::Spawn( void ) {
baseOrg = GetPhysics()->GetOrigin();
baseAxis = GetPhysics()->GetAxis();
isSlave = !spawnArgs.GetBool( "groupmaster" );
maxHealth = health = spawnArgs.GetInt( "health" );
// default wait of 3 seconds
waitTime = SEC2MS( spawnArgs.GetFloat( "wait", "3" ) );
reverseOnBlock = spawnArgs.GetBool( "reverse_on_block", "1" );
crushOnBlock = spawnArgs.GetBool( "crush_on_block", "1" );
toggle = spawnArgs.GetBool( "toggle" );
if ( spawnArgs.GetBool( "continuous" ) ) {
PostEventSec( &EV_Activate, spawnArgs.GetFloat( "delay" ), this );
}
// sounds have a habit of stuttering when portals close, so make them unoccluded
refSound.parms.soundShaderFlags |= SSF_NO_OCCLUSION;
checkOpenFunc = scriptObject->GetFunction( "allowOpen" );
BecomeActive( TH_THINK );
int i;
for ( i = 0; i < s_doorInfo.Num(); i++ ) {
if ( !s_doorInfo[ i ]->name.Icmp( GetName() ) ) {
break;
}
}
if ( i == s_doorInfo.Num() ) {
doorSpawnInfo_t* info = new doorSpawnInfo_t;
info->renderEnt = renderEntity;
CalcPositions( info->pos1, info->pos2 );
info->axes = GetPhysics()->GetAxis();
GetTraceModel( info->trm );
info->name = GetName();
info->group = spawnArgs.GetString( "group" );
info->pusher = spawnArgs.GetBool( "pusher" );
s_doorInfo.Alloc() = info;
}
}
/*
================
idDoor::CalcPositions
================
*/
void idDoor::CalcPositions( idVec3& pos1, idVec3& pos2 ) {
pos1 = GetPhysics()->GetOrigin();
idVec3 moveDelta;
if ( spawnArgs.GetVector( "move_delta", "0 0 0", moveDelta ) ) {
bool absolute = spawnArgs.GetBool( "move_absolute", "0" );
if( !absolute ) {
pos2 = pos1 + moveDelta * GetPhysics()->GetAxis();
} else {
pos2 = pos1 + moveDelta;
}
} else {
float dir;
// get the direction to move
if ( !spawnArgs.GetFloat( "movedir", "0", dir ) ) {
// no movedir, so angle defines movement direction and not orientation,
// a la oldschool Quake
SetAngles( ang_zero );
dir = spawnArgs.GetFloat( "angle" );
}
idVec3 movedir;
// jrad - support for all the other motion types
VectorForDir( dir, movedir );
// default lip of 8 units
float lip = spawnArgs.GetFloat( "lip", "8" );
// calculate second position
idVec3 absMovedir;
absMovedir[ 0 ] = idMath::Fabs( movedir[ 0 ] );
absMovedir[ 1 ] = idMath::Fabs( movedir[ 1 ] );
absMovedir[ 2 ] = idMath::Fabs( movedir[ 2 ] );
idVec3 size = GetPhysics()->GetAbsBounds().Size();
float distance = ( absMovedir * size ) - lip;
pos2 = pos1 + distance * movedir;
}
}
/*
================
idDoor::OnMasterReady
================
*/
void idDoor::OnMasterReady( void ) {
idBounds absBounds = physicsObj.GetAbsBounds( -1 );
portal.Init( absBounds );
refSound.origin = absBounds.GetCenter();
CalcTriggerBounds( 0.f, baseBounds );
ClosePortals( false );
if ( health ) {
fl.takedamage = true;
}
const char* sndtemp = spawnArgs.GetString( "snd_locked" );
if ( *sndtemp ) {
SpawnSoundTrigger();
}
if ( !spawnArgs.GetBool( "no_touch" ) ) {
// spawn trigger
SpawnDoorTrigger();
}
}
/*
================
idDoor::EnableClip
================
*/
void idDoor::EnableClip( void ) {
if ( fl.forceDisableClip ) {
return;
}
if ( trigger ) {
LinkTrigger();
}
if ( sndTrigger ) {
LinkSoundTrigger();
}
sdScriptEntity::EnableClip();
}
/*
================
idDoor::DisableClip
================
*/
void idDoor::DisableClip( bool activateContacting ) {
if ( trigger != NULL ) {
trigger->Unlink( gameLocal.clip );
}
if ( sndTrigger != NULL ) {
sndTrigger->Unlink( gameLocal.clip );
}
sdScriptEntity::DisableClip( activateContacting );
}
/*
================
idDoor::Hide
================
*/
void idDoor::Hide( void ) {
if ( IsHidden() ) {
return;
}
fl.hidden = true;
OpenPortals();
for ( int i = 0; i < parts.Num(); i++ ) {
parts[ i ]->Hide();
}
}
/*
================
idDoor::Show
================
*/
void idDoor::Show( void ) {
if ( !IsHidden() ) {
return;
}
fl.hidden = false;
if ( physicsObj.IsClosed() ) {
ClosePortals( true );
}
for ( int i = 0; i < parts.Num(); i++ ) {
parts[ i ]->Show();
}
}
/*
================
idDoor::Use
================
*/
void idDoor::Use( idEntity *activator ) {
activator = activator;
if ( physicsObj.IsClosed() ) {
Open();
return;
}
if ( physicsObj.IsOpen() ) {
if ( toggle ) {
Close();
return;
}
if ( waitTime < 0 ) {
return;
}
// if all the way up, just delay before coming down
SetCloseTime( gameLocal.time + waitTime );
return;
}
// only partway down before reversing
if ( physicsObj.IsClosing() ) {
Open();
return;
}
// only partway up before reversing
if ( physicsObj.IsOpening() ) {
Close();
return;
}
}
/*
================
idDoor::Open
================
*/
void idDoor::Open( void ) {
if ( physicsObj.IsOpening() ) {
// already there, or on the way
return;
}
physicsObj.Open();
if ( physicsObj.IsClosed() ) {
// open areaportal
OpenPortals();
return;
}
}
/*
================
idDoor::Close
================
*/
void idDoor::Close( void ) {
if ( physicsObj.IsClosing() ) {
// already there, or on the way
return;
}
physicsObj.Close();
}
/*
================
idDoor::IsOpen
================
*/
bool idDoor::IsOpen( void ) const {
return physicsObj.IsOpen();
}
/*
================
idDoor::IsPermanentlyOpen
================
*/
bool idDoor::IsPermanentlyOpen( void ) const {
bool open = physicsObj.IsOpen();
if ( open && waitTime < 0 ) {
return true;
}
return false;
}
/*
======================
idDoor::CalcTriggerBounds
Calcs bounds for a trigger.
======================
*/
void idDoor::CalcTriggerBounds( float size, idBounds &bounds ) {
// find the bounds of everything on the team
bounds.Clear();
const idVec3& org = baseOrg;
idMat3 transpose = baseAxis.Transpose();
if ( health ) {
fl.takedamage = true;
}
for ( int i = 0; i < parts.Num(); i++ ) {
const idVec3& otherOrg = physicsObj.GetOrigin( i );
const idMat3& otherAxis = physicsObj.GetAxis( i );
idMat3 axisDiff = otherAxis * transpose;
idVec3 diff = otherOrg - org;
diff = diff * transpose;
idBounds otherBounds = physicsObj.GetBounds( i );
otherBounds.Translate( diff );
idBounds addBounds;
addBounds.FromTransformedBounds( otherBounds, diff, axisDiff );
bounds.AddBounds( addBounds );
}
// find the thinnest axis, which will be the one we expand
int best = 0;
for ( int i = 1 ; i < 3 ; i++ ) {
if ( bounds[ 1 ][ i ] - bounds[ 0 ][ i ] < bounds[ 1 ][ best ] - bounds[ 0 ][ best ] ) {
best = i;
}
}
normalAxisIndex = best;
bounds[ 0 ][ best ] -= size;
bounds[ 1 ][ best ] += size;
}
/*
======================
idDoor::LinkTrigger
======================
*/
void idDoor::LinkTrigger( void ) {
trigger->Link( gameLocal.clip, this, TRIGGER_ID, baseOrg, baseAxis );
}
/*
======================
idDoor::SpawnDoorTrigger
======================
*/
void idDoor::SpawnDoorTrigger( void ) {
if ( trigger ) {
// already have a trigger, so don't spawn a new one.
LinkTrigger();
return;
}
float triggerSize = spawnArgs.GetFloat( "triggersize", "120" );
idBounds bounds;
CalcTriggerBounds( triggerSize, bounds );
// create a trigger clip model
trigger = new idClipModel( idTraceModel( bounds ), true );
trigger->SetContents( CONTENTS_TRIGGER );
trigger->SetPosition( baseOrg, baseAxis, gameLocal.clip );
LinkTrigger();
}
/*
======================
idDoor::LinkSoundTrigger
======================
*/
void idDoor::LinkSoundTrigger( void ) {
sndTrigger->Link( gameLocal.clip, this, SND_TRIGGER_ID, baseOrg, baseAxis );
}
/*
======================
idDoor::SpawnSoundTrigger
======================
*/
void idDoor::SpawnSoundTrigger( void ) {
if ( sndTrigger ) {
LinkSoundTrigger();
return;
}
float triggerSize = spawnArgs.GetFloat( "triggersize", "120" );
idBounds bounds;
CalcTriggerBounds( triggerSize * 0.5f, bounds );
// create a trigger clip model
sndTrigger = new idClipModel( idTraceModel( bounds ), true );
sndTrigger->SetContents( CONTENTS_TRIGGER );
sndTrigger->SetPosition( baseOrg, baseAxis, gameLocal.clip );
LinkSoundTrigger();
}
/*
================
idDoor::Script_AllowOpen
================
*/
bool idDoor::Script_AllowOpen( idEntity* other ) {
sdScriptHelper helper;
helper.Push( other->GetScriptObject() );
return CallFloatNonBlockingScriptEvent( checkOpenFunc, helper ) != 0.f;
}
/*
================
idDoor::OnTouch
================
*/
void idDoor::OnTouch( idEntity *other, const trace_t& trace ) {
if ( IsHidden() ) {
return;
}
idPlayer* p = other->Cast< idPlayer >();
if ( p && p->IsSpectating() ) {
SpectatorTouch( p, trace );
return;
}
if ( p != NULL && p->GetHealth() <= 0 ) {
return;
}
bool hasRequirements = true;
if ( checkOpenFunc && !Script_AllowOpen( other ) ) {
hasRequirements = false;
}
if ( trigger && trace.c.id == trigger->GetId() ) {
if ( hasRequirements && !physicsObj.IsOpening() ) {
Use( other );
}
return;
}
if ( sndTrigger && trace.c.id == sndTrigger->GetId() ) {
if ( !hasRequirements && gameLocal.time > nextSndTriggerTime ) {
StartSound( "snd_locked", SND_ANY, 0, NULL );
nextSndTriggerTime = gameLocal.time + SEC2MS( 10.f );
}
return;
}
}
/*
================
idDoor::SpectatorTouch
================
*/
void idDoor::SpectatorTouch( idPlayer* p, const trace_t& trace ) {
assert( p && p->IsSpectating() );
if ( IsPermanentlyOpen() ) {
return;
}
// use sndTrigger as it should always be smaller than trigger
if ( sndTrigger && trace.c.id == sndTrigger->GetId() ) {
idVec3 relativeOrg = ( trace.endpos - baseOrg ) * baseAxis.Transpose();
const idBounds& bounds = sndTrigger->GetBounds();
idVec3 translate = bounds.GetCenter();
idVec3 playerSize = p->GetPhysics()->GetBounds().Size();
playerSize.z = 0.f;
if ( relativeOrg[ normalAxisIndex ] > bounds.GetCenter()[ normalAxisIndex ] ) {
translate[ normalAxisIndex ] = bounds.GetMins()[ normalAxisIndex ];
translate[ normalAxisIndex ] -= playerSize.Length();
} else {
translate[ normalAxisIndex ] = bounds.GetMaxs()[ normalAxisIndex ];
translate[ normalAxisIndex ] += playerSize.Length();
}
translate = baseOrg + ( translate * baseAxis );
p->SetOrigin( translate );
p->lastSpectateTeleport = gameLocal.time;
}
}
/*
================
idDoor::Event_Activate
================
*/
void idDoor::Event_Activate( idEntity *activator ) {
Use( activator );
}
/*
================
idDoor::Event_Open
================
*/
void idDoor::Event_Open( void ) {
Open();
}
/*
================
idDoor::Event_Close
================
*/
void idDoor::Event_Close( void ) {
Close();
}
/*
================
idDoor::Event_IsOpen
================
*/
void idDoor::Event_IsOpen( void ) {
sdProgram::ReturnBoolean( IsOpen() );
}
/*
================
idDoor::Event_IsClosed
================
*/
void idDoor::Event_IsClosed( void ) {
sdProgram::ReturnBoolean( physicsObj.IsClosed() );
}
/*
================
idDoor::Event_IsOpening
================
*/
void idDoor::Event_IsOpening( void ) {
sdProgram::ReturnBoolean( physicsObj.IsOpening() );
}
/*
================
idDoor::Event_IsClosing
================
*/
void idDoor::Event_IsClosing( void ) {
sdProgram::ReturnBoolean( physicsObj.IsClosing() );
}
/*
================
idDoor::OpenPortals
================
*/
void idDoor::OpenPortals( void ) {
if ( !portal.IsValid() ) {
return;
}
portal.Open();
if ( gameLocal.isServer ) {
sdEntityBroadcastEvent msg( this, EVENT_PORTALSTATE );
msg.WriteBool( true );
msg.Send( true, sdReliableMessageClientInfoAll() );
}
}
/*
================
idDoor::ClosePortals
================
*/
void idDoor::ClosePortals( bool force ) {
if ( !portal.IsValid() ) {
return;
}
if ( !force && IsHidden() ) {
return;
}
portal.Close();
if ( gameLocal.isServer ) {
sdEntityBroadcastEvent msg( this, EVENT_PORTALSTATE );
msg.WriteBool( false );
msg.Send( true, sdReliableMessageClientInfoAll() );
}
}
/*
================
idDoor::Event_OpenPortal
Sets the portal associtated with this door to be open
================
*/
void idDoor::Event_OpenPortal( void ) {
OpenPortals();
}
/*
================
idDoor::Event_ForceClosePortal
Sets the portal associtated with this door to be closed
================
*/
void idDoor::Event_ForceClosePortal( void ) {
ClosePortals( true );
}
/*
================
idDoor::Event_ClosePortal
Sets the portal associtated with this door to be closed
================
*/
void idDoor::Event_ClosePortal( void ) {
ClosePortals( false );
}
/*
================
idDoor::GetTraceModel
================
*/
void idDoor::GetTraceModel( idTraceModel& trm ) {
const char* clipModelName = GetClipModelName();
if ( !gameLocal.clip.LoadTraceModel( clipModelName, trm ) ) {
idClipModel *mdl = new idClipModel( clipModelName );
trm.SetupBox( mdl->GetBounds() );
gameLocal.clip.DeleteClipModel( mdl );
// gameLocal.Warning( "idDoor '%s': cannot load trace model %s", name.c_str(), clipModelName );
}
}
/*
================
idDoor::PostMapSpawn
================
*/
void idDoor::PostMapSpawn( void ) {
sdScriptEntity::PostMapSpawn();
if ( isSlave ) {
if ( !gameLocal.isClient ) {
PostEventMS( &EV_Remove, 0 );
}
return;
}
SetPhysics( &physicsObj );
physicsObj.SetSelf( this );
const char* groupName = spawnArgs.GetString( "group" );
for ( int i = 0; i < s_doorInfo.Num(); i++ ) {
doorSpawnInfo_t& info = *s_doorInfo[ i ];
if ( ( !*groupName || info.group.Icmp( groupName ) ) && info.name.Icmp( GetName() ) ) {
continue;
}
sdRenderEntityBundle* bundle = new sdRenderEntityBundle();
bundle->Copy( info.renderEnt );
bundle->Show();
int newIndex = physicsObj.GetNumClipModels();
physicsObj.SetClipModel( new idClipModel( info.trm, false ), 1.f, newIndex );
physicsObj.SetBodyProperties( newIndex, info.pos1, info.pos2, info.axes, info.pusher );
physicsObj.SetClipMask( MASK_PLAYERSOLID | CONTENTS_MONSTER, newIndex );
physicsObj.SetContents( CONTENTS_SOLID, newIndex );
parts.Alloc() = bundle;
}
float time;
if ( !spawnArgs.GetFloat( "time", "1", time ) ) {
idVec3 pos1, pos2;
CalcPositions( pos1, pos2 );
// default speed of 400
float dist = ( pos2 - pos1 ).Length();
float speed = spawnArgs.GetFloat( "speed", "400" );
time = dist / speed;
gameLocal.Warning( "idDoor::PostMapSpawn No Time Set, Using Speed Instead" );
}
physicsObj.SetSpeed( 1 / time );
OnMasterReady();
}
/*
================
idDoor::ReachedPosition
================
*/
void idDoor::ReachedPosition( void ) {
if ( physicsObj.IsOpen() ) {
StartSound( "snd_opened", SND_DOOR, 0, NULL );
if ( waitTime > 0 && !toggle ) {
SetCloseTime( gameLocal.time + waitTime );
}
return;
}
if ( physicsObj.IsClosed() ) {
StartSound( "snd_closed", SND_DOOR, 0, NULL );
ClosePortals( false );
return;
}
}
/*
================
idDoor::Think
================
*/
void idDoor::Think( void ) {
sdScriptEntity::Think();
if ( closeTime != 0 && gameLocal.time > closeTime ) {
Close();
SetCloseTime( 0 );
}
}
/*
================
idDoor::UpdateModelTransform
================
*/
void idDoor::UpdateModelTransform( void ) {
for ( int i = 0; i < parts.Num(); i++ ) {
renderEntity_t& rEnt = parts[ i ]->GetEntity();
rEnt.axis = physicsObj.GetAxis( i );
rEnt.origin = physicsObj.GetOrigin( i );
}
}
/*
================
idDoor::Present
================
*/
void idDoor::Present( void ) {
if ( ( thinkFlags & TH_UPDATEVISUALS ) == 0 ) {
return;
}
BecomeInactive( TH_UPDATEVISUALS );
OnUpdateVisuals();
if ( IsHidden() ) {
return;
}
for ( int i = 0; i < parts.Num(); i++ ) {
parts[ i ]->Update();
}
}
/*
================
idDoor::CanCollide
================
*/
bool idDoor::CanCollide( const idEntity* other, int traceId ) const {
return !moving || other->GetPhysics()->IsPushable() || other->fl.forceDoorCollision;
}
/*
================
idDoor::OnNewMapLoad
================
*/
void idDoor::OnNewMapLoad( void ) {
s_doorInfo.DeleteContents( true );
}
/*
================
idDoor::OnMapClear
================
*/
void idDoor::OnMapClear( void ) {
s_doorInfo.DeleteContents( true );
}
/*
================
idDoor::SetCloseTime
================
*/
void idDoor::SetCloseTime( int time ) {
closeTime = time;
if ( closeTime != 0 ) {
BecomeActive( TH_THINK );
}
}
/*
================
idDoor::ApplyNetworkState
================
*/
void idDoor::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
if ( mode == NSM_VISIBLE ) {
NET_GET_NEW( sdDoorNetworkData );
SetCloseTime( newData.closeTime );
}
sdScriptEntity::ApplyNetworkState( mode, newState );
}
/*
================
idDoor::ReadNetworkState
================
*/
void idDoor::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
if ( mode == NSM_VISIBLE ) {
NET_GET_STATES( sdDoorNetworkData );
newData.closeTime = msg.ReadDeltaLong( baseData.closeTime );
}
sdScriptEntity::ReadNetworkState( mode, baseState, newState, msg );
}
/*
================
idDoor::WriteNetworkState
================
*/
void idDoor::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
if ( mode == NSM_VISIBLE ) {
NET_GET_STATES( sdDoorNetworkData );
newData.closeTime = closeTime;
msg.WriteDeltaLong( baseData.closeTime, newData.closeTime );
}
sdScriptEntity::WriteNetworkState( mode, baseState, newState, msg );
}
/*
================
idDoor::CheckNetworkStateChanges
================
*/
bool idDoor::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
if ( isSlave ) {
return false;
}
if ( mode == NSM_VISIBLE ) {
NET_GET_BASE( sdDoorNetworkData );
NET_CHECK_FIELD( closeTime, closeTime );
}
return sdScriptEntity::CheckNetworkStateChanges( mode, baseState );
}
/*
================
idDoor::CreateNetworkStructure
================
*/
sdEntityStateNetworkData* idDoor::CreateNetworkStructure( networkStateMode_t mode ) const {
if ( isSlave ) {
return NULL;
}
if ( mode == NSM_VISIBLE ) {
sdDoorNetworkData* newData = new sdDoorNetworkData();
newData->physicsData = physicsObj.CreateNetworkStructure( mode );
return newData;
}
if ( mode == NSM_BROADCAST ) {
sdScriptEntityBroadcastData* newData = new sdScriptEntityBroadcastData();
newData->physicsData = physicsObj.CreateNetworkStructure( mode );
return newData;
}
return NULL;
}
/*
================
idDoor::WantsToThink
================
*/
bool idDoor::WantsToThink( void ) const {
return closeTime != 0 || sdScriptEntity::WantsToThink();
}
/*
================
idDoor::Event_SetSkin
================
*/
void idDoor::Event_SetSkin( const char* skinname ) {
const idDeclSkin* skin = *skinname ? declHolder.declSkinType.LocalFind( skinname ) : NULL;
for( int i = 0; i < parts.Num(); i++ ) {
parts[ i ]->GetEntity().customSkin = skin;
}
UpdateVisuals();
}
/*
================
idDoor::Event_SetToggle
================
*/
void idDoor::Event_SetToggle( bool t ) {
toggle = t;
closeTime = 0;
}
/*
================
idDoor::ClientReceiveEvent
================
*/
bool idDoor::ClientReceiveEvent( int event, int time, const idBitMsg& msg ) {
switch ( event ) {
case EVENT_PORTALSTATE: {
bool state = msg.ReadBool();
if ( state ) {
OpenPortals();
} else {
ClosePortals( true );
}
return true;
}
}
return sdScriptEntity::ClientReceiveEvent( event, time, msg );
}
/*
===============
idDoor::WriteDemoBaseData
==============
*/
void idDoor::WriteDemoBaseData( idFile* file ) const {
idEntity::WriteDemoBaseData( file );
file->WriteBool( portal.IsOpen() );
}
/*
===============
idDoor::ReadDemoBaseData
==============
*/
void idDoor::ReadDemoBaseData( idFile* file ) {
idEntity::ReadDemoBaseData( file );
bool state;
file->ReadBool( state );
if ( state ) {
OpenPortals();
} else {
ClosePortals( true );
}
}
/*
===============
idDoor::GetModelDefHandle
==============
*/
int idDoor::GetModelDefHandle( int id ) {
if ( id < 0 || id >= parts.Num() ) {
return -1;
} else {
return parts[id]->GetHandle();
}
}