2019-08-31 19:18:15 -07:00
|
|
|
/*
|
2020-04-07 05:46:23 -07:00
|
|
|
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
2019-08-31 19:18:15 -07:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
2019-03-19 12:01:24 -07:00
|
|
|
|
2020-03-27 02:37:01 -07:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
vector dest;
|
|
|
|
int linkflags;
|
|
|
|
} nodeslist_t;
|
|
|
|
|
|
|
|
/* Begin calculating a route. The callback function will be called once the
|
|
|
|
* route has finished being calculated. The route must be memfreed once it is
|
|
|
|
* no longer needed. The route must be followed in reverse order (ie: the
|
|
|
|
* first node that must be reached is at index numnodes-1).
|
|
|
|
* If no route is available then the callback will be called with no nodes. */
|
|
|
|
void(entity, vector, int, void(entity, vector, int, nodeslist_t *)) route_calculate = #0:route_calculate;
|
|
|
|
|
2020-03-25 14:35:05 -07:00
|
|
|
enum {
|
|
|
|
MONSTER_IDLE,
|
|
|
|
MONSTER_WALK,
|
|
|
|
MONSTER_RUN,
|
2020-03-29 02:21:26 -07:00
|
|
|
MONSTER_DEAD
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
SEQUENCESTATE_NONE,
|
|
|
|
SEQUENCESTATE_ACTIVE,
|
|
|
|
SEQUENCESTATE_ENDING
|
2020-03-25 14:35:05 -07:00
|
|
|
};
|
|
|
|
|
2020-03-26 23:34:24 -07:00
|
|
|
enumflags {
|
|
|
|
MSF_WAITTILLSEEN,
|
|
|
|
MSF_GAG,
|
|
|
|
MSF_MONSTERCLIP,
|
|
|
|
MSF_RESERVED1,
|
|
|
|
MSF_PRISONER,
|
|
|
|
MSF_RESERVED2,
|
|
|
|
MSF_RESERVED3,
|
|
|
|
MSF_WAITFORSCRIPT,
|
|
|
|
MSF_PREDISASTER,
|
|
|
|
MSF_FADECORPSE
|
|
|
|
};
|
|
|
|
|
2019-03-19 12:01:24 -07:00
|
|
|
class CBaseMonster:CBaseEntity
|
|
|
|
{
|
2020-03-25 05:58:19 -07:00
|
|
|
vector oldnet_velocity;
|
|
|
|
float m_flPitch;
|
2020-03-25 14:35:05 -07:00
|
|
|
int m_iFlags;
|
|
|
|
vector base_mins;
|
|
|
|
vector base_maxs;
|
|
|
|
int base_health;
|
2020-03-29 02:21:26 -07:00
|
|
|
|
2020-03-29 03:56:46 -07:00
|
|
|
int m_iSequenceRemove;
|
2020-03-29 02:21:26 -07:00
|
|
|
int m_iSequenceState;
|
|
|
|
float m_flSequenceEnd;
|
2020-03-27 02:37:01 -07:00
|
|
|
float m_flSequenceSpeed;
|
2020-03-29 03:56:46 -07:00
|
|
|
vector m_vecSequenceAngle;
|
2020-03-27 02:37:01 -07:00
|
|
|
|
|
|
|
/* pathfinding */
|
|
|
|
int m_iNodes;
|
|
|
|
int m_iCurNode;
|
|
|
|
nodeslist_t *m_pRoute;
|
2020-03-29 03:56:46 -07:00
|
|
|
vector m_vecLastNode;
|
2020-03-27 02:37:01 -07:00
|
|
|
|
|
|
|
/* sequences */
|
|
|
|
string m_strRouteEnded;
|
2020-03-25 05:58:19 -07:00
|
|
|
|
2019-03-19 12:01:24 -07:00
|
|
|
void() CBaseMonster;
|
|
|
|
|
|
|
|
virtual void() touch;
|
|
|
|
virtual void() Hide;
|
|
|
|
virtual void() Respawn;
|
|
|
|
virtual void() PlayerUse;
|
2020-03-25 14:35:05 -07:00
|
|
|
virtual void(int) Pain;
|
|
|
|
virtual void(int) Death;
|
2019-03-19 12:01:24 -07:00
|
|
|
virtual void() Physics;
|
2020-03-25 14:35:05 -07:00
|
|
|
virtual void() IdleNoise;
|
2019-03-19 12:01:24 -07:00
|
|
|
virtual void() Gib;
|
2020-03-26 03:24:33 -07:00
|
|
|
virtual void(string) Sound;
|
2020-03-27 02:37:01 -07:00
|
|
|
|
2020-03-30 00:11:57 -07:00
|
|
|
/* sequences */
|
|
|
|
virtual void() FreeState;
|
|
|
|
|
2020-03-27 02:37:01 -07:00
|
|
|
virtual void() ClearRoute;
|
|
|
|
virtual void() CheckRoute;
|
|
|
|
virtual void() WalkRoute;
|
|
|
|
virtual void(vector) NewRoute;
|
2020-03-30 04:51:48 -07:00
|
|
|
|
|
|
|
/* animation cycles */
|
|
|
|
float m_flAnimTime;
|
|
|
|
virtual int() AnimIdle;
|
|
|
|
virtual int() AnimWalk;
|
|
|
|
virtual int() AnimRun;
|
2019-03-19 12:01:24 -07:00
|
|
|
};
|
|
|
|
|
2020-03-30 04:51:48 -07:00
|
|
|
int
|
|
|
|
CBaseMonster::AnimIdle(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CBaseMonster::AnimWalk(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CBaseMonster::AnimRun(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-26 03:24:33 -07:00
|
|
|
void CBaseMonster::Sound(string msg)
|
2020-03-25 05:58:19 -07:00
|
|
|
{
|
2020-03-26 03:24:33 -07:00
|
|
|
sound(this, CHAN_VOICE, msg, 1.0, ATTN_NORM);
|
2020-03-25 05:58:19 -07:00
|
|
|
}
|
|
|
|
|
2019-03-19 12:01:24 -07:00
|
|
|
void CBaseMonster::Gib(void)
|
|
|
|
{
|
|
|
|
takedamage = DAMAGE_NO;
|
|
|
|
Effect_GibHuman(this.origin);
|
|
|
|
Hide();
|
|
|
|
}
|
|
|
|
|
2020-03-25 14:35:05 -07:00
|
|
|
void CBaseMonster::IdleNoise(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:37:01 -07:00
|
|
|
void CBaseMonster::ClearRoute(void)
|
|
|
|
{
|
|
|
|
if (m_iNodes) {
|
|
|
|
m_iNodes = 0;
|
|
|
|
memfree(m_pRoute);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 02:21:26 -07:00
|
|
|
void CBaseMonster::FreeState(void)
|
|
|
|
{
|
|
|
|
m_flSequenceEnd = 0;
|
|
|
|
m_iSequenceState = SEQUENCESTATE_NONE;
|
|
|
|
|
|
|
|
/* trigger when required */
|
|
|
|
if (m_strRouteEnded) {
|
2020-03-30 13:29:05 -07:00
|
|
|
CBaseTrigger trigger = 0;
|
2020-03-30 01:00:37 -07:00
|
|
|
trigger = (CBaseTrigger)find(trigger, CBaseTrigger::m_strTargetName, m_strRouteEnded);
|
|
|
|
if (!trigger) {
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^1CBaseMonster::^3FreeState^7: %s doesn't exist. Won't trigger\n", m_strRouteEnded));
|
2020-03-29 02:21:26 -07:00
|
|
|
}
|
2020-03-29 03:56:46 -07:00
|
|
|
|
2020-03-30 01:00:37 -07:00
|
|
|
if (trigger.Trigger != __NULL__) {
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^2CBaseMonster::^3FreeState^7: %s triggered %f\n", m_strRouteEnded, time));
|
2020-03-30 01:00:37 -07:00
|
|
|
trigger.Trigger();
|
|
|
|
} else {
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^1CBaseMonster::^3FreeState^7: %s not a valid trigger\n", m_strRouteEnded));
|
2020-03-30 01:00:37 -07:00
|
|
|
}
|
|
|
|
}
|
2020-03-30 00:11:57 -07:00
|
|
|
|
2020-03-29 03:56:46 -07:00
|
|
|
if (m_iSequenceRemove) {
|
|
|
|
Hide();
|
|
|
|
}
|
2020-03-29 02:21:26 -07:00
|
|
|
}
|
|
|
|
|
2020-03-27 02:37:01 -07:00
|
|
|
void CBaseMonster::CheckRoute(void)
|
|
|
|
{
|
|
|
|
float flDist;
|
2020-03-29 03:56:46 -07:00
|
|
|
vector evenpos;
|
2020-03-27 02:37:01 -07:00
|
|
|
|
|
|
|
if (!m_iNodes) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-29 03:56:46 -07:00
|
|
|
/* level out position/node stuff */
|
|
|
|
if (m_iCurNode < 0) {
|
|
|
|
evenpos = m_vecLastNode;
|
|
|
|
evenpos[2] = origin[2];
|
|
|
|
} else {
|
|
|
|
evenpos = m_pRoute[m_iCurNode].dest;
|
|
|
|
evenpos[2] = origin[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
flDist = floor( vlen( evenpos - origin ) );
|
2020-03-27 02:37:01 -07:00
|
|
|
|
2020-03-29 03:56:46 -07:00
|
|
|
if ( flDist < 8 ) {
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^2CBaseMonster::^3CheckRoute^7: %s reached node\n", this.m_strTargetName));
|
2020-03-27 02:37:01 -07:00
|
|
|
m_iCurNode--;
|
|
|
|
velocity = [0,0,0]; /* clamp friction */
|
|
|
|
}
|
|
|
|
|
2020-03-29 03:56:46 -07:00
|
|
|
if (m_iCurNode < -1) {
|
|
|
|
ClearRoute();
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^2CBaseMonster::^3CheckRoute^7: %s reached end\n", this.m_strTargetName));
|
2020-03-29 03:56:46 -07:00
|
|
|
|
2020-03-29 02:21:26 -07:00
|
|
|
/* mark that we've ended a sequence, if we're in one and que anim */
|
|
|
|
if (m_iSequenceState == SEQUENCESTATE_ACTIVE) {
|
|
|
|
if (m_flSequenceEnd) {
|
|
|
|
float duration = frameduration(modelindex, m_flSequenceEnd);
|
|
|
|
m_iSequenceState = SEQUENCESTATE_ENDING;
|
|
|
|
think = FreeState;
|
|
|
|
nextthink = time + duration;
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^2CBaseMonster::^3CheckRoute^7: %s overriding anim for %f seconds (modelindex %d, frame %d)\n", this.m_strTargetName, duration, modelindex, m_flSequenceEnd));
|
2020-03-29 03:56:46 -07:00
|
|
|
} else {
|
|
|
|
/* we still need to trigger targets */
|
|
|
|
think = FreeState;
|
|
|
|
nextthink = time;
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint(sprintf("^2CBaseMonster::^3CheckRoute^7: %s has no anim, finished sequence.\n", this.m_strTargetName));
|
2020-03-27 02:37:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*if ( flDist == m_flLastDist ) {
|
|
|
|
m_flNodeGiveup += frametime;
|
|
|
|
} else {
|
|
|
|
m_flNodeGiveup = bound( 0, m_flNodeGiveup - frametime, 1.0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_flLastDist = flDist;
|
|
|
|
|
|
|
|
if ( m_flNodeGiveup >= 1.0f ) {
|
|
|
|
print(sprintf("CBaseMonster::CheckNode: %s gave up route\n",
|
|
|
|
this.netname));
|
|
|
|
ClearRoute();
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::WalkRoute(void)
|
|
|
|
{
|
|
|
|
if (m_iNodes) {
|
|
|
|
vector endangles;
|
2020-03-29 03:56:46 -07:00
|
|
|
/* we're on our last node */
|
|
|
|
if (m_iCurNode < 0) {
|
|
|
|
endangles = vectoangles(m_vecLastNode - origin);
|
|
|
|
} else {
|
|
|
|
endangles = vectoangles(m_pRoute[m_iCurNode].dest - origin);
|
|
|
|
}
|
2020-03-27 02:37:01 -07:00
|
|
|
input_angles[1] = endangles[1];
|
|
|
|
input_movevalues = [m_flSequenceSpeed, 0, 0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::NewRoute(vector destination)
|
|
|
|
{
|
|
|
|
/* engine calls this upon successfully creating a route */
|
|
|
|
static void NewRoute_RouteCB(entity ent, vector dest, int numnodes, nodeslist_t *nodelist)
|
|
|
|
{
|
|
|
|
CBaseMonster p = (CBaseMonster)ent;
|
|
|
|
p.m_iNodes = numnodes;
|
|
|
|
p.m_iCurNode = numnodes - 1;
|
|
|
|
p.m_pRoute = nodelist;
|
2020-03-30 04:51:48 -07:00
|
|
|
|
|
|
|
/* we can walk there directly */
|
|
|
|
tracebox(p.origin, p.mins, p.maxs, dest, TRUE, this);
|
|
|
|
if (trace_fraction == 1.0) {
|
2020-03-31 00:04:05 -07:00
|
|
|
dprint("^2CBaseMonster::^3NewRoute^7: Walking directly to last node\n");
|
2020-03-30 04:51:48 -07:00
|
|
|
p.m_iCurNode = -1;
|
|
|
|
}
|
2020-03-27 02:37:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ClearRoute();
|
|
|
|
|
|
|
|
if (!m_iNodes) {
|
|
|
|
route_calculate(this, destination, 0, NewRoute_RouteCB);
|
2020-03-29 03:56:46 -07:00
|
|
|
m_vecLastNode = destination;
|
2020-03-27 02:37:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-19 12:01:24 -07:00
|
|
|
void CBaseMonster::Physics(void)
|
|
|
|
{
|
|
|
|
input_movevalues = [0,0,0];
|
|
|
|
input_impulse = 0;
|
|
|
|
input_buttons = 0;
|
|
|
|
input_angles = angles = v_angle;
|
|
|
|
input_timelength = frametime;
|
|
|
|
|
2020-03-29 02:21:26 -07:00
|
|
|
/* override whatever we did above with this */
|
2020-03-29 03:56:46 -07:00
|
|
|
if (m_iSequenceState == SEQUENCESTATE_ENDING) {
|
|
|
|
input_angles = angles = v_angle = m_vecSequenceAngle;
|
2020-03-29 02:21:26 -07:00
|
|
|
frame = m_flSequenceEnd;
|
2020-04-01 07:00:00 -07:00
|
|
|
} else if (movetype == MOVETYPE_WALK) {
|
2020-03-29 02:21:26 -07:00
|
|
|
CheckRoute();
|
|
|
|
WalkRoute();
|
|
|
|
runstandardplayerphysics(this);
|
|
|
|
IdleNoise();
|
2020-03-30 04:51:48 -07:00
|
|
|
|
|
|
|
if (style != MONSTER_DEAD) {
|
|
|
|
if (m_flAnimTime > time) {
|
|
|
|
input_movevalues = [0,0,0];
|
|
|
|
} else {
|
|
|
|
float spvel = vlen(velocity);
|
|
|
|
|
|
|
|
if (spvel < 5) {
|
|
|
|
frame = AnimIdle();
|
|
|
|
} else if (spvel <= 140) {
|
|
|
|
frame = AnimWalk();
|
|
|
|
} else if (spvel <= 240) {
|
|
|
|
frame = AnimRun();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-29 02:21:26 -07:00
|
|
|
}
|
2020-03-27 02:37:01 -07:00
|
|
|
|
|
|
|
/* support for think/nextthink */
|
2020-03-30 01:40:38 -07:00
|
|
|
if (think && nextthink > 0.0f) {
|
2020-03-27 02:37:01 -07:00
|
|
|
if (nextthink < time) {
|
2020-03-30 01:00:37 -07:00
|
|
|
nextthink = 0.0f;
|
2020-03-27 02:37:01 -07:00
|
|
|
think();
|
|
|
|
}
|
|
|
|
}
|
2019-03-19 12:01:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::touch(void)
|
|
|
|
{
|
2020-03-30 04:51:48 -07:00
|
|
|
if (movetype != MOVETYPE_WALK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-19 12:01:24 -07:00
|
|
|
if (other.movetype == MOVETYPE_WALK) {
|
|
|
|
velocity = normalize(other.origin - origin) * -128;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::PlayerUse(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-25 14:35:05 -07:00
|
|
|
void CBaseMonster::Pain(int iHitBody)
|
2019-03-19 12:01:24 -07:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-25 14:35:05 -07:00
|
|
|
void CBaseMonster::Death(int iHitBody)
|
2019-03-19 12:01:24 -07:00
|
|
|
{
|
2020-03-25 14:35:05 -07:00
|
|
|
m_iFlags = 0x0;
|
2019-03-19 12:01:24 -07:00
|
|
|
|
|
|
|
if (health < -50) {
|
|
|
|
Gib();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-25 14:35:05 -07:00
|
|
|
/* make sure we're not causing any more obituaries */
|
|
|
|
flags &= ~FL_MONSTER;
|
|
|
|
|
|
|
|
/* gibbing action */
|
|
|
|
movetype = MOVETYPE_NONE;
|
2019-03-19 12:01:24 -07:00
|
|
|
solid = SOLID_CORPSE;
|
2020-03-25 14:35:05 -07:00
|
|
|
style = MONSTER_DEAD;
|
2019-03-19 12:01:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::Hide(void)
|
|
|
|
{
|
|
|
|
setmodel(this, "");
|
|
|
|
solid = SOLID_NOT;
|
|
|
|
movetype = MOVETYPE_NONE;
|
|
|
|
customphysics = __NULL__;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::Respawn(void)
|
|
|
|
{
|
2020-03-25 14:35:05 -07:00
|
|
|
v_angle[0] = Math_FixDelta(m_oldAngle[0]);
|
|
|
|
v_angle[1] = Math_FixDelta(m_oldAngle[1]);
|
|
|
|
v_angle[2] = Math_FixDelta(m_oldAngle[2]);
|
2020-03-25 23:02:41 -07:00
|
|
|
flags |= FL_MONSTER;
|
2019-03-19 12:01:24 -07:00
|
|
|
angles = v_angle;
|
|
|
|
solid = SOLID_SLIDEBOX;
|
2020-03-25 14:35:05 -07:00
|
|
|
movetype = MOVETYPE_WALK;
|
2019-03-19 12:01:24 -07:00
|
|
|
takedamage = DAMAGE_YES;
|
|
|
|
iBleeds = TRUE;
|
|
|
|
customphysics = Physics;
|
|
|
|
velocity = [0,0,0];
|
2020-03-25 14:35:05 -07:00
|
|
|
m_iFlags = 0x0;
|
|
|
|
SendFlags = 0xff;
|
|
|
|
style = MONSTER_IDLE;
|
|
|
|
health = base_health;
|
|
|
|
setmodel(this, m_oldModel);
|
|
|
|
setsize(this, base_mins, base_maxs);
|
|
|
|
setorigin(this, m_oldOrigin);
|
2020-03-31 00:04:05 -07:00
|
|
|
droptofloor();
|
2019-03-19 12:01:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBaseMonster::CBaseMonster(void)
|
|
|
|
{
|
|
|
|
CBaseEntity::CBaseEntity();
|
2020-03-31 00:04:05 -07:00
|
|
|
Respawn();
|
2019-03-19 12:01:24 -07:00
|
|
|
}
|