initial checkin of the frikbot source (not standalone)

This commit is contained in:
Bill Currie 2003-02-18 23:11:05 +00:00
parent 4bb2aaeb14
commit 5167b9f833
21 changed files with 9598 additions and 0 deletions

3
fbxa/bot.cfg Normal file
View file

@ -0,0 +1,3 @@
alias addbot "impulse 100"
alias removebot "impulse 102"
alias botcam "impulse 103"

1245
fbxa/bot.qc Normal file

File diff suppressed because it is too large Load diff

995
fbxa/bot_ai.qc Normal file
View file

@ -0,0 +1,995 @@
/***********************************************
* *
* FrikBot General AI *
* "The I'd rather be playing Quake AI" *
* *
***********************************************/
/*
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.
*/
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
target_onstack
checks to see if an entity is on the bot's stack
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float(entity scot) target_onstack =
{
if (scot == world)
return FALSE;
else if (self.target1 == scot)
return 1;
else if (self.target2 == scot)
return 2;
else if (self.target3 == scot)
return 3;
else if (self.target4 == scot)
return 4;
else
return FALSE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
target_add
adds a new entity to the stack, since it's a
LIFO stack, this will be the bot's new target1
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void(entity ent) target_add =
{
if (ent == world)
return;
if (target_onstack(ent))
return;
self.target4 = self.target3;
self.target3 = self.target2;
self.target2 = self.target1;
self.target1 = ent;
self.search_time = time + 5;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
target_drop
Removes an entity from the bot's target stack.
The stack will empty everything up to the object
So if you have target2 item_health, target1
waypoint, and you drop the health, the waypoint
is gone too.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void(entity ent) target_drop =
{
local float tg;
tg = target_onstack(ent);
if (tg == 1)
{
self.target1 = self.target2;
self.target2 = self.target3;
self.target3 = self.target4;
self.target4 = world;
}
else if (tg == 2)
{
self.target1 = self.target3;
self.target2 = self.target4;
self.target3 = self.target4 = world;
}
else if (tg == 3)
{
self.target1 = self.target4;
self.target2 = self.target3 = self.target4 = world;
}
else if (tg == 4)
self.target1 = self.target2 = self.target3 = self.target4 = world;
self.search_time = time + 5;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bot_lost
Bot has lost its target.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void(entity targ, float success) bot_lost =
{
if (!targ)
return;
target_drop(targ);
if (targ.classname == "waypoint")
targ.b_sound = targ.b_sound - (targ.b_sound & ClientBitFlag(self.b_clientno));
// find a new route
if (!success)
{
self.target1 = self.target2 = self.target3 = self.target4 = world;
self.last_way = FindWayPoint(self.current_way);
ClearMyRoute();
self.b_aiflags = 0;
}
else
{
if (targ.classname == "item_artifact_invisibility")
if (self.items & 524288)
bot_start_topic(3);
if (targ.flags & FL_ITEM)
{
if (targ.model == string_null)
targ._last = world;
else
targ._last = self;
}
}
if (targ.classname != "player")
targ.search_time = time + 5;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bot_check_lost
decide if my most immediate target should be
removed.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void(entity targ) bot_check_lost =
{
local vector dist;
dist = realorigin(targ) - self.origin;
dist_z = 0;
if (targ == world)
return;
// waypoints and items are lost if you get close enough to them
else if (targ.flags & FL_ITEM)
{
if (vlen(targ.origin - self.origin) < 32)
bot_lost(targ, TRUE);
else if (targ.model == string_null)
bot_lost(targ, TRUE);
}
else if (targ.classname == "waypoint")
{
if (!(self.b_aiflags & (AI_SNIPER | AI_AMBUSH)))
{
if (self.b_aiflags & AI_RIDE_TRAIN)
{
if (vlen(targ.origin - self.origin) < 48)
bot_lost(targ, TRUE);
}
else if (self.b_aiflags & AI_PRECISION)
{
if (vlen(targ.origin - self.origin) < 24)
bot_lost(targ, TRUE);
}
else if (vlen(targ.origin - self.origin) < 32)
bot_lost(targ, TRUE);
}
}
else if (targ.classname == "temp_waypoint")
{
if (vlen(targ.origin - self.origin) < 32)
bot_lost(targ, TRUE);
}
else if (targ.classname == "player")
{
if (targ.health <= 0)
bot_lost(targ, TRUE);
else if ((coop) || (teamplay && targ.team == self.team))
{
if (targ.target1.classname == "player")
{
if (!targ.target1.ishuman)
bot_lost(targ, TRUE);
}
else if (targ.teleport_time > time)
{
// try not to telefrag teammates
self.keys = self.keys & 960;
}
else if (vlen(targ.origin - self.origin) < 128)
{
if (vlen(targ.origin - self.origin) < 48)
frik_walkmove(self.origin - targ.origin);
else
{
self.keys = self.keys & 960;
bot_start_topic(4);
}
self.search_time = time + 5; // never time out
}
else if (!fisible(targ))
bot_lost(targ, FALSE);
}
else if (waypoint_mode > WM_LOADED)
{
if (vlen(targ.origin - self.origin) < 128)
{
bot_lost(targ, TRUE);
}
}
}
// buttons are lost of their frame changes
else if (targ.classname == "func_button")
{
if (targ.frame)
{
bot_lost(targ, TRUE);
if (self.enemy == targ)
self.enemy = world;
//if (self.target1)
// bot_get_path(self.target1, TRUE);
}
}
// trigger_multiple style triggers are lost if their thinktime changes
else if ((targ.movetype == MOVETYPE_NONE) && (targ.solid == SOLID_TRIGGER))
{
if (targ.nextthink >= time)
{
bot_lost(targ, TRUE);
//if (self.target1)
// bot_get_path(self.target1, TRUE);
}
}
// lose any target way above the bot's head
// FIXME: if the bot can fly in your mod..
if ((targ.origin_z - self.origin_z) > 64)
{
dist = targ.origin - self.origin;
dist_z = 0;
if (vlen(dist) < 32)
if (self.flags & FL_ONGROUND)
if(!frik_recognize_plat(FALSE))
bot_lost(targ, FALSE);
}
else if (targ.classname == "train")
{
if (frik_recognize_plat(FALSE))
bot_lost(targ, TRUE);
}
// targets are lost if the bot's search time has expired
if (time > self.search_time)
bot_lost(targ, FALSE);
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bot_handle_ai
This is a 0.10 addition. Handles any action
based b_aiflags.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() bot_handle_ai =
{
local entity newt;
local vector v;
// handle ai flags -- note, not all aiflags are handled
// here, just those that perform some sort of action
// wait is used by the ai to stop the bot until his search time expires / or route changes
if (self.b_aiflags & AI_WAIT)
self.keys = self.keys & 960;
if (self.b_aiflags & AI_DOORFLAG) // was on a door when spawned
{
b_temp3 = self;
self = self.last_way;
if (!frik_recognize_plat(FALSE)) // if there is nothing there now
{
newt = FindThing("door"); // this is likely the door responsible (crossfingers)
self = b_temp3;
if (self.b_aiflags & AI_DOOR_NO_OPEN)
{
if (newt.nextthink)
self.keys = self.keys & 960; // wait until it closes
else
{
bot_lost(self.last_way, FALSE);
}
}
else
{
if (newt.targetname)
{
newt = find(world, target, newt.targetname);
if (newt.health > 0)
{
self.enemy = newt;
bot_weapon_switch(1);
}
else
{
// target_drop(self.last_way);
target_add(newt);
// bot_get_path(newt, TRUE);
}
}
self.b_aiflags = self.b_aiflags - AI_DOORFLAG;
}
}
else
self = b_temp3;
}
if (self.b_aiflags & AI_JUMP)
{
if (self.flags & FL_ONGROUND)
{
bot_jump();
self.b_aiflags = self.b_aiflags - AI_JUMP;
}
}
else if (self.b_aiflags & AI_SUPER_JUMP)
{
if (self.weapon != 32)
self.impulse = 7;
else if (self.flags & FL_ONGROUND)
{
self.b_aiflags = self.b_aiflags - AI_SUPER_JUMP;
if (bot_can_rj(self))
{
bot_jump();
self.v_angle_x = self.b_angle_x = 80;
self.button0 = TRUE;
}
else
bot_lost(self.target1, FALSE);
}
}
if (self.b_aiflags & AI_SURFACE)
{
if (self.waterlevel > 2)
{
self.keys = KEY_MOVEUP;
self.button2 = TRUE; // swim!
}
else
self.b_aiflags = self.b_aiflags - AI_SURFACE;
}
if (self.b_aiflags & AI_RIDE_TRAIN)
{
// simple, but effective
// this can probably be used for a lot of different
// things, not just trains (door elevators come to mind)
b_temp3 = self;
self = self.last_way;
if (!frik_recognize_plat(FALSE)) // if there is nothing there now
{
self = b_temp3;
self.keys = self.keys & 960;
}
else
{
self = b_temp3;
if (frik_recognize_plat(FALSE))
{
v = realorigin(trace_ent) + trace_ent.origin - self.origin;
v_z = 0;
if (vlen(v) < 24)
self.keys = self.keys & 960;
else
{
self.b_aiflags = self.b_aiflags | AI_PRECISION;
self.keys = frik_KeysForDir(v);
}
}
}
}
if (self.b_aiflags & AI_PLAT_BOTTOM)
{
newt = FindThing("plat");
if (newt.state != 1)
{
v = self.origin - realorigin(newt);
v_z = 0;
if (vlen(v) > 96)
self.keys = self.keys & 960;
else
frik_walkmove(v);
}
else
self.b_aiflags = self.b_aiflags - AI_PLAT_BOTTOM;
}
if (self.b_aiflags & AI_DIRECTIONAL)
{
if ((normalize(self.last_way.origin - self.origin) * self.b_dir) > 0.4)
{
self.b_aiflags = self.b_aiflags - AI_DIRECTIONAL;
bot_lost(self.target1, TRUE);
}
}
if (self.b_aiflags & AI_SNIPER)
{
self.b_aiflags = (self.b_aiflags | AI_WAIT | AI_PRECISION) - AI_SNIPER;
// FIXME: Add a switch to wep command
// FIXME: increase delay?
}
if (self.b_aiflags & AI_AMBUSH)
{
self.b_aiflags = (self.b_aiflags | AI_WAIT) - AI_AMBUSH;
// FIXME: Add a switch to wep command
// FIXME: increase delay?
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bot_path
Bot will follow a route generated by the
begin_route set of functions in bot_way.qc.
This code, while it works pretty well, can get
confused
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() bot_path =
{
local entity jj, tele;
local vector org;
bot_check_lost(self.target1);
if (!self.target1)
{
self.keys=0;
return;
}
if (target_onstack(self.last_way))
return; // old waypoint still being hunted
jj = FindRoute(self.last_way);
if (!jj)
{
// this is an ugly hack
if (self.target1.current_way != self.last_way)
{
if (self.target1.classname != "temp_waypoint")
if (self.target1.classname != "player")
bot_lost(self.target1, FALSE);
}
return;
}
// update the bot's special ai features
// Readahed types are AI conditions to perform while heading to a waypoint
// point types are AI flags that should be executed once reaching a waypoint
self.b_aiflags = (jj.b_aiflags & AI_READAHEAD_TYPES) | (self.last_way.b_aiflags & AI_POINT_TYPES);
target_add(jj);
if (self.last_way)
{
if (CheckLinked(self.last_way, jj) == 2) // waypoints are telelinked
{
tele = FindThing("trigger_teleport"); // this is probbly the teleport responsible
target_add(tele);
}
traceline(self.last_way.origin, jj.origin, FALSE, self); // check for blockage
if (trace_fraction != 1)
{
if (trace_ent.classname == "door" && !(self.b_aiflags & AI_DOOR_NO_OPEN)) // a door blocks the way
{
// linked doors fix
if (trace_ent.owner)
trace_ent = trace_ent.owner;
if ((trace_ent.health > 0) && (self.enemy == world))
{
self.enemy = trace_ent;
bot_weapon_switch(1);
self.b_aiflags = self.b_aiflags | AI_BLIND; // nick knack paddy hack
}
else if (trace_ent.targetname)
{
tele = find(world, target, trace_ent.targetname);
if (tele.health > 0)
{
self.enemy = tele;
bot_weapon_switch(1);
}
else
{
// target_drop(jj);
target_add(tele);
// bot_get_path(tele, TRUE);
self.b_aiflags = self.b_aiflags | AI_BLIND; // give a bot a bone
return;
}
}
}
else if (trace_ent.classname == "func_wall")
{
// give up
bot_lost(self.target1, FALSE);
return;
}
}
}
// this is used for AI_DRIECTIONAL
self.b_dir = normalize(jj.origin - self.last_way.origin);
self.last_way = jj;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Bot Priority Look. What a stupid name. This is where
the bot finds things it wants to kill/grab.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
// priority scale
// 0 - 10 virtually ignore
// 10 - 30 normal item range
// 30 - 50 bot will consider this a target worth changing course for
// 50 - 90 bot will hunt these as vital items
// *!* Make sure you add code to bot_check_lost to remove the target *!*
float(entity thing) priority_for_thing =
{
local float thisp;
thisp = 0;
// This is the most executed function in the bot. Careful what you do here.
if ((thing.flags & FL_ITEM) && thing.model != string_null && thing.search_time < time)
{
// ugly hack
if (thing._last != self)
thisp = 20;
if (thing.classname == "item_artifact_super_damage")
thisp = 65;
else if (thing.classname == "item_artifact_invulnerability")
thisp = 65;
else if (thing.classname == "item_health")
{
if (thing.spawnflags & 2)
thisp = 55;
if (self.health < 40)
thisp = thisp + 50;
}
else if (thing.model == "progs/armor.mdl")
{
if (self.armorvalue < 200)
{
if (thing.skin == 2)
thisp = 60;
else if (self.armorvalue < 100)
thisp = thisp + 25;
}
}
else if (thing.classname == "weapon_supershotgun")
{
if (!(self.items & 2)) // IT_SUPER_SHOTGUN
thisp = 25;
}
else if (thing.classname == "weapon_nailgun")
{
if (!(self.items & 4)) // IT_NAILGUN
thisp = 30;
}
else if (thing.classname == "weapon_supernailgun")
{
if (!(self.items & 8)) // IT_SUPER_NAILGUN
thisp = 35;
}
else if (thing.classname == "weapon_grenadelauncher")
{
if (!(self.items & 16)) // IT_GRENADE_LAUNCHER
thisp = 45;
}
else if (thing.classname == "weapon_rocketlauncher")
{
if (!(self.items & 32)) // IT_ROCKET_LAUNCHER
thisp = 60;
}
else if (thing.classname == "weapon_lightning")
{
if (!(self.items & 64)) // IT_LIGHTNING
thisp = 50;
}
}
else if ((thing.flags & FL_MONSTER) && thing.health > 0)
thisp = 45;
else if (thing.classname == "player")
{
if (thing.health > 0)
{
if (thing == self)
return 0;
else
{
if (thing.items & IT_INVISIBILITY) //FIXME
thisp = 2;
else if (coop)
{
thisp = 100;
if (thing.target1.classname == "player")
if (!thing.target1.ishuman)
return 0;
}
else if (teamplay && thing.team == self.team)
{
thisp = 100;
if (thing.target1.classname == "player")
return 0;
}
else thisp = 30;
}
}
}
else if (thing.classname == "waypoint")
{
if (thing.b_aiflags & AI_SNIPER)
thisp = 30;
else if (thing.b_aiflags & AI_AMBUSH)
thisp = 30;
}
if (pointcontents(thing.origin) < -3)
return 0;
if (thisp)
{
if (thing.current_way)
{
// check to see if it's unreachable
if (thing.current_way.items == -1)
return 0;
else
thisp = thisp + (13000 - thing.current_way.items) * 0.05;
}
}
return thisp;
};
void(float scope) bot_look_for_crap =
{
local entity foe, best;
local float thatp, bestp, dist;
if (scope == 1)
foe = findradius(self.origin, 13000);
else
foe = findradius(self.origin, 500);
bestp = 1;
while(foe)
{
thatp = priority_for_thing(foe);
if (thatp)
if (!scope)
if (!sisible(foe))
thatp = 0;
if (thatp > bestp)
{
bestp = thatp;
best = foe;
dist = vlen(self.origin - foe.origin);
}
foe = foe.chain;
}
if (best == world)
return;
if (!target_onstack(best))
{
target_add(best);
if (scope)
{
bot_get_path(best, FALSE);
self.b_aiflags = self.b_aiflags | AI_WAIT;
}
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bot_angle_set
Sets the bots look keys & b_angle to point at
the target - used for fighting and just
generally making the bot look good.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() bot_angle_set =
{
local float h;
local vector view;
if (self.enemy)
{
if (self.enemy.items & 524288)
if (random() > 0.2)
return;
if (self.missile_speed == 0)
self.missile_speed = 10000;
if (self.enemy.solid == SOLID_BSP)
{
view = (((self.enemy.absmin + self.enemy.absmax) * 0.5) - self.origin);
}
else
{
h = vlen(self.enemy.origin - self.origin) / self.missile_speed;
if (self.enemy.flags & FL_ONGROUND)
view = self.enemy.velocity * h;
else
view = (self.enemy.velocity - (sv_gravity * '0 0 1') * h) * h;
view = self.enemy.origin + view;
// FIXME: ?
traceline(self.enemy.origin, view, FALSE, self);
view = trace_endpos;
if (self.weapon == 32)
view = view - '0 0 22';
view = normalize(view - self.origin);
}
view = vectoangles(view);
view_x = view_x * -1;
self.b_angle = view;
}
else if (self.target1)
{
view = realorigin(self.target1);
if (self.target1.flags & FL_ITEM)
view = view + '0 0 48';
view = view - (self.origin + self.view_ofs);
view = vectoangles(view);
view_x = view_x * -1;
self.b_angle = view;
}
else
self.b_angle_x = 0;
// HACK HACK HACK HACK
// The bot falls off ledges a lot because of "turning around"
// so let the bot use instant turn around when not hunting a player
if (self.b_skill == 3)
{
self.keys = self.keys & 63;
self.v_angle = self.b_angle;
while (self.v_angle_x < -180)
self.v_angle_x = self.v_angle_x + 360;
while (self.v_angle_x > 180)
self.v_angle_x = self.v_angle_x - 360;
}
else if ((self.enemy == world || self.enemy.movetype == MOVETYPE_PUSH) && self.target1.classname != "player")
{
self.keys = self.keys & 63;
self.v_angle = self.b_angle;
while (self.v_angle_x < -180)
self.v_angle_x = self.v_angle_x + 360;
while (self.v_angle_x > 180)
self.v_angle_x = self.v_angle_x - 360;
}
else if (self.b_skill < 2) // skill 2 handled in bot_phys
{
if (self.b_angle_x > 180)
self.b_angle_x = self.b_angle_x - 360;
self.keys = self.keys & 63;
if (angcomp(self.b_angle_y, self.v_angle_y) > 10)
self.keys = self.keys | KEY_LOOKLEFT;
else if (angcomp(self.b_angle_y, self.v_angle_y) < -10)
self.keys = self.keys | KEY_LOOKRIGHT;
if (angcomp(self.b_angle_x, self.v_angle_x) < -10)
self.keys = self.keys | KEY_LOOKUP;
else if (angcomp(self.b_angle_x, self.v_angle_x) > 10)
self.keys = self.keys | KEY_LOOKDOWN;
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
BotAI
This is the main ai loop. Though called every
frame, the ai_time limits it's actual updating
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float stagger_think;
void() BotAI =
{
// am I dead? Fire randomly until I respawn
// health < 1 is used because fractional healths show up as 0 on normal player
// status bars, and the mod probably already compensated for that
if (self.health < 1)
{
self.button0 = floor(random() * 2);
self.button2 = 0;
self.keys = 0;
self.b_aiflags = 0;
ClearMyRoute();
self.target1 = self.target2 = self.target3 = self.target4 = self.enemy = world;
self.last_way = world;
return;
}
// stagger the bot's AI out so they all don't think at the same time, causing game
// 'spikes'
if (self.b_skill < 2)
{
if (self.ai_time > time)
return;
self.ai_time = time + 0.05;
if (bot_count > 0)
{
if ((time - stagger_think) < (0.1 / bot_count))
self.ai_time = self.ai_time + 0.1 / (2 * bot_count);
}
else
return;
}
if (self.view_ofs == '0 0 0')
bot_start_topic(7);
stagger_think = time;
// shut the bot's buttons off, various functions will turn them on by AI end
self.button2 = 0;
self.button0 = 0;
// target1 is like goalentity in normal Quake monster AI.
// it's the bot's most immediate target
if (route_table == self)
{
if (busy_waypoints <= 0)
{
if (waypoint_mode < WM_EDITOR)
bot_look_for_crap(TRUE);
}
self.b_aiflags = 0;
self.keys = 0;
}
else if (self.target1)
{
frik_movetogoal();
bot_path();
}
else
{
if (waypoint_mode < WM_EDITOR)
{
if(self.route_failed)
{
frik_bot_roam();
self.route_failed = 0;
}
else if(!begin_route())
{
bot_look_for_crap(FALSE);
}
self.keys = 0;
}
else
{
self.b_aiflags = AI_WAIT;
self.keys = 0;
}
}
// bot_angle_set points the bot at it's goal (self.enemy or target1)
bot_angle_set();
// fight my enemy. Enemy is probably a field QC coders will most likely use a lot
// for their own needs, since it's unused on a normal player
// FIXME
if (self.enemy)
bot_fight_style();
else if (random() < 0.2)
if (random() < 0.2)
bot_weapon_switch(-1);
bot_dodge_stuff();
// checks to see if bot needs to start going up for air
if (self.waterlevel > 2)
{
if (time > (self.air_finished - 2))
{
traceline (self.origin, self.origin + '0 0 6800', TRUE, self);
if (trace_inopen)
{
self.keys = KEY_MOVEUP;
self.button2 = TRUE; // swim!
return; // skip ai flags for now - this is life or death
}
}
}
// b_aiflags handling
if (self.b_aiflags)
bot_handle_ai();
else
bot_chat(); // don't want chat to screw him up if he's rjing or something
};

1354
fbxa/bot_ed.qc Normal file

File diff suppressed because it is too large Load diff

458
fbxa/bot_fight.qc Normal file
View file

@ -0,0 +1,458 @@
/***********************************************
* *
* FrikBot Fight Code *
* "Because I ain't no Ghandi code" *
* *
***********************************************/
/*
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.
*/
.entity avoid;
float(entity e) bot_size_player =
{
local float sz;
sz = e.health + e.armorvalue * e.armortype;
if (e.weapon == 32)
sz = sz + 60;
else if (e.weapon == 64)
sz = sz + 60;
else if (e.weapon == 16)
sz = sz + 50;
else if (e.weapon == 8)
sz = sz + 50;
else if (e.weapon == 4)
sz = sz + 40;
else if (e.weapon == 2)
sz = sz + 40;
else if (e.weapon == 1)
sz = sz + 10;
else if (e.weapon == 4096)
sz = sz - 50;
if (e.items & 4194304) // Quad
sz = sz + 200;
if (e.items & 1048576) // Invul
sz = sz + 300;
if (e.items & 524288) // Invis
sz = sz + 250;
return sz;
};
void() bot_dodge_stuff =
{
local entity foe;
local float foedist, avdist, scandist, foesz, flen, tsz;
local vector v;
if (waypoint_mode > WM_LOADED)
return;
self.avoid = world;
if (self.enemy)
{
v = self.origin - realorigin(self.enemy);
foedist = vlen(v);
foesz = bot_size_player(self.enemy);
}
else
{
foedist = 3000;
foesz = 9999999;
}
avdist = 256;
foe = find(world, classname, "grenade");
while(foe)
{
flen = vlen(foe.origin - self.origin);
if (flen < avdist)
{
avdist = flen;
self.avoid = foe;
}
foe = find(foe, classname, "grenade");
}
if (!self.avoid)
{
foe = find(world, classname, "missile");
while(foe)
{
if (foe.owner != self)
{
flen = vlen(foe.origin - self.origin);
if (flen < avdist)
{
avdist = flen;
self.avoid = foe;
}
}
foe = find(foe, classname, "missile");
}
if (!self.avoid)
{
foe = find(world, classname, "spike");
while(foe)
{
if (foe.owner != self)
{
flen = vlen(foe.origin - self.origin);
if (flen < avdist)
{
avdist = flen;
self.avoid = foe;
}
}
foe = find(foe, classname, "spike");
}
}
}
if (coop)
{
if (!self.enemy)
{
foe = findradius(self.origin, foedist);
while(foe)
{
if(foe.flags & FL_MONSTER)
{
if(foe.health > 0)
{
flen = vlen(foe.origin - self.origin);
if (flen < foedist)
{
tsz = bot_size_player(foe);
if (tsz < foesz)
{
if (fisible(foe))
{
self.enemy = foe;
foedist = flen;
foesz = tsz;
}
}
}
}
}
foe = foe.chain;
}
}
}
else
{
foe = player_head;
while(foe)
{
if(foe != self)
{
if (foe.modelindex != 0)
{
if (foe.health > 0)
{
if (!(teamplay && self.team == foe.team))
{
flen = vlen(foe.origin - self.origin);
if (flen < foedist)
{
tsz = bot_size_player(foe);
if (tsz < foesz)
{
if (fov(foe) || foe.b_sound > time || self.b_skill == 3)
{
if (fisible(foe))
{
self.enemy = foe;
foedist = vlen(foe.origin - self.origin);
}
}
}
}
}
}
}
}
foe = foe._next;
}
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
weapon_range
_x "sweet spot range" - try to maintain this range if possible
_y minimum range bot can be to be effective (rl/gl) (move away)
_z maximum range bot can be to be effective (lg/axe) (move in)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
vector(float wep) weapon_range =
{
if (wep == 4096) // IT_AXE
return '48 0 64';
else if (wep == 1) // IT_SHOTGUN
return '128 0 99999';
else if (wep == 2) // IT_SUPER_SHOTGUN
return '128 0 99999';
else if (wep == 4) // IT_NAILGUN
return '180 0 3000';
else if (wep == 8) // IT_SUPER_NAILGUN
return '180 0 3000';
else if (wep == 16) // IT_GRENADE_LAUNCHER
return '180 48 3000';
else if (wep == 32) // IT_ROCKET_LAUNCHER
return '180 48 3000';
else if (wep == 64) // IT_LIGHTNING
return '350 0 512';
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bot_weapon_switch
Pick a weapon based on range / ammo
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void(float brange) bot_weapon_switch =
{
local float it, flag, pulse;
local vector v;
it = self.items & 127;
while(it)
{
if ((self.ammo_rockets >= 1) && (it & 32))
{
flag = 32;
pulse = 7;
}
else if (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & 64))
{
flag = 64;
pulse = 8;
}
else if(self.ammo_nails >= 2 && (it & 8))
{
flag = 8;
pulse = 5;
}
else if ((self.ammo_rockets >= 1) && (it & 16))
{
flag = 16;
pulse = 6;
}
else if(self.ammo_shells >= 2 && (it & 2))
{
flag = 2;
pulse = 3;
}
else if(self.ammo_nails >= 1 && (it & 4))
{
flag = 4;
pulse = 4;
}
else if(self.ammo_shells >= 1 && (it & 1))
{
flag = 1;
pulse = 2;
}
else
{
if (pulse)
self.impulse = pulse;
return;
}
if (brange == -1)
{
if (pulse)
self.impulse = pulse;
return;
}
v = weapon_range(flag);
if (brange < v_y || brange > v_z)
it = it - flag;
else
{
if (pulse)
self.impulse = pulse;
return;
}
}
};
void() bot_shoot =
{
// quick little function to stop making him shoot the wrong way ! Argh
local float g;
g = angcomp(self.v_angle_x, self.b_angle_x);
if (fabs(g) > 30)
return; // argh, too far away
g = angcomp(self.v_angle_y, self.b_angle_y);
if (fabs(g) > 30)
return; // not again!
self.button0 = TRUE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Bot_fight_style
This is the core of the bot's thinking when
attacking an enemy.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() bot_fight_style =
{
local vector v, v1, v2, org;
local float foedist, mysz, foesz;
if (self.enemy.health <= 0)
{
self.enemy = world;
return;
}
else if (!self.enemy.takedamage)
{
self.enemy = world;
return;
}
else if (!fisible(self.enemy))
{
self.enemy = world;
return;
}
org = realorigin(self.enemy);
makevectors(self.v_angle);
// decide if I should shoot
foedist = vlen(org - self.origin);
v = weapon_range(self.weapon);
if (foedist > v_y && foedist < v_z)
{
traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * v_z, FALSE, self);
if (vlen(trace_endpos - (self.origin + self.view_ofs)) >= v_y)
{
// try to avoid shooting teammates
if (trace_ent.classname == "player")
if ((trace_ent.team == self.team && teamplay) || (coop))
return;
bot_shoot();
}
}
else
bot_weapon_switch(foedist);
if (!(self.b_aiflags & (AI_PRECISION | AI_BLIND | AI_OBSTRUCTED)))
{
foesz = bot_size_player(self.enemy);
mysz = bot_size_player(self) + 5;
if (foesz > mysz)
{
if (teamplay)
{
if (random() < 0.02)
{
bot_start_topic(5);
self.b_chattime = 1;
}
}
return;
}
else if (mysz < 140)
return;
else if (self.avoid)
{
if (self.avoid.velocity)
v = self.avoid.velocity;
else
v = normalize(self.avoid.origin - self.origin);
v1_x = v_y;
v1_y = v_y * -1;
v2_x = v_y;
v2_y = v_y * -1;
foedist = vlen(self.avoid.origin - (self.origin + v1));
if (foedist < vlen(self.avoid.origin - (self.origin + v2)))
frik_walkmove(v2);
else
frik_walkmove(v1);
}
else if (!self.enemy.flags & FL_MONSTER)
{
if (foedist + 32 < v_x)
frik_walkmove(self.origin - org);
else if (foedist - 32 > v_x)
frik_walkmove(org - self.origin);
else if (self.wallhug)
frik_walkmove(v_right);
else
frik_walkmove(v_right * -1);
}
}
else
{
foesz = bot_size_player(self.enemy);
mysz = bot_size_player(self) + 5;
if (foesz > mysz)
return;
else if (mysz < 140)
return;
self.keys = self.keys & 960;
}
};

777
fbxa/bot_misc.qc Normal file
View file

@ -0,0 +1,777 @@
/***********************************************
* *
* FrikBot Misc Code *
* "Because you can't name it anything else" *
* *
***********************************************/
/*
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.
*/
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
BotName
Sets bot's name and colors
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
string(float r) BotName =
{
self.b_num = r;
if (r == 1)
{
self.b_pants = 11;
self.b_shirt = 0;
return "Vincent";
}
else if (r == 2)
{
self.b_pants = 1;
self.b_shirt = 3;
return "Bishop";
}
else if (r == 3)
{
self.b_pants = 13;
self.b_shirt = 2;
return "Nomad";
}
else if (r == 4)
{
self.b_pants = 7;
self.b_shirt = 6;
return "Hudson";
}
else if (r == 5)
{
self.b_pants = 12;
self.b_shirt = 6;
return "Lore";
}
else if (r == 6)
{
self.b_pants = 4;
self.b_shirt = 4;
return "Servo";
}
else if (r == 7)
{
self.b_pants = 2;
self.b_shirt = 5;
return "Gort";
}
else if (r == 8)
{
self.b_pants = 10;
self.b_shirt = 3;
return "Kryten";
}
else if (r == 9)
{
self.b_pants = 9;
self.b_shirt = 4;
return "Pimp Bot";
}
else if (r == 10)
{
self.b_pants = 4;
self.b_shirt = 7;
return "Max";
}
else if (r == 11)
{
self.b_pants = 3;
self.b_shirt = 11;
return "Marvin";
}
else if (r == 12)
{
self.b_pants = 13;
self.b_shirt = 12;
return "Erwin";
}
else if (r == 13)
{
self.b_pants = 11;
self.b_shirt = 2;
return "FrikBot";
}
else if (r == 14)
{
self.b_pants = 0;
self.b_shirt = 2;
return "Krosis";
}
else if (r == 15)
{
self.b_pants = 8;
self.b_shirt = 9;
return "Gypsy";
}
else if (r == 16)
{
self.b_pants = 5;
self.b_shirt = 10;
return "Hal";
}
};
string () PickARandomName =
{
if (bot_count > 16)
return "player";
local float y, test;
local string h;
local entity t;
y = TRUE;
while(y)
{
test = ceil(random() * 16);
h = BotName(test);
t = find(world, netname, h);
if (t == world)
y = FALSE;
}
return h;
};
// I didn't like the old code so this is very stripped down
entity b_originator;
float b_topic;
/* FBX Topics
b_originator == self
1 - sign on
2 - killed targ
3 - team message "friendly eyes"
4 - team message "on your back"
5 - team message "need back up"
6 - excuses
----
7 - gameover
----
8 - welcoming someone onto server
9 - ridicule lost frag (killed self?)
10 - ridicule lost frag (lava)
11 - lag
b_originator == targ
*/
void(float tpic) bot_start_topic =
{
if (random() < 0.2)
{
b_topic = tpic;
b_originator = self;
}
else
b_topic = 0;
};
void() bot_chat =
{
local float r;
if (b_options & OPT_NOCHAT)
return;
r = ceil (random() * 6);
if (self.b_chattime > time)
{
if (self.b_skill < 2)
self.keys = self.button0 = self.button2 = 0;
return;
}
else if (self.b_chattime)
{
if (b_topic == 1)
{
if (b_originator == self)
{
if (r == 1)
{
BotSay(": lo all\n");
bot_start_topic(8);
}
else if (r == 2)
{
BotSay(": hey everyone\n");
bot_start_topic(8);
}
else if (r == 3)
{
BotSay(": prepare to be fragged!\n");
bot_start_topic(0);
}
else if (r == 4)
{
BotSay(": boy this is laggy\n");
bot_start_topic(11);
}
else if (r == 5)
{
BotSay(": #mm getting some lag here\n");
bot_start_topic(11);
}
else
{
BotSay(": hi everyone\n");
bot_start_topic(8);
}
}
}
else if (b_topic == 2)
{
if (b_originator == self)
{
if (r == 1)
BotSay(": take that\n");
else if (r == 2)
BotSay(": yehaww!\n");
else if (r == 3)
BotSay(": wh00p\n");
else if (r == 4)
BotSay(": j00_sawk();\n");
else if (r == 5)
BotSay(": i rule\n");
else
BotSay(": eat that\n");
bot_start_topic(0);
}
}
else if (b_topic == 3)
{
if (b_originator == self)
{
if (r < 3)
BotSayTeam(": friendly eyes\n");
else
BotSayTeam(": team eyes\n");
bot_start_topic(0);
}
}
else if (b_topic == 4)
{
if (b_originator == self)
{
if (r < 3)
BotSayTeam(": on your back\n");
else
BotSayTeam(": I'm with you\n");
bot_start_topic(0);
}
}
else if (b_topic == 5)
{
if (b_originator == self)
{
if (r < 3)
BotSayTeam(": I need help\n");
else
BotSayTeam(": need backup\n");
bot_start_topic(0);
}
}
else if (b_topic == 6)
{
if (b_originator == self)
{
if (r == 1)
{
BotSay(": sun got in my eyes\n");
bot_start_topic(0);
}
else if (r == 2)
{
BotSay(": mouse needs cleaning\n");
bot_start_topic(0);
}
else if (r == 3)
{
BotSay(": i meant to do that\n");
bot_start_topic(0);
}
else if (r == 4)
{
BotSay(": lag\n");
bot_start_topic(11);
}
else if (r == 5)
{
BotSay(": killer lag\n");
bot_start_topic(11);
}
else
{
BotSay(": 100% lag\n");
bot_start_topic(11);
}
}
}
else if (b_topic == 7)
{
if (r == 1)
BotSay(": gg\n");
else if (r == 2)
BotSay(": gg all\n");
else if (r == 3)
BotSay(": that was fun\n");
else if (r == 4)
BotSay(": good game\n");
else if (r == 5)
BotSay(": pah\n");
else
BotSay(": hrm\n");
bot_start_topic(0);
}
else if (b_topic == 8)
{
if (b_originator != self)
{
if (r == 1)
{
BotSay(": heya\n");
bot_start_topic(0);
}
else if (r == 2)
{
BotSay(": welcome\n");
bot_start_topic(0);
}
else if (r == 3)
{
BotSayInit();
BotSay2(": hi ");
BotSay2(b_originator.netname);
BotSay2("\n");
bot_start_topic(0);
}
else if (r == 4)
{
BotSayInit();
BotSay2(": hey ");
BotSay2(b_originator.netname);
BotSay2("\n");
bot_start_topic(0);
}
else if (r == 5)
{
BotSay(": howdy\n");
bot_start_topic(0);
}
else
{
BotSay(": lo\n");
bot_start_topic(0);
}
}
}
else if (b_topic == 9)
{
if (b_originator != self)
{
if (r == 1)
BotSay(": hah\n");
else if (r == 2)
BotSay(": heheh\n");
else if (r == 3)
{
BotSayInit();
BotSay2(": good work ");
BotSay2(b_originator.netname);
BotSay2("\n");
}
else if (r == 4)
{
BotSayInit();
BotSay2(": nice1 ");
BotSay2(b_originator.netname);
BotSay2("\n");
}
else if (r == 5)
BotSay(": lol\n");
else
BotSay(": :)\n");
b_topic = 6;
}
}
else if (b_topic == 10)
{
if (b_originator != self)
{
if (r == 1)
BotSay(": have a nice dip?\n");
else if (r == 2)
BotSay(": bah I hate levels with lava\n");
else if (r == 3)
{
BotSayInit();
BotSay2(": good job ");
BotSay2(b_originator.netname);
BotSay2("\n");
}
else if (r == 4)
{
BotSayInit();
BotSay2(": nice backflip ");
BotSay2(b_originator.netname);
BotSay2("\n");
}
else if (r == 5)
BotSay(": watch your step\n");
else
BotSay(": hehe\n");
b_topic = 6;
}
}
else if (b_topic == 11)
{
if (b_originator != self)
{
if (r == 1)
{
BotSayInit();
BotSay2(": yeah right ");
BotSay2(b_originator.netname);
BotSay2("\n");
bot_start_topic(0);
}
else if (r == 2)
{
BotSay(": ping\n");
bot_start_topic(0);
}
else if (r == 3)
{
BotSay(": shuddup, you're an lpb\n");
bot_start_topic(0);
}
else if (r == 4)
{
BotSay(": lag my eye\n");
bot_start_topic(0);
}
else if (r == 5)
{
BotSay(": yeah\n");
bot_start_topic(11);
}
else
{
BotSay(": totally\n");
bot_start_topic(11);
}
}
}
self.b_chattime = 0;
}
else if (b_topic)
{
if (random() < 0.5)
{
if (self == b_originator)
{
if (b_topic <= 7)
self.b_chattime = time + 2;
}
else
{
if (b_topic >= 7)
self.b_chattime = time + 2;
}
}
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Kick A Bot.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() KickABot =
{
local entity ty;
ty = find(world, classname, "player");
while (ty != world)
{
if (!(ty.ishuman))
{
BotDisconnect(ty);
ty.ishuman = TRUE;
ty = world;
}
else
ty = find(ty, classname, "player");
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Simplified origin checking.
God, I wish I had inline
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
vector(entity ent) realorigin =
{
// even more simplified...
return (ent.absmin + ent.absmax) * 0.5;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
fisible
a version of visible that checks for corners
of the bounding boxes
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float (entity targ) fisible =
{
local vector spot1, org;
local float thruwater, pc1, pc2;
org = realorigin(targ);
spot1 = self.origin + self.view_ofs;
if (targ.solid == SOLID_BSP)
{
traceline (spot1, org, TRUE, self);
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
}
else
{
pc1 = pointcontents(org);
pc2 = pointcontents(spot1);
if (targ.classname == "player")
thruwater = FALSE;
else if (pc1 == CONTENT_LAVA)
return FALSE;
else
thruwater = TRUE;
}
if (pc1 < -1) // targ's origin is in water or other liquid
{
if (pc2 != pc1)
{
// look for their head
traceline (spot1, org + targ.mins, TRUE, self);
// cross the water check
if (trace_inopen)
if (trace_inwater)
if (!thruwater)
return FALSE;
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
}
}
else
{
if (pc2 != pc1)
{
traceline (spot1, org + targ.maxs, TRUE, self);
if (trace_inopen)
if (trace_inwater)
if (!thruwater)
return FALSE;
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
}
}
traceline (spot1, org, TRUE, self);
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
traceline (spot1, org + targ.maxs, TRUE, self);
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
traceline (spot1, org + targ.mins, TRUE, self);
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Wisible
goes through movable brushes/entities, used
for waypoints
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
// this is used for waypoint stuff....
float (entity targ1, entity targ2) wisible =
{
local vector spot1, spot2;
local entity ignore;
spot1 = targ1.origin;
spot2 = realorigin(targ2);
ignore = self;
do
{
traceline (spot1, spot2, TRUE, ignore);
spot1 = realorigin(trace_ent);
ignore = trace_ent;
} while ((trace_ent != world) && (trace_fraction != 1));
if (trace_endpos == spot2)
return TRUE;
else
return FALSE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
sisible
Now this is getting ridiculous. Simple visible,
used when we need just a simple traceline nothing else
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float (entity targ) sisible =
{
traceline (self.origin, targ.origin, TRUE, self);
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
angcomp
subtracts one angle from another
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float (float y1, float y2) angcomp =
{
y1 = frik_anglemod(y1);
y2 = frik_anglemod(y2);
local float answer;
answer = y1 - y2;
if (answer > 180)
answer = (360 - answer) * -1;
else if (answer < -180)
answer = answer + 360;
return answer;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
fov
is the entity in the bot's field of view
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float (entity targ) fov =
{
local vector yawn;
local float g;
yawn = realorigin(targ);
yawn = (yawn + targ.view_ofs) - (self.origin + self.view_ofs);
yawn = normalize(yawn);
yawn = vectoangles(yawn);
g = angcomp(self.v_angle_x, yawn_x);
if (fabs(g) > 45)
return FALSE;
g = angcomp(self.v_angle_y, yawn_y);
if (fabs(g) > 60)
return FALSE;
return TRUE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
frik_anglemod
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
float(float v) frik_anglemod =
{
return v - floor(v/360) * 360;
};

512
fbxa/bot_move.qc Normal file
View file

@ -0,0 +1,512 @@
/***********************************************
* *
* FrikBot Movement AI *
* "The slightly better movement AI" *
* *
***********************************************/
/*
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.
*/
void() bot_jump =
{
// TODO check for precision, etc.
self.button2 = TRUE;
};
float(entity e) bot_can_rj =
{
// this returns true of the bot can rocket/superjump/hook
// if your mod doesn't have an RL you can just return FALSE all the time
// if it has a hook or some other means for the bot to get to high places
// you can check here for that capability
// am I dumb?
if (e.b_skill == 0)
return FALSE;
// quad = bad
if (e.items & 4194304)
return FALSE;
// do I have rockets & RL?
if (!((e.items & 32) && (e.ammo_rockets > 0)))
return FALSE;
// do I have pent?
if (e.items & 1048576)
return TRUE;
if (e.health > 50)
return TRUE;
else
return FALSE;
};
float(float flag) frik_recognize_plat =
{
if ((self.classname != "waypoint") && !(self.flags & FL_ONGROUND))
return FALSE;
traceline(self.origin, self.origin - '0 0 64', TRUE, self);
if (trace_ent != world)
{
if (flag) // afect bot movement too
{
if (self.keys & KEY_MOVEUP)
{
if (trace_ent.velocity_z > 0)
self.keys = self.keys & 960; // 960 is all view keys
}
else if (self.keys & KEY_MOVEDOWN)
{
if (trace_ent.velocity_z < 0)
self.keys = self.keys & 960;
}
}
return TRUE;
}
else
return FALSE;
};
float(vector sdir) frik_KeysForDir =
{
local vector keydir;
local float outkeys, tang;
outkeys = 0;
if (sdir_x || sdir_y)
{
// Everything is tested against 60 degrees,
// this allows the bot to overlap the keys
// 30 degrees on each diagonal 45 degrees
// might look more realistic
keydir = vectoangles(sdir);
tang = angcomp(keydir_y, self.v_angle_y);
if ((tang <= 150) && (tang >= 30))
outkeys = outkeys + KEY_MOVELEFT;
else if ((tang >= -150) && (tang <= -30))
outkeys = outkeys + KEY_MOVERIGHT;
if (fabs(tang) <= 60)
outkeys = outkeys + KEY_MOVEFORWARD;
else if (fabs(tang) >= 120)
outkeys = outkeys + KEY_MOVEBACK;
}
if (sdir_z > 0.7)
outkeys = outkeys + KEY_MOVEUP;
else if (sdir_z < 0.7)
outkeys = outkeys + KEY_MOVEDOWN;
return outkeys;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
frik_obstructed
Bot has hit a ledge or wall that he should
manuever around.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void(vector whichway, float danger) frik_obstructed =
{
local float dist;
local vector disway, org;
// TODO: something
if (self.b_aiflags & AI_BLIND)
return;
org = realorigin(self.target1);
if (danger)
{
self.b_aiflags = self.b_aiflags | AI_DANGER;
self.keys = frik_KeysForDir('0 0 0' - whichway);
}
if (self.b_aiflags & AI_PRECISION)
return;
if (self.target1)
{
if (self.b_aiflags & AI_OBSTRUCTED)
{
if (!(self.b_aiflags & AI_DANGER))
{
self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
return;
}
else if (!danger)
return;
}
self.obs_dir = whichway;
disway_x = whichway_y * -1;
disway_y = whichway_x;
dist = vlen(org - (self.origin + disway));
disway_x = whichway_y;
disway_y = whichway_x * -1;
self.wallhug = vlen(org - (self.origin + disway)) > dist;
self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED;
}
else
{
disway_x = whichway_y * -1;
disway_y = whichway_x;
dist = vlen(disway - self.obs_dir);
disway_x = whichway_y;
disway_y = whichway_x * -1;
self.wallhug = vlen(disway - self.obs_dir) < dist;
self.obs_dir = whichway;
self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED;
}
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
frik_obstacles
Detects small bumps the bot needs to jump over
or ledges the bot should avoid falling in.
Also responsible for jumping gaps.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() frik_obstacles =
{
local vector start, stop, ang;
local float test, conts, dist, hgt;
if (!(self.flags & FL_ONGROUND))
return;
if (self.b_aiflags & AI_BLIND)
return;
ang = normalize(self.velocity);
ang_z = 0;
start = self.origin + ang * 32; // ahem
start_z = self.origin_z + self.maxs_z;
stop = start;
stop_z = self.origin_z + self.mins_z;
traceline(start, stop - '0 0 256', TRUE, self);
if (trace_allsolid || trace_startsolid)
return;
hgt = trace_endpos_z - stop_z;
if (hgt > 18)
{
bot_jump();
return;
}
if (hgt >= 0)
return;
conts = pointcontents(trace_endpos + '0 0 4');
start = stop - '0 0 8';
stop = start + ang * 256;
traceline(start, stop, TRUE, self);
test = vlen(trace_endpos - start);
if (test <= 20)
return; // it's a walkable gap, do nothing
ang_x = self.velocity_y * -1;
ang_y = self.velocity_x;
ang = normalize(ang);
traceline(start - (ang * 10), start + (ang * 10), TRUE, self);
if ((trace_fraction != 1) || trace_startsolid)
return; // gap is only 20 wide, walkable
ang = self.velocity;
ang_z = 0;
dist = ((540 / sv_gravity) * vlen(ang))/* + 32*/;
if (test > dist) // I can't make it
{
if (conts < -3) // bad stuff down dare
{
frik_obstructed(ang, TRUE);
return;
}
else
{
if (self.target1)
{
stop = realorigin(self.target1);
if ((stop_z - self.origin_z) < -32)
return; // safe to fall
}
frik_obstructed(ang, FALSE);
return;
}
}
else
{
ang = normalize(ang);
//look for a ledge
traceline(self.origin, self.origin + (ang * (test + 20)), TRUE, self);
if (trace_fraction != 1)
{
if (conts < -3) // bad stuff down dare
{
frik_obstructed(ang, TRUE);
return;
}
else
{
if (self.target1)
{
stop = realorigin(self.target1);
if ((stop_z - self.origin_z) < -32)
return; // safe to fall
}
frik_obstructed(ang, FALSE);
return;
}
}
if (self.target1)
{
// getting furter away from my target?
test = vlen(self.target1.origin - (ang + self.origin));
if (test > vlen(self.target1.origin - self.origin))
{
if (conts < -3) // bad stuff down dare
{
frik_obstructed(ang, TRUE);
return;
}
else
{
frik_obstructed(ang, FALSE);
return;
}
}
}
}
if (hgt < -18)
{
if (self.target1)
{
stop = realorigin(self.target1);
if ((stop_z - self.origin_z) < -32)
return; // safe to fall
}
bot_jump();
}
// go for it
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
After frik_obstructed, the bot uses the
following funtion to move "around" the obstacle
I have no idea how well it will work
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() frik_dodge_obstruction =
{
local vector way, org;
local float oflags, yaw;
if (!(self.b_aiflags & AI_OBSTRUCTED))
return;
if ((self.b_aiflags & (AI_BLIND | AI_PRECISION)) || !(self.flags & FL_ONGROUND))
{
self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
return;
}
// perform a walkmove check to see if the obs_dir is still obstructed
// walkmove is less forgiving than frik_obstacles, so I dunno
// how well this will work
oflags = self.flags;
org = self.origin;
yaw = vectoyaw(self.obs_dir);
if (walkmove(yaw, 32))
self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
else
{
if (self.b_aiflags & AI_DANGER)
{
way = '0 0 0' - self.obs_dir;
}
else if (self.wallhug)
{
way_x = self.obs_dir_y * -1;
way_y = self.obs_dir_x;
}
else
{
way_x = self.obs_dir_y;
way_y = self.obs_dir_x * -1;
}
self.keys = self.keys & 960 + frik_KeysForDir(way);
}
// fix the bot
self.origin = org;
self.flags = oflags;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
movetogoal and walkmove replacements
blah
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() frik_movetogoal =
{
local vector way, start, stop, ang;
local float g;
if (self.target1 == world)
{
makevectors(self.v_angle);
frik_walkmove(v_forward);
return;
}
way = realorigin(self.target1) - self.origin;
if (vlen(way) < 25)
{
self.keys = self.keys & 960;
return;
}
way = normalize(way);
self.keys = self.keys & 960 + frik_KeysForDir(way);
frik_dodge_obstruction();
frik_recognize_plat(TRUE);
if (self.b_aiflags & AI_PRECISION)
{
g = angcomp(self.v_angle_x, self.b_angle_x);
if (fabs(g) > 10)
self.keys = self.keys & 960;
g = angcomp(self.v_angle_y, self.b_angle_y);
if (fabs(g) > 10)
self.keys = self.keys & 960;
}
};
float(vector weird) frik_walkmove =
{
// okay so it's not walkmove
// sue me
self.keys = self.keys & 960 + frik_KeysForDir(weird);
frik_dodge_obstruction();
frik_recognize_plat(TRUE);
if (self.b_aiflags & AI_OBSTRUCTED)
return FALSE;
else
return TRUE;
};
/*
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
The "hook" method of navigation. This nav
system is copyrighted 1999 by Ryan "Frika C"
Smith, keep that in mind when you steal it.
I brought this back because normal roaming
won't work - the bot gets distracted by it's
own waypoints.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/
void() frik_bot_roam =
{
local vector org, ang, org1;
local float loopcount, flag, dist;
loopcount = 26;
flag = FALSE;
while((loopcount > 0) && !flag)
{
loopcount = loopcount - 1;
org = self.origin + self.view_ofs;
ang = self.angles;
ang_y = frik_anglemod(ang_y - 90 + (random() * 180));
ang_x = 0; // avoid upward sloping
makevectors(ang);
traceline(org, org + v_forward * 2300, TRUE, self);
if (trace_fraction != 1)
{
org1 = trace_endpos;
ang = normalize(trace_plane_normal);
ang_z = 0; // avoid upward sloping
traceline(org1, org1 + (ang * 2300), TRUE, self);
if ((trace_fraction != 1) && (vlen(trace_endpos - org1) >= 64))
{
org = trace_endpos;
traceline(org, self.origin + self.view_ofs, TRUE, self);
if (trace_fraction != 1)
{
dist = vlen(org1 - org) /2;
org = org1 + (ang * dist);
traceline(org, org - '0 0 48', TRUE, self);
if (trace_fraction != 1)
{
SpawnTempWaypoint(org);
flag = TRUE;
}
}
}
}
}
self.b_angle_y = self.v_angle_y + 10;
};

678
fbxa/bot_phys.qc Normal file
View file

@ -0,0 +1,678 @@
/***********************************************
* *
* FrikBot Physics *
* The near-perfect emulation of *
* Client movement *
* *
* Special Thanks to: Asdf, Frog *
* Alan "Strider" Kivlin *
* *
* *
***********************************************/
/*
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.
*/
/*
=========================================
Stuff mimicking cl_input.c code
=========================================
*/
float(float key) CL_KeyState =
{
return ((self.keys & key) > 0);
};
void() CL_KeyMove = // CL_BaseMove + CL_AdjustAngles
{
local float anglespeed;
local vector view;
if (self.keys != self.oldkeys)
{
self.movevect = '0 0 0';
self.movevect_y = self.movevect_y + (350 * CL_KeyState(KEY_MOVERIGHT));
// 350 is the default cl_sidespeed
self.movevect_y = self.movevect_y - (350 * CL_KeyState(KEY_MOVELEFT));
// 350 is the default cl_sidespeed
self.movevect_x = self.movevect_x + (200 * CL_KeyState(KEY_MOVEFORWARD));
// 200 is the default cl_forwardspeed
self.movevect_x = self.movevect_x - (200 * CL_KeyState(KEY_MOVEBACK));
// 200 is the default cl_backspeed
self.movevect_z = self.movevect_z + (200 * CL_KeyState(KEY_MOVEUP));
// 200 is the default cl_upspeed
self.movevect_z = self.movevect_z - (200 * CL_KeyState(KEY_MOVEDOWN));
// 200 is the default cl_upspeed
if (!self.b_aiflags & AI_PRECISION)
self.movevect = self.movevect * 2;
// 2 is the default cl_movespeedkey & bot always has +speed
}
self.oldkeys = self.keys;
if (self.b_skill != 2) // use mouse emulation
{
anglespeed = 1.5 * real_frametime;
// 1.5 is the default cl_anglespeedkey & bot always has +speed
self.v_angle_y = self.v_angle_y + anglespeed * CL_KeyState(KEY_LOOKLEFT) * 140;
// 140 is default cl_yawspeed
self.v_angle_y = self.v_angle_y - anglespeed * CL_KeyState(KEY_LOOKRIGHT) * 140;
// 140 is default cl_yawspeed
self.v_angle_x = self.v_angle_x - anglespeed * CL_KeyState(KEY_LOOKUP) * 150;
// 150 is default cl_pitchspeed
self.v_angle_x = self.v_angle_x + anglespeed * CL_KeyState(KEY_LOOKDOWN) * 150;
// 150 is default cl_pitchspeed
}
else
{
view_x = angcomp(self.b_angle_x, self.v_angle_x);
view_y = angcomp(self.b_angle_y, self.v_angle_y);
if (vlen(view) > 30)
{
self.mouse_emu = self.mouse_emu + (view * 30);
if (vlen(self.mouse_emu) > 180)
self.mouse_emu = normalize(self.mouse_emu) * 180;
}
else
self.mouse_emu = view * (1 / real_frametime);
self.v_angle = self.v_angle + self.mouse_emu * real_frametime;
}
if (self.v_angle_x > 80)
self.v_angle_x = 80;
else if (self.v_angle_x < -70)
self.v_angle_x = -70;
if (self.v_angle_z > 50)
self.v_angle_z = 50;
else if (self.v_angle_z < -50)
self.v_angle_z = -50;
self.v_angle_y = frik_anglemod(self.v_angle_y);
};
/*
=========================================
Stuff mimicking sv_user.c
=========================================
*/
void() SV_UserFriction =
{
local vector vel, start, stop;
local float sped, friction, newspeed;
vel = self.velocity;
vel_z =0;
sped = vlen(vel);
vel = self.velocity;
if (!sped)
return;
// if the leading edge is over a dropoff, increase friction
start_x = stop_x = self.origin_x + vel_x / (sped * 16);
start_y = stop_y = self.origin_y + vel_y / (sped * 16);
start_z = self.origin_z + self.mins_z;
stop_z = start_z - 34;
traceline(start, stop, TRUE, self);
if (trace_fraction == 1)
friction = sv_friction * 2; // 2 is default edgefriction, removed for QW compatability
else
friction = sv_friction;
if (sped < sv_stopspeed)
newspeed = sped - real_frametime * sv_stopspeed * friction;
else
newspeed = sped - real_frametime * sped * friction;
if (newspeed < 0)
newspeed = 0;
newspeed = newspeed / sped;
self.velocity_y = vel_y * newspeed;
self.velocity_x = vel_x * newspeed;
};
void() SV_WaterJump =
{
if (time > self.teleport_time || !self.waterlevel)
{
self.flags = self.flags - (self.flags & FL_WATERJUMP);
self.teleport_time = 0;
}
self.velocity_x = self.movedir_x;
self.velocity_y = self.movedir_y;
};
void() DropPunchAngle =
{
local float len;
len = vlen(self.punchangle);
self.punchangle = normalize(self.punchangle);
len = len - 10 * real_frametime;
if (len < 0)
len = 0;
self.punchangle = self.punchangle * len;
};
void(vector wishvel) SV_AirAccelerate =
{
local float addspeed, wishspd, accelspeed, currentspeed;
wishspd = vlen(wishvel);
wishvel = normalize(wishvel);
if (wishspd > 30)
wishspd = 30;
currentspeed = self.velocity * wishvel;
addspeed = wishspd - currentspeed;
if (addspeed <= 0)
return;
accelspeed = 10 * sv_accelerate * wishspd * real_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
self.velocity = self.velocity + accelspeed * wishvel;
};
void(vector wishvel) SV_Accelerate =
{
local float addspeed, wishspd, accelspeed, currentspeed;
wishspd = vlen(wishvel);
wishvel = normalize(wishvel);
currentspeed = self.velocity * wishvel;
addspeed = wishspd - currentspeed;
if (addspeed <= 0)
return;
accelspeed = sv_accelerate * wishspd * real_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
self.velocity = self.velocity + accelspeed * wishvel;
};
void() SV_WaterMove =
{
local vector wishvel;
local float wishspeed, addspeed, cspeed, newspeed;
makevectors(self.v_angle);
wishvel = v_right * self.movevect_y + v_forward * self.movevect_x;
if (self.movevect == '0 0 0')
wishvel_z = wishvel_z - 60;
else
wishvel_z = wishvel_z + self.movevect_z;
wishspeed = vlen(wishvel);
if (wishspeed > sv_maxspeed)
{
wishvel = (sv_maxspeed / wishspeed) * wishvel;
wishspeed = sv_maxspeed;
}
wishspeed = wishspeed * 0.7;
cspeed = vlen(self.velocity);
if (cspeed)
{
newspeed = cspeed - (real_frametime * cspeed * sv_friction);
if (newspeed < 0)
newspeed = 0;
self.velocity = self.velocity * (newspeed / cspeed);
}
else
newspeed = 0;
if (!wishspeed)
return;
addspeed = wishspeed - newspeed;
if (addspeed <= 0)
return;
wishvel = normalize(wishvel);
cspeed = sv_accelerate * wishspeed * real_frametime;
if (cspeed > addspeed)
cspeed = addspeed;
self.velocity = self.velocity + cspeed * wishvel;
};
void() SV_AirMove =
{
local vector wishvel, vangle;
vangle = self.v_angle;
vangle_x = vangle_z = 0;
makevectors(vangle);
if (time < self.teleport_time && (self.movevect_x < 0))
self.movevect_x = 0;
wishvel = v_right * self.movevect_y + v_forward * self.movevect_x;
if (self.movetype != MOVETYPE_WALK)
wishvel_z = self.movevect_z;
else
wishvel_z = 0;
if (vlen(wishvel) > sv_maxspeed)
wishvel = normalize(wishvel) * sv_maxspeed;
if (self.movetype == MOVETYPE_NOCLIP)
self.velocity = wishvel;
else if (self.flags & FL_ONGROUND)
{
SV_UserFriction();
SV_Accelerate(wishvel);
}
else
SV_AirAccelerate (wishvel);
};
void() SV_ClientThink =
{
local vector vangle;
if (self.movetype == MOVETYPE_NONE)
return;
DropPunchAngle();
if (self.health <= 0)
return;
self.v_angle_z = 0; // V_CalcRoll removed, sucks
self.angles_z = self.v_angle_z * 4;
vangle = self.v_angle + self.punchangle;
if (!self.fixangle)
{
self.angles_x = (vangle_x / -3);
self.angles_y = vangle_y;
} else
{
self.v_angle = self.angles;
self.fixangle = 0;
}
if (self.flags & FL_WATERJUMP)
{
SV_WaterJump();
return;
}
if ((self.waterlevel >= 2) && (self.movetype != MOVETYPE_NOCLIP))
{
SV_WaterMove();
return;
}
SV_AirMove();
};
/*
=========================================
Stuff mimicking sv_phys.c
=========================================
*/
float() SV_RunThink =
{
local float thinktime, bkuptime;
thinktime = self.nextthink;
bkuptime = time;
if (thinktime <= 0 || thinktime > (time + real_frametime))
return TRUE;
if (thinktime < time)
thinktime = time;
self.nextthink = 0;
time = thinktime;
other = world;
makevectors(self.v_angle); // hack
self.think();
time = bkuptime;
return TRUE;
};
void(float scale) SV_AddGravity =
{
self.velocity_z = self.velocity_z - (scale * sv_gravity * real_frametime);
};
float() SV_CheckWater =
{
local vector point;
local float cont;
point_x = self.origin_x;
point_y = self.origin_y;
self.waterlevel = 0;
self.watertype = CONTENT_EMPTY;
point_z = self.origin_z + self.mins_z + 1;
cont = pointcontents(point);
if (cont <= CONTENT_WATER)
{
self.watertype = cont;
self.waterlevel = 1;
point_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
cont = pointcontents(point);
if (cont <= CONTENT_WATER)
{
self.waterlevel = 2;
point_z = self.origin_z + self.view_ofs_z;
cont = pointcontents(point);
if (cont <= CONTENT_WATER)
self.waterlevel = 3;
}
}
return (self.waterlevel > 1);
};
void() RemoveThud = // well sometimes
{
local entity oself;
if (other == world)
{
if (self.flags & FL_ONGROUND)
{
self.flags = self.flags - FL_ONGROUND;
}
}
else
{
if (other.solid == SOLID_BSP && (self.flags & FL_ONGROUND))
{
// RM: Does this break anything?
// If not, then some more thuds have been removed.
self.flags = self.flags - FL_ONGROUND;
}
if (other == self.owner)
return;
if (self.owner.solid == SOLID_NOT)
return;
oself = other;
other = self.owner;
self = oself;
if (self.solid == SOLID_BSP)
if (self.touch)
self.touch();
}
};
void() SV_CheckOnGround =
{
local vector org, v;
org = self.origin;
local float currentflags;
currentflags = self.flags;
self.flags = self.flags | FL_ONGROUND | FL_PARTIALGROUND;
walkmove(0,0); // perform C touch function
self.flags = currentflags | FL_ONGROUND;
if ((org_x != self.origin_x) || (org_y != self.origin_y))
org = self.origin;
else
self.origin = org;
v = org;
v_z = self.maxs_z + org_z + 1;
traceline (org, v, TRUE, self);
if ((self.waterlevel == 3) && (self.movetype == MOVETYPE_WALK))
self.flags = self.flags - FL_ONGROUND;
else if ((trace_plane_normal_z <= 0.7) && (trace_fraction != 1))
self.flags = self.flags - FL_ONGROUND;
else if (!droptofloor(0,0))
self.flags = self.flags - FL_ONGROUND;
else if (org_z - self.origin_z < 2)
self.flags = self.flags | FL_ONGROUND;
else
self.flags = self.flags - FL_ONGROUND;
setorigin(self, org);
};
// Thanks to Alan Kivlin for this function
// modified heavily by me
float(vector dir) botCheckForStep =
{
local vector currentorigin, v;
local float currentflags, yaw, stepdistance, movedistance;
currentorigin = self.origin;
currentflags = self.flags;
self.flags = FL_ONGROUND | FL_PARTIALGROUND;
dir = normalize(dir);
dir_z = 0;
yaw = vectoyaw(dir);
if(walkmove(yaw, 3))
{
if(droptofloor(0,0))
{
stepdistance = self.origin_z - currentorigin_z;
v = self.origin - currentorigin;
v_z = 0;
movedistance = vlen(v);
if((stepdistance > 0 && stepdistance <= 16) && movedistance != 0)
{
self.flags = currentflags | FL_PARTIALGROUND;
return 1;
}
}
}
self.flags = currentflags;
setorigin(self, currentorigin);
return 0;
};
// this is merely here to fix a problem with e3m5
void(vector dir) BruteForceStep =
{
local vector currentorigin;
local float currentflags, i, len;
currentorigin = self.origin;
currentflags = self.flags;
len = vlen(dir);
if (len > 16)
dir = normalize(dir) * 16;
setorigin(self, currentorigin + dir);
while(i < 18 && !walkmove(0, 0))
{
self.origin_z = currentorigin_z + i;
i = i + 2;
}
self.flags = currentflags;
if (i >=18)
setorigin(self, currentorigin);
};
void() PostPhysics =
{
local vector obstr, org;
local float back, dst,cflags;
self = self.owner;
self.velocity = self.velocity - self.phys_obj.dest1 + self.phys_obj.velocity;
if (self.phys_obj.dest2 == self.origin)
{
setorigin(self, self.phys_obj.origin);
// might've been moved during other person's physics
// (teleporters / plats)
if (self.movetype == MOVETYPE_WALK)
{
if (self.phys_obj.dest1_x || self.phys_obj.dest1_y)
{
if ((self.flags & FL_ONGROUND) || (self.waterlevel <= 2))
{
obstr = self.phys_obj.movedir - self.origin;
obstr_z = 0;
if (vlen(obstr) > 0.1)
{
dst = vlen(obstr);
back = vectoyaw(obstr);
cflags = self.flags;
self.flags = self.flags | FL_PARTIALGROUND;
if(walkmove(back, dst))
{
self.flags = cflags;
self.phys_obj.dest1_z = 0;
self.velocity = self.velocity + self.phys_obj.dest1 - self.phys_obj.velocity;
}
else
{
if (dst > 1)
frik_obstructed(obstr, FALSE);
org = self.origin;
self.flags = cflags;
obstr = self.phys_obj.dest1;
obstr_x = 0;
if (!botCheckForStep(obstr))
{
obstr = self.phys_obj.dest1;
obstr_y = 0;
if (!botCheckForStep(obstr))
{
// if no steps were found, bot is really obstucted
BruteForceStep(self.phys_obj.dest1);
}
}
}
}
}
}
}
}
SV_CheckOnGround();
PlayerPostThink();
BotAI();
self.dmg_take = self.dmg_save = 0;
};
// Avoid calling BotAI and the physics at the same time
// Can trip the runaway loop counter
void() SV_FlyMove =
{
// This is nothing like the Quake function.
if (self.phys_obj == world)
{
self.phys_obj = find(world,classname,"phys_obj");
while (self.phys_obj.owner != self)
{
self.phys_obj = find(self.phys_obj,classname,"phys_obj");
if (self.phys_obj == world)
{
error("No physics entity spawned!\nMake sure BotInit was called\n");
}
}
}
setmodel (self.phys_obj, string_null);
self.phys_obj.movetype = MOVETYPE_STEP;
self.phys_obj.solid = SOLID_TRIGGER;
self.phys_obj.touch = RemoveThud;
setsize(self.phys_obj, self.mins, self.maxs);
self.phys_obj.dest2 = self.phys_obj.origin = self.origin;
self.phys_obj.watertype = 0;
self.phys_obj.movedir = self.origin + real_frametime * self.velocity;
self.phys_obj.dest1 = self.phys_obj.velocity = self.velocity;
self.phys_obj.velocity_z = self.phys_obj.velocity_z + sv_gravity * real_frametime;
self.phys_obj.flags = 0;
self.phys_obj.think = PostPhysics;
self.phys_obj.nextthink = time;
};
void() SV_Physics_Toss =
{
if (!SV_RunThink())
return;
if (self.flags & FL_ONGROUND)
{
self.velocity = '0 0 0';
BotAI();
return;
}
if (self.movetype != MOVETYPE_FLY)
SV_AddGravity(1);
self.angles = self.angles + real_frametime * self.avelocity;
SV_FlyMove();
};
void() SV_Physics_Client =
{
PlayerPreThink();
if (self.movetype == MOVETYPE_NONE)
{
if (!SV_RunThink())
return;
PlayerPostThink();
BotAI();
}
else if ((self.movetype == MOVETYPE_WALK) || (self.movetype == MOVETYPE_STEP))
{
if (!SV_RunThink())
return;
if (!(SV_CheckWater()) && (!(self.flags & FL_WATERJUMP)))
SV_AddGravity(1);
SV_FlyMove();
}
else if ((self.movetype == MOVETYPE_TOSS) || (self.movetype == MOVETYPE_BOUNCE))
{
SV_Physics_Toss();
}
else if (self.movetype == MOVETYPE_FLY)
{
if (!SV_RunThink())
return;
SV_FlyMove();
}
else if (self.movetype == MOVETYPE_NOCLIP)
{
if (!SV_RunThink())
return;
self.origin = self.origin + real_frametime * self.velocity;
PlayerPostThink();
BotAI();
}
else
error ("SV_Physics_Client: Bad Movetype (BOT)");
};

944
fbxa/bot_qw.qc Normal file
View file

@ -0,0 +1,944 @@
/*
======================================
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/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
--------------------------------------
*/
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
// --------defines-----
float SVC_UPDATENAME = 13;
float SVC_UPDATEFRAGS = 14;
float SVC_UPDATECOLORS = 17;
// used for the physics & movement AI
float KEY_MOVEUP = 1;
float KEY_MOVEDOWN = 2;
float KEY_MOVELEFT = 4;
float KEY_MOVERIGHT = 8;
float KEY_MOVEFORWARD = 16;
float KEY_MOVEBACK = 32;
float KEY_LOOKUP = 64;
float KEY_LOOKDOWN = 128;
float KEY_LOOKLEFT = 256;
float KEY_LOOKRIGHT = 512;
// these are aiflags for waypoints
// some overlap to the bot
float AI_TELELINK_1 = 1; // link type
float AI_TELELINK_2 = 2; // link type
float AI_TELELINK_3 = 4; // link type
float AI_TELELINK_4 = 8; // link type
float AI_DOORFLAG = 16; // read ahead
float AI_PRECISION = 32; // read ahead + point
float AI_SURFACE = 64; // point
float AI_BLIND = 128; // read ahead + point
float AI_JUMP = 256; // point + ignore
float AI_DIRECTIONAL = 512; // read ahead + ignore
float AI_PLAT_BOTTOM = 1024; // read ahead
float AI_RIDE_TRAIN = 2048; // read ahead
float AI_SUPER_JUMP = 4096; // point + ignore + route test
float AI_SNIPER = 8192; // point type
float AI_AMBUSH = 16384; // point type
float AI_DOOR_NO_OPEN = 32768; // read ahead
float AI_DIFFICULT = 65536; // route test
float AI_TRACE_TEST = 131072; // route test
// these are flags for bots/players (dynamic/editor flags)
float AI_OBSTRUCTED = 1;
float AI_HOLD_SELECT = 2;
float AI_ROUTE_FAILED = 2;
float AI_WAIT = 4;
float AI_DANGER = 8;
// addition masks
float AI_POINT_TYPES = 29152;
float AI_READAHEAD_TYPES = 36528;
float AI_IGNORE_TYPES = 4864;
float WM_UNINIT = 0;
float WM_DYNAMIC = 1;
float WM_LOADING = 2;
float WM_LOADED = 3;
// editor modes aren't available in QW, but we retain support of them
// since the editor is still built into the AI in places
float WM_EDITOR = 4;
float WM_EDITOR_DYNAMIC = 5;
float WM_EDITOR_DYNLINK = 6;
float OPT_NOCHAT = 2;
// -------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, phys_head, way_head;
float busy_waypoints;
float coop = 0; // hack
// -------ProtoTypes------
// external
void() ClientConnect;
void() ClientDisconnect;
void() SetNewParms;
// rankings
float(float clientno) ClientBitFlag;
float() ClientNextAvailable;
void(float whatbot, float whatskill) BotConnect;
void(entity bot) BotDisconnect;
void(float clientno) BotInvalidClientNo;
void(entity who) UpdateClient;
// waypointing
void() DynamicWaypoint;
entity(vector org) make_waypoint;
void() ClearAllWays;
void() FixWaypoints;
float() begin_route;
void(entity this, float direct) bot_get_path;
void() WaypointThink;
entity(entity start) FindWayPoint;
// physics & movement
float(entity e) bot_can_rj;
void() bot_jump;
void() frik_bot_roam;
float(vector weird) frik_walkmove;
void() frik_movetogoal;
void() frik_obstacles;
float(float flag) frik_recognize_plat;
float(vector sdir) frik_KeysForDir;
void(vector whichway, float danger) frik_obstructed;
void() SV_Physics_Client;
void() SV_ClientThink;
void() CL_KeyMove;
// ai & misc
string() PickARandomName;
float(entity targ) fov;
float(float y1, float y2) angcomp;
float(entity targ1, entity targ2) wisible;
float(entity targ) sisible;
float(entity targ) fisible;
vector(entity ent) realorigin;
void(entity ent) target_drop;
void(entity ent) target_add;
void() KickABot;
void() BotImpulses;
void(entity targ, float success) bot_lost;
string(float r) BotName;
float(float v) frik_anglemod;
void() bot_chat;
void(float tpic) bot_start_topic;
// ----------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(world);
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 (player_head)
player_head._last = self;
self._next = player_head;
player_head = self;
userid = userid + 1;
self.b_userid = userid;
if (!self.phys_obj)
{
b_temp2 = phys_head;
while (b_temp2 != world && 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 (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;
// spawn entities for the physics
ent = nextent(world);
max_clients = 0;
while(ent != world)
{
max_clients = max_clients + 1;
ent = nextent(ent);
}
ent = nextent(world);
fisent = world;
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 = world;
num = num + 1;
while (num > 0)
{
num = num - 1;
upsy = nextent(upsy);
}
return upsy;
};
void(float whatbot, float whatskill) BotConnect =
{
local float f;
local string h;
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 string h;
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(world, "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(world);
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(world, "skill");
f = stof(h);
BotConnect(0, f);
}
else if (self.impulse == 102)
KickABot();
else
return;
self.impulse = 0;
};

1001
fbxa/bot_way.qc Normal file

File diff suppressed because it is too large Load diff

446
fbxa/foo.html Normal file
View file

@ -0,0 +1,446 @@
<HTML>
<HEAD>
<TITLE>FrikBot Readme</TITLE>
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function Display(section)
{
if (section == "intro")
intro.style.visibility = "visible";
else
intro.style.visibility = "hidden";
if (section == "install")
install.style.visibility = "visible";
else
install.style.visibility = "hidden";
if (section == "play")
play.style.visibility = "visible";
else
play.style.visibility = "hidden";
if (section == "waypoints")
waypoints.style.visibility = "visible";
else
waypoints.style.visibility = "hidden";
if (section == "mods")
mods.style.visibility = "visible";
else
mods.style.visibility = "hidden";
if (section == "author")
me.style.visibility = "visible";
else
me.style.visibility = "hidden";
if (section == "web")
web.style.visibility = "visible";
else
web.style.visibility = "hidden";
if (section == "wayed")
wayed.style.visibility = "visible";
else
wayed.style.visibility = "hidden";
if (section == "probs")
probs.style.visibility = "visible";
else
probs.style.visibility = "hidden";
}
//-->
</SCRIPT>
</HEAD>
<BODY BGCOLOR=#000000 TEXT=#FFFFFF>
<TABLE WIDTH=100% HEIGHT=100%>
<TR><TD ALIGN=CENTER VALIGN=TOP WIDTH=200>
<BR><BR><BR><BR><BR><BR>
<FORM>
<input type="button" value="INTRODUCTION" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('intro');">
<p>
<input type="button" value="INSTALLATION" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('install');">
<p>
<input type="button" value="PLAYING" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('play');">
<p>
<input type="button" value="ADD-ON WAYPOINTS" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('waypoints');">
<p>
<input type="button" value="MODS" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('mods');">
<p>
<input type="button" value="WAYPOINTING" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('wayed');">
<p>
<input type="button" value="COMMON PORBLEMS" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('probs');">
<P>
<input type="button" value="AUTHOR" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('author');">
<p>
<input type="button" value="WEB" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('web');">
</FORM>
</TD><TD>
<TABLE style="font-family: Arial; font-size: 8pt;">
<TR><TD>Title:</TD><TD>FrikBot X</TD></TR>
<TR><TD>Filename:</TD><TD>fbxa.zip</TD></TR>
<TR><TD>Version:</TD><TD>0.10.1</TD></TR>
<TR><TD>Date:</TD><TD>7-27-2001</TD></TR>
<TR><TD>Author:</TD><TD>Ryan "Frika C" Smith</TD></TR>
<TR><TD>Email:</TD><TD><A HREF="mailto:frika-c@earthling.net">frika-c@earthling.net</TD></TR>
<TR><TH COLSPAN=2>Credits</TH></TR>
<TR><TD>Horn, scar3crow, Electro, Akuma, ze0</TD><TD>Play testing and suggestions.</TD></TR>
<TR><TD>Quest, Plumb, SSJ4-Death</TD><TD>Editor testing.</TD></TR>
<TR><TD>Koolio</TD><TD>Mod testing</TD></TR>
<TR><TD>Raymond Martineau, Quest, Akuma</TD><TD>For supporting the "FrikBot community"</TD></TR>
<TR><TD>Alan Kivlin, Requiem</TD><TD>Code snippets</TD></TR>
</TABLE>
<P><H1>Introduction</H1>
<P>
This is the final version of FrikBot. I mean it this time. Really. Stop looking at me like that. This is it. For those that know what this bot is all about, skip the next paragraph and save yourself some time. If not, read on:
<P>
FrikBot is a unique bot for Quake. Maybe you're familiar with Reaper Bots, or the fabulous Frogbot and Omicron bots. These are wonderful opponents to deathmatch against, but for mod authors (the people that make those nice mods you and I play, such as Capture the Flag, etc) they're not so great. Why? I hear you ask - well the bot code heavily intrudes into the rest of the QuakeC (the language you use to make mods for Quake). Because of this, you either need to build the mod onto the bot's source base, or spend a few good months finding each part that links into the mod and carefully replicate it on the new mod. In addition Reaper is not legal to modify and Omicron's source is heavily obfuscated. FrikBot has introduced about 2 years ago as an alternative. It's the first Quake bot (aside from TutorBot) to be heavily geared to mod authors and customization. With the advent of this version, it's also a hoot to play too.
<P>This release (nicknamed FrikBotX or simply FBX) represents several months of slow off and on work and is the 10th (or 12th - I'm not sure) and final release of the bot. FrikBot X, much like the infamous Frogbot relies heavily on hand-built waypoints for excellence in combat. It can however run without them, and it will attempt to create it's own waypoints as it plays. This is not recommended however.
<P>To get started playing FrikBot, read the installation instructions which can be found by clicking the "Installation" button at left (on DHTML capable browsers), or scrolling down (on "normal" browsers).
<P><P><P><!-- I never close P tags. pbbt -->
<H1>Installation</H1>
<P><FONT COLOR="red">Please note FrikBot requires the registered version of Quake.</FONT>
<P>These installation instructions assume you're using some version of Windows. If you're lucky enough to be using a better operating system (read: *nux, BeOS, whatever) you likely don't need me to tell you how to install a Quake mod. You probably also enjoy doing binary-long division during long car rides. If you're using a Macintosh, simply unpack this archive, move it to your Quake folder and click-drag it onto your MacQuake executable. (A Mac user once told me these instructions. If this isn't accurate let me know.)
<P>
Unzip this file with your favorite zip utility. This is usually Winzip on most systems. After it's unzipped, create a new folder in Quake with the name "FrikBot". Move all files and folders from the archive to this new folder. In the folder you'll see two .bat batch files. These have been set up to run FBX with the standard command line. Run.bat will execute Quake.exe and GLRun.bat will run GLQuake.exe. The latter is a special 3D-Accelerated version of Quake. If you have 3D hardware, I recommend you <A HREF="ftp://ftp.cdrom.com/pub/idgames/idstuff/unsup/glq1114.exe">download this</A> and read the provided instructions.
<P>
<FONT COLOR="red">FrikBot requires Quake version 1.08 or higher.</FONT> This is available as a free update from id software. If you use GLQuake or WinQuake or any recent custom engine to run FrikBot, don't worry, you're safe. If however you're still using the Quake.exe that came off your old Quake CD, you can download the update by clicking <A HREF="ftp://ftp.idsoftware.com/idstuff/quake/quake108.zip">here</A>.
<P>
To begin playing a match against the bot, double click the batch file of your choice. Choose the Multiplayer option from the main menu. Choose "New Game" from the Multiplayer menu. Select either IPX or TCP/IP from the next menu. Select a map, fraglimit (if desired), timelimit (if desired) and choose "Begin Game". To start fighting some FrikBots, continue reading in the "Playing" section of this readme.
<P><P><P>
<h1>Playing FrikBot X</h1>
<P>Okay, you know how to install and begin a multiplayer game of FBX (and if you don't, please read <A HREF="javascript: Display('install');">the last section</A>). <FONT COLOR="red">It is recommended you play one of the 6 id deathmatch maps.</FONT>. These are dm1, dm2, dm3, dm4, dm5 and dm6. If you did not start the game with one of these maps, bring down the console with the ~ key, and type "map dm6" and press enter (minus the quotes).
<P>To add a bot, bring down the console (again with the ~ key) and type "impulse 100" (without the quotes) and press enter. A new bot combatant will enter the game. The bot you have added will probably be a skill 1 (Normal skill) bot. If the bot is too difficult for you, type "impulse 102" in the console and press enter. This will disconnect the bot from the game. Then type "skill 0" to set the game on Easy skill in the console, then "impulse 100" to add an easy level bot. When you're ready for a greater challenge try skill 2 (Hard) and skill 3 (Nightmare) bots.
<P>
<h2>Teamplay</h2>
<P>FrikBot can also be used in a team match game. The bots can be teammates or your opposition. To get started here, change the map then type "teamplay 1" in the console. Set your desired skill level then use "impulse 100" to add a bot that will be one of your team members. Use "impulse 101" in the console to add enemy team members.
<P><h2>Cooperative</h2>
<P>You can also have FBX as a 'friend' as you battle through Quake's single player game. To do this, bring down the console, type "deathmatch 0" and press enter. Type "coop 1" then press enter. Next, use the map command to go to the episode selection - in the console type "map start". Add your bot companions as above (with "impulse 100"), then head off starting the game as you normally would. <FONT COLOR="red">It is recommended you download waypoints for the id single player maps before playing coop</FONT>.
<P><h2>Online</h2>
<P>FrikBotX can also be played with the popular internet version of Quake called "QuakeWorld". To do this you need to run the QuakeWorld sever executable "QWSV.exe". If you don't have this, it can be downloaded from <A HREF="http://www.quakeworld.net/">QuakeWorld's official website</A>. After starting it, type in "gamedir frikbot" then press enter. Then change the map by using the map command, eg. "map dm6". To adjust the skill level of bots, you must use the QW console and type "localinfo skill x" where x is the desired skill level. Connect with QWCL to your server and add bots with "impulse 100" and remove them with "impulse 102".
<P>Due to lazy coding the QW version of frikbot does not support teamplay at this time. In addition, the bots will appear to move very choppily, this is due to QW's restricting all non client entities to a very low frames per second. This cannot be avoided without engine modifications.
<P>When playing QWSV and QWCL on the same machine under Windows 9x it's important to point out that QWSV will not receive enough priority, which will cause unneeded lag. There's an excellent tool to solve this called priority.exe. You can find it at the ever helpful <A HREF="http://www.inside3d.com/qip/">Quake Info Pool</A>.
<P><H2>Advanced Options</h2>
<P>FrikBotX has two options to make you're lives a bit easier. In normal quake you can set them by using the saved1 cvar. The first option is to make the bots return between map changes in DM as they do in Coop. To activate the option type "saved1 1" in the console. After the next map load, bots will return between matches. The next option allows you to disable the bot chatter (it gets on some people's nerves). To do this type "saved 2" in the console. If you wish to both options enabled, add them together and type "saved 3". In QuakeWorld, use localinfo b_options on the server instead of saved1. Good luck.
<P><H2>Map cycling</H2>
<P>Included in the FrikBotX progs.dat is an Omicron-style map cycler. To use it, you must create a maps.cfg file, an example one has been included with this archive. Unlike Omicron, FrikBot's map cycler can deal with an unlimited number of maps. The cfg file must should contain lines in the following format:
<blockquote>alias map1 "changelevel mapname"</blockquote>
<P>Samelevel controls how the map cycler behaves. If it is 0, quake will behave as normal. Samelevel 1 will force FBX to stay on the samelevel, just as normal Quake. However, any setting above this will cause FBX to randomly select a map from the specified number of maps. The number you set it to should be the number of aliases you provided in your maps.cfg. If you'd rather not let it select maps randomly (which can result in repeated maps), setting samelevel to a the negative of the number of maps you have will make an infinite looping rotation. If you have six map aliases, setting samelevel -6 will cycle through them successively.
<P><H2>Botcam</H2>
<P>Using "impulse 103" in the console will allow you to cycle to the view of every bot and player on the server. This view is very interesting, and if you have the time and patience you can record demos from the bot's perspective. Unlike 0.09, FrikBot X will seem much choppier from the bot's perspective. Sorry.
<P><P><P>
<h1>Using Add-on waypoints</h1>
<P>The benefits of using add on .way files cannot be stressed enough. Waypoint give the ability to navigate levels perfectly, they make the bot a fierce deathmatch and coop opponent and they actually decrease the load FBX has on the game by a significant amount.
<P>FrikBot Waypoints can be made using the in game editor or may be supplied to you by friends or in future add-on packs. The best source for additional waypoints is the <A HREF="http://www.botepidemic.com/fwd/">FrikBot Waypoint Depot</A>, an excellent website created by Akuma and maintained by Quest.
<P>Waypoints usually come as a single .way file, however some maps are very large or intricate and require multiple files. The additional files share the same name, but have the extensions wa1, wa2, wa3, etc.
<P>To use these waypoints you place the .way (and all additional files, if they exist) in your quake\frikbot\maps folder. If installed correctly, when you go to play that map in Quake you will see in the console "Execing maps/mapnam.way". <FONT COLOR="red">Please note: FBX already contains waypoints for DM1, DM2, DM3, DM4, DM5 and DM6.</FONT> These internal waypoints are improved versions of those found on FrikBot Waypoint Depot. Do not install the waypoints for the id deathmatch maps unless they have been updated over the internal points.
<P>Occasionally you may run across FrikBot waypoints in two other formats. One is map_mapname.qc. <FONT COLOR="red">Please note that although Frogbot uses a similar convention, Frogbot waypoints are not compatible.</FONT><!-- Idiot red again -->
<P>To install these you will need the source code to the mod and a QuakeC compiler (May I recommend my own compiler, <A HREF="http://www.inside3d.com/frikbot/">FrikQCC</A>). To do this, open up the progs.src file from the mod with a text editor. Just after the defs.qc line, add the name of the .qc file you've received. Next, open bot.qc or bot_qw.qc (depending if this is intended for QuakeWorld or Normal Quake) and scroll down past the large installation comment until you find the function bot_map_load. Add a line that reads something like this:
<P>
if (mapname == "mymap")
<BLOCKQUOTE>map_mymap();</BLOCKQUOTE>
<P>
Where the keyword mymap is a placeholder for the name of the map the waypoints are intended for. Under extremely rare circumstances, you'll encounter a third type of waypoints. These come as an .ent file patch. <FONT COLOR="red">Please note that although Omicron uses a similar convention, Omicron bot waypoints are NOT compatible.</FONT><!-- Idiot red again --> To install these you will need the map's BSP file and the compile tool QBSP. QBSP can be found on <A HREF="ftp://ftp://ftp.idsoftware.com/idstuff/source/">id software's FTP</A> in the file q1tools_gpl.zip. You will need to run QBSP with the command line
<P>
qbsp -onlyents mymap
<P>Where mymap is the name of the BSP file you wish to apply waypoints to. <FONT COLOR="red">Please note: The raw dump of the BSP ents option in the in-game editor is not a complete .ent file. It must be appended to the existing ents first. Read the waypoint editing portion of this readme for complete instructions.</FONT>
<P>If you were confused by the above, don't worry. Stick with .way files and you'll be alright. BSP and QC waypoints are intended to get around QuakeWorld's many, many limitations, if you don't use the QuakeWorld FrikBot then you should never need to use those features.
<H1>Information for Mod Authors</H1>
<P>FrikBot is designed for you. With great certainty I can tell you that after plugging FrikBot into your mod the bot will use all the rules you have changed, will fire your custom weapons and will behave like a client in every way discernable. However, he will probably behave like an incredibly stupid client, but that isn't the point.
<P>If you already have FrikBot installed in your mod skip down to the next section, otherwise, keep reading. All the code to install FrikBot has been included with this archive. The files you need are bot.qc, bot_way.qc, bot_ai.qc, bot_fight.qc, bot_ed.qc, bot_misc.qc, bot_phys.qc and bot_move.qc. If you intend to use the QuakeWorld version of FrikBot, substitute everything I say about bot.qc with "bot_qw.qc", okay? To begin installing FrikBot into your mod, copy these files into your mod's source folder. To be neat I recommend creating a subdirectory called "frikbot" within your source folder and placing the files in there. This is not required though.
<P>Next thing you should do is open up bot.qc, scroll down past the license and read the instructions carefully. There's a few functions to comment out in defs.qc, you accomplish these by placing two forward slashes in front of the line - "//". Once you've followed the installation instructions, you can compile and play the mod now with FrikBots. Most likely you'll need to modify code in bot_ai.qc to make the bot behave intelligently. The main function in this file is BotAI located at the bottom. From there you can follow up all the calls it makes and understand how the bot thinks. Priority_for_thing is the place to add code if you want the bots to hunt a new item, or add conditions to hunting existing items. I leave the rest to you. Note that you will probably never need to edit bot(_qw).qc (except for when installing qc waypoints), bot_move.qc, bot_way.qc or bot_phys.qc. The last one being highly unlikely as it is more or less a direct port of the engine's physics code, and only needs to be changed when the corresponding code in the engine is changed.
<P>Please also note that the waypoint editor mode can be used to cheat in your mod (Even in multiplayer). If you don't want to allow this, safeguard the entrance by adding a cvar("developer") check to its impulse in BotImpulses in bot.qc.
<P>When making mods I strongly suggest you do a few things. Open up bot_misc.qc and give the bots new names and colors. Please. I'm tired of seeing a ton of mods with my default names and chat messages. Use a little creativity, and use some extended chars to give the bots "fun names".
<P>Next, I'd ask you avoid using the name "Frik" in your mod's title. Even if all you did was combine FrikBot with another mod, don't call it FrikSomemod. A more appropriate title may be Somemod + FrikBot, or something along those lines. This is not a commandment from Heaven, it's just that I like to use Frik for my own work, and it tends to confuse people seeing my name part of some mod that I had very little to do with. Thanks.
<P>In the past it has been common to include my entire FrikBot readme with the mod. Please do not include this massive .html file, that's the last thing we need. Instead, excerpt here and there and patch together some instructions for the bot. You have permission to use everything in the archive any way you want, this includes this readme. Put a little effort into it, for your user's sake.
<P>If you make your mod for QuakeWorld I really recommend you make the mod open source. Not only will it be much more convenient for server operators, but QC is one of the best ways to bet waypoints into the QW version of FrikBot. Again, open source is the way to go.
<P>If you're really feeling nice, you can place a small thank-you to me in your mods credits. Thanks for listening, now get out there and get coding!
<H1>Common Problems</H1>
This portion of the readme will help you if you encounter an error when using FrikBot. This is by far not a comprehensive guide, but it will help you with a number of problems. If you need further help, please see the "Author" section of this readme. Thanks.
<p>
<h2>"Unable to connect a bot, server is full"</h2>
</h2>
<p>
FrikBot requires client slots like real players. If you're playing a single player game (by choosing Single Player from the main menu, or using the 'map' command), you cannot connect bots because the game can't accept connections. It is recommended you use -listen 16 in the command line.
<P>If you're getting this message after installing FrikBot X on a mod even though you have multiplayer set up properly, this probably the result of improper installation. Make sure you read the installation instructions correctly (especially the part about BotInit in worldspawn(), world.qc).
<p>
<h2>"CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"</h2>
</h2>
<p>
This (and other errors like it) are typically the result of placing BotInit(); below InitBodyQue(); or some other call in worldspawn. When I say at the top of the function, I mean it!
<P>
<H2>"D_SurfCacheGuard: failed"</H2>
<P>
This problem can occur when you look directly at a waypoint in editor mode when it is
linked to another waypoint at point blank. To fix this, you will need
run the command line parameter "-surfcachesize 1500" when you run
quake. If you still get the message, increase the value until the error
disappears.
<p>
<h2>"Ed_Alloc: No Free Edicts"</h2>
<p>
This problem can occur with large maps that require a lot of waypoints. Also note that this can also be caused by an entity leak in your mod. id's code has an entity leak with the bubble spawner code in player.qc. The only way to fix this is to recompile the Quake source code after setting
MAX_EDICTS in quakedef.h to 1024 (or higher).
<p>
<h2>"Cbuf_AddText: Overflow"
</h2>
<p>
If you receive this message it means you probably didn't split up the .way file properly. Look for the comments made by the editor instructing you to separate the file into multiple parts.
<p>
<h2>"SZ_GetSpace: Overflow without allowoverflow set"
</h2>
<p>
This is the result of the client network buffer filling up with too much data. If you receive this message it usually means a bot was receiving messages he shouldn't have, and all this data is building up in his outgoing buffer (since the client isn't real, he never collects the data and the game crashes). To fix this find places where you stuffcmd'd or otherwise sent messages to the bot that wouldn't be picked up my function redeclarations.
<p>
<h2>Waypoints are missing and links are screwed up in DarkPlaces
</h2>
<p>
Darkplaces caps server activity in listen servers with the sys_ticrate cvar. This can foul the .way file loading, to get around this set "sys_ticrate 0" in the console before changing map.
<H1>Author</H1>
<P>I, Ryan Smith, am deranged psychopath that lives in Massachusetts, USA. I started coding for the TRS-80 Model 3 about 18 years ago, graduated to Commodore 64s, and eventually found my way to Doom and Quake. Quake eventually caught a hold of me. Deathmatch became a sort of digital crack and making mods was absolutely the best thing I had done with my computer. At one point in my early naivety I had stumbled across the Reaper Bot. Little did I know this choppy, poorly playing (and mostly cheating) bot was considered the best bot of the time. I had long assumed that a proper bot couldn't be too difficult.
<P>Anyway, you can reach me at the following address should you have any questions or comments about the bot (feedback much appreciated!) <A HREF="mailto:frika-c@earthling.net">frika-c@earthling.net</A>. I'd also like to hear about any improvements you've found, bugs you've encountered or mods you've made of using the bot. Keep in mind this is often a slow address and it may take a few days for me to receive your mail. <FONT COLOR="red">Also notice I will not reply to any mail asking questions which are answered in this readme or in any material found on my site or at FWD.</FONT> If your message doesn't contain a technical question, you may not expect a reply. My time is very limited, and though I will read your mail and often times act upon it, I may not reply.<P>
All these rules may sound self centered or something, but I have encountered people that think of e-mail as an instant message service and my mailbox has been filled with mail such as "Why haven't you replied? It's been 5 hours!" etc.
<H1>FrikBot on the web</H1>
<P>There are a number of websites you may want to visit for latest news. If you have an active internet connection, just click.
<UL>
<LI><A HREF="http://www.inside3d.com/frikbot">FrikaC's projects</A>
<LI><A HREF="http://www.botepidemic.com/frikbot/">FrikBotX home page</A>
<LI><A HREF="http://www.botepidemic.com/fwd/">FrikBot Waypoint Depot</A>
<LI><A HREF="http://www.planetquake.com/5thD/">5thD Team</A>
</UL>
<H1>Waypoint Editing</H1>
<P>Waypoints are incredibly useful and powerful tools for FrikBot X. To edit the bot's waypoints, you should use FrikBot X's built-in waypoint editor. If you're already familiar with FrikBot Waypoint Studio this should be a fairly easy guide to follow, as the editor in FBX lends much of it's features and design from FBWS.
<P>To begin using the editor, you must first ensure that the file beam.mdl included with this archive is in the mod's progs/ directory. This model file is used to display the links of a waypoint; we'll get to that in a bit. To demonstrate more clearly how waypointing works, load up FBX as you would to fight the bots. Go to the map dm6 (waypoints are included for this map in the progs.dat). Use impulse 104 in the console to start the editor. If everything goes well, you'll now see many tiny white dots ("bubbles") floating mid air. These sprites represent each waypoint on the map. As you near a waypoint, it will change to a very large gold "light ball". This is referred to as the selected waypoint. As each waypoint is selected, it will cast off little red beams that connect to nearby waypoints. These are called 'links'. <FONT COLOR="red">Each link represents that a bot can travel one way from the selected waypoint to the linked waypoint.</FONT> Also, when near a teleporter you may see a link that looks like quake lightning and passes straight through walls. This is called a "telelink" and is really a special flagged link telling the bot to travel through a teleporter in between the two waypoints.
<P>Each waypoint can have a maximum of 4 out bound links. Although this may seem like a severe limitation at first, consider you can place as many waypoints as you need in one spot, and link them all together to get more outbound links. There is no limit to the number of inbound links to a single waypoint.
<P>What follows is a basic break down of the editor's commands. This is not meant as a comprehensive getting started in waypointing tutorial. The subject is actually quite simple, and with a little experimenting you should be waypointing like a pro in no time. <FONT COLOR="red">Be sure to bind the 0 (zero) key to "impulse 10" in order to properly use the editor menus.</FONT>
<H2>Main Menu</H2>
<P>The main menu contains a few basic commands and the ability to switch to any of the other menus in the editor. This is your starting point in the editor.
<UL>
<LI><B>Waypoint Management</B> This command takes you to the waypoint management menu which
contains functions for moving, modifying and handling waypoints.
<LI><B>Link Management</B> This command takes you to the link management menu which contains
options and commands for changing the links between waypoints.
<LI><B>AI Flag Management</B> This command takes you to the first page of the AI flag management menu which contains toggles for all the commonly used AI flags.
<LI><B>Bot Management</B> This command takes you to the bot management menu which allows you to control bots and use them for testing your waypoints.
<LI><B>Waylist Management</B> This command takes you to the waypoint list management menu which provides commands and functions that work on all waypoint data.
<LI><B>Noclip</B> This is a toggle command that gives you exactly the same effect as the single player cheat NOCLIP. It's provided here because it's incredibly useful for getting to hard to reach and/or mid air locations you couldn't effectively modify waypoints in otherwise.
<LI><B>God mode</B> This is a toggle command that gives you exactly the same effect as the single player cheat GOD. It's provided here to allow you to withstand traps and lava that would otherwise kill you, so you can effectively waypoint around them.
<LI><B>Hold Select</B> This toggle will prevent the editor from selecting new waypoints as you pass them, effectively holding onto the selection you had when you turn this toggle on. This is incredibly useful for moving waypoints long distance (where you'd lose the waypoint selection if you pass other waypoints).
<LI><B>Teleport to Way #</B> This command brings up a prompt that allows you to teleport directly to a waypoint by specifying a waypoint number. The waypoint numbers can be read by using Waypoint Management's "show waypoint info" command.
<LI><B>Close Menu</B> This closes the editor menu and returns the game back to normal game play. All waypoints will again vanish and bots will resume normal functioning. Cheats activated in the editor will remain in effect however. Any changes you made to the waypoints will <B>not</B> be lost. Waypoints are only lost when the map changes or when you quit the game. Use the Dump command in Waylist Management to create a permanent record of your waypoints.
</UL>
<H2>Waypoint Management</H2>
<P>Waypoint management is probably the most useful menu in the editor. It contains basic waypoint creation and deletion but also more advanced functions such as make way and link functions.
<UL>
<LI><B>Move Waypoint</B> Incredibly simple, it moves the selected waypoint to your location. This is useful for fine tuning your waypoint sets. Remember, location is really what defines waypoints. Put them in the right location and you can make excellent waypoint sets, put them in the wrong location and the bots will suffer for your mistakes.
<LI><B>Delete Waypoint</B> Deletes the selected waypoint and any links that may exist to it on other waypoints. You'll be prompted to confirm the deletion. Follow the on screen instructions to remove the waypoint.
<LI><B>Make Waypoint</B> This is the original and generic make waypoint function. It creates a new waypoint at your location. The waypoint is essentially blank, no flags and no links. You must set up any links to and from with the Link Management menu.
<LI><B>Make Waypoint + Link</B> The operates as above, the big difference is it also creates a link from the selected waypoint to the new one you've just created. This will save time and allows you to jump off a cliff and not have to noclip back up in order to create the link.
<LI><B>Make Waypoint + Link X2</B> This operates as Make Waypoint + Link but also creates a link from the new waypoint *back* to the selected waypoint. In other words, a link in either direction - both to and from the new and selected waypoints.
<LI><B>Make Waypoint + Telelink</B> This works similar to Make Way + Link, but instead creates a Telelink to the new waypoint - indicating a pass through a teleporter.
<LI><B>Show waypoint info</B> This command spits a few facts about the currently selected waypoint in the console. It's waypoint number, the value of it's AI flags (mostly useless), and which waypoints it links to. It's useful for debugging your waypoint list, or just satisfying your curiosity.
<LI><B>Link Management</B> Jumps you over to the Link Management menu. Because waypointing generally involves a lot of jumping between these two menus a lot, this is provided here for convenience.
<LI><B>AI Flag Management</B> Jumps you over to the AI Flag Management menu, should you ever need to go there.
<LI><B>Main Menu</B> Brings you back to everyone's favorite menu, the Main Menu.
</UL>
<H2>Link Management</H2>
<P>Link Management is the place to deal with individual links. It's commands somewhat echo Waypoint Management's, but on a link scale.
<UL>
<LI><B>Unlink Current Way</B> Removes all links from the selected waypoint. Please Note: There is no "are you sure" on this, it just does it, so be careful.
<LI><B>Create Link</B> Allows you to create a link from the selected waypoint to
another. The editor will prompt you to move to another waypoint and press 1 to
create the link. Note the link you create is one way, from the waypoint you
started at to the waypoint you ended up at.
<LI><B>Create Telelink</B> Just like Create Link but indicates a teleporter in the
middle. Telelinks are indicated by a thin lightning, as opposed to the red beam of regular links.
<LI><B>Delete Link</B> Deletes a link (either Telelink or normal) between two waypoints. You
will be prompted to move from the waypoint that has the link to the waypoint
that receives the link and press 1.
<LI><B>Create Link X2</B> Similar to Create Link, but instead creates two links, both ways between the two waypoints.
<LI><B>Delete Link X2</B> Similar to Delete Link, but deletes links both ways between the two chosen
waypoints.
<LI><B>Make Waypoint</B> Same command as the make waypoint on the Waypoint
Management Menu. Here for convenience.
<LI><B>Waypoint Management</B> This command brings you to the Waypoint Management
menu, I found that when creating a waypoint set, I often flipped between both
menus far too often, and this has been added here for convenience.
<LI><B>AI Flag Management</B> Jumps you over to the AI Flag Management menu, should you ever need to go there.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>AI Flag Management</H2>
<P>Every command on the AI Flag management menus is a toggle that will effect the currently selected waypoint. FrikBot 0.09 supported the door flag AI flag and no more (loading FBX waypoints in 0.09 is perfectly acceptable by the way, new flags will merely be ignored). The new flags have many varied and useful effects on the bots way of thinking and action he takes as he uses the waypoint. The best way to understand how to use a few of these is to study how the bot reacts to them, I will however attempt to explain here as much as I can.
<UL>
<LI><B>Door flag</B> This flag means the waypoint was spawned on top of a door. (Any type of door). If the door is not under the waypoint when the bot attempts to reach this waypoint, the bot will find the trigger for the door and activate it. Clever waypointers have also used this flag to force bots to trigger doors that do not seemingly block their path.
<LI><B>Precision</B>This tells the bot it should attempt to be more precise around this waypoint since there is a potential for falling or screwing up in some way. It will not attempt to dodge or charge an enemy under the influence of precision. The bot will walk slower and will only use movement keys if already pointing in the correct direction.
<LI><B>Surface for Air</B> This flag forces the bot up for air just above this waypoint. He will swim up, catch a breath of fresh air and continue. <FONT COLOR="red">Do not place this on all your water waypoints.</FONT> The bot will go up for air when it senses it's about to drown on it's own. This flag is intended to tell the bot to go up for air before heading down long underwater hallways that it may drown in otherwise.
<LI><B>Blind Mode</B> Blind mode is an incredibly useful AI flag. In short it means the bot must follow the waypoints blindly and avoid making it's own decisions about it's surroundings. The net result of this is the bot will completely ignore lava, will not jump over gaps, will not try to avoid walls it bumps into and will not deviate from following the waypoints for any reason.
<LI><B>Jump</B> This waypoint merely tells the bot to squeeze the jump button upon reaching the waypoint. Another effect of this flag is to make the waypoint 'invisible' to the AI, it will never consider this waypoint as a location of an item or itself, so be careful when using this flag and make sure it's near other, normal waypoints.
<LI><B>Directional</B> This flag will make a waypoint directional rather than 'positional'. The bot will travel in the direction of the waypoint until the direction to the waypoint has changed radically. This is useful where the bot has little control over his direction, such as in a wind tunnel.
<LI><B>Super Jump</B>This flag tells the bot it needs to rocket jump upon reaching this waypoint. This flag also makes the waypoint 'invisible to the AI'. Remember the bot will rocket jump to every outbound link of the waypoint.
<LI><B>Make Waypoint</B> Same command as the make waypoint on the Waypoint
Management Menu. Here for convenience.
<LI><B>AI Flags Pg. 2</B> This command brings you to the second page of the AI Flags menu, where even more flags are kept.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>AI Flag Management Page 2</H2>
<P>These are the less used AI Flags, though they are useful in some ways.
<UL>
<LI><B>Difficult</B> This flag marks the waypoint as being a long a more difficult route. As a result, the bot will place less priority toward this route when choosing it's shortest path. This will also devalue items on the other end lowering their relative priority. As such, this flag is useful for making the bot both no longer favor a certain route and no longer favor certain items.
<LI><B>Wait for Plat</B> This flag instructs the bot to stand at this waypoint and wait for the nearby func_plat to reach it's bottom state before it continues. This is useful if the plat in question can have people 'wedged' underneath it, and the bot's enthusiastic charge to the next waypoint would get him stuck beneath it.
<LI><B>Ride Train</B> This is a complicated flag that affects multiple behaviors and attempts to allow the bots to ride trains. The first behavior is the bot will wait before going to the flagged waypoint until a plat, door or train is underneath the waypoint. Upon reaching the waypoint the bot will not move as long as he's on top of the train, door or plat.
<LI><B>Door flag no open</B> This is intended as a modifier to the classic door flag . When the bot encounters the a waypoint flagged with both door flag and door flag no open, the bot will not attempt to trigger the missing door beneath the waypoint, but instead will wait for the door to return itself. Additionally, when used alone this flag will prevent bots from opening doors as they usually do if one blocks their path.
<LI><B>Ambush</B> This is not an officially supported waypoint. This flag will make the waypoint have a priority value, the bot will hunt the waypoint after getting the better items on the level. Upon reaching it, he will merely wait until he sees someone.
<LI><B>Snipe</B> Operates just like Ambush, however the bots will not charge out and attack upon seeing an enemy. They will sit quietly, firing their weapon.
<LI><B>Trace Test</B> This flag is meant for single player maps that 'mutate' into deathmatch maps with the use of the Not in Deathmatch spawnflag and func_dmonly entity. The waypoints themselves will traceline to all outgoing links during route calculation, and will only follow the link if the trace was clear. This can be used for some other things, such as triggerable func_walls, and (very) slow moving obstacles.
<LI><B>AI Flags Pg. 1</B> This command brings you back to the first page of the AI Flags menu, where even more flags are kept.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>Bot Management Menu</H2>
<P>This menu allows you to control the FrikBots in unique ways to allow you to accurately test how the bot will behave as they pass waypoints.
<UL>
<LI><B>Add a test bot</B> This is the same as the impulse 100 command. It connects a new bot.
<LI><B>Order Test Bot Here</B> Causes the test bot to come to your location. The bot will immediate head toward you, following the waypoints as he goes. This is the important command here.
<LI><B>Remove Test Bot</B> This disconnects the bot (removes him from the server).
This is the same as the impulse command 102. It is recommended that you remove
all bots before dumping waypoints, as they may cause messages to appear that
will ruin the dump info.
<LI><B>Stop Test Bot</B> This causes the bot to stop whatever he is doing and
stand still.
<LI><B>Teleport Bot here</B> Instantly moves the bot to your currently selected waypoint.
This can be useful in testing certain areas of the map, be careful not to be too
close to the selected waypoint, or you may become stuck inside the bot. If you
do, you can use the teleport and/or the noclip command to free yourself.
<LI><B>Teleport to Way #</B> This is the same as the Main Menu's Teleport to Way # command. It is useful when dealing with bots, so it is here for convenience.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>Waylist Management</H2>
<P>This menu allows you to control the FrikBots in unique ways to allow you to accurately test how the bot will behave as they pass waypoints.
<UL>
<LI><B>Delete All Waypoints</B> Deletes all the waypoints on the level so you can start over.
<LI><B>Dump Waypoints</B> This prints to the console all the waypoint data in the below selected format. Please read the bottom of this section for details about what to do with waypoint dump data.
<LI><B>Check for Errors</B> Checks every waypoint for "duds". These are waypoints
with no outbound links. A waypoint should always have at least on outbound link
incase a bot reaches the waypoint, even if it is at a dead end. If a situation
is inescapable, remove the dud waypoint for improved performance with the bot.
<LI><B>Save Waypoints</B> This command is *still* not available in this version.
<LI><B>Dynamic Mode</B> This toggle turns on the dynamic waypointing mod present in non waypointed maps. Dynamic mode is terrible and can ruin waypoint sets. I suggest you do not use it for any reason.
<LI><B>Dynamic Link</B> This command turns on only the part of Dynamic mode associated with linking together waypoints. This can be time saving as the way in which the dynamic link functionality works is quite predictable.
<LI><B>WAY output</B> This command selects .way file output for the Dump command. Way files are recommended for distribution.
<LI><B>QuakeC output</B> This command selects .qc file output for the Dump command. QuakeC files are useful for running FrikBot in QW, and are an ideal way to package waypoints within mods.
<LI><B>BSP ents output</B> This command selects .ent-like output for the Dump command, as FrikBot chooses not to assume the starting condition of every other map entity it ONLY prints the waypoint entities. You must add the ent data it provides onto the map's entity list. Details for doing so can be found in the next section.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>Using Waypoint Dumps</H2>
<P>In order to use the waypoint editor effectively, you must save your waypoints. The way you do this is to make sure you ran quake with the command line option -condebug. Once done tweaking or creating the waypoints, use the Waylist's Dump command to print out all the data on the console. Quit the game. In your frikbot directory you'll find the file qconsole.log. Scroll through it until you find the comments flagging the top and bottom of the dump, take all the data and do the appropriate action:
<UL>
<LI>If it's .way file data, place it all in a .way file in your maps folder. Observer the comments to split the file if it is extremely long
<LI>If this is .qc file data, place the entire dump into a .qc file then include this in the progs.src about your bot.qc line. Open bot.qc and add the line map_mapname(); into bot_map_load. See the Add-on waypoints section for further details.
<LI>If this is BSP data, you'll need to obtain the original .ent data for the level. An excellent tool for this job is Maddes' bspentex, you can find it on <A HREF="http://www.inside3d.com/qip/">his site</A>. Another great tool is QuArK, you can open the BSP in quark, find the entity list (in plane text, scroll to the bottom and paste the entity patch data FrikBot dumped.
</UL>
<P>If you have any further questions on this topic, email me. See the Author section for details.
<H2>Shortcuts</H2>
<P>Shortcuts are a rapid but advanced way to use the editor. To use them, you need to know the formula: It's menu # times 16 plus the menu option. The menus were covered in this file in numerical order. The main menu is the first menu, 1 * 16, toggle noclip mode is menu option 6, so 16 + 6 is 22. Now that you have the code number for the command, you use it as such:
<blockquote>bind n "saved2 22; impulse 104"</blockquote>
<P>This will allow you to toggle noclip mode in the editor from any menu by merely pressing the N key. This is merely a time saving tool, if you don't understand how to use it, don't worry.
<P><P><P>
</TR></TD>
</BODY>
</HTML>

209
fbxa/kc18doc.txt Normal file
View file

@ -0,0 +1,209 @@
Title : Kasuha's DeathMatch Camera
Version : 1.8
Filename : kascam18.zip
Date : 6 Jul 1997
Author : Karel Suhajda aka KASUHA
Email : suhajda@serverpha.czcom.cz
Note : this patch is based on DMCAM and QCAM patches
Type of Mod
-----------
Progs.dat: yes
Sound : no
MDL : no
BSP : no
QC : YES!!!
Time spent: don't ask me, I don't know...
--------------------------------------------------------------------------------
* Credits
- Id software for Quake
- Rogier R. Mulhuijzen for DMCAM and its sources
- Paul Jordan for QCAM and its sources
- The NecroRaisers clan for betatesting
- All you who sent me suggestions and help :-)
--------------------------------------------------------------------------------
* Introduction
I have seen some demos done using DMCAM and QCAM and I got the idea to do it
a better way.
Then I have seen some demos taken using KasCam and I got the idea how to do it
even better way...
--------------------------------------------------------------------------------
* Installation
Put the progs.dat in a sub-dir of the server's quake-directory (kascam for
example).
--------------------------------------------------------------------------------
* How to use the patch
Start up the server with -game <kascam's dir>. If you called the dir "kascam"
you would type:
quake -game kascam
Next, change the name of the client you want to use as the camera into
"". You can do it at console typing NAME "".
You can still use old "CamClient" name also, but "" will not appear in the
score table.
When that client connects it is automaticaly turned into the
camera. It is a good idea to run the camera on a listen server.
You can also turn normal player to camera using "IMPULSE 250" command
without renaming.
If you don't want to see CamClient in results table, change its name to ""
Every player can change to camera now! There is no limit on one camera per game!
I don't recommend too many cameras in a game - camera code is highly processor
intensive and you will get a slowdown or even 'runaway loop error'.
--------------------------------------------------------------------------------
* How it works
The player turns to camera-man when his name is "" or "CamClient" during
connect or when he types "impulse 250" on console.
The camera has eight working modes - five automatic and three manual.
o IDLE mode: camera flies randomly in the current room. This mode is used only
when there is no live player in the level.
When there appears any live player, camera enters the next mode:
o FLYBY mode: generates in the vicinity of targeted player and
looks on him until he escapes round a corner or until he is too far
away. Then a new camera is generated. When a player is near the camera
(up to 200) and he is facing away, camera enters the next mode:
o FOLLOW mode: follows targeted player everywhere he goes. When the player
disappears (thru a teleport or under/off water), camera switches to FLYBY.
o FIXED mode: used automatically from FLYBY mode when there is enough players
visible at the same time. Tries to see as many players as possible, doesn't
concentrate on single player. When here is less than two players visible
for more than three seconds, camera searches for a new individual target.
If this mode is entered automatically, it lasts no more than 15 seconds.
o DEATH mode: selected automatically when targeted player turns dead. Tracks
dead body or head and focuses on it. Reports player's name and frag count.
This mode is not manually selectible.
o HAND mode: camera is unmovable but you can look around using controls.
o FLY mode: 'flying' camera - can't go thru walls. Moves slowly.
o NOCLIP mode: figure yourself.
When a player is targeted, camera shows also his status - weapons, ammo, armor,
health, keys. It doesn't show items which change the view color (biosuit,
pentagram, ring, quad).
Camera reports a new "victim" by its name and frag count. This is reported
to camera only. You can turn it off.
When the camera selects a player, it pursuits him 60 seconds or until death
if it comes sooner :-) Camera swaps FLYBY/FOLLOW mode or can swap to FIXED
mode when there are many players visible. Camera goes to DEATH mode when its
target die.
The selected player is always a live player. Selecting criteria is randomly
selected from following possibilities:
20% most frags
20% most health (max damage player can bear, counting current health and armor)
60% random selection
The CamClient can also control the camera:
IMPULSE 100: Select automatically a new target.
IMPULSE 1xx: Select xx-th player in order they connected. Starts in FLYBY.
(CamClient is not a player)
Doesn't switch to a new target if it is dead or dying.
IMPULSE 18x: Store current camera position to memory x (0 to 9).
IMPULSE 19x: Restore camera position from memory x. Works only in
non-automatic modes.
IMPULSE 200: Force FLYBY mode on current target (may immediately turn to
FOLLOW) When no target is selected camera chooses a player
nearest to the screen centre. When here is nobody visible
nothing happens.
IMPULSE 201: Force FOLLOW mode on current target.
When no target is selected camera chooses a player nearest to the
screen centre. When here is nobody visible nothing happens.
IMPULSE 202: Force HAND mode. Also moves camera to the point it is aiming at
(not if in FLYBY or FIXED mode).
You can move camera in HAND mode using IMPULSE 202.
IMPULSE 203: Force FLY mode.
IMPULSE 204: Force NOCLIP mode.
IMPULSE 205: Force FIXED mode.
IMPULSE 210: Turn "NOW TAKING" and "R.I.P." messages off/on.
IMPULSE 211: Reports current camera position. Somebody wanted it...
IMPULSE 212: Turn "NOW TAKING" and "R.I.P." messages ON
IMPULSE 213: Turn "NOW TAKING" and "R.I.P." messages OFF
IMPULSE 214: Always force FOLLOW mode - when a new player is selected, camera
immediately goes to FOLLOW mode.
IMPULSE 215: Stop 'always force FOLLOW' - camera behaves normally from now on.
IMPULSE 216: Lock on current target. No other target is selected until
IMPULSE 100 or IMPULSE 1xx is used. Designed to take demos
of individual people (e.g. during a final match :-)
IMPULSE 217: Skip to next player. If the current player is locked using
IMPULSE 216, lock is moved also. Skips all non-live targets.
When no target is selected, nothing happens.
IMPULSE 250: Turn to camera if you aren't already.
NOTE: Described IMPULSEs excluding IMPULSE 250 work for camera only.
IMPULSE 250 works for non-cameras only.
Standard impulses work for non-cameras only.
------------------------------------------------------------------------------
* Known bugs
- Camera may not have any good spot points in FOLLOW/FLYBY. Then it behaves
like IDLE though a target is selected. This is *very* unlikely to happen.
- Camera sometimes loses contact with its target on a lift in FLYBY mode.
- Camera doesn't move to intermission spot during intermission.
I am not sure if these can be considered 'bugs'. I call it 'minor problems' ;-)
A real bug:
- Sometimes camera does "automatic centerview". Don't know why it happens.
A workaround: enter both "+LOOKUP" and "+LOOKDOWN" at the console.
This may interfere with manual modes.
Really don't know how to remove this :-(
------------------------------------------------------------------------------
* short FAQ
Q: Can I use KasCam on single computer?
A: No. Well, you can, but you'll have nothing to take. KasCam is one player
converted to camera so on single computer you'll have nobody to do any
action. You can only make demo of monsters walking thru corridors :-)
Q: Can I use KasCam take my single-player game?
A: Yes, but you need two computers. One to play on and one for camera. You can
start in coop and then turn the second player to camera.
Q: How can I remove CamClient from score table?
A: Change its name to "". You can do it both before or after starting the
camera.
Q: How can I switch to a saved position if I am in automatic mode?
A: Use the following: "IMPULSE 203;WAIT;IMPULSE 19x". It first changes to FLY
mode (camera doesn't move) and then to your selected saved position.
------------------------------------------------------------------------------
* Copyright and Distribution Permissions (I like these from DMCAM so here goes:)
Authors MAY use these modifications as a basis for other
publically available work, after all, I did.
You may distribute this Quake modification in any free electronic format
as long as this description file remains intact and unmodified and is
retained along with all of the files in the archive.
You may not publish or distribute this Quake modification in a non-free
form without the permission of the author.

53
fbxa/map_dm1.qc Normal file
View file

@ -0,0 +1,53 @@
/* QC Waypoint Dump - src/frikbot/map_dm1.qc
For instructions please read the
readme.html that comes with FrikBot */
void(vector org, vector bit1, float bit4, float flargs) make_way;
// Ways by Electro
void() map_dm1 =
{
make_way('639.9 716.3 46.0', '2 22 0', 0, 0);
make_way('1040.1 749.3 94.0', '1 3 0', 0, 0);
make_way('1053.5 1321.0 94.0', '2 4 0', 0, 0);
make_way('782.5 1313.2 46.0', '6 41 0', 0, 0);
make_way('584.6 882.9 46.0', '4 6 41', 0, 0);
make_way('934.2 1048.0 46.0', '4 5 41', 0, 0);
make_way('447.8 1313.4 42.0', '41 31 8', 0, 0);
make_way('435.8 1562.0 46.0', '7 9 0', 0, 0);
make_way('238.3 1592.6 46.0', '8 10 19', 0, 0);
make_way('-122.1 1582.6 -98.0', '9 11 0', 0, 0);
make_way('-127.2 1414.0 -98.0', '10 12 0', 0, 0);
make_way('113.2 1416.1 -98.0', '11 13 0', 0, 0);
make_way('108.8 1286.5 -98.0', '12 14 15', 35, 0);
make_way('417.0 1258.0 -98.0', '13 39 35', 0, 0);
make_way('152.0 951.8 -98.0', '13 16 35', 0, 0);
make_way('168.6 776.6 -98.0', '15 17 0', 0, 0);
make_way('-283.5 879.9 -98.0', '16 42 0', 0, 0);
make_way('396.7 1594.7 -98.0', '39 7 0', 0, 2);
make_way('253.6 1416.7 46.0', '9 20 0', 0, 128);
make_way('-126.0 1418.0 50.8', '19 21 0', 0, 32784);
make_way('-359.5 1398.6 46.0', '40 0 0', 0, 0);
make_way('596.8 603.6 46.0', '1 23 0', 0, 0);
make_way('484.9 598.6 46.0', '22 24 38', 0, 0);
make_way('77.0 614.3 46.0', '23 25 0', 0, 0);
make_way('56.8 738.3 46.0', '24 26 15', 42, 0);
make_way('-501.1 769.8 46.0', '25 27 0', 0, 0);
make_way('-534.0 988.3 46.0', '26 28 0', 0, 0);
make_way('-654.8 991.8 46.0', '27 29 0', 0, 0);
make_way('-650.7 1227.0 46.0', '28 30 36', 0, 0);
make_way('-523.7 1591.5 46.0', '29 10 43', 0, 128);
make_way('443.3 1145.0 46.0', '7 32 33', 0, 0);
make_way('220.2 1235.6 46.0', '31 34 0', 0, 0);
make_way('177.9 1026.7 46.0', '31 34 0', 0, 0);
make_way('28.2 1157.3 46.0', '33 32 35', 0, 128);
make_way('178.2 1111.7 -98.0', '13 15 42', 14, 0);
make_way('-307.0 1218.6 94.0', '29 37 43', 0, 0);
make_way('-292.1 1100.2 94.0', '36 17 42', 0, 0);
make_way('463.9 816.7 46.0', '23 16 15', 0, 128);
make_way('434.7 1451.0 -98.0', '18 0 0', 0, 128);
make_way('-314.2 1410.9 46.0', '11 0 0', 0, 256);
make_way('599.9 1309.0 46.0', '4 5 7', 6, 0);
make_way('-321.9 1006.5 -98.0', '17 35 0', 0, 0);
make_way('-542.5 1222.3 46.0', '30 36 0', 0, 0);
};

98
fbxa/map_dm2.qc Normal file
View file

@ -0,0 +1,98 @@
/* QC Waypoint Dump - src/frikbot/map_dm2.qc
For instructions please read the
readme.html that comes with FrikBot */
void(vector org, vector bit1, float bit4, float flargs) make_way;
// Ways by Frika C
void() map_dm2 =
{
make_way('2441.0 -189.1 46.0', '2 72 0', 0, 0);
make_way('2036.7 -141.6 46.0', '26 1 41', 46, 0);
make_way('2126.7 238.6 46.0', '0 43 41', 0, 0);
make_way('2615.4 -7.7 142.0', '68 41 0', 0, 0);
make_way('2614.6 -781.7 142.0', '73 6 62', 0, 0);
make_way('2455.8 -830.3 142.0', '5 7 48', 62, 0);
make_way('2511.8 -1395.2 46.0', '8 11 62', 0, 0);
make_way('2481.5 -1681.3 46.0', '28 9 7', 0, 0);
make_way('2391.6 -1861.9 46.0', '10 8 0', 0, 0);
make_way('2184.0 -1867.0 46.0', '53 9 0', 0, 0);
make_way('2741.6 -1424.7 46.0', '7 12 0', 0, 0);
make_way('2735.0 -1693.3 142.0', '11 47 0', 0, 0);
make_way('2927.9 -1852.4 142.0', '47 14 0', 0, 16);
make_way('2946.4 -1993.7 142.0', '13 37 0', 0, 16);
make_way('2254.5 -185.3 -114.0', '16 0 0', 0, 0);
make_way('2249.8 -27.2 -114.0', '15 17 0', 0, 0);
make_way('2611.8 -8.2 -50.0', '44 16 45', 0, 0);
make_way('1347.5 -1024.0 366.0', '59 58 0', 0, 0);
make_way('2002.3 -1024.0 366.0', '35 0 0', 0, 0);
make_way('1330.1 -714.6 366.0', '18 0 59', 0, 1024);
make_way('1323.0 -381.6 206.0', '22 24 0', 0, 0);
make_way('1328.2 -487.0 206.0', '21 20 61', 0, 0);
make_way('1559.7 -654.4 206.0', '0 60 0', 0, 0);
make_way('1837.4 -363.6 206.0', '21 25 0', 0, 0);
make_way('1877.0 -652.0 206.0', '24 26 0', 0, 0);
make_way('2038.3 -473.0 126.0', '2 27 50', 0, 0);
make_way('1683.4 -506.8 46.0', '26 64 63', 71, 0);
make_way('1699.3 -1694.6 46.0', '65 8 0', 0, 65536);
make_way('1697.9 -1336.4 42.0', '35 31 30', 65, 32784);
make_way('2058.4 -1354.2 158.0', '29 33 0', 0, 0);
make_way('1307.4 -1338.4 166.0', '29 32 0', 0, 0);
make_way('1315.8 -1034.8 222.0', '31 36 0', 0, 0);
make_way('2043.6 -1030.7 222.0', '30 34 0', 0, 0);
make_way('2043.6 -904.3 54.0', '35 0 0', 0, 1);
make_way('1696.0 -1037.4 42.0', '64 29 55', 56, 32784);
make_way('1307.4 -872.6 54.0', '35 0 0', 0, 1);
make_way('2733.5 -2008.2 142.0', '38 0 0', 0, 0);
make_way('2731.0 -2283.1 142.0', '37 39 0', 0, 0);
make_way('2425.1 -2305.7 142.0', '38 40 0', 0, 0);
make_way('2423.1 -2444.5 142.0', '39 67 31', 0, 4);
make_way('2175.6 -55.9 46.0', '4 2 0', 3, 0);
make_way('2181.9 -2388.8 222.0', '67 70 0', 0, 0);
make_way('2347.3 317.0 30.0', '3 74 0', 0, 0);
make_way('2644.7 235.2 -50.0', '74 17 0', 0, 0);
make_way('2806.9 -27.3 -50.0', '18 0 0', 0, 1);
make_way('2408.5 -401.3 30.0', '2 0 0', 0, 0);
make_way('2742.7 -1857.6 142.0', '12 13 0', 0, 0);
make_way('2352.0 -858.9 78.0', '49 0 0', 0, 0);
make_way('2375.9 -685.3 78.0', '48 46 0', 0, 0);
make_way('1971.3 -556.1 126.0', '25 0 0', 0, 4096);
make_way('2429.1 -1968.0 142.0', '52 66 0', 0, 128);
make_way('2280.0 -1968.0 142.0', '51 53 0', 0, 160);
make_way('2170.1 -1977.8 94.0', '10 69 0', 0, 160);
make_way('2189.7 -2258.8 106.0', '42 0 0', 0, 4096);
make_way('1424.0 -944.4 42.0', '35 0 0', 0, 32768);
make_way('1908.5 -928.4 42.0', '35 0 0', 0, 32768);
make_way('1825.4 -1029.8 366.0', '19 0 0', 0, 2048);
make_way('1512.9 -1015.2 366.0', '18 57 0', 0, 2048);
make_way('1264.0 -721.5 366.0', '20 22 18', 0, 0);
make_way('1440.6 -673.6 206.0', '23 61 0', 0, 32);
make_way('1449.1 -566.9 206.0', '60 22 63', 0, 32);
make_way('2525.9 -867.2 142.0', '6 0 5', 0, 1024);
make_way('1545.7 -488.4 46.0', '27 0 0', 0, 128);
make_way('1687.9 -744.0 42.0', '27 35 0', 0, 32784);
make_way('1700.6 -1513.2 46.0', '29 28 0', 0, 0);
make_way('2553.9 -1971.9 142.0', '51 0 0', 0, 160);
make_way('2160.2 -2469.1 222.0', '42 40 0', 0, 0);
make_way('2601.2 -176.0 142.0', '4 73 0', 0, 0);
make_way('2178.2 -2134.2 106.0', '53 54 70', 0, 0);
make_way('2172.0 -2347.6 110.0', '69 75 0', 0, 0);
make_way('1622.4 -528.7 46.0', '61 0 0', 0, 4096);
make_way('2576.0 -192.1 54.0', '1 0 0', 0, 0);
make_way('2611.3 -441.3 142.0', '5 68 0', 0, 0);
make_way('2456.5 297.3 -34.0', '43 44 0', 0, 0);
make_way('2168.4 -2336.3 110.0', '76 0 0', 0, 16);
make_way('2165.7 -1808.0 46.0', '77 0 0', 0, 0);
make_way('2529.4 -1844.5 46.0', '78 0 0', 0, 0);
make_way('2585.1 -1394.2 46.0', '79 0 0', 0, 0);
make_way('2752.6 -1389.0 46.0', '80 0 0', 0, 0);
make_way('2734.7 -1822.2 142.0', '81 0 0', 0, 0);
make_way('2980.3 -1832.3 142.0', '82 0 0', 0, 16);
make_way('2957.0 -2032.2 142.0', '83 0 0', 0, 16);
make_way('2720.9 -2057.8 150.0', '84 0 0', 0, 0);
make_way('2693.6 -2275.7 142.0', '85 0 0', 0, 0);
make_way('2429.9 -2295.8 142.0', '86 0 0', 0, 0);
make_way('2431.5 -2115.8 142.0', '51 0 0', 0, 32784);
};
// End dump

149
fbxa/map_dm3.qc Normal file
View file

@ -0,0 +1,149 @@
/* QC Waypoint Dump - src/frikbot/map_dm3.qc
For instructions please read the
wayedit.txt that comes with FrikBot */
void(vector org, vector bit1, float bit4, float flargs) make_way;
// Ways by Frika C
void() map_dm3 =
{
make_way('-888.8 -71.0 6.0', '44 3 2', 0, 0);
make_way('-937.4 19.2 6.0', '3 1 45', 0, 0);
make_way('-827.2 26.1 6.0', '1 2 4', 0, 0);
make_way('-801.5 366.8 6.0', '3 5 0', 0, 0);
make_way('-783.8 774.2 6.0', '4 6 0', 0, 0);
make_way('-550.2 759.6 6.0', '5 7 0', 0, 0);
make_way('-574.0 368.0 6.0', '6 8 0', 0, 0);
make_way('-430.2 378.3 6.0', '7 9 0', 0, 0);
make_way('-406.9 784.0 6.0', '8 10 0', 0, 0);
make_way('-259.9 772.3 6.0', '9 11 12', 0, 0);
make_way('-243.7 250.5 14.0', '10 12 0', 0, 0);
make_way('-35.8 581.6 62.0', '10 11 13', 14, 0);
make_way(' 29.5 647.5 62.0', '12 15 49', 0, 0);
make_way('521.9 603.4 78.0', '12 15 0', 0, 0);
make_way('507.6 514.3 78.0', '14 13 16', 0, 0);
make_way('504.3 308.4 78.0', '15 20 17', 0, 0);
make_way('175.2 -34.7 78.0', '16 19 20', 18, 0);
make_way('485.8 -387.2 78.0', '17 21 19', 0, 0);
make_way('368.0 -201.7 78.0', '17 18 51', 0, 0);
make_way('397.0 124.9 78.0', '16 17 51', 0, 0);
make_way('496.0 -558.1 78.0', '18 22 58', 31, 0);
make_way('147.5 -528.0 174.0', '21 23 0', 0, 128);
make_way('-213.3 -528.0 174.0', '22 24 0', 0, 128);
make_way('-229.6 -865.0 174.0', '23 25 0', 0, 128);
make_way('339.0 -873.6 286.0', '24 26 0', 0, 128);
make_way('341.2 -538.2 286.0', '25 27 0', 0, 128);
make_way('-143.0 -535.0 350.0', '26 28 0', 0, 128);
make_way('-143.3 -696.6 350.0', '27 29 0', 0, 128);
make_way('283.6 -703.7 350.0', '28 30 0', 0, 128);
make_way('371.0 -699.8 286.0', '31 0 0', 0, 128);
make_way('416.0 -691.3 6.0', '33 32 0', 0, 0);
make_way('253.9 -794.9 6.0', '31 57 34', 0, 0);
make_way('253.7 -610.4 6.0', '57 31 0', 0, 0);
make_way('-64.0 -727.5 6.0', '32 35 36', 37, 0);
make_way('-40.6 -874.3 6.0', '34 37 58', 0, 128);
make_way(' 43.7 -535.1 6.0', '37 57 34', 56, 0);
make_way('-172.1 -560.9 6.0', '38 36 35', 34, 0);
make_way('-298.6 -563.7 6.0', '39 37 0', 0, 0);
make_way('-318.6 -710.5 6.0', '38 40 0', 0, 0);
make_way('-629.4 -683.2 6.0', '39 41 0', 0, 0);
make_way('-626.9 -449.0 6.0', '40 42 43', 0, 0);
make_way('-565.7 -449.0 6.0', '41 19 0', 0, 2);
make_way('-654.9 -271.1 6.0', '41 44 0', 0, 0);
make_way('-873.3 -247.0 6.0', '43 1 0', 0, 0);
make_way('-939.9 347.7 142.0', '46 2 0', 0, 0);
make_way('-912.7 821.5 142.0', '47 45 5', 0, 0);
make_way('-577.0 859.7 142.0', '50 46 48', 0, 0);
make_way('-556.0 784.6 142.0', '47 6 59', 0, 256);
make_way('-98.5 799.3 142.0', '13 50 0', 0, 0);
make_way('-198.8 861.0 142.0', '49 47 10', 0, 0);
make_way('584.5 -30.1 -146.0', '52 69 71', 0, 0);
make_way('288.5 -27.4 -154.0', '53 51 0', 0, 0);
make_way('126.7 -188.8 -154.0', '54 52 0', 0, 0);
make_way('-164.4 -210.3 -154.0', '55 53 0', 0, 0);
make_way('-177.9 -425.6 -90.0', '56 54 0', 0, 0);
make_way(' 52.6 -443.0 -10.0', '36 55 0', 0, 0);
make_way(' 71.4 -634.1 6.0', '36 33 32', 0, 0);
make_way('496.0 -880.0 78.0', '35 21 31', 0, 128);
make_way('-528.0 664.4 142.0', '60 6 0', 0, 65536);
make_way('-496.8 461.0 142.0', '59 8 7', 61, 65792);
make_way('-466.9 320.2 142.0', '62 11 7', 60, 65824);
make_way('-459.7 277.2 166.0', '63 0 0', 0, 65824);
make_way('-467.3 208.0 198.0', '11 64 0', 0, 65536);
make_way('-719.9 208.0 206.0', '63 68 0', 0, 65664);
make_way('-825.0 251.2 206.0', '68 66 3', 0, 65792);
make_way('-799.7 108.2 206.0', '67 2 0', 0, 65568);
make_way('-688.0 99.6 206.0', '66 0 0', 0, 65536);
make_way('-797.7 318.5 206.0', '65 64 45', 0, 65664);
make_way('822.8 -265.4 -162.0', '51 70 0', 0, 0);
make_way('1000.0 -29.9 -162.0', '71 69 122', 0, 0);
make_way('827.2 198.1 -162.0', '51 70 0', 0, 0);
make_way('1970.0 -31.5 -146.0', '123 78 73', 0, 0);
make_way('1982.6 197.3 -66.0', '72 74 0', 0, 0);
make_way('1964.1 437.3 -66.0', '73 75 76', 0, 0);
make_way('1544.8 491.8 -66.0', '74 76 0', 0, 0);
make_way('1693.0 400.0 -66.0', '75 74 77', 0, 256);
make_way('1695.4 362.0 -42.0', '132 131 0', 0, 0);
make_way('1963.1 -436.8 -2.0', '72 79 0', 0, 0);
make_way('1380.4 -427.4 -2.0', '78 102 80', 0, 0);
make_way('1186.5 -681.2 -2.0', '81 79 121', 0, 0);
make_way('1322.4 -745.5 -2.0', '82 80 0', 0, 0);
make_way('1461.8 -756.2 -2.0', '85 83 81', 0, 0);
make_way('1432.7 -944.0 -2.0', '82 84 0', 0, 0);
make_way('1248.5 -926.6 -2.0', '83 101 0', 0, 2);
make_way('1707.9 -715.7 -2.0', '86 82 0', 0, 0);
make_way('1821.0 -850.6 -2.0', '87 85 0', 0, 0);
make_way('1531.5 -899.7 110.0', '88 86 82', 85, 0);
make_way('1125.5 -896.2 110.0', '89 87 0', 0, 0);
make_way('986.5 -799.6 110.0', '90 88 80', 121, 0);
make_way('811.1 -657.7 110.0', '91 89 0', 0, 0);
make_way('658.1 -638.4 110.0', '92 90 0', 0, 0);
make_way('656.0 -356.4 78.0', '93 91 0', 0, 0);
make_way('824.9 -256.7 78.0', '94 92 51', 0, 0);
make_way('952.1 -32.8 78.0', '95 93 0', 0, 0);
make_way('817.3 175.9 78.0', '96 94 51', 0, 0);
make_way('633.2 323.0 78.0', '97 95 0', 0, 0);
make_way('631.7 415.3 78.0', '98 96 0', 0, 0);
make_way('822.9 444.2 78.0', '99 101 97', 0, 0);
make_way('1111.5 624.0 78.0', '101 100 98', 0, 256);
make_way('1114.6 651.3 102.0', '111 0 0', 0, 768);
make_way('1338.2 383.4 78.0', '102 99 98', 0, 0);
make_way('1366.0 -48.8 -2.0', '79 101 123', 122, 0);
make_way('1355.8 65.9 -370.0', '128 125 132', 104, 65600);
make_way('1362.6 635.0 -334.0', '103 135 0', 0, 65792);
make_way('1366.7 875.6 -274.0', '135 111 0', 0, 0);
make_way('841.8 1105.2 -242.0', '111 112 107', 0, 0);
make_way('1439.7 1113.7 -213.0', '106 108 0', 0, 0);
make_way('1871.4 990.6 -191.0', '107 109 0', 0, 0);
make_way('1851.0 655.5 -158.0', '108 110 0', 0, 0);
make_way('1542.3 802.5 -274.0', '105 104 0', 0, 0);
make_way('1074.9 805.1 -274.0', '105 112 106', 135, 0);
make_way('703.5 681.7 -242.0', '111 106 113', 0, 1024);
make_way('608.0 681.2 -82.0', '114 0 0', 0, 0);
make_way('612.4 789.1 -82.0', '115 111 0', 0, 1024);
make_way('614.1 858.3 46.0', '116 0 0', 0, 0);
make_way('618.9 1020.4 46.0', '115 117 0', 0, 0);
make_way('539.7 1025.0 62.0', '116 118 0', 0, 0);
make_way('516.6 923.8 62.0', '117 119 0', 0, 1024);
make_way('511.4 848.0 238.0', '120 0 0', 0, 0);
make_way('504.8 654.8 238.0', '119 14 0', 0, 0);
make_way('1112.4 -706.8 -2.0', '89 80 0', 0, 4096);
make_way('1124.4 -31.4 -146.0', '138 70 124', 126, 0);
make_way('1654.0 -38.6 -146.0', '137 72 131', 129, 0);
make_way('1160.0 78.5 -147.2', '125 122 0', 0, 512);
make_way('1137.3 45.9 -370.0', '124 103 127', 0, 65536);
make_way('1161.1 -144.4 -148.3', '122 127 0', 0, 66048);
make_way('1159.7 -167.3 -370.0', '126 125 128', 134, 65536);
make_way('1468.4 -211.2 -370.0', '129 103 133', 130, 65536);
make_way('1654.8 -127.0 -162.2', '123 128 134', 0, 65600);
make_way('1798.0 -355.0 -370.0', '128 133 0', 0, 65536);
make_way('1660.2 51.5 -152.0', '132 123 0', 0, 64);
make_way('1657.1 71.4 -370.0', '103 131 133', 0, 65536);
make_way('1621.6 -110.8 -370.0', '128 132 130', 0, 65536);
make_way('1344.8 -141.7 -370.0', '127 103 128', 129, 65536);
make_way('1368.7 785.3 -271.1', '105 104 0', 0, 0);
make_way('1365.6 -34.1 -146.0', '138 137 0', 0, 0);
make_way('1536.2 -35.6 -146.0', '136 123 0', 0, 0);
make_way('1197.9 -32.8 -146.0', '122 136 0', 0, 0);
make_way('1537.0 8.8 -146.0', '123 102 0', 0, 4096);
};

50
fbxa/map_dm4.qc Normal file
View file

@ -0,0 +1,50 @@
/* QC Waypoint Dump - src/frikbot/map_dm4.qc
For instructions please read the
wayedit.txt that comes with FrikBot */
void(vector org, vector bit1, float bit4, float flargs) make_way;
void() map_dm4 =
{
make_way('-114.0 529.9 -274.0', '0 38 0', 0, 128);
make_way('-21.9 -216.6 -274.0', '0 33 37', 0, 128);
make_way('344.2 -524.5 -82.0', '4 15 14', 36, 128);
make_way('220.5 -432.0 -82.0', '3 0 0', 0, 160);
make_way('362.8 -237.4 -274.0', '0 6 7', 37, 128);
make_way('362.8 -4.9 -274.0', '5 0 0', 0, 128);
make_way('605.3 -238.8 -274.0', '8 5 0', 0, 128);
make_way('614.0 49.8 -274.0', '9 7 0', 0, 1);
make_way('312.0 -923.0 46.0', '10 16 0', 0, 0);
make_way('326.1 -1067.7 46.0', '11 9 0', 0, 0);
make_way('329.8 -1219.7 -2.0', '12 10 13', 0, 0);
make_way('113.8 -1191.1 -82.0', '13 11 14', 0, 0);
make_way(' 89.9 -980.1 -82.0', '14 12 36', 0, 0);
make_way('326.0 -896.1 -82.0', '3 13 12', 36, 0);
make_way('553.1 -287.3 -82.0', '30 32 41', 3, 128);
make_way('355.7 -527.4 46.0', '17 26 9', 0, 128);
make_way('518.1 -475.3 46.0', '18 16 41', 0, 128);
make_way('603.5 -188.0 46.0', '19 17 32', 0, 128);
make_way('847.2 -182.0 46.0', '20 18 0', 0, 0);
make_way('855.5 -554.8 46.0', '21 22 19', 0, 0);
make_way('1092.6 -573.4 46.0', '20 0 0', 0, 0);
make_way('1007.5 -432.0 -82.0', '23 39 0', 0, 0);
make_way('777.1 -458.1 -82.0', '24 0 0', 0, 0);
make_way('772.6 -642.4 -138.7', '25 23 0', 0, 0);
make_way('773.9 -853.5 -210.0', '6 24 0', 0, 1);
make_way('112.0 -486.9 46.0', '27 16 0', 0, 128);
make_way('136.7 -272.5 -34.0', '40 28 26', 0, 128);
make_way('-106.0 -235.8 -50.0', '29 27 0', 0, 0);
make_way('-124.8 -568.3 -50.0', '19 28 0', 0, 1);
make_way('627.7 -426.3 -98.0', '7 15 32', 0, 1);
make_way('224.0 81.0 -82.0', '37 0 0', 0, 0);
make_way('506.2 -96.8 -82.0', '15 30 6', 0, 128);
make_way('-70.0 78.4 -274.0', '34 2 0', 38, 128);
make_way(' 88.6 71.9 -274.0', '33 0 0', 0, 160);
make_way('112.0 -176.0 -415.1', '0 0 0', 0, 0);
make_way('337.6 -687.8 -82.0', '14 3 13', 0, 0);
make_way('159.2 -250.3 -274.0', '5 2 0', 0, 128);
make_way('-77.0 287.6 -274.0', '33 1 0', 0, 160);
make_way('964.6 -623.9 -82.0', '0 24 22', 0, 0);
make_way('150.1 -191.5 -50.0', '31 0 0', 0, 384);
make_way('502.7 -271.1 -82.0', '15 0 0', 0, 0);
};

56
fbxa/map_dm5.qc Normal file
View file

@ -0,0 +1,56 @@
/* QC Waypoint Dump - src/frikbot/map_dm5.qc
For instructions please read the
readme.html that comes with FrikBot */
void(vector org, vector bit1, float bit4, float flargs) make_way;
// Ways by Electro
void() map_dm5 =
{
make_way('90.2 -380.0 62.0', '2 11 0', 0, 0);
make_way('-88.5 -373.4 62.0', '3 1 28', 0, 0);
make_way('-71.1 321.2 62.0', '12 0 2', 37, 0);
make_way('288.7 451.8 14.0', '3 5 6', 37, 0);
make_way('500.1 370.9 14.0', '4 0 0', 0, 0);
make_way('287.3 374.0 -10.0', '4 7 0', 0, 0);
make_way('296.2 -97.3 -90.0', '6 8 0', 0, 0);
make_way('657.0 -24.1 14.0', '7 45 0', 0, 0);
make_way('280.9 -314.6 -90.0', '7 45 0', 0, 0);
make_way('265.3 -582.0 -90.0', '9 43 0', 0, 0);
make_way('90.5 -582.4 -90.0', '10 0 0', 0, 0);
make_way('-72.3 511.6 62.0', '13 3 0', 0, 0);
make_way('106.7 693.8 142.0', '14 12 0', 0, 0);
make_way('286.6 704.6 142.0', '17 13 15', 16, 0);
make_way('286.5 208.0 142.0', '14 36 0', 0, 32768);
make_way('259.1 938.8 142.0', '0 39 0', 0, 0);
make_way('450.5 686.4 142.0', '18 14 0', 0, 0);
make_way('694.2 466.0 238.0', '19 17 0', 0, 0);
make_way('729.0 367.2 238.0', '27 18 20', 0, 1);
make_way('305.5 371.9 238.0', '19 21 15', 36, 0);
make_way('-85.4 351.2 238.0', '20 22 34', 12, 0);
make_way('-98.1 -226.8 238.0', '21 33 32', 23, 0);
make_way('-96.1 -776.4 238.0', '24 22 38', 11, 0);
make_way('362.9 -768.2 238.0', '23 46 35', 0, 0);
make_way('611.0 -762.8 238.0', '26 46 0', 0, 0);
make_way('893.3 -768.3 238.0', '31 25 27', 0, 0);
make_way('1048.0 -769.1 238.0', '19 26 0', 0, 1);
make_way('-105.7 -761.0 62.0', '2 29 0', 0, 0);
make_way('309.1 -759.3 142.0', '35 28 30', 0, 0);
make_way('338.1 -388.8 142.0', '35 9 29', 42, 0);
make_way('889.2 -228.7 238.0', '32 26 0', 0, 0);
make_way('320.6 -223.8 238.0', '22 31 30', 0, 0);
make_way('-180.2 -200.8 189.9', '2 0 0', 0, 0);
make_way('-178.1 376.2 185.4', '3 0 0', 0, 0);
make_way('399.5 -577.5 174.0', '42 29 30', 0, 0);
make_way('286.3 475.6 142.0', '5 37 14', 0, 0);
make_way('96.3 482.8 14.0', '4 3 6', 0, 0);
make_way('-168.6 -824.8 187.8', '28 0 0', 0, 0);
make_way('288.1 1006.5 142.0', '16 14 0', 0, 32768);
make_way('615.7 -451.0 158.0', '41 25 42', 0, 0);
make_way('662.5 -326.1 142.0', '40 0 0', 0, 0);
make_way('479.8 -463.0 158.0', '35 30 40', 0, 0);
make_way('407.3 -392.4 -90.0', '44 10 0', 0, 0);
make_way('547.8 -309.4 -90.0', '45 43 0', 0, 0);
make_way('544.3 -52.9 -90.0', '9 8 44', 0, 0);
make_way('488.5 -769.3 238.0', '25 24 0', 0, 0);
};

71
fbxa/map_dm6.qc Normal file
View file

@ -0,0 +1,71 @@
/* QC Waypoint Dump - src/frikbot/map_dm6.qc
For instructions please read the
readme.html that comes with FrikBot */
void(vector org, vector bit1, float bit4, float flargs) make_way;
// Ways by Electro
void() map_dm6 =
{
make_way('760.0 -1080.0 278.0', '2 8 51', 0, 0);
make_way('830.4 -879.6 278.0', '1 3 0', 0, 0);
make_way('994.1 -851.7 278.0', '2 4 0', 0, 0);
make_way('1212.9 -877.9 278.0', '3 5 0', 0, 0);
make_way('1301.3 -1089.0 278.0', '4 6 0', 10, 0);
make_way('1199.4 -1302.0 278.0', '5 7 0', 0, 0);
make_way('1040.4 -1348.9 278.0', '6 8 0', 0, 0);
make_way('855.7 -1312.7 278.0', '7 1 0', 0, 0);
make_way('1033.2 -1465.7 278.0', '7 0 0', 0, 0);
make_way('1519.3 -1087.3 231.4', '11 5 0', 0, 0);
make_way('1758.3 -1065.6 190.0', '10 12 0', 0, 0);
make_way('1763.4 -907.5 190.0', '11 13 58', 0, 0);
make_way('1763.9 -706.4 190.0', '12 14 57', 0, 0);
make_way('1765.6 -418.0 190.0', '13 18 15', 23, 0);
make_way('1882.8 -419.8 190.0', '14 16 0', 0, 0);
make_way('1890.5 -157.4 190.0', '15 17 0', 0, 0);
make_way('1628.3 -158.2 190.0', '16 18 0', 0, 0);
make_way('1629.0 -277.9 190.0', '17 19 14', 23, 0);
make_way('1317.8 -318.6 190.0', '18 21 23', 0, 0);
make_way('87.4 -1522.2 62.0', '43 0 0', 0, 0);
make_way('1293.0 -554.7 190.0', '19 22 0', 0, 0);
make_way('1670.4 -535.4 62.0', '23 32 21', 0, 0);
make_way('1555.7 -437.5 62.0', '22 24 56', 0, 0);
make_way('1380.5 -428.9 62.0', '23 25 0', 0, 0);
make_way('1032.9 -439.3 62.0', '24 26 0', 0, 0);
make_way('1021.2 -928.0 -2.0', '27 33 25', 0, 0);
make_way('852.4 -1085.0 -22.0', '28 34 61', 26, 4);
make_way('831.8 -1085.0 -258.0', '29 0 0', 0, 0);
make_way('512.6 -1091.4 -258.0', '28 33 0', 0, 2);
make_way('1379.5 -1096.0 14.0', '33 31 0', 0, 0);
make_way('1699.0 -1086.0 62.0', '30 32 0', 0, 0);
make_way('1720.4 -824.1 62.0', '31 22 0', 0, 0);
make_way('1158.1 -1096.0 -18.0', '30 34 26', 37, 8);
make_way('1019.9 -1256.6 -2.0', '33 35 27', 0, 0);
make_way('1020.0 -1664.8 62.0', '34 36 0', 0, 0);
make_way('684.6 -1661.8 62.0', '35 37 0', 0, 0);
make_way('505.0 -1663.7 62.0', '52 61 42', 36, 0);
make_way('275.2 -1087.1 134.0', '59 41 0', 0, 4);
make_way('422.0 -1086.3 150.0', '38 40 0', 0, 0);
make_way('682.5 -1090.1 89.9', '27 39 34', 26, 0);
make_way('195.9 -961.6 150.0', '38 37 59', 0, 2);
make_way('246.5 -1653.8 62.0', '37 43 61', 60, 0);
make_way('87.1 -1612.3 62.0', '42 44 60', 0, 0);
make_way('64.8 -1782.9 62.0', '60 43 45', 55, 0);
make_way('66.4 -1997.2 110.0', '44 46 0', 0, 0);
make_way('261.6 -1998.1 142.0', '45 47 0', 0, 0);
make_way('446.0 -1999.7 190.0', '46 48 0', 0, 0);
make_way('452.0 -1681.0 254.0', '47 49 42', 61, 0);
make_way('450.4 -1576.2 278.0', '48 50 0', 0, 0);
make_way('446.6 -1364.6 278.0', '49 51 0', 0, 0);
make_way('456.1 -1092.5 278.0', '50 1 0', 0, 0);
make_way('512.0 -1901.4 62.0', '37 53 55', 0, 0);
make_way('506.6 -2037.3 62.0', '52 54 0', 0, 0);
make_way('354.8 -2040.3 62.0', '53 55 61', 0, 0);
make_way('216.7 -1863.2 78.0', '44 54 52', 0, 0);
make_way('1613.3 -426.0 62.0', '14 18 0', 0, 4096);
make_way('1648.0 -701.8 136.9', '32 22 0', 0, 0);
make_way('1648.0 -911.3 128.4', '31 32 0', 0, 0);
make_way('194.1 -1234.7 134.0', '38 41 0', 0, 0);
make_way('150.9 -1784.8 70.0', '44 43 55', 41, 8);
make_way('323.0 -1785.1 78.0', '42 54 37', 41, 8);
};

7
fbxa/maps.cfg Normal file
View file

@ -0,0 +1,7 @@
samelevel 6
alias map1 "changelevel dm1"
alias map2 "changelevel dm2"
alias map3 "changelevel dm3"
alias map4 "changelevel dm4"
alias map5 "changelevel dm5"
alias map6 "changelevel dm6"

466
fbxa/readme.html Normal file
View file

@ -0,0 +1,466 @@
<HTML>
<HEAD>
<TITLE>FrikBot Readme</TITLE>
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function Display(section)
{
if (section == "intro")
intro.style.visibility = "visible";
else
intro.style.visibility = "hidden";
if (section == "install")
install.style.visibility = "visible";
else
install.style.visibility = "hidden";
if (section == "play")
play.style.visibility = "visible";
else
play.style.visibility = "hidden";
if (section == "waypoints")
waypoints.style.visibility = "visible";
else
waypoints.style.visibility = "hidden";
if (section == "mods")
mods.style.visibility = "visible";
else
mods.style.visibility = "hidden";
if (section == "author")
me.style.visibility = "visible";
else
me.style.visibility = "hidden";
if (section == "web")
web.style.visibility = "visible";
else
web.style.visibility = "hidden";
if (section == "wayed")
wayed.style.visibility = "visible";
else
wayed.style.visibility = "hidden";
if (section == "probs")
probs.style.visibility = "visible";
else
probs.style.visibility = "hidden";
}
//-->
</SCRIPT>
</HEAD>
<BODY BGCOLOR=#000000 TEXT=#FFFFFF>
<div id="ex4" style="position: absolute; top: 0px; left: 90px; font-size: 75pt; font-style: bold; font-family: Impact; color: #606000">X</div>
<div id="ex3" style="position: absolute; top: 30px; left: 35px; font-size: 35pt; font-style: bold; font-family: Impact; color: #A0A0A0">FRIKBOT</div>
<TABLE WIDTH=100% HEIGHT=100%>
<TR><TD ALIGN=CENTER VALIGN=TOP WIDTH=200>
<BR><BR><BR><BR><BR><BR>
<FORM>
<input type="button" value="INTRODUCTION" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('intro');">
<p>
<input type="button" value="INSTALLATION" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('install');">
<p>
<input type="button" value="PLAYING" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('play');">
<p>
<input type="button" value="ADD-ON WAYPOINTS" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('waypoints');">
<p>
<input type="button" value="MODS" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('mods');">
<p>
<input type="button" value="WAYPOINTING" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('wayed');">
<p>
<input type="button" value="COMMON PORBLEMS" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('probs');">
<P>
<input type="button" value="AUTHOR" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('author');">
<p>
<input type="button" value="WEB" style="border-width: 1; border-color: #FFFFFF; font-family: Arial; font-size: 8pt; width: 150px; color: #FFFFFF; background-color: #000000" onClick="Display('web');">
</FORM>
</TD><TD>
<div ID="intro" style="visibility: visible; position: absolute; top: 30px;">
<TABLE style="font-family: Arial; font-size: 8pt;">
<TR><TD>Title:</TD><TD>FrikBot X</TD></TR>
<TR><TD>Filename:</TD><TD>fbxa.zip</TD></TR>
<TR><TD>Version:</TD><TD>0.10.1</TD></TR>
<TR><TD>Date:</TD><TD>7-27-2001</TD></TR>
<TR><TD>Author:</TD><TD>Ryan "Frika C" Smith</TD></TR>
<TR><TD>Email:</TD><TD><A HREF="mailto:frika-c@earthling.net">frika-c@earthling.net</TD></TR>
<TR><TH COLSPAN=2>Credits</TH></TR>
<TR><TD>Horn, scar3crow, Electro, Akuma, ze0</TD><TD>Play testing and suggestions.</TD></TR>
<TR><TD>Quest, Plumb, SSJ4-Death</TD><TD>Editor testing.</TD></TR>
<TR><TD>Koolio</TD><TD>Mod testing</TD></TR>
<TR><TD>Raymond Martineau, Quest, Akuma</TD><TD>For supporting the "FrikBot community"</TD></TR>
<TR><TD>Alan Kivlin, Requiem</TD><TD>Code snippets</TD></TR>
</TABLE>
<P><H1>Introduction</H1>
<P>
This is the final version of FrikBot. I mean it this time. Really. Stop looking at me like that. This is it. For those that know what this bot is all about, skip the next paragraph and save yourself some time. If not, read on:
<P>
FrikBot is a unique bot for Quake. Maybe you're familiar with Reaper Bots, or the fabulous Frogbot and Omicron bots. These are wonderful opponents to deathmatch against, but for mod authors (the people that make those nice mods you and I play, such as Capture the Flag, etc) they're not so great. Why? I hear you ask - well the bot code heavily intrudes into the rest of the QuakeC (the language you use to make mods for Quake). Because of this, you either need to build the mod onto the bot's source base, or spend a few good months finding each part that links into the mod and carefully replicate it on the new mod. In addition Reaper is not legal to modify and Omicron's source is heavily obfuscated. FrikBot has introduced about 2 years ago as an alternative. It's the first Quake bot (aside from TutorBot) to be heavily geared to mod authors and customization. With the advent of this version, it's also a hoot to play too.
<P>This release (nicknamed FrikBotX or simply FBX) represents several months of slow off and on work and is the 10th (or 12th - I'm not sure) and final release of the bot. FrikBot X, much like the infamous Frogbot relies heavily on hand-built waypoints for excellence in combat. It can however run without them, and it will attempt to create it's own waypoints as it plays. This is not recommended however.
<P>To get started playing FrikBot, read the installation instructions which can be found by clicking the "Installation" button at left (on DHTML capable browsers), or scrolling down (on "normal" browsers).
<P><P><P><!-- I never close P tags. pbbt -->
</div>
<div ID="install" style="visibility: hidden; position: absolute; top: 30px;">
<H1>Installation</H1>
<P><FONT COLOR="red">Please note FrikBot requires the registered version of Quake.</FONT>
<P>These installation instructions assume you're using some version of Windows. If you're lucky enough to be using a better operating system (read: *nux, BeOS, whatever) you likely don't need me to tell you how to install a Quake mod. You probably also enjoy doing binary-long division during long car rides. If you're using a Macintosh, simply unpack this archive, move it to your Quake folder and click-drag it onto your MacQuake executable. (A Mac user once told me these instructions. If this isn't accurate let me know.)
<P>
Unzip this file with your favorite zip utility. This is usually Winzip on most systems. After it's unzipped, create a new folder in Quake with the name "FrikBot". Move all files and folders from the archive to this new folder. In the folder you'll see two .bat batch files. These have been set up to run FBX with the standard command line. Run.bat will execute Quake.exe and GLRun.bat will run GLQuake.exe. The latter is a special 3D-Accelerated version of Quake. If you have 3D hardware, I recommend you <A HREF="ftp://ftp.cdrom.com/pub/idgames/idstuff/unsup/glq1114.exe">download this</A> and read the provided instructions.
<P>
<FONT COLOR="red">FrikBot requires Quake version 1.08 or higher.</FONT> This is available as a free update from id software. If you use GLQuake or WinQuake or any recent custom engine to run FrikBot, don't worry, you're safe. If however you're still using the Quake.exe that came off your old Quake CD, you can download the update by clicking <A HREF="ftp://ftp.idsoftware.com/idstuff/quake/quake108.zip">here</A>.
<P>
To begin playing a match against the bot, double click the batch file of your choice. Choose the Multiplayer option from the main menu. Choose "New Game" from the Multiplayer menu. Select either IPX or TCP/IP from the next menu. Select a map, fraglimit (if desired), timelimit (if desired) and choose "Begin Game". To start fighting some FrikBots, continue reading in the "Playing" section of this readme.
<P><P><P>
</div>
<div ID="play" style="visibility: hidden; position: absolute; top: 30px;">
<h1>Playing FrikBot X</h1>
<P>Okay, you know how to install and begin a multiplayer game of FBX (and if you don't, please read <A HREF="javascript: Display('install');">the last section</A>). <FONT COLOR="red">It is recommended you play one of the 6 id deathmatch maps.</FONT>. These are dm1, dm2, dm3, dm4, dm5 and dm6. If you did not start the game with one of these maps, bring down the console with the ~ key, and type "map dm6" and press enter (minus the quotes).
<P>To add a bot, bring down the console (again with the ~ key) and type "impulse 100" (without the quotes) and press enter. A new bot combatant will enter the game. The bot you have added will probably be a skill 1 (Normal skill) bot. If the bot is too difficult for you, type "impulse 102" in the console and press enter. This will disconnect the bot from the game. Then type "skill 0" to set the game on Easy skill in the console, then "impulse 100" to add an easy level bot. When you're ready for a greater challenge try skill 2 (Hard) and skill 3 (Nightmare) bots.
<P>
<h2>Teamplay</h2>
<P>FrikBot can also be used in a team match game. The bots can be teammates or your opposition. To get started here, change the map then type "teamplay 1" in the console. Set your desired skill level then use "impulse 100" to add a bot that will be one of your team members. Use "impulse 101" in the console to add enemy team members.
<P><h2>Cooperative</h2>
<P>You can also have FBX as a 'friend' as you battle through Quake's single player game. To do this, bring down the console, type "deathmatch 0" and press enter. Type "coop 1" then press enter. Next, use the map command to go to the episode selection - in the console type "map start". Add your bot companions as above (with "impulse 100"), then head off starting the game as you normally would. <FONT COLOR="red">It is recommended you download waypoints for the id single player maps before playing coop</FONT>.
<P><h2>Online</h2>
<P>FrikBotX can also be played with the popular internet version of Quake called "QuakeWorld". To do this you need to run the QuakeWorld sever executable "QWSV.exe". If you don't have this, it can be downloaded from <A HREF="http://www.quakeworld.net/">QuakeWorld's official website</A>. After starting it, type in "gamedir frikbot" then press enter. Then change the map by using the map command, eg. "map dm6". To adjust the skill level of bots, you must use the QW console and type "localinfo skill x" where x is the desired skill level. Connect with QWCL to your server and add bots with "impulse 100" and remove them with "impulse 102".
<P>Due to lazy coding the QW version of frikbot does not support teamplay at this time. In addition, the bots will appear to move very choppily, this is due to QW's restricting all non client entities to a very low frames per second. This cannot be avoided without engine modifications.
<P>When playing QWSV and QWCL on the same machine under Windows 9x it's important to point out that QWSV will not receive enough priority, which will cause unneeded lag. There's an excellent tool to solve this called priority.exe. You can find it at the ever helpful <A HREF="http://www.inside3d.com/qip/">Quake Info Pool</A>.
<P><H2>Advanced Options</h2>
<P>FrikBotX has two options to make you're lives a bit easier. In normal quake you can set them by using the saved1 cvar. The first option is to make the bots return between map changes in DM as they do in Coop. To activate the option type "saved1 1" in the console. After the next map load, bots will return between matches. The next option allows you to disable the bot chatter (it gets on some people's nerves). To do this type "saved 2" in the console. If you wish to both options enabled, add them together and type "saved 3". In QuakeWorld, use localinfo b_options on the server instead of saved1. Good luck.
<P><H2>Map cycling</H2>
<P>Included in the FrikBotX progs.dat is an Omicron-style map cycler. To use it, you must create a maps.cfg file, an example one has been included with this archive. Unlike Omicron, FrikBot's map cycler can deal with an unlimited number of maps. The cfg file must should contain lines in the following format:
<blockquote>alias map1 "changelevel mapname"</blockquote>
<P>Samelevel controls how the map cycler behaves. If it is 0, quake will behave as normal. Samelevel 1 will force FBX to stay on the samelevel, just as normal Quake. However, any setting above this will cause FBX to randomly select a map from the specified number of maps. The number you set it to should be the number of aliases you provided in your maps.cfg. If you'd rather not let it select maps randomly (which can result in repeated maps), setting samelevel to a the negative of the number of maps you have will make an infinite looping rotation. If you have six map aliases, setting samelevel -6 will cycle through them successively.
<P><H2>Botcam</H2>
<P>Using "impulse 103" in the console will allow you to cycle to the view of every bot and player on the server. This view is very interesting, and if you have the time and patience you can record demos from the bot's perspective. Unlike 0.09, FrikBot X will seem much choppier from the bot's perspective. Sorry.
<P><P><P>
</div>
<div ID="waypoints" style="visibility: hidden; position: absolute; top: 30px;">
<h1>Using Add-on waypoints</h1>
<P>The benefits of using add on .way files cannot be stressed enough. Waypoint give the ability to navigate levels perfectly, they make the bot a fierce deathmatch and coop opponent and they actually decrease the load FBX has on the game by a significant amount.
<P>FrikBot Waypoints can be made using the in game editor or may be supplied to you by friends or in future add-on packs. The best source for additional waypoints is the <A HREF="http://www.botepidemic.com/fwd/">FrikBot Waypoint Depot</A>, an excellent website created by Akuma and maintained by Quest.
<P>Waypoints usually come as a single .way file, however some maps are very large or intricate and require multiple files. The additional files share the same name, but have the extensions wa1, wa2, wa3, etc.
<P>To use these waypoints you place the .way (and all additional files, if they exist) in your quake\frikbot\maps folder. If installed correctly, when you go to play that map in Quake you will see in the console "Execing maps/mapnam.way". <FONT COLOR="red">Please note: FBX already contains waypoints for DM1, DM2, DM3, DM4, DM5 and DM6.</FONT> These internal waypoints are improved versions of those found on FrikBot Waypoint Depot. Do not install the waypoints for the id deathmatch maps unless they have been updated over the internal points.
<P>Occasionally you may run across FrikBot waypoints in two other formats. One is map_mapname.qc. <FONT COLOR="red">Please note that although Frogbot uses a similar convention, Frogbot waypoints are not compatible.</FONT><!-- Idiot red again -->
<P>To install these you will need the source code to the mod and a QuakeC compiler (May I recommend my own compiler, <A HREF="http://www.inside3d.com/frikbot/">FrikQCC</A>). To do this, open up the progs.src file from the mod with a text editor. Just after the defs.qc line, add the name of the .qc file you've received. Next, open bot.qc or bot_qw.qc (depending if this is intended for QuakeWorld or Normal Quake) and scroll down past the large installation comment until you find the function bot_map_load. Add a line that reads something like this:
<P>
if (mapname == "mymap")
<BLOCKQUOTE>map_mymap();</BLOCKQUOTE>
<P>
Where the keyword mymap is a placeholder for the name of the map the waypoints are intended for. Under extremely rare circumstances, you'll encounter a third type of waypoints. These come as an .ent file patch. <FONT COLOR="red">Please note that although Omicron uses a similar convention, Omicron bot waypoints are NOT compatible.</FONT><!-- Idiot red again --> To install these you will need the map's BSP file and the compile tool QBSP. QBSP can be found on <A HREF="ftp://ftp://ftp.idsoftware.com/idstuff/source/">id software's FTP</A> in the file q1tools_gpl.zip. You will need to run QBSP with the command line
<P>
qbsp -onlyents mymap
<P>Where mymap is the name of the BSP file you wish to apply waypoints to. <FONT COLOR="red">Please note: The raw dump of the BSP ents option in the in-game editor is not a complete .ent file. It must be appended to the existing ents first. Read the waypoint editing portion of this readme for complete instructions.</FONT>
<P>If you were confused by the above, don't worry. Stick with .way files and you'll be alright. BSP and QC waypoints are intended to get around QuakeWorld's many, many limitations, if you don't use the QuakeWorld FrikBot then you should never need to use those features.
</div>
<div ID="mods" style="visibility: hidden; position: absolute; top: 30px;">
<H1>Information for Mod Authors</H1>
<P>FrikBot is designed for you. With great certainty I can tell you that after plugging FrikBot into your mod the bot will use all the rules you have changed, will fire your custom weapons and will behave like a client in every way discernable. However, he will probably behave like an incredibly stupid client, but that isn't the point.
<P>If you already have FrikBot installed in your mod skip down to the next section, otherwise, keep reading. All the code to install FrikBot has been included with this archive. The files you need are bot.qc, bot_way.qc, bot_ai.qc, bot_fight.qc, bot_ed.qc, bot_misc.qc, bot_phys.qc and bot_move.qc. If you intend to use the QuakeWorld version of FrikBot, substitute everything I say about bot.qc with "bot_qw.qc", okay? To begin installing FrikBot into your mod, copy these files into your mod's source folder. To be neat I recommend creating a subdirectory called "frikbot" within your source folder and placing the files in there. This is not required though.
<P>Next thing you should do is open up bot.qc, scroll down past the license and read the instructions carefully. There's a few functions to comment out in defs.qc, you accomplish these by placing two forward slashes in front of the line - "//". Once you've followed the installation instructions, you can compile and play the mod now with FrikBots. Most likely you'll need to modify code in bot_ai.qc to make the bot behave intelligently. The main function in this file is BotAI located at the bottom. From there you can follow up all the calls it makes and understand how the bot thinks. Priority_for_thing is the place to add code if you want the bots to hunt a new item, or add conditions to hunting existing items. I leave the rest to you. Note that you will probably never need to edit bot(_qw).qc (except for when installing qc waypoints), bot_move.qc, bot_way.qc or bot_phys.qc. The last one being highly unlikely as it is more or less a direct port of the engine's physics code, and only needs to be changed when the corresponding code in the engine is changed.
<P>Please also note that the waypoint editor mode can be used to cheat in your mod (Even in multiplayer). If you don't want to allow this, safeguard the entrance by adding a cvar("developer") check to its impulse in BotImpulses in bot.qc.
<P>When making mods I strongly suggest you do a few things. Open up bot_misc.qc and give the bots new names and colors. Please. I'm tired of seeing a ton of mods with my default names and chat messages. Use a little creativity, and use some extended chars to give the bots "fun names".
<P>Next, I'd ask you avoid using the name "Frik" in your mod's title. Even if all you did was combine FrikBot with another mod, don't call it FrikSomemod. A more appropriate title may be Somemod + FrikBot, or something along those lines. This is not a commandment from Heaven, it's just that I like to use Frik for my own work, and it tends to confuse people seeing my name part of some mod that I had very little to do with. Thanks.
<P>In the past it has been common to include my entire FrikBot readme with the mod. Please do not include this massive .html file, that's the last thing we need. Instead, excerpt here and there and patch together some instructions for the bot. You have permission to use everything in the archive any way you want, this includes this readme. Put a little effort into it, for your user's sake.
<P>If you make your mod for QuakeWorld I really recommend you make the mod open source. Not only will it be much more convenient for server operators, but QC is one of the best ways to bet waypoints into the QW version of FrikBot. Again, open source is the way to go.
<P>If you're really feeling nice, you can place a small thank-you to me in your mods credits. Thanks for listening, now get out there and get coding!
</div>
<div ID="probs" style="visibility: hidden; position: absolute; top: 30px;">
<H1>Common Problems</H1>
This portion of the readme will help you if you encounter an error when using FrikBot. This is by far not a comprehensive guide, but it will help you with a number of problems. If you need further help, please see the "Author" section of this readme. Thanks.
<p>
<h2>"Unable to connect a bot, server is full"</h2>
</h2>
<p>
FrikBot requires client slots like real players. If you're playing a single player game (by choosing Single Player from the main menu, or using the 'map' command), you cannot connect bots because the game can't accept connections. It is recommended you use -listen 16 in the command line.
<P>If you're getting this message after installing FrikBot X on a mod even though you have multiplayer set up properly, this probably the result of improper installation. Make sure you read the installation instructions correctly (especially the part about BotInit in worldspawn(), world.qc).
<p>
<h2>"CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"</h2>
</h2>
<p>
This (and other errors like it) are typically the result of placing BotInit(); below InitBodyQue(); or some other call in worldspawn. When I say at the top of the function, I mean it!
<P>
<H2>"D_SurfCacheGuard: failed"</H2>
<P>
This problem can occur when you look directly at a waypoint in editor mode when it is
linked to another waypoint at point blank. To fix this, you will need
run the command line parameter "-surfcachesize 1500" when you run
quake. If you still get the message, increase the value until the error
disappears.
<p>
<h2>"Ed_Alloc: No Free Edicts"</h2>
<p>
This problem can occur with large maps that require a lot of waypoints. Also note that this can also be caused by an entity leak in your mod. id's code has an entity leak with the bubble spawner code in player.qc. The only way to fix this is to recompile the Quake source code after setting
MAX_EDICTS in quakedef.h to 1024 (or higher).
<p>
<h2>"Cbuf_AddText: Overflow"
</h2>
<p>
If you receive this message it means you probably didn't split up the .way file properly. Look for the comments made by the editor instructing you to separate the file into multiple parts.
<p>
<h2>"SZ_GetSpace: Overflow without allowoverflow set"
</h2>
<p>
This is the result of the client network buffer filling up with too much data. If you receive this message it usually means a bot was receiving messages he shouldn't have, and all this data is building up in his outgoing buffer (since the client isn't real, he never collects the data and the game crashes). To fix this find places where you stuffcmd'd or otherwise sent messages to the bot that wouldn't be picked up my function redeclarations.
<p>
<h2>Waypoints are missing and links are screwed up in DarkPlaces
</h2>
<p>
Darkplaces caps server activity in listen servers with the sys_ticrate cvar. This can foul the .way file loading, to get around this set "sys_ticrate 0" in the console before changing map.
</div>
<div ID="me" style="visibility: hidden; position: absolute; top: 30px;">
<H1>Author</H1>
<P>I, Ryan Smith, am deranged psychopath that lives in Massachusetts, USA. I started coding for the TRS-80 Model 3 about 18 years ago, graduated to Commodore 64s, and eventually found my way to Doom and Quake. Quake eventually caught a hold of me. Deathmatch became a sort of digital crack and making mods was absolutely the best thing I had done with my computer. At one point in my early naivety I had stumbled across the Reaper Bot. Little did I know this choppy, poorly playing (and mostly cheating) bot was considered the best bot of the time. I had long assumed that a proper bot couldn't be too difficult.
<P>Anyway, you can reach me at the following address should you have any questions or comments about the bot (feedback much appreciated!) <A HREF="mailto:frika-c@earthling.net">frika-c@earthling.net</A>. I'd also like to hear about any improvements you've found, bugs you've encountered or mods you've made of using the bot. Keep in mind this is often a slow address and it may take a few days for me to receive your mail. <FONT COLOR="red">Also notice I will not reply to any mail asking questions which are answered in this readme or in any material found on my site or at FWD.</FONT> If your message doesn't contain a technical question, you may not expect a reply. My time is very limited, and though I will read your mail and often times act upon it, I may not reply.<P>
All these rules may sound self centered or something, but I have encountered people that think of e-mail as an instant message service and my mailbox has been filled with mail such as "Why haven't you replied? It's been 5 hours!" etc.
</div>
<div ID="web" style="visibility: hidden; position: absolute; top: 30px;">
<H1>FrikBot on the web</H1>
<P>There are a number of websites you may want to visit for latest news. If you have an active internet connection, just click.
<UL>
<LI><A HREF="http://www.inside3d.com/frikbot">FrikaC's projects</A>
<LI><A HREF="http://www.botepidemic.com/frikbot/">FrikBotX home page</A>
<LI><A HREF="http://www.botepidemic.com/fwd/">FrikBot Waypoint Depot</A>
<LI><A HREF="http://www.planetquake.com/5thD/">5thD Team</A>
</UL>
</div>
<div ID="wayed" style="visibility: hidden; position: absolute; top: 30px;">
<H1>Waypoint Editing</H1>
<P>Waypoints are incredibly useful and powerful tools for FrikBot X. To edit the bot's waypoints, you should use FrikBot X's built-in waypoint editor. If you're already familiar with FrikBot Waypoint Studio this should be a fairly easy guide to follow, as the editor in FBX lends much of it's features and design from FBWS.
<P>To begin using the editor, you must first ensure that the file beam.mdl included with this archive is in the mod's progs/ directory. This model file is used to display the links of a waypoint; we'll get to that in a bit. To demonstrate more clearly how waypointing works, load up FBX as you would to fight the bots. Go to the map dm6 (waypoints are included for this map in the progs.dat). Use impulse 104 in the console to start the editor. If everything goes well, you'll now see many tiny white dots ("bubbles") floating mid air. These sprites represent each waypoint on the map. As you near a waypoint, it will change to a very large gold "light ball". This is referred to as the selected waypoint. As each waypoint is selected, it will cast off little red beams that connect to nearby waypoints. These are called 'links'. <FONT COLOR="red">Each link represents that a bot can travel one way from the selected waypoint to the linked waypoint.</FONT> Also, when near a teleporter you may see a link that looks like quake lightning and passes straight through walls. This is called a "telelink" and is really a special flagged link telling the bot to travel through a teleporter in between the two waypoints.
<P>Each waypoint can have a maximum of 4 out bound links. Although this may seem like a severe limitation at first, consider you can place as many waypoints as you need in one spot, and link them all together to get more outbound links. There is no limit to the number of inbound links to a single waypoint.
<P>What follows is a basic break down of the editor's commands. This is not meant as a comprehensive getting started in waypointing tutorial. The subject is actually quite simple, and with a little experimenting you should be waypointing like a pro in no time. <FONT COLOR="red">Be sure to bind the 0 (zero) key to "impulse 10" in order to properly use the editor menus.</FONT>
<H2>Main Menu</H2>
<P>The main menu contains a few basic commands and the ability to switch to any of the other menus in the editor. This is your starting point in the editor.
<UL>
<LI><B>Waypoint Management</B> This command takes you to the waypoint management menu which
contains functions for moving, modifying and handling waypoints.
<LI><B>Link Management</B> This command takes you to the link management menu which contains
options and commands for changing the links between waypoints.
<LI><B>AI Flag Management</B> This command takes you to the first page of the AI flag management menu which contains toggles for all the commonly used AI flags.
<LI><B>Bot Management</B> This command takes you to the bot management menu which allows you to control bots and use them for testing your waypoints.
<LI><B>Waylist Management</B> This command takes you to the waypoint list management menu which provides commands and functions that work on all waypoint data.
<LI><B>Noclip</B> This is a toggle command that gives you exactly the same effect as the single player cheat NOCLIP. It's provided here because it's incredibly useful for getting to hard to reach and/or mid air locations you couldn't effectively modify waypoints in otherwise.
<LI><B>God mode</B> This is a toggle command that gives you exactly the same effect as the single player cheat GOD. It's provided here to allow you to withstand traps and lava that would otherwise kill you, so you can effectively waypoint around them.
<LI><B>Hold Select</B> This toggle will prevent the editor from selecting new waypoints as you pass them, effectively holding onto the selection you had when you turn this toggle on. This is incredibly useful for moving waypoints long distance (where you'd lose the waypoint selection if you pass other waypoints).
<LI><B>Teleport to Way #</B> This command brings up a prompt that allows you to teleport directly to a waypoint by specifying a waypoint number. The waypoint numbers can be read by using Waypoint Management's "show waypoint info" command.
<LI><B>Close Menu</B> This closes the editor menu and returns the game back to normal game play. All waypoints will again vanish and bots will resume normal functioning. Cheats activated in the editor will remain in effect however. Any changes you made to the waypoints will <B>not</B> be lost. Waypoints are only lost when the map changes or when you quit the game. Use the Dump command in Waylist Management to create a permanent record of your waypoints.
</UL>
<H2>Waypoint Management</H2>
<P>Waypoint management is probably the most useful menu in the editor. It contains basic waypoint creation and deletion but also more advanced functions such as make way and link functions.
<UL>
<LI><B>Move Waypoint</B> Incredibly simple, it moves the selected waypoint to your location. This is useful for fine tuning your waypoint sets. Remember, location is really what defines waypoints. Put them in the right location and you can make excellent waypoint sets, put them in the wrong location and the bots will suffer for your mistakes.
<LI><B>Delete Waypoint</B> Deletes the selected waypoint and any links that may exist to it on other waypoints. You'll be prompted to confirm the deletion. Follow the on screen instructions to remove the waypoint.
<LI><B>Make Waypoint</B> This is the original and generic make waypoint function. It creates a new waypoint at your location. The waypoint is essentially blank, no flags and no links. You must set up any links to and from with the Link Management menu.
<LI><B>Make Waypoint + Link</B> The operates as above, the big difference is it also creates a link from the selected waypoint to the new one you've just created. This will save time and allows you to jump off a cliff and not have to noclip back up in order to create the link.
<LI><B>Make Waypoint + Link X2</B> This operates as Make Waypoint + Link but also creates a link from the new waypoint *back* to the selected waypoint. In other words, a link in either direction - both to and from the new and selected waypoints.
<LI><B>Make Waypoint + Telelink</B> This works similar to Make Way + Link, but instead creates a Telelink to the new waypoint - indicating a pass through a teleporter.
<LI><B>Show waypoint info</B> This command spits a few facts about the currently selected waypoint in the console. It's waypoint number, the value of it's AI flags (mostly useless), and which waypoints it links to. It's useful for debugging your waypoint list, or just satisfying your curiosity.
<LI><B>Link Management</B> Jumps you over to the Link Management menu. Because waypointing generally involves a lot of jumping between these two menus a lot, this is provided here for convenience.
<LI><B>AI Flag Management</B> Jumps you over to the AI Flag Management menu, should you ever need to go there.
<LI><B>Main Menu</B> Brings you back to everyone's favorite menu, the Main Menu.
</UL>
<H2>Link Management</H2>
<P>Link Management is the place to deal with individual links. It's commands somewhat echo Waypoint Management's, but on a link scale.
<UL>
<LI><B>Unlink Current Way</B> Removes all links from the selected waypoint. Please Note: There is no "are you sure" on this, it just does it, so be careful.
<LI><B>Create Link</B> Allows you to create a link from the selected waypoint to
another. The editor will prompt you to move to another waypoint and press 1 to
create the link. Note the link you create is one way, from the waypoint you
started at to the waypoint you ended up at.
<LI><B>Create Telelink</B> Just like Create Link but indicates a teleporter in the
middle. Telelinks are indicated by a thin lightning, as opposed to the red beam of regular links.
<LI><B>Delete Link</B> Deletes a link (either Telelink or normal) between two waypoints. You
will be prompted to move from the waypoint that has the link to the waypoint
that receives the link and press 1.
<LI><B>Create Link X2</B> Similar to Create Link, but instead creates two links, both ways between the two waypoints.
<LI><B>Delete Link X2</B> Similar to Delete Link, but deletes links both ways between the two chosen
waypoints.
<LI><B>Make Waypoint</B> Same command as the make waypoint on the Waypoint
Management Menu. Here for convenience.
<LI><B>Waypoint Management</B> This command brings you to the Waypoint Management
menu, I found that when creating a waypoint set, I often flipped between both
menus far too often, and this has been added here for convenience.
<LI><B>AI Flag Management</B> Jumps you over to the AI Flag Management menu, should you ever need to go there.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>AI Flag Management</H2>
<P>Every command on the AI Flag management menus is a toggle that will effect the currently selected waypoint. FrikBot 0.09 supported the door flag AI flag and no more (loading FBX waypoints in 0.09 is perfectly acceptable by the way, new flags will merely be ignored). The new flags have many varied and useful effects on the bots way of thinking and action he takes as he uses the waypoint. The best way to understand how to use a few of these is to study how the bot reacts to them, I will however attempt to explain here as much as I can.
<UL>
<LI><B>Door flag</B> This flag means the waypoint was spawned on top of a door. (Any type of door). If the door is not under the waypoint when the bot attempts to reach this waypoint, the bot will find the trigger for the door and activate it. Clever waypointers have also used this flag to force bots to trigger doors that do not seemingly block their path.
<LI><B>Precision</B>This tells the bot it should attempt to be more precise around this waypoint since there is a potential for falling or screwing up in some way. It will not attempt to dodge or charge an enemy under the influence of precision. The bot will walk slower and will only use movement keys if already pointing in the correct direction.
<LI><B>Surface for Air</B> This flag forces the bot up for air just above this waypoint. He will swim up, catch a breath of fresh air and continue. <FONT COLOR="red">Do not place this on all your water waypoints.</FONT> The bot will go up for air when it senses it's about to drown on it's own. This flag is intended to tell the bot to go up for air before heading down long underwater hallways that it may drown in otherwise.
<LI><B>Blind Mode</B> Blind mode is an incredibly useful AI flag. In short it means the bot must follow the waypoints blindly and avoid making it's own decisions about it's surroundings. The net result of this is the bot will completely ignore lava, will not jump over gaps, will not try to avoid walls it bumps into and will not deviate from following the waypoints for any reason.
<LI><B>Jump</B> This waypoint merely tells the bot to squeeze the jump button upon reaching the waypoint. Another effect of this flag is to make the waypoint 'invisible' to the AI, it will never consider this waypoint as a location of an item or itself, so be careful when using this flag and make sure it's near other, normal waypoints.
<LI><B>Directional</B> This flag will make a waypoint directional rather than 'positional'. The bot will travel in the direction of the waypoint until the direction to the waypoint has changed radically. This is useful where the bot has little control over his direction, such as in a wind tunnel.
<LI><B>Super Jump</B>This flag tells the bot it needs to rocket jump upon reaching this waypoint. This flag also makes the waypoint 'invisible to the AI'. Remember the bot will rocket jump to every outbound link of the waypoint.
<LI><B>Make Waypoint</B> Same command as the make waypoint on the Waypoint
Management Menu. Here for convenience.
<LI><B>AI Flags Pg. 2</B> This command brings you to the second page of the AI Flags menu, where even more flags are kept.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>AI Flag Management Page 2</H2>
<P>These are the less used AI Flags, though they are useful in some ways.
<UL>
<LI><B>Difficult</B> This flag marks the waypoint as being a long a more difficult route. As a result, the bot will place less priority toward this route when choosing it's shortest path. This will also devalue items on the other end lowering their relative priority. As such, this flag is useful for making the bot both no longer favor a certain route and no longer favor certain items.
<LI><B>Wait for Plat</B> This flag instructs the bot to stand at this waypoint and wait for the nearby func_plat to reach it's bottom state before it continues. This is useful if the plat in question can have people 'wedged' underneath it, and the bot's enthusiastic charge to the next waypoint would get him stuck beneath it.
<LI><B>Ride Train</B> This is a complicated flag that affects multiple behaviors and attempts to allow the bots to ride trains. The first behavior is the bot will wait before going to the flagged waypoint until a plat, door or train is underneath the waypoint. Upon reaching the waypoint the bot will not move as long as he's on top of the train, door or plat.
<LI><B>Door flag no open</B> This is intended as a modifier to the classic door flag . When the bot encounters the a waypoint flagged with both door flag and door flag no open, the bot will not attempt to trigger the missing door beneath the waypoint, but instead will wait for the door to return itself. Additionally, when used alone this flag will prevent bots from opening doors as they usually do if one blocks their path.
<LI><B>Ambush</B> This is not an officially supported waypoint. This flag will make the waypoint have a priority value, the bot will hunt the waypoint after getting the better items on the level. Upon reaching it, he will merely wait until he sees someone.
<LI><B>Snipe</B> Operates just like Ambush, however the bots will not charge out and attack upon seeing an enemy. They will sit quietly, firing their weapon.
<LI><B>Trace Test</B> This flag is meant for single player maps that 'mutate' into deathmatch maps with the use of the Not in Deathmatch spawnflag and func_dmonly entity. The waypoints themselves will traceline to all outgoing links during route calculation, and will only follow the link if the trace was clear. This can be used for some other things, such as triggerable func_walls, and (very) slow moving obstacles.
<LI><B>AI Flags Pg. 1</B> This command brings you back to the first page of the AI Flags menu, where even more flags are kept.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>Bot Management Menu</H2>
<P>This menu allows you to control the FrikBots in unique ways to allow you to accurately test how the bot will behave as they pass waypoints.
<UL>
<LI><B>Add a test bot</B> This is the same as the impulse 100 command. It connects a new bot.
<LI><B>Order Test Bot Here</B> Causes the test bot to come to your location. The bot will immediate head toward you, following the waypoints as he goes. This is the important command here.
<LI><B>Remove Test Bot</B> This disconnects the bot (removes him from the server).
This is the same as the impulse command 102. It is recommended that you remove
all bots before dumping waypoints, as they may cause messages to appear that
will ruin the dump info.
<LI><B>Stop Test Bot</B> This causes the bot to stop whatever he is doing and
stand still.
<LI><B>Teleport Bot here</B> Instantly moves the bot to your currently selected waypoint.
This can be useful in testing certain areas of the map, be careful not to be too
close to the selected waypoint, or you may become stuck inside the bot. If you
do, you can use the teleport and/or the noclip command to free yourself.
<LI><B>Teleport to Way #</B> This is the same as the Main Menu's Teleport to Way # command. It is useful when dealing with bots, so it is here for convenience.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>Waylist Management</H2>
<P>This menu allows you to control the FrikBots in unique ways to allow you to accurately test how the bot will behave as they pass waypoints.
<UL>
<LI><B>Delete All Waypoints</B> Deletes all the waypoints on the level so you can start over.
<LI><B>Dump Waypoints</B> This prints to the console all the waypoint data in the below selected format. Please read the bottom of this section for details about what to do with waypoint dump data.
<LI><B>Check for Errors</B> Checks every waypoint for "duds". These are waypoints
with no outbound links. A waypoint should always have at least on outbound link
incase a bot reaches the waypoint, even if it is at a dead end. If a situation
is inescapable, remove the dud waypoint for improved performance with the bot.
<LI><B>Save Waypoints</B> This command is *still* not available in this version.
<LI><B>Dynamic Mode</B> This toggle turns on the dynamic waypointing mod present in non waypointed maps. Dynamic mode is terrible and can ruin waypoint sets. I suggest you do not use it for any reason.
<LI><B>Dynamic Link</B> This command turns on only the part of Dynamic mode associated with linking together waypoints. This can be time saving as the way in which the dynamic link functionality works is quite predictable.
<LI><B>WAY output</B> This command selects .way file output for the Dump command. Way files are recommended for distribution.
<LI><B>QuakeC output</B> This command selects .qc file output for the Dump command. QuakeC files are useful for running FrikBot in QW, and are an ideal way to package waypoints within mods.
<LI><B>BSP ents output</B> This command selects .ent-like output for the Dump command, as FrikBot chooses not to assume the starting condition of every other map entity it ONLY prints the waypoint entities. You must add the ent data it provides onto the map's entity list. Details for doing so can be found in the next section.
<LI><B>Main Menu</B> Brings you back to the main menu.
</UL>
<H2>Using Waypoint Dumps</H2>
<P>In order to use the waypoint editor effectively, you must save your waypoints. The way you do this is to make sure you ran quake with the command line option -condebug. Once done tweaking or creating the waypoints, use the Waylist's Dump command to print out all the data on the console. Quit the game. In your frikbot directory you'll find the file qconsole.log. Scroll through it until you find the comments flagging the top and bottom of the dump, take all the data and do the appropriate action:
<UL>
<LI>If it's .way file data, place it all in a .way file in your maps folder. Observer the comments to split the file if it is extremely long
<LI>If this is .qc file data, place the entire dump into a .qc file then include this in the progs.src about your bot.qc line. Open bot.qc and add the line map_mapname(); into bot_map_load. See the Add-on waypoints section for further details.
<LI>If this is BSP data, you'll need to obtain the original .ent data for the level. An excellent tool for this job is Maddes' bspentex, you can find it on <A HREF="http://www.inside3d.com/qip/">his site</A>. Another great tool is QuArK, you can open the BSP in quark, find the entity list (in plane text, scroll to the bottom and paste the entity patch data FrikBot dumped.
</UL>
<P>If you have any further questions on this topic, email me. See the Author section for details.
<H2>Shortcuts</H2>
<P>Shortcuts are a rapid but advanced way to use the editor. To use them, you need to know the formula: It's menu # times 16 plus the menu option. The menus were covered in this file in numerical order. The main menu is the first menu, 1 * 16, toggle noclip mode is menu option 6, so 16 + 6 is 22. Now that you have the code number for the command, you use it as such:
<blockquote>bind n "saved2 22; impulse 104"</blockquote>
<P>This will allow you to toggle noclip mode in the editor from any menu by merely pressing the N key. This is merely a time saving tool, if you don't understand how to use it, don't worry.
<P><P><P>
</div>
</TR></TD>
</BODY>
</HTML>

26
fbxa/upgrade.txt Normal file
View file

@ -0,0 +1,26 @@
======================================
FrikBot version 10
======================================
This file contains details on updating old FrikBot based mods to the current version. Only versions 0.09, 0.09a and 0.09b are covered by this document. To upgrade an even older version, please see the documentation provided with version 0.09.
-------------------------------------------------
If you are upgrading a mod based on FrikBot 0.09:
-------------------------------------------------
Backup up your Frikbot 0.09 files if you modified them!
Replace all 0.09 files with the 10 files.
Add bot_fight.qc above bot_ai.qc in progs.src
Change the line in ClientDisconnect in client.qc from
if (self.ishuman)
clientSetFree( self.fClientNo ); // FrikBot
to
ClientDisconnected(); // FrikBot