Name and document pathfinding functions

This commit is contained in:
Ryan Dwyer 2023-06-13 21:44:29 +10:00
parent 154153b605
commit 38c478259f
9 changed files with 352 additions and 253 deletions

View File

@ -3305,10 +3305,9 @@ void botTickUnpaused(struct chrdata *chr)
struct waypoint *last = waypointFindClosestToPos(&targetprop->pos, targetprop->rooms);
if (first && last) {
s32 hash = (g_Vars.lvframe60 >> 9) * 128 + chr->chrnum * 8;
waypointSetHashThing(hash, hash);
aibot->numwaystepstotarget = waypointFindRoute(last, first, aibot->waypoints, ARRAYCOUNT(aibot->waypoints));
waypointSetHashThing(0, 0);
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
aibot->numwaystepstotarget = navFindRoute(last, first, aibot->waypoints, ARRAYCOUNT(aibot->waypoints));
navSetSeed(0, 0);
}
}
@ -3317,6 +3316,7 @@ void botTickUnpaused(struct chrdata *chr)
// Iterate both hands and handle shooting
{
u32 stack;
bool firingright = false;
s32 i;

View File

@ -448,11 +448,9 @@ bool botactFindRocketRoute(struct chrdata *chr, struct coord *frompos, struct co
s32 numwaypoints;
if (from && to) {
s32 hash = (g_Vars.lvframe60 >> 9) * 128 + chr->chrnum * 8;
waypointSetHashThing(hash, hash);
numwaypoints = waypointFindRoute(from, to, waypoints, 6);
waypointSetHashThing(0, 0);
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
numwaypoints = navFindRoute(from, to, waypoints, 6);
navSetSeed(0, 0);
if (numwaypoints > 1) {
s32 i = 0;

View File

@ -5515,14 +5515,11 @@ void chrGoPosAdvanceWaypoint(struct chrdata *chr)
chr->act_gopos.curindex++;
} else {
struct waypoint *from = chr->act_gopos.waypoints[chr->act_gopos.curindex];
u32 hash;
chr->act_gopos.curindex = 1;
hash = (g_Vars.lvframe60 >> 9) * 0x80 + chr->chrnum * 8;
waypointSetHashThing(hash, hash);
waypointFindRoute(from, chr->act_gopos.target, chr->act_gopos.waypoints, MAX_CHRWAYPOINTS);
waypointSetHashThing(0, 0);
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
navFindRoute(from, chr->act_gopos.target, chr->act_gopos.waypoints, MAX_CHRWAYPOINTS);
navSetSeed(0, 0);
}
chrGoPosInitExpensive(chr);
@ -6109,11 +6106,9 @@ bool chrGoToRoomPos(struct chrdata *chr, struct coord *pos, s16 *room, u32 gopos
lastwaypoint = waypointFindClosestToPos(pos, room);
if (nextwaypoint && lastwaypoint) {
waypointSetHashThing(
((g_Vars.lvframe60 >> 9) << 7) + chr->chrnum * 8,
((g_Vars.lvframe60 >> 9) << 7) + chr->chrnum * 8);
numwaypoints = waypointFindRoute(nextwaypoint, lastwaypoint, waypoints, MAX_CHRWAYPOINTS);
waypointSetHashThing(0, 0);
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
numwaypoints = navFindRoute(nextwaypoint, lastwaypoint, waypoints, MAX_CHRWAYPOINTS);
navSetSeed(0, 0);
}
if (numwaypoints > 1) {
@ -14636,42 +14631,33 @@ s32 chrFindWaypointWithinPosQuadrant(struct coord *pos, s16 *rooms, f32 angle, u
bool func0f04a4ec(struct chrdata *chr, u8 quadrant)
{
if (quadrant == QUADRANT_2NDWPTOTARGET || quadrant == QUADRANT_20) {
if (quadrant == QUADRANT_TOWARDSTARGET || quadrant == QUADRANT_AWAYFROMTARGET) {
struct prop *prop = chr->prop;
struct prop *target = chrGetTargetProp(chr);
struct waypoint *fromwp = waypointFindClosestToPos(&prop->pos, prop->rooms);
struct waypoint *towp = waypointFindClosestToPos(&target->pos, target->rooms);
struct waypoint *chrwp = waypointFindClosestToPos(&prop->pos, prop->rooms);
struct waypoint *tarwp = waypointFindClosestToPos(&target->pos, target->rooms);
// @dangerous: I'm creating an array overflow here to get a match.
// waypoints should have len 3 but this causes a mismatch due to too
// much stack usage. If compiling using anything other than IDO and -O2
// then this will need to be changed to 3.
struct waypoint *waypoints[3];
s32 numwaypoints;
struct waypoint *waypoints[2];
u32 hash;
if (fromwp && towp) {
if (quadrant == QUADRANT_2NDWPTOTARGET) {
hash = (g_Vars.lvframe60 >> 9) * 128 + chr->chrnum * 8;
waypointSetHashThing(hash, hash);
numwaypoints = waypointFindRoute(fromwp, towp, waypoints, 3);
waypointSetHashThing(0, 0);
if (chrwp && tarwp) {
if (quadrant == QUADRANT_TOWARDSTARGET) {
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
numwaypoints = navFindRoute(chrwp, tarwp, waypoints, 3);
navSetSeed(0, 0);
if (numwaypoints >= 3) {
chr->padpreset1 = waypoints[1]->padnum;
return true;
}
} else {
hash = (g_Vars.lvframe60 >> 9) * 128 + chr->chrnum * 8;
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
chrwp = navChooseRetreatPoint(chrwp, tarwp);
navSetSeed(0, 0);
waypointSetHashThing(hash, hash);
fromwp = func0f1155e0(fromwp, towp);
waypointSetHashThing(0, 0);
if (fromwp) {
chr->padpreset1 = fromwp->padnum;
if (chrwp) {
chr->padpreset1 = chrwp->padnum;
return true;
}
}
@ -14694,7 +14680,7 @@ bool chrSetPadPresetToWaypointWithinTargetQuadrant(struct chrdata *chr, u8 quadr
s32 padnum;
struct prop *prop;
if (quadrant == QUADRANT_2NDWPTOTARGET || quadrant == QUADRANT_20) {
if (quadrant == QUADRANT_TOWARDSTARGET || quadrant == QUADRANT_AWAYFROMTARGET) {
return func0f04a4ec(chr, quadrant);
}
@ -14901,12 +14887,10 @@ bool chrSetPadPresetToPadOnRouteToTarget(struct chrdata *chr)
towp = waypointFindClosestToPos(&target->pos, target->rooms);
if (fromwp && towp) {
u32 hash = (g_Vars.lvframe60 >> 9) * 128 + chr->chrnum * 8;
// Note from/to are swapped here, so the route is from target to chr
waypointSetHashThing(hash, hash);
numwaypoints = waypointFindRoute(towp, fromwp, waypoints, 5);
waypointSetHashThing(0, 0);
navSetSeed(CHRNAVSEED(chr), CHRNAVSEED(chr));
numwaypoints = navFindRoute(towp, fromwp, waypoints, 5);
navSetSeed(0, 0);
if (numwaypoints >= 3) {
for (i = 0; waypoints[i] != NULL; i++) {

View File

@ -12,12 +12,50 @@
#include "data.h"
#include "types.h"
u32 g_WaypointHashes[2] = {0};
/**
* Path Finding
*
* Usage:
* - The caller calls waypointFindClosestToPos() twice to find the from and to waypoints.
* - The caller allocates an array of waypoint pointers (typically 6 elements).
* - The caller calls navFindRoute() with the two waypoints, a pointer to the array and the array length.
* - navFindRoute() writes the waypoint pointers into the array and returns the number of elements written.
*
* If there are more waypoints in the path than the array can fit, only the first <array size> waypoints are written.
* In this case, the caller should re-run the path finding once some of the path has been traversed to get the next
* waypoints. However, there is randomness involved in path finding. To ensure the same path is used, the caller can
* preseed the random number generator for the path finder. It can do this by calling navSetSeed() with a random
* seed before calling navFindRoute(), and it should call navSetSeed() with a zero seed to clear it for the
* next caller.
*
* The algorithm is hierarchical, and uses Dijkstra's algorithm but with a cost of 1 for every segment and breaks ties
* using randomness.
*
* More specifically:
* Waypoints are grouped into clusters called waygroups. A waygroup might be the same thing as a room, but not
* necessarily. The algorithm finds the path at the waygroup level, then a path is found through the waypoints in the
* first group, then the second group, and so on until the results array is full or the destination is reached.
*
* To actually find a path, a step number (cost) is assigned to each node until the destination is discovered. It then
* works backwards from the destination, choosing which node will be the one prior to it by looking for the neighbouring
* node which is one step lower. If there are multiple nodes to choose from then the selected node will be random.
*/
void waypointSetHashThing(s32 hash1, s32 hash2)
#define WPSEGFLAG_OUTWARDSONLY 0x4000 // eg. top of ledge
#define WPSEGFLAG_INWARDSONLY 0x8000 // eg. bottom of ledge
#define IGNORE_NONE 0
#define IGNORE_OUTWARDS WPSEGFLAG_OUTWARDSONLY
#define IGNORE_INWARDS WPSEGFLAG_INWARDSONLY
#define WPSEG_GET_ID(seg) (seg & (0xffff & ~(WPSEGFLAG_OUTWARDSONLY | WPSEGFLAG_INWARDSONLY)))
u32 g_NavSeed[2] = {0};
void navSetSeed(u32 upper, u32 lower)
{
g_WaypointHashes[0] = hash1;
g_WaypointHashes[1] = hash2;
g_NavSeed[0] = upper;
g_NavSeed[1] = lower;
}
/**
@ -198,30 +236,43 @@ struct waypoint *waypointFindClosestToPos(struct coord *pos, s16 *rooms)
return closest;
}
struct waygroup *func0f114810(s32 *groupnums, s32 value, u32 mask)
/**
* Given a group that is known to be on the path, groupnums is a pointer to that
* group's neighbours. At least one of these groups should have the given step
* number.
*
* The function chooses which neighbour will be routed through.
*
* If padrandomroutes is false, the first neighbour with the correct step is used.
* If padrandomroutes is true, the algorithm has a 50% chance of searching for
* another neighbour.
*
* padrandomroutes is always true.
*/
struct waygroup *waygroupChooseNeighbour(s32 *groupnums, s32 step, u32 ignoremask)
{
struct waygroup *groups = g_StageSetup.waygroups;
struct waygroup *best = NULL;
while (*groupnums >= 0) {
if ((*groupnums & mask) == 0) {
struct waygroup *group = &groups[*groupnums & 0x3fff];
if ((*groupnums & ignoremask) == 0) {
struct waygroup *group = &groups[WPSEG_GET_ID(*groupnums)];
if (group->unk08 == value) {
if (group->step == step) {
best = group;
if (!g_Vars.padrandomroutes) {
break;
}
if (!g_WaypointHashes[0] && !g_WaypointHashes[1]) {
if (!g_NavSeed[0] && !g_NavSeed[1]) {
if (random() % 2 == 0) {
break;
}
} else {
u64 sp50 = ((u64)g_WaypointHashes[0] << 32) | g_WaypointHashes[1];
u64 seed = ((u64)g_NavSeed[0] << 32) | g_NavSeed[1];
if (rngRotateSeed(&sp50) % 2 == 0) {
if (rngRotateSeed(&seed) % 2 == 0) {
break;
}
}
@ -235,19 +286,18 @@ struct waygroup *func0f114810(s32 *groupnums, s32 value, u32 mask)
}
/**
* For each group number in the given list which matches the mask,
* assign value to their unk08 if their unk08 has no value (ie. is negative).
* Iterate the given groupnums and set their step if they don't already have one.
*/
void func0f114958(s32 *groupnums, s32 value, u32 mask)
void waygroupSetStepIfUndiscovered(s32 *groupnums, s32 step, u32 ignoremask)
{
struct waygroup *groups = g_StageSetup.waygroups;
while (*groupnums >= 0) {
if ((*groupnums & mask) == 0) {
struct waygroup *group = &groups[*groupnums & 0x3fff];
if ((*groupnums & ignoremask) == 0) {
struct waygroup *group = &groups[WPSEG_GET_ID(*groupnums)];
if (group->unk08 < 0) {
group->unk08 = value;
if (group->step < 0) {
group->step = step;
}
}
@ -256,93 +306,117 @@ void func0f114958(s32 *groupnums, s32 value, u32 mask)
}
/**
* Iterate the given groups and find any with an unk08 matching arg1.
* For all groups that match, iterate their neighbouring groups and set their
* unk08s to arg1 + 1, but only if their groupnum matches the given mask and
* they have no existing unk08 value.
*
* Return true if any matched.
* Do one scan of all waygroups, finding ones at the given step.
* Set their neighbours to step + 1 if they haven't been discovered yet.
*/
bool func0f1149b0(struct waygroup *group, s32 arg1, u32 mask)
bool waygroupDiscoverOneStep(struct waygroup *group, s32 step, u32 ignoremask)
{
bool result = false;
bool discovered = false;
while (group->neighbours) {
if (group->unk08 == arg1) {
result = true;
func0f114958(group->neighbours, arg1 + 1, mask);
if (group->step == step) {
discovered = true;
waygroupSetStepIfUndiscovered(group->neighbours, step + 1, ignoremask);
}
group++;
}
return result;
return discovered;
}
bool func0f114a2c(struct waygroup *from, struct waygroup *to, struct waygroup *groups, s32 arg3, u32 mask)
/**
* Figure out every group's step from the starting group.
*
* It does this by resetting all steps to -1, then setting the from group's step
* to 0, then repeatedly iterating the entire group list and expanding the step
* each time.
*
* If discoverall is false, the discovery stops once the to group is discovered.
* If discoverall is true, groups beyond the to group are also discovered.
*/
bool waygroupDiscoverSteps(struct waygroup *from, struct waygroup *to, struct waygroup *groups, bool discoverall, u32 ignoremask)
{
bool result = true;
struct waygroup *group;
s32 i;
s32 step;
for (group = groups; group->neighbours; group++) {
group->unk08 = -1;
group->step = -1;
}
from->unk08 = 0;
from->step = 0;
for (i = 0; (arg3 || to->unk08 < 0) && result; i++) {
result = func0f1149b0(groups, i, mask);
for (step = 0; (discoverall || to->step < 0) && result; step++) {
result = waygroupDiscoverOneStep(groups, step, ignoremask);
}
return result;
}
bool func0f114aec(struct waygroup *from, struct waygroup *to, struct waygroup *groups)
/**
* Find a route at the group level between from and to.
*
* The groups along the chosen route will have their step numbers set >= 10000.
*/
bool waygroupFindRoute(struct waygroup *from, struct waygroup *to, struct waygroup *groups)
{
u32 stack[2];
bool result = func0f114a2c(from, to, groups, 0, 0x8000);
bool result = waygroupDiscoverSteps(from, to, groups, false, IGNORE_INWARDS);
if (result) {
struct waygroup *curto = to;
s32 i = curto->unk08 - 1;
s32 step = curto->step - 1;
while (i >= 0) {
curto->unk08 += 10000;
curto = func0f114810(curto->neighbours, i, 0x4000);
i--;
while (step >= 0) {
curto->step += 10000;
curto = waygroupChooseNeighbour(curto->neighbours, step, IGNORE_OUTWARDS);
step--;
}
curto->unk08 += 10000;
curto->step += 10000;
}
return result;
}
struct waypoint *func0f114b7c(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask)
/**
* Given a waypoint that is known to be on the path, groupnums is a pointer to
* that waypoint's neighbours. At least one of these waypoints should have the
* given step number.
*
* The function chooses which neighbour will be routed through.
*
* If padrandomroutes is false, the first neighbour with the correct step is used.
* If padrandomroutes is true, the algorithm has a 50% chance of searching for
* another neighbour.
*
* padrandomroutes is always true.
*/
struct waypoint *waypointChooseNeighbour(s32 *pointnums, s32 step, s32 groupnum, u32 ignoremask)
{
struct waypoint *points = g_StageSetup.waypoints;
struct waypoint *best = NULL;
while (*pointnums >= 0) {
if ((*pointnums & mask) == 0) {
struct waypoint *point = &points[*pointnums & 0x3fff];
if ((*pointnums & ignoremask) == 0) {
struct waypoint *point = &points[WPSEG_GET_ID(*pointnums)];
if (point->groupnum == groupnum && point->unk0c == arg1) {
if (point->groupnum == groupnum && point->step == step) {
best = point;
if (!g_Vars.padrandomroutes) {
break;
}
if (!g_WaypointHashes[0] && !g_WaypointHashes[1]) {
if (!g_NavSeed[0] && !g_NavSeed[1]) {
if (random() % 2 == 0) {
break;
}
} else {
u64 sp50 = ((u64)g_WaypointHashes[0] << 32) | g_WaypointHashes[1];
u64 seed = ((u64)g_NavSeed[0] << 32) | g_NavSeed[1];
if (rngRotateSeed(&sp50) % 2 == 0) {
if (rngRotateSeed(&seed) % 2 == 0) {
break;
}
}
@ -356,19 +430,18 @@ struct waypoint *func0f114b7c(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask)
}
/**
* For each pointnum, if it matches the mask, belongs to the given group and has
* no unk0c, set its unk0c to the given value.
* Iterate the given pointnums and set their step if they don't already have one.
*/
void func0f114ccc(s32 *pointnums, s32 value, s32 groupnum, u32 mask)
void waypointSetStepIfUndiscovered(s32 *pointnums, s32 value, s32 groupnum, u32 ignoremask)
{
struct waypoint *waypoints = g_StageSetup.waypoints;
while (*pointnums >= 0) {
if ((*pointnums & mask) == 0) {
struct waypoint *waypoint = &waypoints[*pointnums & 0x3fff];
if ((*pointnums & ignoremask) == 0) {
struct waypoint *waypoint = &waypoints[WPSEG_GET_ID(*pointnums)];
if (waypoint->groupnum == groupnum && waypoint->unk0c < 0) {
waypoint->unk0c = value;
if (waypoint->groupnum == groupnum && waypoint->step < 0) {
waypoint->step = value;
}
}
@ -376,7 +449,11 @@ void func0f114ccc(s32 *pointnums, s32 value, s32 groupnum, u32 mask)
}
}
bool func0f114d34(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask)
/**
* Scan the waypoints in the given list, finding ones at the given step.
* Set their neighbours to step + 1 if they haven't been discovered yet.
*/
bool waypointDiscoverOneStep(s32 *pointnums, s32 step, s32 groupnum, u32 ignoremask)
{
bool result = false;
struct waypoint *points = g_StageSetup.waypoints;
@ -384,9 +461,9 @@ bool func0f114d34(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask)
while (*pointnums >= 0) {
struct waypoint *point = &points[*pointnums];
if (arg1 == point->unk0c && point->neighbours) {
if (step == point->step && point->neighbours) {
result = true;
func0f114ccc(point->neighbours, arg1 + 1, groupnum, mask);
waypointSetStepIfUndiscovered(point->neighbours, step + 1, groupnum, ignoremask);
}
pointnums++;
@ -395,7 +472,19 @@ bool func0f114d34(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask)
return result;
}
void func0f114de0(struct waypoint *from, struct waypoint *to, s32 arg2, u32 mask)
/**
* Figure out every point's step within the waygroup.
*
* The from and to points MUST be in the same waygroup.
*
* It does this by resetting all steps to -1, then setting the from point's step
* to 0, then repeatedly iterating the group's waypoints and expanding the step
* each time.
*
* If discoverall is false, the discovery stops once the to point is discovered.
* If discoverall is true, points beyond the to point are also discovered.
*/
void waypointDiscoverSteps(struct waypoint *from, struct waypoint *to, bool discoverall, u32 ignoremask)
{
struct waygroup *groups = g_StageSetup.waygroups;
struct waypoint *points = g_StageSetup.waypoints;
@ -406,60 +495,73 @@ void func0f114de0(struct waypoint *from, struct waypoint *to, s32 arg2, u32 mask
while (*pointnums >= 0) {
point = &points[*pointnums];
point->unk0c = -1;
point->step = -1;
pointnums++;
}
from->unk0c = 0;
from->step = 0;
more = true;
for (i = 0; (arg2 || to->unk0c < 0) && more; i++) {
more = func0f114d34(groups[from->groupnum].waypoints, i, from->groupnum, mask);
for (i = 0; (discoverall || to->step < 0) && more; i++) {
more = waypointDiscoverOneStep(groups[from->groupnum].waypoints, i, from->groupnum, ignoremask);
}
}
void func0f114ee4(struct waypoint *from, struct waypoint *to)
/**
* Find a route at the waypoint level between from and to.
*
* The steps along the chosen route will have their step numbers set >= 10000.
*
* The from and to points should be in the same waygroup.
*/
void waypointFindRoute(struct waypoint *from, struct waypoint *to)
{
struct waypoint *curto;
s32 value;
func0f114de0(from, to, 0, 0x8000);
waypointDiscoverSteps(from, to, false, IGNORE_INWARDS);
value = to->unk0c - 1;
value = to->step - 1;
curto = to;
while (value >= 0) {
curto->unk0c += 10000;
curto = func0f114b7c(curto->neighbours, value, from->groupnum, 0x4000);
curto->step += 10000;
curto = waypointChooseNeighbour(curto->neighbours, value, from->groupnum, IGNORE_OUTWARDS);
value--;
}
curto->unk0c += 10000;
curto->step += 10000;
}
s32 func0f114f70(struct waypoint *from, struct waypoint *to, struct waypoint **arr, s32 arrlen)
/**
* Find the route between the from and to waypoints and write their pointers to
* the supplied array.
*
* The from and to points should be in the same waygroup.
*/
s32 waypointCollectLocal(struct waypoint *from, struct waypoint *to, struct waypoint **arr, s32 arrlen)
{
struct waypoint **arrptr = arr;
struct waypoint *curfrom;
s32 i;
s32 step;
if (arrlen >= 2) {
func0f114ee4(from, to);
waypointFindRoute(from, to);
*arr = from;
arrptr++;
curfrom = from;
arrlen += 9999;
i = 10001;
step = 10001;
while (i <= to->unk0c && i < arrlen) {
curfrom = func0f114b7c(curfrom->neighbours, i, from->groupnum, 0x8000);
while (step <= to->step && step < arrlen) {
curfrom = waypointChooseNeighbour(curfrom->neighbours, step, from->groupnum, IGNORE_INWARDS);
*arrptr = curfrom;
arrptr++;
i++;
step++;
}
}
@ -469,40 +571,46 @@ s32 func0f114f70(struct waypoint *from, struct waypoint *to, struct waypoint **a
return arrptr - arr;
}
void func0f11505c(struct waygroup *groupa, struct waygroup *groupb, struct waypoint **pointa, struct waypoint **pointb)
/**
* Given two neighbouring waygroups, find the path segment that connects the two
* groups. Write pointers to those two waypoints to **frompoint and **topoint.
*
* If there are multiple paths between the two waygroups, choose one at random.
*/
void waypointFindSegmentIntoGroup(struct waygroup *fromgroup, struct waygroup *togroup, struct waypoint **frompoint, struct waypoint **topoint)
{
struct waypoint *points = g_StageSetup.waypoints;
struct waygroup *groups = g_StageSetup.waygroups;
s32 *groupapointnums = groupa->waypoints;
s32 *fromwpptr = fromgroup->waypoints;
s32 stack;
*pointb = NULL;
*pointa = NULL;
*topoint = NULL;
*frompoint = NULL;
while (*groupapointnums >= 0) {
struct waypoint *groupapoint = &points[*groupapointnums];
s32 *neighbournums = groupapoint->neighbours;
while (*fromwpptr >= 0) {
struct waypoint *fromwp = &points[*fromwpptr];
s32 *neighbournums = fromwp->neighbours;
while (*neighbournums >= 0) {
if ((*neighbournums & 0x8000) == 0) {
struct waypoint *neighbour = &points[*neighbournums & 0x3fff];
if ((*neighbournums & IGNORE_INWARDS) == 0) {
struct waypoint *neighbour = &points[WPSEG_GET_ID(*neighbournums)];
if (groupb == &groups[neighbour->groupnum]) {
*pointa = groupapoint;
*pointb = neighbour;
if (togroup == &groups[neighbour->groupnum]) {
*frompoint = fromwp;
*topoint = neighbour;
if (!g_Vars.padrandomroutes) {
break;
}
if (!g_WaypointHashes[0] && !g_WaypointHashes[1]) {
if (!g_NavSeed[0] && !g_NavSeed[1]) {
if (random() % 2 == 0) {
break;
}
} else {
u64 sp50 = ((u64)g_WaypointHashes[0] << 32) | g_WaypointHashes[1];
u64 seed = ((u64)g_NavSeed[0] << 32) | g_NavSeed[1];
if ((rngRotateSeed(&sp50) % 2) == 0) {
if ((rngRotateSeed(&seed) % 2) == 0) {
break;
}
}
@ -512,7 +620,7 @@ void func0f11505c(struct waygroup *groupa, struct waygroup *groupb, struct waypo
neighbournums++;
}
groupapointnums++;
fromwpptr++;
}
}
@ -523,7 +631,7 @@ void func0f11505c(struct waygroup *groupa, struct waygroup *groupb, struct waypo
*
* The return value is the number of elements populated into the array.
*/
s32 waypointFindRoute(struct waypoint *frompoint, struct waypoint *topoint, struct waypoint **arr, s32 arrlen)
s32 navFindRoute(struct waypoint *frompoint, struct waypoint *topoint, struct waypoint **arr, s32 arrlen)
{
struct waypoint **arrptr = arr;
struct waygroup *groups = g_StageSetup.waygroups;
@ -532,28 +640,28 @@ s32 waypointFindRoute(struct waypoint *frompoint, struct waypoint *topoint, stru
struct waygroup *fromgroup = &groups[frompoint->groupnum];
struct waygroup *togroup = &groups[topoint->groupnum];
if (func0f114aec(fromgroup, togroup, groups)) {
if (waygroupFindRoute(fromgroup, togroup, groups)) {
struct waypoint *curfrompoint = frompoint;
struct waygroup *curfromgroup = fromgroup;
s32 i;
s32 step;
for (i = fromgroup->unk08 + 1; i <= togroup->unk08 && arrlen >= 2; i++) {
for (step = fromgroup->step + 1; step <= togroup->step && arrlen >= 2; step++) {
s32 numwritten;
struct waygroup *nextfromgroup = func0f114810(curfromgroup->neighbours, i, 0x8000);
struct waypoint *tmppoint;
struct waypoint *nextfrompoint;
struct waygroup *nextfromgroup = waygroupChooseNeighbour(curfromgroup->neighbours, step, IGNORE_INWARDS);
struct waypoint *curgrouplastwp;
struct waypoint *nextgroupfirstwp;
func0f11505c(curfromgroup, nextfromgroup, &tmppoint, &nextfrompoint);
numwritten = func0f114f70(curfrompoint, tmppoint, arrptr, arrlen) - 1;
waypointFindSegmentIntoGroup(curfromgroup, nextfromgroup, &curgrouplastwp, &nextgroupfirstwp);
numwritten = waypointCollectLocal(curfrompoint, curgrouplastwp, arrptr, arrlen) - 1;
arrlen -= numwritten;
arrptr += numwritten;
curfrompoint = nextfrompoint;
curfrompoint = nextgroupfirstwp;
curfromgroup = nextfromgroup;
}
arrptr += func0f114f70(curfrompoint, topoint, arrptr, arrlen) - 1;
arrptr += waypointCollectLocal(curfrompoint, topoint, arrptr, arrlen) - 1;
}
}
@ -563,17 +671,17 @@ s32 waypointFindRoute(struct waypoint *frompoint, struct waypoint *topoint, stru
return arrptr - arr;
}
void func0f115390(void)
void waypointResetAllSteps(void)
{
struct waypoint *waypoint = g_StageSetup.waypoints;
while (waypoint->padnum >= 0) {
waypoint->unk0c = -1;
waypoint->step = -1;
waypoint++;
}
}
struct waypoint *func0f1153c4(s32 *pointnums, s32 arg1)
struct waypoint *waypointFindRandomAtStep(s32 *pointnums, s32 step)
{
s32 len = 0;
s32 randomindex;
@ -583,26 +691,20 @@ struct waypoint *func0f1153c4(s32 *pointnums, s32 arg1)
len++;
}
// This is effectively randomly dividing the pointnums list into two,
// then checking the upper portion before the lower portion. Both loops
// have the same logic so this seems unusual, but there is reason to do
// this if they want the returned waypoint to be any random waypoint that
// meets the arg1 criteria, with equal weighting.
randomindex = random() % len;
for (i = randomindex; i < len; i++) {
struct waypoint *point = &g_StageSetup.waypoints[pointnums[i] & 0x3fff];
struct waypoint *point = &g_StageSetup.waypoints[WPSEG_GET_ID(pointnums[i])];
if (point->unk0c == arg1) {
if (point->step == step) {
return point;
}
}
for (i = 0; i < randomindex; i++) {
struct waypoint *point = &g_StageSetup.waypoints[pointnums[i] & 0x3fff];
struct waypoint *point = &g_StageSetup.waypoints[WPSEG_GET_ID(pointnums[i])];
if (point->unk0c == arg1) {
if (point->step == step) {
return point;
}
}
@ -610,7 +712,7 @@ struct waypoint *func0f1153c4(s32 *pointnums, s32 arg1)
return NULL;
}
struct waygroup *func0f1154cc(s32 *groupnums, s32 arg1)
struct waygroup *waygroupFindRandomAtStep(s32 *groupnums, s32 step)
{
s32 len = 0;
s32 randomindex;
@ -620,22 +722,20 @@ struct waygroup *func0f1154cc(s32 *groupnums, s32 arg1)
len++;
}
// Similar to the above function, return a random waygroup
// which matches the arg1 criteria.
randomindex = random() % len;
for (i = randomindex; i < len; i++) {
struct waygroup *group = &g_StageSetup.waygroups[groupnums[i] & 0x3fff];
struct waygroup *group = &g_StageSetup.waygroups[WPSEG_GET_ID(groupnums[i])];
if (group->unk08 == arg1) {
if (group->step == step) {
return group;
}
}
for (i = 0; i < randomindex; i++) {
struct waygroup *group = &g_StageSetup.waygroups[groupnums[i] & 0x3fff];
struct waygroup *group = &g_StageSetup.waygroups[WPSEG_GET_ID(groupnums[i])];
if (group->unk08 == arg1) {
if (group->step == step) {
return group;
}
}
@ -643,59 +743,75 @@ struct waygroup *func0f1154cc(s32 *groupnums, s32 arg1)
return NULL;
}
struct waypoint *func0f1155e0(struct waypoint *pointa, struct waypoint *pointb)
/**
* Try to find a waypoint not on the route towards the target, and return it.
*/
struct waypoint *navChooseRetreatPoint(struct waypoint *chrpoint, struct waypoint *tarpoint)
{
if (g_StageSetup.waygroups) {
struct waygroup *groupa = &g_StageSetup.waygroups[pointa->groupnum];
struct waygroup *groupb = &g_StageSetup.waygroups[pointb->groupnum];
struct waygroup *chrgroup = &g_StageSetup.waygroups[chrpoint->groupnum];
struct waygroup *targroup = &g_StageSetup.waygroups[tarpoint->groupnum];
struct waypoint *result;
s32 stack;
if (groupa == groupb) {
func0f115390();
func0f114de0(pointb, pointa, 1, 0);
if (chrgroup == targroup) {
waypointResetAllSteps();
result = func0f1153c4(pointa->neighbours, -1);
// Mark steps from target to chr
waypointDiscoverSteps(tarpoint, chrpoint, true, IGNORE_NONE);
// If the chr has a neighbouring waypoint into another group (room), select it
result = waypointFindRandomAtStep(chrpoint->neighbours, -1);
if (result) {
return result;
}
result = func0f1153c4(pointa->neighbours, pointa->unk0c + 1);
// Otherwise, choose a waypoint not between the two points
result = waypointFindRandomAtStep(chrpoint->neighbours, chrpoint->step + 1);
if (result) {
return result;
}
} else {
func0f114a2c(groupb, groupa, g_StageSetup.waygroups, 0, 0x8000);
waygroupDiscoverSteps(targroup, chrgroup, g_StageSetup.waygroups, false, IGNORE_INWARDS);
if (groupa->unk08 >= 0) {
struct waygroup *tmpgroup = func0f1154cc(groupa->neighbours, -1);
if (chrgroup->step >= 0) {
// Find a neighbouring group not in the route to target
struct waygroup *safetygroup = waygroupFindRandomAtStep(chrgroup->neighbours, -1);
if (tmpgroup) {
struct waypoint *sp48;
struct waypoint *sp44;
struct waypoint *arr[3];
if (safetygroup) {
struct waypoint *segfrompoint;
struct waypoint *segtopoint;
struct waypoint *route[3];
func0f11505c(groupa, tmpgroup, &sp48, &sp44);
waypointFindSegmentIntoGroup(chrgroup, safetygroup, &segfrompoint, &segtopoint);
if (sp48 == pointa) {
return sp44;
// Return the entry waypoint in safetygroup
if (segfrompoint == chrpoint) {
return segtopoint;
}
if (func0f114f70(pointa, sp48, arr, 3) >= 3) {
return arr[1];
// Return first waypoint towards safetygroup
if (waypointCollectLocal(chrpoint, segfrompoint, route, 3) >= 3) {
return route[1];
}
} else {
struct waygroup *tmpgroup = func0f114810(groupa->neighbours, groupa->unk08 - 1, 0x8000);
// There are no waygroups outside of the route between the two points.
// ie. The chr and target are at opposite ends of the level, and the level is mostly linear.
if (tmpgroup) {
struct waypoint *sp30;
struct waypoint *sp2c;
// Choose a group one step closer to the target
struct waygroup *safetygroup = waygroupChooseNeighbour(chrgroup->neighbours, chrgroup->step - 1, IGNORE_INWARDS);
func0f11505c(groupa, tmpgroup, &sp30, &sp2c);
func0f114de0(sp30, pointa, 1, 0);
result = func0f114b7c(pointa->neighbours, pointa->unk0c + 1, pointa->groupnum, 0x8000);
if (safetygroup) {
struct waypoint *segfrompoint;
struct waypoint *segtopoint;
waypointFindSegmentIntoGroup(chrgroup, safetygroup, &segfrompoint, &segtopoint);
waypointDiscoverSteps(segfrompoint, chrpoint, true, IGNORE_NONE);
// Return first waypoint towards safetygroup
result = waypointChooseNeighbour(chrpoint->neighbours, chrpoint->step + 1, chrpoint->groupnum, IGNORE_INWARDS);
if (result) {
return result;
@ -720,7 +836,7 @@ struct waypoint *func0f1155e0(struct waypoint *pointa, struct waypoint *pointb)
* A's group and B's group then group B is removed from group A's neighbour
* list.
*/
void waypointDisableSegmentInDirection(struct waypoint *a, struct waypoint *b)
void navDisableSegmentInDirection(struct waypoint *a, struct waypoint *b)
{
struct waygroup *agroup = &g_StageSetup.waygroups[a->groupnum];
s32 bindex = b - g_StageSetup.waypoints;
@ -731,11 +847,11 @@ void waypointDisableSegmentInDirection(struct waypoint *a, struct waypoint *b)
s32 tmp;
// Find index of the neighbour point to remove, or index of end if not found
for (i = 0; (tmp = a->neighbours[i]) >= 0 && (tmp & 0x3fff) != bindex; i++);
for (i = 0; (tmp = a->neighbours[i]) >= 0 && WPSEG_GET_ID(tmp) != bindex; i++);
// If neighbour was found, shuffle the rest of the neighbour list back by
// one, effectively removing it.
if ((tmp & 0x3fff) == bindex) {
if (WPSEG_GET_ID(tmp) == bindex) {
for (; a->neighbours[i] >= 0; i++) {
a->neighbours[i] = a->neighbours[i + 1];
}
@ -747,7 +863,7 @@ void waypointDisableSegmentInDirection(struct waypoint *a, struct waypoint *b)
struct waypoint *apoint = &g_StageSetup.waypoints[tmp];
for (j = 0; (tmp = apoint->neighbours[j]) >= 0 && !foundlink; j++) {
struct waypoint *neighbour = &g_StageSetup.waypoints[tmp & 0x3fff];
struct waypoint *neighbour = &g_StageSetup.waypoints[WPSEG_GET_ID(tmp)];
if (neighbour->groupnum == bgroupnum) {
foundlink = true;
@ -757,9 +873,9 @@ void waypointDisableSegmentInDirection(struct waypoint *a, struct waypoint *b)
// If no link was found, remove group B from group A's neighbour list
if (!foundlink) {
for (i = 0; (tmp = agroup->neighbours[i]) >= 0 && (tmp & 0x3fff) != bgroupnum; i++);
for (i = 0; (tmp = agroup->neighbours[i]) >= 0 && WPSEG_GET_ID(tmp) != bgroupnum; i++);
if ((tmp & 0x3fff) == bgroupnum) {
if (WPSEG_GET_ID(tmp) == bgroupnum) {
for (; agroup->neighbours[i] >= 0; i++) {
agroup->neighbours[i] = agroup->neighbours[i + 1];
}
@ -776,7 +892,7 @@ void waypointDisableSegmentInDirection(struct waypoint *a, struct waypoint *b)
* This code assumes that A's neighbours array is big enough to add the new
* neighbour, which it will be if B was disabled previously.
*/
void waypointEnableSegmentInDirection(struct waypoint *a, struct waypoint *b)
void navEnableSegmentInDirection(struct waypoint *a, struct waypoint *b)
{
struct waygroup *agroup = &g_StageSetup.waygroups[a->groupnum];
s32 bpointnum = b - g_StageSetup.waypoints;
@ -787,31 +903,31 @@ void waypointEnableSegmentInDirection(struct waypoint *a, struct waypoint *b)
// Find index in A's neighbour list where B can be added.
// This will either be at the -1 terminator, or if B already exists in the
// list then the index of B.
for (i = 0; (neighbournum = a->neighbours[i]) >= 0 && (neighbournum & 0x3fff) != bpointnum; i++);
for (i = 0; (neighbournum = a->neighbours[i]) >= 0 && WPSEG_GET_ID(neighbournum) != bpointnum; i++);
// Add B to A's neighbour list if it doesn't exist
if ((neighbournum & 0x3fff) != bpointnum) {
if (WPSEG_GET_ID(neighbournum) != bpointnum) {
a->neighbours[i] = bpointnum;
a->neighbours[i + 1] = -1;
}
// Now the same for groups. Make sure B's group is a neighbour of A's group.
for (i = 0; (neighbournum = agroup->neighbours[i]) >= 0 && (neighbournum & 0x3fff) != bgroupnum; i++);
for (i = 0; (neighbournum = agroup->neighbours[i]) >= 0 && WPSEG_GET_ID(neighbournum) != bgroupnum; i++);
if (bgroupnum != (neighbournum & 0x3fff)) {
if (bgroupnum != WPSEG_GET_ID(neighbournum)) {
agroup->neighbours[i] = bgroupnum;
agroup->neighbours[i + 1] = -1;
}
}
void waypointDisableSegment(struct waypoint *a, struct waypoint *b)
void navDisableSegment(struct waypoint *a, struct waypoint *b)
{
waypointDisableSegmentInDirection(a, b);
waypointDisableSegmentInDirection(b, a);
navDisableSegmentInDirection(a, b);
navDisableSegmentInDirection(b, a);
}
void waypointEnableSegment(struct waypoint *a, struct waypoint *b)
void navEnableSegment(struct waypoint *a, struct waypoint *b)
{
waypointEnableSegmentInDirection(a, b);
waypointEnableSegmentInDirection(b, a);
navEnableSegmentInDirection(a, b);
navEnableSegmentInDirection(b, a);
}

View File

@ -3132,7 +3132,7 @@ void htBegin(void)
chrSetStageFlag(NULL, func0f1a25c0(htGetIndexBySlot(var80088bb4)));
// Disable segment leading out of the door
waypointDisableSegment(&waypoints[0x20], &waypoints[0x31]);
navDisableSegment(&waypoints[0x20], &waypoints[0x31]);
g_Vars.currentplayer->training = true;
bgunSetPassiveMode(false);
@ -3153,7 +3153,7 @@ void htEnd(void)
chrUnsetStageFlag(NULL, func0f1a25c0(htGetIndexBySlot(var80088bb4)));
// Enable segment leading out of the door
waypointEnableSegment(&waypoints[0x20], &waypoints[0x31]);
navEnableSegment(&waypoints[0x20], &waypoints[0x31]);
g_Vars.currentplayer->training = false;
roomGetProps(rooms, propnums, 256);

View File

@ -53,6 +53,7 @@
#define ALIGN16(val) ((((val) + 0xf) | 0xf) ^ 0xf)
#define ALIGN64(val) (((((u32)(val)) + 0x3f) | 0x3f) ^ 0x3f)
#define ARRAYCOUNT(a) (s32)(sizeof(a) / sizeof(a[0]))
#define CHRNAVSEED(chr) ((g_Vars.lvframe60 >> 9) * 128 + chr->chrnum * 8)
#define CHRRACE(chr) (chr ? chr->race : RACE_HUMAN)
#define CRASH() *(u8 *)0 = 69
#define CYCLES_PER_FRAME ((s32) OS_CPU_COUNTER / (PAL ? 50 : 60))
@ -3446,12 +3447,12 @@
#define PSTYPE_FOOTSTEP 16
#define PSTYPE_CHRSHOOT 17
#define QUADRANT_BACK 0x01
#define QUADRANT_SIDE1 0x02
#define QUADRANT_SIDE2 0x04
#define QUADRANT_FRONT 0x08
#define QUADRANT_2NDWPTOTARGET 0x10 // second waypoint on route to target
#define QUADRANT_20 0x20
#define QUADRANT_BACK 0x01
#define QUADRANT_SIDE1 0x02
#define QUADRANT_SIDE2 0x04
#define QUADRANT_FRONT 0x08
#define QUADRANT_TOWARDSTARGET 0x10
#define QUADRANT_AWAYFROMTARGET 0x20
#define QUIP_ATTACK1 0
#define QUIP_ATTACK2 1

View File

@ -4,28 +4,28 @@
#include "data.h"
#include "types.h"
void waypointSetHashThing(s32 hash1, s32 hash2);
void navSetSeed(u32 upper, u32 lower);
struct waypoint *waypointFindClosestToPos(struct coord *pos, s16 *rooms);
struct waygroup *func0f114810(s32 *groupnums, s32 value, u32 mask);
void func0f114958(s32 *groupnums, s32 value, u32 mask);
bool func0f1149b0(struct waygroup *group, s32 arg1, u32 mask);
bool func0f114a2c(struct waygroup *from, struct waygroup *to, struct waygroup *groups, s32 arg3, u32 mask);
bool func0f114aec(struct waygroup *from, struct waygroup *to, struct waygroup *groups);
struct waypoint *func0f114b7c(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask);
void func0f114ccc(s32 *pointnums, s32 value, s32 groupnum, u32 mask);
bool func0f114d34(s32 *pointnums, s32 arg1, s32 groupnum, u32 mask);
void func0f114de0(struct waypoint *from, struct waypoint *to, s32 arg2, u32 mask);
void func0f114ee4(struct waypoint *from, struct waypoint *to);
s32 func0f114f70(struct waypoint *from, struct waypoint *to, struct waypoint **arr, s32 arrlen);
void func0f11505c(struct waygroup *arg0, struct waygroup *arg1, struct waypoint **arg2, struct waypoint **arg3);
s32 waypointFindRoute(struct waypoint *from, struct waypoint *to, struct waypoint **arr, s32 arrlen);
void func0f115390(void);
struct waypoint *func0f1153c4(s32 *pointnums, s32 arg1);
struct waygroup *func0f1154cc(s32 *groupnums, s32 arg1);
struct waypoint *func0f1155e0(struct waypoint *pointa, struct waypoint *pointb);
void waypointDisableSegmentInDirection(struct waypoint *a, struct waypoint *b);
void waypointEnableSegmentInDirection(struct waypoint *a, struct waypoint *b);
void waypointDisableSegment(struct waypoint *a, struct waypoint *b);
void waypointEnableSegment(struct waypoint *a, struct waypoint *b);
struct waygroup *waygroupChooseNeighbour(s32 *groupnums, s32 step, u32 ignoremask);
void waygroupSetStepIfUndiscovered(s32 *groupnums, s32 step, u32 ignoremask);
bool waygroupDiscoverOneStep(struct waygroup *group, s32 step, u32 ignoremask);
bool waygroupDiscoverSteps(struct waygroup *from, struct waygroup *to, struct waygroup *groups, bool discoverall, u32 ignoremask);
bool waygroupFindRoute(struct waygroup *from, struct waygroup *to, struct waygroup *groups);
struct waypoint *waypointChooseNeighbour(s32 *pointnums, s32 step, s32 groupnum, u32 ignoremask);
void waypointSetStepIfUndiscovered(s32 *pointnums, s32 value, s32 groupnum, u32 ignoremask);
bool waypointDiscoverOneStep(s32 *pointnums, s32 step, s32 groupnum, u32 ignoremask);
void waypointDiscoverSteps(struct waypoint *from, struct waypoint *to, bool discoverall, u32 ignoremask);
void waypointFindRoute(struct waypoint *from, struct waypoint *to);
s32 waypointCollectLocal(struct waypoint *from, struct waypoint *to, struct waypoint **arr, s32 arrlen);
void waypointFindSegmentIntoGroup(struct waygroup *fromgroup, struct waygroup *togroup, struct waypoint **frompoint, struct waypoint **topoint);
s32 navFindRoute(struct waypoint *from, struct waypoint *to, struct waypoint **arr, s32 arrlen);
void waypointResetAllSteps(void);
struct waypoint *waypointFindRandomAtStep(s32 *pointnums, s32 step);
struct waygroup *waygroupFindRandomAtStep(s32 *groupnums, s32 step);
struct waypoint *navChooseRetreatPoint(struct waypoint *chrpoint, struct waypoint *targetpoint);
void navDisableSegmentInDirection(struct waypoint *a, struct waypoint *b);
void navEnableSegmentInDirection(struct waypoint *a, struct waypoint *b);
void navDisableSegment(struct waypoint *a, struct waypoint *b);
void navEnableSegment(struct waypoint *a, struct waypoint *b);
#endif

View File

@ -684,14 +684,14 @@ union modelrwdata {
struct waygroup {
s32 *neighbours;
s32 *waypoints;
s32 unk08;
s32 step;
};
struct waypoint {
s32 padnum;
s32 *neighbours; // most significant two bits are booleans, remaining bits are waypoint index
s32 groupnum;
s32 unk0c;
s32 step;
};
struct aibot {

View File

@ -13,10 +13,10 @@ void objSetBlockedPathUnblocked(struct defaultobj *blocker, bool unblocked)
while (bp) {
if (bp->blocker == blocker) {
if (unblocked) {
waypointEnableSegment(&g_StageSetup.waypoints[bp->waypoint1], &g_StageSetup.waypoints[bp->waypoint2]);
navEnableSegment(&g_StageSetup.waypoints[bp->waypoint1], &g_StageSetup.waypoints[bp->waypoint2]);
break;
} else {
waypointDisableSegment(&g_StageSetup.waypoints[bp->waypoint1], &g_StageSetup.waypoints[bp->waypoint2]);
navDisableSegment(&g_StageSetup.waypoints[bp->waypoint1], &g_StageSetup.waypoints[bp->waypoint2]);
break;
}
}