scummvm/engines/icb/route_manager.cpp
Orgad Shaneh 75bfc1290b ICB: Use nullptr
Using clang-tidy modernize-use-nullptr
2021-11-14 15:51:59 +02:00

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