mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 18:31:37 +00:00
1470 lines
52 KiB
C++
1470 lines
52 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "engines/icb/p4.h"
|
|
#include "engines/icb/common/px_common.h"
|
|
#include "engines/icb/common/px_floor_map.h"
|
|
#include "engines/icb/common/px_linkeddatafile.h"
|
|
#include "engines/icb/common/px_route_barriers.h"
|
|
#include "engines/icb/common/px_prop_anims.h"
|
|
#include "engines/icb/mission.h"
|
|
#include "engines/icb/session.h"
|
|
#include "engines/icb/debug.h"
|
|
#include "engines/icb/floors.h"
|
|
#include "engines/icb/barriers.h"
|
|
#include "engines/icb/global_objects.h"
|
|
#include "engines/icb/res_man.h"
|
|
|
|
#include "common/textconsole.h"
|
|
|
|
namespace ICB {
|
|
|
|
// how much you get nudge along a nudge barrier per cycle
|
|
#define NUDGE_DISTANCE (10)
|
|
|
|
// How close you have to get to barriers to consider a collision
|
|
// Note : the run anim has a delta movement of 30cm hence the 35cm figure
|
|
#define BARRIER_CLOSE (20 * FLOAT_ONE)
|
|
#define RUN_BARRIER_CLOSE (35 * FLOAT_ONE)
|
|
|
|
// Only do fort-knox solution on barriers closer than this distance
|
|
#define IGNORE_BARRIER_CLOSE (100 * FLOAT_ONE)
|
|
|
|
// made bigger than barrier close so we hit stairs before atual barriers that surround them
|
|
#define STAIR_CLOSE 25 * FLOAT_ONE
|
|
#define LADDER_TOP_CLOSE 50 * FLOAT_ONE
|
|
|
|
// Ignore barriers that are more than this away from players height
|
|
#define BARRIER_TOO_HIGH (100 * REAL_ONE)
|
|
|
|
// above this angle the mega is blocked, below this angle it is aligned
|
|
#define BARRIER_TOLERANCE ((FULL_TURN * 70) / 360) // 70 deg
|
|
|
|
// For Sony testing make these variables for altering at run-time
|
|
// in long term they will become constants
|
|
|
|
// How much you get reflected away from a barrier when you are aligned with it
|
|
PXreal REPEL_TURN = ((FULL_TURN * 6) / 360); // 6 deg
|
|
|
|
// how much you get repulsed away from a barrier per cycle when you
|
|
// are being aligned with it
|
|
PXreal REPULSE_DISTANCE = (15 * REAL_ONE); // 15 cm
|
|
|
|
__barrier_result _game_session::Check_barrier_bump_and_bounce(PXreal newx, PXreal newy, PXreal newz, PXreal oldx, PXreal /* oldy */, PXreal oldz, bool8 pl) {
|
|
// see if the coordinates passed are close to the players current barriers
|
|
// returns 0 no barrier found thats too near
|
|
// 1 a barrier was too close
|
|
|
|
_route_barrier *bar;
|
|
PXreal pdist, dist;
|
|
uint32 j;
|
|
PXfloat barrier_tolerance = BARRIER_TOLERANCE; // 1/8 of a turn = 45 degress
|
|
PXfloat diff;
|
|
int32 ignoreThis;
|
|
|
|
// 1 = means don't do fort knox solution on this barrier
|
|
// 0 = means do fort knox solution on this barrier
|
|
int32 ignoreBarrier[MAX_barriers];
|
|
|
|
PXreal bar_close = BARRIER_CLOSE; // changes if player running or walking
|
|
|
|
if (pl) {
|
|
// is player running
|
|
if (Get_motion() == __MOTION_RUN) // running
|
|
bar_close = RUN_BARRIER_CLOSE;
|
|
|
|
// check for stair entry complience - oh yes
|
|
for (j = 0; j < num_stairs; j++) {
|
|
|
|
bar = &stairs[j].bar;
|
|
|
|
// blocked if stair/ladder is disabled
|
|
if (!stairs[j].live)
|
|
continue;
|
|
|
|
if (newy != bar->bottom())
|
|
continue; // not on our floor so continue with next
|
|
|
|
pdist = ((newx * bar->bcm().lpx()) + (newz * bar->bcm().lpz())) - bar->bcm().linedist();
|
|
|
|
if (((PXfloat)PXfabs(pdist) < STAIR_CLOSE) || // stair
|
|
((!stairs[j].is_stair) && (!stairs[j].up) && ((PXfloat)PXfabs(pdist) < LADDER_TOP_CLOSE))) { // top of stairs
|
|
// we are near the plane so now we must check the end points
|
|
// check the left end of the line
|
|
dist = ((newx * bar->bcm().alpx()) + (newz * bar->bcm().alpz())) - bar->bcm().alinedist();
|
|
|
|
// check the right end
|
|
if (dist >= 0) {
|
|
dist = ((newx * bar->bcm().blpx()) + (newz * bar->bcm().blpz())) - bar->bcm().blinedist();
|
|
|
|
if (dist >= 0) {
|
|
// ok, its a hit
|
|
// we need to crash into the barrier if we're crouched
|
|
|
|
if (M->Is_crouched())
|
|
return __BLOCKED;
|
|
|
|
// and crash if we're armed
|
|
if (M->Fetch_armed_status())
|
|
return __BLOCKED;
|
|
|
|
// must be walking or running upright - so off we go
|
|
|
|
diff = L->pan - stairs[j].pan;
|
|
// correct
|
|
if (diff > HALF_TURN)
|
|
diff -= FULL_TURN;
|
|
else if (diff < -HALF_TURN)
|
|
diff += FULL_TURN;
|
|
|
|
if (PXfabs(diff) < (FULL_TURN / 10)) { // 36 deg = +/- 18 deg
|
|
L->pan = stairs[j].pan;
|
|
MS->player.stair_num = (uint8)j; // actual stair index number
|
|
MS->player.stair_unit = 0; // units into cycle
|
|
MS->player.stair_dir = stairs[j].up; // 1 is up, 0 is down
|
|
MS->player.begun_at_bottom = stairs[j].up; // 1 is started at bottom
|
|
MS->player.was_climbing = FALSE8; // no y movement into the cycle
|
|
|
|
if (stairs[j].is_stair) { // stair or ladder
|
|
M->on_stairs = TRUE8; // for shadow correction system
|
|
|
|
// stairs
|
|
if (Get_motion() == __MOTION_RUN) // running
|
|
MS->player.Set_player_status(RUNNING_ON_STAIRS);
|
|
|
|
else
|
|
MS->player.Set_player_status(ON_STAIRS);
|
|
|
|
if (MS->player.stair_dir) // going up
|
|
MS->player.step_sample_num = 0; // starting at bottom so sampling starts at 0
|
|
else
|
|
MS->player.step_sample_num = TOP_stair_num; // starting at top
|
|
|
|
// set the stairway coordinate correction system
|
|
for (uint32 k = 0; k < MAX_stair_length; k++)
|
|
MS->player.step_samples[k].stepped_on_step = FALSE8;
|
|
} else { // ladder
|
|
MS->player.left_right = 0;
|
|
if (MS->player.stair_dir) { // going up
|
|
#define SNAP_BACK_DOWN 70
|
|
#define SNAP_BACK_UP 25
|
|
Snap_to_ladder(&stairs[j], SNAP_BACK_UP);
|
|
MS->player.was_climbing = TRUE8; // into anim has movement
|
|
MS->player.Easy_start_new_mode(ON_LADDER, __CORD_STAND_TO_CLIMB_UP_LADDER);
|
|
} else {
|
|
M->drawShadow = FALSE8; // shadows off
|
|
Snap_to_ladder(&stairs[j], SNAP_BACK_DOWN);
|
|
camera_lock = TRUE8; // stop rough room cut through effect
|
|
MS->player.Easy_start_new_mode(BEGIN_DOWN_LADDER, __STAND_TO_CLIMB_DOWN_LADDER_RIGHT);
|
|
}
|
|
return (__NUDGED);
|
|
}
|
|
return (__OK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for collision with nudge barriers
|
|
for (j = 0; j < M->number_of_nudge; j++) {
|
|
int32 b = Fetch_megas_nudge_barrier_number(j);
|
|
bar = session_barriers->Fetch_barrier(b);
|
|
|
|
pdist = ((newx * bar->bcm().lpx()) + (newz * bar->bcm().lpz())) - bar->bcm().linedist();
|
|
if ((PXfloat)PXfabs(pdist) < BARRIER_CLOSE) {
|
|
// we are near the plane so now we must check the end points
|
|
// check the left end of the line
|
|
dist = ((newx * bar->bcm().alpx()) + (newz * bar->bcm().alpz())) - bar->bcm().alinedist();
|
|
// check the right end
|
|
// Make barrier a bit longer to nudge player through the doorway nicely
|
|
if (dist > -bar_close) {
|
|
dist = ((newx * bar->bcm().blpx()) + (newz * bar->bcm().blpz())) - bar->bcm().blinedist();
|
|
// Make barrier a bit longer to nudge player through the doorway nicely
|
|
if (dist > -bar_close) {
|
|
// check angle - narrow ones are ignored
|
|
|
|
PXfloat delta = remainder(L->pan - bar->pan(), FULL_TURN, HALF_TURN);
|
|
PXfloat delta2 = delta;
|
|
|
|
if (delta < -QUARTER_TURN)
|
|
delta2 += HALF_TURN;
|
|
if (delta > QUARTER_TURN)
|
|
delta2 -= HALF_TURN;
|
|
PXfloat fd = (PXfloat)PXfabs(delta2);
|
|
if (fd >= barrier_tolerance) {
|
|
// ok, we are close to the barrier and at an acceptable angle - now nudge along
|
|
// work out pan of barrier
|
|
|
|
// we have our coordinate and a direction to shift in
|
|
PXfloat ang = bar->pan() * TWO_PI;
|
|
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
// Let's ignore the zero*something as it is always zero
|
|
M->actor_xyz.x += PXfloat2PXreal(NUDGE_DISTANCE * REAL_ONE * sang);
|
|
M->actor_xyz.z += PXfloat2PXreal(NUDGE_DISTANCE * REAL_ONE * cang);
|
|
|
|
return (__NUDGED); // we've avoided the hit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // nudge for loop
|
|
} // if player
|
|
|
|
adjusted_pan = FLOAT_ZERO;
|
|
made_adjust = FALSE8;
|
|
normalAngle = 3 * FULL_TURN; // place holder to mean not set
|
|
int32 nFortKnox = 0;
|
|
uint32 nBarriers = (M->number_of_barriers + M->number_of_animating);
|
|
|
|
for (j = 0; j < nBarriers; j++) {
|
|
int32 b = Fetch_megas_barrier_number(j);
|
|
bar = session_barriers->Fetch_barrier(b);
|
|
ignoreBarrier[j] = 1;
|
|
|
|
if ((PXfloat)PXfabs(newy - bar->bottom()) > BARRIER_TOO_HIGH)
|
|
continue; // ignore abars that are now too high
|
|
|
|
__barrier_result result = Check_this_barrier(bar, newx, newz, oldx, oldz, bar_close, &ignoreThis);
|
|
|
|
// Ignore barrier culling isn't working - so do them all except animating barriers
|
|
ignoreThis = 0;
|
|
|
|
ignoreBarrier[j] = ignoreThis;
|
|
if (ignoreThis == 0)
|
|
nFortKnox++;
|
|
|
|
if (result != __OK)
|
|
return (result);
|
|
}
|
|
if ((!made_adjust) && (MS->player.player_status != RUNNING) && (MS->player.player_status != WALKING)) {
|
|
// didnt hit a normal barrier so check if we hit a stair or ladder barrier
|
|
|
|
// check for stair entry complience - oh yes
|
|
for (j = 0; j < num_stairs; j++) {
|
|
bar = &stairs[j].bar;
|
|
|
|
if (newy != bar->bottom())
|
|
continue; // not on our floor so continue with next
|
|
|
|
__barrier_result result = Check_this_barrier(bar, newx, newz, oldx, oldz, bar_close, &ignoreThis);
|
|
if (result != __OK)
|
|
return (result);
|
|
}
|
|
}
|
|
|
|
// if we hit a single corectable barrier then we can make that adjustment now
|
|
int32 repulsed = 0;
|
|
PXfloat destx = FLOAT_ZERO;
|
|
PXfloat destz = FLOAT_ZERO;
|
|
|
|
if (made_adjust) {
|
|
L->pan = adjusted_pan;
|
|
|
|
if (normalAngle < 2 * FULL_TURN) {
|
|
// Repulse the mega back a bit
|
|
PXfloat ang = normalAngle * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
// Let's ignore the zero*something as it is always zero
|
|
// the stored angle normalAngle is a normal angle pointing away
|
|
// from the barrier by 90 deg AND towards the players side of the
|
|
// barrier
|
|
repulsed = 1;
|
|
destx = M->actor_xyz.x + PXfloat2PXreal(REPULSE_DISTANCE * REAL_ONE * sang);
|
|
destz = M->actor_xyz.z + PXfloat2PXreal(REPULSE_DISTANCE * REAL_ONE * cang);
|
|
}
|
|
} else {
|
|
destx = newx;
|
|
destz = newz;
|
|
}
|
|
|
|
// Right so finally do a line intersection between the old position and the "final" new position
|
|
// old position is : oldx, oldz
|
|
// new position is : destx, destz
|
|
int32 hit = 0;
|
|
|
|
// Special treatment for the player
|
|
if (pl) {
|
|
for (j = 0; j < nBarriers; j++) {
|
|
int32 b = Fetch_megas_barrier_number(j);
|
|
bar = session_barriers->Fetch_barrier(b);
|
|
|
|
// Ignore barriers which are in the ignore list
|
|
if (ignoreBarrier[j] == 1)
|
|
continue;
|
|
|
|
hit = troute.Get_intersect(oldx, oldz, destx, destz, bar->x1(), bar->z1(), bar->x2(), bar->z2());
|
|
|
|
if (hit == 1) {
|
|
warning("Player crossed the line nBars %d nFortKnox %d : player %f %f -> %f %f bar: %f %f -> %f %f", nBarriers, nFortKnox, oldx, oldz, destx, destz,
|
|
bar->x1(), bar->z1(), bar->x2(), bar->z2());
|
|
break;
|
|
}
|
|
}
|
|
// Oh dear we went through a barrier
|
|
if (hit == 1)
|
|
return (__BLOCKED); // conflict, so finish
|
|
}
|
|
|
|
// The repulsed position looks good - so use it !
|
|
if (repulsed) {
|
|
M->actor_xyz.x = destx;
|
|
M->actor_xyz.z = destz;
|
|
}
|
|
|
|
// Return the correct values
|
|
if (made_adjust)
|
|
return (__CORRECTED);
|
|
|
|
// finally, if player is still looking good then do a check for him hitting other megas
|
|
if (pl) {
|
|
/*
|
|
/
|
|
mega /
|
|
position : mx, mz /
|
|
* /
|
|
/
|
|
/
|
|
/
|
|
/
|
|
/
|
|
/
|
|
/
|
|
/
|
|
/ - his pan direction
|
|
#
|
|
player @ position : px, pz direction: pan
|
|
|
|
dx = PXsin( pan )
|
|
dz = PXcos( pan )
|
|
|
|
then the mega is on the left-hand side of the line (or on the line)
|
|
if ( (dz * ( mx - px )) <= ( dx * ( mz - pz )) )
|
|
else
|
|
// he is on the right-hand side of the line
|
|
*/
|
|
|
|
static int32 total_adjusts = 0;
|
|
|
|
if ((!total_adjusts) && (MS->player.interact_selected) && (logic_structs[MS->player.cur_interact_id]->image_type == VOXEL)) {
|
|
// player is highlighting a mega
|
|
|
|
// check nearness
|
|
|
|
PXreal sub1, sub2;
|
|
|
|
sub1 = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.x - M->actor_xyz.x;
|
|
sub2 = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.z - M->actor_xyz.z;
|
|
|
|
// dist
|
|
PXreal distance = ((sub1 * sub1) + (sub2 * sub2));
|
|
|
|
// near
|
|
if (distance < (120 * 120)) {
|
|
|
|
if ((MS->player.cur_state.momentum != __FORWARD_1) && (MS->player.cur_state.momentum != __FORWARD_2))
|
|
return __OK;
|
|
|
|
PXreal dx = (PXreal)PXsin(L->pan * TWO_PI);
|
|
PXreal dz = (PXreal)PXcos(L->pan * TWO_PI);
|
|
|
|
PXreal mx = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.x;
|
|
PXreal mz = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.z;
|
|
|
|
if ((dz * (mx - M->actor_xyz.x)) <= (dx * (mz - M->actor_xyz.z))) {
|
|
// right
|
|
if ((distance > (50 * 50)) && (distance < (120 * 120)))
|
|
L->pan += 0.03f;
|
|
} else {
|
|
// left
|
|
if ((distance > (50 * 50)) && (distance < (120 * 120)))
|
|
L->pan -= 0.03f;
|
|
}
|
|
|
|
total_adjusts++;
|
|
|
|
return (__OK);
|
|
}
|
|
}
|
|
total_adjusts = 0; // reset
|
|
}
|
|
return (__OK);
|
|
}
|
|
|
|
__barrier_result _game_session::Check_this_barrier(_route_barrier *bar, PXreal newx, PXreal newz, PXreal /* oldx */, PXreal /* oldz */, PXreal bar_close, int32 *ignoreThis) {
|
|
PXfloat delta;
|
|
PXfloat delta2;
|
|
PXfloat barrier_tolerance = BARRIER_TOLERANCE; // 1/8 of a turn = 45 degress
|
|
PXreal pdist, dist;
|
|
PXreal ignore_bar_close = IGNORE_BARRIER_CLOSE;
|
|
|
|
*ignoreThis = 1;
|
|
|
|
pdist = ((newx * bar->bcm().lpx()) + (newz * bar->bcm().lpz())) - bar->bcm().linedist();
|
|
|
|
if ((PXfloat)PXfabs(pdist) < bar_close) {
|
|
// we are near the plane so now we must check the end points
|
|
// check the left end of the line
|
|
|
|
dist = ((newx * bar->bcm().alpx()) + (newz * bar->bcm().alpz())) - bar->bcm().alinedist();
|
|
|
|
// check the right end
|
|
if (dist >= 0) {
|
|
dist = ((newx * bar->bcm().blpx()) + (newz * bar->bcm().blpz())) - bar->bcm().blinedist();
|
|
|
|
if (dist >= 0) {
|
|
*ignoreThis = 0;
|
|
|
|
// we are going to hit this barrier
|
|
// but, if the angle is narrow we can aquire the barriers pan and continue unmolested
|
|
delta = remainder(L->pan - bar->pan(), FULL_TURN, HALF_TURN);
|
|
delta2 = delta;
|
|
|
|
if (delta < -QUARTER_TURN)
|
|
delta2 += HALF_TURN;
|
|
if (delta > QUARTER_TURN)
|
|
delta2 -= HALF_TURN;
|
|
if (PXfabs(delta2) < barrier_tolerance) {
|
|
if (made_adjust)
|
|
return (__BLOCKED); // conflict, so finish
|
|
|
|
if ((delta > QUARTER_TURN) || (delta < -QUARTER_TURN)) {
|
|
adjusted_pan = remainder(bar->pan() + HALF_TURN, FULL_TURN, HALF_TURN);
|
|
} else {
|
|
adjusted_pan = bar->pan();
|
|
}
|
|
made_adjust = TRUE8;
|
|
if (adjusted_pan > L->pan) {
|
|
adjusted_pan += REPEL_TURN;
|
|
} else if (adjusted_pan < L->pan) {
|
|
adjusted_pan -= REPEL_TURN;
|
|
}
|
|
if (pdist > 0)
|
|
normalAngle = bar->pan() + QUARTER_TURN;
|
|
else if (pdist < 0)
|
|
normalAngle = bar->pan() - QUARTER_TURN;
|
|
} else {
|
|
// cant adjust
|
|
return (__BLOCKED);
|
|
}
|
|
} else
|
|
*ignoreThis = 1;
|
|
} else
|
|
*ignoreThis = 1;
|
|
} else if ((PXfloat)PXfabs(pdist) < ignore_bar_close) {
|
|
*ignoreThis = 0;
|
|
}
|
|
|
|
return __OK;
|
|
}
|
|
|
|
void _barrier_handler::___init() {
|
|
_routing_slice *slice;
|
|
uint32 *num_bars;
|
|
uint32 len;
|
|
|
|
Zdebug("_barrier_handler");
|
|
Zdebug("\n+init _barrier_handler %s", MS->Fetch_session_name());
|
|
|
|
// load the raw barrier file for this session
|
|
// When clustered the session files have the base stripped
|
|
len = sprintf(temp_buf, "%s", PX_FILENAME_BARRIERLIST);
|
|
if (len > ENGINE_STRING_LEN)
|
|
Fatal_error("_barrier_handler::___init string len error");
|
|
|
|
Tdebug("barriers.txt", "%s", (const char *)temp_buf);
|
|
uint32 buf_hash = NULL_HASH;
|
|
uint32 cluster_hash = MS->Fetch_session_cluster_hash();
|
|
raw_barriers = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, MS->Fetch_session_cluster(), cluster_hash);
|
|
|
|
num_bars = (uint32 *)raw_barriers->Fetch_item_by_name("Count");
|
|
|
|
total_barriers = *(num_bars);
|
|
|
|
Tdebug("barriers.txt", "%d raw barriers", total_barriers);
|
|
|
|
// load in the routing wrapper
|
|
// When clustered the session files have the base stripped
|
|
len = sprintf(temp_buf, "%s", PX_FILENAME_ROUTING);
|
|
if (len > ENGINE_STRING_LEN)
|
|
Fatal_error("_barrier_handler::___init string len error");
|
|
|
|
Tdebug("barriers.txt", "%s", temp_buf);
|
|
buf_hash = NULL_HASH;
|
|
route_wrapper = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, MS->Fetch_session_cluster(), cluster_hash);
|
|
|
|
total_slices = route_wrapper->Fetch_number_of_items();
|
|
|
|
if (total_slices > MAX_slices)
|
|
Fatal_error("_barrier_handler::___init finds too many slices - %d but only %d allowed", total_slices, MAX_slices);
|
|
|
|
Tdebug("slice.txt", "%d routing levels", total_slices);
|
|
|
|
if (!total_slices) {
|
|
Zdebug("[%s]", (const char *)temp_buf);
|
|
Fatal_error("no parent routing levels (no parent boxes) engine cannot proceed");
|
|
}
|
|
|
|
uint32 j;
|
|
for (j = 0; j < total_slices; j++) {
|
|
slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(j);
|
|
Tdebug("slice.txt", "bottom %3.1f top %3.1f", slice->bottom, slice->top);
|
|
Tdebug("slice.txt", "%d parents", slice->num_parent_boxes);
|
|
}
|
|
|
|
// reset prop list for each
|
|
// fully reset abar systems
|
|
for (j = 0; j < MAX_slices; j++) {
|
|
anim_slices[j].num_props_in_slice = 0;
|
|
for (uint32 l = 0; l < MAX_parents_per_anim_slice; l++)
|
|
anim_slices[j].anim_parents[l] = nullptr; // unasigned pointer
|
|
}
|
|
for (j = 0; j < MAX_props; j++) {
|
|
anim_prop_info[j].barriers_per_state = 0;
|
|
anim_prop_info[j].total_states = 0;
|
|
}
|
|
for (j = 0; j < MAX_floors; j++) // reset the unassigned parents
|
|
anim_parent_table[j].num_props = 0;
|
|
parents_used = 0; // no parents have been assigned
|
|
|
|
Zdebug("anim bars");
|
|
|
|
Prepare_animating_barriers();
|
|
|
|
Zdebug("done barriers");
|
|
}
|
|
|
|
#define ADD_CHILD \
|
|
if (clist[j]->num_barriers) \
|
|
for (k = 0; k < clist[j]->num_barriers; k++) { \
|
|
bar = Fetch_barrier(clist[j]->barriers[k]); \
|
|
if (bar->bottom() == y) { \
|
|
if (barrier_mask) { \
|
|
if (MS->troute.LineIntersectsRect(mask, (int32)bar->x1(), (int32)bar->z1(), (int32)bar->x2(), (int32)bar->z2())) \
|
|
MS->troute.Add_barrier(bar); \
|
|
} else \
|
|
MS->troute.Add_barrier(bar); \
|
|
} \
|
|
}
|
|
|
|
#define EXPAND_ROUTE_BOX \
|
|
if (CHILDL < RBL) \
|
|
RBL = CHILDL; \
|
|
if (CHILDR > RBR) \
|
|
RBR = CHILDR; \
|
|
if (CHILDT < RBT) \
|
|
RBT = CHILDT; \
|
|
if (CHILDB > RBB) \
|
|
RBB = CHILDB; \
|
|
expanded_this_go++;
|
|
|
|
#define CHILDL clist[j]->left
|
|
#define CHILDR clist[j]->right
|
|
#define CHILDT clist[j]->back
|
|
#define CHILDB clist[j]->front
|
|
#define RBL rb.x1
|
|
#define RBR rb.x2
|
|
#define RBT rb.z1
|
|
#define RBB rb.z2
|
|
|
|
void _barrier_handler::Form_parent_barrier_list(PXreal x, PXreal y, PXreal z) {
|
|
// we are routing into a room - just get the parent barriers
|
|
_parent_box *endb;
|
|
uint32 parent_a, slice_a, k;
|
|
_route_barrier *bar;
|
|
uint32 *array;
|
|
|
|
endb = Fetch_parent_box_for_xyz(x, y, z, parent_a, slice_a);
|
|
if (!endb)
|
|
return; // will be because of between floor gap - not to worry
|
|
|
|
if (endb->num_barriers) {
|
|
array = (uint32 *)(((char *)endb) + (endb->barriers));
|
|
for (k = 0; k < endb->num_barriers; k++) {
|
|
bar = Fetch_barrier(array[k]);
|
|
MS->troute.Add_barrier(bar);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _barrier_handler::Form_route_barrier_list(PXreal x, PXreal y, PXreal z, PXreal x2, PXreal z2) {
|
|
// add all barriers to the prim route building system for the route x,z,x2,z2
|
|
// after this the route_manager can call the prim route builder for the final route to be made
|
|
// the restriction is that we can only route from one place to another within a single floor-rect/parent-box
|
|
// or
|
|
// from within one parent-box/floor-rect to an adjacent parent-box/floor-rect
|
|
// it is a higher level job to divide long routes up into rect to rect chunks before calling here
|
|
// but this is quite sensible anyway as we only want to be auto-routing across tiny areas at a time
|
|
|
|
_parent_box *startb;
|
|
_parent_box *endb;
|
|
_rect rb; // rb meaning 'Route-Box'
|
|
uint32 j;
|
|
_child_group *clist[MAX_child_groups_per_parent * 2];
|
|
uint32 total_childs = 0; // separate total for safety
|
|
int32 expanded_this_go;
|
|
_route_barrier *bar;
|
|
uint32 k;
|
|
uint32 parent_a, parent_b;
|
|
uint32 slice_a, slice_b;
|
|
|
|
// which floors are the start and end positions in?
|
|
// list child boxes for each floor - ignoring the parent box concept?
|
|
// build route
|
|
// add parent boundaries for all parents that the final route box intersects - !?!
|
|
|
|
// find the parent box
|
|
// first, find _parent_box for start point and end points
|
|
|
|
startb = Fetch_parent_box_for_xyz(x, y, z, parent_a, slice_a);
|
|
endb = Fetch_parent_box_for_xyz(x2, y, z2, parent_b, slice_b);
|
|
|
|
if ((!startb) && (endb))
|
|
startb = endb; // no start but end
|
|
if ((!endb) && (startb))
|
|
endb = startb; // no end but start
|
|
|
|
if (!startb)
|
|
Fatal_error("_barrier_handler::Form_route_barrier_list start and end not on floor - %s", MS->Fetch_object_name(MS->Fetch_cur_id()));
|
|
// make a list of pointers to the child groups
|
|
|
|
if (startb != endb) { // oh dear - not going to allow this anymore
|
|
Form_parent_barrier_list(x2, y, z2);
|
|
Form_parent_barrier_list(x, y, z);
|
|
return;
|
|
}
|
|
|
|
Zdebug("%3.1f %3.1f %3.1f %3.1f", startb->back, startb->left, startb->front, startb->right);
|
|
|
|
if (startb->num_childgroups) {
|
|
for (j = 0; j < startb->num_childgroups; j++) {
|
|
if (total_childs == (MAX_child_groups_per_parent * 2))
|
|
Fatal_error("_barrier_handler::Form_route_barrier_list - clist ran out of space");
|
|
clist[total_childs++] = Fetch_child_box(startb, j);
|
|
}
|
|
}
|
|
|
|
// we now know the _parent_box that our start point is on - we therefore have the list of child boxes
|
|
// same or different?
|
|
if (startb != endb) { // different
|
|
Zdebug("different parent for end box");
|
|
if (endb->num_childgroups) {
|
|
Zdebug("adding %d child boxes for end point", endb->num_childgroups);
|
|
for (j = 0; j < endb->num_childgroups; j++) {
|
|
if (total_childs == (MAX_child_groups_per_parent * 2))
|
|
Fatal_error("_barrier_handler::Form_route_barrier_list - clist ran out of space");
|
|
clist[total_childs++] = Fetch_child_box(endb, j);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ok, we now have a list of pointers to all the child groups relevant
|
|
|
|
// create the initial route box
|
|
if (x < x2) {
|
|
rb.x1 = x;
|
|
rb.x2 = x2;
|
|
} else {
|
|
rb.x1 = x2;
|
|
rb.x2 = x;
|
|
}
|
|
if (z < z2) {
|
|
rb.z1 = z;
|
|
rb.z2 = z2;
|
|
} else {
|
|
rb.z1 = z2;
|
|
rb.z2 = z;
|
|
}
|
|
|
|
// we now have our initial route box
|
|
|
|
// enter our main loop
|
|
do {
|
|
expanded_this_go = 0; // flag to say whether or no the route-box got expanded this loop
|
|
|
|
for (j = 0; j < total_childs; j++) {
|
|
if (clist[j]) {
|
|
// child not deleted
|
|
|
|
if (((CHILDR < RBL) || (CHILDL > RBR)) && ((CHILDB < RBT) || (CHILDT > RBB))) {
|
|
// child is wholly outside the route box - do nothing except prime the ELSE
|
|
} else if (((CHILDR >= RBL) && (CHILDL <= RBR)) && ((CHILDB >= RBT) && (CHILDT <= RBB))) {
|
|
// child is wholly inside route box - add it and delete child
|
|
ADD_CHILD
|
|
clist[j] = nullptr; // delete the child now it has been absorbed
|
|
} else if (((RBR >= CHILDL) && (RBL <= CHILDR)) && ((RBT >= CHILDT) && (RBB <= CHILDB))) {
|
|
// route box is wholly inside child box - expand route box, add and delete child
|
|
ADD_CHILD
|
|
clist[j] = nullptr; // delete the child now it has been absorbed
|
|
EXPAND_ROUTE_BOX
|
|
} else {
|
|
if ((CHILDL > RBL) && (CHILDL < RBR) && ((CHILDT > RBT) && (CHILDT < RBB))) {
|
|
// child top/left is within route box - expand route box, add and delete child
|
|
ADD_CHILD
|
|
clist[j] = nullptr; // delete the child now it has been absorbed
|
|
EXPAND_ROUTE_BOX
|
|
} else if ((CHILDR > RBL) && (CHILDR < RBR) && ((CHILDT > RBT) && (CHILDT < RBB))) {
|
|
// child top/right is within route box - expand route box, add and delete child
|
|
ADD_CHILD
|
|
clist[j] = nullptr; // delete the child now it has been absorbed
|
|
EXPAND_ROUTE_BOX
|
|
} else if ((CHILDL > RBL) && (CHILDL < RBR) && ((CHILDB > RBT) && (CHILDB < RBB))) {
|
|
// child bottom/left is within route box - expand route box, add and delete child
|
|
ADD_CHILD
|
|
clist[j] = nullptr; // delete the child now it has been absorbed
|
|
EXPAND_ROUTE_BOX
|
|
} else if ((CHILDR > RBL) && (CHILDR < RBR) && ((CHILDB > RBT) && (CHILDB < RBB))) {
|
|
// child bottom/right is within route box - expand route box, add and delete child
|
|
ADD_CHILD
|
|
clist[j] = nullptr; // delete the child now it has been absorbed
|
|
EXPAND_ROUTE_BOX
|
|
} else {
|
|
// we must check for our route line intersecting a horizontal and vertical child box edge
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if this loop we did not expand the route_box then we quit this DO-WHILE loop
|
|
if (!expanded_this_go)
|
|
break;
|
|
} while (1);
|
|
|
|
// if there are two parent boxes we must add the parent barriers from each to the list
|
|
if (startb != endb) {
|
|
// add both parent box barriers in
|
|
uint32 *array;
|
|
|
|
if (startb->num_barriers) {
|
|
array = (uint32 *)(((char *)startb) + (startb->barriers));
|
|
for (k = 0; k < startb->num_barriers; k++) {
|
|
bar = Fetch_barrier(array[k]);
|
|
MS->troute.Add_barrier(bar);
|
|
}
|
|
}
|
|
if (endb->num_barriers) {
|
|
array = (uint32 *)(((char *)endb) + (endb->barriers));
|
|
for (k = 0; k < endb->num_barriers; k++) {
|
|
bar = Fetch_barrier(array[k]);
|
|
MS->troute.Add_barrier(bar);
|
|
}
|
|
}
|
|
} else { // route lies within a single floor - so draw a box around it
|
|
_route_barrier newbar;
|
|
|
|
// left hand barrier
|
|
newbar.x1(startb->left);
|
|
newbar.z1(startb->back);
|
|
newbar.x2(startb->left);
|
|
newbar.z2(startb->front);
|
|
MS->troute.Add_barrier(&newbar);
|
|
|
|
// right hand barrier
|
|
newbar.x1(startb->right);
|
|
newbar.z1(startb->back);
|
|
newbar.x2(startb->right);
|
|
newbar.z2(startb->front);
|
|
MS->troute.Add_barrier(&newbar);
|
|
|
|
// top barrier
|
|
newbar.x1(startb->left);
|
|
newbar.z1(startb->back);
|
|
newbar.x2(startb->right);
|
|
newbar.z2(startb->back);
|
|
MS->troute.Add_barrier(&newbar);
|
|
|
|
// bottom barrier
|
|
newbar.x1(startb->left);
|
|
newbar.z1(startb->front);
|
|
newbar.x2(startb->right);
|
|
newbar.z2(startb->front);
|
|
MS->troute.Add_barrier(&newbar);
|
|
}
|
|
}
|
|
|
|
_parent_box *_barrier_handler::Fetch_parent_num_on_slice_y(uint32 requested_parent, PXreal y) {
|
|
// fetch the parent of the number passed for a given y level
|
|
// ie 0 means first, 1 means second, etc
|
|
// this is called by the plan-viewer which just keeps asking for the next one until we say there are no more
|
|
// by passing back a 0 instead of a pointer to a parent
|
|
static _routing_slice *slice;
|
|
uint32 cur_slice = 0;
|
|
|
|
// first time in so compute the slice
|
|
if (!requested_parent) {
|
|
while (1) {
|
|
slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(cur_slice);
|
|
|
|
if ((y >= slice->bottom) && (y < slice->top))
|
|
break;
|
|
|
|
// safety
|
|
cur_slice++;
|
|
if (cur_slice == total_slices) // if so then must be last slice :O
|
|
Fatal_error("Fetch_parent_num_on_slice_y ran out of slices");
|
|
|
|
// next
|
|
slice++;
|
|
}
|
|
}
|
|
|
|
// ok, we have the slice
|
|
// return the parent of the requested number - or, 0 if there is no more
|
|
|
|
// reached total?
|
|
if (requested_parent == slice->num_parent_boxes)
|
|
return (nullptr);
|
|
|
|
// simply return the pointer
|
|
|
|
return ((_parent_box *)(((uint8 *)slice) + slice->parent_boxes[requested_parent]));
|
|
}
|
|
|
|
_route_barrier *_barrier_handler::Fetch_barrier(uint32 num) {
|
|
// return a pointer to numbered barrier
|
|
_route_barrier *bar;
|
|
|
|
assert(num < total_barriers);
|
|
|
|
if (num >= total_barriers)
|
|
Fatal_error("illegal barrier request %d", num);
|
|
|
|
bar = (_route_barrier *)raw_barriers->Fetch_item_by_name("Data");
|
|
|
|
return &bar[num];
|
|
}
|
|
|
|
_parent_box *_barrier_handler::Fetch_parent_box_for_xyz(PXreal x, PXreal y, PXreal z, uint32 &par_num, uint32 &slice_num) {
|
|
// return a pointer to the parent box of a point in world space
|
|
// returns 0 if the point does not lie within a parent box
|
|
|
|
_routing_slice *slice = nullptr;
|
|
_parent_box *parent = nullptr;
|
|
|
|
// find correct slice according to height
|
|
// fetch first
|
|
|
|
slice_num = 0;
|
|
|
|
while (1) {
|
|
slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(slice_num);
|
|
|
|
if ((y >= slice->bottom) && (y < slice->top))
|
|
break;
|
|
|
|
// safety
|
|
slice_num++;
|
|
if (slice_num == total_slices) { // if so then must be last slice :O
|
|
Fatal_error("_barrier_handler::Fetch_parent_box_for_xyz ran out of slices: object [%s] (%3.1f %3.1f %3.1f) has an "
|
|
"illegal marker",
|
|
MS->Fetch_object_name(MS->Fetch_cur_id()), x, y, z);
|
|
}
|
|
// next
|
|
slice++;
|
|
}
|
|
|
|
// ok, we found the right y slice
|
|
// now find the right parent box
|
|
if (!slice->num_parent_boxes)
|
|
Fatal_error("_barrier_handler::Fetch_parent_box_for_xyz slice has no parent boxes");
|
|
|
|
for (par_num = 0; par_num < slice->num_parent_boxes; par_num++) {
|
|
parent = (_parent_box *)(((uint8 *)slice) + slice->parent_boxes[par_num]);
|
|
|
|
// do we lie within the box?
|
|
if ((x > parent->left) && (x < parent->right) && (z > parent->back) && (z < parent->front)) {
|
|
return (parent);
|
|
}
|
|
}
|
|
|
|
return (nullptr);
|
|
}
|
|
|
|
void _game_session::Prepare_megas_route_barriers(bool8 pl) {
|
|
// see which parent box we're owned by
|
|
// if different from previous fetch all the barriers for the new parent
|
|
// this system is custom for the player object - routing megas use their own system
|
|
// this routine fecthes the 'special' player only line-of-sight barriers too
|
|
|
|
_parent_box *par = nullptr;
|
|
_child_group *pchild;
|
|
uint32 total_childs;
|
|
uint32 j, k;
|
|
uint32 *list;
|
|
_route_barrier *bar;
|
|
uint32 parent_number;
|
|
_routing_slice *slice;
|
|
PXreal x, y, z;
|
|
|
|
x = M->actor_xyz.x;
|
|
y = floor_def->Return_true_y(M->actor_xyz.y);
|
|
z = M->actor_xyz.z;
|
|
|
|
// on previous slice?
|
|
slice = (_routing_slice *)session_barriers->route_wrapper->Fetch_item_by_number(M->cur_slice);
|
|
if ((y >= slice->bottom) && (y < slice->top) && (M->cur_parent))
|
|
if ((x > M->cur_parent->left) && (x < M->cur_parent->right) && (z > M->cur_parent->back) && (z < M->cur_parent->front)) {
|
|
// nothing has changed
|
|
Prepare_megas_abarriers(M->cur_slice, M->par_number);
|
|
|
|
// player should have added the barriers of stood megas
|
|
if (pl)
|
|
Fetch_mega_barriers_for_player();
|
|
|
|
return;
|
|
}
|
|
|
|
M->cur_slice = 0;
|
|
while (1) {
|
|
slice = (_routing_slice *)session_barriers->route_wrapper->Fetch_item_by_number(M->cur_slice);
|
|
if ((y >= slice->bottom) && (y < slice->top))
|
|
break;
|
|
|
|
// safety
|
|
M->cur_slice++;
|
|
if (M->cur_slice == session_barriers->total_slices) { // if so then must be last slice :O
|
|
M->cur_slice--;
|
|
slice = (_routing_slice *)session_barriers->route_wrapper->Fetch_item_by_number(M->cur_slice);
|
|
break;
|
|
}
|
|
}
|
|
// ok, we found the right y slice
|
|
// now find the right parent box
|
|
if (!slice->num_parent_boxes)
|
|
Fatal_error("Prepare_megas_route_barriers slice has no parent boxes");
|
|
|
|
for (parent_number = 0; parent_number < slice->num_parent_boxes; parent_number++) {
|
|
par = (_parent_box *)(((uint8 *)slice) + slice->parent_boxes[parent_number]);
|
|
|
|
// do we lie within the box?
|
|
if ((x > par->left) && (x < par->right) && (z > par->back) && (z < par->front)) {
|
|
break; // found
|
|
}
|
|
}
|
|
|
|
if (parent_number == slice->num_parent_boxes) {
|
|
// not on a legal position - can happen
|
|
M->cur_parent = nullptr; // null pointer
|
|
M->number_of_barriers = 0;
|
|
M->number_of_nudge = 0;
|
|
M->number_of_animating = 0;
|
|
return;
|
|
}
|
|
|
|
// has a parent box different from the current one been found?
|
|
{
|
|
// new one
|
|
M->cur_parent = par;
|
|
M->par_number = parent_number;
|
|
|
|
// reset list of barrier ids
|
|
M->number_of_barriers = 0;
|
|
M->number_of_animating = 0;
|
|
|
|
// firstly, drag out the parents bounding barriers if it has any
|
|
if (par->num_barriers) {
|
|
list = (uint32 *)((uint8 *)par + par->barriers);
|
|
// get all the barriers
|
|
for (j = 0; j < par->num_barriers; j++) {
|
|
// fetch each barrier and check that its bottom edge is on the floor - otherwise we ignore it
|
|
bar = session_barriers->Fetch_barrier(*(list));
|
|
if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
|
|
// ok, this barrier is on the floor so we add it to our list
|
|
M->barrier_list[M->number_of_barriers++] = *(list++);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pl)
|
|
M->number_of_nudge = 0; // reset now - in case there are none
|
|
|
|
// now player only specials
|
|
if ((pl) && (par->num_specials)) {
|
|
#ifdef VERBOSE
|
|
Zdebug("getting player specials");
|
|
#endif
|
|
|
|
list = (uint32 *)((uint8 *)par + par->specials);
|
|
// get all the barriers
|
|
for (j = 0; j < par->num_specials; j++) {
|
|
// fetch each barrier and check that its bottom edge is on the floor - otherwise we ignore it
|
|
bar = session_barriers->Fetch_barrier(*(list));
|
|
|
|
if (bar->material() == VIEW_FIELD) {
|
|
if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
|
|
// ok, this barrier is on the floor so we add it to our list
|
|
M->barrier_list[M->number_of_barriers++] = *(list++);
|
|
}
|
|
} else if (bar->material() >= LEFT_NUDGE) {
|
|
if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
|
|
// ok, this barrier is on the floor so we add it to our list
|
|
M->nudge_list[M->number_of_nudge++] = *(list++);
|
|
}
|
|
} else {
|
|
Fatal_error("illegal barrier [%d], special list - type %d, x1 %3.2f, x2 %3.2f, z1 %3.2f, z2 %3.2f", *(list), bar->material(), bar->x1(),
|
|
bar->x2(), bar->z1(), bar->z2());
|
|
}
|
|
}
|
|
}
|
|
|
|
// find out have many child boxes this parent has
|
|
total_childs = session_barriers->Fetch_number_of_child_boxes(par);
|
|
|
|
for (j = 0; j < total_childs; j++) {
|
|
pchild = session_barriers->Fetch_child_box(par, j);
|
|
|
|
for (k = 0; k < pchild->num_barriers; k++) {
|
|
bar = session_barriers->Fetch_barrier(pchild->barriers[k]);
|
|
if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
|
|
M->barrier_list[M->number_of_barriers++] = pchild->barriers[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now build the animating barrier list - regardless of whether or not the parent has changed
|
|
Prepare_megas_abarriers(M->cur_slice, M->par_number);
|
|
|
|
if (M->number_of_barriers + M->number_of_animating > MAX_bars)
|
|
Fatal_error("[%s] finds too many barriers - found %d + %d animating, total max %d", object->GetName(), M->number_of_barriers, M->number_of_animating, MAX_bars);
|
|
|
|
if (M->number_of_nudge > MAX_bars)
|
|
Fatal_error("too many player nudge barriers");
|
|
}
|
|
|
|
void _barrier_handler::Prepare_animating_barriers() {
|
|
// the nightmare that is animating barriers
|
|
// search through all prop animations getting out the animating barriers - checking for duplicates
|
|
|
|
#define MAX_anim_barriers 400
|
|
uint32 j;
|
|
uint16 barrier_table[MAX_anim_barriers];
|
|
uint32 total_anim_bars = 0;
|
|
_route_barrier *bar;
|
|
_routing_slice *slice;
|
|
_parent_box *parent;
|
|
uint32 cur_slice = 0;
|
|
uint32 l, f, pbar_num;
|
|
uint32 abar_index = 0;
|
|
|
|
Tdebug("anim_barriers.txt", "Preparing animating barriers");
|
|
|
|
for (j = 0; j < MS->prop_anims->Fetch_number_of_items(); j++) {
|
|
Tdebug("anim_barriers.txt", "\n%d %s", j, MS->prop_anims->Fetch_items_name_by_number(j));
|
|
|
|
_animating_prop *index;
|
|
_animation_entry *anim;
|
|
|
|
index = (_animating_prop *)MS->prop_anims->Fetch_item_by_number(j);
|
|
|
|
Tdebug("anim_barriers.txt", " has %d anims", index->num_anims);
|
|
|
|
// loop through all looking for our named anim
|
|
if (index->num_anims) {
|
|
// get the prop number
|
|
|
|
for (uint32 k = 0; k < index->num_anims; k++) {
|
|
anim = (_animation_entry *)(((char *)index) + index->anims[k]);
|
|
Tdebug("anim_barriers.txt", " '%s' - %d frames", (((char *)index) + anim->name), anim->num_frames);
|
|
|
|
if (anim->num_barriers_per_frame) {
|
|
Tdebug("anim_barriers.txt", " has %d anim barriers per frame", anim->num_barriers_per_frame);
|
|
uint16 *bars = (uint16 *)(((char *)index) + anim->offset_barriers);
|
|
|
|
for (uint32 i = 0; i < (uint32)(anim->num_barriers_per_frame * anim->num_frames); i++) {
|
|
if (bars[i] != 0xffff) {
|
|
// check each barrier for duplication and add in to correct slice, parent list
|
|
|
|
for (l = 0; l < total_anim_bars; l++) {
|
|
if (barrier_table[l] == bars[i]) {
|
|
Tdebug("anim_barriers.txt", " %d in list already - index %d", bars[i], i);
|
|
break; // found this bar
|
|
}
|
|
}
|
|
// i is barrier index
|
|
|
|
bar = Fetch_barrier(bars[i]);
|
|
|
|
if (l == total_anim_bars) { // didnt find in list
|
|
Tdebug("anim_barriers.txt", " new barrier %d x%3.2f y%3.2f z%3.2f", bars[i], bar->x1(), bar->bottom(),
|
|
bar->z1());
|
|
|
|
barrier_table[total_anim_bars++] = (uint16)bars[i]; // write the bar down
|
|
|
|
if (total_anim_bars == MAX_anim_barriers)
|
|
Fatal_error("Prepare_animating_barriers finds too many barriers "
|
|
"for scratch table");
|
|
}
|
|
cur_slice = 0;
|
|
|
|
do {
|
|
slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(cur_slice);
|
|
|
|
if ((bar->bottom() >= slice->bottom) && (bar->bottom() < slice->top))
|
|
break;
|
|
|
|
cur_slice++;
|
|
} while (cur_slice != total_slices);
|
|
|
|
if (cur_slice == total_slices) { // if so then must be last slice :O
|
|
Tdebug("anim_barriers.txt", " **ran out of slices :O\n");
|
|
} else {
|
|
Tdebug("anim_barriers.txt", " exists on slice %d", cur_slice);
|
|
|
|
// check legality of number of parents
|
|
if (slice->num_parent_boxes > MAX_parents_per_anim_slice)
|
|
Fatal_error("prepare anim barriers finds too many parents in slice "
|
|
"- %d parents",
|
|
slice->num_parent_boxes);
|
|
|
|
int32 prop_number =
|
|
MS->objects->Fetch_item_number_by_name((const char *)MS->prop_anims->Fetch_items_name_by_number(j));
|
|
|
|
if (prop_number == -1) {
|
|
Tdebug("anim_barriers.txt", " !!associated prop [%s] not a game object - so ignoring",
|
|
(const char *)MS->prop_anims->Fetch_items_name_by_number(j));
|
|
} else {
|
|
// now find parent the barrier would belong to
|
|
for (f = 0; f < slice->num_parent_boxes; f++) {
|
|
Tdebug("anim_barriers.txt", " check parent %d", f);
|
|
|
|
parent = (_parent_box *)(((uint8 *)slice) + slice->parent_boxes[f]);
|
|
|
|
// do we lie within the box?
|
|
if ((bar->x1() > parent->left) && (bar->x1() < parent->right) && (bar->z1() > parent->back) &&
|
|
(bar->z1() < parent->front)) {
|
|
char *props_name = (char *)MS->prop_anims->Fetch_items_name_by_number(j);
|
|
uint32 props_number = MS->objects->Fetch_item_number_by_name(props_name);
|
|
|
|
if (!anim_slices[cur_slice].anim_parents[f]) {
|
|
anim_slices[cur_slice].anim_parents[f] = &anim_parent_table[parents_used++];
|
|
Tdebug("anim_barriers.txt", " new aparent");
|
|
}
|
|
|
|
pbar_num = anim_slices[cur_slice].anim_parents[f]->num_props;
|
|
|
|
uint32 n;
|
|
for (n = 0; n < pbar_num; n++)
|
|
if (anim_slices[cur_slice].anim_parents[f]->prop_number[n] == props_number)
|
|
break;
|
|
|
|
if (n == pbar_num) { // new
|
|
anim_slices[cur_slice].anim_parents[f]->num_props++;
|
|
|
|
if (pbar_num == MAX_props_per_parent)
|
|
Fatal_error("too many props in "
|
|
"parent - max = %d",
|
|
MAX_props_per_parent);
|
|
|
|
Tdebug("anim_barriers.txt", " found - slice %d, parent %d "
|
|
"[%d bars so far], prop [%s, %d] state "
|
|
"%d\n",
|
|
cur_slice, f, anim_slices[cur_slice].anim_parents[f]->num_props, props_name,
|
|
props_number, anim->frames[i / anim->num_barriers_per_frame]);
|
|
|
|
anim_slices[cur_slice].anim_parents[f]->prop_number[pbar_num] = (uint8)props_number;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (f == slice->num_parent_boxes)
|
|
Tdebug("anim_barriers.txt", " !!barrier not located within a parent box!!\n");
|
|
}
|
|
}
|
|
} else
|
|
Tdebug("anim_barriers.txt", " !!barrier is type 0xffffffff");
|
|
}
|
|
} else
|
|
Tdebug("anim_barriers.txt", " 0 anim bars");
|
|
}
|
|
}
|
|
}
|
|
|
|
// now reverse engineer the target lists
|
|
for (j = 0; j < total_slices; j++) {
|
|
Tdebug("anim_barriers.txt", " slice %d", j);
|
|
|
|
for (uint32 k = 0; k < MAX_parents_per_anim_slice; k++) {
|
|
if (!anim_slices[j].anim_parents[k])
|
|
Tdebug("anim_barriers.txt", " par %d free", k);
|
|
else {
|
|
Tdebug("anim_barriers.txt", " parent %d has %d animating props", k, anim_slices[j].anim_parents[k]->num_props);
|
|
for (uint32 i = 0; i < anim_slices[j].anim_parents[k]->num_props; i++)
|
|
Tdebug("anim_barriers.txt", " prop num %d", anim_slices[j].anim_parents[k]->prop_number[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Tdebug("anim_barriers.txt", "\n\n\n\n\n--------preparing prop slice format---------------\n");
|
|
|
|
uint32 total_states, i;
|
|
|
|
// now compute the special prop table for run-time LOS injection
|
|
// first reset special prop fixed list
|
|
for (j = 0; j < MAX_props; j++)
|
|
anim_prop_info[j].barriers_per_state = 0; // do it here so it can double up as 0 being an unused entry
|
|
|
|
for (j = 0; j < MS->prop_anims->Fetch_number_of_items(); j++) {
|
|
Tdebug("anim_barriers.txt", "\n**%d %s", j, MS->prop_anims->Fetch_items_name_by_number(j));
|
|
_animating_prop *index;
|
|
_animation_entry *anim;
|
|
|
|
index = (_animating_prop *)MS->prop_anims->Fetch_item_by_number(j);
|
|
|
|
// loop through all looking for our named anim
|
|
if (index->num_anims) {
|
|
// got a prop
|
|
// it has anims
|
|
// is it a prop with animating barriers?
|
|
|
|
// get first anim
|
|
anim = (_animation_entry *)(((char *)index) + index->anims[0]);
|
|
|
|
// does it have anim barriers
|
|
if (anim->num_barriers_per_frame) {
|
|
// ok, we're on
|
|
|
|
Tdebug("anim_barriers.txt", " has %d anims and %d per frame", index->num_anims, anim->num_barriers_per_frame);
|
|
|
|
// reset our total number of states for this prop counter
|
|
total_states = 0;
|
|
|
|
// find the first legal barrier in this anim
|
|
uint16 *bars = (uint16 *)(((char *)index) + anim->offset_barriers);
|
|
i = 0;
|
|
while ((bars[i] == 0xffff) && (i < (uint32)(anim->num_barriers_per_frame * anim->num_frames)))
|
|
i++;
|
|
|
|
// better make sure the anim has a legal barrier - if not then assume the prop is well knackered and leave
|
|
// it out of the anim barrier system
|
|
if (i < (uint32)(anim->num_barriers_per_frame * anim->num_frames)) {
|
|
// get our chosen sample barrier
|
|
bar = Fetch_barrier(bars[i]);
|
|
|
|
// now compute slice for sample barrier
|
|
cur_slice = 0;
|
|
do {
|
|
slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(cur_slice);
|
|
if ((bar->bottom() >= slice->bottom) && (bar->bottom() < slice->top))
|
|
break;
|
|
cur_slice++;
|
|
} while (cur_slice != total_slices);
|
|
|
|
// check for not on a slice - not good
|
|
if (cur_slice == total_slices) { // if so then must be last slice :O
|
|
Tdebug("anim_barriers.txt", " **ran out of slices :O\n");
|
|
} else {
|
|
Tdebug("anim_barriers.txt", " sample bar puts prop in slice %d", cur_slice);
|
|
|
|
// get the prop number
|
|
uint32 prop_number = MS->objects->Fetch_item_number_by_name((const char *)MS->prop_anims->Fetch_items_name_by_number(j));
|
|
|
|
// gotta check for anims with no equivelent game objects
|
|
if (prop_number != 0xffffffff) {
|
|
Tdebug("anim_barriers.txt", " listing as prop number %d", anim_slices[cur_slice].num_props_in_slice);
|
|
// ok, add the prop to the slices list of props
|
|
anim_slices[cur_slice].prop_list[anim_slices[cur_slice].num_props_in_slice++] = (uint8)prop_number;
|
|
|
|
if (anim_slices[cur_slice].num_props_in_slice >= MAX_props)
|
|
Fatal_error("form anim barrier list found too many props in the slice");
|
|
|
|
// set number of barriers per frame for this prop
|
|
anim_prop_info[prop_number].barriers_per_state = anim->num_barriers_per_frame; // set from our sample first anim
|
|
|
|
// now loop through all the anims filling in the state table
|
|
// in theory, having done this, there will be entries for each state referenced
|
|
for (uint32 k = 0; k < index->num_anims; k++) {
|
|
// get anim k
|
|
anim = (_animation_entry *)(((char *)index) + index->anims[k]);
|
|
|
|
// get anims list of barriers
|
|
uint16 *barriers = (uint16 *)(((char *)index) + anim->offset_barriers);
|
|
|
|
// go through each frame/state
|
|
for (uint32 c = 0; c < anim->num_frames; c++) {
|
|
// get state
|
|
uint32 state = anim->frames[c];
|
|
|
|
// keep computing the total number of states for this prop
|
|
if (state > total_states)
|
|
total_states = state;
|
|
|
|
// for each state go through all the barriers per frame/state
|
|
for (i = 0; i < anim->num_barriers_per_frame; i++) {
|
|
// gotta check for funny illegal barriers
|
|
if (barriers[(c * anim->num_barriers_per_frame) + i] != 0xffff) {
|
|
// legal barrier
|
|
barrier_table[(state * anim->num_barriers_per_frame) + i] =
|
|
(uint16)barriers[(c * anim->num_barriers_per_frame) + i];
|
|
} else {
|
|
// dodgy barrier
|
|
// setting to barrier 0 but this is not a proper
|
|
// solution
|
|
barrier_table[(state * anim->num_barriers_per_frame) + i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// ok, we've set up the anim barriers for this prop
|
|
|
|
Tdebug("anim_barriers.txt", " prop [%s] highest state %d, %d bars per state",
|
|
(const char *)MS->prop_anims->Fetch_items_name_by_number(j), total_states,
|
|
anim_prop_info[prop_number].barriers_per_state);
|
|
|
|
// note down total states
|
|
anim_prop_info[prop_number].total_states = (uint8)(total_states + 1);
|
|
|
|
// create space for table
|
|
Tdebug("anim_barriers.txt", "prop %d needs %d uint16's", prop_number,
|
|
(total_states + 1) * anim_prop_info[prop_number].barriers_per_state);
|
|
|
|
if (abar_index >= MAX_prop_abars)
|
|
Fatal_error("too many animating barriers - current max = %d", MAX_prop_abars);
|
|
|
|
// move the barrier table
|
|
memcpy(&prop_abar_table[abar_index], barrier_table,
|
|
(sizeof(uint16) * ((total_states + 1) * anim_prop_info[prop_number].barriers_per_state)));
|
|
|
|
anim_prop_info[prop_number].barrier_list = &prop_abar_table[abar_index];
|
|
|
|
abar_index += ((total_states + 1) * anim_prop_info[prop_number].barriers_per_state);
|
|
|
|
} else { // if prop number legal
|
|
Tdebug("anim_barriers.txt", " prop has no object - ignoring");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Tdebug("anim_barriers.txt", " ");
|
|
|
|
for (j = 0; j < MS->Fetch_number_of_objects(); j++)
|
|
if (anim_prop_info[j].barriers_per_state) {
|
|
Tdebug("anim_barriers.txt", "prop %d", j);
|
|
Tdebug("anim_barriers.txt", "prop %d [%s] has %d anim barriers, %d per frame", j, (const char *)MS->objects->Fetch_items_name_by_number(j),
|
|
anim_prop_info[j].total_states, anim_prop_info[j].barriers_per_state);
|
|
Tdebug("anim_barriers.txt", "total %d", anim_prop_info[j].barriers_per_state * (anim_prop_info[j].total_states));
|
|
for (uint16 k = 0; k < anim_prop_info[j].barriers_per_state * (anim_prop_info[j].total_states); k++)
|
|
Tdebug("anim_barriers.txt", "%d %d", k, anim_prop_info[j].barrier_list[k]);
|
|
}
|
|
|
|
Tdebug("anim_barriers.txt", "\nDone");
|
|
}
|
|
|
|
uint32 _barrier_handler::Get_anim_barriers(uint32 n, uint32 *oThisCubesBarriers, uint32 slice) {
|
|
// gives you all the 'current' anim barriers on an ENTIRE slice
|
|
// used by LOS, planview,
|
|
uint32 prop_id;
|
|
uint32 prop_state;
|
|
uint32 bars_per_state;
|
|
uint32 bar_index;
|
|
uint16 *bars;
|
|
uint32 j, k;
|
|
|
|
// how many props in this slice
|
|
uint32 num_props = anim_slices[slice].num_props_in_slice; // prop_list.GetNoItems();
|
|
|
|
for (j = 0; j < num_props; j++) {
|
|
// get id of prop
|
|
prop_id = anim_slices[slice].prop_list[j];
|
|
|
|
// get prop state
|
|
prop_state = MS->prop_state_table[prop_id];
|
|
|
|
// get number of barriers per state
|
|
bars_per_state = anim_prop_info[prop_id].barriers_per_state;
|
|
|
|
// index into barrier list to first barrier for this state
|
|
bar_index = (prop_state * bars_per_state);
|
|
|
|
bars = (uint16 *)&anim_prop_info[prop_id].barrier_list[0];
|
|
bars += bar_index;
|
|
|
|
for (k = 0; k < bars_per_state; k++) {
|
|
if (*(bars) >= total_barriers)
|
|
Fatal_error("Get_anim_barriers - illegal barrier request %d", *(bars));
|
|
|
|
oThisCubesBarriers[n++] = (uint32) * (bars++);
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
void _game_session::Prepare_megas_abarriers(uint32 slice_number, uint32 parent_number) {
|
|
// fetch abars for the current parent ONLY
|
|
M->number_of_animating = 0;
|
|
uint32 num_props;
|
|
uint32 prop_id;
|
|
uint32 prop_state;
|
|
uint32 bars_per_state;
|
|
uint32 bar_index;
|
|
uint16 *bars;
|
|
uint32 k;
|
|
|
|
if (!session_barriers->anim_slices[slice_number].anim_parents[parent_number])
|
|
return; // parent has none at all
|
|
|
|
num_props = session_barriers->anim_slices[slice_number].anim_parents[parent_number]->num_props;
|
|
|
|
for (uint32 i = 0; i < num_props; i++) {
|
|
// yup - our parent does have animating props
|
|
|
|
// get prop id on this parent
|
|
prop_id = session_barriers->anim_slices[slice_number].anim_parents[parent_number]->prop_number[i];
|
|
|
|
// get prop state
|
|
prop_state = prop_state_table[prop_id];
|
|
|
|
// get number of barriers per state
|
|
bars_per_state = session_barriers->anim_prop_info[prop_id].barriers_per_state;
|
|
|
|
// index into barrier list to first barrier for this state
|
|
bar_index = (prop_state * bars_per_state);
|
|
|
|
bars = (uint16 *)&session_barriers->anim_prop_info[prop_id].barrier_list[0];
|
|
bars += bar_index;
|
|
|
|
for (k = 0; k < bars_per_state; k++)
|
|
M->barrier_list[M->number_of_barriers + M->number_of_animating++] = (uint32) * (bars++);
|
|
}
|
|
}
|
|
|
|
void _game_session::Fetch_mega_barriers_for_player() {
|
|
// find all the megas on our floor
|
|
// then add in barriers for each of them that is stood
|
|
// add the barriers on the end of the anim barrier list which MUST have already been computed
|
|
}
|
|
|
|
} // End of namespace ICB
|