1352 lines
44 KiB
Plaintext
1352 lines
44 KiB
Plaintext
//=======================================================================
|
|
// Copyright (C) Shambler Team 2005
|
|
// basemover.cpp - base code for linear and
|
|
// angular moving brushes e.g. doors, buttons e.t.c
|
|
//=======================================================================
|
|
|
|
#include "extdll.h"
|
|
#include "defaults.h"
|
|
#include "utils.h"
|
|
#include "cbase.h"
|
|
#include "client.h"
|
|
#include "saverestore.h"
|
|
#include "player.h"
|
|
|
|
//=======================================================================
|
|
// main functions ()
|
|
//=======================================================================
|
|
TYPEDESCRIPTION CBaseMover::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CBaseMover, m_flBlockedTime, FIELD_TIME ),
|
|
DEFINE_FIELD( CBaseMover, m_iMode, FIELD_INTEGER ),
|
|
DEFINE_FIELD( CBaseMover, m_flMoveDistance, FIELD_FLOAT ),
|
|
DEFINE_FIELD( CBaseMover, m_flLip, FIELD_FLOAT ),
|
|
DEFINE_FIELD( CBaseMover, m_flHeight, FIELD_FLOAT ),
|
|
DEFINE_FIELD( CBaseMover, m_flValue, FIELD_FLOAT ),
|
|
DEFINE_FIELD( CBaseMover, m_vecFinalDest, FIELD_POSITION_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, m_vecPosition1, FIELD_POSITION_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, m_vecPosition2, FIELD_POSITION_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, m_vecFloor, FIELD_POSITION_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, m_pfnCallWhenMoveDone, FIELD_FUNCTION ),
|
|
DEFINE_FIELD( CBaseMover, m_vecAngle1, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, m_vecAngle2, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, m_flLinearMoveSpeed, FIELD_FLOAT ),
|
|
DEFINE_FIELD( CBaseMover, m_flAngularMoveSpeed, FIELD_FLOAT ),
|
|
DEFINE_FIELD( CBaseMover, m_vecFinalAngle, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseMover, flTravelTime, FIELD_FLOAT ),
|
|
}; IMPLEMENT_SAVERESTORE( CBaseMover, CBaseBrush );
|
|
|
|
void CBaseMover :: AxisDir( void )
|
|
{
|
|
//make backward compatibility
|
|
if ( pev->movedir != g_vecZero) return;
|
|
if ( FBitSet(pev->spawnflags, 128))
|
|
pev->movedir = Vector ( 0, 0, 1 ); // around z-axis
|
|
else if ( FBitSet(pev->spawnflags, 64))
|
|
pev->movedir = Vector ( 1, 0, 0 ); // around x-axis
|
|
else pev->movedir = Vector ( 0, 1, 0 ); // around y-axis
|
|
}
|
|
|
|
void CBaseMover::KeyValue( KeyValueData *pkvd )
|
|
{
|
|
if (FStrEq(pkvd->szKeyName, "lip"))
|
|
{
|
|
m_flLip = atof(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
if (FStrEq(pkvd->szKeyName, "waveheight"))
|
|
{
|
|
//field for volume_water
|
|
pev->scale = atof(pkvd->szValue) * (1.0/8.0);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "type"))
|
|
{
|
|
m_iMode = atoi(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "locksound"))
|
|
{
|
|
m_iStartSound = ALLOC_STRING(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "movesound"))
|
|
{
|
|
m_iMoveSound = ALLOC_STRING(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "stopsound"))
|
|
{
|
|
m_iStopSound = ALLOC_STRING(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "distance"))
|
|
{
|
|
m_flMoveDistance = atof(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "height"))
|
|
{
|
|
m_flHeight = atof(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "waveheight"))
|
|
{
|
|
//func_water wave height
|
|
pev->scale = atof(pkvd->szValue) * (1.0/8.0);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else if (FStrEq(pkvd->szKeyName, "contents"))
|
|
{
|
|
//func_water contents
|
|
pev->skin = atoi(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else CBaseBrush::KeyValue( pkvd );
|
|
}
|
|
|
|
//=======================================================================
|
|
// LinearMove
|
|
//
|
|
// calculate pev->velocity and pev->nextthink to reach vecDest from
|
|
// pev->origin traveling at flSpeed
|
|
//=======================================================================
|
|
void CBaseMover :: LinearMove( Vector vecInput, float flSpeed )
|
|
{
|
|
ASSERTSZ(flSpeed != 0, "LinearMove: no speed is defined!");
|
|
|
|
m_flLinearMoveSpeed = flSpeed;
|
|
m_vecFinalDest = vecInput;
|
|
|
|
SetThink( LinearMoveNow );
|
|
UTIL_SetThink( this );
|
|
}
|
|
|
|
|
|
void CBaseMover :: LinearMoveNow( void )
|
|
{
|
|
Vector vecDest;
|
|
|
|
if (m_pParent)vecDest = m_vecFinalDest + m_pParent->pev->origin;
|
|
else vecDest = m_vecFinalDest;
|
|
|
|
// Already there?
|
|
if (vecDest == pev->origin)
|
|
{
|
|
LinearMoveDone();
|
|
return;
|
|
}
|
|
|
|
// set destdelta to the vector needed to move
|
|
Vector vecDestDelta = vecDest - pev->origin;
|
|
|
|
// divide vector length by speed to get time to reach dest
|
|
flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed;
|
|
|
|
// set nextthink to trigger a call to LinearMoveDone when dest is reached
|
|
SetNextThink( flTravelTime );
|
|
SetThink( LinearMoveDone );
|
|
|
|
UTIL_SetVelocity( this, vecDestDelta / flTravelTime );
|
|
}
|
|
|
|
//=======================================================================
|
|
// After moving, set origin to exact final destination,
|
|
// call "move done" function
|
|
//=======================================================================
|
|
void CBaseMover :: LinearMoveDone( void )
|
|
{
|
|
SetThink(LinearMoveDoneNow);
|
|
UTIL_SetThink( this );
|
|
}
|
|
|
|
void CBaseMover :: LinearMoveDoneNow( void )
|
|
{
|
|
UTIL_SetVelocity(this, g_vecZero);
|
|
if (m_pParent) UTIL_AssignOrigin(this, m_vecFinalDest + m_pParent->pev->origin);
|
|
else UTIL_AssignOrigin(this, m_vecFinalDest);
|
|
|
|
DontThink();
|
|
if( m_pfnCallWhenMoveDone )(this->*m_pfnCallWhenMoveDone)();
|
|
}
|
|
|
|
//=======================================================================
|
|
// AngularMove
|
|
//
|
|
// calculate pev->velocity and pev->nextthink to reach vecDest from
|
|
// pev->origin traveling at flSpeed
|
|
// Just like LinearMove, but rotational.
|
|
//=======================================================================
|
|
void CBaseMover :: AngularMove( Vector vecDestAngle, float flSpeed )
|
|
{
|
|
ASSERTSZ(flSpeed != 0, "AngularMove: no speed is defined!");
|
|
|
|
m_vecFinalAngle = vecDestAngle;
|
|
m_flAngularMoveSpeed = flSpeed;
|
|
|
|
SetThink( AngularMoveNow );
|
|
UTIL_SetThink( this );
|
|
}
|
|
|
|
void CBaseMover :: AngularMoveNow()
|
|
{
|
|
Vector vecDestAngle;
|
|
|
|
if (m_pParent) vecDestAngle = m_vecFinalAngle + m_pParent->pev->angles;
|
|
else vecDestAngle = m_vecFinalAngle;
|
|
|
|
// Already there?
|
|
if (vecDestAngle == pev->angles)
|
|
{
|
|
AngularMoveDone();
|
|
return;
|
|
}
|
|
|
|
// set destdelta to the vector needed to move
|
|
Vector vecDestDelta = vecDestAngle - pev->angles;
|
|
|
|
// divide by speed to get time to reach dest
|
|
flTravelTime = vecDestDelta.Length() / m_flAngularMoveSpeed;
|
|
|
|
// set nextthink to trigger a call to AngularMoveDone when dest is reached
|
|
SetNextThink( flTravelTime );
|
|
SetThink( AngularMoveDone );
|
|
|
|
// scale the destdelta vector by the time spent traveling to get velocity
|
|
UTIL_SetAvelocity(this, vecDestDelta / flTravelTime );
|
|
}
|
|
|
|
void CBaseMover :: AngularMoveDone( void )
|
|
{
|
|
SetThink( AngularMoveDoneNow );
|
|
UTIL_SetThink( this );
|
|
}
|
|
|
|
//=======================================================================
|
|
// After rotating, set angle to exact final angle, call "move done" function
|
|
//=======================================================================
|
|
void CBaseMover :: AngularMoveDoneNow( void )
|
|
{
|
|
UTIL_SetAvelocity(this, g_vecZero);
|
|
if (m_pParent) UTIL_AssignAngles(this, m_vecFinalAngle + m_pParent->pev->angles);
|
|
else UTIL_AssignAngles(this, m_vecFinalAngle);
|
|
|
|
DontThink();
|
|
if ( m_pfnCallWhenMoveDone ) (this->*m_pfnCallWhenMoveDone)();
|
|
}
|
|
|
|
//=======================================================================
|
|
// ComplexMove
|
|
//
|
|
// combinate LinearMove and AngularMove
|
|
//=======================================================================
|
|
void CBaseMover :: ComplexMove( Vector vecInput, Vector vecDestAngle, float flSpeed )
|
|
{
|
|
ASSERTSZ(flSpeed != 0, "ComplexMove: no speed is defined!");
|
|
|
|
//set shared speed for moving and rotating
|
|
m_flLinearMoveSpeed = flSpeed;
|
|
m_flAngularMoveSpeed = flSpeed;
|
|
|
|
//save local variables into global containers
|
|
m_vecFinalDest = vecInput;
|
|
m_vecFinalAngle = vecDestAngle;
|
|
|
|
SetThink( ComplexMoveNow );
|
|
UTIL_SetThink( this );
|
|
}
|
|
|
|
void CBaseMover :: ComplexMoveNow( void )
|
|
{
|
|
Vector vecDest, vecDestAngle;
|
|
|
|
if (m_pParent)//calculate destination
|
|
{
|
|
vecDest = m_vecFinalDest + m_pParent->pev->origin;
|
|
vecDestAngle = m_vecFinalAngle + m_pParent->pev->angles;
|
|
}
|
|
else
|
|
{
|
|
vecDestAngle = m_vecFinalAngle;
|
|
vecDest = m_vecFinalDest;
|
|
}
|
|
|
|
// Already there?
|
|
if (vecDest == pev->origin && vecDestAngle == pev->angles)
|
|
{
|
|
ComplexMoveDone();
|
|
return;
|
|
}
|
|
|
|
// Calculate TravelTime and final angles
|
|
Vector vecDestLDelta = vecDest - pev->origin;
|
|
Vector vecDestADelta = vecDestAngle - pev->angles;
|
|
|
|
// divide vector length by speed to get time to reach dest
|
|
flTravelTime = vecDestLDelta.Length() / m_flLinearMoveSpeed;
|
|
|
|
// set nextthink to trigger a call to LinearMoveDone when dest is reached
|
|
SetNextThink( flTravelTime );
|
|
SetThink( ComplexMoveDone );
|
|
|
|
//set linear and angular velocity now
|
|
UTIL_SetVelocity( this, vecDestLDelta / flTravelTime );
|
|
UTIL_SetAvelocity(this, vecDestADelta / flTravelTime );
|
|
}
|
|
|
|
void CBaseMover :: ComplexMoveDone( void )
|
|
{
|
|
SetThink(ComplexMoveDoneNow);
|
|
UTIL_SetThink( this );
|
|
}
|
|
|
|
void CBaseMover :: ComplexMoveDoneNow( void )
|
|
{
|
|
UTIL_SetVelocity(this, g_vecZero);
|
|
UTIL_SetAvelocity(this, g_vecZero);
|
|
|
|
if (m_pParent)
|
|
{
|
|
UTIL_AssignOrigin(this, m_vecFinalDest + m_pParent->pev->origin);
|
|
UTIL_AssignAngles(this, m_vecFinalAngle + m_pParent->pev->angles);
|
|
}
|
|
else
|
|
{
|
|
UTIL_AssignOrigin(this, m_vecFinalDest);
|
|
UTIL_AssignAngles(this, m_vecFinalAngle);
|
|
}
|
|
|
|
DontThink();
|
|
if( m_pfnCallWhenMoveDone )(this->*m_pfnCallWhenMoveDone)();
|
|
}
|
|
|
|
//=======================================================================
|
|
// func_door - classic QUAKE door
|
|
//=======================================================================
|
|
void CBaseDoor::Spawn( )
|
|
{
|
|
Precache();
|
|
if(!IsRotatingDoor())UTIL_LinearVector( this );
|
|
CBaseBrush::Spawn();
|
|
|
|
if(pev->spawnflags & SF_NOTSOLID)//make illusionary door
|
|
{
|
|
pev->solid = SOLID_NOT;
|
|
pev->movetype = MOVETYPE_NONE;
|
|
}
|
|
else
|
|
{
|
|
pev->solid = SOLID_BSP;
|
|
pev->movetype = MOVETYPE_PUSH;
|
|
}
|
|
|
|
if(IsRotatingDoor())
|
|
{
|
|
// check for clockwise rotation
|
|
if ( m_flMoveDistance < 0 ) pev->movedir = pev->movedir * -1;
|
|
|
|
AxisDir();
|
|
m_vecAngle1 = pev->angles;
|
|
m_vecAngle2 = pev->angles + pev->movedir * m_flMoveDistance;
|
|
|
|
ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating door start/end positions are equal");
|
|
|
|
SetBits (pFlags, PF_ANGULAR);
|
|
}
|
|
|
|
UTIL_SetModel( ENT(pev), pev->model );
|
|
UTIL_SetOrigin(this, pev->origin);
|
|
|
|
//determine work style
|
|
if(m_iMode == 0)//normal door - only USE
|
|
{
|
|
SetUse ( DoorUse );
|
|
SetTouch ( NULL );
|
|
pev->team = 1;//info_node identifier. Do not edit!!!
|
|
}
|
|
if(m_iMode == 1)//classic QUAKE & HL door - only TOUCH
|
|
{
|
|
SetUse ( ShowInfo );//show info only
|
|
SetTouch ( DoorTouch );
|
|
}
|
|
if(m_iMode == 2)//combo door - USE and TOUCH
|
|
{
|
|
SetUse ( DoorUse );
|
|
SetTouch ( DoorTouch );
|
|
}
|
|
|
|
//as default any door is toggleable, but if mapmaker set waittime > 0
|
|
//door will de transformed into timebased door
|
|
//if waittime is -1 - button forever stay pressed
|
|
if(m_flWait == 0) pev->impulse = 1;//toggleable door
|
|
if (m_flLip == 0) m_flLip = 4;//standart offset from Quake1
|
|
|
|
if (pev->speed == 0) pev->speed = 100;//default speed
|
|
m_iState = STATE_OFF;
|
|
}
|
|
|
|
void CBaseDoor :: PostSpawn( void )
|
|
{
|
|
if (m_pParent) m_vecPosition1 = pev->origin - m_pParent->pev->origin;
|
|
else m_vecPosition1 = pev->origin;
|
|
|
|
// Subtract 2 from size because the engine expands bboxes by 1 in all directions
|
|
m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip));
|
|
|
|
ASSERTSZ(m_vecPosition1 != m_vecPosition2, "door start/end positions are equal");
|
|
if ( FBitSet (pev->spawnflags, SF_START_ON) )
|
|
{
|
|
if (m_pParent)
|
|
{
|
|
m_vecSpawnOffset = m_vecSpawnOffset + (m_vecPosition2 + m_pParent->pev->origin) - pev->origin;
|
|
UTIL_AssignOrigin(this, m_vecPosition2 + m_pParent->pev->origin);
|
|
}
|
|
else
|
|
{
|
|
m_vecSpawnOffset = m_vecSpawnOffset + m_vecPosition2 - pev->origin;
|
|
UTIL_AssignOrigin(this, m_vecPosition2);
|
|
}
|
|
Vector vecTemp = m_vecPosition2;
|
|
m_vecPosition2 = m_vecPosition1;
|
|
m_vecPosition1 = vecTemp;
|
|
}
|
|
}
|
|
|
|
void CBaseDoor :: SetToggleState( int state )
|
|
{
|
|
if ( m_iState == STATE_ON )
|
|
{
|
|
if (m_pParent) UTIL_AssignOrigin( this, m_vecPosition2 + m_pParent->pev->origin);
|
|
else UTIL_AssignOrigin( this, m_vecPosition2 );
|
|
}
|
|
else
|
|
{
|
|
if (m_pParent) UTIL_AssignOrigin( this, m_vecPosition1 + m_pParent->pev->origin);
|
|
else UTIL_AssignOrigin( this, m_vecPosition1 );
|
|
}
|
|
}
|
|
|
|
void CBaseDoor::Precache( void )
|
|
{
|
|
CBaseBrush::Precache();//precache damage sound
|
|
|
|
int m_sounds = UTIL_LoadSoundPreset(m_iMoveSound);
|
|
switch (m_sounds)//load movesound sounds (sound will play when door is moving)
|
|
{
|
|
case 1: pev->noise1 = UTIL_PrecacheSound ("doors/doormove1.wav");break;
|
|
case 2: pev->noise1 = UTIL_PrecacheSound ("doors/doormove2.wav");break;
|
|
case 3: pev->noise1 = UTIL_PrecacheSound ("doors/doormove3.wav");break;
|
|
case 4: pev->noise1 = UTIL_PrecacheSound ("doors/doormove4.wav");break;
|
|
case 5: pev->noise1 = UTIL_PrecacheSound ("doors/doormove5.wav");break;
|
|
case 6: pev->noise1 = UTIL_PrecacheSound ("doors/doormove6.wav");break;
|
|
case 7: pev->noise1 = UTIL_PrecacheSound ("doors/doormove7.wav");break;
|
|
case 8: pev->noise1 = UTIL_PrecacheSound ("doors/doormove8.wav");break;
|
|
case 9: pev->noise1 = UTIL_PrecacheSound ("doors/doormove9.wav");break;
|
|
case 10: pev->noise1 = UTIL_PrecacheSound ("doors/doormove10.wav");break;
|
|
case 0: pev->noise1 = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise1 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
|
|
m_sounds = UTIL_LoadSoundPreset(m_iStopSound);
|
|
switch (m_sounds)//load pushed sounds (sound will play at activate or pushed button)
|
|
{
|
|
case 1: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop1.wav");break;
|
|
case 2: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop2.wav");break;
|
|
case 3: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop3.wav");break;
|
|
case 4: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop4.wav");break;
|
|
case 5: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop5.wav");break;
|
|
case 6: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop6.wav");break;
|
|
case 7: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop7.wav");break;
|
|
case 8: pev->noise2 = UTIL_PrecacheSound ("doors/doorstop8.wav");break;
|
|
case 0: pev->noise2 = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise2 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
|
|
if (!FStringNull(m_sMaster))//door has master
|
|
{
|
|
m_sounds = UTIL_LoadSoundPreset(m_iStartSound);
|
|
switch (m_sounds)//load locked sounds
|
|
{
|
|
case 1: pev->noise3 = UTIL_PrecacheSound ("!NA"); break;
|
|
case 2: pev->noise3 = UTIL_PrecacheSound ("!ND"); break;
|
|
case 3: pev->noise3 = UTIL_PrecacheSound ("!NF"); break;
|
|
case 4: pev->noise3 = UTIL_PrecacheSound ("!NFIRE"); break;
|
|
case 5: pev->noise3 = UTIL_PrecacheSound ("!NCHEM"); break;
|
|
case 6: pev->noise3 = UTIL_PrecacheSound ("!NRAD"); break;
|
|
case 7: pev->noise3 = UTIL_PrecacheSound ("!NCON"); break;
|
|
case 8: pev->noise3 = UTIL_PrecacheSound ("!NH"); break;
|
|
case 9: pev->noise3 = UTIL_PrecacheSound ("!NG"); break;
|
|
case 0: pev->noise3 = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise3 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBaseDoor::DoorTouch( CBaseEntity *pOther )
|
|
{
|
|
//make delay before retouching
|
|
if ( gpGlobals->time < pev->dmgtime) return;
|
|
pev->dmgtime = gpGlobals->time + 1.0;
|
|
m_hActivator = pOther;// remember who activated the door
|
|
|
|
if(pOther->IsPlayer())DoorUse ( pOther, this, USE_TOGGLE, 1 );//player always sending 1
|
|
}
|
|
|
|
void CBaseDoor::DoorUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
m_hActivator = pActivator;
|
|
if(IsLockedByMaster( useType ))//passed only USE_SHOWINFO
|
|
{
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise3), 1, ATTN_NORM);
|
|
return;
|
|
}
|
|
if(useType == USE_SHOWINFO)//show info
|
|
{
|
|
ALERT(at_console, "======/Xash Debug System/======\n");
|
|
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
|
|
ALERT(at_console, "State: %s, Speed %.2f\n", GetStringForState( GetState()), pev->speed );
|
|
ALERT(at_console, "Texture frame: %.f. WaitTime: %.2f\n", pev->frame, m_flWait);
|
|
}
|
|
else if(m_iState != STATE_DEAD)//activate door
|
|
{
|
|
//NOTE: STATE_DEAD is better method for simulate m_flWait -1 without fucking SetThink()
|
|
if (m_iState == STATE_TURN_ON || m_iState == STATE_TURN_OFF )return;//door in-moving
|
|
if (useType == USE_TOGGLE)
|
|
{
|
|
if(m_iState == STATE_OFF) useType = USE_ON;
|
|
else useType = USE_OFF;
|
|
}
|
|
if(useType == USE_ON)
|
|
{
|
|
if( m_iState == STATE_OFF )DoorGoUp();
|
|
}
|
|
else if( useType == USE_OFF )
|
|
{
|
|
if(m_iState == STATE_ON && pev->impulse) DoorGoDown();
|
|
}
|
|
else if( useType == USE_SET )
|
|
{
|
|
if(value)
|
|
{
|
|
m_flWait = value;
|
|
pev->impulse = 0;
|
|
}
|
|
}
|
|
else if( useType == USE_RESET )
|
|
{
|
|
m_flWait = 0;
|
|
pev->impulse = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBaseDoor :: ShowInfo ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
if(useType == USE_SHOWINFO)//show info
|
|
{
|
|
ALERT(at_console, "======/Xash Debug System/======\n");
|
|
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
|
|
ALERT(at_console, "State: %s, Speed %.2f\n", GetStringForState( GetState()), pev->speed );
|
|
ALERT(at_console, "Texture frame: %.f. WaitTime: %.2f\n", pev->frame, m_flWait);
|
|
}
|
|
}
|
|
|
|
void CBaseDoor::DoorGoUp( void )
|
|
{
|
|
// It could be going-down, if blocked.
|
|
ASSERT(m_iState == STATE_OFF || m_iState == STATE_TURN_OFF);
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise1), 1, ATTN_NORM);
|
|
m_iState = STATE_TURN_ON;
|
|
SetMoveDone( DoorHitTop );
|
|
|
|
UTIL_FireTargets( pev->target, m_hActivator, this, USE_ON );
|
|
if(IsRotatingDoor()) AngularMove(m_vecAngle2, pev->speed);
|
|
else LinearMove(m_vecPosition2, pev->speed);
|
|
}
|
|
|
|
void CBaseDoor::DoorHitTop( void )
|
|
{
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise1) );
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise2), 1, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_TURN_ON);
|
|
m_iState = STATE_ON;
|
|
|
|
if(m_flWait == -1)
|
|
{
|
|
m_iState = STATE_DEAD;//keep door in this position
|
|
return;
|
|
}
|
|
|
|
if(pev->impulse == 0)//time base door
|
|
{
|
|
SetThink( DoorGoDown );
|
|
SetNextThink( m_flWait );
|
|
}
|
|
|
|
// Fire the close target (if startopen is set, then "top" is closed)
|
|
if (pev->spawnflags & SF_START_ON)
|
|
UTIL_FireTargets( pev->target, m_hActivator, this, USE_OFF );
|
|
else UTIL_FireTargets( pev->target, m_hActivator, this, USE_ON );
|
|
}
|
|
|
|
void CBaseDoor::DoorGoDown( void )
|
|
{
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise1), 1, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_ON);
|
|
m_iState = STATE_TURN_OFF;
|
|
SetMoveDone( DoorHitBottom );
|
|
if(IsRotatingDoor())AngularMove( m_vecAngle1, pev->speed);
|
|
else LinearMove(m_vecPosition1, pev->speed);
|
|
}
|
|
|
|
void CBaseDoor::DoorHitBottom( void )
|
|
{
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise1) );
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise2), 1, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_TURN_OFF);
|
|
m_iState = STATE_OFF;
|
|
|
|
if (pev->spawnflags & SF_START_ON)
|
|
UTIL_FireTargets( pev->target, m_hActivator, this, USE_ON );
|
|
else UTIL_FireTargets( pev->target, m_hActivator, this, USE_OFF );
|
|
|
|
UTIL_FireTargets( pev->target, m_hActivator, this, USE_ON );
|
|
}
|
|
|
|
void CBaseDoor::Blocked( CBaseEntity *pOther )
|
|
{
|
|
CBaseEntity *pTarget = NULL;
|
|
CBaseDoor *pDoor = NULL;
|
|
|
|
UTIL_AssignOrigin(this, pev->origin);
|
|
//make delay before retouching
|
|
if ( gpGlobals->time < m_flBlockedTime) return;
|
|
m_flBlockedTime = gpGlobals->time + 0.5;
|
|
|
|
if(m_pParent && m_pParent->edict() && pFlags & PF_PARENTMOVE) m_pParent->Blocked( pOther);
|
|
if ( pev->dmg ) pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH );
|
|
|
|
if (m_flWait >= 0)
|
|
{
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise1) );
|
|
if(IsRotatingDoor())
|
|
{
|
|
if (m_iState == STATE_TURN_ON) DoorGoUp();
|
|
else if (m_iState == STATE_TURN_OFF) DoorGoDown();
|
|
}
|
|
else
|
|
{
|
|
if (m_iState == STATE_TURN_ON) DoorGoDown();
|
|
else if (m_iState == STATE_TURN_OFF) DoorGoUp();
|
|
}
|
|
}
|
|
SetNextThink( 0 );
|
|
}
|
|
LINK_ENTITY_TO_CLASS( func_door, CBaseDoor );
|
|
|
|
//=======================================================================
|
|
// func_door_rotating - classic rotating door
|
|
//=======================================================================
|
|
void CRotDoor::Spawn( void )
|
|
{
|
|
CBaseDoor::Spawn();
|
|
|
|
if ( FBitSet (pev->spawnflags, SF_START_ON) )
|
|
{
|
|
pev->angles = m_vecAngle2;
|
|
Vector vecSav = m_vecAngle1;
|
|
m_vecAngle2 = m_vecAngle1;
|
|
m_vecAngle1 = vecSav;
|
|
pev->movedir = pev->movedir * -1;
|
|
}
|
|
}
|
|
|
|
void CRotDoor :: SetToggleState( int state )
|
|
{
|
|
if ( state == STATE_ON ) pev->angles = m_vecAngle2;
|
|
else pev->angles = m_vecAngle1;
|
|
UTIL_SetOrigin( this, pev->origin );
|
|
}
|
|
LINK_ENTITY_TO_CLASS( func_door_rotating, CRotDoor );
|
|
|
|
//=======================================================================
|
|
// func_momentary_door
|
|
//=======================================================================
|
|
void CMomentaryDoor::Precache( void )
|
|
{
|
|
CBaseBrush::Precache();
|
|
|
|
int m_sounds = UTIL_LoadSoundPreset(m_iMoveSound);
|
|
switch (m_sounds)//load pushed sounds (sound will play at activate or pushed button)
|
|
{
|
|
case 1: pev->noise = UTIL_PrecacheSound ("materials/doors/doormove1.wav");break;
|
|
case 2: pev->noise = UTIL_PrecacheSound ("materials/doors/doormove2.wav");break;
|
|
case 3: pev->noise = UTIL_PrecacheSound ("materials/doors/doormove3.wav");break;
|
|
case 4: pev->noise = UTIL_PrecacheSound ("materials/doors/doormove4.wav");break;
|
|
case 5: pev->noise = UTIL_PrecacheSound ("materials/doors/doormove5.wav");break;
|
|
case 6: pev->noise = UTIL_PrecacheSound ("materials/doors/doormove6.wav");break;
|
|
case 0: pev->noise = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
|
|
m_sounds = UTIL_LoadSoundPreset(m_iStopSound);
|
|
switch (m_sounds)//load pushed sounds (sound will play at activate or pushed button)
|
|
{
|
|
case 1: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop1.wav");break;
|
|
case 2: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop2.wav");break;
|
|
case 3: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop3.wav");break;
|
|
case 4: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop4.wav");break;
|
|
case 5: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop5.wav");break;
|
|
case 6: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop6.wav");break;
|
|
case 7: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop7.wav");break;
|
|
case 8: pev->noise2 = UTIL_PrecacheSound ("materials/doors/doorstop8.wav");break;
|
|
case 0: pev->noise2 = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise2 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
}
|
|
|
|
void CMomentaryDoor::Spawn( void )
|
|
{
|
|
Precache();
|
|
CBaseBrush::Spawn();
|
|
UTIL_LinearVector( this );//movement direction
|
|
|
|
if(pev->spawnflags & SF_NOTSOLID)pev->solid = SOLID_NOT; //make illusionary wall
|
|
else pev->solid = SOLID_BSP;
|
|
pev->movetype = MOVETYPE_PUSH;
|
|
|
|
m_iState = STATE_OFF;
|
|
UTIL_SetOrigin(this, pev->origin);
|
|
UTIL_SetModel( ENT(pev), pev->model );
|
|
SetTouch( NULL );//just in case
|
|
}
|
|
|
|
void CMomentaryDoor::PostSpawn( void )
|
|
{
|
|
if (m_pParent) m_vecPosition1 = pev->origin - m_pParent->pev->origin;
|
|
else m_vecPosition1 = pev->origin;
|
|
|
|
// Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
|
|
m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip));
|
|
ASSERTSZ(m_vecPosition1 != m_vecPosition2, "door start/end positions are equal");
|
|
|
|
if(pev->spawnflags & SF_START_ON)
|
|
{
|
|
if (m_pParent)
|
|
{
|
|
m_vecSpawnOffset = m_vecSpawnOffset + (m_vecPosition2 + m_pParent->pev->origin) - pev->origin;
|
|
UTIL_AssignOrigin(this, m_vecPosition2 + m_pParent->pev->origin);
|
|
}
|
|
else
|
|
{
|
|
m_vecSpawnOffset = m_vecSpawnOffset + m_vecPosition2 - pev->origin;
|
|
UTIL_AssignOrigin(this, m_vecPosition2);
|
|
}
|
|
Vector vecTemp = m_vecPosition2;
|
|
m_vecPosition2 = m_vecPosition1;
|
|
m_vecPosition1 = vecTemp;
|
|
}
|
|
}
|
|
|
|
void CMomentaryDoor::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
if (useType == USE_ON) value = 1;
|
|
else if(useType == USE_OFF) value = 0;
|
|
else if(useType == USE_SET);
|
|
else if(useType == USE_SHOWINFO)//show info
|
|
{
|
|
Msg("======/Xash Debug System/======\n");
|
|
Msg("classname: %s\n", STRING(pev->classname));
|
|
Msg("State: %s, Lip %.2f\n", GetStringForState( GetState()), m_flLip );
|
|
SHIFT;
|
|
}
|
|
else return;
|
|
|
|
if ( value > 1.0 )value = 1.0;
|
|
|
|
if (IsLockedByMaster()) return;
|
|
Vector move = m_vecPosition1 + (value * (m_vecPosition2 - m_vecPosition1));
|
|
|
|
float speed = 0;
|
|
Vector delta;
|
|
|
|
if (pev->speed) speed = pev->speed;
|
|
else
|
|
{
|
|
// default: get there in 0.1 secs
|
|
delta = move - pev->origin;
|
|
speed = delta.Length() * 10;
|
|
}
|
|
|
|
//FIXME: allow for it being told to move at the same speed in the _opposite_ direction!
|
|
if ( speed != 0 )
|
|
{
|
|
// This entity only thinks when it moves
|
|
if ( m_iState == STATE_OFF )
|
|
{
|
|
//ALERT(at_console,"USE: start moving to %f %f %f.\n", move.x, move.y, move.z);
|
|
m_iState = STATE_ON;
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), 1, ATTN_NORM);
|
|
}
|
|
|
|
LinearMove( move, speed );
|
|
SetMoveDone( MomentaryMoveDone );
|
|
}
|
|
}
|
|
|
|
void CMomentaryDoor::MomentaryMoveDone( void )
|
|
{
|
|
m_iState = STATE_OFF;
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise2), 1, ATTN_NORM);
|
|
}
|
|
LINK_ENTITY_TO_CLASS( func_momentary_door, CMomentaryDoor );
|
|
|
|
//=======================================================================
|
|
// func_platform (a lift\elevator)
|
|
//=======================================================================
|
|
void CBasePlatform::Precache( void )
|
|
{
|
|
CBaseBrush::Precache();//precache damage sound
|
|
|
|
int m_sounds = UTIL_LoadSoundPreset(m_iMoveSound);
|
|
switch (m_sounds)//load movesound sounds (sound will play when door is moving)
|
|
{
|
|
case 1: pev->noise = UTIL_PrecacheSound ("plats/bigmove1.wav");break;
|
|
case 2: pev->noise = UTIL_PrecacheSound ("plats/bigmove2.wav");break;
|
|
case 3: pev->noise = UTIL_PrecacheSound ("plats/elevmove1.wav");break;
|
|
case 4: pev->noise = UTIL_PrecacheSound ("plats/elevmove2.wav");break;
|
|
case 5: pev->noise = UTIL_PrecacheSound ("plats/elevmove3.wav");break;
|
|
case 6: pev->noise = UTIL_PrecacheSound ("plats/freightmove1.wav");break;
|
|
case 7: pev->noise = UTIL_PrecacheSound ("plats/freightmove2.wav");break;
|
|
case 8: pev->noise = UTIL_PrecacheSound ("plats/heavymove1.wav");break;
|
|
case 9: pev->noise = UTIL_PrecacheSound ("plats/rackmove1.wav");break;
|
|
case 10: pev->noise = UTIL_PrecacheSound ("plats/railmove1.wav");break;
|
|
case 11: pev->noise = UTIL_PrecacheSound ("plats/squeekmove1.wav");break;
|
|
case 12: pev->noise = UTIL_PrecacheSound ("plats/talkmove1.wav");break;
|
|
case 13: pev->noise = UTIL_PrecacheSound ("plats/talkmove2.wav");break;
|
|
case 0: pev->noise = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
|
|
m_sounds = UTIL_LoadSoundPreset(m_iStopSound);
|
|
switch (m_sounds)//load pushed sounds (sound will play at activate or pushed button)
|
|
{
|
|
case 1: pev->noise1 = UTIL_PrecacheSound ("plats/bigstop1.wav");break;
|
|
case 2: pev->noise1 = UTIL_PrecacheSound ("plats/bigstop2.wav");break;
|
|
case 3: pev->noise1 = UTIL_PrecacheSound ("plats/freightstop1.wav");break;
|
|
case 4: pev->noise1 = UTIL_PrecacheSound ("plats/heavystop2.wav");break;
|
|
case 5: pev->noise1 = UTIL_PrecacheSound ("plats/rackstop1.wav");break;
|
|
case 6: pev->noise1 = UTIL_PrecacheSound ("plats/railstop1.wav");break;
|
|
case 7: pev->noise1 = UTIL_PrecacheSound ("plats/squeekstop1.wav");break;
|
|
case 8: pev->noise1 = UTIL_PrecacheSound ("plats/talkstop1.wav");break;
|
|
case 0: pev->noise1 = UTIL_PrecacheSound ("common/null.wav"); break;
|
|
default: pev->noise1 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
|
|
}
|
|
|
|
UTIL_PrecacheSound( "buttons/button11.wav" );//error sound
|
|
}
|
|
|
|
void CBasePlatform :: Setup( void )
|
|
{
|
|
Precache(); //precache moving & stop sounds
|
|
|
|
pev->angles = g_vecZero;
|
|
|
|
if (IsWater()) pev->solid = SOLID_NOT; // special contents (water, slime, e.t.c. )
|
|
else pev->solid = SOLID_BSP;
|
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
|
UTIL_SetOrigin(this, pev->origin);
|
|
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
|
UTIL_SetModel(ENT(pev), pev->model );
|
|
// vecPosition1 is the top position, vecPosition2 is the bottom
|
|
if (m_pParent) m_vecPosition1 = pev->origin - m_pParent->pev->origin;
|
|
else m_vecPosition1 = pev->origin;
|
|
m_vecPosition2 = m_vecPosition1;
|
|
|
|
if(IsMovingPlatform() || IsComplexPlatform())
|
|
{
|
|
m_vecPosition2.z = m_vecPosition2.z + step();
|
|
ASSERTSZ(m_vecPosition1 != m_vecPosition2, "moving platform start/end positions are equal\n");
|
|
}
|
|
if(IsRotatingPlatform() || IsComplexPlatform())
|
|
{
|
|
if ( m_flMoveDistance < 0 ) pev->movedir = pev->movedir * -1;
|
|
|
|
AxisDir();
|
|
m_vecAngle1 = pev->angles;
|
|
m_vecAngle2 = pev->angles + pev->movedir * m_flMoveDistance;
|
|
|
|
ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating platform start/end positions are equal\n");
|
|
SetBits (pFlags, PF_ANGULAR);
|
|
}
|
|
if (!IsWater())CBaseBrush::Spawn();
|
|
|
|
if (pev->speed == 0) pev->speed = 150;
|
|
m_iState = STATE_OFF;
|
|
|
|
}
|
|
|
|
void CBasePlatform :: StartMessage( CBasePlayer *pPlayer )
|
|
{
|
|
if(IsWater())
|
|
{
|
|
MESSAGE_BEGIN( MSG_ONE, gmsgAddMirror, NULL, pPlayer->pev );
|
|
WRITE_SHORT( entindex() );
|
|
MESSAGE_END();
|
|
pev->body = 1; //enable water
|
|
}
|
|
}
|
|
|
|
void CBasePlatform :: PostSpawn( void )
|
|
{
|
|
if ( FBitSet( pev->spawnflags, SF_START_ON ) )
|
|
{
|
|
if (m_pParent) UTIL_AssignOrigin (this, m_vecPosition2 + m_pParent->pev->origin);
|
|
else UTIL_AssignOrigin (this, m_vecPosition2);
|
|
UTIL_AssignAngles(this, m_vecAngle2);
|
|
m_iState = STATE_ON;
|
|
}
|
|
else
|
|
{
|
|
if (m_pParent) UTIL_AssignOrigin (this, m_vecPosition1 + m_pParent->pev->origin);
|
|
else UTIL_AssignOrigin (this, m_vecPosition1);
|
|
UTIL_AssignAngles(this, m_vecAngle1);
|
|
m_iState = STATE_OFF;
|
|
}
|
|
}
|
|
|
|
void CBasePlatform :: Spawn( void )
|
|
{
|
|
Setup();
|
|
}
|
|
|
|
void CBasePlatform :: PostActivate( void )
|
|
{
|
|
if(m_iState == STATE_TURN_OFF || m_iState == STATE_TURN_ON)//platform "in-moving" ? restore sound!
|
|
{
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
void CBasePlatform :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
m_hActivator = pActivator;
|
|
|
|
if (useType == USE_TOGGLE)
|
|
{
|
|
if(m_iState == STATE_ON) useType = USE_OFF;
|
|
else useType = USE_ON;
|
|
}
|
|
if (useType == USE_ON)
|
|
{
|
|
GoUp();
|
|
}
|
|
else if ( useType == USE_OFF )
|
|
{
|
|
GoDown();
|
|
}
|
|
else if ( useType == USE_SET )
|
|
{
|
|
GoToFloor( value );
|
|
}
|
|
else if ( useType == USE_RESET )
|
|
{
|
|
GoToFloor( 1 );
|
|
}
|
|
else if (useType == USE_SHOWINFO)
|
|
{
|
|
Msg( "======/Xash Debug System/======\n");
|
|
Msg( "classname: %s\n", STRING(pev->classname));
|
|
if(IsWater())Msg( "Contents: %s, WaveHeight %g\n", GetContentsString( pev->skin ), pev->scale);
|
|
else Msg( "State: %s, floor %g\n", GetStringForState( GetState()), CalcFloor());
|
|
Msg( "distance %g, speed %g\n", m_flMoveDistance, pev->speed);
|
|
|
|
}
|
|
}
|
|
|
|
void CBasePlatform :: GoToFloor( float floor )
|
|
{
|
|
float curfloor = CalcFloor();
|
|
m_flValue = floor;
|
|
|
|
if(curfloor <= 0) return;
|
|
if(curfloor == floor) //already there?
|
|
{
|
|
//pass trough
|
|
UTIL_FireTargets( pev->target, m_hActivator, this, USE_RESET, m_flValue );
|
|
return;
|
|
}
|
|
|
|
m_vecFloor = m_vecPosition1;
|
|
m_vecFloor.z = pev->origin.z + (floor * step()) - (curfloor * step());
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM);
|
|
|
|
if(floor > curfloor)m_iState = STATE_TURN_ON;
|
|
else m_iState = STATE_TURN_OFF;
|
|
|
|
SetMoveDone( HitFloor );
|
|
|
|
if(fabs(floor - curfloor) > 1.0) //create floor informator for prop_counter
|
|
{
|
|
CBaseEntity *pFloor = CBaseEntity::Create( "floorent", pev->origin, g_vecZero, edict() );
|
|
pFloor->pev->target = pev->netname;
|
|
pFloor->PostActivate();
|
|
}
|
|
LinearMove(m_vecFloor, pev->speed);
|
|
}
|
|
|
|
void CBasePlatform :: HitFloor( void )
|
|
{
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noise1), m_flVolume, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_TURN_ON || m_iState == STATE_TURN_OFF);
|
|
UTIL_FireTargets( pev->target, m_hActivator, this, USE_TOGGLE, m_flValue );
|
|
UTIL_FireTargets( pev->netname, m_hActivator, this, USE_SET, m_flValue );
|
|
m_vecPosition2 = pev->origin; //save current floor
|
|
if(m_iState == STATE_TURN_ON) m_iState = STATE_ON;
|
|
if(m_iState == STATE_TURN_OFF) m_iState = STATE_OFF;
|
|
}
|
|
|
|
void CBasePlatform :: GoDown( void )
|
|
{
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_ON || m_iState == STATE_TURN_ON);
|
|
m_iState = STATE_TURN_OFF;
|
|
SetMoveDone( HitBottom );
|
|
|
|
if(IsRotatingPlatform()) AngularMove( m_vecAngle1, pev->speed );
|
|
else if(IsMovingPlatform()) LinearMove( m_vecPosition1, pev->speed );
|
|
else if(IsComplexPlatform())ComplexMove( m_vecPosition1, m_vecAngle1, pev->speed );
|
|
else HitBottom();//don't brake platform status
|
|
}
|
|
|
|
void CBasePlatform :: HitBottom( void )
|
|
{
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noise1), m_flVolume, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_TURN_OFF);
|
|
UTIL_FireTargets( pev->netname, m_hActivator, this, USE_SET, 1 );
|
|
m_iState = STATE_OFF;
|
|
}
|
|
|
|
void CBasePlatform :: GoUp( void )
|
|
{
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_OFF || m_iState == STATE_TURN_OFF);
|
|
m_iState = STATE_TURN_ON;
|
|
SetMoveDone( HitTop );
|
|
|
|
if(IsRotatingPlatform()) AngularMove( m_vecAngle2, pev->speed );
|
|
else if(IsMovingPlatform()) LinearMove( m_vecPosition2, pev->speed );
|
|
else if(IsComplexPlatform())ComplexMove( m_vecPosition2, m_vecAngle2, pev->speed );
|
|
else HitTop();//don't brake platform status
|
|
}
|
|
|
|
void CBasePlatform :: HitTop( void )
|
|
{
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noise1), m_flVolume, ATTN_NORM);
|
|
|
|
ASSERT(m_iState == STATE_TURN_ON);
|
|
UTIL_FireTargets( pev->netname, m_hActivator, this, USE_SET, 2 );
|
|
m_iState = STATE_ON;
|
|
}
|
|
|
|
void CBasePlatform :: Blocked( CBaseEntity *pOther )
|
|
{
|
|
UTIL_AssignOrigin(this, pev->origin);
|
|
//make delay before retouching
|
|
if ( gpGlobals->time < m_flBlockedTime) return;
|
|
m_flBlockedTime = gpGlobals->time + 0.5;
|
|
|
|
if(m_pParent && m_pParent->edict() && pFlags & PF_PARENTMOVE) m_pParent->Blocked( pOther);
|
|
pOther->TakeDamage( pev, pev, 1, DMG_CRUSH );
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
ASSERT(m_iState == STATE_TURN_ON || m_iState == STATE_TURN_OFF);
|
|
|
|
if (m_iState == STATE_TURN_ON) GoDown();
|
|
else if (m_iState == STATE_TURN_OFF) GoUp();
|
|
SetNextThink( 0 );
|
|
}
|
|
|
|
LINK_ENTITY_TO_CLASS( func_water, CBasePlatform );
|
|
LINK_ENTITY_TO_CLASS( func_platform, CBasePlatform );
|
|
LINK_ENTITY_TO_CLASS( func_platform_rotating, CBasePlatform );
|
|
|
|
|
|
//=======================================================================
|
|
// func_train (Classic QUAKE Train)
|
|
//=======================================================================
|
|
TYPEDESCRIPTION CFuncTrain::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CFuncTrain, pCurPath, FIELD_CLASSPTR ),
|
|
DEFINE_FIELD( CFuncTrain, pNextPath, FIELD_CLASSPTR ),
|
|
};
|
|
IMPLEMENT_SAVERESTORE( CFuncTrain, CBasePlatform );
|
|
|
|
void CFuncTrain :: Spawn( void )
|
|
{
|
|
Precache(); //precache moving & stop sounds
|
|
|
|
if(pev->spawnflags & SF_NOTSOLID)//make illusionary train
|
|
{
|
|
pev->solid = SOLID_NOT;
|
|
pev->movetype = MOVETYPE_NONE;
|
|
}
|
|
else
|
|
{
|
|
pev->solid = SOLID_BSP;
|
|
pev->movetype = MOVETYPE_PUSH;
|
|
}
|
|
|
|
UTIL_SetOrigin(this, pev->origin);
|
|
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
|
UTIL_SetModel(ENT(pev), pev->model );
|
|
CBaseBrush::Spawn();
|
|
|
|
//determine method for calculating origin
|
|
if(pev->origin != g_vecZero) pev->impulse = 1;
|
|
|
|
if (pev->speed == 0) pev->speed = 100;
|
|
m_iState = STATE_OFF;
|
|
}
|
|
|
|
void CFuncTrain :: PostSpawn( void )
|
|
{
|
|
if (!FindPath()) return;
|
|
|
|
if (pev->impulse)
|
|
{
|
|
m_vecSpawnOffset = m_vecSpawnOffset + pCurPath->pev->origin - pev->origin;
|
|
if (m_pParent) UTIL_AssignOrigin (this, pCurPath->pev->origin - m_pParent->pev->origin );
|
|
else UTIL_AssignOrigin (this, pCurPath->pev->origin );
|
|
}
|
|
else
|
|
{
|
|
m_vecSpawnOffset = m_vecSpawnOffset + (pCurPath->pev->origin - TrainOrg()) - pev->origin;
|
|
if (m_pParent) UTIL_AssignOrigin (this, pCurPath->pev->origin - TrainOrg() - m_pParent->pev->origin );
|
|
else UTIL_AssignOrigin (this, pCurPath->pev->origin - TrainOrg());
|
|
}
|
|
pev->target = pCurPath->pev->target;
|
|
}
|
|
|
|
void CFuncTrain :: PostActivate( void )
|
|
{
|
|
if(m_iState == STATE_ON)//platform "in-moving" ? restore sound!
|
|
{
|
|
EMIT_SOUND (ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM);
|
|
}
|
|
if ( pev->spawnflags & SF_START_ON )
|
|
{
|
|
m_iState = STATE_OFF; //restore sound on a next level
|
|
SetThink( Next ); //evil stuff...
|
|
SetNextThink( 0.1 );
|
|
ClearBits( pev->spawnflags, SF_START_ON );//fire once
|
|
}
|
|
}
|
|
|
|
void CFuncTrain::ClearPointers( void )
|
|
{
|
|
CBaseEntity::ClearPointers();
|
|
pCurPath = NULL;
|
|
pNextPath = NULL;
|
|
}
|
|
|
|
void CFuncTrain::OverrideReset( void )
|
|
{
|
|
// Are we moving?
|
|
if ( m_iState == STATE_ON )
|
|
{
|
|
pev->target = pev->message;
|
|
Msg("Override target %s\n", STRING( pev->target ));
|
|
if (FindPath()) SetBits( pev->spawnflags, SF_START_ON );//PostActivate member
|
|
else Stop();
|
|
}
|
|
}
|
|
|
|
CBaseEntity *CFuncTrain::FindPath( void )
|
|
{
|
|
//find start track
|
|
Msg("Find corner %s\n", STRING( pev->target ));
|
|
pCurPath = UTIL_FindEntityByTargetname (NULL, STRING(pev->target));
|
|
if(pCurPath && pCurPath->edict())
|
|
{
|
|
return pCurPath;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CBaseEntity *CFuncTrain::FindNextPath( void )
|
|
{
|
|
//safe way to check patch
|
|
if(!pCurPath) return FALSE;
|
|
|
|
// get pointer to next target
|
|
if(pev->speed > 0)pNextPath = ((CPathCorner *)pCurPath)->GetNext();
|
|
if(pev->speed < 0)pNextPath = ((CPathCorner *)pCurPath)->GetPrev();
|
|
|
|
if(pNextPath && pNextPath->edict()) //validate path
|
|
{
|
|
//record new value (this will be used after changelevel)
|
|
//pev->target = pNextPath->pev->targetname;
|
|
|
|
//pCurPath = pNextPath;
|
|
Msg("CFuncTrain::FindNextPath: pCurPath %s pNextPath %s\n", STRING( pCurPath->pev->targetname ), STRING( pNextPath->pev->targetname ));
|
|
return pNextPath; //path found
|
|
}
|
|
|
|
Stop();//stopping platform
|
|
return NULL;
|
|
}
|
|
|
|
void CFuncTrain :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
m_hActivator = pActivator;
|
|
|
|
if (useType == USE_TOGGLE)
|
|
{
|
|
if(m_iState == STATE_ON) useType = USE_OFF;
|
|
else useType = USE_ON;
|
|
}
|
|
if (useType == USE_ON) Next();
|
|
else if ( useType == USE_OFF ) Stop();
|
|
else if (useType == USE_SET) Reverse();//reverse train
|
|
else if (useType == USE_SHOWINFO)
|
|
{
|
|
DEBUGHEAD;
|
|
ALERT(at_console, "State: %s, speed %g\n", GetStringForState( GetState()), pev->speed );
|
|
if(GetPrev() && GetPrev()->edict()) Msg("Prev path %s", STRING(GetPrev()->pev->targetname) );
|
|
if(GetNext() && GetNext()->edict()) Msg("Next path %s", STRING(GetNext()->pev->targetname) );
|
|
SHIFT;
|
|
}
|
|
}
|
|
|
|
void CFuncTrain :: Next( void )
|
|
{
|
|
CBaseEntity *pTarg = FindPath();
|
|
|
|
if(!pTarg) return;
|
|
|
|
//linear move to next corner.
|
|
if(m_iState == STATE_OFF)//enable train sound
|
|
{
|
|
STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noise) );
|
|
EMIT_SOUND (ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM);
|
|
}
|
|
|
|
ClearBits(pev->effects, EF_NOINTERP); //enable interpolation
|
|
m_iState = STATE_ON;
|
|
//pev->message = ((CPathCorner *)pCurPath)->GetNext()->pev->targetname;
|
|
pev->message = pev->target;
|
|
pev->target = pTarg->pev->target;
|
|
|
|
/*if( pev->speed < 0 && FBitSet(pNextPath->pev->spawnflags, SF_CORNER_TELEPORT))
|
|
{
|
|
SetBits(pev->effects, EF_NOINTERP);
|
|
if (m_pParent) UTIL_AssignOrigin(this, pNextPath->pev->origin - m_pParent->pev->origin );
|
|
else UTIL_AssignOrigin(this, pNextPath->pev->origin );
|
|
Wait(); // Get on with doing the next path corner.
|
|
return;
|
|
}*/
|
|
|
|
if (m_pParent)
|
|
{
|
|
if (pev->impulse) LinearMove( pTarg->pev->origin - m_pParent->pev->origin, fabs( pev->speed));
|
|
else LinearMove (pTarg->pev->origin - TrainOrg() - m_pParent->pev->origin, fabs(pev->speed));
|
|
}
|
|
else
|
|
{
|
|
if (pev->impulse) LinearMove( pTarg->pev->origin, fabs(pev->speed) );
|
|
else LinearMove (pTarg->pev->origin - TrainOrg(), fabs(pev->speed) );
|
|
}
|
|
pCurPath = pTarg;
|
|
SetMoveDone( Wait );
|
|
}
|
|
|
|
void CFuncTrain :: Wait( void )
|
|
{
|
|
UTIL_FireTargets(pev->netname, this, this, USE_TOGGLE );
|
|
((CPathCorner *)pCurPath)->UpdateTargets();
|
|
((CPathCorner *)pCurPath)->GetSpeed( &pev->speed );
|
|
if(!Stop(((CPathCorner *)pCurPath)->GetDelay())) Next();// go to next corner
|
|
}
|
|
|
|
BOOL CFuncTrain :: Teleport( void )
|
|
{
|
|
if( FBitSet(pNextPath->pev->spawnflags, SF_CORNER_TELEPORT))
|
|
{
|
|
SetBits(pev->effects, EF_NOINTERP);
|
|
|
|
//determine teleportation point
|
|
if(pev->speed > 0)
|
|
{
|
|
pNextPath = pNextPath->GetNext();
|
|
((CPathCorner *)pCurPath)->UpdateTargets();
|
|
UTIL_FireTargets(pev->netname, this, this, USE_TOGGLE );
|
|
}
|
|
if (m_pParent) UTIL_AssignOrigin(this, pNextPath->pev->origin - m_pParent->pev->origin );
|
|
else UTIL_AssignOrigin(this, pNextPath->pev->origin );
|
|
|
|
pCurPath = pNextPath;
|
|
Next(); // Get on with doing the next path corner.
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CFuncTrain :: Stop( float flWait )
|
|
{
|
|
if( flWait == 0 ) return FALSE;
|
|
m_iState = STATE_OFF;
|
|
|
|
// clear the sound channel.
|
|
STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noise) );
|
|
EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise1), m_flVolume, ATTN_NORM);
|
|
|
|
UTIL_SetVelocity(this, g_vecZero);
|
|
UTIL_SetAvelocity(this, g_vecZero);
|
|
if( flWait > 0)
|
|
{
|
|
SetNextThink( flWait );
|
|
SetThink( Next );
|
|
}
|
|
|
|
//wait for retrigger
|
|
if(flWait == -1) DontThink();
|
|
return TRUE;
|
|
}
|
|
|
|
void CFuncTrain::Reverse( void )
|
|
{
|
|
//update path if dir changed
|
|
if(pev->speed != 0 && pNextPath && pNextPath->edict()) pCurPath = pNextPath;
|
|
pev->speed = -pev->speed;
|
|
if(m_iState == STATE_ON) Next(); //reverse now!
|
|
}
|
|
|
|
void CFuncTrain :: Blocked( CBaseEntity *pOther )
|
|
{
|
|
// Keep "movewith" entities in line
|
|
UTIL_AssignOrigin(this, pev->origin);
|
|
|
|
if ( gpGlobals->time < m_flBlockedTime) return;
|
|
m_flBlockedTime = gpGlobals->time + 0.5;
|
|
|
|
if(m_pParent && m_pParent->edict() && pFlags & PF_PARENTMOVE) m_pParent->Blocked( pOther);
|
|
pOther->TakeDamage( pev, pev, 1, DMG_CRUSH );
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
ASSERT(m_iState == STATE_ON);
|
|
|
|
Stop( 0.5 );
|
|
}
|
|
LINK_ENTITY_TO_CLASS( func_train, CFuncTrain ); |