This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/server/ents/basemover.cpp.save

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 );