2022-12-17 13:32:43 +01:00
|
|
|
/***
|
2013-08-30 13:34:05 -07:00
|
|
|
*
|
|
|
|
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
|
|
|
|
*
|
|
|
|
* This product contains software technology licensed from Id
|
|
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
|
|
* without written permission from Valve LLC.
|
|
|
|
*
|
|
|
|
****/
|
|
|
|
/*
|
|
|
|
|
|
|
|
===== plats.cpp ========================================================
|
|
|
|
|
|
|
|
spawn, think, and touch functions for trains, etc
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "extdll.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "trains.h"
|
|
|
|
#include "saverestore.h"
|
|
|
|
|
|
|
|
static void PlatSpawnInsideTrigger(entvars_t* pevPlatform);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define SF_PLAT_TOGGLE 0x0001
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
class CBasePlatTrain : public CBaseToggle
|
|
|
|
{
|
|
|
|
public:
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2021-11-28 16:54:48 +01:00
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
2021-03-05 23:07:22 +01:00
|
|
|
void Precache() override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// This is done to fix spawn flag collisions between this class and a derived class
|
2021-11-28 15:32:26 +01:00
|
|
|
virtual bool IsTogglePlat() { return (pev->spawnflags & SF_PLAT_TOGGLE) != 0; }
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
byte m_bMoveSnd; // sound a plat makes while moving
|
|
|
|
byte m_bStopSnd; // sound a plat makes when it stops
|
|
|
|
float m_volume; // Sound volume
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CBasePlatTrain::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CBasePlatTrain, m_bMoveSnd, FIELD_CHARACTER),
|
|
|
|
DEFINE_FIELD(CBasePlatTrain, m_bStopSnd, FIELD_CHARACTER),
|
|
|
|
DEFINE_FIELD(CBasePlatTrain, m_volume, FIELD_FLOAT),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CBasePlatTrain, CBaseToggle);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CBasePlatTrain::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "lip"))
|
|
|
|
{
|
|
|
|
m_flLip = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "wait"))
|
|
|
|
{
|
|
|
|
m_flWait = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "height"))
|
|
|
|
{
|
|
|
|
m_flHeight = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "rotation"))
|
|
|
|
{
|
|
|
|
m_vecFinalAngle.x = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "movesnd"))
|
|
|
|
{
|
|
|
|
m_bMoveSnd = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "stopsnd"))
|
|
|
|
{
|
|
|
|
m_bStopSnd = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "volume"))
|
|
|
|
{
|
|
|
|
m_volume = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CBaseToggle::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define noiseMoving noise
|
|
|
|
#define noiseArrived noise1
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CBasePlatTrain::Precache()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// set the plat's "in-motion" sound
|
2013-08-30 13:34:05 -07:00
|
|
|
switch (m_bMoveSnd)
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
case 0:
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("common/null.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 1:
|
|
|
|
PRECACHE_SOUND("plats/bigmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/bigmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 2:
|
|
|
|
PRECACHE_SOUND("plats/bigmove2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/bigmove2.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 3:
|
|
|
|
PRECACHE_SOUND("plats/elevmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/elevmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 4:
|
|
|
|
PRECACHE_SOUND("plats/elevmove2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/elevmove2.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 5:
|
|
|
|
PRECACHE_SOUND("plats/elevmove3.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/elevmove3.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 6:
|
|
|
|
PRECACHE_SOUND("plats/freightmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/freightmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 7:
|
|
|
|
PRECACHE_SOUND("plats/freightmove2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/freightmove2.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 8:
|
|
|
|
PRECACHE_SOUND("plats/heavymove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/heavymove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 9:
|
|
|
|
PRECACHE_SOUND("plats/rackmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/rackmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 10:
|
|
|
|
PRECACHE_SOUND("plats/railmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/railmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 11:
|
|
|
|
PRECACHE_SOUND("plats/squeekmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/squeekmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 12:
|
|
|
|
PRECACHE_SOUND("plats/talkmove1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/talkmove1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 13:
|
|
|
|
PRECACHE_SOUND("plats/talkmove2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseMoving = MAKE_STRING("plats/talkmove2.wav");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pev->noiseMoving = MAKE_STRING("common/null.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
// set the plat's 'reached destination' stop sound
|
2013-08-30 13:34:05 -07:00
|
|
|
switch (m_bStopSnd)
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
case 0:
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("common/null.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 1:
|
|
|
|
PRECACHE_SOUND("plats/bigstop1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/bigstop1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 2:
|
|
|
|
PRECACHE_SOUND("plats/bigstop2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/bigstop2.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 3:
|
|
|
|
PRECACHE_SOUND("plats/freightstop1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/freightstop1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 4:
|
|
|
|
PRECACHE_SOUND("plats/heavystop2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/heavystop2.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 5:
|
|
|
|
PRECACHE_SOUND("plats/rackstop1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/rackstop1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 6:
|
|
|
|
PRECACHE_SOUND("plats/railstop1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/railstop1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 7:
|
|
|
|
PRECACHE_SOUND("plats/squeekstop1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/squeekstop1.wav");
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 8:
|
|
|
|
PRECACHE_SOUND("plats/talkstop1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseArrived = MAKE_STRING("plats/talkstop1.wav");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
pev->noiseArrived = MAKE_STRING("common/null.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
//====================== PLAT code ====================================================
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#define noiseMovement noise
|
|
|
|
#define noiseStopMoving noise1
|
|
|
|
|
|
|
|
class CFuncPlat : public CBasePlatTrain
|
|
|
|
{
|
|
|
|
public:
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Precache() override;
|
2021-03-05 20:54:33 +01:00
|
|
|
void Setup();
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void Blocked(CBaseEntity* pOther) override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void EXPORT PlatUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void EXPORT CallGoDown() { GoDown(); }
|
|
|
|
void EXPORT CallHitTop() { HitTop(); }
|
|
|
|
void EXPORT CallHitBottom() { HitBottom(); }
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
virtual void GoUp();
|
|
|
|
virtual void GoDown();
|
|
|
|
virtual void HitTop();
|
|
|
|
virtual void HitBottom();
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_plat, CFuncPlat);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
|
|
|
// UNDONE: Need to save this!!! It needs class & linkage
|
|
|
|
class CPlatTrigger : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
|
2021-11-28 16:54:48 +01:00
|
|
|
void SpawnInsideTrigger(CFuncPlat* pPlatform);
|
|
|
|
void Touch(CBaseEntity* pOther) override;
|
2021-03-16 14:04:05 +01:00
|
|
|
EHANDLE m_hPlatform;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER
|
|
|
|
speed default 150
|
|
|
|
|
|
|
|
Plats are always drawn in the extended position, so they will light correctly.
|
|
|
|
|
|
|
|
If the plat is the target of another trigger or button, it will start out disabled in
|
|
|
|
the extended position until it is trigger, when it will lower and become a normal plat.
|
|
|
|
|
|
|
|
If the "height" key is set, that will determine the amount the plat moves, instead of
|
|
|
|
being implicitly determined by the model's height.
|
|
|
|
|
|
|
|
Set "sounds" to one of the following:
|
|
|
|
1) base fast
|
|
|
|
2) chain slow
|
|
|
|
*/
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::Setup()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
//pev->noiseMovement = MAKE_STRING("plats/platmove1.wav");
|
|
|
|
//pev->noiseStopMoving = MAKE_STRING("plats/platstop1.wav");
|
|
|
|
|
|
|
|
if (m_flTLength == 0)
|
|
|
|
m_flTLength = 80;
|
|
|
|
if (m_flTWidth == 0)
|
|
|
|
m_flTWidth = 10;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->angles = g_vecZero;
|
|
|
|
|
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, pev->origin); // set size and link into world
|
2013-08-30 13:34:05 -07:00
|
|
|
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// vecPosition1 is the top position, vecPosition2 is the bottom
|
|
|
|
m_vecPosition1 = pev->origin;
|
|
|
|
m_vecPosition2 = pev->origin;
|
|
|
|
if (m_flHeight != 0)
|
|
|
|
m_vecPosition2.z = pev->origin.z - m_flHeight;
|
|
|
|
else
|
|
|
|
m_vecPosition2.z = pev->origin.z - pev->size.z + 8;
|
|
|
|
if (pev->speed == 0)
|
|
|
|
pev->speed = 150;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_volume == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
m_volume = 0.85;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::Precache()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
CBasePlatTrain::Precache();
|
|
|
|
//PRECACHE_SOUND("plats/platmove1.wav");
|
|
|
|
//PRECACHE_SOUND("plats/platstop1.wav");
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!IsTogglePlat())
|
|
|
|
PlatSpawnInsideTrigger(pev); // the "start moving" trigger
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Setup();
|
|
|
|
|
|
|
|
Precache();
|
|
|
|
|
|
|
|
// If this platform is the target of some button, it starts at the TOP position,
|
|
|
|
// and is brought down by that button. Otherwise, it starts at BOTTOM.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->targetname))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, m_vecPosition1);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_toggle_state = TS_AT_TOP;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetUse(&CFuncPlat::PlatUse);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, m_vecPosition2);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_toggle_state = TS_AT_BOTTOM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void PlatSpawnInsideTrigger(entvars_t* pevPlatform)
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
GetClassPtr((CPlatTrigger*)NULL)->SpawnInsideTrigger(GetClassPtr((CFuncPlat*)pevPlatform));
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// Create a trigger entity for a platform.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPlatTrigger::SpawnInsideTrigger(CFuncPlat* pPlatform)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 14:04:05 +01:00
|
|
|
m_hPlatform = pPlatform;
|
2013-08-30 13:34:05 -07:00
|
|
|
// Create trigger entity, "point" it at the owning platform, give it a touch method
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->solid = SOLID_TRIGGER;
|
|
|
|
pev->movetype = MOVETYPE_NONE;
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->origin = pPlatform->pev->origin;
|
|
|
|
|
|
|
|
// Establish the trigger field's size
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector vecTMin = pPlatform->pev->mins + Vector(25, 25, 0);
|
|
|
|
Vector vecTMax = pPlatform->pev->maxs + Vector(25, 25, 8);
|
|
|
|
vecTMin.z = vecTMax.z - (pPlatform->m_vecPosition1.z - pPlatform->m_vecPosition2.z + 8);
|
2021-03-16 14:04:05 +01:00
|
|
|
if (pPlatform->pev->size.x <= 50)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 14:04:05 +01:00
|
|
|
vecTMin.x = (pPlatform->pev->mins.x + pPlatform->pev->maxs.x) / 2;
|
2013-08-30 13:34:05 -07:00
|
|
|
vecTMax.x = vecTMin.x + 1;
|
|
|
|
}
|
2021-03-16 14:04:05 +01:00
|
|
|
if (pPlatform->pev->size.y <= 50)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 14:04:05 +01:00
|
|
|
vecTMin.y = (pPlatform->pev->mins.y + pPlatform->pev->maxs.y) / 2;
|
2013-08-30 13:34:05 -07:00
|
|
|
vecTMax.y = vecTMin.y + 1;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetSize(pev, vecTMin, vecTMax);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// When the platform's trigger field is touched, the platform ???
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPlatTrigger::Touch(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 14:04:05 +01:00
|
|
|
//Platform was removed, remove trigger
|
|
|
|
if (!m_hPlatform || !m_hPlatform->pev)
|
|
|
|
{
|
|
|
|
UTIL_Remove(this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Ignore touches by non-players
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevToucher = pOther->pev;
|
|
|
|
if (!FClassnameIs(pevToucher, "player"))
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore touches by corpses
|
2021-03-16 14:04:05 +01:00
|
|
|
if (!pOther->IsAlive())
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
2021-03-16 14:04:05 +01:00
|
|
|
|
|
|
|
CFuncPlat* platform = static_cast<CFuncPlat*>(static_cast<CBaseEntity*>(m_hPlatform));
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Make linked platform go up/down.
|
2021-03-16 14:04:05 +01:00
|
|
|
if (platform->m_toggle_state == TS_AT_BOTTOM)
|
|
|
|
platform->GoUp();
|
|
|
|
else if (platform->m_toggle_state == TS_AT_TOP)
|
2021-11-28 16:54:48 +01:00
|
|
|
platform->pev->nextthink = platform->pev->ltime + 1; // delay going down
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Used by SUB_UseTargets, when a platform is the target of a button.
|
|
|
|
// Start bringing platform down.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::PlatUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (IsTogglePlat())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Top is off, bottom is on
|
2021-11-19 14:40:35 +01:00
|
|
|
bool on = (m_toggle_state == TS_AT_BOTTOM) ? true : false;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!ShouldToggle(useType, on))
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_toggle_state == TS_AT_TOP)
|
|
|
|
GoDown();
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (m_toggle_state == TS_AT_BOTTOM)
|
2013-08-30 13:34:05 -07:00
|
|
|
GoUp();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetUse(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (m_toggle_state == TS_AT_TOP)
|
|
|
|
GoDown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform is at top, now starts moving down.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::GoDown()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
2013-08-30 13:34:05 -07:00
|
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM);
|
|
|
|
|
|
|
|
ASSERT(m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP);
|
|
|
|
m_toggle_state = TS_GOING_DOWN;
|
|
|
|
SetMoveDone(&CFuncPlat::CallHitBottom);
|
|
|
|
LinearMove(m_vecPosition2, pev->speed);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform has hit bottom. Stops and waits forever.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::HitBottom()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
2013-08-30 13:34:05 -07:00
|
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
if (!FStringNull(pev->noiseStopMoving))
|
2013-08-30 13:34:05 -07:00
|
|
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
|
|
|
|
|
|
|
|
ASSERT(m_toggle_state == TS_GOING_DOWN);
|
|
|
|
m_toggle_state = TS_AT_BOTTOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform is at bottom, now starts moving up
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::GoUp()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 15:32:26 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
2013-08-30 13:34:05 -07:00
|
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
ASSERT(m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN);
|
|
|
|
m_toggle_state = TS_GOING_UP;
|
|
|
|
SetMoveDone(&CFuncPlat::CallHitTop);
|
|
|
|
LinearMove(m_vecPosition1, pev->speed);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform has hit top. Pauses, then starts back down again.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::HitTop()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
2013-08-30 13:34:05 -07:00
|
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
if (!FStringNull(pev->noiseStopMoving))
|
2013-08-30 13:34:05 -07:00
|
|
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
ASSERT(m_toggle_state == TS_GOING_UP);
|
|
|
|
m_toggle_state = TS_AT_TOP;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!IsTogglePlat())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// After a delay, the platform will automatically start going down again.
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncPlat::CallGoDown);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlat::Blocked(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "%s Blocked by %s\n", STRING(pev->classname), STRING(pOther->pev->classname));
|
2013-08-30 13:34:05 -07:00
|
|
|
// Hurt the blocker a little
|
|
|
|
pOther->TakeDamage(pev, pev, 1, DMG_CRUSH);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
2013-08-30 13:34:05 -07:00
|
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Send the platform back where it came from
|
|
|
|
ASSERT(m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN);
|
|
|
|
if (m_toggle_state == TS_GOING_UP)
|
|
|
|
GoDown();
|
|
|
|
else if (m_toggle_state == TS_GOING_DOWN)
|
2021-11-28 16:54:48 +01:00
|
|
|
GoUp();
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class CFuncPlatRot : public CFuncPlat
|
|
|
|
{
|
|
|
|
public:
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
2021-03-05 20:54:33 +01:00
|
|
|
void SetupRotation();
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void GoUp() override;
|
|
|
|
void GoDown() override;
|
|
|
|
void HitTop() override;
|
|
|
|
void HitBottom() override;
|
|
|
|
|
|
|
|
void RotMove(Vector& destAngle, float time);
|
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
|
|
|
|
Vector m_end, m_start;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_platrot, CFuncPlatRot);
|
|
|
|
TYPEDESCRIPTION CFuncPlatRot::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CFuncPlatRot, m_end, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CFuncPlatRot, m_start, FIELD_VECTOR),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CFuncPlatRot, CFuncPlat);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::SetupRotation()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_vecFinalAngle.x != 0) // This plat rotates too!
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CBaseToggle::AxisDir(pev);
|
2021-11-28 16:54:48 +01:00
|
|
|
m_start = pev->angles;
|
2013-08-30 13:34:05 -07:00
|
|
|
m_end = pev->angles + pev->movedir * m_vecFinalAngle.x;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_start = g_vecZero;
|
|
|
|
m_end = g_vecZero;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->targetname)) // Start at top
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->angles = m_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::Spawn();
|
2013-08-30 13:34:05 -07:00
|
|
|
SetupRotation();
|
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::GoDown()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::GoDown();
|
2021-11-28 16:54:48 +01:00
|
|
|
RotMove(m_start, pev->nextthink - pev->ltime);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform has hit bottom. Stops and waits forever.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::HitBottom()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::HitBottom();
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
pev->angles = m_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform is at bottom, now starts moving up
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::GoUp()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::GoUp();
|
2021-11-28 16:54:48 +01:00
|
|
|
RotMove(m_end, pev->nextthink - pev->ltime);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform has hit top. Pauses, then starts back down again.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::HitTop()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::HitTop();
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
pev->angles = m_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncPlatRot::RotMove(Vector& destAngle, float time)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// set destdelta to the vector needed to move
|
|
|
|
Vector vecDestDelta = destAngle - pev->angles;
|
|
|
|
|
|
|
|
// Travel time is so short, we're practically there already; so make it so.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (time >= 0.1)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->avelocity = vecDestDelta / time;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->avelocity = vecDestDelta;
|
|
|
|
pev->nextthink = pev->ltime + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//====================== TRAIN code ==================================================
|
|
|
|
//
|
|
|
|
|
|
|
|
class CFuncTrain : public CBasePlatTrain
|
|
|
|
{
|
|
|
|
public:
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Precache() override;
|
|
|
|
void Activate() override;
|
|
|
|
void OverrideReset() override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void Blocked(CBaseEntity* pOther) override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void EXPORT Wait();
|
|
|
|
void EXPORT Next();
|
2021-11-28 16:54:48 +01:00
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* m_pevCurrentTarget;
|
|
|
|
int m_sounds;
|
|
|
|
bool m_activated;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_train, CFuncTrain);
|
|
|
|
TYPEDESCRIPTION CFuncTrain::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CFuncTrain, m_sounds, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CFuncTrain, m_pevCurrentTarget, FIELD_EVARS),
|
|
|
|
DEFINE_FIELD(CFuncTrain, m_activated, FIELD_BOOLEAN),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CFuncTrain, CBasePlatTrain);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncTrain::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "sounds"))
|
|
|
|
{
|
|
|
|
m_sounds = atoi(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CBasePlatTrain::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrain::Blocked(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (gpGlobals->time < m_flActivateFinished)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
m_flActivateFinished = gpGlobals->time + 0.5;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrain::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Move toward my target
|
|
|
|
pev->spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER;
|
|
|
|
Next();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER;
|
|
|
|
// Pop back to last target if it's available
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->enemy)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->target = pev->enemy->v.targetname;
|
|
|
|
pev->nextthink = 0;
|
|
|
|
pev->velocity = g_vecZero;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseStopMoving))
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrain::Wait()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Fire the pass target if there is one
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(m_pevCurrentTarget->message))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
FireTargets(STRING(m_pevCurrentTarget->message), this, this, USE_TOGGLE, 0);
|
|
|
|
if (FBitSet(m_pevCurrentTarget->spawnflags, SF_CORNER_FIREONCE))
|
2013-08-30 13:34:05 -07:00
|
|
|
m_pevCurrentTarget->message = 0;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// need pointer to LAST target.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(m_pevCurrentTarget->spawnflags, SF_TRAIN_WAIT_RETRIGGER) || (pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER) != 0)
|
|
|
|
{
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER;
|
2021-11-28 16:54:48 +01:00
|
|
|
// clear the sound channel.
|
|
|
|
if (!FStringNull(pev->noiseMovement))
|
|
|
|
STOP_SOUND(edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
|
|
|
if (!FStringNull(pev->noiseStopMoving))
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ALERT ( at_console, "%f\n", m_flWait );
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (m_flWait != 0)
|
2021-11-28 16:54:48 +01:00
|
|
|
{ // -1 wait will wait forever!
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + m_flWait;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
|
|
|
STOP_SOUND(edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
|
|
|
if (!FStringNull(pev->noiseStopMoving))
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
|
|
|
|
SetThink(&CFuncTrain::Next);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Next(); // do it RIGHT now!
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2021-11-28 16:54:48 +01:00
|
|
|
// Train next - path corner needs to change to next target
|
2013-08-30 13:34:05 -07:00
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrain::Next()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTarg;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// now find our next target
|
|
|
|
pTarg = GetNextTarget();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!pTarg)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
|
|
|
STOP_SOUND(edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
2013-08-30 13:34:05 -07:00
|
|
|
// Play stop sound
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseStopMoving))
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save last target in case we need to find it again
|
|
|
|
pev->message = pev->target;
|
|
|
|
|
|
|
|
pev->target = pTarg->pev->target;
|
|
|
|
m_flWait = pTarg->GetDelay();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_pevCurrentTarget && m_pevCurrentTarget->speed != 0)
|
|
|
|
{ // don't copy speed from target if it is 0 (uninitialized)
|
|
|
|
pev->speed = m_pevCurrentTarget->speed;
|
|
|
|
ALERT(at_aiconsole, "Train %s speed to %4.2f\n", STRING(pev->targetname), pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
m_pevCurrentTarget = pTarg->pev; // keep track of this since path corners change our target for us.
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->enemy = pTarg->edict(); //hack
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(m_pevCurrentTarget->spawnflags, SF_CORNER_TELEPORT))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Path corner has indicated a teleport to the next corner.
|
|
|
|
SetBits(pev->effects, EF_NOINTERP);
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5);
|
2013-08-30 13:34:05 -07:00
|
|
|
Wait(); // Get on with doing the next path corner.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Normal linear move.
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// CHANGED this from CHAN_VOICE to CHAN_STATIC around OEM beta time because trains should
|
|
|
|
// use CHAN_STATIC for their movement sounds to prevent sound field problems.
|
|
|
|
// this is not a hack or temporary fix, this is how things should be. (sjb).
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->noiseMovement))
|
|
|
|
STOP_SOUND(edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement));
|
|
|
|
if (!FStringNull(pev->noiseMovement))
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM);
|
2013-08-30 13:34:05 -07:00
|
|
|
ClearBits(pev->effects, EF_NOINTERP);
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMoveDone(&CFuncTrain::Wait);
|
|
|
|
LinearMove(pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5, pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrain::Activate()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Not yet active, so teleport to first target
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!m_activated)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-19 13:45:16 +01:00
|
|
|
m_activated = true;
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevTarg = VARS(FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->target)));
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->target = pevTarg->target;
|
2021-11-28 16:54:48 +01:00
|
|
|
m_pevCurrentTarget = pevTarg; // keep track of this since path corners change our target for us.
|
|
|
|
|
|
|
|
UTIL_SetOrigin(pev, pevTarg->origin - (pev->mins + pev->maxs) * 0.5);
|
|
|
|
|
|
|
|
if (FStringNull(pev->targetname))
|
|
|
|
{ // not triggered, so start immediately
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrain::Next);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*QUAKED func_train (0 .5 .8) ?
|
|
|
|
Trains are moving platforms that players can ride.
|
|
|
|
The targets origin specifies the min point of the train at each corner.
|
|
|
|
The train spawns at the first target it is pointing at.
|
|
|
|
If the train is the target of a button or trigger, it will not begin moving until activated.
|
|
|
|
speed default 100
|
|
|
|
dmg default 2
|
|
|
|
sounds
|
|
|
|
1) ratchet metal
|
|
|
|
*/
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrain::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Precache();
|
|
|
|
if (pev->speed == 0)
|
|
|
|
pev->speed = 100;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (FStringNull(pev->target))
|
2013-08-30 13:34:05 -07:00
|
|
|
ALERT(at_console, "FuncTrain with no target");
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
if (pev->dmg == 0)
|
|
|
|
pev->dmg = 2;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (FBitSet(pev->spawnflags, SF_TRACKTRAIN_PASSABLE))
|
|
|
|
pev->solid = SOLID_NOT;
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->solid = SOLID_BSP;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
|
|
|
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
2013-08-30 13:34:05 -07:00
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
|
|
|
|
2021-11-19 13:43:33 +01:00
|
|
|
m_activated = false;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_volume == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
m_volume = 0.85;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CFuncTrain::Precache()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
CBasePlatTrain::Precache();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#if 0 // obsolete
|
2013-08-30 13:34:05 -07:00
|
|
|
// otherwise use preset sound
|
|
|
|
switch (m_sounds)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
pev->noise = 0;
|
|
|
|
pev->noise1 = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
PRECACHE_SOUND ("plats/train2.wav");
|
|
|
|
PRECACHE_SOUND ("plats/train1.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/train2.wav");
|
|
|
|
pev->noise1 = MAKE_STRING("plats/train1.wav");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
PRECACHE_SOUND ("plats/platmove1.wav");
|
|
|
|
PRECACHE_SOUND ("plats/platstop1.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/platstop1.wav");
|
|
|
|
pev->noise1 = MAKE_STRING("plats/platmove1.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CFuncTrain::OverrideReset()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTarg;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Are we moving?
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->velocity != g_vecZero && pev->nextthink != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->target = pev->message;
|
|
|
|
// now find our next target
|
|
|
|
pTarg = GetNextTarget();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!pTarg)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->nextthink = 0;
|
|
|
|
pev->velocity = g_vecZero;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else // Keep moving for 0.1 secs, then find path_corner again and restart
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrain::Next);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Track Train
|
|
|
|
//
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CFuncTrackTrain::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_ppath, FIELD_CLASSPTR),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_length, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_height, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_speed, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_dir, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_startSpeed, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_controlMins, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_controlMaxs, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_sounds, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_flVolume, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_flBank, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncTrackTrain, m_oldSpeed, FIELD_FLOAT),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CFuncTrackTrain, CBaseEntity);
|
|
|
|
LINK_ENTITY_TO_CLASS(func_tracktrain, CFuncTrackTrain);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncTrackTrain::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "wheels"))
|
|
|
|
{
|
|
|
|
m_length = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "height"))
|
|
|
|
{
|
|
|
|
m_height = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "startspeed"))
|
|
|
|
{
|
|
|
|
m_startSpeed = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "sounds"))
|
|
|
|
{
|
|
|
|
m_sounds = atoi(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "volume"))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_flVolume = (float)(atoi(pkvd->szValue));
|
2013-08-30 13:34:05 -07:00
|
|
|
m_flVolume *= 0.1;
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "bank"))
|
|
|
|
{
|
|
|
|
m_flBank = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CBaseEntity::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::NextThink(float thinkTime, bool alwaysThink)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (alwaysThink)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->flags |= FL_ALWAYSTHINK;
|
|
|
|
else
|
|
|
|
pev->flags &= ~FL_ALWAYSTHINK;
|
|
|
|
|
|
|
|
pev->nextthink = thinkTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::Blocked(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevOther = pOther->pev;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Blocker is on-ground on the train
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pevOther->flags, FL_ONGROUND) && VARS(pevOther->groundentity) == pev)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float deltaSpeed = fabs(pev->speed);
|
2021-11-28 16:54:48 +01:00
|
|
|
if (deltaSpeed > 50)
|
2013-08-30 13:34:05 -07:00
|
|
|
deltaSpeed = 50;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 == pevOther->velocity.z)
|
2013-08-30 13:34:05 -07:00
|
|
|
pevOther->velocity.z += deltaSpeed;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
pevOther->velocity = (pevOther->origin - pev->origin).Normalize() * pev->dmg;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING(pev->targetname), STRING(pOther->pev->classname), pev->dmg);
|
|
|
|
if (pev->dmg <= 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
// we can't hurt this thing, so we're not concerned with it
|
|
|
|
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (useType != USE_SET)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!ShouldToggle(useType, (pev->speed != 0)))
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->speed == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->speed = m_speed * m_dir;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
Next();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->speed = 0;
|
|
|
|
pev->velocity = g_vecZero;
|
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
StopSound();
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float delta = value;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
delta = ((int)(pev->speed * 4) / (int)m_speed) * 0.25 + 0.25 * delta;
|
|
|
|
if (delta > 1)
|
2013-08-30 13:34:05 -07:00
|
|
|
delta = 1;
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (delta < -1)
|
2013-08-30 13:34:05 -07:00
|
|
|
delta = -1;
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRACKTRAIN_FORWARDONLY) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (delta < 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
delta = 0;
|
|
|
|
}
|
|
|
|
pev->speed = m_speed * delta;
|
2021-11-28 16:54:48 +01:00
|
|
|
Next();
|
|
|
|
ALERT(at_aiconsole, "TRAIN(%s), speed to %.2f\n", STRING(pev->targetname), pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
static float Fix(float angle)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
while (angle < 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
angle += 360;
|
2021-11-28 16:54:48 +01:00
|
|
|
while (angle > 360)
|
2013-08-30 13:34:05 -07:00
|
|
|
angle -= 360;
|
|
|
|
|
|
|
|
return angle;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
static void FixupAngles(Vector& v)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
v.x = Fix(v.x);
|
|
|
|
v.y = Fix(v.y);
|
|
|
|
v.z = Fix(v.z);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define TRAIN_STARTPITCH 60
|
|
|
|
#define TRAIN_MAXPITCH 200
|
|
|
|
#define TRAIN_MAXSPEED 1000 // approx max speed for sound pitch calculation
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::StopSound()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// if sound playing, stop it
|
2021-11-28 15:32:26 +01:00
|
|
|
if (m_soundPlaying && !FStringNull(pev->noise))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
unsigned short us_encode;
|
2021-11-28 16:54:48 +01:00
|
|
|
unsigned short us_sound = ((unsigned short)(m_sounds)&0x0007) << 12;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
us_encode = us_sound;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
PLAYBACK_EVENT_FULL(FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0,
|
|
|
|
g_vecZero, g_vecZero, 0.0, 0.0, us_encode, 0, 1, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise));
|
|
|
|
*/
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_brake1.wav", m_flVolume, ATTN_NORM, 0, 100);
|
|
|
|
}
|
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
m_soundPlaying = false;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// update pitch based on speed, start sound if not playing
|
2021-11-28 16:54:48 +01:00
|
|
|
// NOTE: when train goes through transition, m_soundPlaying should go to false,
|
2013-08-30 13:34:05 -07:00
|
|
|
// which will cause the looped sound to restart.
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::UpdateSound()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float flpitch;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
if (FStringNull(pev->noise))
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
flpitch = TRAIN_STARTPITCH + (fabs(pev->speed) * (TRAIN_MAXPITCH - TRAIN_STARTPITCH) / TRAIN_MAXSPEED);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (!m_soundPlaying)
|
|
|
|
{
|
|
|
|
// play startup sound for train
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_start1.wav", m_flVolume, ATTN_NORM, 0, 100);
|
2021-11-28 16:54:48 +01:00
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, (int)flpitch);
|
2021-11-28 15:32:26 +01:00
|
|
|
m_soundPlaying = true;
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
/*
|
2013-08-30 13:34:05 -07:00
|
|
|
// update pitch
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, (int) flpitch);
|
|
|
|
*/
|
|
|
|
// volume 0.0 - 1.0 - 6 bits
|
|
|
|
// m_sounds 3 bits
|
|
|
|
// flpitch = 6 bits
|
|
|
|
// 15 bits total
|
|
|
|
|
|
|
|
unsigned short us_encode;
|
2021-11-28 16:54:48 +01:00
|
|
|
unsigned short us_sound = ((unsigned short)(m_sounds)&0x0007) << 12;
|
|
|
|
unsigned short us_pitch = ((unsigned short)(flpitch / 10.0) & 0x003f) << 6;
|
|
|
|
unsigned short us_volume = ((unsigned short)(m_flVolume * 40.0) & 0x003f);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
us_encode = us_sound | us_pitch | us_volume;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
PLAYBACK_EVENT_FULL(FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0,
|
|
|
|
g_vecZero, g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::Next()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float time = 0.5;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 == pev->speed)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "TRAIN(%s): Speed is 0\n", STRING(pev->targetname));
|
2013-08-30 13:34:05 -07:00
|
|
|
StopSound();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
// if ( !m_ppath )
|
|
|
|
// m_ppath = CPathTrack::Instance(FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) ));
|
|
|
|
if (!m_ppath)
|
|
|
|
{
|
|
|
|
ALERT(at_aiconsole, "TRAIN(%s): Lost path\n", STRING(pev->targetname));
|
2013-08-30 13:34:05 -07:00
|
|
|
StopSound();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateSound();
|
|
|
|
|
|
|
|
Vector nextPos = pev->origin;
|
|
|
|
|
|
|
|
nextPos.z -= m_height;
|
2021-11-28 16:54:48 +01:00
|
|
|
CPathTrack* pnext = m_ppath->LookAhead(&nextPos, pev->speed * 0.1, true);
|
2013-08-30 13:34:05 -07:00
|
|
|
nextPos.z += m_height;
|
|
|
|
|
|
|
|
pev->velocity = (nextPos - pev->origin) * 10;
|
|
|
|
Vector nextFront = pev->origin;
|
|
|
|
|
|
|
|
nextFront.z -= m_height;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_length > 0)
|
|
|
|
m_ppath->LookAhead(&nextFront, m_length, false);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
m_ppath->LookAhead(&nextFront, 100, false);
|
2013-08-30 13:34:05 -07:00
|
|
|
nextFront.z += m_height;
|
|
|
|
|
|
|
|
Vector delta = nextFront - pev->origin;
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector angles = UTIL_VecToAngles(delta);
|
2013-08-30 13:34:05 -07:00
|
|
|
// The train actually points west
|
|
|
|
angles.y += 180;
|
|
|
|
|
|
|
|
// !!! All of this crap has to be done to make the angles not wrap around, revisit this.
|
2021-11-28 16:54:48 +01:00
|
|
|
FixupAngles(angles);
|
|
|
|
FixupAngles(pev->angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!pnext || (delta.x == 0 && delta.y == 0))
|
2013-08-30 13:34:05 -07:00
|
|
|
angles = pev->angles;
|
|
|
|
|
|
|
|
float vy, vx;
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRACKTRAIN_NOPITCH) == 0)
|
|
|
|
vx = UTIL_AngleDistance(angles.x, pev->angles.x);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
|
|
|
vx = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
vy = UTIL_AngleDistance(angles.y, pev->angles.y);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
pev->avelocity.y = vy * 10;
|
|
|
|
pev->avelocity.x = vx * 10;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_flBank != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->avelocity.y < -5)
|
|
|
|
pev->avelocity.z = UTIL_AngleDistance(UTIL_ApproachAngle(-m_flBank, pev->angles.z, m_flBank * 2), pev->angles.z);
|
|
|
|
else if (pev->avelocity.y > 5)
|
|
|
|
pev->avelocity.z = UTIL_AngleDistance(UTIL_ApproachAngle(m_flBank, pev->angles.z, m_flBank * 2), pev->angles.z);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->avelocity.z = UTIL_AngleDistance(UTIL_ApproachAngle(0, pev->angles.z, m_flBank * 4), pev->angles.z) * 4;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (pnext)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pnext != m_ppath)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CPathTrack* pFire;
|
|
|
|
if (pev->speed >= 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pFire = pnext;
|
2021-11-28 16:54:48 +01:00
|
|
|
else
|
2013-08-30 13:34:05 -07:00
|
|
|
pFire = m_ppath;
|
|
|
|
|
|
|
|
m_ppath = pnext;
|
|
|
|
// Fire the pass target if there is one
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pFire->pev->message))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
FireTargets(STRING(pFire->pev->message), this, this, USE_TOGGLE, 0);
|
|
|
|
if (FBitSet(pFire->pev->spawnflags, SF_PATH_FIREONCE))
|
2013-08-30 13:34:05 -07:00
|
|
|
pFire->pev->message = 0;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pFire->pev->spawnflags & SF_PATH_DISABLE_TRAIN) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->spawnflags |= SF_TRACKTRAIN_NOCONTROL;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Don't override speed if under user control
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRACKTRAIN_NOCONTROL) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pFire->pev->speed != 0)
|
|
|
|
{ // don't copy speed from target if it is 0 (uninitialized)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = pFire->pev->speed;
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "TrackTrain %s speed to %4.2f\n", STRING(pev->targetname), pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrackTrain::Next);
|
|
|
|
NextThink(pev->ltime + time, true);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else // end of path, stop
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
StopSound();
|
|
|
|
pev->velocity = (nextPos - pev->origin);
|
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
float distance = pev->velocity.Length();
|
|
|
|
m_oldSpeed = pev->speed;
|
|
|
|
|
|
|
|
|
|
|
|
pev->speed = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Move to the dead end
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Are we there yet?
|
2021-11-28 16:54:48 +01:00
|
|
|
if (distance > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// no, how long to get there?
|
|
|
|
time = distance / m_oldSpeed;
|
|
|
|
pev->velocity = pev->velocity * (m_oldSpeed / distance);
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrackTrain::DeadEnd);
|
|
|
|
NextThink(pev->ltime + time, false);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DeadEnd();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CFuncTrackTrain::DeadEnd()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Fire the dead-end target if there is one
|
|
|
|
CPathTrack *pTrack, *pNext;
|
|
|
|
|
|
|
|
pTrack = m_ppath;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "TRAIN(%s): Dead end ", STRING(pev->targetname));
|
2013-08-30 13:34:05 -07:00
|
|
|
// Find the dead end path node
|
|
|
|
// HACKHACK -- This is bugly, but the train can actually stop moving at a different node depending on it's speed
|
|
|
|
// so we have to traverse the list to it's end.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pTrack)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_oldSpeed < 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pNext = pTrack->ValidPath(pTrack->GetPrevious(), true);
|
|
|
|
if (pNext)
|
2013-08-30 13:34:05 -07:00
|
|
|
pTrack = pNext;
|
2021-11-28 16:54:48 +01:00
|
|
|
} while (nullptr != pNext);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pNext = pTrack->ValidPath(pTrack->GetNext(), true);
|
|
|
|
if (pNext)
|
2013-08-30 13:34:05 -07:00
|
|
|
pTrack = pNext;
|
2021-11-28 16:54:48 +01:00
|
|
|
} while (nullptr != pNext);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pev->velocity = g_vecZero;
|
|
|
|
pev->avelocity = g_vecZero;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pTrack)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "at %s\n", STRING(pTrack->pev->targetname));
|
|
|
|
if (!FStringNull(pTrack->pev->netname))
|
|
|
|
FireTargets(STRING(pTrack->pev->netname), this, this, USE_TOGGLE, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::SetControls(entvars_t* pevControls)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Vector offset = pevControls->origin - pev->oldorigin;
|
|
|
|
|
|
|
|
m_controlMins = pevControls->mins + offset;
|
|
|
|
m_controlMaxs = pevControls->maxs + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncTrackTrain::OnControls(entvars_t* pevTest)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Vector offset = pevTest->origin - pev->origin;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRACKTRAIN_NOCONTROL) != 0)
|
2021-11-19 13:43:33 +01:00
|
|
|
return false;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Transform offset into local coordinates
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_MakeVectors(pev->angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
Vector local;
|
2021-11-28 16:54:48 +01:00
|
|
|
local.x = DotProduct(offset, gpGlobals->v_forward);
|
|
|
|
local.y = -DotProduct(offset, gpGlobals->v_right);
|
|
|
|
local.z = DotProduct(offset, gpGlobals->v_up);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z &&
|
|
|
|
local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z)
|
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-19 13:43:33 +01:00
|
|
|
return false;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::Find()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_ppath = CPathTrack::Instance(FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->target)));
|
|
|
|
if (!m_ppath)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevTarget = m_ppath->pev;
|
|
|
|
if (!FClassnameIs(pevTarget, "path_track"))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_error, "func_track_train must be on a path of path_track\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
m_ppath = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector nextPos = pevTarget->origin;
|
|
|
|
nextPos.z += m_height;
|
|
|
|
|
|
|
|
Vector look = nextPos;
|
|
|
|
look.z -= m_height;
|
2021-11-28 16:54:48 +01:00
|
|
|
m_ppath->LookAhead(&look, m_length, false);
|
2013-08-30 13:34:05 -07:00
|
|
|
look.z += m_height;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->angles = UTIL_VecToAngles(look - nextPos);
|
2013-08-30 13:34:05 -07:00
|
|
|
// The train actually points west
|
|
|
|
pev->angles.y += 180;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRACKTRAIN_NOPITCH) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->angles.x = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, nextPos);
|
|
|
|
NextThink(pev->ltime + 0.1, false);
|
|
|
|
SetThink(&CFuncTrackTrain::Next);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = m_startSpeed;
|
|
|
|
|
|
|
|
UpdateSound();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::NearestPath()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTrack = NULL;
|
|
|
|
CBaseEntity* pNearest = NULL;
|
2013-08-30 13:34:05 -07:00
|
|
|
float dist, closest;
|
|
|
|
|
|
|
|
closest = 1024;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
while ((pTrack = UTIL_FindEntityInSphere(pTrack, pev->origin, 1024)) != NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// filter out non-tracks
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pTrack->pev->flags & (FL_CLIENT | FL_MONSTER)) == 0 && FClassnameIs(pTrack->pev, "path_track"))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
dist = (pev->origin - pTrack->pev->origin).Length();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (dist < closest)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
closest = dist;
|
|
|
|
pNearest = pTrack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!pNearest)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_console, "Can't find a nearby track !!!\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
SetThink(NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING(pev->targetname), STRING(pNearest->pev->targetname));
|
2013-08-30 13:34:05 -07:00
|
|
|
// If I'm closer to the next path_track on this path, then it's my real path
|
2021-11-28 16:54:48 +01:00
|
|
|
pTrack = ((CPathTrack*)pNearest)->GetNext();
|
|
|
|
if (pTrack)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->origin - pTrack->pev->origin).Length() < (pev->origin - pNearest->pev->origin).Length())
|
2013-08-30 13:34:05 -07:00
|
|
|
pNearest = pTrack;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
m_ppath = (CPathTrack*)pNearest;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->speed != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
NextThink(pev->ltime + 0.1, false);
|
|
|
|
SetThink(&CFuncTrackTrain::Next);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CFuncTrackTrain::OverrideReset()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
NextThink(pev->ltime + 0.1, false);
|
|
|
|
SetThink(&CFuncTrackTrain::NearestPath);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
CFuncTrackTrain* CFuncTrackTrain::Instance(edict_t* pent)
|
|
|
|
{
|
|
|
|
if (FClassnameIs(pent, "func_tracktrain"))
|
|
|
|
return (CFuncTrackTrain*)GET_PRIVATE(pent);
|
2013-08-30 13:34:05 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*QUAKED func_train (0 .5 .8) ?
|
|
|
|
Trains are moving platforms that players can ride.
|
|
|
|
The targets origin specifies the min point of the train at each corner.
|
|
|
|
The train spawns at the first target it is pointing at.
|
|
|
|
If the train is the target of a button or trigger, it will not begin moving until activated.
|
|
|
|
speed default 100
|
|
|
|
dmg default 2
|
|
|
|
sounds
|
|
|
|
1) ratchet metal
|
|
|
|
*/
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->speed == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
m_speed = 100;
|
|
|
|
else
|
|
|
|
m_speed = pev->speed;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = 0;
|
|
|
|
pev->velocity = g_vecZero;
|
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
pev->impulse = m_speed;
|
|
|
|
|
|
|
|
m_dir = 1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FStringNull(pev->target))
|
|
|
|
ALERT(at_console, "FuncTrain with no target");
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_TRACKTRAIN_PASSABLE) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->solid = SOLID_NOT;
|
|
|
|
else
|
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
|
|
|
|
|
|
|
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Cache off placed origin for train controls
|
|
|
|
pev->oldorigin = pev->origin;
|
|
|
|
|
|
|
|
m_controlMins = pev->mins;
|
|
|
|
m_controlMaxs = pev->maxs;
|
|
|
|
m_controlMaxs.z += 72;
|
2021-11-28 16:54:48 +01:00
|
|
|
// start trains on the next frame, to make sure their targets have had
|
|
|
|
// a chance to spawn/activate
|
|
|
|
NextThink(pev->ltime + 0.1, false);
|
|
|
|
SetThink(&CFuncTrackTrain::Find);
|
2013-08-30 13:34:05 -07:00
|
|
|
Precache();
|
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackTrain::Precache()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (m_flVolume == 0.0)
|
|
|
|
m_flVolume = 1.0;
|
|
|
|
|
|
|
|
switch (m_sounds)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
// no sound
|
|
|
|
pev->noise = 0;
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
case 1:
|
|
|
|
PRECACHE_SOUND("plats/ttrain1.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/ttrain1.wav");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
PRECACHE_SOUND("plats/ttrain2.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/ttrain2.wav");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
PRECACHE_SOUND("plats/ttrain3.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/ttrain3.wav");
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
PRECACHE_SOUND("plats/ttrain4.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/ttrain4.wav");
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
PRECACHE_SOUND("plats/ttrain6.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/ttrain6.wav");
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
PRECACHE_SOUND("plats/ttrain7.wav");
|
|
|
|
pev->noise = MAKE_STRING("plats/ttrain7.wav");
|
|
|
|
break;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRECACHE_SOUND("plats/ttrain_brake1.wav");
|
|
|
|
PRECACHE_SOUND("plats/ttrain_start1.wav");
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
m_usAdjustPitch = PRECACHE_EVENT(1, "events/train.sc");
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// This class defines the volume of space that the player must stand in to control the train
|
|
|
|
class CFuncTrainControls : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
2021-03-05 20:54:33 +01:00
|
|
|
void EXPORT Find();
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_traincontrols, CFuncTrainControls);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrainControls::Find()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
edict_t* pTarget = NULL;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
do
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pTarget = FIND_ENTITY_BY_TARGETNAME(pTarget, STRING(pev->target));
|
|
|
|
} while (!FNullEnt(pTarget) && !FClassnameIs(pTarget, "func_tracktrain"));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FNullEnt(pTarget))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_console, "No train %s\n", STRING(pev->target));
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
CFuncTrackTrain* ptrain = CFuncTrackTrain::Instance(pTarget);
|
|
|
|
ptrain->SetControls(pev);
|
|
|
|
UTIL_Remove(this);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrainControls::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->solid = SOLID_NOT;
|
|
|
|
pev->movetype = MOVETYPE_NONE;
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
|
|
|
|
|
|
|
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrainControls::Find);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = gpGlobals->time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Track changer / Train elevator
|
|
|
|
//
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define SF_TRACK_ACTIVATETRAIN 0x00000001
|
|
|
|
#define SF_TRACK_RELINK 0x00000002
|
|
|
|
#define SF_TRACK_ROTMOVE 0x00000004
|
|
|
|
#define SF_TRACK_STARTBOTTOM 0x00000008
|
|
|
|
#define SF_TRACK_DONT_MOVE 0x00000010
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// This entity is a rotating/moving platform that will carry a train to a new track.
|
|
|
|
// It must be larger in X-Y planar area than the train, since it must contain the
|
|
|
|
// train within these dimensions in order to operate when the train is near it.
|
|
|
|
//
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
TRAIN_SAFE,
|
|
|
|
TRAIN_BLOCKING,
|
|
|
|
TRAIN_FOLLOWING
|
|
|
|
} TRAIN_CODE;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
class CFuncTrackChange : public CFuncPlatRot
|
|
|
|
{
|
|
|
|
public:
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Precache() override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
// void Blocked() override;
|
|
|
|
void EXPORT GoUp() override;
|
|
|
|
void EXPORT GoDown() override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
|
|
|
void EXPORT Find();
|
|
|
|
TRAIN_CODE EvaluateTrain(CPathTrack* pcurrent);
|
|
|
|
void UpdateTrain(Vector& dest);
|
|
|
|
void HitBottom() override;
|
|
|
|
void HitTop() override;
|
|
|
|
void Touch(CBaseEntity* pOther) override;
|
|
|
|
virtual void UpdateAutoTargets(int toggleState);
|
|
|
|
bool IsTogglePlat() override { return true; }
|
|
|
|
|
|
|
|
void DisableUse() { m_use = false; }
|
|
|
|
void EnableUse() { m_use = true; }
|
|
|
|
bool UseEnabled() { return m_use; }
|
|
|
|
|
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void OverrideReset() override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
CPathTrack* m_trackTop;
|
|
|
|
CPathTrack* m_trackBottom;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
CFuncTrackTrain* m_train;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
int m_trackTopName;
|
|
|
|
int m_trackBottomName;
|
|
|
|
int m_trainName;
|
|
|
|
TRAIN_CODE m_code;
|
|
|
|
int m_targetState;
|
|
|
|
bool m_use;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_trackchange, CFuncTrackChange);
|
|
|
|
|
|
|
|
TYPEDESCRIPTION CFuncTrackChange::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_GLOBAL_FIELD(CFuncTrackChange, m_trackTop, FIELD_CLASSPTR),
|
|
|
|
DEFINE_GLOBAL_FIELD(CFuncTrackChange, m_trackBottom, FIELD_CLASSPTR),
|
|
|
|
DEFINE_GLOBAL_FIELD(CFuncTrackChange, m_train, FIELD_CLASSPTR),
|
|
|
|
DEFINE_GLOBAL_FIELD(CFuncTrackChange, m_trackTopName, FIELD_STRING),
|
|
|
|
DEFINE_GLOBAL_FIELD(CFuncTrackChange, m_trackBottomName, FIELD_STRING),
|
|
|
|
DEFINE_GLOBAL_FIELD(CFuncTrackChange, m_trainName, FIELD_STRING),
|
|
|
|
DEFINE_FIELD(CFuncTrackChange, m_code, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CFuncTrackChange, m_targetState, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CFuncTrackChange, m_use, FIELD_BOOLEAN),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CFuncTrackChange, CFuncPlatRot);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Setup();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_TRACK_DONT_MOVE))
|
2013-08-30 13:34:05 -07:00
|
|
|
m_vecPosition2.z = pev->origin.z;
|
|
|
|
|
|
|
|
SetupRotation();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_TRACK_STARTBOTTOM))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, m_vecPosition2);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_toggle_state = TS_AT_BOTTOM;
|
|
|
|
pev->angles = m_start;
|
|
|
|
m_targetState = TS_AT_TOP;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, m_vecPosition1);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_toggle_state = TS_AT_TOP;
|
|
|
|
pev->angles = m_end;
|
|
|
|
m_targetState = TS_AT_BOTTOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnableUse();
|
|
|
|
pev->nextthink = pev->ltime + 2.0;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrackChange::Find);
|
2013-08-30 13:34:05 -07:00
|
|
|
Precache();
|
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::Precache()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Can't trigger sound
|
2021-11-28 16:54:48 +01:00
|
|
|
PRECACHE_SOUND("buttons/button11.wav");
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
CFuncPlatRot::Precache();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UNDONE: Filter touches before re-evaluating the train.
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::Touch(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
TRAIN_CODE code;
|
|
|
|
entvars_t *pevToucher = pOther->pev;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncTrackChange::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FStrEq(pkvd->szKeyName, "train"))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_trainName = ALLOC_STRING(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (FStrEq(pkvd->szKeyName, "toptrack"))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_trackTopName = ALLOC_STRING(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (FStrEq(pkvd->szKeyName, "bottomtrack"))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_trackBottomName = ALLOC_STRING(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CFuncPlatRot::KeyValue(pkvd); // Pass up to base class
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CFuncTrackChange::OverrideReset()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->nextthink = pev->ltime + 1.0;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncTrackChange::Find);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::Find()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Find track entities
|
2021-11-28 16:54:48 +01:00
|
|
|
edict_t* target;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trackTopName));
|
|
|
|
if (!FNullEnt(target))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_trackTop = CPathTrack::Instance(target);
|
|
|
|
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trackBottomName));
|
|
|
|
if (!FNullEnt(target))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_trackBottom = CPathTrack::Instance(target);
|
|
|
|
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trainName));
|
|
|
|
if (!FNullEnt(target))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_train = CFuncTrackTrain::Instance(FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trainName)));
|
|
|
|
if (!m_train)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_error, "Can't find train for track change! %s\n", STRING(m_trainName));
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
Vector center = (pev->absmin + pev->absmax) * 0.5;
|
2021-11-28 16:54:48 +01:00
|
|
|
m_trackBottom = m_trackBottom->Nearest(center);
|
|
|
|
m_trackTop = m_trackTop->Nearest(center);
|
|
|
|
UpdateAutoTargets(m_toggle_state);
|
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_error, "Can't find train for track change! %s\n", STRING(m_trainName));
|
|
|
|
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trainName));
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_error, "Can't find bottom track for track change! %s\n", STRING(m_trackBottomName));
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_error, "Can't find top track for track change! %s\n", STRING(m_trackTopName));
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
TRAIN_CODE CFuncTrackChange::EvaluateTrain(CPathTrack* pcurrent)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Go ahead and work, we don't have anything to switch, so just be an elevator
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!pcurrent || !m_train)
|
2013-08-30 13:34:05 -07:00
|
|
|
return TRAIN_SAFE;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_train->m_ppath == pcurrent || (pcurrent->m_pprevious && m_train->m_ppath == pcurrent->m_pprevious) ||
|
|
|
|
(pcurrent->m_pnext && m_train->m_ppath == pcurrent->m_pnext))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_train->pev->speed != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
return TRAIN_BLOCKING;
|
|
|
|
|
|
|
|
Vector dist = pev->origin - m_train->pev->origin;
|
|
|
|
float length = dist.Length2D();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (length < m_train->m_length) // Empirically determined close distance
|
2013-08-30 13:34:05 -07:00
|
|
|
return TRAIN_FOLLOWING;
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (length > (150 + m_train->m_length))
|
2013-08-30 13:34:05 -07:00
|
|
|
return TRAIN_SAFE;
|
|
|
|
|
|
|
|
return TRAIN_BLOCKING;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
return TRAIN_SAFE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::UpdateTrain(Vector& dest)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float time = (pev->nextthink - pev->ltime);
|
|
|
|
|
|
|
|
m_train->pev->velocity = pev->velocity;
|
|
|
|
m_train->pev->avelocity = pev->avelocity;
|
2021-11-28 16:54:48 +01:00
|
|
|
m_train->NextThink(m_train->pev->ltime + time, false);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Attempt at getting the train to rotate properly around the origin of the trackchange
|
2021-11-28 16:54:48 +01:00
|
|
|
if (time <= 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
Vector offset = m_train->pev->origin - pev->origin;
|
|
|
|
Vector delta = dest - pev->angles;
|
|
|
|
// Transform offset into local coordinates
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_MakeInvVectors(delta, gpGlobals);
|
2013-08-30 13:34:05 -07:00
|
|
|
Vector local;
|
2021-11-28 16:54:48 +01:00
|
|
|
local.x = DotProduct(offset, gpGlobals->v_forward);
|
|
|
|
local.y = DotProduct(offset, gpGlobals->v_right);
|
|
|
|
local.z = DotProduct(offset, gpGlobals->v_up);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
local = local - offset;
|
2021-11-28 16:54:48 +01:00
|
|
|
m_train->pev->velocity = pev->velocity + (local * (1.0 / time));
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::GoDown()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_BLOCKING)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// HitBottom may get called during CFuncPlat::GoDown(), so set up for that
|
|
|
|
// before you call GoDown()
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateAutoTargets(TS_GOING_DOWN);
|
2013-08-30 13:34:05 -07:00
|
|
|
// If ROTMOVE, move & rotate
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_TRACK_DONT_MOVE))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMoveDone(&CFuncTrackChange::CallHitBottom);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_toggle_state = TS_GOING_DOWN;
|
2021-11-28 16:54:48 +01:00
|
|
|
AngularMove(m_start, pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::GoDown();
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMoveDone(&CFuncTrackChange::CallHitBottom);
|
|
|
|
RotMove(m_start, pev->nextthink - pev->ltime);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
// Otherwise, rotate first, move second
|
|
|
|
|
|
|
|
// If the train is moving with the platform, update it
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_FOLLOWING)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateTrain(m_start);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_train->m_ppath = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform is at bottom, now starts moving up
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::GoUp()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_BLOCKING)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// HitTop may get called during CFuncPlat::GoUp(), so set up for that
|
|
|
|
// before you call GoUp();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateAutoTargets(TS_GOING_UP);
|
|
|
|
if (FBitSet(pev->spawnflags, SF_TRACK_DONT_MOVE))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_toggle_state = TS_GOING_UP;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMoveDone(&CFuncTrackChange::CallHitTop);
|
|
|
|
AngularMove(m_end, pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If ROTMOVE, move & rotate
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlat::GoUp();
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMoveDone(&CFuncTrackChange::CallHitTop);
|
|
|
|
RotMove(m_end, pev->nextthink - pev->ltime);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Otherwise, move first, rotate second
|
|
|
|
|
|
|
|
// If the train is moving with the platform, update it
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_FOLLOWING)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateTrain(m_end);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_train->m_ppath = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Normal track change
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::UpdateAutoTargets(int toggleState)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!m_trackTop || !m_trackBottom)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (toggleState == TS_AT_TOP)
|
|
|
|
ClearBits(m_trackTop->pev->spawnflags, SF_PATH_DISABLED);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
SetBits(m_trackTop->pev->spawnflags, SF_PATH_DISABLED);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (toggleState == TS_AT_BOTTOM)
|
|
|
|
ClearBits(m_trackBottom->pev->spawnflags, SF_PATH_DISABLED);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
SetBits(m_trackBottom->pev->spawnflags, SF_PATH_DISABLED);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_toggle_state != TS_AT_TOP && m_toggle_state != TS_AT_BOTTOM)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// If train is in "safe" area, but not on the elevator, play alarm sound
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_toggle_state == TS_AT_TOP)
|
|
|
|
m_code = EvaluateTrain(m_trackTop);
|
|
|
|
else if (m_toggle_state == TS_AT_BOTTOM)
|
|
|
|
m_code = EvaluateTrain(m_trackBottom);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
|
|
|
m_code = TRAIN_BLOCKING;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_BLOCKING)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Play alarm and return
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/button11.wav", 1, ATTN_NORM);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, it's safe to move
|
|
|
|
// If at top, go down
|
|
|
|
// at bottom, go up
|
|
|
|
|
|
|
|
DisableUse();
|
|
|
|
if (m_toggle_state == TS_AT_TOP)
|
|
|
|
GoDown();
|
|
|
|
else
|
|
|
|
GoUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform has hit bottom. Stops and waits forever.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::HitBottom()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlatRot::HitBottom();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_FOLLOWING)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// UpdateTrain();
|
|
|
|
m_train->SetTrack(m_trackBottom);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = -1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateAutoTargets(m_toggle_state);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
EnableUse();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Platform has hit bottom. Stops and waits forever.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackChange::HitTop()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-29 20:31:17 +01:00
|
|
|
CFuncPlatRot::HitTop();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_FOLLOWING)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// UpdateTrain();
|
|
|
|
m_train->SetTrack(m_trackTop);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Don't let the plat go back down
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = -1;
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateAutoTargets(m_toggle_state);
|
2013-08-30 13:34:05 -07:00
|
|
|
EnableUse();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CFuncTrackAuto : public CFuncTrackChange
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
|
|
|
void UpdateAutoTargets(int toggleState) override;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_trackautochange, CFuncTrackAuto);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Auto track change
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackAuto::UpdateAutoTargets(int toggleState)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
CPathTrack *pTarget, *pNextTarget;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!m_trackTop || !m_trackBottom)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_targetState == TS_AT_TOP)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pTarget = m_trackTop->GetNext();
|
|
|
|
pNextTarget = m_trackBottom->GetNext();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pTarget = m_trackBottom->GetNext();
|
|
|
|
pNextTarget = m_trackTop->GetNext();
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pTarget)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ClearBits(pTarget->pev->spawnflags, SF_PATH_DISABLED);
|
|
|
|
if (m_code == TRAIN_FOLLOWING && m_train && m_train->pev->speed == 0)
|
|
|
|
m_train->Use(this, this, USE_ON, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pNextTarget)
|
|
|
|
SetBits(pNextTarget->pev->spawnflags, SF_PATH_DISABLED);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncTrackAuto::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CPathTrack* pTarget;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!UseEnabled())
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_toggle_state == TS_AT_TOP)
|
2013-08-30 13:34:05 -07:00
|
|
|
pTarget = m_trackTop;
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (m_toggle_state == TS_AT_BOTTOM)
|
2013-08-30 13:34:05 -07:00
|
|
|
pTarget = m_trackBottom;
|
|
|
|
else
|
|
|
|
pTarget = NULL;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FClassnameIs(pActivator->pev, "func_tracktrain"))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_code = EvaluateTrain(pTarget);
|
2013-08-30 13:34:05 -07:00
|
|
|
// Safe to fire?
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_code == TRAIN_FOLLOWING && m_toggle_state != m_targetState)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
DisableUse();
|
|
|
|
if (m_toggle_state == TS_AT_TOP)
|
|
|
|
GoDown();
|
|
|
|
else
|
|
|
|
GoUp();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pTarget)
|
2013-08-30 13:34:05 -07:00
|
|
|
pTarget = pTarget->GetNext();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pTarget && m_train->m_ppath != pTarget && ShouldToggle(useType, TS_AT_TOP != m_targetState))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_targetState == TS_AT_TOP)
|
2013-08-30 13:34:05 -07:00
|
|
|
m_targetState = TS_AT_BOTTOM;
|
|
|
|
else
|
|
|
|
m_targetState = TS_AT_TOP;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateAutoTargets(m_targetState);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// pev->speed is the travel speed
|
|
|
|
// pev->health is current health
|
|
|
|
// pev->max_health is the amount to reset to each time it starts
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define FGUNTARGET_START_ON 0x0001
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
class CGunTarget : public CBaseMonster
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Activate() override;
|
|
|
|
void EXPORT Next();
|
|
|
|
void EXPORT Start();
|
|
|
|
void EXPORT Wait();
|
|
|
|
void Stop() override;
|
2021-03-05 20:54:33 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
int BloodColor() override { return DONT_BLEED; }
|
|
|
|
int Classify() override { return CLASS_MACHINE; }
|
|
|
|
bool TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
|
|
|
Vector BodyTarget(const Vector& posSrc) override { return pev->origin; }
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2021-11-28 16:54:48 +01:00
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
private:
|
2021-11-28 16:54:48 +01:00
|
|
|
bool m_on;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_guntarget, CGunTarget);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CGunTarget::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CGunTarget, m_on, FIELD_BOOLEAN),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CGunTarget, CBaseMonster);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CGunTarget::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->speed == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = 100;
|
|
|
|
|
|
|
|
// Don't take damage until "on"
|
|
|
|
pev->takedamage = DAMAGE_NO;
|
|
|
|
pev->flags |= FL_MONSTER;
|
|
|
|
|
2021-11-19 13:43:33 +01:00
|
|
|
m_on = false;
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->max_health = pev->health;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & FGUNTARGET_START_ON) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CGunTarget::Start);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + 0.3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CGunTarget::Activate()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTarg;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// now find our next target
|
|
|
|
pTarg = GetNextTarget();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pTarg)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_hTargetEnt = pTarg;
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CGunTarget::Start()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Use(this, this, USE_ON, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CGunTarget::Next()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
m_hTargetEnt = GetNextTarget();
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTarget = m_hTargetEnt;
|
|
|
|
|
|
|
|
if (!pTarget)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Stop();
|
|
|
|
return;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMoveDone(&CGunTarget::Wait);
|
|
|
|
LinearMove(pTarget->pev->origin - (pev->mins + pev->maxs) * 0.5, pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CGunTarget::Wait()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTarget = m_hTargetEnt;
|
|
|
|
|
|
|
|
if (!pTarget)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Stop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire the pass target if there is one
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pTarget->pev->message))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
FireTargets(STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0);
|
|
|
|
if (FBitSet(pTarget->pev->spawnflags, SF_CORNER_FIREONCE))
|
2013-08-30 13:34:05 -07:00
|
|
|
pTarget->pev->message = 0;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
m_flWait = pTarget->GetDelay();
|
|
|
|
|
|
|
|
pev->target = pTarget->pev->target;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CGunTarget::Next);
|
2013-08-30 13:34:05 -07:00
|
|
|
if (m_flWait != 0)
|
2021-11-28 16:54:48 +01:00
|
|
|
{ // -1 wait will wait forever!
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + m_flWait;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Next(); // do it RIGHT now!
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CGunTarget::Stop()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->velocity = g_vecZero;
|
|
|
|
pev->nextthink = 0;
|
|
|
|
pev->takedamage = DAMAGE_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bool CGunTarget::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->health > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->health -= flDamage;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->health <= 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->health = 0;
|
|
|
|
Stop();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->message))
|
|
|
|
FireTargets(STRING(pev->message), this, this, USE_TOGGLE, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
return false;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void CGunTarget::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!ShouldToggle(useType, m_on))
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_on)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Stop();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->takedamage = DAMAGE_AIM;
|
|
|
|
m_hTargetEnt = GetNextTarget();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_hTargetEnt == NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
pev->health = pev->max_health;
|
|
|
|
Next();
|
|
|
|
}
|
|
|
|
}
|