/* 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 . * */ #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