heretic2-sdk/Toolkit/Programming/GameCode/game/buoy.c
1999-03-18 00:00:00 +00:00

918 lines
27 KiB
C

// ****************************************************************************
// BUOYAH! Navigation System
//
// Heretic II
// Copyright 1998 Raven Software
//
// Mike Gummelt & Josh Weier
// ****************************************************************************
#include "g_local.h"
#include "buoy.h"
#include "vector.h"
#include "random.h"
#include "fx.h"
#include "m_stats.h"
#define SF_JUMP 1//make monster jump
#define SF_ACTIVATE 2//turn something on
#define SF_TURN 4
#define SF_ONEWAY 8//don't back-link
#define SF_BROKEN 64//a bad bad buoy
#define SF_DONT_TRY 128//don't allow monster to head here
#define BUOY_PRINT_INFO_DIST 64
#define MAX_BUOY_BRANCH_CHECKS 1024//only check 1024 branches before giving up
#define MAX_PROGRESSIVE_CHECK_DEPTH 20//only search 20 buoys deep if doing a progressive depth check
static int check_depth;
static int buoy_depth;
static int branch_counter;//short circuit check if 1024
//Returns the buoy's id
int insert_buoy(edict_t *self)
{
buoy_t *buoy = &level.buoy_list[level.active_buoys];
int i;
//Init these values to -1 so we know what's filled
for (i=0;i<MAX_BUOY_BRANCHES;i++)
buoy->nextbuoy[i] = NULL_BUOY;
buoy->jump_target_id = NULL_BUOY;
//Copy all the other important info
buoy->opflags = self->ai_mood_flags;
buoy->modflags = self->spawnflags;
if(buoy->modflags & BUOY_JUMP)
{
buoy->jump_target = self->jumptarget;
buoy->jump_fspeed = self->speed;
buoy->jump_yaw = self->s.angles[YAW];
buoy->jump_uspeed = self->movedir[2];
}
else
{
buoy->jump_fspeed = buoy->jump_yaw = buoy->jump_uspeed = 0;
}
VectorCopy(self->s.origin, buoy->origin);
//Get the contents so that we know if this is a water buoy or not
buoy->contents = gi.pointcontents(buoy->origin);
//are we going to be opening anything?
buoy->pathtarget = self->pathtarget;
//save the connections for easier debugging
buoy->target = self->target;
buoy->jump_target = self->jumptarget;
buoy->targetname = self->targetname;
buoy->wait = self->wait;
buoy->delay = self->delay;
// Gil suggestion: unimplemented
// buoy->failed_depth = 999999999;
//Post incremented on purpose, thank you
buoy->id = level.active_buoys++;
//when done, level.active_buoys will be the total number of buoys,
//level.active_buoys - 1 will be the last valid buoy
return buoy->id;
}
void assign_nextbuoy(edict_t *self, edict_t *ent)
{
buoy_t *buoy = &level.buoy_list[self->count];
int i;
for (i = 0; i < MAX_BUOY_BRANCHES; i++)
{
if (buoy->nextbuoy[i] > NULL_BUOY)
{
if (i==2)
{
gi.dprintf("Buoy %d: Too many connections on buoy %s (%s)\n", self->count, self->targetname, vtos(self->s.origin));
self->ai_mood_flags |= SF_BROKEN;
return;
}
continue;
}
buoy->nextbuoy[i] = ent->count;
return;
}
return;
}
void assign_jumpbuoy(edict_t *self, edict_t *ent)
{//self is supposed to make monsters jump at ent
buoy_t *buoy = &level.buoy_list[self->count];
int i;
for (i = 0; i < MAX_BUOY_BRANCHES; i++)
{
if (buoy->jump_target_id > NULL_BUOY)
{
gi.dprintf("Buoy %s (%s): already has a jump_target(%s), tried to assign another %s!\n", buoy->targetname, vtos(buoy->origin), buoy->jump_target, ent->targetname);
self->ai_mood_flags |= SF_BROKEN;
return;
continue;
}
buoy->jump_target_id = ent->count;
return;
}
return;
}
// ****************************************************************************
// Link the buoys after all entities have been spawned
// ****************************************************************************
void info_buoy_link(edict_t *self)
{
edict_t *ent = NULL;
int i;
if(self->spawnflags & BUOY_ACTIVATE)
{
if(!self->pathtarget)
{
if (BUOY_DEBUG)
{
gi.dprintf("Buoy %s at %s is an ACTIVATE buoy but has no pathtarget!!!\n", self->targetname, vtos(self->s.origin));
self->ai_mood_flags |= SF_BROKEN;
}
self->spawnflags &= ~BUOY_ACTIVATE;
}
}
//Make sure we have a target to link to
if (self->target)
{
ent = NULL;
ent = G_Find(ent, FOFS(targetname), self->target);
if (ent == NULL)
{
if (BUOY_DEBUG)
{
gi.dprintf("info_buoy_link: %s(%s) failed to find target buoy %s\n", self->targetname, vtos(self->s.origin), self->target);
self->ai_mood_flags |= SF_BROKEN;
}
}
else if(ent == self)
{
gi.dprintf("info_buoy_link: %s(%s) target(%s) is self!!!\n", self->targetname, vtos(self->s.origin), self->target);
self->ai_mood_flags |= SF_BROKEN;
}
else
{
if (BUOY_DEBUG)
gi.dprintf("info_buoy_link: linked %s to %s\n", self->targetname, ent->targetname);
//Link this buoy to it's target
assign_nextbuoy(self, ent);
if(BUOY_DEBUG > 1)
{
gi.CreatePersistantEffect(NULL,
FX_M_EFFECTS,//green arrows
CEF_BROADCAST,
self->s.origin,
"bv",
FX_BUOY_PATH,
ent->s.origin);
}
//If it's not one way, then back link it as well
if(!(self->spawnflags&SF_ONEWAY))
assign_nextbuoy(ent, self);
}
}
//Also check for a secondary target
if (self->target2)
{
if ( (self->target) && (!stricmp(self->target2, self->target)) )
{
if (BUOY_DEBUG)
{
gi.dprintf("info_buoy_link2: %s(%s) has target2 same as target %s\n", self->targetname, vtos(self->s.origin), self->target2);
self->ai_mood_flags |= SF_BROKEN;
}
}
else
{
ent = NULL;
ent = G_Find(ent, FOFS(targetname), self->target2);
if (ent == NULL)
{
if (BUOY_DEBUG)
{
gi.dprintf("info_buoy_link2: %s(%s) failed to find target2 buoy %s\n", self->targetname, vtos(self->s.origin), self->target2);
self->ai_mood_flags |= SF_BROKEN;
}
}
else if(ent == self)
{
gi.dprintf("info_buoy_link2: %s(%s) target2(%s) is self!!!\n", self->targetname, vtos(self->s.origin), self->target2);
self->ai_mood_flags |= SF_BROKEN;
}
else
{
//Link this buoy to it's target
assign_nextbuoy(self, ent);
if(BUOY_DEBUG > 1)
{
gi.CreatePersistantEffect(NULL,
FX_M_EFFECTS,//green arrows
CEF_BROADCAST,
self->s.origin,
"bv",
FX_BUOY_PATH,
ent->s.origin);
}
//If it's not one way, then back link it as well
if(!(self->spawnflags&SF_ONEWAY))
assign_nextbuoy(ent, self);
}
}
}
if(self->spawnflags&BUOY_JUMP)
{
if(!self->jumptarget)
{
if (BUOY_DEBUG)
{
gi.dprintf("Buoy %s at %s is a JUMP buoy but has no jumptarget!!!\n", self->targetname, vtos(self->s.origin));
self->ai_mood_flags |= SF_BROKEN;
}
self->spawnflags &= ~BUOY_JUMP;
}
else
{
ent = NULL;
if(ent = G_Find(ent, FOFS(targetname), self->jumptarget))
{
assign_jumpbuoy(self, ent);
if(BUOY_DEBUG>1)
{
gi.CreatePersistantEffect(NULL,
FX_M_EFFECTS,//blue particles
CEF_FLAG8|CEF_BROADCAST,
ent->s.origin,
"bv",
FX_BUOY,
vec3_origin);
}
}
else if(BUOY_DEBUG)
{
gi.dprintf("Buoy %s(%s) could not find jumptarget buoy %s\n", self->targetname, vtos(self->s.origin), self->jumptarget);
self->ai_mood_flags |= SF_BROKEN;
}
}
}
if(self->ai_mood_flags & SF_BROKEN)
{//put an effect on broken buoys?
level.buoy_list[self->count].opflags |= SF_BROKEN;
level.fucked_buoys++;
}
if(self->count == level.active_buoys - 1)
{
for(i = 0; i < level.active_buoys; i++)
{//see if any buoys are loners
if(!level.buoy_list[i].nextbuoy[0]&&
!level.buoy_list[i].nextbuoy[1]&&
!level.buoy_list[i].nextbuoy[2])
{
gi.dprintf("G.D.E. WARNING: buoy %s(%s) has no connections\n", level.buoy_list[i].targetname, vtos(level.buoy_list[i].origin));
}
}
Com_Printf("%d buoys processed by BUOYAH! Navigation System(tm) (%d bad : %d fixed)\n", level.active_buoys, level.fucked_buoys, level.fixed_buoys);
}
if(!BUOY_DEBUG)
{
G_SetToFree(self);
}
else
{//buoys don't need to think
self->think = NULL;
self->nextthink = -1;
}
}
void PrintLocalBuoyInfo(vec3_t org)
{
int i, j;
vec3_t dir;
float dist;
for(i = 0; i<level.active_buoys; i++)
{
if(level.buoy_list[i].print_debounce_time<level.time)
{
VectorSubtract(level.buoy_list[i].origin, org, dir);
dist = VectorLength(dir);
if(dist<BUOY_PRINT_INFO_DIST)
{
gi.dprintf("==============================\n");
gi.dprintf("BUOY %s\n", level.buoy_list[i].targetname);
gi.dprintf("at %s\n", vtos(level.buoy_list[i].origin));
if(level.buoy_list[i].opflags & SF_BROKEN)
gi.dprintf("BROKEN!!!\n");
gi.dprintf("connections: ");
for(j = 0; j < MAX_BUOY_BRANCHES; j++)
{
if(level.buoy_list[i].nextbuoy[j] > NULL_BUOY)
{
if(j!=0)
gi.dprintf(", ");
gi.dprintf("%s", level.buoy_list[level.buoy_list[i].nextbuoy[j]].targetname);
}
}
gi.dprintf("\n");
if(level.buoy_list[i].modflags & BUOY_JUMP)
{
gi.dprintf("\nJUMP\n");
gi.dprintf("jump_target buoy: %s\n", level.buoy_list[i].jump_target);
gi.dprintf("angle: %4.2f\n", level.buoy_list[i].jump_yaw);
gi.dprintf("height: %4.2f\n", level.buoy_list[i].jump_uspeed);
gi.dprintf("speed: %4.2f\n", level.buoy_list[i].jump_fspeed);
level.buoy_list[i].print_debounce_time = level.time + 1;
}
if(level.buoy_list[i].modflags & BUOY_ACTIVATE)
{
edict_t *found;
gi.dprintf("\nACTIVATE\n");
gi.dprintf("pathtarget: %s\n", level.buoy_list[i].pathtarget);
if(found = G_Find(NULL, FOFS(pathtargetname), level.buoy_list[i].pathtarget))
gi.dprintf("entity to activate: %s\n", found->classname);
else
gi.dprintf("ERROR: no entity found to activate!!!\n");
gi.dprintf("wait: %4.2f\n", level.buoy_list[i].wait);
gi.dprintf("delay: %4.2f\n", level.buoy_list[i].delay);
level.buoy_list[i].print_debounce_time = level.time + 1;
}
if(level.buoy_list[i].modflags & SF_ONEWAY)
{
gi.dprintf("\nONE WAY\n");
level.buoy_list[i].print_debounce_time = level.time + 1;
}
gi.dprintf("==============================\n");
}
}
}
}
/*QUAKED info_buoy(0.6 0 0.8) (-24 -24 -24) (24 24 24) JUMP ACTIVATE TURN ONEWAY
BUOYAH! Navigation System
Mike Gummelt & Josh Weier
THOU SHALT NOT COMMIT GRIEVOUS DESIGN ERRORS FOR THEY ARE AN ABOMINATION BEFORE THE EYES OF THE PROGRAMMER AND THE PROGRAMMER'S WORD IS INFALLIBLE!
THE BUOY TEN COMMANDMENTS as Handed Down to M0535 on Mount Sine-AI:
0) Thou shalt not give a buoy more than one targetname
1) Thou shalt have a buoy target up to two OTHER buoies
therefore:
2) Thou shalt connect each buoy to up to three other buoies (only three lines can come off a buoy)
3) Thou shalt knowest that direction of connection does not matter unless you are trying to make a one-way buoy (see ONEWAY) below
4) Thou shalt place thine buoy in an area that a monster can fit into
5) Thou shalt place thine buoies such that each buoy can "see" each buoy it's conencted to (have a clear line of sight)
6) Thou shalt knowest that buoies do not need to be placed throughout wide open areas, monsters can get around fine there.
7) Thou shalt not place buoies in the ground or walls or any other world object
8) Thou shalt not give any two buoies the same targetname, and each buoy should have a targetname, even if it is not targeted (this is for debug purposes)
9) Thou shalt not have any other AI system above the BUOYAH! Navigation System.
Keep in mind that when choosing a buoy, monsters need to be able to find a buoy withing 1024 map units of them. So make sure buoies are placed so that wherever they can get, they are within 1024 of a buoy.
"showbuoys" - At the console, setting "showbuoys" to 1 and restarting the map will allow you to see each buoy. The flags you will see are each monster's indicator of where they are trying to go at the minute. In addition you will get buoy debug messages on the console in this mode, telling you if a monster has a hard time getting to a buoy (it times out) or if, for some reason, a connection cannot be made between two buoies.
"cheating_monsters" - At the console, set this to 1 to allow monsters to teleport to a buoy it's having a hard time getting to.
Lots of info and useful instructions here:
JUMP - Will make monster jump ("angle" is the direction to go in (default = 0), "speed" if the forward velocity in this dir (default = 400), "height" is the height of the jump (default = 400))
ACTIVATE - Will allow monster to activate doors, triggers, plats, etc. NOTE: the activated object's "pathtargetname" must match the buoy's "pathtarget" field.
(not implemented) TURN - Will make monster turn to buoy's angles
ONEWAY - This buoy will not allow buoys it's targeting to send monsters backwards along the path. Basically, does not back-link, paths from it to buoys it's targeting become one-way.
"jumptarget" - used with JUMP - this buoy will only make monsters jump at the buoy whose "targetname" is the same as "jumpbuoy"- without this, the buoy WILL NOT MAKE MONSTERS JUMP!
"wait" - used with ACTIVATE- will make the buoy wait this many seconds before allowing a monster to activate the targeted ent again
"delay" - used with ACTIVATE - will make the monster stand and wait this long after activating the target ent (so it stands and waits on a lift or for the door to open)
NOTE: AVOID GRIEVOUS DESIGN ERRORS!
DEBUG INFO:
For "showbuoys" = 2, in addition to the arrow paths indicating target->targetname direction, particles appear on buoys, here's what each color indicates:
red - the buoy a monster has determined is your closest buoy
green - the buoy a monster has determined is it's next buoy to get to
cyan - this buoy has a jump flag
blue - this buoy is the jumptarget of a jump flagged buoy
magenta - this buoy has an activate flag
white - this buoy has an oneway flag
Standing over a buoy that has a spawnflag of BUOY_JUMP or BUOY_ACTIVATE and hitting "action" will print it's info to the console is in "showbuoys" mode
*/
void SP_info_buoy(edict_t *self)
{
int i;
if(!level.active_buoys)
{//1st buoy, initialize a couple arrays
for(i = 0; i < MAX_CLIENTS; i++)
{
level.player_buoy[i] = NULL_BUOY; //stores current bestbuoy for a player enemy (if any)
level.player_last_buoy[i] = NULL_BUOY; //when player_buoy is invalid, saves it here so monsters can check it first instead of having to do a whole search
}
}
if(self->spawnflags&BUOY_JUMP)
{
if (!self->speed)
self->speed = 400;
if (!st.height)
st.height = 400;
if (self->s.angles[YAW] == 0)
self->s.angles[YAW] = 360;
self->movedir[2] = st.height;
if(BUOY_DEBUG>1)
{
gi.CreatePersistantEffect(NULL,
FX_M_EFFECTS,//cyan particles
CEF_FLAG7|CEF_BROADCAST,
self->s.origin,
"bv",
FX_BUOY,
vec3_origin);
}
}
if(BUOY_DEBUG>1)
{
if(self->spawnflags&BUOY_ACTIVATE)
{
gi.CreatePersistantEffect(NULL,
FX_M_EFFECTS,
CEF_DONT_LINK|CEF_BROADCAST,//magenta particles
self->s.origin,
"bv",
FX_BUOY,
vec3_origin);
}
if(self->spawnflags&SF_ONEWAY)
{
vec3_t extra_shit;
VectorSet(extra_shit, 1, 0, 0);//really hacky way to send a diff effect
gi.CreatePersistantEffect(NULL,
FX_M_EFFECTS,
CEF_BROADCAST,
self->s.origin,
"bv",
FX_BUOY,
extra_shit);//white particles
}
}
if(!self->targetname)
{
if (BUOY_DEBUG)
gi.dprintf("Buoy with no targetname at %s!!!\n", vtos(self->s.origin));
}
//make sure it's not in the ground at all
if(gi.pointcontents(self->s.origin)&CONTENTS_SOLID)
{
gi.dprintf("Buoy %s(%s) in ground!!!\n", self->targetname, vtos(self->s.origin));
self->ai_mood_flags |= SF_BROKEN;
}
else
{//check down against world- does not check against entities! Does not check up against cieling (why would they put one close to a cieling???)
vec3_t top, bottom, mins, maxs;
trace_t trace;
VectorCopy(self->s.origin, top);
VectorCopy(self->s.origin, bottom);
bottom[2] += 23;
bottom[2] -= 24;
VectorSet(mins, -24, -24, 0);
VectorSet(maxs, 24, 24, 1);
gi.trace(top, mins, maxs, bottom, self, MASK_SOLID,&trace);
if(trace.allsolid || trace.startsolid)//bouy in solid, can't be fixed
{
gi.dprintf("Buoy %s(%s) in solid(%s)!!!\n", self->targetname, vtos(self->s.origin), trace.ent->classname);
self->ai_mood_flags |= SF_BROKEN;
}
else if(trace.fraction<1.0)
{//buoy is in the ground
VectorCopy(trace.endpos, bottom);
bottom[2] += 24;
gi.dprintf("Buoy %s was in ground(%s), moved it from %s to %s...\n", self->targetname, trace.ent->classname, vtos(self->s.origin), vtos(bottom));
VectorCopy(bottom, self->s.origin);
self->ai_mood_flags |= SF_BROKEN;
level.fixed_buoys++;
}
}
self->movetype=PHYSICSTYPE_NONE;
self->solid = SOLID_NOT;
self->clipmask = 0;
self->classname = "info_buoy";
if (BUOY_DEBUG)
{
self->s.renderfx = RF_GLOW;
self->s.angles[2] = 180;
self->s.modelindex = gi.modelindex("models/objects/lights/bug/tris.fm");
}
self->think = info_buoy_link;
self->nextthink = level.time + FRAMETIME;
if ((self->count = insert_buoy(self)) == NULL_BUOY)
gi.dprintf("ERROR! SP_info_buoy : Failed to insert buoy into map list!\n");
gi.linkentity(self);
}
qboolean check_buoy_path(edict_t *self, int lb_id, int sb_id, int fb_id)
{
buoy_t *check_buoy = NULL;
buoy_t *last_buoy, *start_buoy, *final_buoy;
int i, branch;
qboolean branch_checked[MAX_BUOY_BRANCHES];
int num_branches_checked = 0;
int infinite_loop_short_circuit = 0;
if(branch_counter++ >= MAX_BUOY_BRANCH_CHECKS)
{
#ifdef _DEVEL
if(BUOY_DEBUG)
gi.dprintf("Passed MAX_BUOY_DEPTH %d!!!\n", branch_counter);
else
#endif
return false;//going too deep into buoys
}
if(check_depth < buoy_depth+1)
{
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("Hit max buoy check_depth (%d/%d) & failed\n", check_depth, buoy_depth);
#endif
return false;//going too deep into buoys
}
for(i = 0; i < MAX_BUOY_BRANCHES; i++)
branch_checked[i] = false;
last_buoy = &level.buoy_list[lb_id];
start_buoy = &level.buoy_list[sb_id];
final_buoy = &level.buoy_list[fb_id];
start_buoy->opflags |= SF_DONT_TRY;
buoy_depth++;//add a level to buoy search depth
for (i = 0; num_branches_checked < MAX_BUOY_BRANCHES; i++)
{
do
{
if(infinite_loop_short_circuit++ > 1000)
assert(0);
branch = irand(0, MAX_BUOY_BRANCHES - 1);
}
while(branch_checked[branch] == true);
branch_checked[branch] = true;
num_branches_checked++;
if (start_buoy->nextbuoy[branch] == NULL_BUOY)
{
#ifdef _DEVEL
if (BUOY_DEBUG>2)
gi.dprintf("No #%d Branch off of %s\n", branch + 1, start_buoy->targetname);
#endif
if(num_branches_checked == MAX_BUOY_BRANCHES)
{
start_buoy->opflags &= ~SF_DONT_TRY;
buoy_depth--;//take last level off
return false;
}
else
continue;//check others
}
check_buoy = &level.buoy_list[start_buoy->nextbuoy[branch]];
#ifdef _DEVEL
if(BUOY_DEBUG>2)
gi.dprintf("Checking buoy %s off of %s\n", check_buoy->targetname, start_buoy->targetname);
#endif
if (check_buoy == final_buoy)
{
#ifdef _DEVEL
if (BUOY_DEBUG)
gi.dprintf("buoy found...\n");
#endif
start_buoy->opflags &= ~SF_DONT_TRY;
return true;
}
if ((check_buoy->opflags&SF_DONT_TRY))
// Gil suggestion: unimplemented
// || check_buoy->failed_depth <= buoy_depth)
{
#ifdef _DEVEL
if(BUOY_DEBUG>2)
gi.dprintf("Buoy %s marked as don't try, skipping\n", check_buoy->targetname);
#endif
continue;
}
if (check_buoy == last_buoy)
{
continue;
}
if(check_buoy_path(self, start_buoy->id, check_buoy->id, final_buoy->id))
{
start_buoy->opflags &= ~SF_DONT_TRY;
return true;
}
// Gil suggestion: unimplemented
/*
//this buoy cannot reach goal at this depth, so don't try it again this search unless come across it at a lower depth
if(BUOY_DEBUG>2)
gi.dprintf("Buoy %s marked as failed at depth %d, will skip for rest of checks of lower depth\n", check_buoy->targetname, buoy_depth);
//NOTE: EXPERIMENTAL - remove if can't prove it works!
check_buoy->failed_depth = buoy_depth;
*/
}
start_buoy->opflags &= ~SF_DONT_TRY;
buoy_depth--;//take last level off
return false;
}
buoy_t *find_next_buoy_2(edict_t *self, int sb_id, int fb_id)
{
buoy_t *check_buoy = NULL, *save_buoy = NULL, *start_buoy, *final_buoy;
int i, branch;
qboolean branch_checked[MAX_BUOY_BRANCHES];
int num_branches_checked = 0;
int infinite_loop_short_circuit = 0;
for(i = 0; i < MAX_BUOY_BRANCHES; i++)
branch_checked[i] = false;
start_buoy = &level.buoy_list[sb_id];
final_buoy = &level.buoy_list[fb_id];
buoy_depth = 1;
start_buoy->opflags |= SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
{//don't loop back around, the save_buoy last branch check will be a shorter path
#ifdef _DEVEL
if(BUOY_DEBUG)
gi.dprintf("Last buoy was %s...\n", level.buoy_list[self->lastbuoy].targetname);
#endif
level.buoy_list[self->lastbuoy].opflags |= SF_DONT_TRY;
}
//fixme: make my last_buoy also a dont_try?
for (i = 0; num_branches_checked < MAX_BUOY_BRANCHES; i++)
{
do
{
if(infinite_loop_short_circuit++ > 1000)
assert(0);
branch = irand(0, MAX_BUOY_BRANCHES - 1);
}
while(branch_checked[branch] == true);
branch_checked[branch] = true;
num_branches_checked++;
if (start_buoy->nextbuoy[branch] == NULL_BUOY)
{
if(num_branches_checked == MAX_BUOY_BRANCHES)
{
start_buoy->opflags &= ~SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
level.buoy_list[self->lastbuoy].opflags &= ~SF_DONT_TRY;
return NULL;//hasn't found one before here, and last branch was false and next is null, failed!
}
else
continue;//check others
}
if(self->lastbuoy == start_buoy->nextbuoy[branch])
{
#ifdef _DEVEL
if (BUOY_DEBUG>2)
gi.dprintf("Saving %s's last (previous) buoy %s for last path check\n", self->classname, level.buoy_list[self->lastbuoy].targetname);
#endif
save_buoy = &level.buoy_list[self->lastbuoy];
continue;
}
check_buoy = &level.buoy_list[start_buoy->nextbuoy[branch]];
#ifdef _DEVEL
if(BUOY_DEBUG>2)
gi.dprintf("(Start) Checking buoy %s off of %s\n", check_buoy->targetname, start_buoy->targetname);
#endif
if (check_buoy == final_buoy)
{
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("%s FOUND CONNECTION FROM %s TO %s IN 1 STEP, %d BRANCHES CHECKED\n", self->classname, start_buoy->targetname, final_buoy->targetname, branch_counter);
#endif
start_buoy->opflags &= ~SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
level.buoy_list[self->lastbuoy].opflags &= ~SF_DONT_TRY;
return check_buoy;
}
if ((check_buoy->opflags & SF_DONT_TRY))
// Gil suggestion: unimplemented
// || check_buoy->failed_depth <= buoy_depth)
{
#ifdef _DEVEL
if(BUOY_DEBUG>2)
gi.dprintf("Buoy %s marked as don't try, skipping\n", check_buoy->targetname);
#endif
continue;
}
if(check_buoy_path(self, start_buoy->id, check_buoy->id, final_buoy->id))
{
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("%s FOUND CONNECTION FROM %s TO %s IN %d STEPS, %d BRANCHES CHECKED\n", self->classname, start_buoy->targetname, final_buoy->targetname, buoy_depth, branch_counter);
#endif
start_buoy->opflags &= ~SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
level.buoy_list[self->lastbuoy].opflags &= ~SF_DONT_TRY;
if(check_buoy->id == self->lastbuoy)
assert(0);//should NEVER happen!!!
return check_buoy;
}
else
continue;
}
if(save_buoy)
{
save_buoy->opflags &= ~SF_DONT_TRY;
check_buoy = save_buoy;
#ifdef _DEVEL
if (BUOY_DEBUG>2)
gi.dprintf("Now checking saved buoy %s for last path check\n", check_buoy->targetname);
#endif
if (check_buoy == final_buoy)
{
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("%s FOUND CONNECTION FROM %s TO %s IN 1 STEP, %d BRANCHES CHECKED\n", self->classname, start_buoy->targetname, final_buoy->targetname, branch_counter);
#endif
start_buoy->opflags &= ~SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
level.buoy_list[self->lastbuoy].opflags &= ~SF_DONT_TRY;
return check_buoy;
}
if (!(check_buoy->opflags & SF_DONT_TRY))
// Gil suggestion: unimplemented
// && check_buoy->failed_depth > buoy_depth)
{
if(check_buoy_path(self, start_buoy->id, check_buoy->id, final_buoy->id))
{
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("%s FOUND CONNECTION FROM %s TO %s IN %d STEPS, %d BRANCHES CHECKED\n", self->classname, start_buoy->targetname, final_buoy->targetname, buoy_depth, branch_counter);
#endif
start_buoy->opflags &= ~SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
level.buoy_list[self->lastbuoy].opflags &= ~SF_DONT_TRY;
return check_buoy;
}
}
}
start_buoy->opflags &= ~SF_DONT_TRY;
if(self->lastbuoy > NULL_BUOY)
level.buoy_list[self->lastbuoy].opflags &= ~SF_DONT_TRY;
return NULL;
}
buoy_t *find_next_buoy(edict_t *self, int sb_id, int fb_id)
{
buoy_t *found = NULL, *start_buoy, *final_buoy;
int i;
if(!self->mintel)
{
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("Can't use buoys- no mintel!...\n");
#endif
return NULL;
}
start_buoy = &level.buoy_list[sb_id];
final_buoy = &level.buoy_list[fb_id];
#ifdef _DEVEL
if(BUOY_DEBUG)
gi.dprintf("********************************************************\n %s Beginning search from %s to %s\n********************************************************\n", self->classname, start_buoy->targetname, final_buoy->targetname);
#endif
branch_counter = 0;
if(irand(0, 1))
{//progressive_depth- finds shortest
#ifdef _DEVEL
if(BUOY_DEBUG)
gi.dprintf("%s starting progressive depth buoy path check\n", self->classname);
#endif
check_depth = 0;
while(check_depth < self->mintel && check_depth < MAX_PROGRESSIVE_CHECK_DEPTH)
{//only search to max of 20 buoys deep if doing a progressive depth check
check_depth++;
found = find_next_buoy_2(self, start_buoy->id, final_buoy->id);
if(found)
{
check_depth = 0;
return found;
}
}
}
else
{//start at max depth- finds first
#ifdef _DEVEL
if(BUOY_DEBUG)
gi.dprintf("%s starting max depth(%d) buoy path check\n", self->classname, self->mintel);
#endif
check_depth = self->mintel;
found = find_next_buoy_2(self, start_buoy->id, final_buoy->id);
if(found)
{
check_depth = 0;
return found;
}
}
for(i = 0; i <= level.active_buoys; i++)
{
level.buoy_list[i].opflags &= ~SF_DONT_TRY;
// Gil suggestion: unimplemented
// level.buoy_list[i].failed_depth = 999999999;
}
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
if(check_depth == self->mintel)
gi.dprintf("Hit my max buoy depth (%d) & failed\n", check_depth);
#endif
#ifdef _DEVEL
if(BUOY_DEBUG_LITE||BUOY_DEBUG)
gi.dprintf("Path from buoy %s(%s) to buoy %s(%s) not possible at depth of %d!\n", start_buoy->targetname, vtos(start_buoy->origin), final_buoy->targetname, vtos(final_buoy->origin), check_depth);
#endif
check_depth = 0;
return NULL;
}