2003-07-29 20:28:18 +00:00
|
|
|
/***********************************************
|
|
|
|
* *
|
|
|
|
* FrikBot Waypoints *
|
|
|
|
* "The better than roaming 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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "libfrikbot.h"
|
|
|
|
#include "Array.h"
|
|
|
|
#include "List.h"
|
|
|
|
|
2003-07-31 16:57:01 +00:00
|
|
|
Array waypoint_array;
|
2003-07-29 20:28:18 +00:00
|
|
|
@static entity waypoint_thinker;
|
|
|
|
@static List waypoint_queue;
|
|
|
|
|
|
|
|
@implementation Waypoint
|
|
|
|
|
|
|
|
-(id)init
|
|
|
|
{
|
|
|
|
if (!waypoint_array) {
|
|
|
|
waypoint_array = [[Array alloc] init];
|
2003-08-22 06:35:19 +00:00
|
|
|
[waypoint_array addItem:NIL];
|
2003-07-29 20:28:18 +00:00
|
|
|
waypoint_queue = [[List alloc] init];
|
|
|
|
waypoint_thinker = spawn ();
|
2003-07-30 16:45:28 +00:00
|
|
|
waypoint_thinker.classname = "waypoint_thinker";
|
2003-07-29 20:28:18 +00:00
|
|
|
}
|
2003-08-01 05:25:07 +00:00
|
|
|
/*XXX
|
|
|
|
ent.classname = "temp_waypoint";
|
|
|
|
ent.solid = SOLID_TRIGGER;
|
|
|
|
ent.movetype = MOVETYPE_NOCLIP;
|
|
|
|
setsize(ent, VEC_HULL_MIN, VEC_HULL_MAX); // FIXME: convert these to numerical
|
|
|
|
*/
|
2003-07-29 20:28:18 +00:00
|
|
|
return [super init];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(id)initAt:(vector)org
|
|
|
|
{
|
|
|
|
[self init];
|
|
|
|
[waypoint_array addItem: self];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(id)initFromEntity:(entity)ent
|
|
|
|
{
|
|
|
|
[self initAt:ent.origin];
|
|
|
|
}
|
2003-08-01 05:25:07 +00:00
|
|
|
|
|
|
|
-(void)setOrigin:(vector)org
|
|
|
|
{
|
|
|
|
origin = org;
|
|
|
|
}
|
2003-08-18 06:30:05 +00:00
|
|
|
|
|
|
|
-(vector)origin
|
|
|
|
{
|
|
|
|
return origin;
|
|
|
|
}
|
2003-07-29 20:28:18 +00:00
|
|
|
/*
|
|
|
|
entity (vector org)
|
|
|
|
make_waypoint =
|
|
|
|
{
|
|
|
|
local entity point;
|
|
|
|
|
|
|
|
point = spawn ();
|
|
|
|
point.classname = "waypoint";
|
|
|
|
|
|
|
|
point.search_time = time; // don't double back for me;
|
|
|
|
point.solid = SOLID_TRIGGER;
|
|
|
|
point.movetype = MOVETYPE_NONE;
|
|
|
|
point.items = -1;
|
|
|
|
setorigin (point, org);
|
|
|
|
|
|
|
|
setsize (point, VEC_HULL_MIN, VEC_HULL_MAX);
|
|
|
|
|
|
|
|
if (waypoint_mode > WM_LOADED) // editor modes
|
|
|
|
setmodel (point, "progs/s_bubble.spr");
|
|
|
|
return point;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
-(integer)isLinkedTo:(Waypoint)way
|
|
|
|
{
|
|
|
|
local integer i;
|
|
|
|
|
|
|
|
if (way == self || !way || !self)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
2003-08-22 06:35:19 +00:00
|
|
|
if (links[i] == way) {
|
2003-07-29 20:28:18 +00:00
|
|
|
if (flags & (AI_TELELINK_1 << i))
|
|
|
|
return 2;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(integer)linkWay:(Waypoint)way
|
|
|
|
{
|
|
|
|
local integer i;
|
|
|
|
|
|
|
|
if (self == way || !self || !way)
|
|
|
|
return 0;
|
|
|
|
else if ([self isLinkedTo:way])
|
|
|
|
return 0; // already linked!!!
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
2003-08-22 06:35:19 +00:00
|
|
|
if (!links[i]) {
|
|
|
|
links[i] = way;
|
2003-07-29 20:28:18 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Link Ways part 2, used only for teleporters
|
|
|
|
-(integer)teleLinkWay:(Waypoint)way
|
|
|
|
{
|
|
|
|
local integer i;
|
|
|
|
|
|
|
|
if (self == way || !self || !way)
|
|
|
|
return 0;
|
|
|
|
else if ([self isLinkedTo:way])
|
|
|
|
return 0; // already linked!!!
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
2003-08-22 06:35:19 +00:00
|
|
|
if (!links[i]) {
|
|
|
|
links[i] = way;
|
2003-07-29 20:28:18 +00:00
|
|
|
flags |= AI_TELELINK_1 << i;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)unlinkWay:(Waypoint)way
|
|
|
|
{
|
|
|
|
local integer i;
|
|
|
|
|
|
|
|
if (self == way || !self || !way)
|
|
|
|
return;
|
|
|
|
else if (![self isLinkedTo:way])
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
2003-08-22 06:35:19 +00:00
|
|
|
if (links[i] == way) {
|
2003-07-29 20:28:18 +00:00
|
|
|
flags &= ~(AI_TELELINK_1 << i);
|
2003-08-22 06:35:19 +00:00
|
|
|
links[i] = NIL;
|
2003-07-29 20:28:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
|
|
|
|
Waypoint Loading from file
|
|
|
|
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
*/
|
|
|
|
|
|
|
|
+(void)clearAll
|
|
|
|
{
|
2003-08-18 06:30:05 +00:00
|
|
|
if (waypoint_array)
|
|
|
|
[waypoint_array free];
|
2003-07-29 20:28:18 +00:00
|
|
|
waypoint_array = [[Array alloc] init];
|
|
|
|
}
|
|
|
|
|
|
|
|
+(Waypoint)waypointForNum:(integer)num
|
|
|
|
{
|
|
|
|
return [waypoint_array getItemAt:num];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)fix
|
|
|
|
{
|
2003-08-22 06:35:19 +00:00
|
|
|
links[0] = [Waypoint waypointForNum:b_pants];
|
|
|
|
links[1] = [Waypoint waypointForNum:b_skill];
|
|
|
|
links[2] = [Waypoint waypointForNum:b_shirt];
|
|
|
|
links[3] = [Waypoint waypointForNum:b_frags];
|
2003-07-29 20:28:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+(void) fixWaypoints
|
|
|
|
{
|
|
|
|
[waypoint_array makeObjectsPerformSelector:@selector(fix)];
|
|
|
|
}
|
|
|
|
|
2003-08-22 06:56:31 +00:00
|
|
|
+(Waypoint)find:(vector)org radius:(float)rad
|
|
|
|
{
|
|
|
|
local vector dif;
|
|
|
|
local float dist;
|
|
|
|
local integer i, count;
|
|
|
|
local Waypoint way = NIL, w;
|
|
|
|
|
|
|
|
rad = rad * rad; // radius squared
|
|
|
|
|
|
|
|
count = [waypoint_array count];
|
|
|
|
for (i = 1; i < count; i++) {
|
|
|
|
w = [waypoint_array getItemAt:i];
|
|
|
|
dif = w.origin - org;
|
|
|
|
dist = dif * dif; // dist squared, really
|
|
|
|
if (dist < rad) {
|
|
|
|
w.chain = way;
|
|
|
|
way = w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return way;
|
|
|
|
}
|
|
|
|
|
2003-07-29 20:28:18 +00:00
|
|
|
/*
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
|
|
|
|
Route & path table management
|
|
|
|
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
*/
|
|
|
|
|
|
|
|
-(void)clearRoute
|
|
|
|
{
|
|
|
|
keys = FALSE;
|
|
|
|
enemy = NIL;
|
|
|
|
items = -1; // not in table
|
|
|
|
}
|
|
|
|
|
|
|
|
+(void)clearRouteTable
|
|
|
|
{
|
|
|
|
// cleans up route table
|
|
|
|
[waypoint_array makeObjectsPerformSelector:@selector (clearRoute)];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)clearRouteForBot:(Bot)bot
|
|
|
|
{
|
|
|
|
local integer flag;
|
2003-07-31 16:57:01 +00:00
|
|
|
flag = bot.b_clientflag;
|
2003-07-29 20:28:18 +00:00
|
|
|
b_sound &= ~flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
+(void)clearMyRoute:(Bot) bot
|
|
|
|
{
|
|
|
|
[waypoint_array makeObjectsPerformSelector:@selector (clearRoute)
|
|
|
|
withObject:bot];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
|
|
|
|
WaypointThink
|
|
|
|
|
|
|
|
Calculates the routes. We use thinks to avoid
|
|
|
|
tripping the runaway loop counter
|
|
|
|
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
*/
|
|
|
|
|
2003-08-18 16:48:53 +00:00
|
|
|
-(void)followLink:(Waypoint)e2 :(integer)bBit
|
2003-07-29 20:28:18 +00:00
|
|
|
{
|
|
|
|
local float dist;
|
|
|
|
|
2003-08-18 16:48:53 +00:00
|
|
|
if (flags & bBit)
|
2003-07-29 20:28:18 +00:00
|
|
|
dist = items;
|
|
|
|
else
|
|
|
|
dist = vlen(origin - e2.origin) + items;
|
|
|
|
|
|
|
|
// check if this is an RJ link
|
|
|
|
if (e2.flags & AI_SUPER_JUMP) {
|
2003-08-18 16:48:53 +00:00
|
|
|
if (![route_table canRJ])
|
2003-07-29 20:28:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (e2.flags & AI_DIFFICULT)
|
|
|
|
dist = dist + 1000;
|
|
|
|
|
|
|
|
dist = dist + random() * 100; // add a little chaos
|
|
|
|
|
|
|
|
if ((dist < e2.items) || (e2.items == -1)) {
|
|
|
|
if (!e2.keys)
|
|
|
|
busy_waypoints = busy_waypoints + 1;
|
|
|
|
e2.keys = TRUE;
|
|
|
|
e2.items = dist;
|
|
|
|
e2.enemy = self;
|
|
|
|
[e2 queueForThink];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)waypointThink
|
|
|
|
{
|
|
|
|
local integer i;
|
|
|
|
|
|
|
|
if (items == -1)
|
|
|
|
return;
|
|
|
|
// can you say ugly?
|
|
|
|
if (flags & AI_TRACE_TEST) {
|
|
|
|
for (i = 0; i < 4; i++) {
|
2003-08-22 06:35:19 +00:00
|
|
|
if (links[i]) {
|
|
|
|
traceline (origin, links[i].origin, TRUE, /*self*/NIL);
|
2003-07-29 20:28:18 +00:00
|
|
|
if (trace_fraction == 1)
|
2003-08-22 06:35:19 +00:00
|
|
|
[self followLink:links[i] :AI_TELELINK_1 << i];
|
2003-07-29 20:28:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < 4; i++) {
|
2003-08-22 06:35:19 +00:00
|
|
|
if (links[i]) {
|
|
|
|
[self followLink:links[i] :AI_TELELINK_1 << i];
|
2003-07-29 20:28:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
busy_waypoints--;
|
|
|
|
keys = FALSE;
|
|
|
|
|
|
|
|
if (busy_waypoints <= 0) {
|
|
|
|
if (direct_route) {
|
2003-08-18 16:48:53 +00:00
|
|
|
[route_table getPath:route_table.targets[0] :FALSE];
|
2003-07-29 20:28:18 +00:00
|
|
|
direct_route = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (waypoint_thinker.@this = [waypoint_queue removeItemAtHead]) {
|
|
|
|
local id obj = waypoint_thinker.@this;
|
|
|
|
local IMP imp = [obj methodForSelector: @selector (waypointThink)];
|
|
|
|
(IMP)waypoint_thinker.think = imp;
|
|
|
|
waypoint_thinker.nextthink = time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-(id)queueForThink
|
|
|
|
{
|
|
|
|
if (waypoint_thinker.@this) {
|
|
|
|
[waypoint_queue addItemAtTail: self];
|
|
|
|
} else {
|
|
|
|
local IMP imp = [self methodForSelector: @selector (waypointThink)];
|
|
|
|
(IMP)waypoint_thinker.think = imp;
|
|
|
|
waypoint_thinker.nextthink = time;
|
|
|
|
waypoint_thinker.@this = self;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2003-08-22 06:35:19 +00:00
|
|
|
-(integer)priority:(Bot)bot
|
|
|
|
{
|
|
|
|
if (flags & AI_SNIPER)
|
|
|
|
return 30;
|
|
|
|
else if (flags & AI_AMBUSH)
|
|
|
|
return 30;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-07-29 20:28:18 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
|
|
|
|
BSP/QC Waypoint loading
|
|
|
|
|
|
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
*/
|
|
|
|
|
|
|
|
void() waypoint =
|
|
|
|
{
|
|
|
|
local Waypoint way = [[Waypoint alloc] initFromEntity: @self];
|
|
|
|
/*
|
|
|
|
search_time = time;
|
|
|
|
solid = SOLID_TRIGGER;
|
|
|
|
movetype = MOVETYPE_NONE;
|
|
|
|
setorigin(self, origin);
|
|
|
|
|
|
|
|
setsize(self, VEC_HULL_MIN, VEC_HULL_MAX);
|
|
|
|
waypoints = waypoints + 1;
|
|
|
|
if (!way_head) {
|
|
|
|
way_head = self;
|
|
|
|
way_foot = self;
|
|
|
|
} else {
|
|
|
|
way_foot._next = self;
|
|
|
|
_last = way_foot;
|
|
|
|
way_foot = self;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = waypoints;
|
|
|
|
waypoint_mode = WM_LOADED;
|
|
|
|
if (count == 1) {
|
|
|
|
think = FixWaypoints; // wait until all bsp loaded points are spawned
|
|
|
|
nextthink = time;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
|
|
|
void(vector org, vector bit1, integer bit4, integer flargs) make_way =
|
|
|
|
{
|
|
|
|
local Waypoint y = [[Waypoint alloc] initAt:org];
|
|
|
|
//local entity y;
|
|
|
|
//waypoint_mode = WM_LOADED;
|
|
|
|
//y = make_waypoint(org);
|
|
|
|
y.flags = flargs;
|
|
|
|
y.b_pants = (integer)bit1_x;
|
|
|
|
y.b_skill = (integer)bit1_y;
|
|
|
|
y.b_shirt = (integer)bit1_z;
|
|
|
|
y.b_frags = bit4;
|
|
|
|
/*
|
|
|
|
if (y.count == 1) {
|
|
|
|
y.think = FixWaypoints; // wait until all qc loaded points are spawned
|
|
|
|
y.nextthink = time;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
};
|