yay, it compiles again. certainly doesn't work yet

This commit is contained in:
Bill Currie 2003-07-24 18:23:13 +00:00
parent e730793581
commit d81bb81732
7 changed files with 268 additions and 259 deletions

View file

@ -236,25 +236,25 @@ removed.
bot_start_topic (4);
}
ent.search_time = time + 5; // never time out
} else if (!fisible(targ))
} else if (![self canSee:targ])
[self lost:targ :FALSE];
} else if (waypoint_mode > WM_LOADED) {
if (vlen (targ.origin - ent.origin) < 128) {
[self lost:targ :TRUE];
}
}
} else if (targ.classname == "func_button") {
} else if (targ.ent.classname == "func_button") {
// buttons are lost of their frame changes
if (targ.frame) {
if (targ.ent.frame) {
[self lost:targ :TRUE];
if (ent.enemy == targ)
if (ent.enemy == targ.ent)
ent.enemy = NIL;
// if (target[0])
// bot_get_path (target[0], TRUE);
}
} else if ((targ.movetype == MOVETYPE_NONE) && (targ.solid == SOLID_TRIGGER)) {
} else if ((targ.ent.movetype == MOVETYPE_NONE) && (targ.ent.solid == SOLID_TRIGGER)) {
// trigger_multiple style triggers are lost if their thinktime changes
if (targ.nextthink >= time) {
if (targ.ent.nextthink >= time) {
[self lost:targ :TRUE];
// if (target[0])
// bot_get_path (target[0], TRUE);
@ -262,14 +262,14 @@ removed.
}
// lose any target way above the bot's head
// FIXME: if the bot can fly in your mod..
if ((targ.origin_z - ent.origin_z) > 64) {
dist = targ.origin - ent.origin;
if ((targ.ent.origin_z - ent.origin_z) > 64) {
dist = targ.ent.origin - ent.origin;
dist_z = 0;
if (vlen (dist) < 32)
if (ent.flags & FL_ONGROUND)
if (![self recognize_plat:FALSE])
[self lost:targ :FALSE];
} else if (targ.classname == "train") {
} else if (targ.ent.classname == "train") {
if ([self recognize_plat:FALSE])
[self lost:targ :TRUE];
}
@ -305,7 +305,7 @@ based b_aiflags.
// was on a door when spawned
// if there is nothing there now
if (![ent.@this recognize_plat:FALSE]) {
newt = FindThing (ent, "door"); // this is likely the door responsible (crossfingers)
newt = [self findThing: "door"]; // this is likely the door responsible (crossfingers)
if (b_aiflags & AI_DOOR_NO_OPEN) {
if (newt.nextthink)
@ -376,7 +376,7 @@ based b_aiflags.
}
}
if (b_aiflags & AI_PLAT_BOTTOM) {
newt = FindThing (ent, "plat");
newt = [self findThing:"plat"];
if (newt.state != 1) {
v = ent.origin - realorigin (newt);
v_z = 0;
@ -432,7 +432,7 @@ confused
if ([self target_onstack:last_way])
return; // old waypoint still being hunted
jj = FindRoute (last_way);
jj = [self findRoute:last_way];
if (!jj) {
// this is an ugly hack
/*XXX
@ -456,7 +456,7 @@ confused
if (last_way) {
if ([last_way isLinkedTo:jj] == 2) {
// waypoints are telelinked
tele = FindThing (ent, "trigger_teleport").@this; // this is probbly the teleport responsible
tele = [self findThing:"trigger_teleport"].@this; // this is probbly the teleport responsible
[self target_add:tele];
}
traceline (last_way.origin, jj.origin, FALSE, ent); // check for blockage
@ -520,8 +520,8 @@ the bot finds things it wants to kill/grab.
// This is the most executed function in the bot. Careful what you do here.
if ((thing.flags & FL_ITEM) && thing.model && thing.search_time < time) {
// ugly hack
if (thing._last != ent)
thisp = 20;
//XXX if (thing._last != ent)
//XXX thisp = 20;
if (thing.classname == "item_artifact_super_damage")
thisp = 65;
else if (thing.classname == "item_artifact_invulnerability")
@ -568,26 +568,27 @@ the bot finds things it wants to kill/grab.
thisp = 2;
else if (coop) {
thisp = 100;
if (thing.target1.classname == "player")
if (!thing.target1.ishuman)
return 0;
//XXX if (thing.targets[0].classname == "player")
//XXX if (!thing.targets[0].ishuman)
//XXX return 0;
} else if (teamplay && thing.team == ent.team) {
thisp = 100;
if (thing.target1.classname == "player")
return 0;
//XXX if (thing.targets[0].classname == "player")
//XXX 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;
//XXX if (thing.b_aiflags & AI_SNIPER)
//XXX thisp = 30;
//XXX else if (thing.b_aiflags & AI_AMBUSH)
//XXX thisp = 30;
}
if (pointcontents (thing.origin) < -3)
return 0;
if (thisp) {
/*XXX
if (thing.current_way) {
// check to see if it's unreachable
if (thing.current_way.items == -1)
@ -595,6 +596,7 @@ the bot finds things it wants to kill/grab.
else
thisp += (13000 - thing.current_way.items) * 0.05;
}
*/
}
return thisp;
}
@ -614,7 +616,7 @@ the bot finds things it wants to kill/grab.
thatp = [self priority_for_thing:foe];
if (thatp)
if (!scope)
if (!sisible (foe))
if (!sisible (ent, foe))
thatp = 0;
if (thatp > bestp) {
bestp = thatp;
@ -628,7 +630,7 @@ the bot finds things it wants to kill/grab.
if (![self target_onstack:best]) {
[self target_add:best];
if (scope) {
bot_get_path (best, FALSE);
[self get_path:best :FALSE];
b_aiflags |= AI_WAIT;
}
}
@ -743,7 +745,7 @@ frame, the ai_time limits it's actual updating
ent.button2 = 0;
keys = 0;
b_aiflags = 0;
ClearMyRoute ();
[WayPoint clearMyRoute:self];
targets[0] = targets[1] = targets[2] = targets[3] = ent.enemy = NIL;
last_way = NIL;
return;
@ -787,7 +789,7 @@ frame, the ai_time limits it's actual updating
if (route_failed) {
[self roam];
route_failed = 0;
} else if (!begin_route()) {
} else if (![self begin_route]) {
[self look_for_crap:FALSE];
}
keys = 0;

View file

@ -184,7 +184,7 @@ weapon_range =
if (flen < foedist) {
tsz = [self size_player:foe];
if (tsz < foesz) {
if (fisible (foe)) {
if ([self canSee:foe]) {
ent.enemy = foe;
foedist = flen;
foesz = tsz;
@ -209,12 +209,14 @@ weapon_range =
if (flen < foedist) {
tsz = [self size_player:foe];
if (tsz < foesz) {
/*XXX
if (fov(foe) || foe.b_sound > time || self.b_skill == 3) {
if (fisible (foe)) {
if ([self canSee:foe]) {
ent.enemy = foe;
foedist = vlen (foe.origin - ent.origin);
}
}
*/
}
}
}
@ -313,7 +315,7 @@ attacking an enemy.
} else if (!ent.enemy.takedamage) {
ent.enemy = NIL;
return;
} else if (!fisible (ent.enemy)) {
} else if (![self canSee:ent.enemy]) {
ent.enemy = NIL;
return;
}

View file

@ -82,43 +82,8 @@ bot_data_t [32] bot_data = {
{"Mercury", 10, 5},
};
/*
BotName
Sets bot's name and colors
*/
string (integer r)
BotName =
{
self.b_num = r;
if (r < 0 || r >= 32)
return NIL;
self.b_pants = bot_data[r].pants;
self.b_shirt = bot_data[r].shirt;
return bot_data[r].name;
};
string ()
PickARandomName =
{
local integer y, test;
local string h;
local entity t;
y = TRUE;
while (y) {
test = (integer) (32 * random ());
h = BotName (test);
t = find (NIL, netname, h);
if (t == NIL)
y = FALSE;
}
return h;
};
// I didn't like the old code so this is very stripped down
entity b_originator;
float b_topic;
Bot b_originator;
integer b_topic;
/* FBX Topics
b_originator == self
@ -137,18 +102,50 @@ b_originator == self
11 - lag
b_originator == targ
*/
void (float tpic)
bot_start_topic =
@implementation Bot (Misc)
/*
BotName
Sets bot's name and colors
*/
-(string)name:(integer)r
{
b_num = r;
if (r < 0 || r >= 32)
return NIL;
b_pants = bot_data[r].pants;
b_shirt = bot_data[r].shirt;
return bot_data[r].name;
}
-(string)randomName
{
local integer y, test;
local string h;
local entity t;
y = TRUE;
while (y) {
test = (integer) (32 * random ());
h = [self name:test];
t = find (NIL, netname, h);
if (t == NIL)
y = FALSE;
}
return h;
}
// I didn't like the old code so this is very stripped down
-(void)start_topic:(integer)topic
{
if (random() < 0.2) {
b_topic = tpic;
b_topic = topic;
b_originator = self;
} else
b_topic = 0;
};
}
void ()
bot_chat =
-(void)chat
{
local float r;
@ -157,11 +154,12 @@ bot_chat =
r = ceil (6 * random ());
if (self.b_chattime > time) {
if (self.b_skill < 2)
self.keys = self.button0 = self.button2 = 0;
if (b_chattime > time) {
if (b_skill < 2)
keys = 0;
ent.button0 = ent.button2 = 0;
return;
} else if (self.b_chattime) {
} else if (b_chattime) {
switch (b_topic) {
case 1:
if (b_originator == self) {
@ -312,14 +310,14 @@ bot_chat =
case 3:
BotSayInit ();
BotSay2 (": hi ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
bot_start_topic (0);
break;
case 5:
BotSayInit ();
BotSay2 (": hey ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
bot_start_topic (0);
break;
@ -346,13 +344,13 @@ bot_chat =
case 3:
BotSayInit ();
BotSay2 (": good work ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
break;
case 4:
BotSayInit ();
BotSay2 (": nice1 ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
break;
case 5:
@ -377,13 +375,13 @@ bot_chat =
case 3:
BotSayInit();
BotSay2 (": good job ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
break;
case 4:
BotSayInit ();
BotSay2 (": nice backflip ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
break;
case 5:
@ -402,7 +400,7 @@ bot_chat =
case 1:
BotSayInit ();
BotSay2 (": yeah right ");
BotSay2 (b_originator.netname);
BotSay2 (b_originator.ent.netname);
BotSay2 ("\n");
bot_start_topic (0);
break;
@ -431,34 +429,65 @@ bot_chat =
default:
break;
}
self.b_chattime = 0;
b_chattime = 0;
} else if (b_topic) {
if (random () < 0.5) {
if (self == b_originator) {
if (b_topic <= 7)
self.b_chattime = time + 2;
b_chattime = time + 2;
} else {
if (b_topic >= 7)
self.b_chattime = time + 2;
b_chattime = time + 2;
}
}
}
};
}
void ()
KickABot =
+(void)kick
{
local entity ty;
ty = find (NIL, classname, "player");
while (ty != NIL) {
/*XXX
if (!(ty.ishuman)) {
BotDisconnect (ty);
ty.ishuman = TRUE;
ty = NIL;
} else
ty = find (ty, classname, "player");
*/
}
}
/*
fov
is the entity in the bot's field of view
*/
-(integer)fov:(entity)targ
{
local float g;
local vector yawn;
yawn = realorigin (targ);
yawn = (yawn + targ.view_ofs) - (ent.origin + ent.view_ofs);
yawn = normalize (yawn);
yawn = vectoangles (yawn);
g = angcomp (ent.v_angle_x, yawn_x);
if (fabs (g) > 45)
return FALSE;
g = angcomp (ent.v_angle_y, yawn_y);
if (fabs (g) > 60)
return FALSE;
return TRUE;
}
@end
float (float v)
frik_anglemod =
{
return v - floor (v / 360) * 360;
};
/*
@ -473,98 +502,16 @@ realorigin =
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 float thruwater = 0, pc1 = 0, pc2 = 0;
local vector spot1, org;
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;
};
/*
sisible
Now this is getting ridiculous. Simple visible,
used when we need just a simple traceline nothing else
*/
float (entity targ)
float (entity ent, entity targ)
sisible =
{
traceline (self.origin, targ.origin, TRUE, self);
traceline (ent.origin, targ.origin, TRUE, ent);
if (trace_ent == targ)
return TRUE;
else if (trace_fraction == 1)
@ -591,33 +538,3 @@ angcomp =
answer += 360;
return answer;
};
/*
fov
is the entity in the bot's field of view
*/
float (entity targ)
fov =
{
local float g;
local vector yawn;
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;
};
float (float v)
frik_anglemod =
{
return v - floor (v / 360) * 360;
};

View file

@ -331,11 +331,11 @@ I have no idea how well it will work
if (b_aiflags & AI_DANGER) {
way = '0 0 0' - obs_dir;
} else if (wallhug) {
way_x = ent.obs_dir_y * -1;
way_y = ent.obs_dir_x;
way.x = obs_dir.y * -1;
way.y = obs_dir.x;
} else {
way_x = ent.obs_dir_y;
way_y = ent.obs_dir_x * -1;
way.x = obs_dir.y;
way.y = obs_dir.x * -1;
}
way_z = 0;
keys &= 960;
@ -440,7 +440,7 @@ own waypoints.
org = org1 + (ang * dist);
traceline(org, org - '0 0 48', TRUE, ent);
if (trace_fraction != 1) {
SpawnTempWaypoint (org);
[self spawnTempWaypoint:org];
flag = TRUE;
}
}

View file

@ -153,13 +153,14 @@ integer max_clients;
float real_frametime;
float bot_count, b_options, lasttime;
float waypoint_mode, dump_mode;
float waypoints, direct_route, userid;
integer waypoints;
float direct_route, userid;
float sv_friction, sv_gravity;
float sv_accelerate, sv_maxspeed, sv_stopspeed;
entity fixer;
entity route_table;
Bot route_table;
entity b_temp1, b_temp2, b_temp3;
entity way_head;
WayPoint way_head;
float busy_waypoints;
float coop = 0; // hack
@ -299,7 +300,7 @@ BotPreFrame =
bot_start_topic (2);
self.b_frags = self.frags;
}
DynamicWaypoint();
[self.@this dynamicWaypoint];
return FALSE;
};
float ()
@ -409,17 +410,10 @@ UpdateClient =
// FIXME: do teams properly
};
float (integer clientno)
integer (integer clientno)
ClientBitFlag =
{
local float bitflag;
bitflag = 1;
while (clientno > 0) {
bitflag *= 2;
clientno--;
}
return bitflag;
return 1 << clientno;
};
integer ()
@ -438,7 +432,7 @@ ClientNextAvailable =
return -1;
};
void(entity e1, entity e2, float flag) DeveloperLightning = {};
void(WayPoint e1, WayPoint e2, integer flag) DeveloperLightning = {};
// BotConnect and related functions. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
@ -489,9 +483,9 @@ BotConnect =
self.b_clientno = f;
self.colormap = (float) f;
if (whatbot)
self.netname = BotName (whatbot);
@self.netname = [((Bot)@self.@this) name:whatbot];
else
self.netname = PickARandomName ();
@self.netname = [((Bot)@self.@this) randomName];
// players can set skill all weird, so leave these checks in
if (whatskill > 3)
@ -586,7 +580,7 @@ void () BotImpulses =
f = (integer) stof (h);
BotConnect (0, f);
} else if (self.impulse == 102)
KickABot ();
[Bot kick];
else
return;
self.impulse = 0;

View file

@ -46,6 +46,32 @@ this notice in its entirety.
WayPoint way_foot; // Ugh. Do I need a foot for this or not?
@implementation Target
-(vector)realorigin
{
return realorigin (ent);
}
-(integer)canSee:(Target)targ ignoring:(entity)ignore
{
local vector spot1, spot2;
spot1 = ent.origin;
spot2 = [targ realorigin];
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;
}
@end
// Waypoint Linking code ======================================================
@implementation WayPoint
@ -301,7 +327,7 @@ tripping the runaway loop counter
busy_waypoints = busy_waypoints + 1;
e2.keys = TRUE;
e2.items = dist;
e2.ent.think = WaypointThink;
(IMP)e2.ent.think = [self methodFor: @selector (waypointThink)];
e2.ent.nextthink = time;
e2.enemy = self;
}
@ -341,24 +367,6 @@ tripping the runaway loop counter
}
}
-(integer)canSee:(WayPoint)way ignoring:(entity)ignore
{
local vector spot1, spot2;
spot1 = origin;
spot2 = realorigin (way.ent);
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;
}
@end
@ -574,7 +582,7 @@ different bot.
local integer flag;
local Bot bot = (Bot)this.@this;
ClearMyRoute();
[WayPoint clearMyRoute:self];
t = [this.@this findWayPoint:bot.current_way];
// FIXME
@ -637,7 +645,7 @@ Boy it's confusing.
if (last_way != NIL) {
last_way.items = vlen(self.last_way.origin - ent.origin);
last_way.ent.nextthink = time;
last_way.ent.think = WaypointThink;
(IMP)last_way.ent.think = [self methodFor: @selector(waypointThink)];
last_way.keys = TRUE;
busy_waypoints = 1;
return TRUE;
@ -867,4 +875,78 @@ very good all things considered.
t.flags |= AI_DOORFLAG;
}
}
-(integer)canSee:(Target)targ
{
local float thruwater = 0, pc1 = 0, pc2 = 0;
local vector spot1, org;
org = [targ realorigin];
spot1 = ent.origin + ent.view_ofs;
if (targ.ent.solid == SOLID_BSP) {
traceline (spot1, org, TRUE, ent);
if (trace_ent == targ.ent)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
} else {
pc1 = pointcontents (org);
pc2 = pointcontents (spot1);
if (targ.ent.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.ent.mins, TRUE, ent);
// cross the water check
if (trace_inopen)
if (trace_inwater)
if (!thruwater)
return FALSE;
if (trace_ent == targ.ent)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
}
} else {
if (pc2 != pc1) {
traceline (spot1, org + targ.ent.maxs, TRUE, ent);
if (trace_inopen)
if (trace_inwater)
if (!thruwater)
return FALSE;
if (trace_ent == targ.ent)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
}
}
traceline (spot1, org, TRUE, ent);
if (trace_ent == targ.ent)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
traceline (spot1, org + targ.ent.maxs, TRUE, ent);
if (trace_ent == targ.ent)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
traceline (spot1, org + targ.ent.mins, TRUE, ent);
if (trace_ent == targ.ent)
return TRUE;
else if (trace_fraction == 1)
return TRUE;
return FALSE;
}
@end

View file

@ -1,8 +1,16 @@
#include "Entity.h"
@class Bot;
@class WayPoint;
@interface WayPoint: Entity
@interface Target: Entity
{
}
-(vector)realorigin;
-(integer)canSee:(Target)targ ignoring:(entity)ignore;
@end
@interface WayPoint: Target
{
@public
WayPoint [4] targets;
@ -36,11 +44,9 @@
-(void)followLink:(WayPoint)e2 :(integer)b_bit;
-(void)waypointThink;
-(integer)canSee:(WayPoint)way ignoring:(entity)ignore;
@end
@interface Bot: Entity
@interface Bot: Target
{
@public
//model modelindex
@ -76,7 +82,6 @@
WayPoint temp_way, last_way, current_way;
entity [4] targets;
entity avoid;
entity _next, _last;
vector obs_dir;
vector b_dir;
vector dyn_dest;
@ -88,6 +93,16 @@
- (id) initWithEntity: (entity) e;
@end
@interface Bot (Misc)
-(string)name:(integer)r;
-(string)randomName;
-(void)start_topic:(integer)topic;
-(void)chat;
-(integer)fov:(entity)targ;
+(void)kick;
@end
@interface Bot (Physics)
- (void)sendMove;
@end
@ -137,6 +152,8 @@
-(integer)begin_route;
-(void)spawnTempWaypoint:(vector)org;
-(void)dynamicWaypoint;
-(integer)canSee:(Target)targ;
@end
#define FALSE 0
@ -257,16 +274,11 @@
@extern void() CL_KeyMove;
// ai & misc
@extern void() BotAI;
@extern string() PickARandomName;
@extern float(entity targ) fov;
@extern float(float y1, float y2) angcomp;
@extern float(entity targ) sisible;
@extern float(entity targ) fisible;
@extern float(entity ent, entity targ) sisible;
@extern vector(entity ent) realorigin;
@extern void() KickABot;
@extern void() BotImpulses;
@extern string(integer r) BotName;
@extern float(float v) frik_anglemod;
@extern void() bot_chat;
@extern void(float tpic) bot_start_topic;