mirror of
https://github.com/dhewm/dhewm3-sdk.git
synced 2024-11-25 22:11:11 +00:00
afebd7e1e5
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.
4695 lines
111 KiB
C++
4695 lines
111 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 "gamesys/SysCvar.h"
|
|
#include "script/Script_Thread.h"
|
|
#include "Player.h"
|
|
|
|
#include "Mover.h"
|
|
|
|
// _D3XP : rename all gameLocal.time to gameLocal.slow.time for merge!
|
|
|
|
// a mover will update any gui entities in it's target list with
|
|
// a key/val pair of "mover" "state" from below.. guis can represent
|
|
// realtime info like this
|
|
// binary only
|
|
static const char *guiBinaryMoverStates[] = {
|
|
"1", // pos 1
|
|
"2", // pos 2
|
|
"3", // moving 1 to 2
|
|
"4" // moving 2 to 1
|
|
};
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idMover
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_FindGuiTargets( "<FindGuiTargets>", NULL );
|
|
const idEventDef EV_TeamBlocked( "<teamblocked>", "ee" );
|
|
const idEventDef EV_PartBlocked( "<partblocked>", "e" );
|
|
const idEventDef EV_ReachedPos( "<reachedpos>", NULL );
|
|
const idEventDef EV_ReachedAng( "<reachedang>", NULL );
|
|
const idEventDef EV_PostRestore( "<postrestore>", "ddddd" );
|
|
const idEventDef EV_StopMoving( "stopMoving", NULL );
|
|
const idEventDef EV_StopRotating( "stopRotating", NULL );
|
|
const idEventDef EV_Speed( "speed", "f" );
|
|
const idEventDef EV_Time( "time", "f" );
|
|
const idEventDef EV_AccelTime( "accelTime", "f" );
|
|
const idEventDef EV_DecelTime( "decelTime", "f" );
|
|
const idEventDef EV_MoveTo( "moveTo", "e" );
|
|
const idEventDef EV_MoveToPos( "moveToPos", "v" );
|
|
const idEventDef EV_Move( "move", "ff" );
|
|
const idEventDef EV_MoveAccelerateTo( "accelTo", "ff" );
|
|
const idEventDef EV_MoveDecelerateTo( "decelTo", "ff" );
|
|
const idEventDef EV_RotateDownTo( "rotateDownTo", "df" );
|
|
const idEventDef EV_RotateUpTo( "rotateUpTo", "df" );
|
|
const idEventDef EV_RotateTo( "rotateTo", "v" );
|
|
const idEventDef EV_Rotate( "rotate", "v" );
|
|
const idEventDef EV_RotateOnce( "rotateOnce", "v" );
|
|
const idEventDef EV_Bob( "bob", "ffv" );
|
|
const idEventDef EV_Sway( "sway", "ffv" );
|
|
const idEventDef EV_Mover_OpenPortal( "openPortal" );
|
|
const idEventDef EV_Mover_ClosePortal( "closePortal" );
|
|
const idEventDef EV_AccelSound( "accelSound", "s" );
|
|
const idEventDef EV_DecelSound( "decelSound", "s" );
|
|
const idEventDef EV_MoveSound( "moveSound", "s" );
|
|
const idEventDef EV_Mover_InitGuiTargets( "<initguitargets>", NULL );
|
|
const idEventDef EV_EnableSplineAngles( "enableSplineAngles", NULL );
|
|
const idEventDef EV_DisableSplineAngles( "disableSplineAngles", NULL );
|
|
const idEventDef EV_RemoveInitialSplineAngles( "removeInitialSplineAngles", NULL );
|
|
const idEventDef EV_StartSpline( "startSpline", "e" );
|
|
const idEventDef EV_StopSpline( "stopSpline", NULL );
|
|
const idEventDef EV_IsMoving( "isMoving", NULL, 'd' );
|
|
const idEventDef EV_IsRotating( "isRotating", NULL, 'd' );
|
|
|
|
CLASS_DECLARATION( idEntity, idMover )
|
|
EVENT( EV_FindGuiTargets, idMover::Event_FindGuiTargets )
|
|
EVENT( EV_Thread_SetCallback, idMover::Event_SetCallback )
|
|
EVENT( EV_TeamBlocked, idMover::Event_TeamBlocked )
|
|
EVENT( EV_PartBlocked, idMover::Event_PartBlocked )
|
|
EVENT( EV_ReachedPos, idMover::Event_UpdateMove )
|
|
EVENT( EV_ReachedAng, idMover::Event_UpdateRotation )
|
|
EVENT( EV_PostRestore, idMover::Event_PostRestore )
|
|
EVENT( EV_StopMoving, idMover::Event_StopMoving )
|
|
EVENT( EV_StopRotating, idMover::Event_StopRotating )
|
|
EVENT( EV_Speed, idMover::Event_SetMoveSpeed )
|
|
EVENT( EV_Time, idMover::Event_SetMoveTime )
|
|
EVENT( EV_AccelTime, idMover::Event_SetAccellerationTime )
|
|
EVENT( EV_DecelTime, idMover::Event_SetDecelerationTime )
|
|
EVENT( EV_MoveTo, idMover::Event_MoveTo )
|
|
EVENT( EV_MoveToPos, idMover::Event_MoveToPos )
|
|
EVENT( EV_Move, idMover::Event_MoveDir )
|
|
EVENT( EV_MoveAccelerateTo, idMover::Event_MoveAccelerateTo )
|
|
EVENT( EV_MoveDecelerateTo, idMover::Event_MoveDecelerateTo )
|
|
EVENT( EV_RotateDownTo, idMover::Event_RotateDownTo )
|
|
EVENT( EV_RotateUpTo, idMover::Event_RotateUpTo )
|
|
EVENT( EV_RotateTo, idMover::Event_RotateTo )
|
|
EVENT( EV_Rotate, idMover::Event_Rotate )
|
|
EVENT( EV_RotateOnce, idMover::Event_RotateOnce )
|
|
EVENT( EV_Bob, idMover::Event_Bob )
|
|
EVENT( EV_Sway, idMover::Event_Sway )
|
|
EVENT( EV_Mover_OpenPortal, idMover::Event_OpenPortal )
|
|
EVENT( EV_Mover_ClosePortal, idMover::Event_ClosePortal )
|
|
EVENT( EV_AccelSound, idMover::Event_SetAccelSound )
|
|
EVENT( EV_DecelSound, idMover::Event_SetDecelSound )
|
|
EVENT( EV_MoveSound, idMover::Event_SetMoveSound )
|
|
EVENT( EV_Mover_InitGuiTargets, idMover::Event_InitGuiTargets )
|
|
EVENT( EV_EnableSplineAngles, idMover::Event_EnableSplineAngles )
|
|
EVENT( EV_DisableSplineAngles, idMover::Event_DisableSplineAngles )
|
|
EVENT( EV_RemoveInitialSplineAngles, idMover::Event_RemoveInitialSplineAngles )
|
|
EVENT( EV_StartSpline, idMover::Event_StartSpline )
|
|
EVENT( EV_StopSpline, idMover::Event_StopSpline )
|
|
EVENT( EV_Activate, idMover::Event_Activate )
|
|
EVENT( EV_IsMoving, idMover::Event_IsMoving )
|
|
EVENT( EV_IsRotating, idMover::Event_IsRotating )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idMover::idMover
|
|
================
|
|
*/
|
|
idMover::idMover( void ) {
|
|
memset( &move, 0, sizeof( move ) );
|
|
memset( &rot, 0, sizeof( rot ) );
|
|
move_thread = 0;
|
|
rotate_thread = 0;
|
|
dest_angles.Zero();
|
|
angle_delta.Zero();
|
|
dest_position.Zero();
|
|
move_delta.Zero();
|
|
move_speed = 0.0f;
|
|
move_time = 0;
|
|
deceltime = 0;
|
|
acceltime = 0;
|
|
stopRotation = false;
|
|
useSplineAngles = true;
|
|
lastCommand = MOVER_NONE;
|
|
damage = 0.0f;
|
|
areaPortal = 0;
|
|
fl.networkSync = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Save
|
|
================
|
|
*/
|
|
void idMover::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
|
|
savefile->WriteStaticObject( physicsObj );
|
|
|
|
savefile->WriteInt( move.stage );
|
|
savefile->WriteInt( move.acceleration );
|
|
savefile->WriteInt( move.movetime );
|
|
savefile->WriteInt( move.deceleration );
|
|
savefile->WriteVec3( move.dir );
|
|
|
|
savefile->WriteInt( rot.stage );
|
|
savefile->WriteInt( rot.acceleration );
|
|
savefile->WriteInt( rot.movetime );
|
|
savefile->WriteInt( rot.deceleration );
|
|
savefile->WriteFloat( rot.rot.pitch );
|
|
savefile->WriteFloat( rot.rot.yaw );
|
|
savefile->WriteFloat( rot.rot.roll );
|
|
|
|
savefile->WriteInt( move_thread );
|
|
savefile->WriteInt( rotate_thread );
|
|
|
|
savefile->WriteAngles( dest_angles );
|
|
savefile->WriteAngles( angle_delta );
|
|
savefile->WriteVec3( dest_position );
|
|
savefile->WriteVec3( move_delta );
|
|
|
|
savefile->WriteFloat( move_speed );
|
|
savefile->WriteInt( move_time );
|
|
savefile->WriteInt( deceltime );
|
|
savefile->WriteInt( acceltime );
|
|
savefile->WriteBool( stopRotation );
|
|
savefile->WriteBool( useSplineAngles );
|
|
savefile->WriteInt( lastCommand );
|
|
savefile->WriteFloat( damage );
|
|
|
|
savefile->WriteInt( areaPortal );
|
|
if ( areaPortal > 0 ) {
|
|
savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
|
|
}
|
|
|
|
savefile->WriteInt( guiTargets.Num() );
|
|
for( i = 0; i < guiTargets.Num(); i++ ) {
|
|
guiTargets[ i ].Save( savefile );
|
|
}
|
|
|
|
if ( splineEnt.GetEntity() && splineEnt.GetEntity()->GetSpline() ) {
|
|
idCurve_Spline<idVec3> *spline = physicsObj.GetSpline();
|
|
|
|
savefile->WriteBool( true );
|
|
splineEnt.Save( savefile );
|
|
savefile->WriteInt( spline->GetTime( 0 ) );
|
|
savefile->WriteInt( spline->GetTime( spline->GetNumValues() - 1 ) - spline->GetTime( 0 ) );
|
|
savefile->WriteInt( physicsObj.GetSplineAcceleration() );
|
|
savefile->WriteInt( physicsObj.GetSplineDeceleration() );
|
|
savefile->WriteInt( (int)physicsObj.UsingSplineAngles() );
|
|
|
|
} else {
|
|
savefile->WriteBool( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Restore
|
|
================
|
|
*/
|
|
void idMover::Restore( idRestoreGame *savefile ) {
|
|
int i, num;
|
|
bool hasSpline = false;
|
|
|
|
savefile->ReadStaticObject( physicsObj );
|
|
RestorePhysics( &physicsObj );
|
|
|
|
savefile->ReadInt( (int&)move.stage );
|
|
savefile->ReadInt( move.acceleration );
|
|
savefile->ReadInt( move.movetime );
|
|
savefile->ReadInt( move.deceleration );
|
|
savefile->ReadVec3( move.dir );
|
|
|
|
savefile->ReadInt( (int&)rot.stage );
|
|
savefile->ReadInt( rot.acceleration );
|
|
savefile->ReadInt( rot.movetime );
|
|
savefile->ReadInt( rot.deceleration );
|
|
savefile->ReadFloat( rot.rot.pitch );
|
|
savefile->ReadFloat( rot.rot.yaw );
|
|
savefile->ReadFloat( rot.rot.roll );
|
|
|
|
savefile->ReadInt( move_thread );
|
|
savefile->ReadInt( rotate_thread );
|
|
|
|
savefile->ReadAngles( dest_angles );
|
|
savefile->ReadAngles( angle_delta );
|
|
savefile->ReadVec3( dest_position );
|
|
savefile->ReadVec3( move_delta );
|
|
|
|
savefile->ReadFloat( move_speed );
|
|
savefile->ReadInt( move_time );
|
|
savefile->ReadInt( deceltime );
|
|
savefile->ReadInt( acceltime );
|
|
savefile->ReadBool( stopRotation );
|
|
savefile->ReadBool( useSplineAngles );
|
|
savefile->ReadInt( (int &)lastCommand );
|
|
savefile->ReadFloat( damage );
|
|
|
|
savefile->ReadInt( areaPortal );
|
|
if ( areaPortal > 0 ) {
|
|
int portalState = 0;
|
|
savefile->ReadInt( portalState );
|
|
gameLocal.SetPortalState( areaPortal, portalState );
|
|
}
|
|
|
|
guiTargets.Clear();
|
|
savefile->ReadInt( num );
|
|
guiTargets.SetNum( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
guiTargets[ i ].Restore( savefile );
|
|
}
|
|
|
|
savefile->ReadBool( hasSpline );
|
|
if ( hasSpline ) {
|
|
int starttime;
|
|
int totaltime;
|
|
int accel;
|
|
int decel;
|
|
int useAngles;
|
|
|
|
splineEnt.Restore( savefile );
|
|
savefile->ReadInt( starttime );
|
|
savefile->ReadInt( totaltime );
|
|
savefile->ReadInt( accel );
|
|
savefile->ReadInt( decel );
|
|
savefile->ReadInt( useAngles );
|
|
|
|
PostEventMS( &EV_PostRestore, 0, starttime, totaltime, accel, decel, useAngles );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_PostRestore
|
|
================
|
|
*/
|
|
void idMover::Event_PostRestore( int start, int total, int accel, int decel, int useSplineAng ) {
|
|
idCurve_Spline<idVec3> *spline;
|
|
|
|
idEntity *splineEntity = splineEnt.GetEntity();
|
|
if ( !splineEntity ) {
|
|
// We should never get this event if splineEnt is invalid
|
|
common->Warning( "Invalid spline entity during restore\n" );
|
|
return;
|
|
}
|
|
|
|
spline = splineEntity->GetSpline();
|
|
|
|
spline->MakeUniform( total );
|
|
spline->ShiftTime( start - spline->GetTime( 0 ) );
|
|
|
|
physicsObj.SetSpline( spline, accel, decel, ( useSplineAng != 0 ) );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Spawn
|
|
================
|
|
*/
|
|
void idMover::Spawn( void ) {
|
|
move_thread = 0;
|
|
rotate_thread = 0;
|
|
stopRotation = false;
|
|
lastCommand = MOVER_NONE;
|
|
|
|
acceltime = 1000.0f * spawnArgs.GetFloat( "accel_time", "0" );
|
|
deceltime = 1000.0f * spawnArgs.GetFloat( "decel_time", "0" );
|
|
move_time = 1000.0f * spawnArgs.GetFloat( "move_time", "1" ); // safe default value
|
|
move_speed = spawnArgs.GetFloat( "move_speed", "0" );
|
|
|
|
spawnArgs.GetFloat( "damage" , "0", damage );
|
|
|
|
dest_position = GetPhysics()->GetOrigin();
|
|
dest_angles = GetPhysics()->GetAxis().ToAngles();
|
|
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
if ( !spawnArgs.GetBool( "solid", "1" ) ) {
|
|
physicsObj.SetContents( 0 );
|
|
}
|
|
if ( !renderEntity.hModel || !spawnArgs.GetBool( "nopush" ) ) {
|
|
physicsObj.SetPusher( 0 );
|
|
}
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
|
|
SetPhysics( &physicsObj );
|
|
|
|
// see if we are on an areaportal
|
|
areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
|
|
|
|
if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
|
|
if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
|
|
PostEventMS( &EV_FindGuiTargets, 0 );
|
|
} else {
|
|
// not during spawn, so it's ok to get the targets
|
|
FindGuiTargets();
|
|
}
|
|
}
|
|
|
|
health = spawnArgs.GetInt( "health" );
|
|
if ( health ) {
|
|
fl.takedamage = true;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Hide
|
|
================
|
|
*/
|
|
void idMover::Hide( void ) {
|
|
idEntity::Hide();
|
|
physicsObj.SetContents( 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Show
|
|
================
|
|
*/
|
|
void idMover::Show( void ) {
|
|
idEntity::Show();
|
|
if ( spawnArgs.GetBool( "solid", "1" ) ) {
|
|
physicsObj.SetContents( CONTENTS_SOLID );
|
|
}
|
|
SetPhysics( &physicsObj );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idMover::Killed
|
|
============
|
|
*/
|
|
void idMover::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
fl.takedamage = false;
|
|
ActivateTargets( this );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetCallback
|
|
================
|
|
*/
|
|
void idMover::Event_SetCallback( void ) {
|
|
if ( ( lastCommand == MOVER_ROTATING ) && !rotate_thread ) {
|
|
lastCommand = MOVER_NONE;
|
|
rotate_thread = idThread::CurrentThreadNum();
|
|
idThread::ReturnInt( true );
|
|
} else if ( ( lastCommand == MOVER_MOVING || lastCommand == MOVER_SPLINE ) && !move_thread ) {
|
|
lastCommand = MOVER_NONE;
|
|
move_thread = idThread::CurrentThreadNum();
|
|
idThread::ReturnInt( true );
|
|
} else {
|
|
idThread::ReturnInt( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::VectorForDir
|
|
================
|
|
*/
|
|
void idMover::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 :
|
|
physicsObj.GetLocalAngles( ang );
|
|
ang.pitch = 0;
|
|
ang.roll = 0;
|
|
ang.yaw += 90;
|
|
vec = ang.ToForward();
|
|
break;
|
|
|
|
case DIR_RIGHT :
|
|
physicsObj.GetLocalAngles( ang );
|
|
ang.pitch = 0;
|
|
ang.roll = 0;
|
|
ang.yaw -= 90;
|
|
vec = ang.ToForward();
|
|
break;
|
|
|
|
case DIR_FORWARD :
|
|
physicsObj.GetLocalAngles( ang );
|
|
ang.pitch = 0;
|
|
ang.roll = 0;
|
|
vec = ang.ToForward();
|
|
break;
|
|
|
|
case DIR_BACK :
|
|
physicsObj.GetLocalAngles( ang );
|
|
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 :
|
|
physicsObj.GetLocalAngles( ang );
|
|
ang.ToVectors( NULL, &vec );
|
|
vec *= -1;
|
|
break;
|
|
|
|
case DIR_REL_RIGHT :
|
|
physicsObj.GetLocalAngles( ang );
|
|
ang.ToVectors( NULL, &vec );
|
|
break;
|
|
|
|
case DIR_REL_FORWARD :
|
|
physicsObj.GetLocalAngles( ang );
|
|
vec = ang.ToForward();
|
|
break;
|
|
|
|
case DIR_REL_BACK :
|
|
physicsObj.GetLocalAngles( ang );
|
|
vec = ang.ToForward() * -1;
|
|
break;
|
|
|
|
default:
|
|
ang.Set( 0, angle, 0 );
|
|
vec = GetWorldVector( ang.ToForward() );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::FindGuiTargets
|
|
================
|
|
*/
|
|
void idMover::FindGuiTargets( void ) {
|
|
gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idMover::SetGuiState
|
|
|
|
key/val will be set to any renderEntity->gui's on the list
|
|
==============================
|
|
*/
|
|
void idMover::SetGuiState( const char *key, const char *val ) const {
|
|
gameLocal.Printf( "Setting %s to %s\n", key, val );
|
|
for( int i = 0; i < guiTargets.Num(); i++ ) {
|
|
idEntity *ent = guiTargets[ i ].GetEntity();
|
|
if ( ent ) {
|
|
for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
|
|
if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
|
|
ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
|
|
ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
|
|
}
|
|
}
|
|
ent->UpdateVisuals();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_InitGuiTargets
|
|
================
|
|
*/
|
|
void idMover::Event_FindGuiTargets( void ) {
|
|
FindGuiTargets();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::SetGuiStates
|
|
================
|
|
*/
|
|
void idMover::SetGuiStates( const char *state ) {
|
|
int i;
|
|
if ( guiTargets.Num() ) {
|
|
SetGuiState( "movestate", state );
|
|
}
|
|
for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
|
if ( renderEntity.gui[ i ] ) {
|
|
renderEntity.gui[ i ]->SetStateString( "movestate", state );
|
|
renderEntity.gui[ i ]->StateChanged( gameLocal.slow.time, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_InitGuiTargets
|
|
================
|
|
*/
|
|
void idMover::Event_InitGuiTargets( void ) {
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Translation control functions
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idMover::Event_StopMoving
|
|
================
|
|
*/
|
|
void idMover::Event_StopMoving( void ) {
|
|
physicsObj.GetLocalOrigin( dest_position );
|
|
DoneMoving();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::DoneMoving
|
|
================
|
|
*/
|
|
void idMover::DoneMoving( void ) {
|
|
|
|
if ( lastCommand != MOVER_SPLINE ) {
|
|
// set our final position so that we get rid of any numerical inaccuracy
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
|
|
}
|
|
|
|
lastCommand = MOVER_NONE;
|
|
idThread::ObjectMoveDone( move_thread, this );
|
|
move_thread = 0;
|
|
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::UpdateMoveSound
|
|
================
|
|
*/
|
|
void idMover::UpdateMoveSound( moveStage_t stage ) {
|
|
switch( stage ) {
|
|
case ACCELERATION_STAGE: {
|
|
StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
|
|
break;
|
|
}
|
|
case LINEAR_STAGE: {
|
|
StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
|
|
break;
|
|
}
|
|
case DECELERATION_STAGE: {
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
break;
|
|
}
|
|
case FINISHED_STAGE: {
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_UpdateMove
|
|
================
|
|
*/
|
|
void idMover::Event_UpdateMove( void ) {
|
|
idVec3 org;
|
|
|
|
physicsObj.GetLocalOrigin( org );
|
|
|
|
UpdateMoveSound( move.stage );
|
|
|
|
switch( move.stage ) {
|
|
case ACCELERATION_STAGE: {
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, move.acceleration, org, move.dir, vec3_origin );
|
|
if ( move.movetime > 0 ) {
|
|
move.stage = LINEAR_STAGE;
|
|
} else if ( move.deceleration > 0 ) {
|
|
move.stage = DECELERATION_STAGE;
|
|
} else {
|
|
move.stage = FINISHED_STAGE;
|
|
}
|
|
break;
|
|
}
|
|
case LINEAR_STAGE: {
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, move.movetime, org, move.dir, vec3_origin );
|
|
if ( move.deceleration ) {
|
|
move.stage = DECELERATION_STAGE;
|
|
} else {
|
|
move.stage = FINISHED_STAGE;
|
|
}
|
|
break;
|
|
}
|
|
case DECELERATION_STAGE: {
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, move.deceleration, org, move.dir, vec3_origin );
|
|
move.stage = FINISHED_STAGE;
|
|
break;
|
|
}
|
|
case FINISHED_STAGE: {
|
|
if ( g_debugMover.GetBool() ) {
|
|
gameLocal.Printf( "%d: '%s' move done\n", gameLocal.slow.time, name.c_str() );
|
|
}
|
|
DoneMoving();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::BeginMove
|
|
================
|
|
*/
|
|
void idMover::BeginMove( idThread *thread ) {
|
|
moveStage_t stage;
|
|
idVec3 org;
|
|
float dist;
|
|
float acceldist;
|
|
int totalacceltime;
|
|
int at;
|
|
int dt;
|
|
|
|
lastCommand = MOVER_MOVING;
|
|
move_thread = 0;
|
|
|
|
physicsObj.GetLocalOrigin( org );
|
|
|
|
move_delta = dest_position - org;
|
|
if ( move_delta.Compare( vec3_zero ) ) {
|
|
DoneMoving();
|
|
return;
|
|
}
|
|
|
|
// scale times up to whole physics frames
|
|
at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
|
|
move_time += at - acceltime;
|
|
acceltime = at;
|
|
dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
|
|
move_time += dt - deceltime;
|
|
deceltime = dt;
|
|
|
|
// if we're moving at a specific speed, we need to calculate the move time
|
|
if ( move_speed ) {
|
|
dist = move_delta.Length();
|
|
|
|
totalacceltime = acceltime + deceltime;
|
|
|
|
// calculate the distance we'll move during acceleration and deceleration
|
|
acceldist = totalacceltime * 0.5f * 0.001f * move_speed;
|
|
if ( acceldist >= dist ) {
|
|
// going too slow for this distance to move at a constant speed
|
|
move_time = totalacceltime;
|
|
} else {
|
|
// calculate move time taking acceleration into account
|
|
move_time = totalacceltime + 1000.0f * ( dist - acceldist ) / move_speed;
|
|
}
|
|
}
|
|
|
|
// scale time up to a whole physics frames
|
|
move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
|
|
|
|
if ( acceltime ) {
|
|
stage = ACCELERATION_STAGE;
|
|
} else if ( move_time <= deceltime ) {
|
|
stage = DECELERATION_STAGE;
|
|
} else {
|
|
stage = LINEAR_STAGE;
|
|
}
|
|
|
|
at = acceltime;
|
|
dt = deceltime;
|
|
|
|
if ( at + dt > move_time ) {
|
|
// there's no real correct way to handle this, so we just scale
|
|
// the times to fit into the move time in the same proportions
|
|
at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
|
|
dt = move_time - at;
|
|
}
|
|
|
|
move_delta = move_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
|
|
|
|
move.stage = stage;
|
|
move.acceleration = at;
|
|
move.movetime = move_time - at - dt;
|
|
move.deceleration = dt;
|
|
move.dir = move_delta;
|
|
|
|
ProcessEvent( &EV_ReachedPos );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Rotation control functions
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idMover::Event_StopRotating
|
|
================
|
|
*/
|
|
void idMover::Event_StopRotating( void ) {
|
|
physicsObj.GetLocalAngles( dest_angles );
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
|
|
DoneRotating();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::DoneRotating
|
|
================
|
|
*/
|
|
void idMover::DoneRotating( void ) {
|
|
lastCommand = MOVER_NONE;
|
|
idThread::ObjectMoveDone( rotate_thread, this );
|
|
rotate_thread = 0;
|
|
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::UpdateRotationSound
|
|
================
|
|
*/
|
|
void idMover::UpdateRotationSound( moveStage_t stage ) {
|
|
switch( stage ) {
|
|
case ACCELERATION_STAGE: {
|
|
StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
|
|
break;
|
|
}
|
|
case LINEAR_STAGE: {
|
|
StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
|
|
break;
|
|
}
|
|
case DECELERATION_STAGE: {
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
break;
|
|
}
|
|
case FINISHED_STAGE: {
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_UpdateRotation
|
|
================
|
|
*/
|
|
void idMover::Event_UpdateRotation( void ) {
|
|
idAngles ang;
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
|
|
UpdateRotationSound( rot.stage );
|
|
|
|
switch( rot.stage ) {
|
|
case ACCELERATION_STAGE: {
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, rot.acceleration, ang, rot.rot, ang_zero );
|
|
if ( rot.movetime > 0 ) {
|
|
rot.stage = LINEAR_STAGE;
|
|
} else if ( rot.deceleration > 0 ) {
|
|
rot.stage = DECELERATION_STAGE;
|
|
} else {
|
|
rot.stage = FINISHED_STAGE;
|
|
}
|
|
break;
|
|
}
|
|
case LINEAR_STAGE: {
|
|
if ( !stopRotation && !rot.deceleration ) {
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, rot.movetime, ang, rot.rot, ang_zero );
|
|
} else {
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, rot.movetime, ang, rot.rot, ang_zero );
|
|
}
|
|
|
|
if ( rot.deceleration ) {
|
|
rot.stage = DECELERATION_STAGE;
|
|
} else {
|
|
rot.stage = FINISHED_STAGE;
|
|
}
|
|
break;
|
|
}
|
|
case DECELERATION_STAGE: {
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, rot.deceleration, ang, rot.rot, ang_zero );
|
|
rot.stage = FINISHED_STAGE;
|
|
break;
|
|
}
|
|
case FINISHED_STAGE: {
|
|
lastCommand = MOVER_NONE;
|
|
if ( stopRotation ) {
|
|
// set our final angles so that we get rid of any numerical inaccuracy
|
|
dest_angles.Normalize360();
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
|
|
stopRotation = false;
|
|
} else if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_ACCELLINEAR ) {
|
|
// keep our angular velocity constant
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, ang, rot.rot, ang_zero );
|
|
}
|
|
|
|
if ( g_debugMover.GetBool() ) {
|
|
gameLocal.Printf( "%d: '%s' rotation done\n", gameLocal.slow.time, name.c_str() );
|
|
}
|
|
|
|
DoneRotating();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::BeginRotation
|
|
================
|
|
*/
|
|
void idMover::BeginRotation( idThread *thread, bool stopwhendone ) {
|
|
moveStage_t stage;
|
|
idAngles ang;
|
|
int at;
|
|
int dt;
|
|
|
|
lastCommand = MOVER_ROTATING;
|
|
rotate_thread = 0;
|
|
|
|
// rotation always uses move_time so that if a move was started before the rotation,
|
|
// the rotation will take the same amount of time as the move. If no move has been
|
|
// started and no time is set, the rotation takes 1 second.
|
|
if ( !move_time ) {
|
|
move_time = 1;
|
|
}
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
angle_delta = dest_angles - ang;
|
|
if ( angle_delta == ang_zero ) {
|
|
// set our final angles so that we get rid of any numerical inaccuracy
|
|
dest_angles.Normalize360();
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
|
|
stopRotation = false;
|
|
DoneRotating();
|
|
return;
|
|
}
|
|
|
|
// scale times up to whole physics frames
|
|
at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
|
|
move_time += at - acceltime;
|
|
acceltime = at;
|
|
dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
|
|
move_time += dt - deceltime;
|
|
deceltime = dt;
|
|
move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
|
|
|
|
if ( acceltime ) {
|
|
stage = ACCELERATION_STAGE;
|
|
} else if ( move_time <= deceltime ) {
|
|
stage = DECELERATION_STAGE;
|
|
} else {
|
|
stage = LINEAR_STAGE;
|
|
}
|
|
|
|
at = acceltime;
|
|
dt = deceltime;
|
|
|
|
if ( at + dt > move_time ) {
|
|
// there's no real correct way to handle this, so we just scale
|
|
// the times to fit into the move time in the same proportions
|
|
at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
|
|
dt = move_time - at;
|
|
}
|
|
|
|
angle_delta = angle_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
|
|
|
|
stopRotation = stopwhendone || ( dt != 0 );
|
|
|
|
rot.stage = stage;
|
|
rot.acceleration = at;
|
|
rot.movetime = move_time - at - dt;
|
|
rot.deceleration = dt;
|
|
rot.rot = angle_delta;
|
|
|
|
ProcessEvent( &EV_ReachedAng );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Script callable routines
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
===============
|
|
idMover::Event_TeamBlocked
|
|
===============
|
|
*/
|
|
void idMover::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
|
|
if ( g_debugMover.GetBool() ) {
|
|
gameLocal.Printf( "%d: '%s' stopped due to team member '%s' blocked by '%s'\n", gameLocal.slow.time, name.c_str(), blockedEntity->name.c_str(), blockingEntity->name.c_str() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover::Event_PartBlocked
|
|
===============
|
|
*/
|
|
void idMover::Event_PartBlocked( idEntity *blockingEntity ) {
|
|
if ( damage > 0.0f ) {
|
|
blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
|
|
}
|
|
if ( g_debugMover.GetBool() ) {
|
|
gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.slow.time, name.c_str(), blockingEntity->name.c_str() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetMoveSpeed
|
|
================
|
|
*/
|
|
void idMover::Event_SetMoveSpeed( float speed ) {
|
|
if ( speed <= 0 ) {
|
|
gameLocal.Error( "Cannot set speed less than or equal to 0." );
|
|
}
|
|
|
|
move_speed = speed;
|
|
move_time = 0; // move_time is calculated for each move when move_speed is non-0
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetMoveTime
|
|
================
|
|
*/
|
|
void idMover::Event_SetMoveTime( float time ) {
|
|
if ( time <= 0 ) {
|
|
gameLocal.Error( "Cannot set time less than or equal to 0." );
|
|
}
|
|
|
|
move_speed = 0;
|
|
move_time = SEC2MS( time );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetAccellerationTime
|
|
================
|
|
*/
|
|
void idMover::Event_SetAccellerationTime( float time ) {
|
|
if ( time < 0 ) {
|
|
gameLocal.Error( "Cannot set acceleration time less than 0." );
|
|
}
|
|
|
|
acceltime = SEC2MS( time );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetDecelerationTime
|
|
================
|
|
*/
|
|
void idMover::Event_SetDecelerationTime( float time ) {
|
|
if ( time < 0 ) {
|
|
gameLocal.Error( "Cannot set deceleration time less than 0." );
|
|
}
|
|
|
|
deceltime = SEC2MS( time );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_MoveTo
|
|
================
|
|
*/
|
|
void idMover::Event_MoveTo( idEntity *ent ) {
|
|
if ( !ent ) {
|
|
gameLocal.Warning( "Entity not found" );
|
|
}
|
|
|
|
dest_position = GetLocalCoordinates( ent->GetPhysics()->GetOrigin() );
|
|
BeginMove( idThread::CurrentThread() );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::MoveToPos
|
|
================
|
|
*/
|
|
void idMover::MoveToPos( const idVec3 &pos ) {
|
|
dest_position = GetLocalCoordinates( pos );
|
|
BeginMove( NULL );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_MoveToPos
|
|
================
|
|
*/
|
|
void idMover::Event_MoveToPos( idVec3 &pos ) {
|
|
MoveToPos( pos );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_MoveDir
|
|
================
|
|
*/
|
|
void idMover::Event_MoveDir( float angle, float distance ) {
|
|
idVec3 dir;
|
|
idVec3 org;
|
|
|
|
physicsObj.GetLocalOrigin( org );
|
|
VectorForDir( angle, dir );
|
|
dest_position = org + dir * distance;
|
|
|
|
BeginMove( idThread::CurrentThread() );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_MoveAccelerateTo
|
|
================
|
|
*/
|
|
void idMover::Event_MoveAccelerateTo( float speed, float time ) {
|
|
float v;
|
|
idVec3 org, dir;
|
|
int at;
|
|
|
|
if ( time < 0 ) {
|
|
gameLocal.Error( "idMover::Event_MoveAccelerateTo: cannot set acceleration time less than 0." );
|
|
}
|
|
|
|
dir = physicsObj.GetLinearVelocity();
|
|
v = dir.Normalize();
|
|
|
|
// if not moving already
|
|
if ( v == 0.0f ) {
|
|
gameLocal.Error( "idMover::Event_MoveAccelerateTo: not moving." );
|
|
}
|
|
|
|
// if already moving faster than the desired speed
|
|
if ( v >= speed ) {
|
|
return;
|
|
}
|
|
|
|
at = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
|
|
|
|
lastCommand = MOVER_MOVING;
|
|
|
|
physicsObj.GetLocalOrigin( org );
|
|
|
|
move.stage = ACCELERATION_STAGE;
|
|
move.acceleration = at;
|
|
move.movetime = 0;
|
|
move.deceleration = 0;
|
|
|
|
StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.slow.time, move.acceleration, org, dir * ( speed - v ), dir * v );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_MoveDecelerateTo
|
|
================
|
|
*/
|
|
void idMover::Event_MoveDecelerateTo( float speed, float time ) {
|
|
float v;
|
|
idVec3 org, dir;
|
|
int dt;
|
|
|
|
if ( time < 0 ) {
|
|
gameLocal.Error( "idMover::Event_MoveDecelerateTo: cannot set deceleration time less than 0." );
|
|
}
|
|
|
|
dir = physicsObj.GetLinearVelocity();
|
|
v = dir.Normalize();
|
|
|
|
// if not moving already
|
|
if ( v == 0.0f ) {
|
|
gameLocal.Error( "idMover::Event_MoveDecelerateTo: not moving." );
|
|
}
|
|
|
|
// if already moving slower than the desired speed
|
|
if ( v <= speed ) {
|
|
return;
|
|
}
|
|
|
|
dt = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
|
|
|
|
lastCommand = MOVER_MOVING;
|
|
|
|
physicsObj.GetLocalOrigin( org );
|
|
|
|
move.stage = DECELERATION_STAGE;
|
|
move.acceleration = 0;
|
|
move.movetime = 0;
|
|
move.deceleration = dt;
|
|
|
|
StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.slow.time, move.deceleration, org, dir * ( v - speed ), dir * speed );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_RotateDownTo
|
|
================
|
|
*/
|
|
void idMover::Event_RotateDownTo( int axis, float angle ) {
|
|
idAngles ang;
|
|
|
|
if ( ( axis < 0 ) || ( axis > 2 ) ) {
|
|
gameLocal.Error( "Invalid axis" );
|
|
}
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
|
|
dest_angles[ axis ] = angle;
|
|
if ( dest_angles[ axis ] > ang[ axis ] ) {
|
|
dest_angles[ axis ] -= 360;
|
|
}
|
|
|
|
BeginRotation( idThread::CurrentThread(), true );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_RotateUpTo
|
|
================
|
|
*/
|
|
void idMover::Event_RotateUpTo( int axis, float angle ) {
|
|
idAngles ang;
|
|
|
|
if ( ( axis < 0 ) || ( axis > 2 ) ) {
|
|
gameLocal.Error( "Invalid axis" );
|
|
}
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
|
|
dest_angles[ axis ] = angle;
|
|
if ( dest_angles[ axis ] < ang[ axis ] ) {
|
|
dest_angles[ axis ] += 360;
|
|
}
|
|
|
|
BeginRotation( idThread::CurrentThread(), true );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_RotateTo
|
|
================
|
|
*/
|
|
void idMover::Event_RotateTo( idAngles &angles ) {
|
|
dest_angles = angles;
|
|
BeginRotation( idThread::CurrentThread(), true );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_Rotate
|
|
================
|
|
*/
|
|
void idMover::Event_Rotate( idAngles &angles ) {
|
|
idAngles ang;
|
|
|
|
if ( rotate_thread ) {
|
|
DoneRotating();
|
|
}
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
dest_angles = ang + angles * ( move_time - ( acceltime + deceltime ) / 2 ) * 0.001f;
|
|
|
|
BeginRotation( idThread::CurrentThread(), false );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_RotateOnce
|
|
================
|
|
*/
|
|
void idMover::Event_RotateOnce( idAngles &angles ) {
|
|
idAngles ang;
|
|
|
|
if ( rotate_thread ) {
|
|
DoneRotating();
|
|
}
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
dest_angles = ang + angles;
|
|
|
|
BeginRotation( idThread::CurrentThread(), true );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_Bob
|
|
================
|
|
*/
|
|
void idMover::Event_Bob( float speed, float phase, idVec3 &depth ) {
|
|
idVec3 org;
|
|
|
|
physicsObj.GetLocalOrigin( org );
|
|
physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), speed * 1000 * phase, speed * 500, org, depth * 2.0f, vec3_origin );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_Sway
|
|
================
|
|
*/
|
|
void idMover::Event_Sway( float speed, float phase, idAngles &depth ) {
|
|
idAngles ang, angSpeed;
|
|
float duration;
|
|
|
|
physicsObj.GetLocalAngles( ang );
|
|
assert ( speed > 0.0f );
|
|
duration = idMath::Sqrt( depth[0] * depth[0] + depth[1] * depth[1] + depth[2] * depth[2] ) / speed;
|
|
angSpeed = depth / ( duration * idMath::SQRT_1OVER2 );
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), duration * 1000.0f * phase, duration * 1000.0f, ang, angSpeed, ang_zero );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_OpenPortal
|
|
|
|
Sets the portal associtated with this mover to be open
|
|
================
|
|
*/
|
|
void idMover::Event_OpenPortal( void ) {
|
|
if ( areaPortal ) {
|
|
SetPortalState( true );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_ClosePortal
|
|
|
|
Sets the portal associtated with this mover to be closed
|
|
================
|
|
*/
|
|
void idMover::Event_ClosePortal( void ) {
|
|
if ( areaPortal ) {
|
|
SetPortalState( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetAccelSound
|
|
================
|
|
*/
|
|
void idMover::Event_SetAccelSound( const char *sound ) {
|
|
// refSound.SetSound( "accel", sound );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetDecelSound
|
|
================
|
|
*/
|
|
void idMover::Event_SetDecelSound( const char *sound ) {
|
|
// refSound.SetSound( "decel", sound );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_SetMoveSound
|
|
================
|
|
*/
|
|
void idMover::Event_SetMoveSound( const char *sound ) {
|
|
// refSound.SetSound( "move", sound );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_EnableSplineAngles
|
|
================
|
|
*/
|
|
void idMover::Event_EnableSplineAngles( void ) {
|
|
useSplineAngles = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_DisableSplineAngles
|
|
================
|
|
*/
|
|
void idMover::Event_DisableSplineAngles( void ) {
|
|
useSplineAngles = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_RemoveInitialSplineAngles
|
|
================
|
|
*/
|
|
void idMover::Event_RemoveInitialSplineAngles( void ) {
|
|
idCurve_Spline<idVec3> *spline;
|
|
idAngles ang;
|
|
|
|
spline = physicsObj.GetSpline();
|
|
if ( !spline ) {
|
|
return;
|
|
}
|
|
ang = spline->GetCurrentFirstDerivative( 0 ).ToAngles();
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, -ang, ang_zero, ang_zero );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_StartSpline
|
|
================
|
|
*/
|
|
void idMover::Event_StartSpline( idEntity *splineEntity ) {
|
|
idCurve_Spline<idVec3> *spline;
|
|
|
|
if ( !splineEntity ) {
|
|
return;
|
|
}
|
|
|
|
// Needed for savegames
|
|
splineEnt = splineEntity;
|
|
|
|
spline = splineEntity->GetSpline();
|
|
if ( !spline ) {
|
|
return;
|
|
}
|
|
|
|
lastCommand = MOVER_SPLINE;
|
|
move_thread = 0;
|
|
|
|
if ( acceltime + deceltime > move_time ) {
|
|
acceltime = move_time / 2;
|
|
deceltime = move_time - acceltime;
|
|
}
|
|
move.stage = FINISHED_STAGE;
|
|
move.acceleration = acceltime;
|
|
move.movetime = move_time;
|
|
move.deceleration = deceltime;
|
|
|
|
spline->MakeUniform( move_time );
|
|
spline->ShiftTime( gameLocal.slow.time - spline->GetTime( 0 ) );
|
|
|
|
physicsObj.SetSpline( spline, move.acceleration, move.deceleration, useSplineAngles );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_StopSpline
|
|
================
|
|
*/
|
|
void idMover::Event_StopSpline( void ) {
|
|
physicsObj.SetSpline( NULL, 0, 0, useSplineAngles );
|
|
splineEnt = NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_Activate
|
|
================
|
|
*/
|
|
void idMover::Event_Activate( idEntity *activator ) {
|
|
Show();
|
|
Event_StartSpline( this );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_IsMoving
|
|
================
|
|
*/
|
|
void idMover::Event_IsMoving( void ) {
|
|
if ( physicsObj.GetLinearExtrapolationType() == EXTRAPOLATION_NONE ) {
|
|
idThread::ReturnInt( false );
|
|
} else {
|
|
idThread::ReturnInt( true );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::Event_IsRotating
|
|
================
|
|
*/
|
|
void idMover::Event_IsRotating( void ) {
|
|
if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_NONE ) {
|
|
idThread::ReturnInt( false );
|
|
} else {
|
|
idThread::ReturnInt( true );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idMover::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
physicsObj.WriteToSnapshot( msg );
|
|
msg.WriteBits( move.stage, 3 );
|
|
msg.WriteBits( rot.stage, 3 );
|
|
WriteBindToSnapshot( msg );
|
|
WriteGUIToSnapshot( msg );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idMover::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
moveStage_t oldMoveStage = move.stage;
|
|
moveStage_t oldRotStage = rot.stage;
|
|
|
|
physicsObj.ReadFromSnapshot( msg );
|
|
move.stage = (moveStage_t) msg.ReadBits( 3 );
|
|
rot.stage = (moveStage_t) msg.ReadBits( 3 );
|
|
ReadBindFromSnapshot( msg );
|
|
ReadGUIFromSnapshot( msg );
|
|
|
|
if ( msg.HasChanged() ) {
|
|
if ( move.stage != oldMoveStage ) {
|
|
UpdateMoveSound( oldMoveStage );
|
|
}
|
|
if ( rot.stage != oldRotStage ) {
|
|
UpdateRotationSound( oldRotStage );
|
|
}
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover::SetPortalState
|
|
================
|
|
*/
|
|
void idMover::SetPortalState( bool open ) {
|
|
assert( areaPortal );
|
|
gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idSplinePath, holds a spline path to be used by an idMover
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idSplinePath )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idSplinePath::idSplinePath
|
|
================
|
|
*/
|
|
idSplinePath::idSplinePath() {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idSplinePath::Spawn
|
|
================
|
|
*/
|
|
void idSplinePath::Spawn( void ) {
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idElevator
|
|
|
|
===============================================================================
|
|
*/
|
|
const idEventDef EV_PostArrival( "postArrival", NULL );
|
|
const idEventDef EV_GotoFloor( "gotoFloor", "d" );
|
|
#ifdef _D3XP
|
|
const idEventDef EV_SetGuiStates( "setGuiStates" );
|
|
#endif
|
|
|
|
CLASS_DECLARATION( idMover, idElevator )
|
|
EVENT( EV_Activate, idElevator::Event_Activate )
|
|
EVENT( EV_TeamBlocked, idElevator::Event_TeamBlocked )
|
|
EVENT( EV_PartBlocked, idElevator::Event_PartBlocked )
|
|
EVENT( EV_PostArrival, idElevator::Event_PostFloorArrival )
|
|
EVENT( EV_GotoFloor, idElevator::Event_GotoFloor )
|
|
EVENT( EV_Touch, idElevator::Event_Touch )
|
|
#ifdef _D3XP
|
|
EVENT( EV_SetGuiStates, idElevator::Event_SetGuiStates )
|
|
#endif
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idElevator::idElevator
|
|
================
|
|
*/
|
|
idElevator::idElevator( void ) {
|
|
state = INIT;
|
|
floorInfo.Clear();
|
|
currentFloor = 0;
|
|
pendingFloor = 0;
|
|
lastFloor = 0;
|
|
controlsDisabled = false;
|
|
lastTouchTime = 0;
|
|
returnFloor = 0;
|
|
returnTime = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Save
|
|
================
|
|
*/
|
|
void idElevator::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
|
|
savefile->WriteInt( (int)state );
|
|
|
|
savefile->WriteInt( floorInfo.Num() );
|
|
for ( i = 0; i < floorInfo.Num(); i++ ) {
|
|
savefile->WriteVec3( floorInfo[ i ].pos );
|
|
savefile->WriteString( floorInfo[ i ].door );
|
|
savefile->WriteInt( floorInfo[ i ].floor );
|
|
}
|
|
|
|
savefile->WriteInt( currentFloor );
|
|
savefile->WriteInt( pendingFloor );
|
|
savefile->WriteInt( lastFloor );
|
|
savefile->WriteBool( controlsDisabled );
|
|
savefile->WriteFloat( returnTime );
|
|
savefile->WriteInt( returnFloor );
|
|
savefile->WriteInt( lastTouchTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Restore
|
|
================
|
|
*/
|
|
void idElevator::Restore( idRestoreGame *savefile ) {
|
|
int i, num;
|
|
|
|
savefile->ReadInt( (int &)state );
|
|
|
|
savefile->ReadInt( num );
|
|
for ( i = 0; i < num; i++ ) {
|
|
floorInfo_s floor;
|
|
|
|
savefile->ReadVec3( floor.pos );
|
|
savefile->ReadString( floor.door );
|
|
savefile->ReadInt( floor.floor );
|
|
|
|
floorInfo.Append( floor );
|
|
}
|
|
|
|
savefile->ReadInt( currentFloor );
|
|
savefile->ReadInt( pendingFloor );
|
|
savefile->ReadInt( lastFloor );
|
|
savefile->ReadBool( controlsDisabled );
|
|
savefile->ReadFloat( returnTime );
|
|
savefile->ReadInt( returnFloor );
|
|
savefile->ReadInt( lastTouchTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Spawn
|
|
================
|
|
*/
|
|
void idElevator::Spawn( void ) {
|
|
idStr str;
|
|
int len1;
|
|
|
|
lastFloor = 0;
|
|
currentFloor = 0;
|
|
pendingFloor = spawnArgs.GetInt( "floor", "1" );
|
|
SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1]);
|
|
|
|
returnTime = spawnArgs.GetFloat( "returnTime" );
|
|
returnFloor = spawnArgs.GetInt( "returnFloor" );
|
|
|
|
len1 = strlen( "floorPos_" );
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "floorPos_", NULL );
|
|
while( kv ) {
|
|
str = kv->GetKey().Right( kv->GetKey().Length() - len1 );
|
|
floorInfo_s fi;
|
|
fi.floor = atoi( str );
|
|
fi.door = spawnArgs.GetString( va( "floorDoor_%i", fi.floor ) );
|
|
fi.pos = spawnArgs.GetVector( kv->GetKey() );
|
|
floorInfo.Append( fi );
|
|
kv = spawnArgs.MatchPrefix( "floorPos_", kv );
|
|
}
|
|
lastTouchTime = 0;
|
|
state = INIT;
|
|
BecomeActive( TH_THINK | TH_PHYSICS );
|
|
PostEventMS( &EV_Mover_InitGuiTargets, 0 );
|
|
controlsDisabled = false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idElevator::Event_Touch
|
|
===============
|
|
*/
|
|
void idElevator::Event_Touch( idEntity *other, trace_t *trace ) {
|
|
|
|
if ( gameLocal.slow.time < lastTouchTime + 2000 ) {
|
|
return;
|
|
}
|
|
|
|
if ( !other->IsType( idPlayer::Type ) ) {
|
|
return;
|
|
}
|
|
|
|
lastTouchTime = gameLocal.slow.time;
|
|
|
|
if ( thinkFlags & TH_PHYSICS ) {
|
|
return;
|
|
}
|
|
|
|
int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
|
|
if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
|
|
PostEventSec( &EV_GotoFloor, 0.25f, triggerFloor );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Think
|
|
================
|
|
*/
|
|
void idElevator::Think( void ) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
idDoor *doorent = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( state == INIT ) {
|
|
state = IDLE;
|
|
if ( doorent ) {
|
|
doorent->BindTeam( this );
|
|
doorent->spawnArgs.Set( "snd_open", "" );
|
|
doorent->spawnArgs.Set( "snd_close", "" );
|
|
doorent->spawnArgs.Set( "snd_opened", "" );
|
|
}
|
|
for ( int i = 0; i < floorInfo.Num(); i++ ) {
|
|
idDoor *door = GetDoor( floorInfo[i].door );
|
|
if ( door ) {
|
|
door->SetCompanion( doorent );
|
|
}
|
|
}
|
|
|
|
Event_GotoFloor( pendingFloor );
|
|
DisableAllDoors();
|
|
SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
|
|
} else if ( state == WAITING_ON_DOORS ) {
|
|
if ( doorent ) {
|
|
state = doorent->IsOpen() ? WAITING_ON_DOORS : IDLE;
|
|
} else {
|
|
state = IDLE;
|
|
}
|
|
if ( state == IDLE ) {
|
|
lastFloor = currentFloor;
|
|
currentFloor = pendingFloor;
|
|
floorInfo_s *fi = GetFloorInfo( currentFloor );
|
|
if ( fi ) {
|
|
MoveToPos( fi->pos );
|
|
}
|
|
}
|
|
}
|
|
RunPhysics();
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Event_Activate
|
|
================
|
|
*/
|
|
void idElevator::Event_Activate( idEntity *activator ) {
|
|
int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
|
|
if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
|
|
Event_GotoFloor( triggerFloor );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Event_TeamBlocked
|
|
================
|
|
*/
|
|
void idElevator::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
|
|
if ( blockedEntity == this ) {
|
|
Event_GotoFloor( lastFloor );
|
|
} else if ( blockedEntity && blockedEntity->IsType( idDoor::Type ) ) {
|
|
// open the inner doors if one is blocked
|
|
idDoor *blocked = static_cast<idDoor *>( blockedEntity );
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door && blocked->GetMoveMaster() == door->GetMoveMaster() ) {
|
|
door->SetBlocked(true);
|
|
OpenInnerDoor();
|
|
OpenFloorDoor( currentFloor );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idElevator::HandleSingleGuiCommand
|
|
===============
|
|
*/
|
|
bool idElevator::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
|
|
idToken token;
|
|
|
|
if ( controlsDisabled ) {
|
|
return false;
|
|
}
|
|
|
|
if ( !src->ReadToken( &token ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( token == ";" ) {
|
|
return false;
|
|
}
|
|
|
|
if ( token.Icmp( "changefloor" ) == 0 ) {
|
|
if ( src->ReadToken( &token ) ) {
|
|
int newFloor = atoi( token );
|
|
if ( newFloor == currentFloor ) {
|
|
// open currentFloor and interior doors
|
|
OpenInnerDoor();
|
|
OpenFloorDoor( currentFloor );
|
|
} else {
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door && door->IsOpen() ) {
|
|
PostEventSec( &EV_GotoFloor, 0.5f, newFloor );
|
|
} else {
|
|
ProcessEvent( &EV_GotoFloor, newFloor );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
src->UnreadToken( &token );
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::OpenFloorDoor
|
|
================
|
|
*/
|
|
void idElevator::OpenFloorDoor( int floor ) {
|
|
floorInfo_s *fi = GetFloorInfo( floor );
|
|
if ( fi ) {
|
|
idDoor *door = GetDoor( fi->door );
|
|
if ( door ) {
|
|
door->Open();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::OpenInnerDoor
|
|
================
|
|
*/
|
|
void idElevator::OpenInnerDoor( void ) {
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door ) {
|
|
door->Open();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::GetFloorInfo
|
|
================
|
|
*/
|
|
floorInfo_s *idElevator::GetFloorInfo( int floor ) {
|
|
for ( int i = 0; i < floorInfo.Num(); i++ ) {
|
|
if ( floorInfo[i].floor == floor ) {
|
|
return &floorInfo[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Event_GotoFloor
|
|
================
|
|
*/
|
|
void idElevator::Event_GotoFloor( int floor ) {
|
|
floorInfo_s *fi = GetFloorInfo( floor );
|
|
if ( fi ) {
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door ) {
|
|
if ( door->IsBlocked() || door->IsOpen() ) {
|
|
PostEventSec( &EV_GotoFloor, 0.5f, floor );
|
|
return;
|
|
}
|
|
}
|
|
DisableAllDoors();
|
|
CloseAllDoors();
|
|
state = WAITING_ON_DOORS;
|
|
pendingFloor = floor;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::BeginMove
|
|
================
|
|
*/
|
|
void idElevator::BeginMove( idThread *thread ) {
|
|
controlsDisabled = true;
|
|
CloseAllDoors();
|
|
DisableAllDoors();
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
|
|
if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
|
|
ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", "" );
|
|
ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
|
|
}
|
|
}
|
|
ent->UpdateVisuals();
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "statusGui", kv );
|
|
}
|
|
SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[3] : guiBinaryMoverStates[2] );
|
|
idMover::BeginMove( thread );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::GetDoor
|
|
================
|
|
*/
|
|
idDoor *idElevator::GetDoor( const char *name ) {
|
|
idEntity *ent;
|
|
idEntity *master;
|
|
idDoor *doorEnt;
|
|
|
|
doorEnt = NULL;
|
|
if ( name && *name ) {
|
|
ent = gameLocal.FindEntity( name );
|
|
if ( ent && ent->IsType( idDoor::Type ) ) {
|
|
doorEnt = static_cast<idDoor*>( ent );
|
|
master = doorEnt->GetMoveMaster();
|
|
if ( master != doorEnt ) {
|
|
if ( master->IsType( idDoor::Type ) ) {
|
|
doorEnt = static_cast<idDoor*>( master );
|
|
} else {
|
|
doorEnt = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return doorEnt;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::Event_PostFloorArrival
|
|
================
|
|
*/
|
|
void idElevator::Event_PostFloorArrival() {
|
|
OpenFloorDoor( currentFloor );
|
|
OpenInnerDoor();
|
|
SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
|
|
controlsDisabled = false;
|
|
if ( returnTime > 0.0f && returnFloor != currentFloor ) {
|
|
PostEventSec( &EV_GotoFloor, returnTime, returnFloor );
|
|
}
|
|
}
|
|
|
|
#ifdef _D3XP
|
|
void idElevator::Event_SetGuiStates() {
|
|
SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
================
|
|
idElevator::DoneMoving
|
|
================
|
|
*/
|
|
void idElevator::DoneMoving( void ) {
|
|
idMover::DoneMoving();
|
|
EnableProperDoors();
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
|
|
if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
|
|
ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", va( "%i", currentFloor ) );
|
|
ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
|
|
}
|
|
}
|
|
ent->UpdateVisuals();
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "statusGui", kv );
|
|
}
|
|
if ( spawnArgs.GetInt( "pauseOnFloor", "-1" ) == currentFloor ) {
|
|
PostEventSec( &EV_PostArrival, spawnArgs.GetFloat( "pauseTime" ) );
|
|
} else {
|
|
Event_PostFloorArrival();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::CloseAllDoors
|
|
================
|
|
*/
|
|
void idElevator::CloseAllDoors( void ) {
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door ) {
|
|
door->Close();
|
|
}
|
|
for ( int i = 0; i < floorInfo.Num(); i++ ) {
|
|
door = GetDoor( floorInfo[i].door );
|
|
if ( door ) {
|
|
door->Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::DisableAllDoors
|
|
================
|
|
*/
|
|
void idElevator::DisableAllDoors( void ) {
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door ) {
|
|
door->Enable( false );
|
|
}
|
|
for ( int i = 0; i < floorInfo.Num(); i++ ) {
|
|
door = GetDoor( floorInfo[i].door );
|
|
if ( door ) {
|
|
door->Enable( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idElevator::EnableProperDoors
|
|
================
|
|
*/
|
|
void idElevator::EnableProperDoors( void ) {
|
|
idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
|
|
if ( door ) {
|
|
door->Enable( true );
|
|
}
|
|
for ( int i = 0; i < floorInfo.Num(); i++ ) {
|
|
if ( floorInfo[i].floor == currentFloor ) {
|
|
door = GetDoor( floorInfo[i].door );
|
|
if ( door ) {
|
|
door->Enable( true );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idMover_Binary
|
|
|
|
Doors, plats, and buttons are all binary (two position) movers
|
|
Pos1 is "at rest", pos2 is "activated"
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_Mover_ReturnToPos1( "<returntopos1>", NULL );
|
|
const idEventDef EV_Mover_MatchTeam( "<matchteam>", "dd" );
|
|
const idEventDef EV_Mover_Enable( "enable", NULL );
|
|
const idEventDef EV_Mover_Disable( "disable", NULL );
|
|
|
|
CLASS_DECLARATION( idEntity, idMover_Binary )
|
|
EVENT( EV_FindGuiTargets, idMover_Binary::Event_FindGuiTargets )
|
|
EVENT( EV_Thread_SetCallback, idMover_Binary::Event_SetCallback )
|
|
EVENT( EV_Mover_ReturnToPos1, idMover_Binary::Event_ReturnToPos1 )
|
|
EVENT( EV_Activate, idMover_Binary::Event_Use_BinaryMover )
|
|
EVENT( EV_ReachedPos, idMover_Binary::Event_Reached_BinaryMover )
|
|
EVENT( EV_Mover_MatchTeam, idMover_Binary::Event_MatchActivateTeam )
|
|
EVENT( EV_Mover_Enable, idMover_Binary::Event_Enable )
|
|
EVENT( EV_Mover_Disable, idMover_Binary::Event_Disable )
|
|
EVENT( EV_Mover_OpenPortal, idMover_Binary::Event_OpenPortal )
|
|
EVENT( EV_Mover_ClosePortal, idMover_Binary::Event_ClosePortal )
|
|
EVENT( EV_Mover_InitGuiTargets, idMover_Binary::Event_InitGuiTargets )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::idMover_Binary()
|
|
================
|
|
*/
|
|
idMover_Binary::idMover_Binary() {
|
|
pos1.Zero();
|
|
pos2.Zero();
|
|
moverState = MOVER_POS1;
|
|
moveMaster = NULL;
|
|
activateChain = NULL;
|
|
soundPos1 = 0;
|
|
sound1to2 = 0;
|
|
sound2to1 = 0;
|
|
soundPos2 = 0;
|
|
soundLoop = 0;
|
|
wait = 0.0f;
|
|
damage = 0.0f;
|
|
duration = 0;
|
|
accelTime = 0;
|
|
decelTime = 0;
|
|
activatedBy = this;
|
|
stateStartTime = 0;
|
|
team.Clear();
|
|
enabled = false;
|
|
move_thread = 0;
|
|
updateStatus = 0;
|
|
areaPortal = 0;
|
|
blocked = false;
|
|
#ifdef _D3XP
|
|
playerOnly = false;
|
|
#endif
|
|
fl.networkSync = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::~idMover_Binary
|
|
================
|
|
*/
|
|
idMover_Binary::~idMover_Binary() {
|
|
idMover_Binary *mover;
|
|
|
|
// if this is the mover master
|
|
if ( this == moveMaster ) {
|
|
// make the next mover in the chain the move master
|
|
for ( mover = moveMaster; mover; mover = mover->activateChain ) {
|
|
mover->moveMaster = this->activateChain;
|
|
}
|
|
}
|
|
else {
|
|
// remove mover from the activate chain
|
|
for ( mover = moveMaster; mover; mover = mover->activateChain ) {
|
|
if ( mover->activateChain == this ) {
|
|
mover->activateChain = this->activateChain;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Save
|
|
================
|
|
*/
|
|
void idMover_Binary::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
|
|
savefile->WriteVec3( pos1 );
|
|
savefile->WriteVec3( pos2 );
|
|
savefile->WriteInt( (moverState_t)moverState );
|
|
|
|
savefile->WriteObject( moveMaster );
|
|
savefile->WriteObject( activateChain );
|
|
|
|
savefile->WriteInt( soundPos1 );
|
|
savefile->WriteInt( sound1to2 );
|
|
savefile->WriteInt( sound2to1 );
|
|
savefile->WriteInt( soundPos2 );
|
|
savefile->WriteInt( soundLoop );
|
|
|
|
savefile->WriteFloat( wait );
|
|
savefile->WriteFloat( damage );
|
|
|
|
savefile->WriteInt( duration );
|
|
savefile->WriteInt( accelTime );
|
|
savefile->WriteInt( decelTime );
|
|
|
|
activatedBy.Save( savefile );
|
|
|
|
savefile->WriteInt( stateStartTime );
|
|
savefile->WriteString( team );
|
|
savefile->WriteBool( enabled );
|
|
|
|
savefile->WriteInt( move_thread );
|
|
savefile->WriteInt( updateStatus );
|
|
|
|
savefile->WriteInt( buddies.Num() );
|
|
for ( i = 0; i < buddies.Num(); i++ ) {
|
|
savefile->WriteString( buddies[ i ] );
|
|
}
|
|
|
|
savefile->WriteStaticObject( physicsObj );
|
|
|
|
savefile->WriteInt( areaPortal );
|
|
if ( areaPortal ) {
|
|
savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
|
|
}
|
|
savefile->WriteBool( blocked );
|
|
#ifdef _D3XP
|
|
savefile->WriteBool( playerOnly );
|
|
#endif
|
|
|
|
savefile->WriteInt( guiTargets.Num() );
|
|
for( i = 0; i < guiTargets.Num(); i++ ) {
|
|
guiTargets[ i ].Save( savefile );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Restore
|
|
================
|
|
*/
|
|
void idMover_Binary::Restore( idRestoreGame *savefile ) {
|
|
int i, num, portalState;
|
|
idStr temp;
|
|
|
|
savefile->ReadVec3( pos1 );
|
|
savefile->ReadVec3( pos2 );
|
|
savefile->ReadInt( (int &)moverState );
|
|
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( moveMaster ) );
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( activateChain ) );
|
|
|
|
savefile->ReadInt( soundPos1 );
|
|
savefile->ReadInt( sound1to2 );
|
|
savefile->ReadInt( sound2to1 );
|
|
savefile->ReadInt( soundPos2 );
|
|
savefile->ReadInt( soundLoop );
|
|
|
|
savefile->ReadFloat( wait );
|
|
savefile->ReadFloat( damage );
|
|
|
|
savefile->ReadInt( duration );
|
|
savefile->ReadInt( accelTime );
|
|
savefile->ReadInt( decelTime );
|
|
|
|
activatedBy.Restore( savefile );
|
|
|
|
savefile->ReadInt( stateStartTime );
|
|
|
|
savefile->ReadString( team );
|
|
savefile->ReadBool( enabled );
|
|
|
|
savefile->ReadInt( move_thread );
|
|
savefile->ReadInt( updateStatus );
|
|
|
|
savefile->ReadInt( num );
|
|
for ( i = 0; i < num; i++ ) {
|
|
savefile->ReadString( temp );
|
|
buddies.Append( temp );
|
|
}
|
|
|
|
savefile->ReadStaticObject( physicsObj );
|
|
RestorePhysics( &physicsObj );
|
|
|
|
savefile->ReadInt( areaPortal );
|
|
if ( areaPortal ) {
|
|
savefile->ReadInt( portalState );
|
|
gameLocal.SetPortalState( areaPortal, portalState );
|
|
}
|
|
savefile->ReadBool( blocked );
|
|
#ifdef _D3XP
|
|
savefile->ReadBool( playerOnly );
|
|
#endif
|
|
|
|
guiTargets.Clear();
|
|
savefile->ReadInt( num );
|
|
guiTargets.SetNum( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
guiTargets[ i ].Restore( savefile );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Spawn
|
|
|
|
Base class for all movers.
|
|
|
|
"wait" wait before returning (3 default, -1 = never return)
|
|
"speed" movement speed
|
|
================
|
|
*/
|
|
void idMover_Binary::Spawn( void ) {
|
|
idEntity *ent;
|
|
const char *temp;
|
|
|
|
move_thread = 0;
|
|
enabled = true;
|
|
areaPortal = 0;
|
|
|
|
activateChain = NULL;
|
|
|
|
spawnArgs.GetFloat( "wait", "0", wait );
|
|
|
|
spawnArgs.GetInt( "updateStatus", "0", updateStatus );
|
|
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "buddy", NULL );
|
|
while( kv ) {
|
|
buddies.Append( kv->GetValue() );
|
|
kv = spawnArgs.MatchPrefix( "buddy", kv );
|
|
}
|
|
|
|
spawnArgs.GetString( "team", "", &temp );
|
|
team = temp;
|
|
|
|
if ( !team.Length() ) {
|
|
ent = this;
|
|
} else {
|
|
// find the first entity spawned on this team (which could be us)
|
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
|
if ( ent->IsType( idMover_Binary::Type ) && !idStr::Icmp( static_cast<idMover_Binary *>(ent)->team.c_str(), temp ) ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( !ent ) {
|
|
ent = this;
|
|
}
|
|
}
|
|
moveMaster = static_cast<idMover_Binary *>(ent);
|
|
|
|
// create a physics team for the binary mover parts
|
|
if ( ent != this ) {
|
|
JoinTeam( ent );
|
|
}
|
|
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
if ( !spawnArgs.GetBool( "solid", "1" ) ) {
|
|
physicsObj.SetContents( 0 );
|
|
}
|
|
if ( !spawnArgs.GetBool( "nopush" ) ) {
|
|
physicsObj.SetPusher( 0 );
|
|
}
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
|
|
physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
|
|
SetPhysics( &physicsObj );
|
|
|
|
if ( moveMaster != this ) {
|
|
JoinActivateTeam( moveMaster );
|
|
}
|
|
|
|
idBounds soundOrigin;
|
|
idMover_Binary *slave;
|
|
|
|
soundOrigin.Clear();
|
|
for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
|
|
soundOrigin += slave->GetPhysics()->GetAbsBounds();
|
|
}
|
|
moveMaster->refSound.origin = soundOrigin.GetCenter();
|
|
|
|
if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
|
|
if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
|
|
PostEventMS( &EV_FindGuiTargets, 0 );
|
|
} else {
|
|
// not during spawn, so it's ok to get the targets
|
|
FindGuiTargets();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Binary::GetMovedir
|
|
|
|
The editor only specifies a single value for angles (yaw),
|
|
but we have special constants to generate an up or down direction.
|
|
Angles will be cleared, because it is being used to represent a direction
|
|
instead of an orientation.
|
|
===============
|
|
*/
|
|
void idMover_Binary::GetMovedir( float angle, idVec3 &movedir ) {
|
|
if ( angle == -1 ) {
|
|
movedir.Set( 0, 0, 1 );
|
|
} else if ( angle == -2 ) {
|
|
movedir.Set( 0, 0, -1 );
|
|
} else {
|
|
movedir = idAngles( 0, angle, 0 ).ToForward();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_SetCallback
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_SetCallback( void ) {
|
|
if ( ( moverState == MOVER_1TO2 ) || ( moverState == MOVER_2TO1 ) ) {
|
|
move_thread = idThread::CurrentThreadNum();
|
|
idThread::ReturnInt( true );
|
|
} else {
|
|
idThread::ReturnInt( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Binary::UpdateMoverSound
|
|
===============
|
|
*/
|
|
void idMover_Binary::UpdateMoverSound( moverState_t state ) {
|
|
if ( moveMaster == this ) {
|
|
switch( state ) {
|
|
case MOVER_POS1:
|
|
break;
|
|
case MOVER_POS2:
|
|
break;
|
|
case MOVER_1TO2:
|
|
StartSound( "snd_open", SND_CHANNEL_ANY, 0, false, NULL );
|
|
break;
|
|
case MOVER_2TO1:
|
|
StartSound( "snd_close", SND_CHANNEL_ANY, 0, false, NULL );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Binary::SetMoverState
|
|
===============
|
|
*/
|
|
void idMover_Binary::SetMoverState( moverState_t newstate, int time ) {
|
|
idVec3 delta;
|
|
|
|
moverState = newstate;
|
|
move_thread = 0;
|
|
|
|
UpdateMoverSound( newstate );
|
|
|
|
stateStartTime = time;
|
|
switch( moverState ) {
|
|
case MOVER_POS1: {
|
|
Signal( SIG_MOVER_POS1 );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos1, vec3_origin, vec3_origin );
|
|
break;
|
|
}
|
|
case MOVER_POS2: {
|
|
Signal( SIG_MOVER_POS2 );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos2, vec3_origin, vec3_origin );
|
|
break;
|
|
}
|
|
case MOVER_1TO2: {
|
|
Signal( SIG_MOVER_1TO2 );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos1, ( pos2 - pos1 ) * 1000.0f / duration, vec3_origin );
|
|
if ( accelTime != 0 || decelTime != 0 ) {
|
|
physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos1, pos2 );
|
|
} else {
|
|
physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
|
|
}
|
|
break;
|
|
}
|
|
case MOVER_2TO1: {
|
|
Signal( SIG_MOVER_2TO1 );
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos2, ( pos1 - pos2 ) * 1000.0f / duration, vec3_origin );
|
|
if ( accelTime != 0 || decelTime != 0 ) {
|
|
physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos2, pos1 );
|
|
} else {
|
|
physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::MatchActivateTeam
|
|
|
|
All entities in a mover team will move from pos1 to pos2
|
|
in the same amount of time
|
|
================
|
|
*/
|
|
void idMover_Binary::MatchActivateTeam( moverState_t newstate, int time ) {
|
|
idMover_Binary *slave;
|
|
|
|
for ( slave = this; slave != NULL; slave = slave->activateChain ) {
|
|
slave->SetMoverState( newstate, time );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Enable
|
|
================
|
|
*/
|
|
void idMover_Binary::Enable( bool b ) {
|
|
enabled = b;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_MatchActivateTeam
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_MatchActivateTeam( moverState_t newstate, int time ) {
|
|
MatchActivateTeam( newstate, time );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::BindTeam
|
|
|
|
All entities in a mover team will be bound
|
|
================
|
|
*/
|
|
void idMover_Binary::BindTeam( idEntity *bindTo ) {
|
|
idMover_Binary *slave;
|
|
|
|
for ( slave = this; slave != NULL; slave = slave->activateChain ) {
|
|
slave->Bind( bindTo, true );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::JoinActivateTeam
|
|
|
|
Set all entities in a mover team to be enabled
|
|
================
|
|
*/
|
|
void idMover_Binary::JoinActivateTeam( idMover_Binary *master ) {
|
|
this->activateChain = master->activateChain;
|
|
master->activateChain = this;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_Enable
|
|
|
|
Set all entities in a mover team to be enabled
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_Enable( void ) {
|
|
idMover_Binary *slave;
|
|
|
|
for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
|
|
slave->Enable( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_Disable
|
|
|
|
Set all entities in a mover team to be disabled
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_Disable( void ) {
|
|
idMover_Binary *slave;
|
|
|
|
for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
|
|
slave->Enable( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_OpenPortal
|
|
|
|
Sets the portal associtated with this mover to be open
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_OpenPortal( void ) {
|
|
idMover_Binary *slave;
|
|
|
|
for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
|
|
if ( slave->areaPortal ) {
|
|
slave->SetPortalState( true );
|
|
}
|
|
#ifdef _D3XP
|
|
if ( slave->playerOnly ) {
|
|
gameLocal.SetAASAreaState( slave->GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, false );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_ClosePortal
|
|
|
|
Sets the portal associtated with this mover to be closed
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_ClosePortal( void ) {
|
|
idMover_Binary *slave;
|
|
|
|
for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
|
|
if ( !slave->IsHidden() ) {
|
|
if ( slave->areaPortal ) {
|
|
slave->SetPortalState( false );
|
|
}
|
|
#ifdef _D3XP
|
|
if ( slave->playerOnly ) {
|
|
gameLocal.SetAASAreaState( slave->GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, true );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_ReturnToPos1
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_ReturnToPos1( void ) {
|
|
MatchActivateTeam( MOVER_2TO1, gameLocal.slow.time );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_Reached_BinaryMover
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_Reached_BinaryMover( void ) {
|
|
|
|
if ( moverState == MOVER_1TO2 ) {
|
|
// reached pos2
|
|
idThread::ObjectMoveDone( move_thread, this );
|
|
move_thread = 0;
|
|
|
|
if ( moveMaster == this ) {
|
|
StartSound( "snd_opened", SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
|
|
SetMoverState( MOVER_POS2, gameLocal.slow.time );
|
|
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_POS2] );
|
|
|
|
UpdateBuddies( 1 );
|
|
|
|
if ( enabled && wait >= 0 && !spawnArgs.GetBool( "toggle" ) ) {
|
|
// return to pos1 after a delay
|
|
PostEventSec( &EV_Mover_ReturnToPos1, wait );
|
|
}
|
|
|
|
// fire targets
|
|
ActivateTargets( moveMaster->GetActivator() );
|
|
|
|
SetBlocked(false);
|
|
} else if ( moverState == MOVER_2TO1 ) {
|
|
// reached pos1
|
|
idThread::ObjectMoveDone( move_thread, this );
|
|
move_thread = 0;
|
|
|
|
SetMoverState( MOVER_POS1, gameLocal.slow.time );
|
|
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
|
|
|
|
UpdateBuddies( 0 );
|
|
|
|
// close areaportals
|
|
if ( moveMaster == this ) {
|
|
ProcessEvent( &EV_Mover_ClosePortal );
|
|
}
|
|
|
|
if ( enabled && wait >= 0 && spawnArgs.GetBool( "continuous" ) ) {
|
|
PostEventSec( &EV_Activate, wait, this );
|
|
}
|
|
SetBlocked(false);
|
|
} else {
|
|
gameLocal.Error( "Event_Reached_BinaryMover: bad moverState" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::GotoPosition1
|
|
================
|
|
*/
|
|
void idMover_Binary::GotoPosition1( void ) {
|
|
idMover_Binary *slave;
|
|
int partial;
|
|
|
|
// only the master should control this
|
|
if ( moveMaster != this ) {
|
|
moveMaster->GotoPosition1();
|
|
return;
|
|
}
|
|
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
|
|
|
|
if ( ( moverState == MOVER_POS1 ) || ( moverState == MOVER_2TO1 ) ) {
|
|
// already there, or on the way
|
|
return;
|
|
}
|
|
|
|
if ( moverState == MOVER_POS2 ) {
|
|
for ( slave = this; slave != NULL; slave = slave->activateChain ) {
|
|
slave->CancelEvents( &EV_Mover_ReturnToPos1 );
|
|
}
|
|
if ( !spawnArgs.GetBool( "toggle" ) ) {
|
|
ProcessEvent( &EV_Mover_ReturnToPos1 );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// only partway up before reversing
|
|
if ( moverState == MOVER_1TO2 ) {
|
|
// use the physics times because this might be executed during the physics simulation
|
|
partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
|
|
assert( partial >= 0 );
|
|
if ( partial < 0 ) {
|
|
partial = 0;
|
|
}
|
|
MatchActivateTeam( MOVER_2TO1, physicsObj.GetTime() - partial );
|
|
// if already at at position 1 (partial == duration) execute the reached event
|
|
if ( partial >= duration ) {
|
|
Event_Reached_BinaryMover();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::GotoPosition2
|
|
================
|
|
*/
|
|
void idMover_Binary::GotoPosition2( void ) {
|
|
int partial;
|
|
|
|
// only the master should control this
|
|
if ( moveMaster != this ) {
|
|
moveMaster->GotoPosition2();
|
|
return;
|
|
}
|
|
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
|
|
|
|
if ( ( moverState == MOVER_POS2 ) || ( moverState == MOVER_1TO2 ) ) {
|
|
// already there, or on the way
|
|
return;
|
|
}
|
|
|
|
if ( moverState == MOVER_POS1 ) {
|
|
MatchActivateTeam( MOVER_1TO2, gameLocal.slow.time );
|
|
|
|
// open areaportal
|
|
ProcessEvent( &EV_Mover_OpenPortal );
|
|
return;
|
|
}
|
|
|
|
|
|
// only partway up before reversing
|
|
if ( moverState == MOVER_2TO1 ) {
|
|
// use the physics times because this might be executed during the physics simulation
|
|
partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
|
|
assert( partial >= 0 );
|
|
if ( partial < 0 ) {
|
|
partial = 0;
|
|
}
|
|
MatchActivateTeam( MOVER_1TO2, physicsObj.GetTime() - partial );
|
|
// if already at at position 2 (partial == duration) execute the reached event
|
|
if ( partial >= duration ) {
|
|
Event_Reached_BinaryMover();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::UpdateBuddies
|
|
================
|
|
*/
|
|
void idMover_Binary::UpdateBuddies( int val ) {
|
|
int i, c;
|
|
|
|
if ( updateStatus == 2 ) {
|
|
c = buddies.Num();
|
|
for ( i = 0; i < c; i++ ) {
|
|
idEntity *buddy = gameLocal.FindEntity( buddies[i] );
|
|
if ( buddy ) {
|
|
buddy->SetShaderParm( SHADERPARM_MODE, val );
|
|
buddy->UpdateVisuals();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::SetGuiStates
|
|
================
|
|
*/
|
|
void idMover_Binary::SetGuiStates( const char *state ) {
|
|
if ( guiTargets.Num() ) {
|
|
SetGuiState( "movestate", state );
|
|
}
|
|
|
|
idMover_Binary *mb = activateChain;
|
|
while( mb ) {
|
|
if ( mb->guiTargets.Num() ) {
|
|
mb->SetGuiState( "movestate", state );
|
|
}
|
|
mb = mb->activateChain;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Use_BinaryMover
|
|
================
|
|
*/
|
|
void idMover_Binary::Use_BinaryMover( idEntity *activator ) {
|
|
// only the master should be used
|
|
if ( moveMaster != this ) {
|
|
moveMaster->Use_BinaryMover( activator );
|
|
return;
|
|
}
|
|
|
|
if ( !enabled ) {
|
|
return;
|
|
}
|
|
|
|
activatedBy = activator;
|
|
|
|
if ( moverState == MOVER_POS1 ) {
|
|
// FIXME: start moving USERCMD_MSEC later, because if this was player
|
|
// triggered, gameLocal.time hasn't been advanced yet
|
|
MatchActivateTeam( MOVER_1TO2, gameLocal.slow.time + USERCMD_MSEC );
|
|
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
|
|
// open areaportal
|
|
ProcessEvent( &EV_Mover_OpenPortal );
|
|
return;
|
|
}
|
|
|
|
// if all the way up, just delay before coming down
|
|
if ( moverState == MOVER_POS2 ) {
|
|
idMover_Binary *slave;
|
|
|
|
if ( wait == -1 ) {
|
|
return;
|
|
}
|
|
|
|
SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
|
|
|
|
for ( slave = this; slave != NULL; slave = slave->activateChain ) {
|
|
slave->CancelEvents( &EV_Mover_ReturnToPos1 );
|
|
slave->PostEventSec( &EV_Mover_ReturnToPos1, spawnArgs.GetBool( "toggle" ) ? 0 : wait );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// only partway down before reversing
|
|
if ( moverState == MOVER_2TO1 ) {
|
|
GotoPosition2();
|
|
return;
|
|
}
|
|
|
|
// only partway up before reversing
|
|
if ( moverState == MOVER_1TO2 ) {
|
|
GotoPosition1();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_Use_BinaryMover
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_Use_BinaryMover( idEntity *activator ) {
|
|
Use_BinaryMover( activator );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::PreBind
|
|
================
|
|
*/
|
|
void idMover_Binary::PreBind( void ) {
|
|
pos1 = GetWorldCoordinates( pos1 );
|
|
pos2 = GetWorldCoordinates( pos2 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::PostBind
|
|
================
|
|
*/
|
|
void idMover_Binary::PostBind( void ) {
|
|
pos1 = GetLocalCoordinates( pos1 );
|
|
pos2 = GetLocalCoordinates( pos2 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::FindGuiTargets
|
|
================
|
|
*/
|
|
void idMover_Binary::FindGuiTargets( void ) {
|
|
gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idMover_Binary::SetGuiState
|
|
|
|
key/val will be set to any renderEntity->gui's on the list
|
|
==============================
|
|
*/
|
|
void idMover_Binary::SetGuiState( const char *key, const char *val ) const {
|
|
int i;
|
|
|
|
for( i = 0; i < guiTargets.Num(); i++ ) {
|
|
idEntity *ent = guiTargets[ i ].GetEntity();
|
|
if ( ent ) {
|
|
for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
|
|
if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
|
|
ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
|
|
ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.slow.time, true );
|
|
}
|
|
}
|
|
ent->UpdateVisuals();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_InitGuiTargets
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_FindGuiTargets( void ) {
|
|
FindGuiTargets();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::Event_InitGuiTargets
|
|
================
|
|
*/
|
|
void idMover_Binary::Event_InitGuiTargets( void ) {
|
|
if ( guiTargets.Num() ) {
|
|
SetGuiState( "movestate", guiBinaryMoverStates[MOVER_POS1] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::InitSpeed
|
|
|
|
pos1, pos2, and speed are passed in so the movement delta can be calculated
|
|
================
|
|
*/
|
|
void idMover_Binary::InitSpeed( idVec3 &mpos1, idVec3 &mpos2, float mspeed, float maccelTime, float mdecelTime ) {
|
|
idVec3 move;
|
|
float distance;
|
|
float speed;
|
|
|
|
pos1 = mpos1;
|
|
pos2 = mpos2;
|
|
|
|
accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
|
|
decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
|
|
|
|
speed = mspeed ? mspeed : 100;
|
|
|
|
// calculate time to reach second position from speed
|
|
move = pos2 - pos1;
|
|
distance = move.Length();
|
|
duration = idPhysics::SnapTimeToPhysicsFrame( distance * 1000 / speed );
|
|
if ( duration <= 0 ) {
|
|
duration = 1;
|
|
}
|
|
|
|
moverState = MOVER_POS1;
|
|
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
|
|
physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
|
|
SetOrigin( pos1 );
|
|
|
|
PostEventMS( &EV_Mover_InitGuiTargets, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::InitTime
|
|
|
|
pos1, pos2, and time are passed in so the movement delta can be calculated
|
|
================
|
|
*/
|
|
void idMover_Binary::InitTime( idVec3 &mpos1, idVec3 &mpos2, float mtime, float maccelTime, float mdecelTime ) {
|
|
|
|
pos1 = mpos1;
|
|
pos2 = mpos2;
|
|
|
|
accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
|
|
decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
|
|
|
|
duration = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mtime ) );
|
|
if ( duration <= 0 ) {
|
|
duration = 1;
|
|
}
|
|
|
|
moverState = MOVER_POS1;
|
|
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
|
|
physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
|
|
SetOrigin( pos1 );
|
|
|
|
PostEventMS( &EV_Mover_InitGuiTargets, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::SetBlocked
|
|
================
|
|
*/
|
|
void idMover_Binary::SetBlocked( bool b ) {
|
|
for ( idMover_Binary *slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
|
|
slave->blocked = b;
|
|
if ( b ) {
|
|
const idKeyValue *kv = slave->spawnArgs.MatchPrefix( "triggerBlocked" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
|
|
}
|
|
kv = slave->spawnArgs.MatchPrefix( "triggerBlocked", kv );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::IsBlocked
|
|
================
|
|
*/
|
|
bool idMover_Binary::IsBlocked( void ) {
|
|
return blocked;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::GetActivator
|
|
================
|
|
*/
|
|
idEntity *idMover_Binary::GetActivator( void ) const {
|
|
return activatedBy.GetEntity();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idMover_Binary::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
physicsObj.WriteToSnapshot( msg );
|
|
msg.WriteBits( moverState, 3 );
|
|
WriteBindToSnapshot( msg );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idMover_Binary::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
moverState_t oldMoverState = moverState;
|
|
|
|
physicsObj.ReadFromSnapshot( msg );
|
|
moverState = (moverState_t) msg.ReadBits( 3 );
|
|
ReadBindFromSnapshot( msg );
|
|
|
|
if ( msg.HasChanged() ) {
|
|
if ( moverState != oldMoverState ) {
|
|
UpdateMoverSound( moverState );
|
|
}
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Binary::SetPortalState
|
|
================
|
|
*/
|
|
void idMover_Binary::SetPortalState( bool open ) {
|
|
assert( areaPortal );
|
|
gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idDoor
|
|
|
|
A use can be triggered either by a touch function, by being shot, or by being
|
|
targeted by another entity.
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_Door_StartOpen( "<startOpen>", NULL );
|
|
const idEventDef EV_Door_SpawnDoorTrigger( "<spawnDoorTrigger>", NULL );
|
|
const idEventDef EV_Door_SpawnSoundTrigger( "<spawnSoundTrigger>", NULL );
|
|
const idEventDef EV_Door_Open( "open", NULL );
|
|
const idEventDef EV_Door_Close( "close", NULL );
|
|
const idEventDef EV_Door_Lock( "lock", "d" );
|
|
const idEventDef EV_Door_IsOpen( "isOpen", NULL, 'f' );
|
|
const idEventDef EV_Door_IsLocked( "isLocked", NULL, 'f' );
|
|
|
|
CLASS_DECLARATION( idMover_Binary, idDoor )
|
|
EVENT( EV_TeamBlocked, idDoor::Event_TeamBlocked )
|
|
EVENT( EV_PartBlocked, idDoor::Event_PartBlocked )
|
|
EVENT( EV_Touch, idDoor::Event_Touch )
|
|
EVENT( EV_Activate, idDoor::Event_Activate )
|
|
EVENT( EV_Door_StartOpen, idDoor::Event_StartOpen )
|
|
EVENT( EV_Door_SpawnDoorTrigger, idDoor::Event_SpawnDoorTrigger )
|
|
EVENT( EV_Door_SpawnSoundTrigger, idDoor::Event_SpawnSoundTrigger )
|
|
EVENT( EV_Door_Open, idDoor::Event_Open )
|
|
EVENT( EV_Door_Close, idDoor::Event_Close )
|
|
EVENT( EV_Door_Lock, idDoor::Event_Lock )
|
|
EVENT( EV_Door_IsOpen, idDoor::Event_IsOpen )
|
|
EVENT( EV_Door_IsLocked, idDoor::Event_Locked )
|
|
EVENT( EV_ReachedPos, idDoor::Event_Reached_BinaryMover )
|
|
EVENT( EV_SpectatorTouch, idDoor::Event_SpectatorTouch )
|
|
EVENT( EV_Mover_OpenPortal, idDoor::Event_OpenPortal )
|
|
EVENT( EV_Mover_ClosePortal, idDoor::Event_ClosePortal )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idDoor::idDoor
|
|
================
|
|
*/
|
|
idDoor::idDoor( void ) {
|
|
triggersize = 1.0f;
|
|
crusher = false;
|
|
noTouch = false;
|
|
aas_area_closed = false;
|
|
buddyStr.Clear();
|
|
trigger = NULL;
|
|
sndTrigger = NULL;
|
|
nextSndTriggerTime = 0;
|
|
localTriggerOrigin.Zero();
|
|
localTriggerAxis.Identity();
|
|
requires.Clear();
|
|
removeItem = 0;
|
|
syncLock.Clear();
|
|
companionDoor = NULL;
|
|
normalAxisIndex = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::~idDoor
|
|
================
|
|
*/
|
|
idDoor::~idDoor( void ) {
|
|
if ( trigger ) {
|
|
delete trigger;
|
|
}
|
|
if ( sndTrigger ) {
|
|
delete sndTrigger;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Save
|
|
================
|
|
*/
|
|
void idDoor::Save( idSaveGame *savefile ) const {
|
|
|
|
savefile->WriteFloat( triggersize );
|
|
savefile->WriteBool( crusher );
|
|
savefile->WriteBool( noTouch );
|
|
savefile->WriteBool( aas_area_closed );
|
|
savefile->WriteString( buddyStr );
|
|
savefile->WriteInt( nextSndTriggerTime );
|
|
|
|
savefile->WriteVec3( localTriggerOrigin );
|
|
savefile->WriteMat3( localTriggerAxis );
|
|
|
|
savefile->WriteString( requires );
|
|
savefile->WriteInt( removeItem );
|
|
savefile->WriteString( syncLock );
|
|
savefile->WriteInt( normalAxisIndex );
|
|
|
|
savefile->WriteClipModel( trigger );
|
|
savefile->WriteClipModel( sndTrigger );
|
|
|
|
savefile->WriteObject( companionDoor );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Restore
|
|
================
|
|
*/
|
|
void idDoor::Restore( idRestoreGame *savefile ) {
|
|
|
|
savefile->ReadFloat( triggersize );
|
|
savefile->ReadBool( crusher );
|
|
savefile->ReadBool( noTouch );
|
|
savefile->ReadBool( aas_area_closed );
|
|
SetAASAreaState( aas_area_closed );
|
|
savefile->ReadString( buddyStr );
|
|
savefile->ReadInt( nextSndTriggerTime );
|
|
|
|
savefile->ReadVec3( localTriggerOrigin );
|
|
savefile->ReadMat3( localTriggerAxis );
|
|
|
|
savefile->ReadString( requires );
|
|
savefile->ReadInt( removeItem );
|
|
savefile->ReadString( syncLock );
|
|
savefile->ReadInt( normalAxisIndex );
|
|
|
|
savefile->ReadClipModel( trigger );
|
|
savefile->ReadClipModel( sndTrigger );
|
|
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( companionDoor ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Spawn
|
|
================
|
|
*/
|
|
void idDoor::Spawn( void ) {
|
|
idVec3 abs_movedir;
|
|
float distance;
|
|
idVec3 size;
|
|
idVec3 movedir;
|
|
float dir;
|
|
float lip;
|
|
bool start_open;
|
|
float time;
|
|
float speed;
|
|
|
|
// 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 );
|
|
spawnArgs.GetFloat( "angle", "0", dir );
|
|
}
|
|
GetMovedir( dir, movedir );
|
|
|
|
// default speed of 400
|
|
spawnArgs.GetFloat( "speed", "400", speed );
|
|
|
|
// default wait of 2 seconds
|
|
spawnArgs.GetFloat( "wait", "3", wait );
|
|
|
|
// default lip of 8 units
|
|
spawnArgs.GetFloat( "lip", "8", lip );
|
|
|
|
// by default no damage
|
|
spawnArgs.GetFloat( "damage", "0", damage );
|
|
|
|
// trigger size
|
|
spawnArgs.GetFloat( "triggersize", "120", triggersize );
|
|
|
|
spawnArgs.GetBool( "crusher", "0", crusher );
|
|
spawnArgs.GetBool( "start_open", "0", start_open );
|
|
spawnArgs.GetBool( "no_touch", "0", noTouch );
|
|
#ifdef _D3XP
|
|
spawnArgs.GetBool( "player_only", "0", playerOnly );
|
|
#endif
|
|
|
|
// expects syncLock to be a door that must be closed before this door will open
|
|
spawnArgs.GetString( "syncLock", "", syncLock );
|
|
|
|
spawnArgs.GetString( "buddy", "", buddyStr );
|
|
|
|
spawnArgs.GetString( "requires", "", requires );
|
|
spawnArgs.GetInt( "removeItem", "0", removeItem );
|
|
|
|
// ever separate piece of a door is considered solid when other team mates push entities
|
|
fl.solidForTeam = true;
|
|
|
|
// first position at start
|
|
pos1 = GetPhysics()->GetOrigin();
|
|
|
|
// calculate second position
|
|
abs_movedir[0] = idMath::Fabs( movedir[ 0 ] );
|
|
abs_movedir[1] = idMath::Fabs( movedir[ 1 ] );
|
|
abs_movedir[2] = idMath::Fabs( movedir[ 2 ] );
|
|
size = GetPhysics()->GetAbsBounds()[1] - GetPhysics()->GetAbsBounds()[0];
|
|
distance = ( abs_movedir * size ) - lip;
|
|
pos2 = pos1 + distance * movedir;
|
|
|
|
// if "start_open", reverse position 1 and 2
|
|
if ( start_open ) {
|
|
// post it after EV_SpawnBind
|
|
PostEventMS( &EV_Door_StartOpen, 1 );
|
|
}
|
|
|
|
if ( spawnArgs.GetFloat( "time", "1", time ) ) {
|
|
InitTime( pos1, pos2, time, 0, 0 );
|
|
} else {
|
|
InitSpeed( pos1, pos2, speed, 0, 0 );
|
|
}
|
|
|
|
if ( moveMaster == this ) {
|
|
if ( health ) {
|
|
fl.takedamage = true;
|
|
}
|
|
if ( noTouch || health ) {
|
|
// non touch/shoot doors
|
|
PostEventMS( &EV_Mover_MatchTeam, 0, moverState, gameLocal.slow.time );
|
|
|
|
const char *sndtemp = spawnArgs.GetString( "snd_locked" );
|
|
if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
|
|
PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
|
|
}
|
|
} else {
|
|
// spawn trigger
|
|
PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
|
|
}
|
|
}
|
|
|
|
// see if we are on an areaportal
|
|
areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
|
|
if ( !start_open ) {
|
|
// start closed
|
|
ProcessEvent( &EV_Mover_ClosePortal );
|
|
|
|
#ifdef _D3XP
|
|
if ( playerOnly ) {
|
|
gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, true );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int locked = spawnArgs.GetInt( "locked" );
|
|
if ( locked ) {
|
|
// make sure all members of the team get locked
|
|
PostEventMS( &EV_Door_Lock, 0, locked );
|
|
}
|
|
|
|
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;
|
|
|
|
companionDoor = NULL;
|
|
|
|
enabled = true;
|
|
blocked = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Think
|
|
================
|
|
*/
|
|
void idDoor::Think( void ) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
idMover_Binary::Think();
|
|
|
|
if ( thinkFlags & TH_PHYSICS ) {
|
|
// update trigger position
|
|
if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
|
|
if ( trigger ) {
|
|
trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
|
|
}
|
|
if ( sndTrigger ) {
|
|
sndTrigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::PreBind
|
|
================
|
|
*/
|
|
void idDoor::PreBind( void ) {
|
|
idMover_Binary::PreBind();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::PostBind
|
|
================
|
|
*/
|
|
void idDoor::PostBind( void ) {
|
|
idMover_Binary::PostBind();
|
|
GetLocalTriggerPosition( trigger ? trigger : sndTrigger );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::SetAASAreaState
|
|
================
|
|
*/
|
|
void idDoor::SetAASAreaState( bool closed ) {
|
|
aas_area_closed = closed;
|
|
gameLocal.SetAASAreaState( physicsObj.GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL|AREACONTENTS_OBSTACLE, closed );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Hide
|
|
================
|
|
*/
|
|
void idDoor::Hide( void ) {
|
|
idMover_Binary *slave;
|
|
idMover_Binary *master;
|
|
idDoor *slaveDoor;
|
|
idDoor *companion;
|
|
|
|
master = GetMoveMaster();
|
|
if ( this != master ) {
|
|
master->Hide();
|
|
} else {
|
|
for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
|
|
if ( slave->IsType( idDoor::Type ) ) {
|
|
slaveDoor = static_cast<idDoor *>( slave );
|
|
companion = slaveDoor->companionDoor;
|
|
if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
|
|
companion->Hide();
|
|
}
|
|
if ( slaveDoor->trigger ) {
|
|
slaveDoor->trigger->Disable();
|
|
}
|
|
if ( slaveDoor->sndTrigger ) {
|
|
slaveDoor->sndTrigger->Disable();
|
|
}
|
|
if ( slaveDoor->areaPortal ) {
|
|
slaveDoor->SetPortalState( true );
|
|
}
|
|
slaveDoor->SetAASAreaState( false );
|
|
}
|
|
slave->GetPhysics()->GetClipModel()->Disable();
|
|
slave->idMover_Binary::Hide();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Show
|
|
================
|
|
*/
|
|
void idDoor::Show( void ) {
|
|
idMover_Binary *slave;
|
|
idMover_Binary *master;
|
|
idDoor *slaveDoor;
|
|
idDoor *companion;
|
|
|
|
master = GetMoveMaster();
|
|
if ( this != master ) {
|
|
master->Show();
|
|
} else {
|
|
for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
|
|
if ( slave->IsType( idDoor::Type ) ) {
|
|
slaveDoor = static_cast<idDoor *>( slave );
|
|
companion = slaveDoor->companionDoor;
|
|
if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
|
|
companion->Show();
|
|
}
|
|
if ( slaveDoor->trigger ) {
|
|
slaveDoor->trigger->Enable();
|
|
}
|
|
if ( slaveDoor->sndTrigger ) {
|
|
slaveDoor->sndTrigger->Enable();
|
|
}
|
|
if ( slaveDoor->areaPortal && ( slaveDoor->moverState == MOVER_POS1 ) ) {
|
|
slaveDoor->SetPortalState( false );
|
|
}
|
|
slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
|
|
}
|
|
slave->GetPhysics()->GetClipModel()->Enable();
|
|
slave->idMover_Binary::Show();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::GetLocalTriggerPosition
|
|
================
|
|
*/
|
|
void idDoor::GetLocalTriggerPosition( const idClipModel *trigger ) {
|
|
idVec3 origin;
|
|
idMat3 axis;
|
|
|
|
if ( !trigger ) {
|
|
return;
|
|
}
|
|
|
|
GetMasterPosition( origin, axis );
|
|
localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
|
|
localTriggerAxis = trigger->GetAxis() * axis.Transpose();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Use
|
|
================
|
|
*/
|
|
void idDoor::Use( idEntity *other, idEntity *activator ) {
|
|
if ( gameLocal.RequirementMet( activator, requires, removeItem ) ) {
|
|
if ( syncLock.Length() ) {
|
|
idEntity *sync = gameLocal.FindEntity( syncLock );
|
|
if ( sync && sync->IsType( idDoor::Type ) ) {
|
|
if ( static_cast<idDoor *>( sync )->IsOpen() ) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ActivateTargets( activator );
|
|
Use_BinaryMover( activator );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Open
|
|
================
|
|
*/
|
|
void idDoor::Open( void ) {
|
|
GotoPosition2();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Close
|
|
================
|
|
*/
|
|
void idDoor::Close( void ) {
|
|
GotoPosition1();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Lock
|
|
================
|
|
*/
|
|
void idDoor::Lock( int f ) {
|
|
idMover_Binary *other;
|
|
|
|
// lock all the doors on the team
|
|
for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
|
|
if ( other->IsType( idDoor::Type ) ) {
|
|
idDoor *door = static_cast<idDoor *>( other );
|
|
if ( other == moveMaster ) {
|
|
if ( door->sndTrigger == NULL ) {
|
|
// in this case the sound trigger never got spawned
|
|
const char *sndtemp = door->spawnArgs.GetString( "snd_locked" );
|
|
if ( sndtemp && *sndtemp ) {
|
|
door->PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
|
|
}
|
|
}
|
|
if ( !f && ( door->spawnArgs.GetInt( "locked" ) != 0 ) ) {
|
|
door->StartSound( "snd_unlocked", SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
}
|
|
door->spawnArgs.SetInt( "locked", f );
|
|
if ( ( f == 0 ) || ( !IsHidden() && ( door->moverState == MOVER_POS1 ) ) ) {
|
|
door->SetAASAreaState( f != 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( f ) {
|
|
Close();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::IsLocked
|
|
================
|
|
*/
|
|
int idDoor::IsLocked( void ) {
|
|
return spawnArgs.GetInt( "locked" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::IsOpen
|
|
================
|
|
*/
|
|
bool idDoor::IsOpen( void ) {
|
|
return ( moverState != MOVER_POS1 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::IsNoTouch
|
|
================
|
|
*/
|
|
bool idDoor::IsNoTouch( void ) {
|
|
return noTouch;
|
|
}
|
|
|
|
#ifdef _D3XP
|
|
/*
|
|
================
|
|
idDoor::AllowPlayerOnly
|
|
================
|
|
*/
|
|
bool idDoor::AllowPlayerOnly( idEntity *ent ) {
|
|
if ( playerOnly && !ent->IsType(idPlayer::Type) ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
======================
|
|
idDoor::CalcTriggerBounds
|
|
|
|
Calcs bounds for a trigger.
|
|
======================
|
|
*/
|
|
void idDoor::CalcTriggerBounds( float size, idBounds &bounds ) {
|
|
idMover_Binary *other;
|
|
int i;
|
|
int best;
|
|
|
|
// find the bounds of everything on the team
|
|
bounds = GetPhysics()->GetAbsBounds();
|
|
|
|
fl.takedamage = true;
|
|
for( other = activateChain; other != NULL; other = other->GetActivateChain() ) {
|
|
if ( other->IsType( idDoor::Type ) ) {
|
|
// find the bounds of everything on the team
|
|
bounds.AddBounds( other->GetPhysics()->GetAbsBounds() );
|
|
|
|
// set all of the slaves as shootable
|
|
other->fl.takedamage = true;
|
|
}
|
|
}
|
|
|
|
// find the thinnest axis, which will be the one we expand
|
|
best = 0;
|
|
for ( 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;
|
|
bounds[0] -= GetPhysics()->GetOrigin();
|
|
bounds[1] -= GetPhysics()->GetOrigin();
|
|
}
|
|
|
|
/*
|
|
======================
|
|
idDoor::Event_StartOpen
|
|
|
|
if "start_open", reverse position 1 and 2
|
|
======================
|
|
*/
|
|
void idDoor::Event_StartOpen( void ) {
|
|
float time;
|
|
float speed;
|
|
|
|
// if "start_open", reverse position 1 and 2
|
|
pos1 = pos2;
|
|
pos2 = GetPhysics()->GetOrigin();
|
|
|
|
spawnArgs.GetFloat( "speed", "400", speed );
|
|
|
|
if ( spawnArgs.GetFloat( "time", "1", time ) ) {
|
|
InitTime( pos1, pos2, time, 0, 0 );
|
|
} else {
|
|
InitSpeed( pos1, pos2, speed, 0, 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
======================
|
|
idDoor::Event_SpawnDoorTrigger
|
|
|
|
All of the parts of a door have been spawned, so create
|
|
a trigger that encloses all of them.
|
|
======================
|
|
*/
|
|
void idDoor::Event_SpawnDoorTrigger( void ) {
|
|
idBounds bounds;
|
|
idMover_Binary *other;
|
|
bool toggle;
|
|
|
|
if ( trigger ) {
|
|
// already have a trigger, so don't spawn a new one.
|
|
return;
|
|
}
|
|
|
|
// check if any of the doors are marked as toggled
|
|
toggle = false;
|
|
for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
|
|
if ( other->IsType( idDoor::Type ) && other->spawnArgs.GetBool( "toggle" ) ) {
|
|
toggle = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( toggle ) {
|
|
// mark them all as toggled
|
|
for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
|
|
if ( other->IsType( idDoor::Type ) ) {
|
|
other->spawnArgs.Set( "toggle", "1" );
|
|
}
|
|
}
|
|
// don't spawn trigger
|
|
return;
|
|
}
|
|
|
|
const char *sndtemp = spawnArgs.GetString( "snd_locked" );
|
|
if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
|
|
PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
|
|
}
|
|
|
|
CalcTriggerBounds( triggersize, bounds );
|
|
|
|
// create a trigger clip model
|
|
trigger = new idClipModel( idTraceModel( bounds ) );
|
|
trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
|
|
trigger->SetContents( CONTENTS_TRIGGER );
|
|
|
|
GetLocalTriggerPosition( trigger );
|
|
|
|
MatchActivateTeam( moverState, gameLocal.slow.time );
|
|
}
|
|
|
|
/*
|
|
======================
|
|
idDoor::Event_SpawnSoundTrigger
|
|
|
|
Spawn a sound trigger to activate locked sound if it exists.
|
|
======================
|
|
*/
|
|
void idDoor::Event_SpawnSoundTrigger( void ) {
|
|
idBounds bounds;
|
|
|
|
if ( sndTrigger ) {
|
|
return;
|
|
}
|
|
|
|
CalcTriggerBounds( triggersize * 0.5f, bounds );
|
|
|
|
// create a trigger clip model
|
|
sndTrigger = new idClipModel( idTraceModel( bounds ) );
|
|
sndTrigger->Link( gameLocal.clip, this, 254, GetPhysics()->GetOrigin(), mat3_identity );
|
|
sndTrigger->SetContents( CONTENTS_TRIGGER );
|
|
|
|
GetLocalTriggerPosition( sndTrigger );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Reached_BinaryMover
|
|
================
|
|
*/
|
|
void idDoor::Event_Reached_BinaryMover( void ) {
|
|
if ( moverState == MOVER_2TO1 ) {
|
|
SetBlocked( false );
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerClosed" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "triggerClosed", kv );
|
|
}
|
|
} else if ( moverState == MOVER_1TO2 ) {
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerOpened" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "triggerOpened", kv );
|
|
}
|
|
}
|
|
idMover_Binary::Event_Reached_BinaryMover();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Blocked_Door
|
|
================
|
|
*/
|
|
void idDoor::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
|
|
SetBlocked( true );
|
|
|
|
if ( crusher ) {
|
|
return; // crushers don't reverse
|
|
}
|
|
|
|
// reverse direction
|
|
Use_BinaryMover( moveMaster->GetActivator() );
|
|
|
|
if ( companionDoor ) {
|
|
companionDoor->ProcessEvent( &EV_TeamBlocked, blockedEntity, blockingEntity );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idDoor::SetCompanion
|
|
===============
|
|
*/
|
|
void idDoor::SetCompanion( idDoor *door ) {
|
|
companionDoor = door;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idDoor::Event_PartBlocked
|
|
===============
|
|
*/
|
|
void idDoor::Event_PartBlocked( idEntity *blockingEntity ) {
|
|
if ( damage > 0.0f ) {
|
|
blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Touch
|
|
================
|
|
*/
|
|
void idDoor::Event_Touch( idEntity *other, trace_t *trace ) {
|
|
idVec3 contact, translate;
|
|
idVec3 planeaxis1, planeaxis2, normal;
|
|
idBounds bounds;
|
|
|
|
if ( !enabled ) {
|
|
return;
|
|
}
|
|
|
|
if ( trigger && trace->c.id == trigger->GetId() ) {
|
|
if ( !IsNoTouch() && !IsLocked() && GetMoverState() != MOVER_1TO2 ) {
|
|
#ifdef _D3XP
|
|
if ( AllowPlayerOnly( other ) ) {
|
|
#endif
|
|
Use( this, other );
|
|
#ifdef _D3XP
|
|
}
|
|
#endif
|
|
}
|
|
} else if ( sndTrigger && trace->c.id == sndTrigger->GetId() ) {
|
|
if ( other && other->IsType( idPlayer::Type ) && IsLocked() && gameLocal.slow.time > nextSndTriggerTime ) {
|
|
StartSound( "snd_locked", SND_CHANNEL_ANY, 0, false, NULL );
|
|
nextSndTriggerTime = gameLocal.slow.time + 10000;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_SpectatorTouch
|
|
================
|
|
*/
|
|
void idDoor::Event_SpectatorTouch( idEntity *other, trace_t *trace ) {
|
|
idVec3 contact, translate, normal;
|
|
idBounds bounds;
|
|
idPlayer *p;
|
|
|
|
assert( other && other->IsType( idPlayer::Type ) && static_cast< idPlayer * >( other )->spectating );
|
|
|
|
p = static_cast< idPlayer * >( other );
|
|
// avoid flicker when stopping right at clip box boundaries
|
|
if ( p->lastSpectateTeleport > gameLocal.slow.time - 1000 ) {
|
|
return;
|
|
}
|
|
if ( trigger && !IsOpen() ) {
|
|
// teleport to the other side, center to the middle of the trigger brush
|
|
bounds = trigger->GetAbsBounds();
|
|
contact = trace->endpos - bounds.GetCenter();
|
|
translate = bounds.GetCenter();
|
|
normal.Zero();
|
|
normal[ normalAxisIndex ] = 1.0f;
|
|
if ( normal * contact > 0 ) {
|
|
translate[ normalAxisIndex ] += ( bounds[ 0 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
|
|
} else {
|
|
translate[ normalAxisIndex ] += ( bounds[ 1 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
|
|
}
|
|
p->SetOrigin( translate );
|
|
p->lastSpectateTeleport = gameLocal.slow.time;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Activate
|
|
================
|
|
*/
|
|
void idDoor::Event_Activate( idEntity *activator ) {
|
|
int old_lock;
|
|
|
|
if ( spawnArgs.GetInt( "locked" ) ) {
|
|
if ( !trigger ) {
|
|
PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
|
|
}
|
|
if ( buddyStr.Length() ) {
|
|
idEntity *buddy = gameLocal.FindEntity( buddyStr );
|
|
if ( buddy ) {
|
|
buddy->SetShaderParm( SHADERPARM_MODE, 1 );
|
|
buddy->UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
old_lock = spawnArgs.GetInt( "locked" );
|
|
Lock( 0 );
|
|
if ( old_lock == 2 ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( syncLock.Length() ) {
|
|
idEntity *sync = gameLocal.FindEntity( syncLock );
|
|
if ( sync && sync->IsType( idDoor::Type ) ) {
|
|
if ( static_cast<idDoor *>( sync )->IsOpen() ) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ActivateTargets( activator );
|
|
|
|
renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
|
|
UpdateVisuals();
|
|
|
|
Use_BinaryMover( activator );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Open
|
|
================
|
|
*/
|
|
void idDoor::Event_Open( void ) {
|
|
Open();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Close
|
|
================
|
|
*/
|
|
void idDoor::Event_Close( void ) {
|
|
Close();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Lock
|
|
================
|
|
*/
|
|
void idDoor::Event_Lock( int f ) {
|
|
Lock( f );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_IsOpen
|
|
================
|
|
*/
|
|
void idDoor::Event_IsOpen( void ) {
|
|
bool state;
|
|
|
|
state = IsOpen();
|
|
idThread::ReturnFloat( state );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_Locked
|
|
================
|
|
*/
|
|
void idDoor::Event_Locked( void ) {
|
|
idThread::ReturnFloat( spawnArgs.GetInt("locked") );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_OpenPortal
|
|
|
|
Sets the portal associtated with this door to be open
|
|
================
|
|
*/
|
|
void idDoor::Event_OpenPortal( void ) {
|
|
idMover_Binary *slave;
|
|
idDoor *slaveDoor;
|
|
|
|
for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
|
|
if ( slave->IsType( idDoor::Type ) ) {
|
|
slaveDoor = static_cast<idDoor *>( slave );
|
|
if ( slaveDoor->areaPortal ) {
|
|
slaveDoor->SetPortalState( true );
|
|
}
|
|
slaveDoor->SetAASAreaState( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDoor::Event_ClosePortal
|
|
|
|
Sets the portal associtated with this door to be closed
|
|
================
|
|
*/
|
|
void idDoor::Event_ClosePortal( void ) {
|
|
idMover_Binary *slave;
|
|
idDoor *slaveDoor;
|
|
|
|
for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
|
|
if ( !slave->IsHidden() ) {
|
|
if ( slave->IsType( idDoor::Type ) ) {
|
|
slaveDoor = static_cast<idDoor *>( slave );
|
|
if ( slaveDoor->areaPortal ) {
|
|
slaveDoor->SetPortalState( false );
|
|
}
|
|
slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idPlat
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idMover_Binary, idPlat )
|
|
EVENT( EV_Touch, idPlat::Event_Touch )
|
|
EVENT( EV_TeamBlocked, idPlat::Event_TeamBlocked )
|
|
EVENT( EV_PartBlocked, idPlat::Event_PartBlocked )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idPlat::idPlat
|
|
===============
|
|
*/
|
|
idPlat::idPlat( void ) {
|
|
trigger = NULL;
|
|
localTriggerOrigin.Zero();
|
|
localTriggerAxis.Identity();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlat::~idPlat
|
|
===============
|
|
*/
|
|
idPlat::~idPlat( void ) {
|
|
if ( trigger ) {
|
|
delete trigger;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlat::Save
|
|
===============
|
|
*/
|
|
void idPlat::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteClipModel( trigger );
|
|
savefile->WriteVec3( localTriggerOrigin );
|
|
savefile->WriteMat3( localTriggerAxis );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlat::Restore
|
|
===============
|
|
*/
|
|
void idPlat::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadClipModel( trigger );
|
|
savefile->ReadVec3( localTriggerOrigin );
|
|
savefile->ReadMat3( localTriggerAxis );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlat::Spawn
|
|
===============
|
|
*/
|
|
void idPlat::Spawn( void ) {
|
|
float lip;
|
|
float height;
|
|
float time;
|
|
float speed;
|
|
float accel;
|
|
float decel;
|
|
bool noTouch;
|
|
|
|
spawnArgs.GetFloat( "speed", "100", speed );
|
|
spawnArgs.GetFloat( "damage", "0", damage );
|
|
spawnArgs.GetFloat( "wait", "1", wait );
|
|
spawnArgs.GetFloat( "lip", "8", lip );
|
|
spawnArgs.GetFloat( "accel_time", "0.25", accel );
|
|
spawnArgs.GetFloat( "decel_time", "0.25", decel );
|
|
|
|
// create second position
|
|
if ( !spawnArgs.GetFloat( "height", "0", height ) ) {
|
|
height = ( GetPhysics()->GetBounds()[1][2] - GetPhysics()->GetBounds()[0][2] ) - lip;
|
|
}
|
|
|
|
spawnArgs.GetBool( "no_touch", "0", noTouch );
|
|
|
|
// pos1 is the rest (bottom) position, pos2 is the top
|
|
pos2 = GetPhysics()->GetOrigin();
|
|
pos1 = pos2;
|
|
pos1[2] -= height;
|
|
|
|
if ( spawnArgs.GetFloat( "time", "1", time ) ) {
|
|
InitTime( pos1, pos2, time, accel, decel );
|
|
} else {
|
|
InitSpeed( pos1, pos2, speed, accel, decel );
|
|
}
|
|
|
|
SetMoverState( MOVER_POS1, gameLocal.slow.time );
|
|
UpdateVisuals();
|
|
|
|
// spawn the trigger if one hasn't been custom made
|
|
if ( !noTouch ) {
|
|
// spawn trigger
|
|
SpawnPlatTrigger( pos1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlat::Think
|
|
================
|
|
*/
|
|
void idPlat::Think( void ) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
idMover_Binary::Think();
|
|
|
|
if ( thinkFlags & TH_PHYSICS ) {
|
|
// update trigger position
|
|
if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
|
|
if ( trigger ) {
|
|
trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlat::PreBind
|
|
================
|
|
*/
|
|
void idPlat::PreBind( void ) {
|
|
idMover_Binary::PreBind();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlat::PostBind
|
|
================
|
|
*/
|
|
void idPlat::PostBind( void ) {
|
|
idMover_Binary::PostBind();
|
|
GetLocalTriggerPosition( trigger );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlat::GetLocalTriggerPosition
|
|
================
|
|
*/
|
|
void idPlat::GetLocalTriggerPosition( const idClipModel *trigger ) {
|
|
idVec3 origin;
|
|
idMat3 axis;
|
|
|
|
if ( !trigger ) {
|
|
return;
|
|
}
|
|
|
|
GetMasterPosition( origin, axis );
|
|
localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
|
|
localTriggerAxis = trigger->GetAxis() * axis.Transpose();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlat::SpawnPlatTrigger
|
|
===============
|
|
*/
|
|
void idPlat::SpawnPlatTrigger( idVec3 &pos ) {
|
|
idBounds bounds;
|
|
idVec3 tmin;
|
|
idVec3 tmax;
|
|
|
|
// the middle trigger will be a thin trigger just
|
|
// above the starting position
|
|
|
|
bounds = GetPhysics()->GetBounds();
|
|
|
|
tmin[0] = bounds[0][0] + 33;
|
|
tmin[1] = bounds[0][1] + 33;
|
|
tmin[2] = bounds[0][2];
|
|
|
|
tmax[0] = bounds[1][0] - 33;
|
|
tmax[1] = bounds[1][1] - 33;
|
|
tmax[2] = bounds[1][2] + 8;
|
|
|
|
if ( tmax[0] <= tmin[0] ) {
|
|
tmin[0] = ( bounds[0][0] + bounds[1][0] ) * 0.5f;
|
|
tmax[0] = tmin[0] + 1;
|
|
}
|
|
if ( tmax[1] <= tmin[1] ) {
|
|
tmin[1] = ( bounds[0][1] + bounds[1][1] ) * 0.5f;
|
|
tmax[1] = tmin[1] + 1;
|
|
}
|
|
|
|
trigger = new idClipModel( idTraceModel( idBounds( tmin, tmax ) ) );
|
|
trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
|
|
trigger->SetContents( CONTENTS_TRIGGER );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlat::Event_Touch
|
|
===============
|
|
*/
|
|
void idPlat::Event_Touch( idEntity *other, trace_t *trace ) {
|
|
if ( !other->IsType( idPlayer::Type ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ( GetMoverState() == MOVER_POS1 ) && trigger && ( trace->c.id == trigger->GetId() ) && ( other->health > 0 ) ) {
|
|
Use_BinaryMover( other );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlat::Event_TeamBlocked
|
|
================
|
|
*/
|
|
void idPlat::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
|
|
// reverse direction
|
|
Use_BinaryMover( activatedBy.GetEntity() );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlat::Event_PartBlocked
|
|
===============
|
|
*/
|
|
void idPlat::Event_PartBlocked( idEntity *blockingEntity ) {
|
|
if ( damage > 0.0f ) {
|
|
blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idMover_Periodic
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idMover_Periodic )
|
|
EVENT( EV_TeamBlocked, idMover_Periodic::Event_TeamBlocked )
|
|
EVENT( EV_PartBlocked, idMover_Periodic::Event_PartBlocked )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idMover_Periodic::idMover_Periodic
|
|
===============
|
|
*/
|
|
idMover_Periodic::idMover_Periodic( void ) {
|
|
damage = 0.0f;
|
|
fl.neverDormant = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Periodic::Spawn
|
|
===============
|
|
*/
|
|
void idMover_Periodic::Spawn( void ) {
|
|
spawnArgs.GetFloat( "damage", "0", damage );
|
|
if ( !spawnArgs.GetBool( "solid", "1" ) ) {
|
|
GetPhysics()->SetContents( 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Periodic::Save
|
|
===============
|
|
*/
|
|
void idMover_Periodic::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteFloat( damage );
|
|
savefile->WriteStaticObject( physicsObj );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Periodic::Restore
|
|
===============
|
|
*/
|
|
void idMover_Periodic::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadFloat( damage );
|
|
savefile->ReadStaticObject( physicsObj );
|
|
RestorePhysics( &physicsObj );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Periodic::Think
|
|
================
|
|
*/
|
|
void idMover_Periodic::Think( void ) {
|
|
// if we are completely closed off from the player, don't do anything at all
|
|
if ( CheckDormant() ) {
|
|
return;
|
|
}
|
|
|
|
RunPhysics();
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Periodic::Event_TeamBlocked
|
|
===============
|
|
*/
|
|
void idMover_Periodic::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idMover_Periodic::Event_PartBlocked
|
|
===============
|
|
*/
|
|
void idMover_Periodic::Event_PartBlocked( idEntity *blockingEntity ) {
|
|
if ( damage > 0.0f ) {
|
|
blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Periodic::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idMover_Periodic::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
physicsObj.WriteToSnapshot( msg );
|
|
WriteBindToSnapshot( msg );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idMover_Periodic::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idMover_Periodic::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
physicsObj.ReadFromSnapshot( msg );
|
|
ReadBindFromSnapshot( msg );
|
|
|
|
if ( msg.HasChanged() ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idRotater
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idMover_Periodic, idRotater )
|
|
EVENT( EV_Activate, idRotater::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idRotater::idRotater
|
|
===============
|
|
*/
|
|
idRotater::idRotater( void ) {
|
|
activatedBy = this;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idRotater::Spawn
|
|
===============
|
|
*/
|
|
void idRotater::Spawn( void ) {
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
if ( !spawnArgs.GetBool( "nopush" ) ) {
|
|
physicsObj.SetPusher( 0 );
|
|
}
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, gameLocal.slow.time, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
|
|
SetPhysics( &physicsObj );
|
|
|
|
if ( spawnArgs.GetBool( "start_on" ) ) {
|
|
ProcessEvent( &EV_Activate, this );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idRotater::Save
|
|
===============
|
|
*/
|
|
void idRotater::Save( idSaveGame *savefile ) const {
|
|
activatedBy.Save( savefile );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idRotater::Restore
|
|
===============
|
|
*/
|
|
void idRotater::Restore( idRestoreGame *savefile ) {
|
|
activatedBy.Restore( savefile );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idRotater::Event_Activate
|
|
===============
|
|
*/
|
|
void idRotater::Event_Activate( idEntity *activator ) {
|
|
float speed;
|
|
bool x_axis;
|
|
bool y_axis;
|
|
idAngles delta;
|
|
|
|
activatedBy = activator;
|
|
|
|
delta.Zero();
|
|
|
|
if ( !spawnArgs.GetBool( "rotate" ) ) {
|
|
spawnArgs.Set( "rotate", "1" );
|
|
spawnArgs.GetFloat( "speed", "100", speed );
|
|
spawnArgs.GetBool( "x_axis", "0", x_axis );
|
|
spawnArgs.GetBool( "y_axis", "0", y_axis );
|
|
|
|
// set the axis of rotation
|
|
if ( x_axis ) {
|
|
delta[2] = speed;
|
|
} else if ( y_axis ) {
|
|
delta[0] = speed;
|
|
} else {
|
|
delta[1] = speed;
|
|
}
|
|
} else {
|
|
spawnArgs.Set( "rotate", "0" );
|
|
}
|
|
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.slow.time, 0, physicsObj.GetAxis().ToAngles(), delta, ang_zero );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idBobber
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idMover_Periodic, idBobber )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idBobber::idBobber
|
|
===============
|
|
*/
|
|
idBobber::idBobber( void ) {
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idBobber::Spawn
|
|
===============
|
|
*/
|
|
void idBobber::Spawn( void ) {
|
|
float speed;
|
|
float height;
|
|
float phase;
|
|
bool x_axis;
|
|
bool y_axis;
|
|
idVec3 delta;
|
|
|
|
spawnArgs.GetFloat( "speed", "4", speed );
|
|
spawnArgs.GetFloat( "height", "32", height );
|
|
spawnArgs.GetFloat( "phase", "0", phase );
|
|
spawnArgs.GetBool( "x_axis", "0", x_axis );
|
|
spawnArgs.GetBool( "y_axis", "0", y_axis );
|
|
|
|
// set the axis of bobbing
|
|
delta = vec3_origin;
|
|
if ( x_axis ) {
|
|
delta[ 0 ] = height;
|
|
} else if ( y_axis ) {
|
|
delta[ 1 ] = height;
|
|
} else {
|
|
delta[ 2 ] = height;
|
|
}
|
|
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
if ( !spawnArgs.GetBool( "nopush" ) ) {
|
|
physicsObj.SetPusher( 0 );
|
|
}
|
|
physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, speed * 500, GetPhysics()->GetOrigin(), delta * 2.0f, vec3_origin );
|
|
SetPhysics( &physicsObj );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idPendulum
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idMover_Periodic, idPendulum )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idPendulum::idPendulum
|
|
===============
|
|
*/
|
|
idPendulum::idPendulum( void ) {
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPendulum::Spawn
|
|
===============
|
|
*/
|
|
void idPendulum::Spawn( void ) {
|
|
float speed;
|
|
float freq;
|
|
float length;
|
|
float phase;
|
|
|
|
spawnArgs.GetFloat( "speed", "30", speed );
|
|
spawnArgs.GetFloat( "phase", "0", phase );
|
|
|
|
if ( spawnArgs.GetFloat( "freq", "", freq ) ) {
|
|
if ( freq <= 0.0f ) {
|
|
gameLocal.Error( "Invalid frequency on entity '%s'", GetName() );
|
|
}
|
|
} else {
|
|
// find pendulum length
|
|
length = idMath::Fabs( GetPhysics()->GetBounds()[0][2] );
|
|
if ( length < 8 ) {
|
|
length = 8;
|
|
}
|
|
|
|
freq = 1 / ( idMath::TWO_PI ) * idMath::Sqrt( g_gravity.GetFloat() / ( 3 * length ) );
|
|
}
|
|
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
if ( !spawnArgs.GetBool( "nopush" ) ) {
|
|
physicsObj.SetPusher( 0 );
|
|
}
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
|
|
physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, 500/freq, GetPhysics()->GetAxis().ToAngles(), idAngles( 0, 0, speed * 2.0f ), ang_zero );
|
|
SetPhysics( &physicsObj );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idBobber
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idMover_Periodic, idRiser )
|
|
EVENT( EV_Activate, idRiser::Event_Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
===============
|
|
idRiser::idRiser
|
|
===============
|
|
*/
|
|
idRiser::idRiser( void ) {
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idRiser::Spawn
|
|
===============
|
|
*/
|
|
void idRiser::Spawn( void ) {
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
|
|
|
physicsObj.SetClipMask( MASK_SOLID );
|
|
if ( !spawnArgs.GetBool( "solid", "1" ) ) {
|
|
physicsObj.SetContents( 0 );
|
|
}
|
|
if ( !spawnArgs.GetBool( "nopush" ) ) {
|
|
physicsObj.SetPusher( 0 );
|
|
}
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
|
|
SetPhysics( &physicsObj );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRiser::Event_Activate
|
|
================
|
|
*/
|
|
void idRiser::Event_Activate( idEntity *activator ) {
|
|
|
|
if ( !IsHidden() && spawnArgs.GetBool("hide") ) {
|
|
Hide();
|
|
} else {
|
|
Show();
|
|
float time;
|
|
float height;
|
|
idVec3 delta;
|
|
|
|
spawnArgs.GetFloat( "time", "4", time );
|
|
spawnArgs.GetFloat( "height", "32", height );
|
|
|
|
delta = vec3_origin;
|
|
delta[ 2 ] = height;
|
|
|
|
physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.slow.time, time * 1000, physicsObj.GetOrigin(), delta, vec3_origin );
|
|
}
|
|
}
|