1 Star 0 Fork 0

yoyojacky/CSMoE

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
plats.cpp 48.79 KB
一键复制 编辑 原始数据 按行查看 历史
萌殇小白白๑ 提交于 2019-08-03 00:18 +08:00 . fix chrono warnings
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "trains.h"
#include "saverestore.h"
#include "plats.h"
namespace sv {
/*
* Globals initialization
*/
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),
};
TYPEDESCRIPTION CFuncPlatRot::m_SaveData[] =
{
DEFINE_FIELD(CFuncPlatRot, m_end, FIELD_VECTOR),
DEFINE_FIELD(CFuncPlatRot, m_start, FIELD_VECTOR),
};
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),
};
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),
};
TYPEDESCRIPTION CGunTarget::m_SaveData[] =
{
DEFINE_FIELD(CGunTarget, m_on, FIELD_BOOLEAN),
};
IMPLEMENT_SAVERESTORE(CBasePlatTrain, CBaseToggle);
void CBasePlatTrain::KeyValue(KeyValueData *pkvd)
{
if (FStrEq(pkvd->szKeyName, "lip")) {
m_flLip = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "wait")) {
m_flWait = Q_atof(pkvd->szValue) * 1s;
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "height")) {
m_flHeight = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "rotation")) {
m_vecFinalAngle.x = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "movesnd")) {
m_bMoveSnd = (BYTE) Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "stopsnd")) {
m_bStopSnd = (BYTE) Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "volume")) {
m_volume = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else
CBaseToggle::KeyValue(pkvd);
}
#define noiseMoving noise
#define noiseArrived noise1
void CBasePlatTrain::Precache()
{
// set the plat's "in-motion" sound
switch (m_bMoveSnd) {
case 0:
pev->noiseMoving = MAKE_STRING("common/null.wav");
break;
case 1:
PRECACHE_SOUND("plats/bigmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/bigmove1.wav");
break;
case 2:
PRECACHE_SOUND("plats/bigmove2.wav");
pev->noiseMoving = MAKE_STRING("plats/bigmove2.wav");
break;
case 3:
PRECACHE_SOUND("plats/elevmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/elevmove1.wav");
break;
case 4:
PRECACHE_SOUND("plats/elevmove2.wav");
pev->noiseMoving = MAKE_STRING("plats/elevmove2.wav");
break;
case 5:
PRECACHE_SOUND("plats/elevmove3.wav");
pev->noiseMoving = MAKE_STRING("plats/elevmove3.wav");
break;
case 6:
PRECACHE_SOUND("plats/freightmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/freightmove1.wav");
break;
case 7:
PRECACHE_SOUND("plats/freightmove2.wav");
pev->noiseMoving = MAKE_STRING("plats/freightmove2.wav");
break;
case 8:
PRECACHE_SOUND("plats/heavymove1.wav");
pev->noiseMoving = MAKE_STRING("plats/heavymove1.wav");
break;
case 9:
PRECACHE_SOUND("plats/rackmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/rackmove1.wav");
break;
case 10:
PRECACHE_SOUND("plats/railmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/railmove1.wav");
break;
case 11:
PRECACHE_SOUND("plats/squeekmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/squeekmove1.wav");
break;
case 12:
PRECACHE_SOUND("plats/talkmove1.wav");
pev->noiseMoving = MAKE_STRING("plats/talkmove1.wav");
break;
case 13:
PRECACHE_SOUND("plats/talkmove2.wav");
pev->noiseMoving = MAKE_STRING("plats/talkmove2.wav");
break;
default:
pev->noiseMoving = MAKE_STRING("common/null.wav");
break;
}
// set the plat's 'reached destination' stop sound
switch (m_bStopSnd) {
case 0:
pev->noiseArrived = MAKE_STRING("common/null.wav");
break;
case 1:
PRECACHE_SOUND("plats/bigstop1.wav");
pev->noiseArrived = MAKE_STRING("plats/bigstop1.wav");
break;
case 2:
PRECACHE_SOUND("plats/bigstop2.wav");
pev->noiseArrived = MAKE_STRING("plats/bigstop2.wav");
break;
case 3:
PRECACHE_SOUND("plats/freightstop1.wav");
pev->noiseArrived = MAKE_STRING("plats/freightstop1.wav");
break;
case 4:
PRECACHE_SOUND("plats/heavystop2.wav");
pev->noiseArrived = MAKE_STRING("plats/heavystop2.wav");
break;
case 5:
PRECACHE_SOUND("plats/rackstop1.wav");
pev->noiseArrived = MAKE_STRING("plats/rackstop1.wav");
break;
case 6:
PRECACHE_SOUND("plats/railstop1.wav");
pev->noiseArrived = MAKE_STRING("plats/railstop1.wav");
break;
case 7:
PRECACHE_SOUND("plats/squeekstop1.wav");
pev->noiseArrived = MAKE_STRING("plats/squeekstop1.wav");
break;
case 8:
PRECACHE_SOUND("plats/talkstop1.wav");
pev->noiseArrived = MAKE_STRING("plats/talkstop1.wav");
break;
default:
pev->noiseArrived = MAKE_STRING("common/null.wav");
break;
}
}
void CFuncPlat::CallGoDown()
{
GoDown();
}
void CFuncPlat::CallHitTop()
{
HitTop();
}
void CFuncPlat::CallHitBottom()
{
HitBottom();
}
LINK_ENTITY_TO_CLASS(func_plat, CFuncPlat);
#define noiseMovement noise
#define noiseStopMoving noise1
// 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
void CFuncPlat::Setup()
{
if (m_flTLength == 0)
m_flTLength = 80;
if (m_flTWidth == 0)
m_flTWidth = 10;
pev->angles = g_vecZero;
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
// set size and link into world
UTIL_SetOrigin(pev, pev->origin);
UTIL_SetSize(pev, pev->mins, pev->maxs);
SET_MODEL(ENT(pev), STRING(pev->model));
// 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;
}
if (m_volume == 0) {
m_volume = 0.85;
}
}
void CFuncPlat::Precache()
{
CBasePlatTrain::Precache();
if (!IsTogglePlat()) {
// the "start moving" trigger
PlatSpawnInsideTrigger(pev);
}
}
void CFuncPlat::Spawn()
{
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.
if (!FStringNull(pev->targetname)) {
UTIL_SetOrigin(pev, m_vecPosition1);
m_toggle_state = TS_AT_TOP;
SetUse(&CFuncPlat::PlatUse);
} else {
UTIL_SetOrigin(pev, m_vecPosition2);
m_toggle_state = TS_AT_BOTTOM;
}
}
void PlatSpawnInsideTrigger(entvars_t *pevPlatform)
{
CreateClassPtr<CPlatTrigger>()->SpawnInsideTrigger(GetClassPtr<CFuncPlat>(pevPlatform));
}
// Create a trigger entity for a platform.
void CPlatTrigger::SpawnInsideTrigger(CFuncPlat *pPlatform)
{
m_pPlatform = pPlatform;
// Create trigger entity, "point" it at the owning platform, give it a touch method
pev->solid = SOLID_TRIGGER;
pev->movetype = MOVETYPE_NONE;
pev->origin = pPlatform->pev->origin;
// Establish the trigger field's size
Vector vecTMin = m_pPlatform->pev->mins + Vector(25, 25, 0);
Vector vecTMax = m_pPlatform->pev->maxs + Vector(25, 25, 8);
vecTMin.z = vecTMax.z - (m_pPlatform->m_vecPosition1.z - m_pPlatform->m_vecPosition2.z + 8);
if (m_pPlatform->pev->size.x <= 50) {
vecTMin.x = (m_pPlatform->pev->mins.x + m_pPlatform->pev->maxs.x) / 2;
vecTMax.x = vecTMin.x + 1;
}
if (m_pPlatform->pev->size.y <= 50) {
vecTMin.y = (m_pPlatform->pev->mins.y + m_pPlatform->pev->maxs.y) / 2;
vecTMax.y = vecTMin.y + 1;
}
UTIL_SetSize(pev, vecTMin, vecTMax);
}
void CPlatTrigger::Touch(CBaseEntity *pOther)
{
// Ignore touches by non-players
entvars_t *pevToucher = pOther->pev;
if (!FClassnameIs(pevToucher, "player")) {
return;
}
// Ignore touches by corpses
if (!pOther->IsAlive())
return;
// Make linked platform go up/down.
if (m_pPlatform->m_toggle_state == TS_AT_BOTTOM) {
m_pPlatform->GoUp();
} else if (m_pPlatform->m_toggle_state == TS_AT_TOP) {
// delay going down
m_pPlatform->pev->nextthink = m_pPlatform->pev->ltime + 1s;
}
}
// Used by SUB_UseTargets, when a platform is the target of a button.
// Start bringing platform down.
void CFuncPlat::PlatUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if (IsTogglePlat()) {
// Top is off, bottom is on
BOOL on = (m_toggle_state == TS_AT_BOTTOM) ? TRUE : FALSE;
if (!ShouldToggle(useType, on))
return;
if (m_toggle_state == TS_AT_TOP) {
GoDown();
} else if (m_toggle_state == TS_AT_BOTTOM) {
GoUp();
}
} else {
SetUse(NULL);
if (m_toggle_state == TS_AT_TOP) {
GoDown();
}
}
}
// Platform is at top, now starts moving down.
void CFuncPlat::GoDown()
{
if (pev->noiseMovement) {
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.
void CFuncPlat::HitBottom()
{
if (pev->noiseMovement) {
STOP_SOUND(ENT(pev), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
if (pev->noiseStopMoving) {
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
void CFuncPlat::GoUp()
{
if (pev->noiseMovement) {
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char *) STRING(pev->noiseMovement), m_volume, ATTN_NORM);
}
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.
void CFuncPlat::HitTop()
{
if (pev->noiseMovement) {
STOP_SOUND(ENT(pev), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
if (pev->noiseStopMoving) {
EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char *) STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
}
assert(m_toggle_state == TS_GOING_UP);
m_toggle_state = TS_AT_TOP;
if (!IsTogglePlat()) {
// After a delay, the platform will automatically start going down again.
SetThink(&CFuncPlat::CallGoDown);
pev->nextthink = pev->ltime + 3s;
}
}
void CFuncPlat::Blocked(CBaseEntity *pOther)
{
ALERT(at_aiconsole, "%s Blocked by %s\n", STRING(pev->classname), STRING(pOther->pev->classname));
// Hurt the blocker a little
pOther->TakeDamage(pev, pev, 1, DMG_CRUSH);
if (pev->noiseMovement) {
STOP_SOUND(ENT(pev), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
// 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) {
GoUp();
}
}
LINK_ENTITY_TO_CLASS(func_platrot, CFuncPlatRot);
IMPLEMENT_SAVERESTORE(CFuncPlatRot, CFuncPlat);
void CFuncPlatRot::SetupRotation()
{
// This plat rotates too!
if (m_vecFinalAngle.x != 0) {
CBaseToggle::AxisDir(pev);
m_start = pev->angles;
m_end = pev->angles + pev->movedir * m_vecFinalAngle.x;
} else {
m_start = g_vecZero;
m_end = g_vecZero;
}
// Start at top
if (!FStringNull(pev->targetname)) {
pev->angles = m_end;
}
}
void CFuncPlatRot::Spawn()
{
CFuncPlat::Spawn();
SetupRotation();
}
void CFuncPlatRot::GoDown()
{
CFuncPlat::GoDown();
RotMove(m_start, pev->nextthink - pev->ltime);
}
// Platform has hit bottom. Stops and waits forever.
void CFuncPlatRot::HitBottom()
{
CFuncPlat::HitBottom();
pev->avelocity = g_vecZero;
pev->angles = m_start;
}
// Platform is at bottom, now starts moving up
void CFuncPlatRot::GoUp()
{
CFuncPlat::GoUp();
RotMove(m_end, pev->nextthink - pev->ltime);
}
// Platform has hit top. Pauses, then starts back down again.
void CFuncPlatRot::HitTop()
{
CFuncPlat::HitTop();
pev->avelocity = g_vecZero;
pev->angles = m_end;
}
void CFuncPlatRot::RotMove(Vector &destAngle, duration_t time)
{
// 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.
if (time >= 0.1s) {
pev->avelocity = vecDestDelta / (time / 1s);
} else {
pev->avelocity = vecDestDelta;
pev->nextthink = pev->ltime + 1s;
}
}
LINK_ENTITY_TO_CLASS(func_train, CFuncTrain);
IMPLEMENT_SAVERESTORE(CFuncTrain, CBasePlatTrain);
void CFuncTrain::KeyValue(KeyValueData *pkvd)
{
if (FStrEq(pkvd->szKeyName, "sounds")) {
m_sounds = Q_atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
} else
CBasePlatTrain::KeyValue(pkvd);
}
void CFuncTrain::Blocked(CBaseEntity *pOther)
{
if (gpGlobals->time < m_flActivateFinished)
return;
m_flActivateFinished = gpGlobals->time + 0.5s;
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);
}
void CFuncTrain::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if (pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER) {
// 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
if (pev->enemy) {
pev->target = pev->enemy->v.targetname;
}
pev->nextthink = invalid_time_point;
pev->velocity = g_vecZero;
if (pev->noiseStopMoving) {
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char *) STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
}
}
}
void CFuncTrain::Wait()
{
if (m_pevCurrentTarget != NULL) {
// Fire the pass target if there is one
if (m_pevCurrentTarget->message) {
FireTargets(STRING(m_pevCurrentTarget->message), this, this, USE_TOGGLE, 0);
if (m_pevCurrentTarget->spawnflags & SF_CORNER_FIREONCE) {
m_pevCurrentTarget->message = 0;
}
}
// need pointer to LAST target.
if ((m_pevCurrentTarget->spawnflags & SF_TRAIN_WAIT_RETRIGGER) || (pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER)) {
pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER;
// clear the sound channel.
if (pev->noiseMovement) {
STOP_SOUND(edict(), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
if (pev->noiseStopMoving) {
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char *) STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
}
pev->nextthink = invalid_time_point;
return;
}
}
if (m_flWait != 0s) {
// -1 wait will wait forever!
pev->nextthink = pev->ltime + m_flWait;
if (pev->noiseMovement) {
STOP_SOUND(edict(), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
if (pev->noiseStopMoving) {
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char *) STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
}
SetThink(&CFuncTrain::Next);
} else {
// do it RIGHT now!
Next();
}
}
// Train next - path corner needs to change to next target
void CFuncTrain::Next()
{
CBaseEntity *pTarg;
// now find our next target
pTarg = GetNextTarget();
if (!pTarg) {
if (pev->noiseMovement) {
STOP_SOUND(edict(), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
// Play stop sound
if (pev->noiseStopMoving) {
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char *) STRING(pev->noiseStopMoving), m_volume, ATTN_NORM);
}
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();
if (m_pevCurrentTarget != NULL && 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);
}
// keep track of this since path corners change our target for us.
m_pevCurrentTarget = pTarg->pev;
//hack
pev->enemy = pTarg->edict();
if (m_pevCurrentTarget->spawnflags & SF_CORNER_TELEPORT) {
// Path corner has indicated a teleport to the next corner.
pev->effects |= EF_NOINTERP;
UTIL_SetOrigin(pev, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5);
// Get on with doing the next path corner.
Wait();
} else {
// Normal linear move.
// 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).
if (pev->noiseMovement) {
STOP_SOUND(edict(), CHAN_STATIC, (char *) STRING(pev->noiseMovement));
}
if (pev->noiseMovement) {
EMIT_SOUND(ENT(pev), CHAN_STATIC, (char *) STRING(pev->noiseMovement), m_volume, ATTN_NORM);
}
pev->effects &= ~EF_NOINTERP;
SetMoveDone(&CFuncTrain::Wait);
LinearMove(pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5, pev->speed);
}
}
void CFuncTrain::Activate()
{
// Not yet active, so teleport to first target
if (!m_activated) {
m_activated = TRUE;
entvars_t *pevTarg = VARS(FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->target)));
pev->target = pevTarg->target;
// keep track of this since path corners change our target for us.
m_pevCurrentTarget = pevTarg;
UTIL_SetOrigin(pev, pevTarg->origin - (pev->mins + pev->maxs) * 0.5);
if (FStringNull(pev->targetname)) {
// not triggered, so start immediately
pev->nextthink = pev->ltime + 0.1s;
SetThink(&CFuncTrain::Next);
} 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
void CFuncTrain::Spawn()
{
Precache();
if (pev->speed == 0)
pev->speed = 100;
if (FStringNull(pev->target)) {
ALERT(at_console, "FuncTrain with no target");
}
m_pevFirstTarget = m_pevCurrentTarget;
m_vStartPosition = pev->origin;
if (pev->dmg == 0)
pev->dmg = 2;
pev->movetype = MOVETYPE_PUSH;
if (pev->spawnflags & SF_TRACKTRAIN_PASSABLE)
pev->solid = SOLID_NOT;
else
pev->solid = SOLID_BSP;
SET_MODEL(ENT(pev), STRING(pev->model));
UTIL_SetSize(pev, pev->mins, pev->maxs);
UTIL_SetOrigin(pev, pev->origin);
m_activated = FALSE;
if (m_volume == 0)
m_volume = 0.85;
}
void CFuncTrain::Restart()
{
if (pev->speed == 0)
pev->speed = 100;
if (pev->dmg == 0)
pev->dmg = 2;
pev->movetype = MOVETYPE_PUSH;
m_pevCurrentTarget = m_pevFirstTarget;
UTIL_SetOrigin(pev, m_vStartPosition);
m_activated = FALSE;
if (m_volume == 0)
m_volume = 0.85;
}
void CFuncTrain::Precache()
{
CBasePlatTrain::Precache();
}
void CFuncTrain::OverrideReset()
{
CBaseEntity *pTarg;
// Are we moving?
if (pev->velocity != g_vecZero && pev->nextthink != invalid_time_point) {
pev->target = pev->message;
// now find our next target
pTarg = GetNextTarget();
if (!pTarg) {
pev->nextthink = invalid_time_point;
pev->velocity = g_vecZero;
} else // Keep moving for 0.1 secs, then find path_corner again and restart
{
SetThink(&CFuncTrain::Next);
pev->nextthink = pev->ltime + 0.1s;
}
}
}
IMPLEMENT_SAVERESTORE(CFuncTrackTrain, CBaseEntity);
LINK_ENTITY_TO_CLASS(func_tracktrain, CFuncTrackTrain);
void CFuncTrackTrain::KeyValue(KeyValueData *pkvd)
{
if (FStrEq(pkvd->szKeyName, "wheels")) {
m_length = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "height")) {
m_height = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "startspeed")) {
m_startSpeed = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "sounds")) {
m_sounds = Q_atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "volume")) {
m_flVolume = (float) Q_atoi(pkvd->szValue);
m_flVolume *= 0.1;
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "bank")) {
m_flBank = Q_atof(pkvd->szValue);
pkvd->fHandled = TRUE;
} else
CBaseEntity::KeyValue(pkvd);
}
void CFuncTrackTrain::NextThink(time_point_t thinkTime, BOOL alwaysThink)
{
if (alwaysThink)
pev->flags |= FL_ALWAYSTHINK;
else
pev->flags &= ~FL_ALWAYSTHINK;
pev->nextthink = thinkTime;
}
void CFuncTrackTrain::Blocked(CBaseEntity *pOther)
{
entvars_t *pevOther = pOther->pev;
// Blocker is on-ground on the train
if ((pevOther->flags & FL_ONGROUND) && VARS(pevOther->groundentity) == pev) {
float deltaSpeed = fabs((float) pev->speed);
if (deltaSpeed > 50) {
deltaSpeed = 50;
}
if (!pevOther->velocity.z) {
pevOther->velocity.z += deltaSpeed;
}
return;
} else
pevOther->velocity = (pevOther->origin - pev->origin).Normalize() * pev->dmg;
ALERT(at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING(pev->targetname),
STRING(pOther->pev->classname), pev->dmg);
// TODO: is missing?
#if 0
if (pev->dmg <= 0)
return;
// we can't hurt this thing, so we're not concerned with it
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);
#endif
}
void CFuncTrackTrain::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if (useType != USE_SET) {
if (!ShouldToggle(useType, (pev->speed != 0)))
return;
if (pev->speed == 0) {
pev->speed = m_speed * m_dir;
Next();
} else {
pev->speed = 0;
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
StopSound();
SetThink(NULL);
}
} else {
float delta = ((int) (pev->speed * 4) / (int) m_speed) * 0.25 + 0.25 * value;
if (delta > 1)
delta = 1;
else if (delta < -1)
delta = -1;
if (pev->spawnflags & SF_TRACKTRAIN_FORWARDONLY) {
if (delta < 0)
delta = 0;
}
pev->speed = m_speed * delta;
Next();
ALERT(at_aiconsole, "TRAIN(%s), speed to %.2f\n", STRING(pev->targetname), pev->speed);
}
}
float Fix(float v)
{
float angle = v;
while (angle < 0)
angle += 360;
while (angle > 360)
angle -= 360;
return angle;
}
void FixupAngles(Vector &v)
{
v.x = Fix(v.x);
v.y = Fix(v.y);
v.z = Fix(v.z);
}
void CFuncTrackTrain::StopSound()
{
// if sound playing, stop it
if (m_soundPlaying && pev->noise) {
unsigned short us_encode;
unsigned short us_sound = ((unsigned short) (m_sounds) & 0x0007) << 12;
us_encode = us_sound;
PLAYBACK_EVENT_FULL(FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, (float *) &g_vecZero,
(float *) &g_vecZero, 0.0, 0.0, us_encode, 0, 1, 0);
EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_brake1.wav", m_flVolume, ATTN_NORM, 0, 100);
}
m_soundPlaying = 0;
}
void CFuncTrackTrain::UpdateSound()
{
float flpitch;
if (!pev->noise)
return;
flpitch = TRAIN_STARTPITCH + (abs(pev->speed) * (TRAIN_MAXPITCH - TRAIN_STARTPITCH) / TRAIN_MAXSPEED);
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);
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *) STRING(pev->noise), m_flVolume, ATTN_NORM, 0, (int) flpitch);
m_soundPlaying = 1;
} else {
// 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;
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);
us_encode = us_sound | us_pitch | us_volume;
PLAYBACK_EVENT_FULL(FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, (float *) &g_vecZero,
(float *) &g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0);
}
}
void CFuncTrackTrain::Next()
{
auto time = 0.5s;
if (!pev->speed) {
ALERT(at_aiconsole, "TRAIN(%s): Speed is 0\n", STRING(pev->targetname));
StopSound();
return;
}
if (!m_ppath) {
ALERT(at_aiconsole, "TRAIN(%s): Lost path\n", STRING(pev->targetname));
StopSound();
return;
}
UpdateSound();
Vector nextPos = pev->origin;
nextPos.z -= m_height;
CPathTrack *pnext = m_ppath->LookAhead(&nextPos, pev->speed * 0.1, 1);
nextPos.z += m_height;
pev->velocity = (nextPos - pev->origin) * 10;
Vector nextFront = pev->origin;
nextFront.z -= m_height;
if (m_length > 0)
m_ppath->LookAhead(&nextFront, m_length, 0);
else
m_ppath->LookAhead(&nextFront, 100, 0);
nextFront.z += m_height;
Vector delta = nextFront - pev->origin;
Vector angles = UTIL_VecToAngles(delta);
float fixAngleY = angles.y + 180.0f;
// The train actually points west
angles.y += fixAngleY;
// TODO: All of this crap has to be done to make the angles not wrap around, revisit this.
FixupAngles(angles);
FixupAngles(pev->angles);
if (!pnext || (delta.x == 0 && delta.y == 0))
angles = pev->angles;
float vy, vx;
if (!(pev->spawnflags & SF_TRACKTRAIN_NOPITCH))
vx = UTIL_AngleDistance(angles.x, pev->angles.x);
else
vx = 0;
vy = UTIL_AngleDistance(angles.y, pev->angles.y);
pev->avelocity.y = vy * 10;
pev->avelocity.x = vx * 10;
if (m_flBank != 0) {
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);
} else {
pev->avelocity.z =
UTIL_AngleDistance(UTIL_ApproachAngle(0, pev->angles.z, m_flBank * 4), pev->angles.z) * 4;
}
}
if (pnext != NULL) {
if (pnext != m_ppath) {
CPathTrack *pFire;
if (pev->speed >= 0)
pFire = pnext;
else
pFire = m_ppath;
m_ppath = pnext;
// Fire the pass target if there is one
if (!FStringNull(pFire->pev->message)) {
FireTargets(STRING(pFire->pev->message), this, this, USE_TOGGLE, 0);
if (pFire->pev->spawnflags & SF_PATH_FIREONCE) {
pFire->pev->message = 0;
}
}
if (pFire->pev->spawnflags & SF_PATH_DISABLE_TRAIN) {
pev->spawnflags |= SF_TRACKTRAIN_NOCONTROL;
}
// Don't override speed if under user control
if (pev->spawnflags & SF_TRACKTRAIN_NOCONTROL) {
if (pFire->pev->speed != 0) {
// don't copy speed from target if it is 0 (uninitialized)
pev->speed = pFire->pev->speed;
ALERT(at_aiconsole, "TrackTrain %s speed to %4.2f\n", STRING(pev->targetname), pev->speed);
}
}
}
SetThink(&CFuncTrackTrain::Next);
NextThink(pev->ltime + time, TRUE);
} else // end of path, stop
{
StopSound();
pev->velocity = (nextPos - pev->origin);
pev->avelocity = g_vecZero;
float distance = pev->velocity.Length();
m_oldSpeed = pev->speed;
pev->speed = 0;
// Move to the dead end
// Are we there yet?
if (distance > 0) {
// no, how long to get there?
time = distance / m_oldSpeed * 1s;
pev->velocity = pev->velocity * (m_oldSpeed / distance);
SetThink(&CFuncTrackTrain::DeadEnd);
NextThink(pev->ltime + time, FALSE);
} else {
DeadEnd();
}
}
}
void CFuncTrackTrain::DeadEnd()
{
// Fire the dead-end target if there is one
CPathTrack *pTrack, *pNext;
pTrack = m_ppath;
ALERT(at_aiconsole, "TRAIN(%s): Dead end ", STRING(pev->targetname));
// 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.
if (pTrack != NULL) {
if (m_oldSpeed < 0) {
do {
pNext = pTrack->ValidPath(pTrack->GetPrevious(), TRUE);
if (pNext) {
pTrack = pNext;
}
} while (pNext != NULL);
} else {
do {
pNext = pTrack->ValidPath(pTrack->GetNext(), TRUE);
if (pNext) {
pTrack = pNext;
}
} while (pNext != NULL);
}
}
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
if (pTrack != NULL) {
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);
}
} else
ALERT(at_aiconsole, "\n");
}
void CFuncTrackTrain::SetControls(entvars_t *pevControls)
{
Vector offset = pevControls->origin - pev->oldorigin;
m_controlMins = pevControls->mins + offset;
m_controlMaxs = pevControls->maxs + offset;
}
BOOL CFuncTrackTrain::OnControls(entvars_t *pevTest)
{
Vector offset = pevTest->origin - pev->origin;
if (pev->spawnflags & SF_TRACKTRAIN_NOCONTROL)
return FALSE;
// Transform offset into local coordinates
UTIL_MakeVectors(pev->angles);
Vector local;
local.x = DotProduct(offset, gpGlobals->v_forward);
local.y = -DotProduct(offset, gpGlobals->v_right);
local.z = DotProduct(offset, gpGlobals->v_up);
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;
return FALSE;
}
void CFuncTrackTrain::Find()
{
m_ppath = CPathTrack::Instance(FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->target)));
if (!m_ppath) {
return;
}
entvars_t *pevTarget = m_ppath->pev;
if (!FClassnameIs(pevTarget, "path_track")) {
ALERT(at_error, "func_track_train must be on a path of path_track\n");
m_ppath = NULL;
return;
}
Vector nextPos = pevTarget->origin;
nextPos.z += m_height;
Vector look = nextPos;
look.z -= m_height;
m_ppath->LookAhead(&look, m_length, 0);
look.z += m_height;
pev->angles = UTIL_VecToAngles(look - nextPos);
// The train actually points west
pev->angles.y += 180;
if (pev->spawnflags & SF_TRACKTRAIN_NOPITCH) {
pev->angles.x = 0;
}
UTIL_SetOrigin(pev, nextPos);
NextThink(pev->ltime + 0.1s, FALSE);
SetThink(&CFuncTrackTrain::Next);
pev->speed = m_startSpeed;
UpdateSound();
}
void CFuncTrackTrain::NearestPath()
{
CBaseEntity *pTrack = NULL;
CBaseEntity *pNearest = NULL;
float dist;
float closest;
closest = 1024;
while ((pTrack = UTIL_FindEntityInSphere(pTrack, pev->origin, 1024)) != NULL) {
// filter out non-tracks
if (!(pTrack->pev->flags & (FL_CLIENT | FL_MONSTER)) && FClassnameIs(pTrack->pev, "path_track")) {
dist = (pev->origin - pTrack->pev->origin).Length();
if (dist < closest) {
closest = dist;
pNearest = pTrack;
}
}
}
if (!pNearest) {
ALERT(at_console, "Can't find a nearby track !!!\n");
SetThink(NULL);
return;
}
ALERT(at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING(pev->targetname), STRING(pNearest->pev->targetname));
// If I'm closer to the next path_track on this path, then it's my real path
pTrack = ((CPathTrack *) pNearest)->GetNext();
if (pTrack != NULL) {
if ((pev->origin - pTrack->pev->origin).Length() < (pev->origin - pNearest->pev->origin).Length()) {
pNearest = pTrack;
}
}
m_ppath = static_cast<CPathTrack *>(pNearest);
if (pev->speed != 0) {
NextThink(pev->ltime + 0.1s, FALSE);
SetThink(&CFuncTrackTrain::Next);
}
}
void CFuncTrackTrain::OverrideReset()
{
NextThink(pev->ltime + 0.1s, FALSE);
SetThink(&CFuncTrackTrain::NearestPath);
}
CFuncTrackTrain *CFuncTrackTrain::Instance(edict_t *pent)
{
if (FClassnameIs(pent, "func_tracktrain")) {
return (CFuncTrackTrain *) GET_PRIVATE(pent);
}
return NULL;
}
void CFuncTrackTrain::Spawn()
{
if (pev->speed == 0)
m_speed = 165;
else
m_speed = pev->speed;
ALERT(at_console, "M_speed = %f\n", m_speed);
pev->speed = 0;
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
pev->impulse = (int) m_speed;
m_dir = 1;
if (FStringNull(pev->target)) {
ALERT(at_console, "FuncTrain with no target");
}
if (pev->spawnflags & SF_TRACKTRAIN_PASSABLE)
pev->solid = SOLID_NOT;
else
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
SET_MODEL(ENT(pev), STRING(pev->model));
UTIL_SetSize(pev, pev->mins, pev->maxs);
UTIL_SetOrigin(pev, pev->origin);
// Cache off placed origin for train controls
pev->oldorigin = pev->origin;
m_controlMins = pev->mins;
m_controlMaxs = pev->maxs;
m_controlMaxs.z += 72;
// start trains on the next frame, to make sure their targets have had
// a chance to spawn/activate
NextThink(pev->ltime + 0.1s, FALSE);
SetThink(&CFuncTrackTrain::Find);
Precache();
}
void CFuncTrackTrain::Restart()
{
ALERT(at_console, "M_speed = %f\n", m_speed);
pev->speed = 0;
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
pev->impulse = (int) m_speed;
m_dir = 1;
if (FStringNull(pev->target)) {
ALERT(at_console, "FuncTrain with no target");
}
UTIL_SetOrigin(pev, pev->oldorigin);
NextThink(pev->ltime + 0.1s, FALSE);
SetThink(&CFuncTrackTrain::Find);
}
void CFuncTrackTrain::Precache()
{
if (m_flVolume == 0.0)
m_flVolume = 1.0;
switch (m_sounds) {
default:
// no sound
pev->noise = 0;
break;
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;
}
PRECACHE_SOUND("plats/ttrain_brake1.wav");
PRECACHE_SOUND("plats/ttrain_start1.wav");
m_usAdjustPitch = PRECACHE_EVENT(1, "events/train.sc");
}
LINK_ENTITY_TO_CLASS(func_traincontrols, CFuncTrainControls);
void CFuncTrainControls::Find()
{
edict_t *pTarget = NULL;
do {
pTarget = FIND_ENTITY_BY_TARGETNAME(pTarget, STRING(pev->target));
} while (!FNullEnt(pTarget) && !FClassnameIs(pTarget, "func_tracktrain"));
if (FNullEnt(pTarget)) {
ALERT(at_console, "No train %s\n", STRING(pev->target));
return;
}
CFuncTrackTrain *ptrain = CFuncTrackTrain::Instance(pTarget);
ptrain->SetControls(pev);
UTIL_Remove(this);
}
void CFuncTrainControls::Spawn()
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
SET_MODEL(ENT(pev), STRING(pev->model));
UTIL_SetSize(pev, pev->mins, pev->maxs);
UTIL_SetOrigin(pev, pev->origin);
SetThink(&CFuncTrainControls::Find);
pev->nextthink = gpGlobals->time;
}
typedef enum
{
TRAIN_SAFE, TRAIN_BLOCKING, TRAIN_FOLLOWING
} TRAIN_CODE;
class CFuncTrackChange : public CFuncPlatRot
{
public:
virtual void Spawn(void);
virtual void Precache(void);
virtual void EXPORT GoUp(void);
virtual void EXPORT GoDown(void);
virtual void KeyValue(KeyValueData *pkvd);
virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
virtual void HitBottom(void);
virtual void HitTop(void);
virtual void UpdateAutoTargets(int toggleState);
virtual BOOL IsTogglePlat(void) { return TRUE; }
virtual void Touch(CBaseEntity *pOther);
virtual int Save(CSave &save);
virtual int Restore(CRestore &restore);
virtual void OverrideReset(void);
public:
TRAIN_CODE EvaluateTrain(CPathTrack *pcurrent);
void UpdateTrain(Vector &dest);
void EXPORT Find(void);
void DisableUse(void) { m_use = 0; }
void EnableUse(void) { m_use = 1; }
int UseEnabled(void) { return m_use; }
public:
static TYPEDESCRIPTION m_SaveData[];
public:
CPathTrack *m_trackTop;
CPathTrack *m_trackBottom;
CFuncTrackTrain *m_train;
int m_trackTopName;
int m_trackBottomName;
int m_trainName;
TRAIN_CODE m_code;
int m_targetState;
int m_use;
};
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_INTEGER),
};
IMPLEMENT_SAVERESTORE(CFuncTrackChange, CFuncPlatRot);
void CFuncTrackChange::Spawn()
{
Setup();
if (pev->spawnflags & SF_TRACK_DONT_MOVE) {
m_vecPosition2.z = pev->origin.z;
}
SetupRotation();
if (pev->spawnflags & SF_TRACK_STARTBOTTOM) {
UTIL_SetOrigin(pev, m_vecPosition2);
m_toggle_state = TS_AT_BOTTOM;
pev->angles = m_start;
m_targetState = TS_AT_TOP;
} else {
UTIL_SetOrigin(pev, m_vecPosition1);
m_toggle_state = TS_AT_TOP;
pev->angles = m_end;
m_targetState = TS_AT_BOTTOM;
}
EnableUse();
pev->nextthink = pev->ltime + 2.0s;
SetThink(&CFuncTrackChange::Find);
Precache();
}
void CFuncTrackChange::Precache()
{
// Can't trigger sound
PRECACHE_SOUND("buttons/button11.wav");
CFuncPlatRot::Precache();
}
// UNDONE: Filter touches before re-evaluating the train.
void CFuncTrackChange::Touch(CBaseEntity *pOther)
{
#if 0
TRAIN_CODE code;
entvars_t *pevToucher = pOther->pev;
#endif
}
void CFuncTrackChange::KeyValue(KeyValueData *pkvd)
{
if (FStrEq(pkvd->szKeyName, "train")) {
m_trainName = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "toptrack")) {
m_trackTopName = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
} else if (FStrEq(pkvd->szKeyName, "bottomtrack")) {
m_trackBottomName = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
} else {
// Pass up to base class
CFuncPlatRot::KeyValue(pkvd);
}
}
void CFuncTrackChange::OverrideReset()
{
pev->nextthink = pev->ltime + 1.0s;
SetThink(&CFuncTrackChange::Find);
}
void CFuncTrackChange::Find()
{
// Find track entities
edict_t *target;
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trackTopName));
if (!FNullEnt(target)) {
m_trackTop = CPathTrack::Instance(target);
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trackBottomName));
if (!FNullEnt(target)) {
m_trackBottom = CPathTrack::Instance(target);
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trainName));
if (!FNullEnt(target)) {
m_train = CFuncTrackTrain::Instance(FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trainName)));
if (!m_train) {
ALERT(at_error, "Can't find train for track change! %s\n", STRING(m_trainName));
return;
}
Vector center = (pev->absmin + pev->absmax) * 0.5;
m_trackBottom = m_trackBottom->Nearest(center);
m_trackTop = m_trackTop->Nearest(center);
UpdateAutoTargets(m_toggle_state);
SetThink(NULL);
return;
} else {
ALERT(at_error, "Can't find train for track change! %s\n", STRING(m_trainName));
target = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_trainName));
}
} else
ALERT(at_error, "Can't find bottom track for track change! %s\n", STRING(m_trackBottomName));
} else
ALERT(at_error, "Can't find top track for track change! %s\n", STRING(m_trackTopName));
}
TRAIN_CODE CFuncTrackChange::EvaluateTrain(CPathTrack *pcurrent)
{
// Go ahead and work, we don't have anything to switch, so just be an elevator
if (!pcurrent || !m_train)
return TRAIN_SAFE;
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)) {
if (m_train->pev->speed != 0)
return TRAIN_BLOCKING;
Vector dist = pev->origin - m_train->pev->origin;
float length = dist.Length2D();
// Empirically determined close distance
if (length < m_train->m_length)
return TRAIN_FOLLOWING;
else if (length > (150 + m_train->m_length))
return TRAIN_SAFE;
return TRAIN_BLOCKING;
}
return TRAIN_SAFE;
}
void CFuncTrackChange::UpdateTrain(Vector &dest)
{
duration_t time = (pev->nextthink - pev->ltime);
m_train->pev->velocity = pev->velocity;
m_train->pev->avelocity = pev->avelocity;
m_train->NextThink(m_train->pev->ltime + time, FALSE);
// Attempt at getting the train to rotate properly around the origin of the trackchange
if (time <= 0s)
return;
Vector offset = m_train->pev->origin - pev->origin;
Vector delta = dest - pev->angles;
// Transform offset into local coordinates
UTIL_MakeInvVectors(delta, gpGlobals);
Vector local;
local.x = DotProduct(offset, gpGlobals->v_forward);
local.y = DotProduct(offset, gpGlobals->v_right);
local.z = DotProduct(offset, gpGlobals->v_up);
local = local - offset;
m_train->pev->velocity = pev->velocity + (local * (1.0s / time));
}
void CFuncTrackChange::GoDown()
{
if (m_code == TRAIN_BLOCKING)
return;
// HitBottom may get called during CFuncPlat::GoDown(), so set up for that
// before you call GoDown()
UpdateAutoTargets(TS_GOING_DOWN);
// If ROTMOVE, move & rotate
if (pev->spawnflags & SF_TRACK_DONT_MOVE) {
SetMoveDone(&CFuncTrackChange::CallHitBottom);
m_toggle_state = TS_GOING_DOWN;
AngularMove(m_start, pev->speed);
} else {
CFuncPlat::GoDown();
SetMoveDone(&CFuncTrackChange::CallHitBottom);
RotMove(m_start, pev->nextthink - pev->ltime);
}
// Otherwise, rotate first, move second
// If the train is moving with the platform, update it
if (m_code == TRAIN_FOLLOWING) {
UpdateTrain(m_start);
m_train->m_ppath = NULL;
}
}
// Platform is at bottom, now starts moving up
void CFuncTrackChange::GoUp()
{
if (m_code == TRAIN_BLOCKING)
return;
// HitTop may get called during CFuncPlat::GoUp(), so set up for that
// before you call GoUp();
UpdateAutoTargets(TS_GOING_UP);
if (pev->spawnflags & SF_TRACK_DONT_MOVE) {
m_toggle_state = TS_GOING_UP;
SetMoveDone(&CFuncTrackChange::CallHitTop);
AngularMove(m_end, pev->speed);
} else {
// If ROTMOVE, move & rotate
CFuncPlat::GoUp();
SetMoveDone(&CFuncTrackChange::CallHitTop);
RotMove(m_end, pev->nextthink - pev->ltime);
}
// Otherwise, move first, rotate second
// If the train is moving with the platform, update it
if (m_code == TRAIN_FOLLOWING) {
UpdateTrain(m_end);
m_train->m_ppath = NULL;
}
}
// Normal track change
void CFuncTrackChange::UpdateAutoTargets(int toggleState)
{
if (!m_trackTop || !m_trackBottom)
return;
if (toggleState == TS_AT_TOP)
m_trackTop->pev->spawnflags &= ~SF_PATH_DISABLED;
else
m_trackTop->pev->spawnflags |= SF_PATH_DISABLED;
if (toggleState == TS_AT_BOTTOM)
m_trackBottom->pev->spawnflags &= ~SF_PATH_DISABLED;
else
m_trackBottom->pev->spawnflags |= SF_PATH_DISABLED;
}
void CFuncTrackChange::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if (m_toggle_state != TS_AT_TOP && m_toggle_state != TS_AT_BOTTOM)
return;
// If train is in "safe" area, but not on the elevator, play alarm sound
switch (m_toggle_state) {
case TS_AT_TOP:
m_code = EvaluateTrain(m_trackTop);
break;
case TS_AT_BOTTOM:
m_code = EvaluateTrain(m_trackBottom);
break;
default:
m_code = TRAIN_BLOCKING;
break;
}
if (m_code == TRAIN_BLOCKING) {
// Play alarm and return
EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/button11.wav", VOL_NORM, 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.
void CFuncTrackChange::HitBottom()
{
CFuncPlatRot::HitBottom();
if (m_code == TRAIN_FOLLOWING) {
//UpdateTrain();
m_train->SetTrack(m_trackBottom);
}
SetThink(NULL);
pev->nextthink = invalid_time_point;
UpdateAutoTargets(m_toggle_state);
EnableUse();
}
// Platform has hit bottom. Stops and waits forever.
void CFuncTrackChange::HitTop()
{
CFuncPlatRot::HitTop();
if (m_code == TRAIN_FOLLOWING) {
//UpdateTrain();
m_train->SetTrack(m_trackTop);
}
// Don't let the plat go back down
SetThink(NULL);
pev->nextthink = invalid_time_point;
UpdateAutoTargets(m_toggle_state);
EnableUse();
}
class CFuncTrackAuto : public CFuncTrackChange
{
public:
virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
virtual void UpdateAutoTargets(int toggleState);
};
LINK_ENTITY_TO_CLASS(func_trackautochange, CFuncTrackAuto);
// Auto track change
void CFuncTrackAuto::UpdateAutoTargets(int toggleState)
{
CPathTrack *pTarget, *pNextTarget;
if (!m_trackTop || !m_trackBottom)
return;
if (m_targetState == TS_AT_TOP) {
pTarget = m_trackTop->GetNext();
pNextTarget = m_trackBottom->GetNext();
} else {
pTarget = m_trackBottom->GetNext();
pNextTarget = m_trackTop->GetNext();
}
if (pTarget != NULL) {
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);
}
}
if (pNextTarget != NULL) {
pNextTarget->pev->spawnflags |= SF_PATH_DISABLED;
}
}
void CFuncTrackAuto::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
CPathTrack *pTarget;
if (!UseEnabled())
return;
switch (m_toggle_state) {
case TS_AT_TOP:
pTarget = m_trackTop;
break;
case TS_AT_BOTTOM:
pTarget = m_trackBottom;
break;
default:
pTarget = NULL;
break;
}
if (FClassnameIs(pActivator->pev, "func_tracktrain")) {
m_code = EvaluateTrain(pTarget);
// Safe to fire?
if (m_code == TRAIN_FOLLOWING && m_toggle_state != m_targetState) {
DisableUse();
if (m_toggle_state == TS_AT_TOP)
GoDown();
else
GoUp();
}
} else {
if (pTarget != NULL) {
pTarget = pTarget->GetNext();
}
if (pTarget != NULL && m_train->m_ppath != pTarget && ShouldToggle(useType, m_targetState)) {
if (m_targetState == TS_AT_TOP)
m_targetState = TS_AT_BOTTOM;
else
m_targetState = TS_AT_TOP;
}
UpdateAutoTargets(m_targetState);
}
}
LINK_ENTITY_TO_CLASS(func_guntarget, CGunTarget);
IMPLEMENT_SAVERESTORE(CGunTarget, CBaseMonster);
void CGunTarget::Spawn()
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
UTIL_SetOrigin(pev, pev->origin);
SET_MODEL(ENT(pev), STRING(pev->model));
if (pev->speed == 0) {
pev->speed = 100;
}
// Don't take damage until "on"
pev->takedamage = DAMAGE_NO;
pev->flags |= FL_MONSTER;
m_on = FALSE;
pev->max_health = pev->health;
if (pev->spawnflags & FGUNTARGET_START_ON) {
SetThink(&CGunTarget::Start);
pev->nextthink = pev->ltime + 0.3s;
}
}
void CGunTarget::Activate()
{
CBaseEntity *pTarg;
// now find our next target
pTarg = GetNextTarget();
if (pTarg != NULL) {
m_hTargetEnt = pTarg;
UTIL_SetOrigin(pev, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5);
}
}
void CGunTarget::Start()
{
Use(this, this, USE_ON, 0);
}
void CGunTarget::Next()
{
SetThink(NULL);
m_hTargetEnt = GetNextTarget();
CBaseEntity *pTarget = m_hTargetEnt;
if (!pTarget) {
Stop();
return;
}
SetMoveDone(&CGunTarget::Wait);
LinearMove(pTarget->pev->origin - (pev->mins + pev->maxs) * 0.5, pev->speed);
}
void CGunTarget::Wait()
{
CBaseEntity *pTarget = m_hTargetEnt;
if (!pTarget) {
Stop();
return;
}
// Fire the pass target if there is one
if (pTarget->pev->message) {
FireTargets(STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0);
if (pTarget->pev->spawnflags & SF_CORNER_FIREONCE) {
pTarget->pev->message = 0;
}
}
m_flWait = pTarget->GetDelay();
pev->target = pTarget->pev->target;
SetThink(&CGunTarget::Next);
if (m_flWait != 0s) {
// -1 wait will wait forever!
pev->nextthink = pev->ltime + m_flWait;
} else {
// do it RIGHT now!
Next();
}
}
void CGunTarget::Stop()
{
pev->velocity = g_vecZero;
pev->nextthink = invalid_time_point;
pev->takedamage = DAMAGE_NO;
}
int CGunTarget::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
{
if (pev->health > 0) {
pev->health -= flDamage;
if (pev->health <= 0) {
pev->health = 0;
Stop();
if (pev->message) {
FireTargets(STRING(pev->message), this, this, USE_TOGGLE, 0);
}
}
}
return 0;
}
void CGunTarget::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if (!ShouldToggle(useType, m_on))
return;
if (m_on) {
Stop();
} else {
pev->takedamage = DAMAGE_AIM;
m_hTargetEnt = GetNextTarget();
if (m_hTargetEnt == nullptr)
return;
pev->health = pev->max_health;
Next();
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/yoyojacky/CSMoE.git
git@gitee.com:yoyojacky/CSMoE.git
yoyojacky
CSMoE
CSMoE
master

搜索帮助