mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-17 15:18:11 +00:00
75bfc1290b
Using clang-tidy modernize-use-nullptr
1231 lines
36 KiB
C++
1231 lines
36 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1999-2000 Revolution Software Ltd.
|
|
* This code is based on source code created by Revolution Software,
|
|
* used with permission.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "engines/icb/p4.h"
|
|
#include "engines/icb/common/px_floor_map.h"
|
|
#include "engines/icb/common/px_linkeddatafile.h"
|
|
#include "engines/icb/common/px_scriptengine.h"
|
|
#include "engines/icb/common/ptr_util.h"
|
|
#include "engines/icb/floors.h"
|
|
#include "engines/icb/debug.h"
|
|
#include "engines/icb/route_manager.h"
|
|
#include "engines/icb/global_objects.h"
|
|
#include "engines/icb/session.h"
|
|
#include "engines/icb/global_switches.h"
|
|
#include "engines/icb/res_man.h"
|
|
#include "engines/icb/mission.h"
|
|
|
|
namespace ICB {
|
|
|
|
#define ROUTE_CLOSE 10 * REAL_ONE
|
|
|
|
mcodeFunctionReturnCodes fn_route_to_marker(int32 &result, int32 *params) {
|
|
return (MS->fn_route_to_marker(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_route_to_nico(int32 &result, int32 *params) {
|
|
return (MS->fn_route_to_nico(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_tiny_route(int32 &result, int32 *params) {
|
|
return (MS->fn_tiny_route(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_sharp_route(int32 &result, int32 *params) {
|
|
return (MS->fn_sharp_route(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_route_to_near_mega(int32 &result, int32 *params) {
|
|
return (MS->fn_route_to_near_mega(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_interact_near_mega(int32 &result, int32 *params) {
|
|
return (MS->fn_interact_near_mega(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_sharp_route_to_near_mega(int32 &result, int32 *params) {
|
|
return (MS->fn_sharp_route_to_near_mega(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_spectre_route_to_mega(int32 &result, int32 *params) {
|
|
return (MS->fn_spectre_route_to_mega(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_laser_route(int32 &result, int32 *params) {
|
|
return (MS->fn_laser_route(result, params));
|
|
}
|
|
|
|
mcodeFunctionReturnCodes fn_room_route(int32 &result, int32 *params) {
|
|
return (MS->fn_room_route(result, params));
|
|
}
|
|
|
|
void _game_session::Reset_route_manager() {
|
|
// reset the router object - called by session constructor
|
|
|
|
// remove used routes - just keeps the memory limit down
|
|
|
|
Zdebug("--new sesssion: resetting route manager service--");
|
|
}
|
|
|
|
bool8 _game_session::Is_route_required(PXreal startx, PXreal startz, PXreal destx, PXreal destz) {
|
|
// check if route is really close or exact
|
|
// if close then adjust
|
|
|
|
if ((PXfabs(startx - destx) < ROUTE_CLOSE) && (PXfabs(startz - destz) < ROUTE_CLOSE)) {
|
|
M->actor_xyz.x = destx;
|
|
M->actor_xyz.z = destz;
|
|
|
|
// no route is required
|
|
return (FALSE8);
|
|
}
|
|
|
|
// a route is required
|
|
return (TRUE8);
|
|
}
|
|
|
|
void _game_session::Create_initial_route(__rtype type) {
|
|
// Changed last parameter to int32 from bool to stop a performance warning. Other bools are passed as int anyway,
|
|
// so this is more consistent and faster.
|
|
|
|
// create an initial route for character 'id'
|
|
// this route will replace any route that maybe partway complete at this moment (chr$ can change their plans)
|
|
// the route is of a fixed type specified by the _route_type structure
|
|
// after building a route we return allowing fn_ functions to make any decisions/selections they wish before pressing on with animation
|
|
|
|
_route_stat res;
|
|
uint32 time = 0;
|
|
|
|
if ((g_px->logic_timing) && (g_px->mega_timer))
|
|
time = GetMicroTimer();
|
|
|
|
Zdebug("create_initial_route");
|
|
|
|
if (!Is_route_required(M->m_main_route.request_form.initial_x, M->m_main_route.request_form.initial_z, M->m_main_route.request_form.dest_x,
|
|
M->m_main_route.request_form.dest_z)) {
|
|
M->m_main_route.request_form.error = __RR_NO_ROUTE_REQUIRED;
|
|
Zdebug("no route required");
|
|
return;
|
|
}
|
|
|
|
// set extrapolation size
|
|
troute.extrap_size = M->extrap_size;
|
|
|
|
M->m_phase = RM_MAIN;
|
|
|
|
// reset the routers barrier list
|
|
troute.Reset_barrier_list();
|
|
|
|
switch (type) {
|
|
case __FULL:
|
|
MS->session_barriers->Form_route_barrier_list(M->m_main_route.request_form.initial_x, M->m_main_route.request_form.character_y,
|
|
M->m_main_route.request_form.initial_z, M->m_main_route.request_form.dest_x, M->m_main_route.request_form.dest_z);
|
|
break;
|
|
|
|
case __ENDB:
|
|
MS->session_barriers->Form_parent_barrier_list(M->m_main_route.request_form.dest_x, M->m_main_route.request_form.character_y, M->m_main_route.request_form.dest_z);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// now make the route
|
|
res = troute.Calc_route(M->m_main_route.request_form.initial_x, M->m_main_route.request_form.initial_z, M->m_main_route.request_form.dest_x,
|
|
M->m_main_route.request_form.dest_z);
|
|
|
|
if (res == __PRIM_ROUTE_OK) {
|
|
Zdebug("route ok");
|
|
|
|
// get the route - pass the address of a pointer to a _point where the route can be new'ed and copied
|
|
troute.Give_route(&M->m_main_route);
|
|
|
|
// get barriers for nethack diagnostics
|
|
if (type != __LASER)
|
|
troute.Give_barrier_list(&M->m_main_route);
|
|
|
|
if (L->pan >= HALF_TURN)
|
|
L->pan -= FULL_TURN;
|
|
else if (L->pan <= -HALF_TURN)
|
|
L->pan += FULL_TURN;
|
|
|
|
Calc_dist_and_pan(M->actor_xyz.x, M->actor_xyz.z, &M->m_main_route);
|
|
|
|
M->m_main_route.request_form.error = __ROUTE_REQUEST_OK;
|
|
} else {
|
|
Zdebug("route failed");
|
|
M->m_main_route.request_form.error = __ROUTE_REQUEST_PRIM_FAILED;
|
|
}
|
|
|
|
if ((g_px->logic_timing) && (g_px->mega_timer)) {
|
|
time = GetMicroTimer() - time;
|
|
L->slowest_cycle_time = time;
|
|
}
|
|
}
|
|
|
|
bool8 _game_session::Process_route() {
|
|
// processes the route that has been built by Create_initial_route
|
|
// animate along this characters route
|
|
|
|
// returns 0 still going
|
|
// 1 finished
|
|
|
|
_route_description *route;
|
|
bool8 res;
|
|
|
|
route = &M->m_main_route;
|
|
|
|
// gosub turning
|
|
if ((M->target_pan) && (!L->auto_panning)) { // animated pan - as opposed to engine auto panning
|
|
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
|
|
|
|
return (0);
|
|
// note, when this anim finishes we must snap the x,z back to what they were before the turn began
|
|
} else if (route->arrived) {
|
|
// must have finished route and set up a slow out anim - now run that anim
|
|
if (route->request_form.finish_on_stand) {
|
|
res = Play_anim();
|
|
}
|
|
|
|
else
|
|
res = Play_anim_with_no_movement();
|
|
|
|
return (res);
|
|
|
|
// remove the phase so headup diagnostics disappear now the route is effectively done
|
|
M->m_phase = RM_NONE;
|
|
|
|
return (1); // no slow-out so we're done
|
|
}
|
|
|
|
// ok, process that point-to-point route!
|
|
if (Animate_points(route)) {
|
|
// route has finished
|
|
// has a slow-out been requested?
|
|
|
|
if (route->request_form.finish_on_stand) {
|
|
// set up follow on animation as requested
|
|
|
|
// set up walk-to stand + stand
|
|
|
|
Soft_start_with_double_link(__WALK_TO_STAND, __WALK_TO_OTHER_STAND_LEFT_LEG, __STAND);
|
|
|
|
res = Play_anim();
|
|
|
|
// if more to do then return
|
|
return (res);
|
|
|
|
// else drop down and return fnished after removing the phase
|
|
} else if (route->request_form.finish_on_null_stand) { // play link anim WITHOUT coordinate movement; for interaction accuracy
|
|
// set up follow on animation as requested
|
|
// set up walk-to stand + stand
|
|
|
|
Soft_start_with_double_link(__WALK_TO_STAND, __WALK_TO_OTHER_STAND_LEFT_LEG, __STAND);
|
|
|
|
res = Play_anim_with_no_movement();
|
|
|
|
// if more to do then return
|
|
return (res);
|
|
|
|
// else drop down and return fnished after removing the phase
|
|
}
|
|
|
|
// remove the phase so headup diagnostics disappear now the route is effectively done
|
|
M->m_phase = RM_NONE;
|
|
|
|
return (1); // no slow-out so we're done
|
|
}
|
|
// still auto-routing
|
|
return (0);
|
|
}
|
|
|
|
_route_description *_game_session::Fetch_route_desc(uint32 id) {
|
|
// return a pointer to a built route
|
|
// this is used to draw into the plan viewer
|
|
// returns a 0 if there is no route
|
|
|
|
// no route at all
|
|
if (!logic_structs[id]->mega->m_phase)
|
|
return (nullptr);
|
|
|
|
return (&logic_structs[id]->mega->m_main_route);
|
|
}
|
|
|
|
void _route_description::___init() {
|
|
// nethack diagnostics
|
|
if (diag_bars)
|
|
delete[] diag_bars;
|
|
diag_bars = nullptr;
|
|
|
|
number_of_diag_bars = 0;
|
|
|
|
arrived = FALSE8; // when route has been animated this is set to YES ready for optional slow-out animation
|
|
|
|
total_points = 0; // in-case fails for viewers
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_tiny_route(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to specified x1,z1 from the same or adjoining floor rects
|
|
// player or mega
|
|
|
|
// params 0 x INT32's to be cast to floats :| hmmm...
|
|
// 1 z
|
|
// 2 0='walk', else 'run'
|
|
// 3 0=no turn-on-spot 1=yes
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
int32 sub1, sub2, len;
|
|
bool8 route_res;
|
|
|
|
if (L->looping < 2) {
|
|
|
|
Tdebug("route_async.txt", "%s tiny", object->GetName());
|
|
|
|
// check for free router
|
|
if (Is_router_busy())
|
|
return IR_REPEAT;
|
|
|
|
sub1 = params[0] - (int32)M->actor_xyz.x;
|
|
sub2 = params[1] - (int32)M->actor_xyz.z;
|
|
|
|
if (sub1 < 0)
|
|
sub1 = (0 - sub1);
|
|
if (sub2 < 0)
|
|
sub2 = (0 - sub2);
|
|
|
|
len = sub1 + sub2;
|
|
|
|
session_barriers->Set_route_barrier_mask((int32)M->actor_xyz.x - len, (int32)M->actor_xyz.x + len, (int32)M->actor_xyz.z - len, (int32)M->actor_xyz.z + len);
|
|
route_res = Setup_route(result, params[0], params[1], params[2], __FULL, TRUE8);
|
|
session_barriers->Clear_route_barrier_mask();
|
|
if (!route_res) {
|
|
if (!result) { // failed
|
|
Setup_route(result, params[0], params[1], params[2], __LASER, TRUE8); // must succeed
|
|
} else {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return IR_CONT; // finished so send script on
|
|
}
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_room_route(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to specified x1,z1 in an adjacent room
|
|
// player or mega
|
|
// doesnt end on stand
|
|
|
|
// params 0 x
|
|
// 1 z
|
|
// 2 0='walk', else 'run'
|
|
// 3 end on stand (i.e. tiny or sharp)
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy()) {
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
if (!Setup_route(result, params[0], params[1], params[2], __ENDB, params[3])) {
|
|
L->looping = 0;
|
|
return (IR_CONT);
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_sharp_route(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to specified x1,z1 from the same or adjoining floor rects
|
|
// player or mega
|
|
// doesnt end on stand
|
|
|
|
// params 0 x INT32's to be cast to floats :| hmmm...
|
|
// 1 z
|
|
// 2 0='walk', else 'run'
|
|
// 3 0=no turn-on-spot 1=yes
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
int32 sub1, sub2, len;
|
|
bool8 route_res;
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy()) {
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
sub1 = params[0] - (int32)M->actor_xyz.x;
|
|
sub2 = params[1] - (int32)M->actor_xyz.z;
|
|
|
|
if (sub1 < 0)
|
|
sub1 = (0 - sub1);
|
|
if (sub2 < 0)
|
|
sub2 = (0 - sub2);
|
|
|
|
len = sub1 + sub2;
|
|
|
|
session_barriers->Set_route_barrier_mask((int32)M->actor_xyz.x - len, (int32)M->actor_xyz.x + len, (int32)M->actor_xyz.z - len, (int32)M->actor_xyz.z + len);
|
|
route_res = Setup_route(result, params[0], params[1], params[2], __FULL, FALSE8);
|
|
session_barriers->Clear_route_barrier_mask();
|
|
if (!route_res) {
|
|
if (!result) { // failed
|
|
Setup_route(result, params[0], params[1], params[2], __LASER, FALSE8); // must succeed
|
|
} else {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return IR_CONT; // finished so send script on
|
|
}
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_laser_route(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to specified x1,z1 from the same or adjoining floor rects
|
|
// player or mega
|
|
// doesnt end on stand
|
|
|
|
// params 0 x INT32's to be cast to floats :| hmmm...
|
|
// 1 z
|
|
// 2 0='walk', else 'run'
|
|
// 3 end on stand (i.e. tiny or sharp)
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy()) {
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
if (!Setup_route(result, params[0], params[1], params[2], __LASER, params[3])) {
|
|
L->looping = 0;
|
|
return (IR_CONT);
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_route_to_nico(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to nico x1,z1 from the same or adjoining floor rects
|
|
// walk to a nico marker
|
|
// player or mega
|
|
|
|
// params 0 nico
|
|
// 1 0='walk', else 'run'
|
|
// 2 0=no turn-on-spot 1=yes
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
_feature_info *monica;
|
|
|
|
const char *nico_name = nullptr;
|
|
if (params && params[0]) {
|
|
nico_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
}
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy()) {
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
monica = (_feature_info *)features->Try_fetch_item_by_name(nico_name);
|
|
|
|
if (!monica)
|
|
Fatal_error("fn_route_to_nico - object [%s] cant find nico [%s]", object->GetName(), nico_name);
|
|
|
|
// build route
|
|
if (!Setup_route(result, (int32)monica->x, (int32)monica->z, params[1], __FULL, TRUE8)) {
|
|
if (result == 0)
|
|
Message_box("fn_route_to_nico nico found but route failed");
|
|
|
|
L->looping = 0;
|
|
return (IR_CONT);
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_interact_near_mega(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to nico x1,z1 from the same or adjoining floor rects
|
|
// walk to a nico marker
|
|
// player or mega
|
|
|
|
// params
|
|
// 0 0='walk', else 'run'
|
|
// 1 0=no turn-on-spot 1=yes
|
|
// 2 distance within which we stop
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
PXreal sub1, sub2, len;
|
|
|
|
// check we are within the distance and stop us if so
|
|
sub1 = logic_structs[M->target_id]->mega->actor_xyz.x - M->actor_xyz.x;
|
|
sub2 = logic_structs[M->target_id]->mega->actor_xyz.z - M->actor_xyz.z;
|
|
|
|
// dist
|
|
len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy()) {
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
// dont even build route if too close already
|
|
if (len < (PXreal)(params[2] * params[2])) {
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// build route
|
|
if (!Setup_route(result, (int32)logic_structs[M->target_id]->mega->actor_xyz.x, (int32)logic_structs[M->target_id]->mega->actor_xyz.z, params[0], __FULL, TRUE8)) {
|
|
if (result == 0)
|
|
Message_box("fn_route_to_nico nico found but route failed");
|
|
|
|
L->looping = 0;
|
|
return (IR_CONT);
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
if (len < (PXreal)(params[2] * params[2])) {
|
|
M->m_main_route.dist_left = FLOAT_ZERO;
|
|
M->m_main_route.current_position = (M->m_main_route.total_points - 1);
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_spectre_route_to_mega(int32 &result, int32 *params) {
|
|
// auto-route a mega character to named mega to within specified distance - rebuild the route if the target moves away from original position
|
|
|
|
// params 0 mega object name
|
|
// 1 0='walk', else 'run'
|
|
// 2 0=no turn-on-spot 1=yes
|
|
// 3 distance within which we stop
|
|
// 4 re-route trigger distance
|
|
|
|
PXreal sub1, sub2, len, len2 = REAL_ONE;
|
|
uint32 id;
|
|
#define SPECTRE_INITIAL_ROUTE_TWEEK 4242
|
|
#define SPECTRE_ROUTE_TWEEK 4269
|
|
int32 tx;
|
|
int32 tz;
|
|
bool8 route_res;
|
|
|
|
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
// get object to check
|
|
id = objects->Fetch_item_number_by_name(mega_name);
|
|
if (id == 0xffffffff)
|
|
Fatal_error("fn_spectre_route_to_mega - illegal object [%s]", mega_name);
|
|
|
|
// check for starting off on a different floor - route straight to player
|
|
if ((!L->looping) && (L->owner_floor_rect != logic_structs[id]->owner_floor_rect)) {
|
|
L->list[1] = SPECTRE_INITIAL_ROUTE_TWEEK;
|
|
// build route
|
|
if (!Setup_route(result, (int32)logic_structs[id]->mega->actor_xyz.x, (int32)logic_structs[id]->mega->actor_xyz.z, params[1], __ENDB, 0)) { // sharp
|
|
if (result == 0)
|
|
Message_box("fn_spectre_route_to_mega route failed");
|
|
return IR_CONT;
|
|
}
|
|
}
|
|
|
|
// check we are within the distance and stop us if so
|
|
sub1 = logic_structs[id]->mega->actor_xyz.x - M->actor_xyz.x;
|
|
sub2 = logic_structs[id]->mega->actor_xyz.z - M->actor_xyz.z;
|
|
len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
// are we close enough to finish here?
|
|
if (len < (PXreal)(params[3] * params[3])) {
|
|
L->looping = 0;
|
|
return IR_CONT; // yeah
|
|
}
|
|
|
|
if (L->owner_floor_rect == logic_structs[id]->owner_floor_rect) {
|
|
L->list[0] = cur_history; // cancel tweeky catch up mode
|
|
L->list[1] = 0; // cancel follow modes
|
|
}
|
|
|
|
// see if the target has moved
|
|
// get distance from targets original position
|
|
if (L->looping) {
|
|
sub1 = logic_structs[id]->mega->actor_xyz.x - M->pushed_actor_xyz.x;
|
|
sub2 = logic_structs[id]->mega->actor_xyz.z - M->pushed_actor_xyz.z;
|
|
len2 = (PXreal)((sub1 * sub1) + (sub2 * sub2));
|
|
}
|
|
|
|
// normal same floor following
|
|
if ((L->owner_floor_rect == logic_structs[id]->owner_floor_rect) && ((!L->looping) || (len2 > (PXreal)(params[4] * params[4])))) {
|
|
M->pushed_actor_xyz.x = logic_structs[id]->mega->actor_xyz.x;
|
|
M->pushed_actor_xyz.z = logic_structs[id]->mega->actor_xyz.z;
|
|
M->reverse_route = FALSE8;
|
|
|
|
// build route
|
|
// get target coords
|
|
tx = (int32)logic_structs[id]->mega->actor_xyz.x;
|
|
tz = (int32)logic_structs[id]->mega->actor_xyz.z;
|
|
|
|
// route with a mask - the mask will be the retrigger dist; so should be fine but will occlude some in huge rooms
|
|
session_barriers->Set_route_barrier_mask((int32)tx - params[4], (int32)tx + params[4], (int32)tz - params[4], (int32)tz + params[4]);
|
|
route_res = Setup_route(result, tx, tz, params[1], __FULL, 0);
|
|
session_barriers->Clear_route_barrier_mask();
|
|
|
|
if (!route_res) { // sharp
|
|
if (result == 0) {
|
|
if (!Setup_route(result, (int32)logic_structs[id]->mega->actor_xyz.x, (int32)logic_structs[id]->mega->actor_xyz.z, params[1], __LASER, 0)) {
|
|
if (result == 0) {
|
|
L->looping = 0;
|
|
return IR_CONT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
// check for different floors - route to players exit point - nice straight line
|
|
if ((!L->list[1]) && (L->owner_floor_rect != logic_structs[id]->owner_floor_rect)) {
|
|
// was on same floor but now gone
|
|
// go to coordinate where he left this room
|
|
|
|
L->list[1] = SPECTRE_ROUTE_TWEEK;
|
|
if (!Setup_route(result, (int32)spectre_hist[L->owner_floor_rect].x, (int32)spectre_hist[L->owner_floor_rect].z, params[1], __LASER, 0)) { // sharp
|
|
if (result == 0)
|
|
Message_box("fn_spectre_route_to_mega route failed");
|
|
|
|
L->looping = 0;
|
|
return IR_CONT;
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
|
|
// just finished special to-door route - now do a super route through door
|
|
if (L->list[1] == SPECTRE_ROUTE_TWEEK) {
|
|
// player went and we're now at the door where he left
|
|
|
|
L->list[0] = L->list[0] + 1;
|
|
if (L->list[0] == MAX_player_history)
|
|
L->list[0] = 0; // wrap
|
|
|
|
// do a super route to cross floor
|
|
// use a mask - we can, because the coordinate is at the door
|
|
tx = (int32)history[L->list[0]].first_x;
|
|
tz = (int32)history[L->list[0]].first_z;
|
|
session_barriers->Set_route_barrier_mask((int32)tx - 200, (int32)tx + 200, (int32)tz - 200, (int32)tz + 200);
|
|
route_res = Setup_route(result, tx, tz, params[1], __ENDB, 0);
|
|
session_barriers->Clear_route_barrier_mask();
|
|
|
|
if (!route_res) { // sharp
|
|
if (result == 0) {
|
|
if (!Setup_route(result, (int32)history[L->list[0]].first_x, (int32)history[L->list[0]].first_z, params[1], __LASER, 0)) { // sharp
|
|
if (result == 0) {
|
|
L->looping = 0;
|
|
return IR_CONT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
// not looping any int32er
|
|
L->list[1] = 0; // clear initail-route-tweek
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return IR_CONT; // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_sharp_route_to_near_mega(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to nico x1,z1 from the same or adjoining floor rects
|
|
// walk to a nico marker
|
|
// player or mega
|
|
|
|
// params 0 mega object name
|
|
// 1 0='walk', else 'run'
|
|
// 2 0=no turn-on-spot 1=yes
|
|
// 3 distance within which we stop
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
return Route_to_near_mega_core(mega_name, params[1], params[2], params[3], FALSE8, result);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_route_to_near_mega(int32 &result, int32 *params) {
|
|
// auto-route a mega character characters x,z to nico x1,z1 from the same or adjoining floor rects
|
|
// walk to a nico marker
|
|
// player or mega
|
|
|
|
// params 0 mega object name
|
|
// 1 0='walk', else 'run'
|
|
// 2 0=no turn-on-spot 1=yes
|
|
// 3 distance within which we stop
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
return Route_to_near_mega_core(mega_name, params[1], params[2], params[3], TRUE8, result);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::Route_to_near_mega_core(const char *name, int32 run, int32 initial_turn, uint32 dist, int32 end_on_stand, int32 &result) {
|
|
PXreal sub1, sub2, x, z;
|
|
uint32 id;
|
|
_feature_info *monica;
|
|
bool8 route_res;
|
|
int32 tx;
|
|
int32 tz;
|
|
int32 len, mask_len;
|
|
|
|
// get object to check
|
|
|
|
monica = (_feature_info *)features->Try_fetch_item_by_name(name);
|
|
if (monica) {
|
|
x = monica->x;
|
|
z = monica->z;
|
|
|
|
} else {
|
|
id = objects->Fetch_item_number_by_name(name);
|
|
if (id == 0xffffffff)
|
|
Fatal_error("[%s] calling Route_to_near_mega_core - finds neither object or nico named [%s]", object->GetName(), name);
|
|
// found mega with name!
|
|
// check we are within the distance and stop us if so
|
|
|
|
x = logic_structs[id]->mega->actor_xyz.x;
|
|
z = logic_structs[id]->mega->actor_xyz.z;
|
|
}
|
|
|
|
sub1 = x - M->actor_xyz.x;
|
|
sub2 = z - M->actor_xyz.z;
|
|
|
|
// dist
|
|
len = (int32)((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy())
|
|
return IR_REPEAT;
|
|
|
|
// dont even build route if too close already
|
|
if (len < (int32)(dist * dist)) {
|
|
result = TRUE8;
|
|
L->looping = 0;
|
|
return IR_CONT; // finished so send script on
|
|
}
|
|
|
|
// route mask length - accuracy not required
|
|
|
|
if (sub1 < REAL_ZERO)
|
|
sub1 = (REAL_ZERO - sub1);
|
|
if (sub2 < REAL_ZERO)
|
|
sub2 = (REAL_ZERO - sub2);
|
|
|
|
mask_len = (int32)(sub1 + sub2);
|
|
|
|
// build route
|
|
// apply a route mask
|
|
tx = (int32)x;
|
|
tz = (int32)z;
|
|
session_barriers->Set_route_barrier_mask((int32)tx - mask_len, (int32)tx + mask_len, (int32)tz - mask_len, (int32)tz + mask_len);
|
|
route_res = Setup_route(result, (int32)x, (int32)z, run, __FULL, end_on_stand);
|
|
session_barriers->Clear_route_barrier_mask();
|
|
|
|
if (!route_res) { // no route built
|
|
if (!result) { // failed
|
|
Setup_route(result, (int32)x, (int32)z, run, __LASER, end_on_stand); // must succeed
|
|
} else {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return IR_CONT; // finished so send script on
|
|
}
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return IR_CONT; // finished so send script on
|
|
}
|
|
|
|
if (len < (PXreal)(dist * dist)) {
|
|
M->m_main_route.dist_left = FLOAT_ZERO;
|
|
M->m_main_route.current_position = (M->m_main_route.total_points - 1);
|
|
|
|
if (!end_on_stand) {
|
|
L->looping = 0;
|
|
return IR_CONT;
|
|
}
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_route_to_marker(int32 &result, int32 *params) {
|
|
// walk to a nico marker
|
|
// player or mega
|
|
|
|
// params 0 name of marker
|
|
// 1 0='walk', else 'run'
|
|
// 2 0=no turn-on-spot 1=yes
|
|
|
|
// return IR_CONT or
|
|
// IR_REPEAT
|
|
|
|
_map_marker *marker;
|
|
|
|
const char *marker_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
if (L->looping < 2) {
|
|
// check for free router
|
|
if (Is_router_busy()) {
|
|
return IR_REPEAT;
|
|
}
|
|
|
|
marker = (_map_marker *)markers.Fetch_marker_by_object_name(marker_name);
|
|
|
|
if (!marker)
|
|
Fatal_error("fn_route_to_marker - object [%s] cant find marker [%s]", object->GetName(), marker_name);
|
|
|
|
// build route
|
|
if (!Setup_route(result, (int32)marker->x, (int32)marker->z, params[1], __FULL, TRUE8)) {
|
|
L->looping = 0;
|
|
return (IR_CONT);
|
|
}
|
|
}
|
|
|
|
// animate the route
|
|
if (Process_route()) {
|
|
// not looping any int32er
|
|
L->looping = 0;
|
|
result = TRUE8;
|
|
return (IR_CONT); // finished so send script on
|
|
}
|
|
|
|
// not finished, so see you next cycle
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
// Changed last two parameters to int32 from bool tostop a performance warning. Other bools are passed as int anyway,
|
|
// so this is more consistent and faster.
|
|
bool8 _game_session::Setup_route(int32 &result, int32 corex, int32 corez, int32 runs, __rtype type, int32 end_on_stand) {
|
|
// setup and create the route
|
|
|
|
// returns false if we should return to script - because route failed or is not required
|
|
// true if we need to now go on to animate along the route
|
|
|
|
// result=
|
|
// TRUE - no route required
|
|
// FALSE- route failed
|
|
|
|
// only one of these per cycle
|
|
Set_router_busy();
|
|
|
|
// stop people routing when armed
|
|
if ((cur_id == player.Fetch_player_id()) && (M->Fetch_armed_status())) {
|
|
// tell it no route is required
|
|
result = TRUE8;
|
|
return (FALSE8);
|
|
}
|
|
|
|
// set up walk or run
|
|
M->m_main_route.___init();
|
|
|
|
// set walk or run
|
|
if (!runs) {
|
|
M->m_main_route.request_form.anim_type = __WALK;
|
|
Set_motion(__MOTION_WALK); // for safety
|
|
} else {
|
|
M->m_main_route.request_form.anim_type = __RUN;
|
|
Set_motion(__MOTION_RUN); // for safety
|
|
}
|
|
|
|
// quick CAPS check on the anim
|
|
if ((!L->voxel_info->IsAnimTable(L->cur_anim_type)))
|
|
Fatal_error("mega [%s] has anim [%s] missing", object->GetName(), master_anim_name_table[L->cur_anim_type].name);
|
|
|
|
// new route do prepare a route request form!
|
|
// initial x,z
|
|
M->m_main_route.request_form.initial_x = M->actor_xyz.x;
|
|
M->m_main_route.request_form.initial_z = M->actor_xyz.z;
|
|
|
|
// target x,z
|
|
M->m_main_route.request_form.dest_x = (PXreal)corex;
|
|
M->m_main_route.request_form.dest_z = (PXreal)corez;
|
|
|
|
// need characters y coordinate also
|
|
M->m_main_route.request_form.character_y = M->actor_xyz.y;
|
|
|
|
// this function attempts to finish on stand
|
|
M->m_main_route.request_form.finish_on_stand = (end_on_stand == 0) ? FALSE8 : TRUE8;
|
|
|
|
M->m_main_route.request_form.finish_on_null_stand = FALSE8; // special mode for interactions
|
|
|
|
// set type
|
|
M->m_main_route.request_form.rtype = ROUTE_points_only;
|
|
|
|
// now log and create the initial route
|
|
Create_initial_route(type);
|
|
|
|
// if the route could not be built
|
|
if (M->m_main_route.request_form.error == __ROUTE_REQUEST_PRIM_FAILED) {
|
|
result = FALSE8;
|
|
return (FALSE8);
|
|
}
|
|
|
|
if (M->m_main_route.request_form.error == __RR_NO_ROUTE_REQUIRED) {
|
|
result = TRUE8;
|
|
return (FALSE8);
|
|
}
|
|
|
|
// we are now looping, having done the init
|
|
L->looping = 2;
|
|
|
|
return (TRUE8);
|
|
}
|
|
|
|
uint32 _game_session::Animate_points(_route_description *route) {
|
|
// animate to each point in turn
|
|
|
|
// look at the characters available anims to judge the frames to use - this has a degree of flexibility
|
|
|
|
// compute the pan and distance of the current line
|
|
|
|
// return 0 still going
|
|
// 1 we're finished
|
|
|
|
PXreal sub1, sub2;
|
|
PXreal xnext, znext;
|
|
PXreal x, z;
|
|
PXreal dist;
|
|
|
|
// have we any further to go on the current segment?
|
|
if (route->dist_left == FLOAT_ZERO) {
|
|
Zdebug(" end of seg");
|
|
// check for end of route
|
|
if (route->current_position == (route->total_points - 1)) {
|
|
Zdebug("end of route!");
|
|
|
|
route->arrived = TRUE8;
|
|
|
|
// its the end
|
|
return (1);
|
|
}
|
|
|
|
// advance to the next line segment
|
|
route->current_position++;
|
|
|
|
// calc distance to travel and set the pan value (force the pan for now)
|
|
|
|
Calc_dist_and_pan(M->actor_xyz.x, M->actor_xyz.z, route);
|
|
|
|
// if new pan is acute we may wish to GOSUB to a turn anim?
|
|
}
|
|
|
|
//if not current __WALK then choose nearest leg position from __WALK frame set
|
|
|
|
if (L->cur_anim_type != route->request_form.anim_type) {
|
|
// starting anew so softly into walk anim
|
|
|
|
Soften_up_anim_file(route->request_form.anim_type, 1000000);
|
|
L->cur_anim_type = route->request_form.anim_type;
|
|
}
|
|
|
|
// advance frame and motion - clipping distance if over the end
|
|
|
|
// work out how far we wish to move
|
|
// open the motion anim file
|
|
ANIM_CHECK(L->cur_anim_type);
|
|
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(L->voxel_info->get_info_name(L->cur_anim_type), L->voxel_info->info_name_hash[L->cur_anim_type], L->voxel_info->base_path,
|
|
L->voxel_info->base_path_hash); //
|
|
|
|
if (L->anim_pc + 1 >= pAnim->frame_qty)
|
|
Fatal_error("Animate_points finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(),
|
|
(const char *)L->voxel_info->get_info_name(L->cur_anim_type), L->anim_pc, pAnim->frame_qty);
|
|
|
|
// get motion displacement from currently displayed frame to next one
|
|
// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
|
|
PXreal x1, z1, x2, z2, unused;
|
|
PXFrameEnOfAnim(L->anim_pc + 1, pAnim)->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
|
|
PXFrameEnOfAnim(L->anim_pc, pAnim)->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
|
|
|
|
xnext = x1 - x2;
|
|
znext = z1 - z2;
|
|
|
|
// calculate the new x and z coordinate from this frames motion offset
|
|
// do the z and x together
|
|
PXfloat ang = L->pan * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
|
|
z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
|
|
|
|
// work out how far we are from the end
|
|
|
|
// if over the end then clip the distance
|
|
// we do this by
|
|
// working out how far on we go this time
|
|
// and if that is further than the distance left then we clip
|
|
|
|
// work out how far the next frame moves us
|
|
|
|
sub1 = x - M->actor_xyz.x;
|
|
sub2 = z - M->actor_xyz.z;
|
|
dist = (PXreal)PXsqrt((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
if (dist > route->dist_left) {
|
|
// this would take us too far so clip at the end point
|
|
M->actor_xyz.x = route->prim_route[route->current_position].x;
|
|
M->actor_xyz.z = route->prim_route[route->current_position].z;
|
|
|
|
// set to finished
|
|
route->dist_left = FLOAT_ZERO;
|
|
|
|
// in theory, we must now do extra correctional work on the actor.pos.x,z thing?
|
|
} else {
|
|
// ok, use the desired motion
|
|
M->actor_xyz.x = x;
|
|
M->actor_xyz.z = z;
|
|
|
|
// we've moved this much further
|
|
route->dist_left -= dist;
|
|
}
|
|
|
|
// advance the frame
|
|
L->anim_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void _game_session::Advance_auto_pan() {
|
|
// move the pan closer to target
|
|
|
|
PXfloat turn;
|
|
|
|
if (!M->turn_dir)
|
|
turn = -(FULL_TURN / 10); // 0.1f;
|
|
else
|
|
turn = (FULL_TURN / 10); // 0.1f;
|
|
|
|
if (PXfabs(turn) >= M->target_pan) {
|
|
// too far so snap
|
|
L->auto_panning = FALSE8;
|
|
M->target_pan = ZERO_TURN; // we're done
|
|
L->pan = M->auto_target_pan; // used for things like interact - where we just auto turn to target
|
|
} else {
|
|
L->auto_display_pan += turn;
|
|
M->target_pan = M->target_pan - (PXfloat)PXfabs(turn); // distance left to travel
|
|
}
|
|
|
|
if (L->auto_display_pan >= HALF_TURN)
|
|
L->auto_display_pan -= FULL_TURN;
|
|
else if (L->auto_display_pan <= -HALF_TURN)
|
|
L->auto_display_pan += FULL_TURN; // stop the wrap
|
|
}
|
|
|
|
void _game_session::Calc_dist_and_pan(PXreal x, PXreal z, _route_description *route) {
|
|
// setup the next segment to route down
|
|
// calc 'dist_left'
|
|
|
|
PXreal sub1, sub2;
|
|
|
|
// get length
|
|
sub1 = x - route->prim_route[route->current_position].x;
|
|
sub2 = z - route->prim_route[route->current_position].z;
|
|
|
|
route->dist_left = (PXreal)PXsqrt((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
// calculate the facing pan down this track
|
|
|
|
L->auto_panning = FALSE8; // none
|
|
M->target_pan = ZERO_TURN; // none
|
|
|
|
PXfloat new_pan, diff;
|
|
|
|
if (!M->reverse_route)
|
|
new_pan = PXAngleOfVector(route->prim_route[route->current_position].z - z, route->prim_route[route->current_position].x - x);
|
|
else
|
|
new_pan = PXAngleOfVector(z - route->prim_route[route->current_position].z, x - route->prim_route[route->current_position].x);
|
|
|
|
// get difference between the two
|
|
diff = new_pan - L->pan;
|
|
|
|
if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
|
|
// work out which way to turn
|
|
if (diff > HALF_TURN)
|
|
diff -= FULL_TURN;
|
|
else if (diff < -HALF_TURN)
|
|
diff += FULL_TURN;
|
|
|
|
// diff is now the distance to turn by and its sign denotes direction
|
|
|
|
if (diff < FLOAT_ZERO)
|
|
M->turn_dir = 0; // right
|
|
else
|
|
M->turn_dir = 1; // left
|
|
|
|
M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
|
|
L->auto_display_pan = L->pan; // start where we currently are
|
|
L->auto_panning = TRUE8;
|
|
M->auto_target_pan = new_pan; // need this here as its snapped to at end of auto-pan cycle
|
|
}
|
|
|
|
// normal pan set regardless for movement
|
|
L->pan = new_pan;
|
|
}
|
|
|
|
void _game_session::Calc_dist_and_target_pan(PXreal x, PXreal z, _route_description *route) {
|
|
// usually called to setup the first segment to route down
|
|
// will set target turn-to pan if current pan and pan of route are too far apart for a pan snap
|
|
// calc 'dist_left'
|
|
// checks for anim CAPS should have already been done before we are called
|
|
|
|
PXreal sub1, sub2;
|
|
|
|
// cancel any previous
|
|
L->auto_panning = FALSE8;
|
|
|
|
// get length
|
|
sub1 = x - route->prim_route[route->current_position].x;
|
|
sub2 = z - route->prim_route[route->current_position].z;
|
|
|
|
route->dist_left = (PXreal)PXsqrt((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
Calc_target_pan_no_bones(route->prim_route[route->current_position].x, route->prim_route[route->current_position].z, x, z);
|
|
}
|
|
|
|
void _game_session::Start_new_router_game_cycle() {
|
|
// called at start of game cycle - this allows one route to be built during the cycle
|
|
router_busy = FALSE8;
|
|
}
|
|
|
|
void _game_session::Set_router_busy() {
|
|
// a route has been built - so dont allow anymore
|
|
|
|
router_busy = TRUE8;
|
|
}
|
|
|
|
bool8 _game_session::Is_router_busy() {
|
|
// has a route been built?
|
|
|
|
if (first_session_cycle)
|
|
return FALSE8;
|
|
|
|
return router_busy;
|
|
}
|
|
|
|
} // End of namespace ICB
|