BotLib: My last commit before weekend hits. More pathfinding work, added

a temporary interface for in-game waypointing... Bots will combat you too.
This commit is contained in:
Marco Cawthorne 2020-12-26 07:27:34 +01:00
parent c76cdc5903
commit 5edcdb892c
29 changed files with 583 additions and 168 deletions

View File

@ -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

View File

@ -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";
}

View File

@ -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);

View File

@ -39,7 +39,6 @@ Bot_AddQuick(void)
entity oself;
oself = self;
self = world;
self = spawnclient();
if (!self) {

17
src/botlib/cvar.h Normal file
View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
*
* 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;

View File

@ -17,3 +17,5 @@
#include "bot.h"
#include "botinfo.h"
#include "route.h"
#include "cvar.h"
#include "way.h"

View File

@ -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;
}

View File

@ -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;
}
}

25
src/botlib/way.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
*
* 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
};

View File

@ -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;

View File

@ -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++;
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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]);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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]);

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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