From 5edcdb892ce1835c3d359b915b609450efba9d97 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Sat, 26 Dec 2020 07:27:34 +0100 Subject: [PATCH] BotLib: My last commit before weekend hits. More pathfinding work, added a temporary interface for in-game waypointing... Bots will combat you too. --- build_engine.sh | 29 +- src/botlib/bot.cpp | 258 ++++++++++++++---- src/botlib/bot.h | 16 ++ src/botlib/cmd.c | 1 - src/botlib/cvar.h | 17 ++ src/botlib/defs.h | 2 + src/botlib/route.c | 2 +- src/botlib/way.c | 118 +++++++- src/botlib/way.h | 25 ++ src/client/entry.c | 69 +++++ src/client/titles.c | 23 ++ src/client/valve/hud.c | 2 + src/gs-entbase/server/ambient_generic.cpp | 4 + src/gs-entbase/server/env_laser.cpp | 4 + src/gs-entbase/server/env_sprite.cpp | 5 +- src/gs-entbase/shared/baseentity.cpp | 6 +- src/gs-entbase/shared/decals.cpp | 6 +- .../shared/env_projectedtexture.cpp | 5 +- src/gs-entbase/shared/func_monitor.cpp | 5 +- .../shared/info_particle_system.cpp | 5 +- src/gs-entbase/shared/light_dynamic.cpp | 5 +- src/gs-entbase/shared/spraylogo.cpp | 5 +- src/gs-entbase/shared/trigger_camera.cpp | 7 +- src/server/base/gamerules_multiplayer.cpp | 5 +- src/server/entry.c | 54 +--- src/server/valve/gamerules_multiplayer.cpp | 49 ++-- src/server/valve/player.c | 4 + src/shared/pmove.c | 18 +- valve.fmf | 2 +- 29 files changed, 583 insertions(+), 168 deletions(-) create mode 100644 src/botlib/cvar.h create mode 100644 src/botlib/way.h diff --git a/build_engine.sh b/build_engine.sh index fe72a778..74579df0 100755 --- a/build_engine.sh +++ b/build_engine.sh @@ -3,6 +3,23 @@ set -e FTE_MAKEFILE=./src/engine/engine/Makefile BUILD_SDL2=0 +BUILD_DEBUG=1 + +if [ "$BUILD_DEBUG" -eq 1 ]; then + MAKETARGET=m-dbg + OUTPUT=./debug +else + MAKETARGET=m-rel + OUTPUT=./release +fi + +if [ "$BUILD_SDL2" -eq 1 ]; then + PLATFORM=SDL2 + OUTPUT=$OUTPUT/fteqw64-sdl2 +else + PLATFORM=linux64 + OUTPUT=$OUTPUT/fteqw64 +fi mkdir -p ./bin @@ -18,15 +35,9 @@ else cd ./engine/engine fi -if [ "$BUILD_SDL2" -eq 1 ]; then - make -j $(nproc) makelibs FTE_TARGET=SDL2 - make -j $(nproc) m-rel FTE_TARGET=SDL2 - cp -v ./release/fteqw-sdl2 ../../../bin/fteqw -else - make -j $(nproc) makelibs - make -j $(nproc) m-rel - cp -v ./release/fteqw ../../../bin/fteqw -fi +make -j $(nproc) makelibs FTE_TARGET=$PLATFORM +make -j $(nproc) $MAKETARGET FTE_TARGET=$PLATFORM +cp -v "$OUTPUT" ../../../bin/fteqw make -j $(nproc) sv-rel cp -v ./release/fteqw-sv ../../../bin/fteqw-sv diff --git a/src/botlib/bot.cpp b/src/botlib/bot.cpp index 8727ae66..cb526f6f 100644 --- a/src/botlib/bot.cpp +++ b/src/botlib/bot.cpp @@ -14,6 +14,111 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define BOTROUTE_DESTINATION -1 +#define BOTROUTE_END -2 + +void +bot::Pain(void) +{ + player::Pain(); + + /* might as well target our attacker */ + if (!m_eTarget) + m_eTarget = g_dmg_eAttacker; +} + +void +bot::RouteClear(void) +{ + if (!m_iNodes) + return; + + m_iCurNode = BOTROUTE_END; + m_iNodes = 0; + m_flNodeGiveup = 0.0f; + memfree(m_pRoute); +} + +void +bot::WeaponThink(void) +{ + /* clip empty */ + if (a_ammo1 == 0) { + /* still got ammo left */ + if (a_ammo2 != 0) { + input_buttons &= ~INPUT_BUTTON0; + input_buttons |= INPUT_BUTTON4; + } else { + Weapons_SwitchBest(this); + } + } +} + +void +bot::WeaponAttack(void) +{ + if (m_flAttackTime < time) { + if (!m_iAttackMode) { + input_buttons |= INPUT_BUTTON0; // Attack + } + m_flAttackTime = time + 0.1f; + } + m_iAttackMode = 1 - m_iAttackMode; +} + +void +bot::BrainThink(int enemyvisible, int enemydistant) +{ + /* we had a target and it's now dead. now what? */ + if (m_eTarget && m_eTarget.health <= 0) { + m_eTarget = __NULL__; + RouteClear(); + } else if (m_eTarget && enemyvisible && enemydistant) { + /* we can see the player, but are too far away, plot a route */ + route_calculate(this, m_eTarget.origin, 0, Bot_RouteCB); + } +} + +void +bot::SeeThink(void) +{ + if (m_eTarget) + return; + + if (m_flSeeTime > time) + return; + + if (autocvar_bot_pacifist) + return; + + m_flSeeTime = time + 0.25f; + + for (entity w = world; (w = findfloat(w, ::takedamage, DAMAGE_YES));) { + float flDot; + + if (!(w.flags & FL_CLIENT)) + continue; + if (w.health <= 0) + continue; + + /* first, is the potential enemy in our field of view? */ + makevectors(v_angle); + vector v = normalize(w.origin - origin); + flDot = v * v_forward; + + if (flDot < 90/180) + continue; + + other = world; + traceline(origin, w.origin, MOVE_OTHERONLY, this); + + if (trace_fraction == 1.0f) { + m_eTarget = w; + return; + } + } +} + void bot::CheckRoute(void) { @@ -26,21 +131,29 @@ bot::CheckRoute(void) /* level out position/node stuff */ if (m_iCurNode < 0) { - evenpos = m_vecLastNode; - evenpos[2] = origin[2]; + evenpos = m_vecLastNode - origin; } else { - evenpos = m_pRoute[m_iCurNode].m_vecDest; - evenpos[2] = origin[2]; + evenpos = m_pRoute[m_iCurNode].m_vecDest - origin; } + evenpos[2] *= 0.25f; - flDist = floor(vlen(evenpos - origin)); + flDist = floor(vlen(evenpos)); - if ( flDist < 16 ) { + if (flDist < 16) { dprint(sprintf("^2CBaseMonster::^3CheckRoute^7: " \ "%s reached node\n", this.targetname)); m_iCurNode--; - velocity = [0,0,0]; /* clamp friction */ + velocity *= 0.5f; + if (m_iCurNode >= 0) { + print(sprintf("NODE FLAGS: %i\n", m_pRoute[m_iCurNode].m_iFlags)); + + /* if a node is flagged as jumpy, jump! */ + if (m_pRoute[m_iCurNode].m_iFlags & WP_JUMP) + input_buttons |= INPUT_BUTTON2; + } + +#if 0 /* we've still traveling and from this node we may be able to walk * directly to our end-destination */ if (m_iCurNode > -1) { @@ -53,43 +166,36 @@ bot::CheckRoute(void) m_iCurNode = -1; } } +#endif } else { - traceline( origin + view_ofs, m_pRoute[m_iCurNode].m_vecDest, MOVE_NORMAL, this ); + traceline(origin + view_ofs, m_pRoute[m_iCurNode].m_vecDest, MOVE_NORMAL, this); /* we can't trace against our next waypoint... that should never happen */ - if ( trace_fraction != 1.0f ) { + if (trace_fraction != 1.0f) { m_flNodeGiveup += frametime; } else { /* if we're literally stuck in a corner aiming at something we should * not, also give up */ - if ( flDist == m_flLastDist ) { + if (flDist == m_flLastDist) { m_flNodeGiveup += frametime; } else { - m_flNodeGiveup = bound( 0, m_flNodeGiveup - frametime, 1.0 ); + m_flNodeGiveup = bound(0, m_flNodeGiveup - frametime, 1.0); } } } m_flLastDist = flDist; - if ( m_flNodeGiveup >= 1.0f ) { - dprint(sprintf("bot::CheckRoute: %s gave up route\n", - this.netname)); - - m_iCurNode = -2; - m_flNodeGiveup = 0.0f; - } else if ( m_flNodeGiveup >= 0.5f ) { + /* after one second, also give up the route */ + if (m_flNodeGiveup >= 1.0f || m_iCurNode <= BOTROUTE_END) { + RouteClear(); + } else if (m_flNodeGiveup >= 0.5f) { + /* attempt a jump after half a second */ input_buttons |= INPUT_BUTTON2; - } - - if (m_iCurNode < -1) { - dprint(sprintf("bot::CheckRoute: %s calculates new route\n", - this.netname)); - - m_iNodes = 0; - memfree( m_pRoute ); - route_calculate( this, Route_SelectDestination( this ), 0, Bot_RouteCB ); - return; + } else { + /* entire way-link needs to be crouched. that's the law of the land */ + if (m_pRoute[m_iCurNode].m_iFlags & WP_CROUCH) + input_buttons |= INPUT_BUTTON8; } } @@ -107,7 +213,10 @@ bot::RunAI(void) /* attempt to respawn when dead */ if (health <= 0) { - input_buttons |= INPUT_BUTTON0; + RouteClear(); + WeaponAttack(); + m_eTarget = __NULL__; + return; } /* create our first route */ @@ -123,8 +232,8 @@ bot::RunAI(void) } } - //WeaponThink(); - //PickEnemy(); + WeaponThink(); + SeeThink(); enemyvisible = FALSE; enemydistant = FALSE; @@ -138,69 +247,108 @@ bot::RunAI(void) } if (enemyvisible) { - //WeaponAttack(); + WeaponAttack(); } } - + + BrainThink(enemyvisible, enemydistant); CheckRoute(); if (m_iNodes) { + vector vecNewAngles; + vector vecDirection; + if (!m_eTarget || !enemyvisible) { /* aim at the next node */ - if (m_iCurNode == -1) + if (m_iCurNode == BOTROUTE_DESTINATION) aimpos = m_vecLastNode; - else - aimpos = m_pRoute[m_iCurNode].m_vecDest; + else { + if (m_iCurNode > 0) + aimpos = m_pRoute[m_iCurNode - 1].m_vecDest; + else + aimpos = m_pRoute[m_iCurNode].m_vecDest; + } } else { /* aim towards the enemy */ aimpos = m_eTarget.origin; } - /* lerping speed */ - flLerp = bound(0.0f, 1.0f - (frametime * 15), 1.0f); + /* lerping speed, faster when we've got a target */ + if (m_eTarget && enemyvisible) + flLerp = bound(0.0f, frametime * 45, 1.0f); + else + flLerp = bound(0.0f, frametime * 30, 1.0f); /* that's the old angle */ makevectors(v_angle); - vector vNewAngles = v_forward; + vecNewAngles = v_forward; - /* aimdir = final angle */ + /* aimdir = new final angle */ aimdir = vectoangles(aimpos - origin); makevectors(aimdir); /* slowly lerp towards the final angle */ - vNewAngles[0] = Math_Lerp(vNewAngles[0], v_forward[0], flLerp); - vNewAngles[1] = Math_Lerp(vNewAngles[1], v_forward[1], flLerp); - vNewAngles[2] = Math_Lerp(vNewAngles[2], v_forward[2], flLerp); + vecNewAngles[0] = Math_Lerp(vecNewAngles[0], v_forward[0], flLerp); + vecNewAngles[1] = Math_Lerp(vecNewAngles[1], v_forward[1], flLerp); + vecNewAngles[2] = Math_Lerp(vecNewAngles[2], v_forward[2], flLerp); /* make sure we're aiming tight */ - v_angle = vectoangles(vNewAngles); + v_angle = vectoangles(vecNewAngles); v_angle[0] = Math_FixDelta(v_angle[0]); v_angle[1] = Math_FixDelta(v_angle[1]); v_angle[2] = Math_FixDelta(v_angle[2]); - input_angles = v_angle; angles[0] = Math_FixDelta(v_angle[0]); angles[1] = Math_FixDelta(v_angle[1]); angles[2] = Math_FixDelta(v_angle[2]); + input_angles = v_angle; + + /* now that aiming is sorted, we need to correct the movement */ + if ((m_eTarget && enemyvisible && !enemydistant) && vlen(aimpos - origin) > 256) { + /* we are far away, inch closer */ + aimpos = m_eTarget.origin; + } else { + if (m_iCurNode == BOTROUTE_DESTINATION) + aimpos = m_vecLastNode; + else + aimpos = m_pRoute[m_iCurNode].m_vecDest; + } + /* now we'll set the movevalues relative to the input_angle */ - vector direction = normalize(aimpos - origin) * 240; + vecDirection = normalize(aimpos - origin) * 240; makevectors(input_angles); - input_movevalues = [v_forward * direction, v_right * direction, v_up * direction]; + input_movevalues = [v_forward * vecDirection, v_right * vecDirection, v_up * vecDirection]; } /* press any buttons needed */ - button0 = input_buttons & INPUT_BUTTON0; //attack - button2 = input_buttons & INPUT_BUTTON2; //jump - button3 = input_buttons & INPUT_BUTTON3; //tertiary - button4 = input_buttons & INPUT_BUTTON4; //reload - button5 = input_buttons & INPUT_BUTTON5; //secondary - button6 = input_buttons & INPUT_BUTTON6; //use - button7 = input_buttons & INPUT_BUTTON7; //unused - button8 = input_buttons & INPUT_BUTTON8; //duck + button0 = input_buttons & INPUT_BUTTON0; // attack + button2 = input_buttons & INPUT_BUTTON2; // jump + button3 = input_buttons & INPUT_BUTTON3; // tertiary + button4 = input_buttons & INPUT_BUTTON4; // reload + button5 = input_buttons & INPUT_BUTTON5; // secondary + button6 = input_buttons & INPUT_BUTTON6; // use + button7 = input_buttons & INPUT_BUTTON7; // unused + button8 = input_buttons & INPUT_BUTTON8; // duck movement = input_movevalues; } +void +bot::PreFrame(void) +{ +} + +void +bot::PostFrame(void) +{ + /* we've picked something new up */ + if (m_iOldItems != g_items) { + //Weapons_SwitchBest(this); + m_iOldItems = g_items; + } +} + void bot::bot(void) { + classname = "player"; } diff --git a/src/botlib/bot.h b/src/botlib/bot.h index 5af0d9a0..d02ac9f2 100644 --- a/src/botlib/bot.h +++ b/src/botlib/bot.h @@ -36,11 +36,27 @@ class bot:player /* combat */ entity m_eTarget; + int m_iAttackMode; + float m_flAttackTime; + + /* items */ + int m_iOldItems; + + /* visual */ + float m_flSeeTime; void(void) bot; + virtual void(void) Pain; + virtual void(void) RouteClear; + virtual void(void) WeaponThink; + virtual void(void) WeaponAttack; + virtual void(void) SeeThink; + virtual void(int, int) BrainThink; virtual void(void) RunAI; virtual void(void) CheckRoute; + virtual void(void) PreFrame; + virtual void(void) PostFrame; }; entity Bot_AddQuick(void); diff --git a/src/botlib/cmd.c b/src/botlib/cmd.c index 585e9d9a..0bdd1c31 100644 --- a/src/botlib/cmd.c +++ b/src/botlib/cmd.c @@ -39,7 +39,6 @@ Bot_AddQuick(void) entity oself; oself = self; - self = world; self = spawnclient(); if (!self) { diff --git a/src/botlib/cvar.h b/src/botlib/cvar.h new file mode 100644 index 00000000..d5b015a0 --- /dev/null +++ b/src/botlib/cvar.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +var int autocvar_bot_pacifist = FALSE; diff --git a/src/botlib/defs.h b/src/botlib/defs.h index be10ebcc..afaa58ae 100644 --- a/src/botlib/defs.h +++ b/src/botlib/defs.h @@ -17,3 +17,5 @@ #include "bot.h" #include "botinfo.h" #include "route.h" +#include "cvar.h" +#include "way.h" diff --git a/src/botlib/route.c b/src/botlib/route.c index 0b6b0c93..4e62ec4e 100644 --- a/src/botlib/route.c +++ b/src/botlib/route.c @@ -86,7 +86,7 @@ vector Route_SelectDestination( bot target ) int range; for ( temp = world; ( temp = findfloat( temp, ::botinfo, BOTINFO_HEALTH ) ); ) { range = vlen( temp.origin - target.origin ); - if ( ( range < bestrange ) && ( temp.solid = SOLID_TRIGGER ) ) { + if ( ( range < bestrange ) && ( temp.solid == SOLID_TRIGGER ) ) { bestrange = range; dest = temp; } diff --git a/src/botlib/way.c b/src/botlib/way.c index e8883897..51912f7f 100644 --- a/src/botlib/way.c +++ b/src/botlib/way.c @@ -14,16 +14,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define COST_INFINITE 99999 - -enumflags -{ - WP_JUMP, /* also implies that the bot must first go behind the wp... */ - WP_CLIMB, - WP_CROUCH, - WP_USE -}; - typedef struct waypoint_s { vector vecOrigin; @@ -166,11 +156,18 @@ Way_Waypoint_Create(entity ePlayer, int iAutoLink) n->neighbour = __NULL__; n->iNeighbours = 0; - if (iAutoLink) { + if (iAutoLink == 1) { Way_AutoLink(n); } else { if (iID != 0) { - Way_LinkWaypoints(n, &g_pWaypoints[iID-1]); + if (iAutoLink == 0) { + Way_LinkWaypoints(n, &g_pWaypoints[iID-1]); + Way_LinkWaypoints(&g_pWaypoints[iID-1], n); + } else if (iAutoLink -1) { + Way_LinkWaypoints(&g_pWaypoints[iID-1], n); + } else { + Way_LinkWaypoints(n, &g_pWaypoints[iID-1]); + } } } } @@ -270,6 +267,26 @@ Way_FindClosestWaypoint(vector vecOrigin) return r; } +void +Way_GoToPoint(entity pl) +{ + vector vecSrc; + makevectors(pl.v_angle); + vecSrc = pl.origin + pl.view_ofs; + traceline(vecSrc, vecSrc + (v_forward * 4096), FALSE, pl); + print(sprintf("Telling all bots to go to %v\n", trace_endpos)); + + for (entity a = world; ( a = find( a, classname, "player" ) ); ) { + if ( clienttype(a) != CLIENTTYPE_REAL ) { + bot targ; + targ = (bot)a; + targ.RouteClear(); + route_calculate(targ, pl.origin, 0, Bot_RouteCB); + print(sprintf("Told bot to go to %v\n", trace_endpos)); + } + } +} + void Way_DrawDebugInfo(void) { @@ -317,6 +334,7 @@ Way_DrawDebugInfo(void) for (int j = 0i; j < w->iNeighbours; j++) { int k = w->neighbour[j].node; + int fl = w->neighbour[j].iFlags; if (k < 0i || k >= g_iWaypoints) { break; @@ -324,9 +342,81 @@ Way_DrawDebugInfo(void) waypoint_t *w2 = &g_pWaypoints[k]; - R_PolygonVertex(org, '0 1', '1 0 1', 1); - R_PolygonVertex(w2->vecOrigin, '1 1', [0,1,0], 1); + if (fl & WP_JUMP) { + R_PolygonVertex(org, [0,1], [1,0,0], 1); + R_PolygonVertex(w2->vecOrigin, [1,1], [0,1,0], 1); + } else { + R_PolygonVertex(org, [0,1], [1,0,1], 1); + R_PolygonVertex(w2->vecOrigin, [1,1], [0,1,0], 1); + } R_EndPolygon(); } } } + +void +Way_Cmd(void) +{ + switch (argv(1)) { + case "goto": + if ( !self ) { + return; + } + Way_GoToPoint( self ); + break; + case "add": + if ( !self ) { + return; + } + Way_Waypoint_Create( self, 1 ); + break; + case "addchain": + if ( !self ) { + return; + } + Way_Waypoint_Create( self, 0 ); + break; + case "addltn": + if ( !self ) { + return; + } + Way_Waypoint_Create( self, -1 ); + break; + case "addntl": + if ( !self ) { + return; + } + Way_Waypoint_Create( self, -2 ); + break; + case "addspawns": + if ( !self ) { + return; + } + Way_Waypoint_CreateSpawns(); + break; + case "delete": + if ( !self ) { + return; + } + Way_Waypoint_Delete( Way_FindClosestWaypoint( self.origin ) ); + break; + case "radius": + if ( !self ) { + return; + } + Way_Waypoint_SetRadius( Way_FindClosestWaypoint( self.origin ), stof( argv( 2 ) ) ); + break; + case "makejump": + if ( !self ) { + return; + } + Way_Waypoint_MakeJump( Way_FindClosestWaypoint( self.origin ) ); + break; + case "save": + Way_DumpWaypoints( argv( 2 ) ); + break; + case "load": + Way_ReadWaypoints( argv( 2 ) ); + break; + } +} diff --git a/src/botlib/way.h b/src/botlib/way.h new file mode 100644 index 00000000..0e19eef0 --- /dev/null +++ b/src/botlib/way.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#define COST_INFINITE 99999 + +enumflags +{ + WP_JUMP, /* also implies that the bot must first go behind the wp... */ + WP_CLIMB, + WP_CROUCH, + WP_USE +}; diff --git a/src/client/entry.c b/src/client/entry.c index f98fe9cf..f51b34ac 100644 --- a/src/client/entry.c +++ b/src/client/entry.c @@ -61,6 +61,7 @@ CSQC_Init(float apilevel, string enginename, float engineversion) registercommand("_fnchat_msg"); registercommand("dev_sunpos"); registercommand("dev_measure"); + registercommand("way_menu"); precache_model("sprites/640_pain.spr"); precache_model("sprites/crosshairs.spr"); @@ -87,6 +88,71 @@ CSQC_Init(float apilevel, string enginename, float engineversion) Titles_Init(); Sentences_Init(); Decals_Init(); + + /* waypoint menu */ + { + titles_t way_text; + way_text.m_strName = "WAY_MENU"; + way_text.m_strMessage = "1.\tAdd Waypoint\n" \ + "2.\tAdd Chain Waypoint\n" \ + "3.\tAdd Spawnpoint Waypoints\n" \ + "4.\tDelete Closest Waypoint\n" \ + "5.\tMake Closest Jumpy\n" \ + "6.\tAdd One-Way New-To-Last\n" \ + "7.\tAdd One-Way Last-To-New\n" \ + "8.\tSave File\n" \ + "9.\tLoad File\n" \ + "0.\tExit\n"; + way_text.m_flPosX = 0; + way_text.m_flPosY = -1; + way_text.m_iEffect = 0; + way_text.m_vecColor1 = [1,1,1]; + way_text.m_vecColor2 = [1,1,1]; + way_text.m_flFadeIn = 1.0f; + way_text.m_flFadeOut = 1.0f; + way_text.m_flHoldTime = 1.0f; + way_text.m_flFXTime = 1.0f; + Titles_AddEntry(way_text); + } +} + +void +WAY_MENU(int n) +{ + switch (n) { + case 1: + localcmd("sv way add\n"); + break; + case 2: + localcmd("sv way addchain\n"); + break; + case 3: + localcmd("sv way addspawns\n"); + break; + case 4: + localcmd("sv way delete\n"); + break; + case 5: + localcmd("sv way makejump\n"); + break; + case 6: + localcmd("sv way addntl\n"); + break; + case 7: + localcmd("sv way addltn\n"); + break; + case 8: + localcmd(sprintf("sv way save %s.way\n", mapname)); + Textmenu_Call(""); + break; + case 9: + localcmd(sprintf("sv way load %s.way\n", mapname)); + Textmenu_Call(""); + break; + case 0: + Textmenu_Call(""); + break; + } } /* Rendering Caches */ @@ -725,6 +791,9 @@ CSQC_ConsoleCommand(string sCMD) case "slot10": HUD_SlotSelect(9); break; + case "way_menu": + Textmenu_Call("WAY_MENU"); + break; case "_fnchat_msg": CSQC_Parse_Print(argv(1), PRINT_CHAT); break; diff --git a/src/client/titles.c b/src/client/titles.c index 5e3e9f36..793f3d9b 100644 --- a/src/client/titles.c +++ b/src/client/titles.c @@ -220,3 +220,26 @@ Titles_Init(void) } fclose(fs_titles); } + + +titles_t *g_titles; +int g_titles_count; + +void +Titles_AddEntry(titles_t new) +{ + int newcount = g_titles_count + 1; + g_titles = memrealloc(g_titles, sizeof(titles_t), g_titles_count, newcount); + g_titles[g_titles_count].m_strName = new.m_strName; + g_titles[g_titles_count].m_strMessage = new.m_strMessage; + g_titles[g_titles_count].m_flPosX = new.m_flPosX; + g_titles[g_titles_count].m_flPosY = new.m_flPosY; + g_titles[g_titles_count].m_iEffect = new.m_iEffect; + g_titles[g_titles_count].m_vecColor1 = new.m_vecColor1; + g_titles[g_titles_count].m_vecColor2 = new.m_vecColor2; + g_titles[g_titles_count].m_flFadeIn = new.m_flFadeIn; + g_titles[g_titles_count].m_flFadeOut = new.m_flFadeOut; + g_titles[g_titles_count].m_flHoldTime = new.m_flHoldTime; + g_titles[g_titles_count].m_flFXTime = new.m_flFXTime; + g_titles_count++; +} diff --git a/src/client/valve/hud.c b/src/client/valve/hud.c index 569bd0b0..31a0534a 100644 --- a/src/client/valve/hud.c +++ b/src/client/valve/hud.c @@ -403,6 +403,7 @@ HUD_Draw(void) Weapons_DrawCrosshair(); HUD_DrawWeaponSelect(); Obituary_Draw(); + Textmenu_Draw(); if (!(pl.g_items & ITEM_SUIT)) { return; @@ -420,4 +421,5 @@ void HUD_DrawSpectator(void) { // FIXME + Textmenu_Draw(); } diff --git a/src/gs-entbase/server/ambient_generic.cpp b/src/gs-entbase/server/ambient_generic.cpp index 4dc65acf..b97b7ff3 100644 --- a/src/gs-entbase/server/ambient_generic.cpp +++ b/src/gs-entbase/server/ambient_generic.cpp @@ -69,6 +69,10 @@ class ambient_generic:CBaseTrigger float ambient_generic::SendEntity(entity ePEnt, float fChanged) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) { + return FALSE; + } + /* only override when we're doing the toggle guff */ if (m_iLoop == FALSE) { return FALSE; diff --git a/src/gs-entbase/server/env_laser.cpp b/src/gs-entbase/server/env_laser.cpp index 4588d175..853c4fc1 100644 --- a/src/gs-entbase/server/env_laser.cpp +++ b/src/gs-entbase/server/env_laser.cpp @@ -123,6 +123,10 @@ env_laser::Respawn(void) float env_laser::SendEntity(entity ePEnt, float fChanged) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) { + return FALSE; + } + WriteByte(MSG_ENTITY, ENT_ENVLASER); WriteFloat(MSG_ENTITY, fChanged); diff --git a/src/gs-entbase/server/env_sprite.cpp b/src/gs-entbase/server/env_sprite.cpp index 35693560..fdf04671 100644 --- a/src/gs-entbase/server/env_sprite.cpp +++ b/src/gs-entbase/server/env_sprite.cpp @@ -53,8 +53,11 @@ class env_sprite:CBaseTrigger }; float -env_sprite::SendEntity(entity pvsent, float flags) +env_sprite::SendEntity(entity ePEnt, float flags) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + if (spawnflags & ENVS_PLAYONCE) return FALSE; diff --git a/src/gs-entbase/shared/baseentity.cpp b/src/gs-entbase/shared/baseentity.cpp index afce16af..ea80463b 100644 --- a/src/gs-entbase/shared/baseentity.cpp +++ b/src/gs-entbase/shared/baseentity.cpp @@ -608,9 +608,11 @@ CBaseEntity::SetRenderColor(vector newColor) float CBaseEntity::SendEntity(entity ePEnt, float fChanged) { - if (!modelindex) { + if (!modelindex) + return FALSE; + + if (clienttype(ePEnt) != CLIENTTYPE_REAL) return FALSE; - } WriteByte(MSG_ENTITY, ENT_ENTITY); WriteFloat(MSG_ENTITY, fChanged); diff --git a/src/gs-entbase/shared/decals.cpp b/src/gs-entbase/shared/decals.cpp index 1ff6fc79..84e49ce7 100644 --- a/src/gs-entbase/shared/decals.cpp +++ b/src/gs-entbase/shared/decals.cpp @@ -33,8 +33,12 @@ const string g_decal_shader = \ #ifdef SERVER float -decal::SendEntity(entity pvsent, float changedflags) +decal::SendEntity(entity ePEnt, float changedflags) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) { + return FALSE; + } + WriteByte(MSG_ENTITY, ENT_DECAL); WriteCoord(MSG_ENTITY, origin[0]); WriteCoord(MSG_ENTITY, origin[1]); diff --git a/src/gs-entbase/shared/env_projectedtexture.cpp b/src/gs-entbase/shared/env_projectedtexture.cpp index 094358ef..30ab80fd 100644 --- a/src/gs-entbase/shared/env_projectedtexture.cpp +++ b/src/gs-entbase/shared/env_projectedtexture.cpp @@ -194,8 +194,11 @@ env_projectedtexture::Trigger(entity act, int state) } float -env_projectedtexture::SendEntity(entity ePVSEnt, float flFlags) +env_projectedtexture::SendEntity(entity ePEnt, float flFlags) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + WriteByte(MSG_ENTITY, ENT_PROJECTEDTEXTURE); WriteFloat(MSG_ENTITY, flFlags); diff --git a/src/gs-entbase/shared/func_monitor.cpp b/src/gs-entbase/shared/func_monitor.cpp index 16a32b6d..056a9295 100644 --- a/src/gs-entbase/shared/func_monitor.cpp +++ b/src/gs-entbase/shared/func_monitor.cpp @@ -169,8 +169,11 @@ func_monitor::ReceiveEntity(float flFlags) } #else float -func_monitor::SendEntity(entity ePVSEnt, float flFlags) +func_monitor::SendEntity(entity ePEnt, float flFlags) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + WriteByte(MSG_ENTITY, ENT_MONITOR); WriteFloat(MSG_ENTITY, flFlags); diff --git a/src/gs-entbase/shared/info_particle_system.cpp b/src/gs-entbase/shared/info_particle_system.cpp index ab87f6a0..06cd3e12 100644 --- a/src/gs-entbase/shared/info_particle_system.cpp +++ b/src/gs-entbase/shared/info_particle_system.cpp @@ -124,8 +124,11 @@ info_particle_system::ReceiveEntity(float flFlags) } #else float -info_particle_system::SendEntity(entity ePVSEnt, float flFlags) +info_particle_system::SendEntity(entity ePEnt, float flFlags) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + WriteByte(MSG_ENTITY, ENT_PARTSYSTEM); WriteFloat(MSG_ENTITY, flFlags); diff --git a/src/gs-entbase/shared/light_dynamic.cpp b/src/gs-entbase/shared/light_dynamic.cpp index 6bc0c9d6..61c78f66 100644 --- a/src/gs-entbase/shared/light_dynamic.cpp +++ b/src/gs-entbase/shared/light_dynamic.cpp @@ -196,8 +196,11 @@ light_dynamic::Trigger(entity act, int state) } float -light_dynamic::SendEntity(entity ePVSEnt, float flFlags) +light_dynamic::SendEntity(entity ePEnt, float flFlags) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + WriteByte(MSG_ENTITY, ENT_DLIGHT); WriteFloat(MSG_ENTITY, flFlags); diff --git a/src/gs-entbase/shared/spraylogo.cpp b/src/gs-entbase/shared/spraylogo.cpp index f1c15280..ec657a71 100644 --- a/src/gs-entbase/shared/spraylogo.cpp +++ b/src/gs-entbase/shared/spraylogo.cpp @@ -26,8 +26,11 @@ Spray_RemoveAll(entity entOwner) } float -Spray_SendEntity(entity ePVSEnt, float fChanged) +Spray_SendEntity(entity ePEnt, float fChanged) { + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + WriteByte(MSG_ENTITY, ENT_SPRAY); WriteCoord(MSG_ENTITY, self.origin[0]); WriteCoord(MSG_ENTITY, self.origin[1]); diff --git a/src/gs-entbase/shared/trigger_camera.cpp b/src/gs-entbase/shared/trigger_camera.cpp index ca38d3f9..c51ffcfd 100644 --- a/src/gs-entbase/shared/trigger_camera.cpp +++ b/src/gs-entbase/shared/trigger_camera.cpp @@ -91,9 +91,12 @@ trigger_camera::ReceiveEntity(float flFlags) } #else float -trigger_camera::SendEntity(entity ePVSEnt, float flFlags) +trigger_camera::SendEntity(entity ePEnt, float flFlags) { - if (ePVSEnt != m_eLooker) + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return FALSE; + + if (ePEnt != m_eLooker) return FALSE; WriteByte(MSG_ENTITY, ENT_OLDCAMERA); diff --git a/src/server/base/gamerules_multiplayer.cpp b/src/server/base/gamerules_multiplayer.cpp index 6897d386..5f785a91 100644 --- a/src/server/base/gamerules_multiplayer.cpp +++ b/src/server/base/gamerules_multiplayer.cpp @@ -49,8 +49,9 @@ MultiplayerRules::PlayerDeath(base_player pl) IntermissionStart(); } - pl.movetype = MOVETYPE_NONE; - pl.solid = SOLID_NOT; + pl.SetMovetype(MOVETYPE_NONE); + pl.SetSolid(SOLID_NOT); + pl.SetModelindex(0); pl.takedamage = DAMAGE_NO; pl.gflags &= ~GF_FLASHLIGHT; pl.armor = pl.activeweapon = pl.g_items = 0; diff --git a/src/server/entry.c b/src/server/entry.c index f661ed8e..232409a9 100644 --- a/src/server/entry.c +++ b/src/server/entry.c @@ -97,11 +97,22 @@ void PutClientInServer(void) void PlayerPreThink(void) { +#ifdef BOT_INCLUDED + if (clienttype(self) == CLIENTTYPE_BOT) { + ((bot)self).PreFrame(); + } +#endif g_grMode.PlayerPreFrame((base_player)self); } void PlayerPostThink(void) { +#ifdef BOT_INCLUDED + if (clienttype(self) == CLIENTTYPE_BOT) { + ((bot)self).PostFrame(); + } +#endif + g_grMode.PlayerPostFrame((base_player)self); } @@ -299,47 +310,8 @@ float ConsoleCmd(string cmd) } break; #ifdef BOT_INCLUDED - case "way_add": - if ( !self ) { - return TRUE; - } - Way_Waypoint_Create( self, TRUE ); - break; - case "way_addchain": - if ( !self ) { - return TRUE; - } - Way_Waypoint_Create( self, FALSE ); - break; - case "way_addspawns": - if ( !self ) { - return TRUE; - } - Way_Waypoint_CreateSpawns(); - break; - case "way_delete": - if ( !self ) { - return TRUE; - } - Way_Waypoint_Delete( Way_FindClosestWaypoint( self.origin ) ); - break; - case "way_radius": - if ( !self ) { - return TRUE; - } - Way_Waypoint_SetRadius( Way_FindClosestWaypoint( self.origin ), stof( argv( 1 ) ) ); - break; - case "way_makejump": - if ( !self ) { - return TRUE; - } - Way_Waypoint_MakeJump( Way_FindClosestWaypoint( self.origin ) ); - break; - case "way_save": - Way_DumpWaypoints( argv( 1 ) ); - break; - case "way_load": - Way_ReadWaypoints( argv( 1 ) ); + case "way": + Way_Cmd(); break; #endif default: diff --git a/src/server/valve/gamerules_multiplayer.cpp b/src/server/valve/gamerules_multiplayer.cpp index 3fc1d1eb..b2e6099e 100644 --- a/src/server/valve/gamerules_multiplayer.cpp +++ b/src/server/valve/gamerules_multiplayer.cpp @@ -63,35 +63,36 @@ HLMultiplayerRules::PlayerDeath(base_player pl) } weaponbox_spawn((player)pl); - pl.movetype = MOVETYPE_NONE; - pl.solid = SOLID_NOT; + + /* either gib, or make a corpse */ + if (pl.health < -50) { + FX_GibHuman(pl.origin); + } else { + /* Let's handle corpses on the clientside */ + entity corpse = spawn(); + setorigin(corpse, pl.origin + [0,0,32]); + setmodel(corpse, pl.model); + setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX); + corpse.movetype = MOVETYPE_TOSS; + corpse.solid = SOLID_TRIGGER; + corpse.modelindex = pl.modelindex; + corpse.frame = ANIM_DIESIMPLE; + corpse.angles = pl.angles; + corpse.velocity = pl.velocity; + } + + /* now let's make the real client invisible */ + pl.SetModelindex(0); + pl.SetMovetype(MOVETYPE_NONE); + pl.SetSolid(SOLID_NOT); pl.takedamage = DAMAGE_NO; pl.gflags &= ~GF_FLASHLIGHT; pl.armor = pl.activeweapon = pl.g_items = 0; - - pl.think = PutClientInServer; - pl.nextthink = time + 4.0f; Sound_Play(pl, CHAN_AUTO, "player.die"); - if (pl.health < -50) { - pl.health = 0; - FX_GibHuman(pl.origin); - return; - } - - pl.health = 0; - - /* Let's handle corpses on the clientside */ - entity corpse = spawn(); - setorigin(corpse, pl.origin + [0,0,32]); - setmodel(corpse, pl.model); - setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX); - corpse.movetype = MOVETYPE_TOSS; - corpse.solid = SOLID_TRIGGER; - corpse.modelindex = pl.modelindex; - corpse.frame = ANIM_DIESIMPLE; - corpse.angles = pl.angles; - corpse.velocity = pl.velocity; + /* force respawn */ + pl.think = PutClientInServer; + pl.nextthink = time + 4.0f; } void diff --git a/src/server/valve/player.c b/src/server/valve/player.c index a11d2638..945fbb01 100644 --- a/src/server/valve/player.c +++ b/src/server/valve/player.c @@ -89,6 +89,10 @@ float Player_SendEntity(entity ePEnt, float fChanged) return FALSE; } + if (clienttype(ePEnt) != CLIENTTYPE_REAL) { + return FALSE; + } + WriteByte(MSG_ENTITY, ENT_PLAYER); WriteFloat(MSG_ENTITY, fChanged); diff --git a/src/shared/pmove.c b/src/shared/pmove.c index c73e19a4..40ea0729 100644 --- a/src/shared/pmove.c +++ b/src/shared/pmove.c @@ -126,7 +126,7 @@ PMove_Categorize(void) self.view_ofs = VEC_PLAYER_VIEWPOS; } - tracebox(self.origin, self.mins, self.maxs, self.origin - [0,0,0.25], FALSE, self); + tracebox(self.origin, self.mins, self.maxs, self.origin - [0,0,0.25], MOVE_NORMAL, self); if (!trace_startsolid) { if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) { @@ -149,7 +149,7 @@ PMove_Categorize(void) /* ladder content testing */ int oldhitcontents = self.hitcontentsmaski; self.hitcontentsmaski = CONTENTBIT_FTELADDER; - tracebox(self.origin, self.mins, self.maxs, self.origin, FALSE, self); + tracebox(self.origin, self.mins, self.maxs, self.origin, MOVE_NORMAL, self); self.hitcontentsmaski = oldhitcontents; if (trace_endcontentsi & CONTENTBIT_FTELADDER) { @@ -232,7 +232,7 @@ QPMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs) } bound = eTarget.origin + vOffset; - tracebox(bound, vecMins, vecMaxs, bound, FALSE, eTarget); + tracebox(bound, vecMins, vecMaxs, bound, MOVE_NORMAL, eTarget); return trace_startsolid; } @@ -546,7 +546,7 @@ PMove_Fix_Origin(void) for (y = 0; y < 3; y++) { norg[1] = oorg[1] + ((y==2)?-1:y)*0.0125; - tracebox(norg, self.mins, self.maxs, norg, FALSE, self); + tracebox(norg, self.mins, self.maxs, norg, MOVE_NORMAL, self); if (!trace_startsolid) { self.origin = norg; return TRUE; @@ -584,7 +584,7 @@ PMove_Move(void) for (i = 3, move_time = input_timelength; move_time > 0 && i; i--) { dest = self.origin + (self.velocity * move_time); - tracebox(self.origin, self.mins, self.maxs, dest, FALSE, self); + tracebox(self.origin, self.mins, self.maxs, dest, MOVE_NORMAL, self); if (trace_startsolid) { if (!PMove_Fix_Origin()) { @@ -611,7 +611,7 @@ PMove_Move(void) trace_endpos[2] += serverkeyfloat("phy_airstepheight"); } - tracebox(self.origin, self.mins, self.maxs, trace_endpos, FALSE, self); + tracebox(self.origin, self.mins, self.maxs, trace_endpos, MOVE_NORMAL, self); stepped = trace_endpos[2] - self.origin[2]; float roof_fraction = trace_fraction; @@ -621,7 +621,7 @@ PMove_Move(void) dest[2] = trace_endpos[2]; /*only horizontally*/ /* move forwards */ - tracebox(trace_endpos, self.mins, self.maxs, dest, FALSE, self); + tracebox(trace_endpos, self.mins, self.maxs, dest, MOVE_NORMAL, self); /* if we got anywhere, make this raised-step move count */ if (trace_fraction != 0) { @@ -631,7 +631,7 @@ PMove_Move(void) /* move down */ dest = trace_endpos; dest[2] -= stepped + 1; - tracebox(trace_endpos, self.mins, self.maxs, dest, FALSE, self); + tracebox(trace_endpos, self.mins, self.maxs, dest, MOVE_NORMAL, self); if (trace_fraction < 1 && trace_plane_normal[2] > 0.7f) { move_time -= move_time * fwfrac; @@ -659,7 +659,7 @@ PMove_Move(void) if (self.flags & FL_ONGROUND) { dest = self.origin; dest[2] -= serverkeyfloat("phy_stepheight"); - tracebox(self.origin, self.mins, self.maxs, dest, FALSE, self); + tracebox(self.origin, self.mins, self.maxs, dest, MOVE_NORMAL, self); if (trace_fraction >= 1) { return; } diff --git a/valve.fmf b/valve.fmf index f6a9c84a..cec34ade 100644 --- a/valve.fmf +++ b/valve.fmf @@ -24,7 +24,7 @@ BASEGAME valve -set gameinfo_svonly "0" -set gameinfo_menutrack "" -set gameinfo_chatroom "halflife" --set gameinfo_pkgname "valve_uplink;valve_realmedia;valve_patch1110;valve_opfordemo;valve_hlds;logos_realmedia" +-set gameinfo_pkgname "valve_uplink;valve_realmedia;valve_patch1110;valve_opfordemo;valve_hlds;logos_realmedia;valve_dayone" DOWNLOADSURL http://www.frag-net.com/dl/valve_packages UPDATEURL http://www.frag-net.com/mods/valve.fmf