From e9326f4e1d18dcd747ede355784366ad44805525 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Fri, 27 Mar 2020 10:37:01 +0100 Subject: [PATCH] scripted_sequences: They sorta work now, needs an engine with ENGINE_ROUTING enabled however --- src/gs-entbase/server/basemonster.cpp | 123 ++++++++++++++- src/gs-entbase/server/basenpc.cpp | 2 + src/gs-entbase/server/monster_generic.cpp | 12 -- src/gs-entbase/server/scripted_sequence.cpp | 26 +++- src/server/nodes.c | 49 +++++- src/server/valve/monster_bigmomma.cpp | 160 +++++++++++++++++++- src/server/valve/monster_leech.cpp | 68 ++++++++- src/server/valve/monster_nihilanth.cpp | 151 +++++++++++++++--- 8 files changed, 540 insertions(+), 51 deletions(-) diff --git a/src/gs-entbase/server/basemonster.cpp b/src/gs-entbase/server/basemonster.cpp index 4730d63d..369fde52 100644 --- a/src/gs-entbase/server/basemonster.cpp +++ b/src/gs-entbase/server/basemonster.cpp @@ -14,11 +14,26 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +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; + enum { MONSTER_IDLE, MONSTER_WALK, MONSTER_RUN, - MONSTER_DEAD + MONSTER_DEAD, + MONSTER_INSEQUENCE }; enumflags { @@ -44,6 +59,15 @@ class CBaseMonster:CBaseEntity vector base_mins; vector base_maxs; int base_health; + float m_flSequenceSpeed; + + /* pathfinding */ + int m_iNodes; + int m_iCurNode; + nodeslist_t *m_pRoute; + + /* sequences */ + string m_strRouteEnded; void() CBaseMonster; @@ -59,6 +83,11 @@ class CBaseMonster:CBaseEntity virtual void(string) Sound; virtual float(entity, float) SendEntity; virtual void() ParentUpdate; + + virtual void() ClearRoute; + virtual void() CheckRoute; + virtual void() WalkRoute; + virtual void(vector) NewRoute; }; void CBaseMonster::Sound(string msg) @@ -117,6 +146,87 @@ void CBaseMonster::IdleNoise(void) } +void CBaseMonster::ClearRoute(void) +{ + if (m_iNodes) { + m_iNodes = 0; + memfree(m_pRoute); + } +} + +void CBaseMonster::CheckRoute(void) +{ + float flDist; + + if (!m_iNodes) { + return; + } + + flDist = floor( vlen( m_pRoute[m_iCurNode].dest - origin ) ); + + if ( flDist < 64 ) { + print(sprintf("CBaseMonster::CheckNode: %s reached node\n", this.netname)); + m_iCurNode--; + velocity = [0,0,0]; /* clamp friction */ + } + + if (m_iCurNode < 0) { + print(sprintf("CBaseMonster::CheckNode: %s reached end\n", this.netname)); + /* trigger when required */ + if (m_strRouteEnded) { + for ( entity t = world; ( t = find( t, CBaseTrigger::m_strTargetName, m_strRouteEnded) ); ) { + CBaseTrigger trigger = (CBaseTrigger) t; + if (trigger.Trigger != __NULL__) { + trigger.Trigger(); + } + } + } + ClearRoute(); + } + + /*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; + endangles = vectoangles(m_pRoute[m_iCurNode].dest - origin); + 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; + } + + ClearRoute(); + + if (!m_iNodes) { + route_calculate(this, destination, 0, NewRoute_RouteCB); + } +} + void CBaseMonster::Physics(void) { input_movevalues = [0,0,0]; @@ -127,10 +237,19 @@ void CBaseMonster::Physics(void) input_timelength = frametime; movetype = MOVETYPE_WALK; + CheckRoute(); + WalkRoute(); runstandardplayerphysics(this); movetype = MOVETYPE_NONE; - IdleNoise(); + + /* support for think/nextthink */ + if (think && nextthink > 0) { + if (nextthink < time) { + think(); + nextthink = 0.0f; + } + } } void CBaseMonster::touch(void) diff --git a/src/gs-entbase/server/basenpc.cpp b/src/gs-entbase/server/basenpc.cpp index b9751274..b2e33e87 100644 --- a/src/gs-entbase/server/basenpc.cpp +++ b/src/gs-entbase/server/basenpc.cpp @@ -472,6 +472,8 @@ CBaseNPC::Physics(void) } input_angles = angles = v_angle; + CheckRoute(); + WalkRoute(); input_timelength = frametime; runstandardplayerphysics(this); diff --git a/src/gs-entbase/server/monster_generic.cpp b/src/gs-entbase/server/monster_generic.cpp index ed6b544c..17b2c340 100644 --- a/src/gs-entbase/server/monster_generic.cpp +++ b/src/gs-entbase/server/monster_generic.cpp @@ -22,18 +22,6 @@ Decorative, does nothing yet. */ -enumflags -{ - MF_WAITTILLSEEN, - MF_GAG, - MF_MONSTERCLIP, - MF_PRISONER, - MF_UNUSED, - MF_WAITFORSCRIPT, - MF_PREDISASTER, - MF_FADECORPSE -}; - class monster_generic:CBaseEntity { void() monster_generic; diff --git a/src/gs-entbase/server/scripted_sequence.cpp b/src/gs-entbase/server/scripted_sequence.cpp index 41095cf4..32430db3 100644 --- a/src/gs-entbase/server/scripted_sequence.cpp +++ b/src/gs-entbase/server/scripted_sequence.cpp @@ -85,7 +85,7 @@ class scripted_sequence:CBaseTrigger float m_flSearchRadius; /* How we move to perform m_iActionAnim */ int m_iMove; - + void() scripted_sequence; virtual void() Trigger; virtual void() Respawn; @@ -93,7 +93,26 @@ class scripted_sequence:CBaseTrigger void scripted_sequence::Trigger(void) { - + CBaseMonster f; + + print(sprintf("^2scripted_sequence::Trigger^7: with spawnflags %d\n", spawnflags)); + if (m_iMove == SS_WALK) { + f = (CBaseMonster)find(world, CBaseEntity::m_strTargetName, m_strMonster); + if (f) { + f.NewRoute(origin); + f.style = MONSTER_INSEQUENCE; + f.m_flSequenceSpeed = 64; + f.m_strRouteEnded = m_strTarget; + } + } else if (m_iMove == SS_RUN) { + f = (CBaseMonster)find(world, CBaseEntity::m_strTargetName, m_strMonster); + if (f) { + f.NewRoute(origin); + f.style = MONSTER_INSEQUENCE; + f.m_flSequenceSpeed = 256; + f.m_strRouteEnded = m_strTarget; + } + } } void scripted_sequence::Respawn(void) @@ -106,6 +125,9 @@ void scripted_sequence::scripted_sequence(void) int nfields = tokenize(__fullspawndata); for (int i = 1; i < (nfields-1); i += 2) { switch (argv(i)) { + case "target": + m_strTarget = argv(i+1); + break; case "m_iszEntity": m_strMonster = argv(i+1); break; diff --git a/src/server/nodes.c b/src/server/nodes.c index fa1e51c8..01d54fe7 100644 --- a/src/server/nodes.c +++ b/src/server/nodes.c @@ -15,6 +15,7 @@ */ /* parse info_node entities and convert them to FTE compatible routing data */ +#define NODE_DEBUG 1 typedef struct node_s { vector origin; @@ -76,6 +77,45 @@ Nodes_Save(string filename) fclose(wayfile); } +void +Nodes_Load(string filename) +{ + float wayfile = fopen(filename, FILE_READ); + + if (wayfile < 0) { + return; + } + + /* wipe whatever we've got */ + for (int i = 0; i < g_iNodes; i++) { + memfree(g_pNodes[i].nb); + } + memfree(g_pNodes); + g_iNodes = 0; + + tokenize(fgets(wayfile)); + g_iNodes = stoi(argv(0)); + g_pNodes = memalloc(sizeof(*g_pNodes) * g_iNodes); + + for (int i = 0; i < g_iNodes; i++) { + tokenize(fgets(wayfile)); + g_pNodes[i].origin[0] = stof(argv(0)); + g_pNodes[i].origin[1] = stof(argv(1)); + g_pNodes[i].origin[2] = stof(argv(2)); + g_pNodes[i].radius = stof(argv(3)); + g_pNodes[i].nb_count = stoi(argv(4)); + g_pNodes[i].nb = memalloc(sizeof(*g_pNodes[i].nb) * g_pNodes[i].nb_count); + + for (int j = 0; j < g_pNodes[i].nb_count; j++) { + tokenize(fgets(wayfile)); + g_pNodes[i].nb[j].node = stoi(argv(0)); + g_pNodes[i].nb[j].dist = stof(argv(1)); + g_pNodes[i].nb[j].flags = stoh(argv(2)); + } + } + fclose(wayfile); +} + /* link two nodes together */ static void Node_Link(node_t *n1, node_t *n2) @@ -138,6 +178,9 @@ Nodes_Init(void) /* skip if present. TODO: check if they're out of date? */ if (whichpack(sprintf("data/%s.way", mapname))) { g_nodes_present = TRUE; +#ifdef NODE_DEBUG + Nodes_Load(sprintf("%s.way", mapname)); +#endif return; } @@ -159,8 +202,8 @@ Nodes_Init(void) Nodes_Save(sprintf("%s.way", mapname)); -#ifndef GS_DEVELOPER /* we don't need these any longer */ +#ifndef NODE_DEBUG for (int i = 0; i < g_iNodes; i++) { memfree(g_pNodes[i].nb); } @@ -169,11 +212,11 @@ Nodes_Init(void) #endif } +#ifdef NODE_DEBUG /* draws debug graphics of our node tree */ void SV_AddDebugPolygons(void) { -#ifdef GS_DEVELOPER if (!g_iNodes) { return; } @@ -232,5 +275,5 @@ SV_AddDebugPolygons(void) R_EndPolygon(); } } -#endif } +#endif diff --git a/src/server/valve/monster_bigmomma.cpp b/src/server/valve/monster_bigmomma.cpp index 2251d1a9..cab04a72 100644 --- a/src/server/valve/monster_bigmomma.cpp +++ b/src/server/valve/monster_bigmomma.cpp @@ -20,18 +20,172 @@ Gonarch */ +enum { + GON_IDLE, + GON_IDLE2, + GON_WALK, + GON_RUN, + GON_DIE, + GON_CLAW, + GON_CLAW2, + GON_CLAW3, + GON_SPAWN, + GON_SHOOT, + GON_FLINCH, + GON_DEFEND, + GON_JUMP, + GON_ANGRY, + GON_ANGRY2, + GON_ANGRY3, + GON_BREAKWALL, + GON_FALL, + GON_FALL2, + GON_FALLDIE +}; + +/* the attack sounds are for when she spits? */ +string gon_sndattack[] = { + "gonarch/gon_attack1.wav", + "gonarch/gon_attack2.wav", + "gonarch/gon_attack3.wav" +}; + +/* mourns the death of her children */ +string gon_sndchild[] = { + "gonarch/gon_childdie1.wav", + "gonarch/gon_childdie2.wav", + "gonarch/gon_childdie3.wav" +}; + +string gon_snddie[] = { + "gonarch/gon_die1.wav", + "gonarch/gon_die2.wav", + "gonarch/gon_die3.wav" +}; + +string gon_sndidle[] = { + "gonarch/gon_sack1.wav", + "gonarch/gon_sack2.wav", + "gonarch/gon_sack3.wav" +}; + +string gon_sndpain[] = { + "gonarch/gon_pain2.wav", + "gonarch/gon_pain3.wav", + "gonarch/gon_pain4.wav", + "gonarch/gon_pain5.wav" +}; + +string gon_sndsee[] = { + "gonarch/gon_alert1.wav", + "gonarch/gon_alert2.wav", + "gonarch/gon_alert3.wav" +}; + +/* has unique foot step sounds */ +string gon_sndstep[] = { + "gonarch/gon_step1.wav", + "gonarch/gon_step2.wav", + "gonarch/gon_step3.wav" +}; + class monster_bigmomma:CBaseMonster { + float m_flIdleTime; + float m_flPainTime; + void() monster_bigmomma; + + virtual void(int) Death; + virtual void(int) Pain; + virtual void(void) IdleNoise; + virtual void(void) Respawn; }; +void +monster_bigmomma::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + 2.0f + random(0,5); + + int rand = floor(random(0, gon_sndidle.length)); + Sound(gon_sndidle[rand]); +} + +void +monster_bigmomma::Pain(int iHitBody) +{ + CBaseMonster::Pain(iHitBody); + + if (m_flPainTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + int rand = floor(random(0,gon_sndpain.length)); + Sound(gon_sndpain[rand]); + frame = GON_FLINCH; + m_flPainTime = time + 0.25f; +} + +void +monster_bigmomma::Death(int iHitBody) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + frame = GON_DIE; + Sound("gonarch/gon_die1.wav"); + } + + /* set the functional differences */ + CBaseMonster::Death(iHitBody); +} + +void +monster_bigmomma::Respawn(void) +{ + CBaseMonster::Respawn(); + frame = GON_IDLE; +} + void monster_bigmomma::monster_bigmomma(void) { + for (int i = 0; i time) { + return; + } + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + 2.0f + random(0,5); + + int rand = floor(random(0, nil_sndidle.length)); + Sound(nil_sndidle[rand]); +} + +void +monster_nihilanth::Pain(int iHitBody) +{ + CBaseMonster::Pain(iHitBody); + + if (m_flPainTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + int rand = floor(random(0,nil_sndpain.length)); + Sound(nil_sndpain[rand]); + + frame = (random() < 0.5) ? NIL_FLINCH : NIL_FLINCH2; + m_flPainTime = time + 0.25f; +} + +void +monster_nihilanth::Death(int iHitBody) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + frame = NIL_DIE; + Sound("x/x_die1.wav"); + } + + /* set the functional differences */ + CBaseMonster::Death(iHitBody); +} + +void +monster_nihilanth::Respawn(void) +{ + CBaseMonster::Respawn(); + frame = NIL_IDLE; +} + void monster_nihilanth::monster_nihilanth(void) { + for (int i = 0; i