mirror of
https://git.code.sf.net/p/quake/game-source
synced 2024-11-26 13:51:15 +00:00
876 lines
20 KiB
C++
876 lines
20 KiB
C++
|
|
/*
|
|
======================================
|
|
FrikBot X (Version 0.10.0) QW
|
|
======================================
|
|
|
|
This program is in the Public Domain. My crack legal
|
|
team would like to add:
|
|
|
|
RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS"
|
|
AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE
|
|
ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR
|
|
FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN
|
|
NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY
|
|
GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL,
|
|
EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC"
|
|
SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES.
|
|
|
|
You accept this software on the condition that you
|
|
indemnify and hold harmless Ryan "FrikaC" Smith from
|
|
any and all liability or damages to third parties,
|
|
including attorney fees, court costs, and other
|
|
related costs and expenses, arising out of your use
|
|
of this software irrespective of the cause of said
|
|
liability.
|
|
|
|
The export from the United States or the subsequent
|
|
reexport of this software is subject to compliance
|
|
with United States export control and munitions
|
|
control restrictions. You agree that in the event you
|
|
seek to export this software, you assume full
|
|
responsibility for obtaining all necessary export
|
|
licenses and approvals and for assuring compliance
|
|
with applicable reexport restrictions.
|
|
|
|
Any reproduction of this software must contain
|
|
this notice in its entirety.
|
|
|
|
|
|
======================================
|
|
These installation instructions only apply to QuakeWorld (as does this entire
|
|
file). For Normal Quake, please refer to bot.qc
|
|
|
|
--------------------------------------
|
|
To install on a new mod, do all this:
|
|
--------------------------------------
|
|
Place all included bot*.qc files in the subdirectory "frikbot"
|
|
in your source folder, then...
|
|
|
|
* Add the following lines to progs.src right after the defs.qc line
|
|
frikbot/map_dm1.qc
|
|
frikbot/map_dm2.qc
|
|
frikbot/map_dm3.qc
|
|
frikbot/map_dm4.qc
|
|
frikbot/map_dm5.qc
|
|
frikbot/map_dm6.qc
|
|
frikbot/bot_qw.qc
|
|
frikbot/bot_way.qc
|
|
frikbot/bot_fight.qc
|
|
frikbot/bot_ai.qc
|
|
frikbot/bot_misc.qc
|
|
frikbot/bot_phys.qc
|
|
frikbot/bot_move.qc
|
|
--------------------------------------
|
|
* Comment out the following functions in defs.qc
|
|
sound, stuffcmd, sprint, aim, centerprint, setspawnparms
|
|
WriteByte, WriteChar, WriteShort, WriteLong, WriteCoord
|
|
WriteAngle, WriteString, WriteEntity
|
|
--------------------------------------
|
|
* Add this to worldspawn() in world.qc, right at the very top, before InitBodyQue();
|
|
BotInit(); // FrikBot
|
|
--------------------------------------
|
|
* add this line to StartFrame() in world.qc, at the very top
|
|
BotFrame(); // FrikBot
|
|
--------------------------------------
|
|
* Add these two lines to PlayerPreThink in client.qc at the very top
|
|
if (BotPreFrame()) // FrikBot
|
|
return;
|
|
--------------------------------------
|
|
* Add this line to PlayerPostThink in client.qc at the very top
|
|
if (BotPostFrame()) // FrikBot
|
|
return;
|
|
--------------------------------------
|
|
* Add the following line to the very top of Client Connect in client.qc
|
|
ClientInRankings(); // FrikBot
|
|
--------------------------------------
|
|
* Add these lines to the very top of ClientDisconnect in client.qc
|
|
ClientDisconnected(); // FrikBot
|
|
--------------------------------------
|
|
* Add these lines to the very top of SpectatorConnect in spectate.qc
|
|
ClientFixRankings(); // FrikBot
|
|
--------------------------------------
|
|
*/
|
|
|
|
#include "libfrikbot.h"
|
|
|
|
void() bot_map_load =
|
|
{
|
|
// place your qc loaded waypoints here
|
|
|
|
if (mapname == "dm1")
|
|
map_dm1();
|
|
else if (mapname == "dm2")
|
|
map_dm2();
|
|
else if (mapname == "dm3")
|
|
map_dm3();
|
|
else if (mapname == "dm4")
|
|
map_dm4();
|
|
else if (mapname == "dm5")
|
|
map_dm5();
|
|
else if (mapname == "dm6")
|
|
map_dm6();
|
|
};
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Variables and shtuff
|
|
|
|
bot.qc has become pretty much a header file
|
|
for all variable in the bot...
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
// ----- entity fields ---
|
|
.float wallhug, keys, oldkeys, ishuman;
|
|
.float b_frags, b_clientno, b_shirt, b_pants;
|
|
.float priority, ai_time, b_sound, missile_speed;
|
|
.float portal_time, b_skill, switch_wallhug;
|
|
.float b_aiflags, b_num, b_chattime;
|
|
.float b_entertime, b_userid; // QW shtuff
|
|
.float b_menu, b_menu_time, b_menu_value;
|
|
.float route_failed, dyn_flags, dyn_time;
|
|
.float dyn_plat;
|
|
.entity temp_way, last_way, phys_obj;
|
|
.entity target1, target2, target3, target4;
|
|
.entity _next, _last;
|
|
.entity current_way;
|
|
.vector b_angle, b_dest, mouse_emu, obs_dir;
|
|
.vector movevect, b_dir;
|
|
.vector dyn_dest;
|
|
.vector punchangle; // HACK - Don't want to screw with bot_phys
|
|
|
|
|
|
// -------globals-----
|
|
float active_clients1, active_clients2;
|
|
float max_clients, real_frametime;
|
|
float bot_count, b_options, lasttime;
|
|
float waypoint_mode, dump_mode;
|
|
float waypoints, direct_route, userid;
|
|
float sv_friction, sv_gravity;
|
|
float sv_accelerate, sv_maxspeed, sv_stopspeed;
|
|
entity fixer;
|
|
entity route_table;
|
|
entity b_temp1, b_temp2, b_temp3;
|
|
entity player_head, player_tail, phys_head, way_head;
|
|
float busy_waypoints;
|
|
|
|
float coop = 0; // hack
|
|
|
|
// ----------Commands---------
|
|
void(entity e, float chan, string samp, float vol, float atten) frik_sound = #8;
|
|
void(entity client, string s) frik_stuffcmd = #21;
|
|
void(entity client, float level, string s) frik_sprint = #24;
|
|
vector(entity e, float sped) frik_aim = #44;
|
|
void(entity client, string s) frik_centerprint = #73;
|
|
void(entity e) frik_setspawnparms = #78;
|
|
void(float to, float f) frik_WriteByte = #52;
|
|
void(float to, float f) frik_WriteChar = #53;
|
|
void(float to, float f) frik_WriteShort = #54;
|
|
void(float to, float f) frik_WriteLong = #55;
|
|
void(float to, float f) frik_WriteCoord = #56;
|
|
void(float to, float f) frik_WriteAngle = #57;
|
|
void(float to, string s) frik_WriteString = #58;
|
|
void(float to, entity s) frik_WriteEntity = #59;
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Function redclarations. These allow function
|
|
designed to work for clients (sprint, so forth)
|
|
to mainly not complain when working with a bot
|
|
|
|
Although these shouldn't be needed anymore,
|
|
as the bots truly are clients now, if you don't
|
|
stop the SZ_ buffer from filling up by disabling
|
|
direct messages to the bots, it crashes quake :-(
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
void(entity client, string s) stuffcmd =
|
|
{
|
|
if (client.ishuman)
|
|
frik_stuffcmd(client, s);
|
|
};
|
|
|
|
void(entity e) setspawnparms =
|
|
{
|
|
if (e.ishuman)
|
|
frik_setspawnparms(e);
|
|
else
|
|
SetNewParms();
|
|
};
|
|
void(entity client, float level, string s) sprint =
|
|
{
|
|
if (client.ishuman)
|
|
frik_sprint(client, level, s);
|
|
};
|
|
void(entity client, string s) centerprint =
|
|
{
|
|
if (client.ishuman)
|
|
frik_centerprint(client, s);
|
|
};
|
|
|
|
vector(entity e, float sped) aim =
|
|
{
|
|
e.missile_speed = sped;
|
|
return frik_aim(e, sped);
|
|
};
|
|
|
|
void(entity e, float chan, string samp, float vol, float atten) sound =
|
|
{
|
|
frik_sound(e, chan, samp, vol, atten);
|
|
if (samp == "items/inv3.wav")
|
|
return;
|
|
else if (e.classname == "player")
|
|
e.b_sound = time + 1;
|
|
else if (other.classname == "player")
|
|
other.b_sound = time + 1;
|
|
};
|
|
void(float to, float f) WriteByte =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteByte(to, f);
|
|
};
|
|
void(float to, float f) WriteChar =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteChar(to, f);
|
|
};
|
|
void(float to, float f) WriteShort =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteShort(to, f);
|
|
};
|
|
void(float to, float f) WriteLong =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteLong(to, f);
|
|
};
|
|
void(float to, float f) WriteCoord =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteCoord(to, f);
|
|
};
|
|
void(float to, float f) WriteAngle =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteAngle(to, f);
|
|
};
|
|
void(float to, string s) WriteString =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteString(to, s);
|
|
};
|
|
void(float to, entity s) WriteEntity =
|
|
{
|
|
if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
|
|
return;
|
|
frik_WriteEntity(to, s);
|
|
};
|
|
|
|
|
|
float(float clientno) ClientIsActive =
|
|
{
|
|
if(clientno > 16)
|
|
{
|
|
if(active_clients2 & ClientBitFlag(clientno - 16))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if(active_clients1 & ClientBitFlag(clientno))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
};
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Stuff mentioned up top
|
|
it just links the bot into the mod
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
void() ClientFixRankings =
|
|
{
|
|
local float cno;
|
|
if (self.switch_wallhug > time)
|
|
return;
|
|
self.switch_wallhug = 0;
|
|
|
|
b_temp2 = nextent(NIL);
|
|
cno = 0;
|
|
|
|
while (cno < max_clients)
|
|
{
|
|
if (!b_temp2.ishuman)
|
|
{
|
|
if (ClientIsActive(cno))
|
|
UpdateClient(b_temp2);
|
|
}
|
|
cno = cno + 1;
|
|
b_temp2 = nextent(b_temp2);
|
|
}
|
|
};
|
|
|
|
void() ClientInRankings =
|
|
{
|
|
local float cno;
|
|
|
|
if (!self.phys_obj) {
|
|
// oh no, the server killed our data. THE BASTARD!
|
|
// ie, incoming player
|
|
local entity e;
|
|
|
|
// repair the link
|
|
if (player_head != self) {
|
|
e = player_head;
|
|
while (e) {
|
|
if (e._next == self) {
|
|
self._last = e;
|
|
e = NIL;
|
|
} else {
|
|
e = e._next;
|
|
}
|
|
}
|
|
}
|
|
if (player_tail != self) {
|
|
e = player_tail;
|
|
while (e) {
|
|
if (e._last == self) {
|
|
self._next = e;
|
|
e = NIL;
|
|
} else {
|
|
e = e._last;
|
|
}
|
|
}
|
|
}
|
|
// unlink it again
|
|
if (self._last)
|
|
self._last._next = self._next;
|
|
else if (self._next)
|
|
player_head = self._next;
|
|
if (self._next)
|
|
self._next._last = self._last;
|
|
else if (self._last)
|
|
player_tail = self._last;
|
|
self._last = self._next = NIL;
|
|
}
|
|
if (player_head)
|
|
player_head._last = self;
|
|
self._next = player_head;
|
|
player_head = self;
|
|
if (!player_tail)
|
|
player_tail = self;
|
|
userid = userid + 1;
|
|
self.b_userid = userid;
|
|
|
|
if (!self.phys_obj)
|
|
{
|
|
b_temp2 = phys_head;
|
|
while (b_temp2 != NIL && b_temp2.owner != self)
|
|
b_temp2 = b_temp2._next;
|
|
self.phys_obj = b_temp2;
|
|
}
|
|
|
|
if (self.ishuman == 2)
|
|
{
|
|
self.ishuman = FALSE;
|
|
return;
|
|
}
|
|
cno = self.colormap - 1;
|
|
BotInvalidClientNo (cno);
|
|
if(cno > 16)
|
|
active_clients2 = active_clients2 | ClientBitFlag(cno - 16);
|
|
else
|
|
active_clients1 = active_clients1 | ClientBitFlag(cno);
|
|
self.b_clientno = cno;
|
|
self.ishuman = TRUE;
|
|
self.switch_wallhug = time + 1;
|
|
};
|
|
|
|
|
|
void() ClientDisconnected =
|
|
{
|
|
if (player_head == self)
|
|
player_head = self._next;
|
|
if (player_tail == self)
|
|
player_tail = self._last;
|
|
if (self._next)
|
|
self._next._last = self._last;
|
|
if (self._last)
|
|
self._last._next = self._next;
|
|
if(self.b_clientno > 16)
|
|
active_clients2 = active_clients2 - (active_clients2 & ClientBitFlag(self.b_clientno - 16));
|
|
else
|
|
active_clients1 = active_clients1 - (active_clients1 & ClientBitFlag(self.b_clientno));
|
|
};
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
BotPreFrame & BotPostFrame, used to make the
|
|
bot easier to install
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
float () BotPreFrame =
|
|
{
|
|
if (self.b_clientno == -1)
|
|
return TRUE;
|
|
if (self.ishuman)
|
|
if (self.switch_wallhug)
|
|
ClientFixRankings();
|
|
if (self.b_frags != self.frags)
|
|
{
|
|
if (self.b_frags > self.frags)
|
|
{
|
|
if (pointcontents(self.origin) == CONTENT_LAVA)
|
|
bot_start_topic(10);
|
|
else
|
|
bot_start_topic(9);
|
|
}
|
|
else
|
|
bot_start_topic(2);
|
|
self.b_frags = self.frags;
|
|
if (!self.ishuman)
|
|
{
|
|
WriteByte(2, 14);
|
|
WriteByte(2, self.b_clientno);
|
|
WriteShort(2, self.frags);
|
|
}
|
|
}
|
|
DynamicWaypoint();
|
|
return FALSE;
|
|
};
|
|
float () BotPostFrame =
|
|
{
|
|
if (self.b_clientno == -1)
|
|
return TRUE;
|
|
if (self.ishuman)
|
|
BotImpulses();
|
|
return FALSE;
|
|
};
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Bot Chat code
|
|
|
|
The rest of this code is in bot_rank.qc
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
void(string h) BotSay = // simulate talking by composing a 'chat' message
|
|
{
|
|
WriteByte(MSG_ALL, 8);
|
|
WriteByte(MSG_ALL, 3);
|
|
WriteByte(MSG_ALL, 1);
|
|
WriteString(MSG_ALL, self.netname);
|
|
WriteByte(MSG_ALL, 8);
|
|
WriteByte(MSG_ALL, 3);
|
|
WriteByte(MSG_ALL, 2);
|
|
WriteString(MSG_ALL, h);
|
|
};
|
|
void() BotSayInit =
|
|
{
|
|
WriteByte(MSG_ALL, 8);
|
|
WriteByte(MSG_ALL, 3);
|
|
WriteByte(MSG_ALL, 1);
|
|
WriteString(MSG_ALL, self.netname);
|
|
};
|
|
void(string h) BotSay2 =
|
|
{
|
|
WriteByte(MSG_ALL, 8);
|
|
WriteByte(MSG_ALL, 3);
|
|
WriteByte(MSG_ALL, 2);
|
|
WriteString(MSG_ALL, h);
|
|
};
|
|
void(string h) BotSayTeam =
|
|
{
|
|
// FBX QW doesn't support teamplay...yet
|
|
};
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
BotInit
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
|
|
void() BotInit =
|
|
{
|
|
local entity ent, fisent;
|
|
local float numents = 0;
|
|
|
|
// spawn entities for the physics
|
|
ent = nextent(NIL);
|
|
max_clients = 0;
|
|
|
|
while(ent != NIL)
|
|
{
|
|
max_clients = max_clients + 1;
|
|
ent = nextent(ent);
|
|
}
|
|
|
|
ent = nextent(NIL);
|
|
fisent = NIL;
|
|
while (numents < max_clients)
|
|
{
|
|
|
|
phys_head = spawn();
|
|
if (fisent)
|
|
fisent._next = phys_head;
|
|
phys_head._last = fisent;
|
|
fisent = phys_head;
|
|
ent.phys_obj = phys_head;
|
|
phys_head.classname = "phys_obj";
|
|
phys_head.owner = ent;
|
|
numents = numents + 1;
|
|
ent = nextent(ent);
|
|
}
|
|
bot_map_load();
|
|
};
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Rankings 'utilities'. Written by Alan Kivlin,
|
|
this code just fools clients by sending precisely
|
|
the same network messages as when a real player
|
|
signs on to the server.
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
|
|
void(entity who) UpdateClient =
|
|
{
|
|
WriteByte(2, 14 );
|
|
WriteByte(2, who.b_clientno);
|
|
WriteShort(2, who.frags);
|
|
WriteByte(2, 36);
|
|
WriteByte(2, who.b_clientno);
|
|
WriteShort(2, 100 * (3 - who.b_skill));
|
|
WriteByte(2, 37); // update entertime
|
|
WriteByte(2, who.b_clientno); // client number
|
|
WriteLong(2, time - who.b_entertime); // client entertime
|
|
WriteByte(2, 40 ); // update userinfo
|
|
WriteByte(2, who.b_clientno); // client number
|
|
WriteLong(2, who.b_userid); // client userid
|
|
WriteByte(2, 92); // '\'
|
|
WriteByte(2, 98); // 'b'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 116); // 't'
|
|
WriteByte(2, 116); // 't'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 109); // 'm'
|
|
WriteByte(2, 99); // 'c'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 108); // 'l'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 114); // 'r'
|
|
WriteByte(2, 92); // '\'
|
|
if(who.b_pants > 9)
|
|
{
|
|
WriteByte(2, 49);
|
|
WriteByte(2, 38 + who.b_pants);
|
|
}
|
|
else
|
|
WriteByte(2, 48 + who.b_pants);
|
|
WriteByte(2, 92); // '\'
|
|
WriteByte(2, 116); // 't'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 112); // 'p'
|
|
WriteByte(2, 99); // 'c'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 108); // 'l'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 114); // 'r'
|
|
WriteByte(2, 92); // '\'
|
|
if(who.b_shirt > 9)
|
|
{
|
|
WriteByte(2, 49);
|
|
WriteByte(2, 38 + who.b_shirt);
|
|
}
|
|
else
|
|
WriteByte(2, 48 + who.b_shirt);
|
|
WriteByte(2, 92); // '\'
|
|
WriteByte(2, 116); // 't'
|
|
WriteByte(2, 101); // 'e'
|
|
WriteByte(2, 97); // 'a'
|
|
WriteByte(2, 109); // 'm'
|
|
WriteByte(2, 92); // '\'
|
|
WriteByte(2, 98); // 'b'
|
|
WriteByte(2, 111); // 'o'
|
|
WriteByte(2, 116); // 't'
|
|
// FIXME: do teams properly
|
|
// note this has no effect on infokey
|
|
WriteByte(2, 92 ); // '\'
|
|
WriteByte(2, 115); // 's'
|
|
WriteByte(2, 107); // 'k'
|
|
WriteByte(2, 105); // 'i'
|
|
WriteByte(2, 110); // 'n'
|
|
WriteByte(2, 92); // '\'
|
|
WriteByte(2, 98); // 'b'
|
|
WriteByte(2, 97); // 'a'
|
|
WriteByte(2, 115); // 's'
|
|
WriteByte(2, 101); // 'e'
|
|
WriteByte(2, 92); // '\'
|
|
WriteByte(2, 110); // 'n'
|
|
WriteByte(2, 97); // 'a'
|
|
WriteByte(2, 109); // 'm'
|
|
WriteByte(2, 101); // 'e'
|
|
WriteByte(2, 92); // '\'
|
|
WriteString( 2, who.netname);
|
|
};
|
|
|
|
float(float clientno) ClientBitFlag =
|
|
{
|
|
local float bitflag;
|
|
bitflag = 1;
|
|
while(clientno > 0)
|
|
{
|
|
bitflag = bitflag * 2;
|
|
clientno = clientno - 1;
|
|
}
|
|
return bitflag;
|
|
};
|
|
|
|
float() ClientNextAvailable =
|
|
{
|
|
local float clientno;
|
|
// I want to do this top down, but QW won't let me
|
|
clientno = 0;
|
|
while(clientno < max_clients)
|
|
{
|
|
clientno = clientno + 1;
|
|
|
|
if(!ClientIsActive(clientno))
|
|
return clientno;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
|
|
void(entity e1, entity e2, float flag) DeveloperLightning = {};
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
BotConnect and related functions.
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
entity(float num) GetClientEntity =
|
|
{
|
|
local entity upsy;
|
|
upsy = NIL;
|
|
num = num + 1;
|
|
while (num > 0)
|
|
{
|
|
num = num - 1;
|
|
upsy = nextent(upsy);
|
|
}
|
|
return upsy;
|
|
};
|
|
|
|
void(float whatbot, float whatskill) BotConnect =
|
|
{
|
|
local float f;
|
|
local entity uself;
|
|
|
|
f = ClientNextAvailable();
|
|
uself = self;
|
|
if(f == -1)
|
|
{
|
|
bprint(PRINT_HIGH, "Unable to connect a bot, server is full.\n");
|
|
return;
|
|
}
|
|
if(f > 16)
|
|
active_clients2 = active_clients2 | ClientBitFlag(f - 16);
|
|
else
|
|
active_clients1 = active_clients1 | ClientBitFlag(f);
|
|
bot_count = bot_count + 1;
|
|
self = GetClientEntity(f);
|
|
bot_start_topic(1);
|
|
self.b_clientno = f;
|
|
self.colormap = f + 1;
|
|
if (whatbot)
|
|
self.netname = BotName(whatbot);
|
|
else
|
|
self.netname = PickARandomName();
|
|
|
|
// players can set skill all weird, so leave these checks in
|
|
whatskill = rint(whatskill);
|
|
if (whatskill > 3)
|
|
whatskill = 3;
|
|
else if (whatskill < 0)
|
|
whatskill = 0;
|
|
self.b_skill = whatskill;
|
|
self.b_entertime = time;
|
|
self.team = self.b_pants + 1;
|
|
UpdateClient(self);
|
|
SetNewParms();
|
|
self.ishuman = 2;
|
|
ClientConnect();
|
|
PutClientInServer();
|
|
self = uself;
|
|
};
|
|
|
|
void(entity bot) BotDisconnect =
|
|
{
|
|
local entity uself;
|
|
uself = self;
|
|
self = bot;
|
|
|
|
bot_count = bot_count - 1;
|
|
|
|
ClientDisconnect();
|
|
|
|
if (self.b_clientno != -1)
|
|
{
|
|
// the bot's client number is not in use by a real player so we
|
|
// must remove it's entry in the rankings
|
|
// Quake engine sets all fields to 0, can only do the most important here
|
|
self.b_frags = self.frags = 0;
|
|
self.netname = "";
|
|
self.classname = "";
|
|
self.health = 0;
|
|
self.items = 0;
|
|
self.armorvalue = 0;
|
|
self.weaponmodel = "";
|
|
self.b_pants = 0;
|
|
self.b_shirt = 0;
|
|
self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = 0;
|
|
UpdateClient(self);
|
|
if(self.b_clientno > 16)
|
|
active_clients2 = active_clients2 - (active_clients2 & ClientBitFlag(self.b_clientno - 16));
|
|
else
|
|
active_clients1 = active_clients1 | (active_clients1 & ClientBitFlag(self.b_clientno));
|
|
self.b_clientno = -1;
|
|
}
|
|
self = uself;
|
|
};
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
BotInvalidClientNo
|
|
kicks a bot if a player connects and takes the bot's space
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
void(float clientno) BotInvalidClientNo =
|
|
{
|
|
local entity bot;
|
|
|
|
bot = GetClientEntity(clientno);
|
|
if(bot.b_clientno > 0)
|
|
{
|
|
if (!bot.ishuman)
|
|
{
|
|
bot.b_clientno = -1;
|
|
BotDisconnect(bot);
|
|
if(self.b_clientno > 16)
|
|
active_clients2 = active_clients2 | ClientBitFlag(self.b_clientno - 16);
|
|
else
|
|
active_clients1 = active_clients1 | ClientBitFlag(self.b_clientno);
|
|
//BotConnect(bot.b_num, bot.b_skill);
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
void() BotFrame =
|
|
{
|
|
local float num;
|
|
local string h;
|
|
|
|
h = infokey(NIL, "bot_options");
|
|
b_options = stof(h);
|
|
|
|
// for the sake of speed
|
|
sv_maxspeed = cvar("sv_maxspeed");
|
|
sv_gravity = cvar("sv_gravity");
|
|
sv_friction = cvar("sv_friction");
|
|
sv_accelerate = cvar("sv_accelerate");
|
|
sv_stopspeed = cvar("sv_stopspeed");
|
|
real_frametime = time - lasttime; // in QW frametime is fuxx0red
|
|
lasttime = time;
|
|
|
|
self = nextent(NIL);
|
|
num = 0;
|
|
while (num < max_clients)
|
|
{
|
|
if (self.ishuman == FALSE)
|
|
{
|
|
if (self.b_clientno > 0)
|
|
{
|
|
frik_obstacles();
|
|
CL_KeyMove();
|
|
SV_ClientThink();
|
|
SV_Physics_Client();
|
|
// this is sickening
|
|
if (self.phys_obj)
|
|
{
|
|
if (self.phys_obj.modelindex != self.modelindex)
|
|
{
|
|
setmodel(self.phys_obj, "progs/player.mdl");
|
|
self.phys_obj.modelindex = self.modelindex;
|
|
}
|
|
self.phys_obj.frame = self.frame;
|
|
self.phys_obj.angles = self.angles;
|
|
self.phys_obj.colormap = self.colormap;
|
|
self.phys_obj.effects = self.effects;
|
|
}
|
|
}
|
|
}
|
|
self = nextent(self);
|
|
num = num + 1;
|
|
}
|
|
};
|
|
|
|
/*
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Bot Impulses. Allows the player to perform bot
|
|
related functions.
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
*/
|
|
|
|
void() BotImpulses =
|
|
{
|
|
local float f;
|
|
local string h;
|
|
|
|
if (self.impulse == 100)
|
|
{
|
|
h = infokey(NIL, "skill");
|
|
f = stof(h);
|
|
BotConnect(0, f);
|
|
}
|
|
else if (self.impulse == 102)
|
|
KickABot();
|
|
else
|
|
return;
|
|
self.impulse = 0;
|
|
};
|