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.
|
|
|
|
*
|
|
|
|
****/
|
|
|
|
/*
|
|
|
|
|
|
|
|
===== subs.cpp ========================================================
|
|
|
|
|
|
|
|
frequently used global functions
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "extdll.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "saverestore.h"
|
|
|
|
#include "nodes.h"
|
|
|
|
#include "doors.h"
|
|
|
|
|
2021-11-19 14:31:11 +01:00
|
|
|
extern bool FEntIsVisible(entvars_t* pev, entvars_t* pevTarget);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Landmark class
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPointEntity::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->solid = SOLID_NOT;
|
2021-11-28 16:54:48 +01:00
|
|
|
// UTIL_SetSize(pev, g_vecZero, g_vecZero);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class CNullEntity : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Null Entity, remove on startup
|
2021-11-29 20:31:17 +01:00
|
|
|
void CNullEntity::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
REMOVE_ENTITY(ENT(pev));
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(info_null, CNullEntity);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
class CBaseDMStart : public CPointEntity
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
|
|
|
bool IsTriggered(CBaseEntity* pEntity) override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
private:
|
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
// These are the new entry points to entities.
|
|
|
|
LINK_ENTITY_TO_CLASS(info_player_deathmatch, CBaseDMStart);
|
|
|
|
LINK_ENTITY_TO_CLASS(info_player_start, CPointEntity);
|
|
|
|
LINK_ENTITY_TO_CLASS(info_landmark, CPointEntity);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bool CBaseDMStart::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "master"))
|
|
|
|
{
|
|
|
|
pev->netname = 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 CPointEntity::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bool CBaseDMStart::IsTriggered(CBaseEntity* pEntity)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
bool master = UTIL_IsMasterTriggered(pev->netname, pEntity);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
return master;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This updates global tables that need to know about entities being removed
|
2021-03-05 20:54:33 +01:00
|
|
|
void CBaseEntity::UpdateOnRemove()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
int i;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->flags, FL_GRAPHED))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// this entity was a LinkEnt in the world node graph, so we must remove it from
|
|
|
|
// the graph since we are removing it from the world.
|
|
|
|
for (i = 0; i < WorldGraph.m_cLinks; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (WorldGraph.m_pLinkPool[i].m_pLinkEnt == pev)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// if this link has a link ent which is the same ent that is removing itself, remove it!
|
2021-11-28 16:54:48 +01:00
|
|
|
WorldGraph.m_pLinkPool[i].m_pLinkEnt = NULL;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->globalname))
|
|
|
|
gGlobalState.EntitySetState(pev->globalname, GLOBAL_DEAD);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convenient way to delay removing oneself
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseEntity::SUB_Remove()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
UpdateOnRemove();
|
|
|
|
if (pev->health > 0)
|
|
|
|
{
|
|
|
|
// this situation can screw up monsters who can't tell their entity pointers are invalid.
|
|
|
|
pev->health = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "SUB_Remove called on entity with health > 0\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
REMOVE_ENTITY(ENT(pev));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Convenient way to explicitly do nothing (passed to functions that require a method)
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseEntity::SUB_DoNothing()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Global Savedata for Delay
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CBaseDelay::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CBaseDelay, m_flDelay, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseDelay, m_iszKillTarget, FIELD_STRING),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CBaseDelay, CBaseEntity);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CBaseDelay::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "delay"))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_flDelay = 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, "killtarget"))
|
|
|
|
{
|
|
|
|
m_iszKillTarget = 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 CBaseEntity::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================
|
|
|
|
SUB_UseTargets
|
|
|
|
|
|
|
|
If self.delay is set, a DelayedUse entity will be created that will actually
|
|
|
|
do the SUB_UseTargets after that many seconds have passed.
|
|
|
|
|
|
|
|
Removes all entities with a targetname that match self.killtarget,
|
|
|
|
and removes them, so some events can remove other triggers.
|
|
|
|
|
|
|
|
Search for (string)targetname in all entities that
|
|
|
|
match (string)self.target and call their .use function (if they have one)
|
|
|
|
|
|
|
|
==============================
|
|
|
|
*/
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseEntity::SUB_UseTargets(CBaseEntity* pActivator, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
//
|
|
|
|
// fire targets
|
|
|
|
//
|
|
|
|
if (!FStringNull(pev->target))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
FireTargets(STRING(pev->target), pActivator, this, useType, value);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void FireTargets(const char* targetName, 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
|
|
|
edict_t* pentTarget = NULL;
|
|
|
|
if (!targetName)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "Firing: (%s)\n", targetName);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, targetName);
|
|
|
|
if (FNullEnt(pentTarget))
|
|
|
|
break;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pTarget = CBaseEntity::Instance(pentTarget);
|
|
|
|
if (pTarget && (pTarget->pev->flags & FL_KILLME) == 0) // Don't use dying ents
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "Found: %s, firing (%s)\n", STRING(pTarget->pev->classname), targetName);
|
|
|
|
pTarget->Use(pActivator, pCaller, useType, value);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(DelayedUse, CBaseDelay);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseDelay::SUB_UseTargets(CBaseEntity* pActivator, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
//
|
|
|
|
// exit immediatly if we don't have a target or kill target
|
|
|
|
//
|
2021-11-28 15:32:26 +01:00
|
|
|
if (FStringNull(pev->target) && FStringNull(m_iszKillTarget))
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
//
|
|
|
|
// check for a delay
|
|
|
|
//
|
|
|
|
if (m_flDelay != 0)
|
|
|
|
{
|
|
|
|
// create a temp object to fire at a later time
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseDelay* pTemp = GetClassPtr((CBaseDelay*)NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
pTemp->pev->classname = MAKE_STRING("DelayedUse");
|
|
|
|
|
|
|
|
pTemp->pev->nextthink = gpGlobals->time + m_flDelay;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pTemp->SetThink(&CBaseDelay::DelayThink);
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Save the useType
|
|
|
|
pTemp->pev->button = (int)useType;
|
|
|
|
pTemp->m_iszKillTarget = m_iszKillTarget;
|
|
|
|
pTemp->m_flDelay = 0; // prevent "recursion"
|
|
|
|
pTemp->pev->target = pev->target;
|
|
|
|
|
|
|
|
// HACKHACK
|
|
|
|
// This wasn't in the release build of Half-Life. We should have moved m_hActivator into this class
|
|
|
|
// but changing member variable hierarchy would break save/restore without some ugly code.
|
|
|
|
// This code is not as ugly as that code
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pActivator && pActivator->IsPlayer()) // If a player activates, then save it
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pTemp->pev->owner = pActivator->edict();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pTemp->pev->owner = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// kill the killtargets
|
|
|
|
//
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(m_iszKillTarget))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
edict_t* pentKillTarget = NULL;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "KillTarget: %s\n", STRING(m_iszKillTarget));
|
|
|
|
pentKillTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszKillTarget));
|
|
|
|
while (!FNullEnt(pentKillTarget))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_Remove(CBaseEntity::Instance(pentKillTarget));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "killing %s\n", STRING(pentKillTarget->v.classname));
|
|
|
|
pentKillTarget = FIND_ENTITY_BY_TARGETNAME(pentKillTarget, STRING(m_iszKillTarget));
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
//
|
|
|
|
// fire targets
|
|
|
|
//
|
|
|
|
if (!FStringNull(pev->target))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
FireTargets(STRING(pev->target), pActivator, this, useType, value);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseDelay:: SUB_UseTargetsEntMethod()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
SUB_UseTargets(pev);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
QuakeEd only writes a single float for angles (bad idea), so up and down are
|
|
|
|
just constant angles.
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void SetMovedir(entvars_t* pev)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (pev->angles == Vector(0, -1, 0))
|
|
|
|
{
|
|
|
|
pev->movedir = Vector(0, 0, 1);
|
|
|
|
}
|
|
|
|
else if (pev->angles == Vector(0, -2, 0))
|
|
|
|
{
|
|
|
|
pev->movedir = Vector(0, 0, -1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UTIL_MakeVectors(pev->angles);
|
|
|
|
pev->movedir = gpGlobals->v_forward;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->angles = g_vecZero;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CBaseDelay::DelayThink()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CBaseEntity* pActivator = NULL;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->owner != NULL) // A player activated this on delay
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pActivator = CBaseEntity::Instance(pev->owner);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
// The use type is cached (and stashed) in pev->button
|
2021-11-28 16:54:48 +01:00
|
|
|
SUB_UseTargets(pActivator, (USE_TYPE)pev->button, 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
REMOVE_ENTITY(ENT(pev));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Global Savedata for Toggle
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CBaseToggle::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_toggle_state, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flActivateFinished, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flMoveDistance, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flWait, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flLip, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flTWidth, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flTLength, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_vecPosition1, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_vecPosition2, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_vecAngle1, FIELD_VECTOR), // UNDONE: Position could go through transition, but also angle?
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_vecAngle2, FIELD_VECTOR), // UNDONE: Position could go through transition, but also angle?
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_cTriggersLeft, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_flHeight, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_hActivator, FIELD_EHANDLE),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_pfnCallWhenMoveDone, FIELD_FUNCTION),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_vecFinalDest, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_vecFinalAngle, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_sMaster, FIELD_STRING),
|
|
|
|
DEFINE_FIELD(CBaseToggle, m_bitsDamageInflict, FIELD_INTEGER), // damage type inflicted
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CBaseToggle, CBaseAnimating);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bool CBaseToggle::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, "master"))
|
|
|
|
{
|
|
|
|
m_sMaster = ALLOC_STRING(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, "distance"))
|
|
|
|
{
|
|
|
|
m_flMoveDistance = 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 CBaseDelay::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
LinearMove
|
|
|
|
|
|
|
|
calculate pev->velocity and pev->nextthink to reach vecDest from
|
|
|
|
pev->origin traveling at flSpeed
|
|
|
|
===============
|
|
|
|
*/
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseToggle::LinearMove(Vector vecDest, float flSpeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
ASSERTSZ(flSpeed != 0, "LinearMove: no speed is defined!");
|
2021-11-28 16:54:48 +01:00
|
|
|
// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined");
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
m_vecFinalDest = vecDest;
|
|
|
|
|
|
|
|
// Already there?
|
|
|
|
if (vecDest == pev->origin)
|
|
|
|
{
|
|
|
|
LinearMoveDone();
|
|
|
|
return;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// set destdelta to the vector needed to move
|
|
|
|
Vector vecDestDelta = vecDest - pev->origin;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// divide vector length by speed to get time to reach dest
|
|
|
|
float flTravelTime = vecDestDelta.Length() / flSpeed;
|
|
|
|
|
|
|
|
// set nextthink to trigger a call to LinearMoveDone when dest is reached
|
|
|
|
pev->nextthink = pev->ltime + flTravelTime;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CBaseToggle::LinearMoveDone);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// scale the destdelta vector by the time spent traveling to get velocity
|
|
|
|
pev->velocity = vecDestDelta / flTravelTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
============
|
|
|
|
After moving, set origin to exact final destination, call "move done" function
|
|
|
|
============
|
|
|
|
*/
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseToggle::LinearMoveDone()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
Vector delta = m_vecFinalDest - pev->origin;
|
|
|
|
float error = delta.Length();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (error > 0.03125)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
LinearMove(m_vecFinalDest, 100);
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_SetOrigin(pev, m_vecFinalDest);
|
|
|
|
pev->velocity = g_vecZero;
|
|
|
|
pev->nextthink = -1;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_pfnCallWhenMoveDone)
|
2013-08-30 13:34:05 -07:00
|
|
|
(this->*m_pfnCallWhenMoveDone)();
|
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CBaseToggle::IsLockedByMaster()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 15:32:26 +01:00
|
|
|
return !FStringNull(m_sMaster) && !UTIL_IsMasterTriggered(m_sMaster, m_hActivator);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2024-08-28 16:59:46 +02:00
|
|
|
void CBaseToggle::PlaySentence(const char* pszSentence, float duration, float volume, float attenuation)
|
|
|
|
{
|
|
|
|
ASSERT(pszSentence != nullptr);
|
|
|
|
|
|
|
|
if (!pszSentence || !IsAllowedToSpeak())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaySentenceCore(pszSentence, duration, volume, attenuation);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseToggle::PlaySentenceCore(const char* pszSentence, float duration, float volume, float attenuation)
|
|
|
|
{
|
|
|
|
if (pszSentence[0] == '!')
|
|
|
|
EMIT_SOUND_DYN(edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM);
|
|
|
|
else
|
|
|
|
SENTENCEG_PlayRndSz(edict(), pszSentence, volume, attenuation, 0, PITCH_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseToggle::PlayScriptedSentence(const char* pszSentence, float duration, float volume, float attenuation, bool bConcurrent, CBaseEntity* pListener)
|
|
|
|
{
|
|
|
|
PlaySentence(pszSentence, duration, volume, attenuation);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CBaseToggle::SentenceStop()
|
|
|
|
{
|
|
|
|
EMIT_SOUND(edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE);
|
|
|
|
}
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
/*
|
|
|
|
=============
|
|
|
|
AngularMove
|
|
|
|
|
|
|
|
calculate pev->velocity and pev->nextthink to reach vecDest from
|
|
|
|
pev->origin traveling at flSpeed
|
|
|
|
Just like LinearMove, but rotational.
|
|
|
|
===============
|
|
|
|
*/
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseToggle::AngularMove(Vector vecDestAngle, float flSpeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
ASSERTSZ(flSpeed != 0, "AngularMove: no speed is defined!");
|
2021-11-28 16:54:48 +01:00
|
|
|
// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined");
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
m_vecFinalAngle = vecDestAngle;
|
|
|
|
|
|
|
|
// Already there?
|
|
|
|
if (vecDestAngle == pev->angles)
|
|
|
|
{
|
|
|
|
AngularMoveDone();
|
|
|
|
return;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// set destdelta to the vector needed to move
|
|
|
|
Vector vecDestDelta = vecDestAngle - pev->angles;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// divide by speed to get time to reach dest
|
|
|
|
float flTravelTime = vecDestDelta.Length() / flSpeed;
|
|
|
|
|
|
|
|
// set nextthink to trigger a call to AngularMoveDone when dest is reached
|
|
|
|
pev->nextthink = pev->ltime + flTravelTime;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CBaseToggle::AngularMoveDone);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// scale the destdelta vector by the time spent traveling to get velocity
|
|
|
|
pev->avelocity = vecDestDelta / flTravelTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
============
|
|
|
|
After rotating, set angle to exact final angle, call "move done" function
|
|
|
|
============
|
|
|
|
*/
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseToggle::AngularMoveDone()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->angles = m_vecFinalAngle;
|
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
pev->nextthink = -1;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_pfnCallWhenMoveDone)
|
2013-08-30 13:34:05 -07:00
|
|
|
(this->*m_pfnCallWhenMoveDone)();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
float CBaseToggle::AxisValue(int flags, const Vector& angles)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(flags, SF_DOOR_ROTATE_Z))
|
2013-08-30 13:34:05 -07:00
|
|
|
return angles.z;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(flags, SF_DOOR_ROTATE_X))
|
2013-08-30 13:34:05 -07:00
|
|
|
return angles.x;
|
|
|
|
|
|
|
|
return angles.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseToggle::AxisDir(entvars_t* pev)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_DOOR_ROTATE_Z))
|
|
|
|
pev->movedir = Vector(0, 0, 1); // around z-axis
|
|
|
|
else if (FBitSet(pev->spawnflags, SF_DOOR_ROTATE_X))
|
|
|
|
pev->movedir = Vector(1, 0, 0); // around x-axis
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->movedir = Vector(0, 1, 0); // around y-axis
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
float CBaseToggle::AxisDelta(int flags, const Vector& angle1, const Vector& angle2)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(flags, SF_DOOR_ROTATE_Z))
|
2013-08-30 13:34:05 -07:00
|
|
|
return angle1.z - angle2.z;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (FBitSet(flags, SF_DOOR_ROTATE_X))
|
2013-08-30 13:34:05 -07:00
|
|
|
return angle1.x - angle2.x;
|
|
|
|
|
|
|
|
return angle1.y - angle2.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
FEntIsVisible
|
|
|
|
|
2021-11-19 13:45:16 +01:00
|
|
|
returns true if the passed entity is visible to caller, even if not infront ()
|
2013-08-30 13:34:05 -07:00
|
|
|
=============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
bool FEntIsVisible(
|
|
|
|
entvars_t* pev,
|
|
|
|
entvars_t* pevTarget)
|
|
|
|
{
|
2013-08-30 13:34:05 -07:00
|
|
|
Vector vecSpot1 = pev->origin + pev->view_ofs;
|
|
|
|
Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs;
|
|
|
|
TraceResult tr;
|
|
|
|
|
|
|
|
UTIL_TraceLine(vecSpot1, vecSpot2, ignore_monsters, ENT(pev), &tr);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
if (0 != tr.fInOpen && 0 != tr.fInWater)
|
2021-11-28 16:54:48 +01:00
|
|
|
return false; // sight line crossed contents
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (tr.flFraction == 1)
|
2021-11-19 13:45:16 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-19 13:43:33 +01:00
|
|
|
return false;
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|