1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * Additional copyright for this file:
8  * Copyright (C) 1999-2000 Revolution Software Ltd.
9  * This code is based on source code created by Revolution Software,
10  * used with permission.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  */
27 
28 #include "engines/icb/p4.h"
29 #include "engines/icb/common/px_common.h"
30 #include "engines/icb/common/px_floor_map.h"
31 #include "engines/icb/common/px_linkeddatafile.h"
32 #include "engines/icb/common/px_route_barriers.h"
33 #include "engines/icb/common/px_prop_anims.h"
34 #include "engines/icb/mission.h"
35 #include "engines/icb/session.h"
36 #include "engines/icb/debug.h"
37 #include "engines/icb/floors.h"
38 #include "engines/icb/barriers.h"
39 #include "engines/icb/global_objects.h"
40 #include "engines/icb/res_man.h"
41 
42 #include "common/textconsole.h"
43 
44 namespace ICB {
45 
46 // how much you get nudge along a nudge barrier per cycle
47 #define NUDGE_DISTANCE (10)
48 
49 // How close you have to get to barriers to consider a collision
50 // Note : the run anim has a delta movement of 30cm hence the 35cm figure
51 #define BARRIER_CLOSE (20 * FLOAT_ONE)
52 #define RUN_BARRIER_CLOSE (35 * FLOAT_ONE)
53 
54 // Only do fort-knox solution on barriers closer than this distance
55 #define IGNORE_BARRIER_CLOSE (100 * FLOAT_ONE)
56 
57 // made bigger than barrier close so we hit stairs before atual barriers that surround them
58 #define STAIR_CLOSE 25 * FLOAT_ONE
59 #define LADDER_TOP_CLOSE 50 * FLOAT_ONE
60 
61 // Ignore barriers that are more than this away from players height
62 #define BARRIER_TOO_HIGH (100 * REAL_ONE)
63 
64 // above this angle the mega is blocked, below this angle it is aligned
65 #define BARRIER_TOLERANCE ((FULL_TURN * 70) / 360) // 70 deg
66 
67 // For Sony testing make these variables for altering at run-time
68 // in long term they will become constants
69 
70 // How much you get reflected away from a barrier when you are aligned with it
71 PXreal REPEL_TURN = ((FULL_TURN * 6) / 360); // 6 deg
72 
73 // how much you get repulsed away from a barrier per cycle when you
74 // are being aligned with it
75 PXreal REPULSE_DISTANCE = (15 * REAL_ONE); // 15 cm
76 
Check_barrier_bump_and_bounce(PXreal newx,PXreal newy,PXreal newz,PXreal oldx,PXreal,PXreal oldz,bool8 pl)77 __barrier_result _game_session::Check_barrier_bump_and_bounce(PXreal newx, PXreal newy, PXreal newz, PXreal oldx, PXreal /* oldy */, PXreal oldz, bool8 pl) {
78 	// see if the coordinates passed are close to the players current barriers
79 	// returns   0 no barrier found thats too near
80 	//				1 a barrier was too close
81 
82 	_route_barrier *bar;
83 	PXreal pdist, dist;
84 	uint32 j;
85 	PXfloat barrier_tolerance = BARRIER_TOLERANCE; // 1/8 of a turn = 45 degress
86 	PXfloat diff;
87 	int32 ignoreThis;
88 
89 	// 1 = means don't do fort knox solution on this barrier
90 	// 0 = means do fort knox solution on this barrier
91 	int32 ignoreBarrier[MAX_barriers];
92 
93 	PXreal bar_close = BARRIER_CLOSE; // changes if player running or walking
94 
95 	if (pl) {
96 		// is player running
97 		if (Get_motion() == __MOTION_RUN) // running
98 			bar_close = RUN_BARRIER_CLOSE;
99 
100 		// check for stair entry complience - oh yes
101 		for (j = 0; j < num_stairs; j++) {
102 
103 			bar = &stairs[j].bar;
104 
105 			// blocked if stair/ladder is disabled
106 			if (!stairs[j].live)
107 				continue;
108 
109 			if (newy != bar->bottom())
110 				continue; // not on our floor so continue with next
111 
112 			pdist = ((newx * bar->bcm().lpx()) + (newz * bar->bcm().lpz())) - bar->bcm().linedist();
113 
114 			if (((PXfloat)PXfabs(pdist) < STAIR_CLOSE) ||                                                    // stair
115 			    ((!stairs[j].is_stair) && (!stairs[j].up) && ((PXfloat)PXfabs(pdist) < LADDER_TOP_CLOSE))) { // top of stairs
116 				// we are near the plane so now we must check the end points
117 				// check the left end of the line
118 				dist = ((newx * bar->bcm().alpx()) + (newz * bar->bcm().alpz())) - bar->bcm().alinedist();
119 
120 				// check the right end
121 				if (dist >= 0) {
122 					dist = ((newx * bar->bcm().blpx()) + (newz * bar->bcm().blpz())) - bar->bcm().blinedist();
123 
124 					if (dist >= 0) {
125 						// ok, its a hit
126 						// we need to crash into the barrier if we're crouched
127 
128 						if (M->Is_crouched())
129 							return __BLOCKED;
130 
131 						// and crash if we're armed
132 						if (M->Fetch_armed_status())
133 							return __BLOCKED;
134 
135 						// must be walking or running upright - so off we go
136 
137 						diff = L->pan - stairs[j].pan;
138 						// correct
139 						if (diff > HALF_TURN)
140 							diff -= FULL_TURN;
141 						else if (diff < -HALF_TURN)
142 							diff += FULL_TURN;
143 
144 						if (PXfabs(diff) < (FULL_TURN / 10)) { // 36 deg = +/- 18 deg
145 							L->pan = stairs[j].pan;
146 							MS->player.stair_num = (uint8)j;           // actual stair index number
147 							MS->player.stair_unit = 0;                 // units into cycle
148 							MS->player.stair_dir = stairs[j].up;       // 1 is up, 0 is down
149 							MS->player.begun_at_bottom = stairs[j].up; // 1 is started at bottom
150 							MS->player.was_climbing = FALSE8;          // no y movement into the cycle
151 
152 							if (stairs[j].is_stair) {     // stair or ladder
153 								M->on_stairs = TRUE8; // for shadow correction system
154 
155 								// stairs
156 								if (Get_motion() == __MOTION_RUN) // running
157 									MS->player.Set_player_status(RUNNING_ON_STAIRS);
158 
159 								else
160 									MS->player.Set_player_status(ON_STAIRS);
161 
162 								if (MS->player.stair_dir)               // going up
163 									MS->player.step_sample_num = 0; // starting at bottom so sampling starts at 0
164 								else
165 									MS->player.step_sample_num = TOP_stair_num; // starting at top
166 
167 								// set the stairway coordinate correction system
168 								for (uint32 k = 0; k < MAX_stair_length; k++)
169 									MS->player.step_samples[k].stepped_on_step = FALSE8;
170 							} else { // ladder
171 								MS->player.left_right = 0;
172 								if (MS->player.stair_dir) { // going up
173 #define SNAP_BACK_DOWN 70
174 #define SNAP_BACK_UP 25
175 									Snap_to_ladder(&stairs[j], SNAP_BACK_UP);
176 									MS->player.was_climbing = TRUE8; // into anim has movement
177 									MS->player.Easy_start_new_mode(ON_LADDER, __CORD_STAND_TO_CLIMB_UP_LADDER);
178 								} else {
179 									M->drawShadow = FALSE8; // shadows off
180 									Snap_to_ladder(&stairs[j], SNAP_BACK_DOWN);
181 									camera_lock = TRUE8; // stop rough room cut through effect
182 									MS->player.Easy_start_new_mode(BEGIN_DOWN_LADDER, __STAND_TO_CLIMB_DOWN_LADDER_RIGHT);
183 								}
184 								return (__NUDGED);
185 							}
186 							return (__OK);
187 						}
188 					}
189 				}
190 			}
191 		}
192 
193 		// check for collision with nudge barriers
194 		for (j = 0; j < M->number_of_nudge; j++) {
195 			int32 b = Fetch_megas_nudge_barrier_number(j);
196 			bar = session_barriers->Fetch_barrier(b);
197 
198 			pdist = ((newx * bar->bcm().lpx()) + (newz * bar->bcm().lpz())) - bar->bcm().linedist();
199 			if ((PXfloat)PXfabs(pdist) < BARRIER_CLOSE) {
200 				// we are near the plane so now we must check the end points
201 				// check the left end of the line
202 				dist = ((newx * bar->bcm().alpx()) + (newz * bar->bcm().alpz())) - bar->bcm().alinedist();
203 				// check the right end
204 				// Make barrier a bit longer to nudge player through the doorway nicely
205 				if (dist > -bar_close) {
206 					dist = ((newx * bar->bcm().blpx()) + (newz * bar->bcm().blpz())) - bar->bcm().blinedist();
207 					// Make barrier a bit longer to nudge player through the doorway nicely
208 					if (dist > -bar_close) {
209 						// check angle - narrow ones are ignored
210 
211 						PXfloat delta = remainder(L->pan - bar->pan(), FULL_TURN, HALF_TURN);
212 						PXfloat delta2 = delta;
213 
214 						if (delta < -QUARTER_TURN)
215 							delta2 += HALF_TURN;
216 						if (delta > QUARTER_TURN)
217 							delta2 -= HALF_TURN;
218 						PXfloat fd = (PXfloat)PXfabs(delta2);
219 						if (fd >= barrier_tolerance) {
220 							// ok, we are close to the barrier and at an acceptable angle - now nudge along
221 							// work out pan of barrier
222 
223 							// we have our coordinate and a direction to shift in
224 							PXfloat ang = bar->pan() * TWO_PI;
225 
226 							PXfloat cang = (PXfloat)PXcos(ang);
227 							PXfloat sang = (PXfloat)PXsin(ang);
228 
229 							// Let's ignore the zero*something as it is always zero
230 							M->actor_xyz.x += PXfloat2PXreal(NUDGE_DISTANCE * REAL_ONE * sang);
231 							M->actor_xyz.z += PXfloat2PXreal(NUDGE_DISTANCE * REAL_ONE * cang);
232 
233 							return (__NUDGED); // we've avoided the hit
234 						}
235 					}
236 				}
237 			}
238 		} // nudge for loop
239 	}         // if player
240 
241 	adjusted_pan = FLOAT_ZERO;
242 	made_adjust = FALSE8;
243 	normalAngle = 3 * FULL_TURN; // place holder to mean not set
244 	int32 nFortKnox = 0;
245 	uint32 nBarriers = (M->number_of_barriers + M->number_of_animating);
246 
247 	for (j = 0; j < nBarriers; j++) {
248 		int32 b = Fetch_megas_barrier_number(j);
249 		bar = session_barriers->Fetch_barrier(b);
250 		ignoreBarrier[j] = 1;
251 
252 		if ((PXfloat)PXfabs(newy - bar->bottom()) > BARRIER_TOO_HIGH)
253 			continue; // ignore abars that are now too high
254 
255 		__barrier_result result = Check_this_barrier(bar, newx, newz, oldx, oldz, bar_close, &ignoreThis);
256 
257 		// Ignore barrier culling isn't working - so do them all except animating barriers
258 		ignoreThis = 0;
259 
260 		ignoreBarrier[j] = ignoreThis;
261 		if (ignoreThis == 0)
262 			nFortKnox++;
263 
264 		if (result != __OK)
265 			return (result);
266 	}
267 	if ((!made_adjust) && (MS->player.player_status != RUNNING) && (MS->player.player_status != WALKING)) {
268 		// didnt hit a normal barrier so check if we hit a stair or ladder barrier
269 
270 		// check for stair entry complience - oh yes
271 		for (j = 0; j < num_stairs; j++) {
272 			bar = &stairs[j].bar;
273 
274 			if (newy != bar->bottom())
275 				continue; // not on our floor so continue with next
276 
277 			__barrier_result result = Check_this_barrier(bar, newx, newz, oldx, oldz, bar_close, &ignoreThis);
278 			if (result != __OK)
279 				return (result);
280 		}
281 	}
282 
283 	// if we hit a single corectable barrier then we can make that adjustment now
284 	int32 repulsed = 0;
285 	PXfloat destx = FLOAT_ZERO;
286 	PXfloat destz = FLOAT_ZERO;
287 
288 	if (made_adjust) {
289 		L->pan = adjusted_pan;
290 
291 		if (normalAngle < 2 * FULL_TURN) {
292 			// Repulse the mega back a bit
293 			PXfloat ang = normalAngle * TWO_PI;
294 			PXfloat cang = (PXfloat)PXcos(ang);
295 			PXfloat sang = (PXfloat)PXsin(ang);
296 
297 			// Let's ignore the zero*something as it is always zero
298 			// the stored angle normalAngle is a normal angle pointing away
299 			// from the barrier by 90 deg AND towards the players side of the
300 			// barrier
301 			repulsed = 1;
302 			destx = M->actor_xyz.x + PXfloat2PXreal(REPULSE_DISTANCE * REAL_ONE * sang);
303 			destz = M->actor_xyz.z + PXfloat2PXreal(REPULSE_DISTANCE * REAL_ONE * cang);
304 		}
305 	} else {
306 		destx = newx;
307 		destz = newz;
308 	}
309 
310 	// Right so finally do a line intersection between the old position and the "final" new position
311 	// old position is : oldx, oldz
312 	// new position is : destx, destz
313 	int32 hit = 0;
314 
315 	// Special treatment for the player
316 	if (pl) {
317 		for (j = 0; j < nBarriers; j++) {
318 			int32 b = Fetch_megas_barrier_number(j);
319 			bar = session_barriers->Fetch_barrier(b);
320 
321 			// Ignore barriers which are in the ignore list
322 			if (ignoreBarrier[j] == 1)
323 				continue;
324 
325 			hit = troute.Get_intersect(oldx, oldz, destx, destz, bar->x1(), bar->z1(), bar->x2(), bar->z2());
326 
327 			if (hit == 1) {
328 				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,
329 				        bar->x1(), bar->z1(), bar->x2(), bar->z2());
330 				break;
331 			}
332 		}
333 		// Oh dear we went through a barrier
334 		if (hit == 1)
335 			return (__BLOCKED); // conflict, so finish
336 	}
337 
338 	// The repulsed position looks good - so use it !
339 	if (repulsed) {
340 		M->actor_xyz.x = destx;
341 		M->actor_xyz.z = destz;
342 	}
343 
344 	// Return the correct values
345 	if (made_adjust)
346 		return (__CORRECTED);
347 
348 	// finally, if player is still looking good then do a check for him hitting other megas
349 	if (pl) {
350 		/*
351 		                                           /
352 		                              mega        /
353 		                position : mx, mz        /
354 		                              *         /
355 		                                       /
356 		                                      /
357 		                                     /
358 		                                    /
359 		                                   /
360 		                                  /
361 		                                 /
362 		                                /
363 		                               / - his pan direction
364 		                    #
365 		player @ position : px, pz direction: pan
366 
367 		dx = PXsin( pan )
368 		dz = PXcos( pan )
369 
370 		then the mega is on the left-hand side of the line (or on the line)
371 		if ( (dz * ( mx - px ))  <= ( dx * ( mz - pz )) )
372 		else
373 		// he is on the right-hand side of the line
374 		*/
375 
376 		static int32 total_adjusts = 0;
377 
378 		if ((!total_adjusts) && (MS->player.interact_selected) && (logic_structs[MS->player.cur_interact_id]->image_type == VOXEL)) {
379 			// player is highlighting a mega
380 
381 			// check nearness
382 
383 			PXreal sub1, sub2;
384 
385 			sub1 = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.x - M->actor_xyz.x;
386 			sub2 = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.z - M->actor_xyz.z;
387 
388 			//  dist
389 			PXreal distance = ((sub1 * sub1) + (sub2 * sub2));
390 
391 			// near
392 			if (distance < (120 * 120)) {
393 
394 				if ((MS->player.cur_state.momentum != __FORWARD_1) && (MS->player.cur_state.momentum != __FORWARD_2))
395 					return __OK;
396 
397 				PXreal dx = (PXreal)PXsin(L->pan * TWO_PI);
398 				PXreal dz = (PXreal)PXcos(L->pan * TWO_PI);
399 
400 				PXreal mx = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.x;
401 				PXreal mz = logic_structs[MS->player.cur_interact_id]->mega->actor_xyz.z;
402 
403 				if ((dz * (mx - M->actor_xyz.x)) <= (dx * (mz - M->actor_xyz.z))) {
404 					// right
405 					if ((distance > (50 * 50)) && (distance < (120 * 120)))
406 						L->pan += 0.03f;
407 				} else {
408 					// left
409 					if ((distance > (50 * 50)) && (distance < (120 * 120)))
410 						L->pan -= 0.03f;
411 				}
412 
413 				total_adjusts++;
414 
415 				return (__OK);
416 			}
417 		}
418 		total_adjusts = 0; // reset
419 	}
420 	return (__OK);
421 }
422 
Check_this_barrier(_route_barrier * bar,PXreal newx,PXreal newz,PXreal,PXreal,PXreal bar_close,int32 * ignoreThis)423 __barrier_result _game_session::Check_this_barrier(_route_barrier *bar, PXreal newx, PXreal newz, PXreal /* oldx */, PXreal /* oldz */, PXreal bar_close, int32 *ignoreThis) {
424 	PXfloat delta;
425 	PXfloat delta2;
426 	PXfloat barrier_tolerance = BARRIER_TOLERANCE; // 1/8 of a turn = 45 degress
427 	PXreal pdist, dist;
428 	PXreal ignore_bar_close = IGNORE_BARRIER_CLOSE;
429 
430 	*ignoreThis = 1;
431 
432 	pdist = ((newx * bar->bcm().lpx()) + (newz * bar->bcm().lpz())) - bar->bcm().linedist();
433 
434 	if ((PXfloat)PXfabs(pdist) < bar_close) {
435 		// we are near the plane so now we must check the end points
436 		// check the left end of the line
437 
438 		dist = ((newx * bar->bcm().alpx()) + (newz * bar->bcm().alpz())) - bar->bcm().alinedist();
439 
440 		// check the right end
441 		if (dist >= 0) {
442 			dist = ((newx * bar->bcm().blpx()) + (newz * bar->bcm().blpz())) - bar->bcm().blinedist();
443 
444 			if (dist >= 0) {
445 				*ignoreThis = 0;
446 
447 				// we are going to hit this barrier
448 				// but, if the angle is narrow we can aquire the barriers pan and continue unmolested
449 				delta = remainder(L->pan - bar->pan(), FULL_TURN, HALF_TURN);
450 				delta2 = delta;
451 
452 				if (delta < -QUARTER_TURN)
453 					delta2 += HALF_TURN;
454 				if (delta > QUARTER_TURN)
455 					delta2 -= HALF_TURN;
456 				if (PXfabs(delta2) < barrier_tolerance) {
457 					if (made_adjust)
458 						return (__BLOCKED); // conflict, so finish
459 
460 					if ((delta > QUARTER_TURN) || (delta < -QUARTER_TURN)) {
461 						adjusted_pan = remainder(bar->pan() + HALF_TURN, FULL_TURN, HALF_TURN);
462 					} else {
463 						adjusted_pan = bar->pan();
464 					}
465 					made_adjust = TRUE8;
466 					if (adjusted_pan > L->pan) {
467 						adjusted_pan += REPEL_TURN;
468 					} else if (adjusted_pan < L->pan) {
469 						adjusted_pan -= REPEL_TURN;
470 					}
471 					if (pdist > 0)
472 						normalAngle = bar->pan() + QUARTER_TURN;
473 					else if (pdist < 0)
474 						normalAngle = bar->pan() - QUARTER_TURN;
475 				} else {
476 					// cant adjust
477 					return (__BLOCKED);
478 				}
479 			} else
480 				*ignoreThis = 1;
481 		} else
482 			*ignoreThis = 1;
483 	} else if ((PXfloat)PXfabs(pdist) < ignore_bar_close) {
484 		*ignoreThis = 0;
485 	}
486 
487 	return __OK;
488 }
489 
___init()490 void _barrier_handler::___init() {
491 	_routing_slice *slice;
492 	uint32 *num_bars;
493 	uint32 len;
494 
495 	Zdebug("_barrier_handler");
496 	Zdebug("\n+init _barrier_handler %s", MS->Fetch_session_name());
497 
498 	// load the raw barrier file for this session
499 	// When clustered the session files have the base stripped
500 	len = sprintf(temp_buf, "%s", PX_FILENAME_BARRIERLIST);
501 	if (len > ENGINE_STRING_LEN)
502 		Fatal_error("_barrier_handler::___init string len error");
503 
504 	Tdebug("barriers.txt", "%s", (const char *)temp_buf);
505 	uint32 buf_hash = NULL_HASH;
506 	uint32 cluster_hash = MS->Fetch_session_cluster_hash();
507 	raw_barriers = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, MS->Fetch_session_cluster(), cluster_hash);
508 
509 	num_bars = (uint32 *)raw_barriers->Fetch_item_by_name("Count");
510 
511 	total_barriers = *(num_bars);
512 
513 	Tdebug("barriers.txt", "%d raw barriers", total_barriers);
514 
515 	// load in the routing wrapper
516 	// When clustered the session files have the base stripped
517 	len = sprintf(temp_buf, "%s", PX_FILENAME_ROUTING);
518 	if (len > ENGINE_STRING_LEN)
519 		Fatal_error("_barrier_handler::___init string len error");
520 
521 	Tdebug("barriers.txt", "%s", temp_buf);
522 	buf_hash = NULL_HASH;
523 	route_wrapper = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, MS->Fetch_session_cluster(), cluster_hash);
524 
525 	total_slices = route_wrapper->Fetch_number_of_items();
526 
527 	if (total_slices > MAX_slices)
528 		Fatal_error("_barrier_handler::___init finds too many slices - %d but only %d allowed", total_slices, MAX_slices);
529 
530 	Tdebug("slice.txt", "%d routing levels", total_slices);
531 
532 	if (!total_slices) {
533 		Zdebug("[%s]", (const char *)temp_buf);
534 		Fatal_error("no parent routing levels (no parent boxes) engine cannot proceed");
535 	}
536 
537 	uint32 j;
538 	for (j = 0; j < total_slices; j++) {
539 		slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(j);
540 		Tdebug("slice.txt", "bottom %3.1f top %3.1f", slice->bottom, slice->top);
541 		Tdebug("slice.txt", "%d parents", slice->num_parent_boxes);
542 	}
543 
544 	// reset prop list for each
545 	// fully reset abar systems
546 	for (j = 0; j < MAX_slices; j++) {
547 		anim_slices[j].num_props_in_slice = 0;
548 		for (uint32 l = 0; l < MAX_parents_per_anim_slice; l++)
549 			anim_slices[j].anim_parents[l] = 0; // unasigned pointer
550 	}
551 	for (j = 0; j < MAX_props; j++) {
552 		anim_prop_info[j].barriers_per_state = 0;
553 		anim_prop_info[j].total_states = 0;
554 	}
555 	for (j = 0; j < MAX_floors; j++) // reset the unassigned parents
556 		anim_parent_table[j].num_props = 0;
557 	parents_used = 0; // no parents have been assigned
558 
559 	Zdebug("anim bars");
560 
561 	Prepare_animating_barriers();
562 
563 	Zdebug("done barriers");
564 }
565 
566 #define ADD_CHILD                                                                                                                                                                  \
567 	if (clist[j]->num_barriers)                                                                                                                                                \
568 		for (k = 0; k < clist[j]->num_barriers; k++) {                                                                                                                     \
569 			bar = Fetch_barrier(clist[j]->barriers[k]);                                                                                                                \
570 			if (bar->bottom() == y) {                                                                                                                                  \
571 				if (barrier_mask) {                                                                                                                                \
572 					if (MS->troute.LineIntersectsRect(mask, (int32)bar->x1(), (int32)bar->z1(), (int32)bar->x2(), (int32)bar->z2()))                           \
573 						MS->troute.Add_barrier(bar);                                                                                                       \
574 				} else                                                                                                                                             \
575 					MS->troute.Add_barrier(bar);                                                                                                               \
576 			}                                                                                                                                                          \
577 		}
578 
579 #define EXPAND_ROUTE_BOX                                                                                                                                                           \
580 	if (CHILDL < RBL)                                                                                                                                                          \
581 		RBL = CHILDL;                                                                                                                                                      \
582 	if (CHILDR > RBR)                                                                                                                                                          \
583 		RBR = CHILDR;                                                                                                                                                      \
584 	if (CHILDT < RBT)                                                                                                                                                          \
585 		RBT = CHILDT;                                                                                                                                                      \
586 	if (CHILDB > RBB)                                                                                                                                                          \
587 		RBB = CHILDB;                                                                                                                                                      \
588 	expanded_this_go++;
589 
590 #define CHILDL clist[j]->left
591 #define CHILDR clist[j]->right
592 #define CHILDT clist[j]->back
593 #define CHILDB clist[j]->front
594 #define RBL rb.x1
595 #define RBR rb.x2
596 #define RBT rb.z1
597 #define RBB rb.z2
598 
Form_parent_barrier_list(PXreal x,PXreal y,PXreal z)599 void _barrier_handler::Form_parent_barrier_list(PXreal x, PXreal y, PXreal z) {
600 	// we are routing into a room - just get the parent barriers
601 	_parent_box *endb;
602 	uint32 parent_a, slice_a, k;
603 	_route_barrier *bar;
604 	uint32 *array;
605 
606 	endb = Fetch_parent_box_for_xyz(x, y, z, parent_a, slice_a);
607 	if (!endb)
608 		return; // will be because of between floor gap - not to worry
609 
610 	if (endb->num_barriers) {
611 		array = (uint32 *)(((char *)endb) + (endb->barriers));
612 		for (k = 0; k < endb->num_barriers; k++) {
613 			bar = Fetch_barrier(array[k]);
614 			MS->troute.Add_barrier(bar);
615 		}
616 	}
617 }
618 
Form_route_barrier_list(PXreal x,PXreal y,PXreal z,PXreal x2,PXreal z2)619 void _barrier_handler::Form_route_barrier_list(PXreal x, PXreal y, PXreal z, PXreal x2, PXreal z2) {
620 	// add all barriers to the prim route building system for the route x,z,x2,z2
621 	// after this the route_manager can call the prim route builder for the final route to be made
622 	// the restriction is that we can only route from one place to another within a single floor-rect/parent-box
623 	// or
624 	// from within one parent-box/floor-rect to an adjacent parent-box/floor-rect
625 	// it is a higher level job to divide long routes up into rect to rect chunks before calling here
626 	// but this is quite sensible anyway as we only want to be auto-routing across tiny areas at a time
627 
628 	_parent_box *startb;
629 	_parent_box *endb;
630 	_rect rb; // rb meaning 'Route-Box'
631 	uint32 j;
632 	_child_group *clist[MAX_child_groups_per_parent * 2];
633 	uint32 total_childs = 0; // seperate total for safety
634 	int32 expanded_this_go;
635 	_route_barrier *bar;
636 	uint32 k;
637 	uint32 parent_a, parent_b;
638 	uint32 slice_a, slice_b;
639 
640 	// which floors are the start and end positions in?
641 	// list child boxes for each floor - ignoring the parent box concept?
642 	// build route
643 	// add parent boundaries for all parents that the final route box intersects - !?!
644 
645 	// find the parent box
646 	// first, find _parent_box for start point and end points
647 
648 	startb = Fetch_parent_box_for_xyz(x, y, z, parent_a, slice_a);
649 	endb = Fetch_parent_box_for_xyz(x2, y, z2, parent_b, slice_b);
650 
651 	if ((!startb) && (endb))
652 		startb = endb; // no start but end
653 	if ((!endb) && (startb))
654 		endb = startb; // no end but start
655 
656 	if (!startb)
657 		Fatal_error("_barrier_handler::Form_route_barrier_list start and end not on floor - %s", MS->Fetch_object_name(MS->Fetch_cur_id()));
658 	// make a list of pointers to the child groups
659 
660 	if (startb != endb) { // oh dear - not going to allow this anymore
661 		Form_parent_barrier_list(x2, y, z2);
662 		Form_parent_barrier_list(x, y, z);
663 		return;
664 	}
665 
666 	Zdebug("%3.1f %3.1f %3.1f %3.1f", startb->back, startb->left, startb->front, startb->right);
667 
668 	if (startb->num_childgroups) {
669 		for (j = 0; j < startb->num_childgroups; j++) {
670 			if (total_childs == (MAX_child_groups_per_parent * 2))
671 				Fatal_error("_barrier_handler::Form_route_barrier_list - clist ran out of space");
672 			clist[total_childs++] = Fetch_child_box(startb, j);
673 		}
674 	}
675 
676 	// we now know the _parent_box that our start point is on - we therefore have the list of child boxes
677 	// same or different?
678 	if (startb != endb) { // different
679 		Zdebug("different parent for end box");
680 		if (endb->num_childgroups) {
681 			Zdebug("adding %d child boxes for end point", endb->num_childgroups);
682 			for (j = 0; j < endb->num_childgroups; j++) {
683 				if (total_childs == (MAX_child_groups_per_parent * 2))
684 					Fatal_error("_barrier_handler::Form_route_barrier_list - clist ran out of space");
685 				clist[total_childs++] = Fetch_child_box(endb, j);
686 			}
687 		}
688 	}
689 
690 	// ok, we now have a list of pointers to all the child groups relevant
691 
692 	// create the initial route box
693 	if (x < x2) {
694 		rb.x1 = x;
695 		rb.x2 = x2;
696 	} else {
697 		rb.x1 = x2;
698 		rb.x2 = x;
699 	}
700 	if (z < z2) {
701 		rb.z1 = z;
702 		rb.z2 = z2;
703 	} else {
704 		rb.z1 = z2;
705 		rb.z2 = z;
706 	}
707 
708 	// we now have our initial route box
709 
710 	// enter our main loop
711 	do {
712 		expanded_this_go = 0; // flag to say whether or no the route-box got expanded this loop
713 
714 		for (j = 0; j < total_childs; j++) {
715 			if (clist[j]) {
716 				// child not deleted
717 
718 				if (((CHILDR < RBL) || (CHILDL > RBR)) && ((CHILDB < RBT) || (CHILDT > RBB))) {
719 					// child is wholly outside the route box - do nothing except prime the ELSE
720 				} else if (((CHILDR >= RBL) && (CHILDL <= RBR)) && ((CHILDB >= RBT) && (CHILDT <= RBB))) {
721 					// child is wholly inside route box - add it and delete child
722 					ADD_CHILD
723 					clist[j] = NULL; // delete the child now it has been absorbed
724 				} else if (((RBR >= CHILDL) && (RBL <= CHILDR)) && ((RBT >= CHILDT) && (RBB <= CHILDB))) {
725 					// route box is wholly inside child box - expand route box, add and delete child
726 					ADD_CHILD
727 					clist[j] = NULL; // delete the child now it has been absorbed
728 					EXPAND_ROUTE_BOX
729 				} else {
730 					if ((CHILDL > RBL) && (CHILDL < RBR) && ((CHILDT > RBT) && (CHILDT < RBB))) {
731 						// child top/left is within route box - expand route box, add and delete child
732 						ADD_CHILD
733 						clist[j] = NULL; // delete the child now it has been absorbed
734 						EXPAND_ROUTE_BOX
735 					} else if ((CHILDR > RBL) && (CHILDR < RBR) && ((CHILDT > RBT) && (CHILDT < RBB))) {
736 						// child top/right is within route box - expand route box, add and delete child
737 						ADD_CHILD
738 						clist[j] = NULL; // delete the child now it has been absorbed
739 						EXPAND_ROUTE_BOX
740 					} else if ((CHILDL > RBL) && (CHILDL < RBR) && ((CHILDB > RBT) && (CHILDB < RBB))) {
741 						// child bottom/left is within route box - expand route box, add and delete child
742 						ADD_CHILD
743 						clist[j] = NULL; // delete the child now it has been absorbed
744 						EXPAND_ROUTE_BOX
745 					} else if ((CHILDR > RBL) && (CHILDR < RBR) && ((CHILDB > RBT) && (CHILDB < RBB))) {
746 						// child bottom/right is within route box - expand route box, add and delete child
747 						ADD_CHILD
748 						clist[j] = NULL; // delete the child now it has been absorbed
749 						EXPAND_ROUTE_BOX
750 					} else {
751 						// we must check for our route line intersecting a horizontal and vertical child box edge
752 					}
753 				}
754 			}
755 		}
756 
757 		// if this loop we did not expand the route_box then we quit this DO-WHILE loop
758 		if (!expanded_this_go)
759 			break;
760 	} while (1);
761 
762 	// if there are two parent boxes we must add the parent barriers from each to the list
763 	if (startb != endb) {
764 		// add both parent box barriers in
765 		uint32 *array;
766 
767 		if (startb->num_barriers) {
768 			array = (uint32 *)(((char *)startb) + (startb->barriers));
769 			for (k = 0; k < startb->num_barriers; k++) {
770 				bar = Fetch_barrier(array[k]);
771 				MS->troute.Add_barrier(bar);
772 			}
773 		}
774 		if (endb->num_barriers) {
775 			array = (uint32 *)(((char *)endb) + (endb->barriers));
776 			for (k = 0; k < endb->num_barriers; k++) {
777 				bar = Fetch_barrier(array[k]);
778 				MS->troute.Add_barrier(bar);
779 			}
780 		}
781 	} else { // route lies within a single floor - so draw a box around it
782 		_route_barrier newbar;
783 
784 		// left hand barrier
785 		newbar.x1(startb->left);
786 		newbar.z1(startb->back);
787 		newbar.x2(startb->left);
788 		newbar.z2(startb->front);
789 		MS->troute.Add_barrier(&newbar);
790 
791 		// right hand barrier
792 		newbar.x1(startb->right);
793 		newbar.z1(startb->back);
794 		newbar.x2(startb->right);
795 		newbar.z2(startb->front);
796 		MS->troute.Add_barrier(&newbar);
797 
798 		// top barrier
799 		newbar.x1(startb->left);
800 		newbar.z1(startb->back);
801 		newbar.x2(startb->right);
802 		newbar.z2(startb->back);
803 		MS->troute.Add_barrier(&newbar);
804 
805 		// bottom barrier
806 		newbar.x1(startb->left);
807 		newbar.z1(startb->front);
808 		newbar.x2(startb->right);
809 		newbar.z2(startb->front);
810 		MS->troute.Add_barrier(&newbar);
811 	}
812 }
813 
Fetch_parent_num_on_slice_y(uint32 requested_parent,PXreal y)814 _parent_box *_barrier_handler::Fetch_parent_num_on_slice_y(uint32 requested_parent, PXreal y) {
815 	// fetch the parent of the number passed for a given y level
816 	// ie 0 means first, 1 means second, etc
817 	// this is called by the plan-viewer which just keeps asking for the next one until we say there are no more
818 	// by passing back a 0 instead of a pointer to a parent
819 	static _routing_slice *slice;
820 	uint32 cur_slice = 0;
821 
822 	// first time in so compute the slice
823 	if (!requested_parent) {
824 		while (1) {
825 			slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(cur_slice);
826 
827 			if ((y >= slice->bottom) && (y < slice->top))
828 				break;
829 
830 			// safety
831 			cur_slice++;
832 			if (cur_slice == total_slices) // if so then must be last slice :O
833 				Fatal_error("Fetch_parent_num_on_slice_y ran out of slices");
834 
835 			// next
836 			slice++;
837 		}
838 	}
839 
840 	// ok, we have the slice
841 	// return the parent of the requested number - or, 0 if there is no more
842 
843 	// reached total?
844 	if (requested_parent == slice->num_parent_boxes)
845 		return (0);
846 
847 	// simply return the pointer
848 
849 	return ((_parent_box *)(((uint8 *)slice) + slice->parent_boxes[requested_parent]));
850 }
851 
Fetch_barrier(uint32 num)852 _route_barrier *_barrier_handler::Fetch_barrier(uint32 num) {
853 	// return a pointer to numbered barrier
854 	_route_barrier *bar;
855 
856 	assert(num < total_barriers);
857 
858 	if (num >= total_barriers)
859 		Fatal_error("illegal barrier request %d", num);
860 
861 	bar = (_route_barrier *)raw_barriers->Fetch_item_by_name("Data");
862 
863 	return &bar[num];
864 }
865 
Fetch_parent_box_for_xyz(PXreal x,PXreal y,PXreal z,uint32 & par_num,uint32 & slice_num)866 _parent_box *_barrier_handler::Fetch_parent_box_for_xyz(PXreal x, PXreal y, PXreal z, uint32 &par_num, uint32 &slice_num) {
867 	// return a pointer to the parent box of a point in world space
868 	// returns 0 if the point does not lie within a parent box
869 
870 	_routing_slice *slice = NULL;
871 	_parent_box *parent = NULL;
872 
873 	// find correct slice according to height
874 	// fetch first
875 
876 	slice_num = 0;
877 
878 	while (1) {
879 		slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(slice_num);
880 
881 		if ((y >= slice->bottom) && (y < slice->top))
882 			break;
883 
884 		// safety
885 		slice_num++;
886 		if (slice_num == total_slices) { // if so then must be last slice :O
887 			Fatal_error("_barrier_handler::Fetch_parent_box_for_xyz ran out of slices: object [%s] (%3.1f %3.1f %3.1f) has an "
888 			            "illegal marker",
889 			            MS->Fetch_object_name(MS->Fetch_cur_id()), x, y, z);
890 		}
891 		// next
892 		slice++;
893 	}
894 
895 	// ok, we found the right y slice
896 	// now find the right parent box
897 	if (!slice->num_parent_boxes)
898 		Fatal_error("_barrier_handler::Fetch_parent_box_for_xyz slice has no parent boxes");
899 
900 	for (par_num = 0; par_num < slice->num_parent_boxes; par_num++) {
901 		parent = (_parent_box *)(((uint8 *)slice) + slice->parent_boxes[par_num]);
902 
903 		// do we lie within the box?
904 		if ((x > parent->left) && (x < parent->right) && (z > parent->back) && (z < parent->front)) {
905 			return (parent);
906 		}
907 	}
908 
909 	return (0);
910 }
911 
Prepare_megas_route_barriers(bool8 pl)912 void _game_session::Prepare_megas_route_barriers(bool8 pl) {
913 	// see which parent box we're owned by
914 	// if different from previous fetch all the barriers for the new parent
915 	// this system is custom for the player object - routing megas use their own system
916 	// this routine fecthes the 'special' player only line-of-sight barriers too
917 
918 	_parent_box *par = 0;
919 	_child_group *pchild;
920 	uint32 total_childs;
921 	uint32 j, k;
922 	uint32 *list;
923 	_route_barrier *bar;
924 	uint32 parent_number;
925 	_routing_slice *slice;
926 	PXreal x, y, z;
927 
928 	x = M->actor_xyz.x;
929 	y = floor_def->Return_true_y(M->actor_xyz.y);
930 	z = M->actor_xyz.z;
931 
932 	// on previous slice?
933 	slice = (_routing_slice *)session_barriers->route_wrapper->Fetch_item_by_number(M->cur_slice);
934 	if ((y >= slice->bottom) && (y < slice->top) && (M->cur_parent))
935 		if ((x > M->cur_parent->left) && (x < M->cur_parent->right) && (z > M->cur_parent->back) && (z < M->cur_parent->front)) {
936 			// nothing has changed
937 			Prepare_megas_abarriers(M->cur_slice, M->par_number);
938 
939 			// player should have added the barriers of stood megas
940 			if (pl)
941 				Fetch_mega_barriers_for_player();
942 
943 			return;
944 		}
945 
946 	M->cur_slice = 0;
947 	while (1) {
948 		slice = (_routing_slice *)session_barriers->route_wrapper->Fetch_item_by_number(M->cur_slice);
949 		if ((y >= slice->bottom) && (y < slice->top))
950 			break;
951 
952 		// safety
953 		M->cur_slice++;
954 		if (M->cur_slice == session_barriers->total_slices) { // if so then must be last slice :O
955 			M->cur_slice--;
956 			slice = (_routing_slice *)session_barriers->route_wrapper->Fetch_item_by_number(M->cur_slice);
957 			break;
958 		}
959 	}
960 	// ok, we found the right y slice
961 	// now find the right parent box
962 	if (!slice->num_parent_boxes)
963 		Fatal_error("Prepare_megas_route_barriers slice has no parent boxes");
964 
965 	for (parent_number = 0; parent_number < slice->num_parent_boxes; parent_number++) {
966 		par = (_parent_box *)(((uint8 *)slice) + slice->parent_boxes[parent_number]);
967 
968 		// do we lie within the box?
969 		if ((x > par->left) && (x < par->right) && (z > par->back) && (z < par->front)) {
970 			break; // found
971 		}
972 	}
973 
974 	if (parent_number == slice->num_parent_boxes) {
975 		// not on a legal position - can happen
976 		M->cur_parent = 0; // null pointer
977 		M->number_of_barriers = 0;
978 		M->number_of_nudge = 0;
979 		M->number_of_animating = 0;
980 		return;
981 	}
982 
983 	// has a parent box different from the current one been found?
984 	{
985 		// new one
986 		M->cur_parent = par;
987 		M->par_number = parent_number;
988 
989 		// reset list of barrier ids
990 		M->number_of_barriers = 0;
991 		M->number_of_animating = 0;
992 
993 		// firstly, drag out the parents bounding barriers if it has any
994 		if (par->num_barriers) {
995 			list = (uint32 *)((uint8 *)par + par->barriers);
996 			// get all the barriers
997 			for (j = 0; j < par->num_barriers; j++) {
998 				// fetch each barrier and check that its bottom edge is on the floor - otherwise we ignore it
999 				bar = session_barriers->Fetch_barrier(*(list));
1000 				if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
1001 					// ok, this barrier is on the floor so we add it to our list
1002 					M->barrier_list[M->number_of_barriers++] = *(list++);
1003 				}
1004 			}
1005 		}
1006 
1007 		if (pl)
1008 			M->number_of_nudge = 0; // reset now - in case there are none
1009 
1010 		// now player only specials
1011 		if ((pl) && (par->num_specials)) {
1012 #ifdef VERBOSE
1013 			Zdebug("getting player specials");
1014 #endif
1015 
1016 			list = (uint32 *)((uint8 *)par + par->specials);
1017 			// get all the barriers
1018 			for (j = 0; j < par->num_specials; j++) {
1019 				// fetch each barrier and check that its bottom edge is on the floor - otherwise we ignore it
1020 				bar = session_barriers->Fetch_barrier(*(list));
1021 
1022 				if (bar->material() == VIEW_FIELD) {
1023 					if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
1024 						//                  ok, this barrier is on the floor so we add it to our list
1025 						M->barrier_list[M->number_of_barriers++] = *(list++);
1026 					}
1027 				} else if (bar->material() >= LEFT_NUDGE) {
1028 					if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
1029 						//                  ok, this barrier is on the floor so we add it to our list
1030 						M->nudge_list[M->number_of_nudge++] = *(list++);
1031 					}
1032 				} else {
1033 					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(),
1034 					            bar->x2(), bar->z1(), bar->z2());
1035 				}
1036 			}
1037 		}
1038 
1039 		// find out have many child boxes this parent has
1040 		total_childs = session_barriers->Fetch_number_of_child_boxes(par);
1041 
1042 		for (j = 0; j < total_childs; j++) {
1043 			pchild = session_barriers->Fetch_child_box(par, j);
1044 
1045 			for (k = 0; k < pchild->num_barriers; k++) {
1046 				bar = session_barriers->Fetch_barrier(pchild->barriers[k]);
1047 				if (bar->bottom() == slice->bottom) { // M->actor_xyz.y)
1048 					M->barrier_list[M->number_of_barriers++] = pchild->barriers[k];
1049 				}
1050 			}
1051 		}
1052 	}
1053 
1054 	// now build the animating barrier list - regardless of whether or not the parent has changed
1055 	Prepare_megas_abarriers(M->cur_slice, M->par_number);
1056 
1057 	if (M->number_of_barriers + M->number_of_animating > MAX_bars)
1058 		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);
1059 
1060 	if (M->number_of_nudge > MAX_bars)
1061 		Fatal_error("too many player nudge barriers");
1062 }
1063 
Prepare_animating_barriers()1064 void _barrier_handler::Prepare_animating_barriers() {
1065 	// the nightmare that is animating barriers
1066 	// search through all prop animations getting out the animating barriers - checking for duplicates
1067 
1068 #define MAX_anim_barriers 400
1069 	uint32 j;
1070 	uint16 barrier_table[MAX_anim_barriers];
1071 	uint32 total_anim_bars = 0;
1072 	_route_barrier *bar;
1073 	_routing_slice *slice;
1074 	_parent_box *parent;
1075 	uint32 cur_slice = 0;
1076 	uint32 l, f, pbar_num;
1077 	uint32 abar_index = 0;
1078 
1079 	Tdebug("anim_barriers.txt", "Preparing animating barriers");
1080 
1081 	for (j = 0; j < MS->prop_anims->Fetch_number_of_items(); j++) {
1082 		Tdebug("anim_barriers.txt", "\n%d %s", j, MS->prop_anims->Fetch_items_name_by_number(j));
1083 
1084 		_animating_prop *index;
1085 		_animation_entry *anim;
1086 
1087 		index = (_animating_prop *)MS->prop_anims->Fetch_item_by_number(j);
1088 
1089 		Tdebug("anim_barriers.txt", " has %d anims", index->num_anims);
1090 
1091 		// loop through all looking for our named anim
1092 		if (index->num_anims) {
1093 			// get the prop number
1094 
1095 			for (uint32 k = 0; k < index->num_anims; k++) {
1096 				anim = (_animation_entry *)(((char *)index) + index->anims[k]);
1097 				Tdebug("anim_barriers.txt", "  '%s' - %d frames", (((char *)index) + anim->name), anim->num_frames);
1098 
1099 				if (anim->num_barriers_per_frame) {
1100 					Tdebug("anim_barriers.txt", "   has %d anim barriers per frame", anim->num_barriers_per_frame);
1101 					uint16 *bars = (uint16 *)(((char *)index) + anim->offset_barriers);
1102 
1103 					for (uint32 i = 0; i < (uint32)(anim->num_barriers_per_frame * anim->num_frames); i++) {
1104 						if (bars[i] != 0xffff) {
1105 							// check each barrier for duplication and add in to correct slice, parent list
1106 
1107 							for (l = 0; l < total_anim_bars; l++) {
1108 								if (barrier_table[l] == bars[i]) {
1109 									Tdebug("anim_barriers.txt", "    %d in list already - index %d", bars[i], i);
1110 									break; // found this bar
1111 								}
1112 							}
1113 							// i is barrier index
1114 
1115 							bar = Fetch_barrier(bars[i]);
1116 
1117 							if (l == total_anim_bars) { // didnt find in list
1118 								Tdebug("anim_barriers.txt", "     new barrier %d  x%3.2f y%3.2f z%3.2f", bars[i], bar->x1(), bar->bottom(),
1119 								       bar->z1());
1120 
1121 								barrier_table[total_anim_bars++] = (uint16)bars[i]; // write the bar down
1122 
1123 								if (total_anim_bars == MAX_anim_barriers)
1124 									Fatal_error("Prepare_animating_barriers finds too many barriers "
1125 									            "for scratch table");
1126 							}
1127 							cur_slice = 0;
1128 
1129 							do {
1130 								slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(cur_slice);
1131 
1132 								if ((bar->bottom() >= slice->bottom) && (bar->bottom() < slice->top))
1133 									break;
1134 
1135 								cur_slice++;
1136 							} while (cur_slice != total_slices);
1137 
1138 							if (cur_slice == total_slices) { // if so then must be last slice :O
1139 								Tdebug("anim_barriers.txt", "      **ran out of slices :O\n");
1140 							} else {
1141 								Tdebug("anim_barriers.txt", "      exists on slice %d", cur_slice);
1142 
1143 								// check legality of number of parents
1144 								if (slice->num_parent_boxes > MAX_parents_per_anim_slice)
1145 									Fatal_error("prepare anim barriers finds too many parents in slice "
1146 									            "- %d parents",
1147 									            slice->num_parent_boxes);
1148 
1149 								int32 prop_number =
1150 								    MS->objects->Fetch_item_number_by_name((const char *)MS->prop_anims->Fetch_items_name_by_number(j));
1151 
1152 								if (prop_number == -1) {
1153 									Tdebug("anim_barriers.txt", "       !!associated prop [%s] not a game object - so ignoring",
1154 									       (const char *)MS->prop_anims->Fetch_items_name_by_number(j));
1155 								} else {
1156 									// now find parent the barrier would belong to
1157 									for (f = 0; f < slice->num_parent_boxes; f++) {
1158 										Tdebug("anim_barriers.txt", "       check parent %d", f);
1159 
1160 										parent = (_parent_box *)(((uint8 *)slice) + slice->parent_boxes[f]);
1161 
1162 										// do we lie within the box?
1163 										if ((bar->x1() > parent->left) && (bar->x1() < parent->right) && (bar->z1() > parent->back) &&
1164 										    (bar->z1() < parent->front)) {
1165 											char *props_name = (char *)MS->prop_anims->Fetch_items_name_by_number(j);
1166 											uint32 props_number = MS->objects->Fetch_item_number_by_name(props_name);
1167 
1168 											if (!anim_slices[cur_slice].anim_parents[f]) {
1169 												anim_slices[cur_slice].anim_parents[f] = &anim_parent_table[parents_used++];
1170 												Tdebug("anim_barriers.txt", "        new aparent");
1171 											}
1172 
1173 											pbar_num = anim_slices[cur_slice].anim_parents[f]->num_props;
1174 
1175 											uint32 n;
1176 											for (n = 0; n < pbar_num; n++)
1177 												if (anim_slices[cur_slice].anim_parents[f]->prop_number[n] == props_number)
1178 													break;
1179 
1180 											if (n == pbar_num) { // new
1181 												anim_slices[cur_slice].anim_parents[f]->num_props++;
1182 
1183 												if (pbar_num == MAX_props_per_parent)
1184 													Fatal_error("too many props in "
1185 													            "parent - max = %d",
1186 													            MAX_props_per_parent);
1187 
1188 												Tdebug("anim_barriers.txt", "        found - slice %d, parent %d "
1189 												                            "[%d bars so far], prop [%s, %d] state "
1190 												                            "%d\n",
1191 												       cur_slice, f, anim_slices[cur_slice].anim_parents[f]->num_props, props_name,
1192 												       props_number, anim->frames[i / anim->num_barriers_per_frame]);
1193 
1194 												anim_slices[cur_slice].anim_parents[f]->prop_number[pbar_num] = (uint8)props_number;
1195 											}
1196 											break;
1197 										}
1198 									}
1199 									if (f == slice->num_parent_boxes)
1200 										Tdebug("anim_barriers.txt", "       !!barrier not located within a parent box!!\n");
1201 								}
1202 							}
1203 						} else
1204 							Tdebug("anim_barriers.txt", "     !!barrier is type 0xffffffff");
1205 					}
1206 				} else
1207 					Tdebug("anim_barriers.txt", "   0 anim bars");
1208 			}
1209 		}
1210 	}
1211 
1212 	// now reverse engineer the target lists
1213 	for (j = 0; j < total_slices; j++) {
1214 		Tdebug("anim_barriers.txt", " slice %d", j);
1215 
1216 		for (uint32 k = 0; k < MAX_parents_per_anim_slice; k++) {
1217 			if (!anim_slices[j].anim_parents[k])
1218 				Tdebug("anim_barriers.txt", "   par %d free", k);
1219 			else {
1220 				Tdebug("anim_barriers.txt", "  parent %d has %d animating props", k, anim_slices[j].anim_parents[k]->num_props);
1221 				for (uint32 i = 0; i < anim_slices[j].anim_parents[k]->num_props; i++)
1222 					Tdebug("anim_barriers.txt", "   prop num %d", anim_slices[j].anim_parents[k]->prop_number[i]);
1223 			}
1224 		}
1225 	}
1226 
1227 	Tdebug("anim_barriers.txt", "\n\n\n\n\n--------preparing prop slice format---------------\n");
1228 
1229 	uint32 total_states, i;
1230 
1231 	// now compute the special prop table for run-time LOS injection
1232 	// first reset special prop fixed list
1233 	for (j = 0; j < MAX_props; j++)
1234 		anim_prop_info[j].barriers_per_state = 0; // do it here so it can double up as 0 being an unused entry
1235 
1236 	for (j = 0; j < MS->prop_anims->Fetch_number_of_items(); j++) {
1237 		Tdebug("anim_barriers.txt", "\n**%d %s", j, MS->prop_anims->Fetch_items_name_by_number(j));
1238 		_animating_prop *index;
1239 		_animation_entry *anim;
1240 
1241 		index = (_animating_prop *)MS->prop_anims->Fetch_item_by_number(j);
1242 
1243 		// loop through all looking for our named anim
1244 		if (index->num_anims) {
1245 			// got a prop
1246 			// it has anims
1247 			// is it a prop with animating barriers?
1248 
1249 			// get first anim
1250 			anim = (_animation_entry *)(((char *)index) + index->anims[0]);
1251 
1252 			// does it have anim barriers
1253 			if (anim->num_barriers_per_frame) {
1254 				// ok, we're on
1255 
1256 				Tdebug("anim_barriers.txt", " has %d anims and %d per frame", index->num_anims, anim->num_barriers_per_frame);
1257 
1258 				// reset our total number of states for this prop counter
1259 				total_states = 0;
1260 
1261 				// find the first legal barrier in this anim
1262 				uint16 *bars = (uint16 *)(((char *)index) + anim->offset_barriers);
1263 				i = 0;
1264 				while ((bars[i] == 0xffff) && (i < (uint32)(anim->num_barriers_per_frame * anim->num_frames)))
1265 					i++;
1266 
1267 				// better make sure the anim has a legal barrier - if not then assume the prop is well knackered and leave
1268 				// it out of the anim barrier system
1269 				if (i < (uint32)(anim->num_barriers_per_frame * anim->num_frames)) {
1270 					// get our chosen sample barrier
1271 					bar = Fetch_barrier(bars[i]);
1272 
1273 					// now compute slice for sample barrier
1274 					cur_slice = 0;
1275 					do {
1276 						slice = (_routing_slice *)route_wrapper->Fetch_item_by_number(cur_slice);
1277 						if ((bar->bottom() >= slice->bottom) && (bar->bottom() < slice->top))
1278 							break;
1279 						cur_slice++;
1280 					} while (cur_slice != total_slices);
1281 
1282 					// check for not on a slice - not good
1283 					if (cur_slice == total_slices) { // if so then must be last slice :O
1284 						Tdebug("anim_barriers.txt", "      **ran out of slices :O\n");
1285 					} else {
1286 						Tdebug("anim_barriers.txt", "  sample bar puts prop in slice %d", cur_slice);
1287 
1288 						// get the prop number
1289 						uint32 prop_number = MS->objects->Fetch_item_number_by_name((const char *)MS->prop_anims->Fetch_items_name_by_number(j));
1290 
1291 						// gotta check for anims with no equivelent game objects
1292 						if (prop_number != 0xffffffff) {
1293 							Tdebug("anim_barriers.txt", "  listing as prop number %d", anim_slices[cur_slice].num_props_in_slice);
1294 							// ok, add the prop to the slices list of props
1295 							anim_slices[cur_slice].prop_list[anim_slices[cur_slice].num_props_in_slice++] = (uint8)prop_number;
1296 
1297 							if (anim_slices[cur_slice].num_props_in_slice >= MAX_props)
1298 								Fatal_error("form anim barrier list found too many props in the slice");
1299 
1300 							// set number of barriers per frame for this prop
1301 							anim_prop_info[prop_number].barriers_per_state = anim->num_barriers_per_frame; // set from our sample first anim
1302 
1303 							// now loop through all the anims filling in the state table
1304 							// in theory, having done this, there will be entries for each state referenced
1305 							for (uint32 k = 0; k < index->num_anims; k++) {
1306 								// get anim k
1307 								anim = (_animation_entry *)(((char *)index) + index->anims[k]);
1308 
1309 								// get anims list of barriers
1310 								uint16 *barriers = (uint16 *)(((char *)index) + anim->offset_barriers);
1311 
1312 								// go through each frame/state
1313 								for (uint32 c = 0; c < anim->num_frames; c++) {
1314 									// get state
1315 									uint32 state = anim->frames[c];
1316 
1317 									// keep computing the total number of states for this prop
1318 									if (state > total_states)
1319 										total_states = state;
1320 
1321 									// for each state go through all the barriers per frame/state
1322 									for (i = 0; i < anim->num_barriers_per_frame; i++) {
1323 										// gotta check for funny illegal barriers
1324 										if (barriers[(c * anim->num_barriers_per_frame) + i] != 0xffff) {
1325 											// legal barrier
1326 											barrier_table[(state * anim->num_barriers_per_frame) + i] =
1327 											    (uint16)barriers[(c * anim->num_barriers_per_frame) + i];
1328 										} else {
1329 											// dodgy barrier
1330 											// setting to barrier 0 but this is not a proper
1331 											// solution
1332 											barrier_table[(state * anim->num_barriers_per_frame) + i] = 0;
1333 										}
1334 									}
1335 								}
1336 							}
1337 							// ok, we've set up the anim barriers for this prop
1338 
1339 							Tdebug("anim_barriers.txt", "    prop [%s] highest state %d, %d bars per state",
1340 							       (const char *)MS->prop_anims->Fetch_items_name_by_number(j), total_states,
1341 							       anim_prop_info[prop_number].barriers_per_state);
1342 
1343 							// note down total states
1344 							anim_prop_info[prop_number].total_states = (uint8)(total_states + 1);
1345 
1346 							// create space for table
1347 							Tdebug("anim_barriers.txt", "prop %d needs %d uint16's", prop_number,
1348 							       (total_states + 1) * anim_prop_info[prop_number].barriers_per_state);
1349 
1350 							if (abar_index >= MAX_prop_abars)
1351 								Fatal_error("too many animating barriers - current max = %d", MAX_prop_abars);
1352 
1353 							// move the barrier table
1354 							memcpy(&prop_abar_table[abar_index], barrier_table,
1355 							       (sizeof(uint16) * ((total_states + 1) * anim_prop_info[prop_number].barriers_per_state)));
1356 
1357 							anim_prop_info[prop_number].barrier_list = &prop_abar_table[abar_index];
1358 
1359 							abar_index += ((total_states + 1) * anim_prop_info[prop_number].barriers_per_state);
1360 
1361 						} else { // if prop number legal
1362 							Tdebug("anim_barriers.txt", "    prop has no object - ignoring");
1363 						}
1364 					}
1365 				}
1366 			}
1367 		}
1368 	}
1369 
1370 	Tdebug("anim_barriers.txt", " ");
1371 
1372 	for (j = 0; j < MS->Fetch_number_of_objects(); j++)
1373 		if (anim_prop_info[j].barriers_per_state) {
1374 			Tdebug("anim_barriers.txt", "prop %d", j);
1375 			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),
1376 			       anim_prop_info[j].total_states, anim_prop_info[j].barriers_per_state);
1377 			Tdebug("anim_barriers.txt", "total %d", anim_prop_info[j].barriers_per_state * (anim_prop_info[j].total_states));
1378 			for (uint16 k = 0; k < anim_prop_info[j].barriers_per_state * (anim_prop_info[j].total_states); k++)
1379 				Tdebug("anim_barriers.txt", "%d %d", k, anim_prop_info[j].barrier_list[k]);
1380 		}
1381 
1382 	Tdebug("anim_barriers.txt", "\nDone");
1383 }
1384 
Get_anim_barriers(uint32 n,uint32 * oThisCubesBarriers,uint32 slice)1385 uint32 _barrier_handler::Get_anim_barriers(uint32 n, uint32 *oThisCubesBarriers, uint32 slice) {
1386 	// gives you all the 'current' anim barriers on an ENTIRE slice
1387 	// used by LOS, planview,
1388 	uint32 prop_id;
1389 	uint32 prop_state;
1390 	uint32 bars_per_state;
1391 	uint32 bar_index;
1392 	uint16 *bars;
1393 	uint32 j, k;
1394 
1395 	// how many props in this slice
1396 	uint32 num_props = anim_slices[slice].num_props_in_slice; // prop_list.GetNoItems();
1397 
1398 	for (j = 0; j < num_props; j++) {
1399 		// get id of prop
1400 		prop_id = anim_slices[slice].prop_list[j];
1401 
1402 		// get prop state
1403 		prop_state = MS->prop_state_table[prop_id];
1404 
1405 		// get number of barriers per state
1406 		bars_per_state = anim_prop_info[prop_id].barriers_per_state;
1407 
1408 		// index into barrier list to first barrier for this state
1409 		bar_index = (prop_state * bars_per_state);
1410 
1411 		bars = (uint16 *)&anim_prop_info[prop_id].barrier_list[0];
1412 		bars += bar_index;
1413 
1414 		for (k = 0; k < bars_per_state; k++) {
1415 			if (*(bars) >= total_barriers)
1416 				Fatal_error("Get_anim_barriers - illegal barrier request %d", *(bars));
1417 
1418 			oThisCubesBarriers[n++] = (uint32) * (bars++);
1419 		}
1420 	}
1421 
1422 	return n;
1423 }
1424 
Prepare_megas_abarriers(uint32 slice_number,uint32 parent_number)1425 void _game_session::Prepare_megas_abarriers(uint32 slice_number, uint32 parent_number) {
1426 	// fetch abars for the current parent ONLY
1427 	M->number_of_animating = 0;
1428 	uint32 num_props;
1429 	uint32 prop_id;
1430 	uint32 prop_state;
1431 	uint32 bars_per_state;
1432 	uint32 bar_index;
1433 	uint16 *bars;
1434 	uint32 k;
1435 
1436 	if (!session_barriers->anim_slices[slice_number].anim_parents[parent_number])
1437 		return; // parent has none at all
1438 
1439 	num_props = session_barriers->anim_slices[slice_number].anim_parents[parent_number]->num_props;
1440 
1441 	for (uint32 i = 0; i < num_props; i++) {
1442 		// yup - our parent does have animating props
1443 
1444 		// get prop id on this parent
1445 		prop_id = session_barriers->anim_slices[slice_number].anim_parents[parent_number]->prop_number[i];
1446 
1447 		// get prop state
1448 		prop_state = prop_state_table[prop_id];
1449 
1450 		// get number of barriers per state
1451 		bars_per_state = session_barriers->anim_prop_info[prop_id].barriers_per_state;
1452 
1453 		// index into barrier list to first barrier for this state
1454 		bar_index = (prop_state * bars_per_state);
1455 
1456 		bars = (uint16 *)&session_barriers->anim_prop_info[prop_id].barrier_list[0];
1457 		bars += bar_index;
1458 
1459 		for (k = 0; k < bars_per_state; k++)
1460 			M->barrier_list[M->number_of_barriers + M->number_of_animating++] = (uint32) * (bars++);
1461 	}
1462 }
1463 
Fetch_mega_barriers_for_player()1464 void _game_session::Fetch_mega_barriers_for_player() {
1465 	// find all the megas on our floor
1466 	// then add in barriers for each of them that is stood
1467 	// add the barriers on the end of the anim barrier list which MUST have already been computed
1468 }
1469 
1470 } // End of namespace ICB
1471