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_game_object.h"
31 #include "engines/icb/common/px_scriptengine.h"
32 #include "engines/icb/common/ptr_util.h"
33 #include "engines/icb/session.h"
34 #include "engines/icb/object_structs.h"
35 #include "engines/icb/debug.h"
36 #include "engines/icb/player.h"
37 #include "engines/icb/global_objects.h"
38 #include "engines/icb/global_switches.h"
39 #include "engines/icb/res_man.h"
40 #include "engines/icb/floors.h"
41 #include "engines/icb/mission.h"
42 
43 namespace ICB {
44 
45 #define RUBBER 30
46 #define REAL_RUBBER (30 * REAL_ONE)
47 
48 PXvector *posi;
49 uint32 this_rect;
50 _floor *obfloor;
51 
fn_floor_and_floor_camera_linked(int32 & result,int32 * params)52 mcodeFunctionReturnCodes fn_floor_and_floor_camera_linked(int32 &result, int32 *params) { return (MS->fn_floor_and_floor_camera_linked(result, params)); }
53 
fn_switch_to_manual_camera(int32 & result,int32 * params)54 mcodeFunctionReturnCodes fn_switch_to_manual_camera(int32 &result, int32 *params) { return (MS->fn_switch_to_manual_camera(result, params)); }
55 
fn_cancel_manual_camera(int32 & result,int32 * params)56 mcodeFunctionReturnCodes fn_cancel_manual_camera(int32 &result, int32 *params) { return (MS->fn_cancel_manual_camera(result, params)); }
57 
fn_is_current_camera(int32 & result,int32 * params)58 mcodeFunctionReturnCodes fn_is_current_camera(int32 &result, int32 *params) { return (MS->fn_is_current_camera(result, params)); }
59 
fn_is_current_location(int32 & result,int32 * params)60 mcodeFunctionReturnCodes fn_is_current_location(int32 &result, int32 *params) { return (MS->fn_is_current_location(result, params)); }
61 
fn_floor_and_floor_camera_linked(int32 &,int32 * params)62 mcodeFunctionReturnCodes _game_session::fn_floor_and_floor_camera_linked(int32 &, int32 *params) {
63 	// params    0       ascii name of home floor
64 	//			1      ascii name of viewable floor name
65 
66 	// search for a camera of this name
67 	// confirm floor exists
68 	// add floor number to cameras extra floor list
69 
70 	uint32 home_floor_num;
71 	uint32 floor_num;
72 	const char *home_floor_name = (const char *)MemoryUtil::resolvePtr(params[0]);
73 	const char *floor_name = (const char *)MemoryUtil::resolvePtr(params[1]);
74 
75 	home_floor_num = floor_def->Fetch_floor_number_by_name(home_floor_name);
76 	if (home_floor_num == PX_LINKED_DATA_FILE_ERROR)
77 		Fatal_error("fn_floor_and_floor_camera_linked cant find floor [%s]", home_floor_name);
78 
79 	floor_num = floor_def->Fetch_floor_number_by_name(floor_name);
80 	if (floor_num == PX_LINKED_DATA_FILE_ERROR)
81 		Fatal_error("fn_floor_and_floor_camera_linked cant find floor [%s]", floor_name);
82 
83 	if (home_floor_num == floor_num)
84 		Fatal_error("fn_floor_and_floor_camera_linked finds [%s] and [%s] are same floor!", home_floor_name, floor_name);
85 
86 	// add  viewable floor number to the camera that displays our primary home floor
87 
88 	uint32 index1 = floor_to_camera_index[home_floor_num];
89 	uint32 index2 = floor_to_camera_index[floor_num];
90 
91 	cam_floor_list[index1].extra_floors[cam_floor_list[index1].num_extra_floors++] = floor_num;
92 	cam_floor_list[index2].extra_floors[cam_floor_list[index2].num_extra_floors++] = home_floor_num;
93 
94 	if (cam_floor_list[index1].num_extra_floors == MAX_extra_floors)
95 		Fatal_error("fn_floor_and_floor_camera_linked too many extra floors");
96 
97 	return IR_CONT;
98 }
99 
Build_camera_table()100 void _game_session::Build_camera_table() {
101 	// read each floor to note the name of the camera
102 	// each new camera name is put into a list of names
103 	// the floor number gets the numer of that camerea in the list
104 	uint32 j, k;
105 	_floor *floor;
106 	uint32 tot;
107 
108 	Zdebug("\n***********building camera table**************");
109 
110 	num_cameras = 0; // reset
111 
112 	// reset extra cams table
113 	for (j = 0; j < MAX_floors; j++)
114 		cam_floor_list[j].num_extra_floors = 0;
115 
116 	tot = floor_def->Fetch_total_floors();
117 	if (!tot)
118 		Fatal_error("Build_camera_table finds no floors?");
119 
120 	for (j = 0; j < tot; j++) {
121 		floor = (_floor *)floor_def->Fetch_floor_number(j);
122 		if (floor->camera_name_offset) {
123 			// is this camera name already been assigned a slot?
124 			k = 0;
125 			while ((k < num_cameras) && (strcmp(camera_name_list[k], (((char *)floor) + floor->camera_name_offset))))
126 				++k;
127 
128 			if (k == num_cameras) { // didnt find this camera
129 				Zdebug(" new camera %d [%s] [%s]", num_cameras, (((char *)floor) + floor->camera_name_offset), floor->camera_cluster);
130 				camera_name_list[num_cameras] = (((char *)floor) + floor->camera_name_offset); // add camera name to list of names
131 				camera_cluster_list[num_cameras] = floor->camera_cluster;
132 				floor_to_camera_index[j] = num_cameras; // set this floors camera number
133 
134 				num_cameras++;
135 			} else {
136 				floor_to_camera_index[j] = k; // set this floors camera number
137 			}
138 
139 			Zdebug(" floor %d gets camera number %d", j, floor_to_camera_index[j]);
140 		} else {
141 			// camera name is missing
142 			floor_to_camera_index[j] = NO_CAMERA_CHOSEN;
143 			Zdebug("floor %d camera missing!!!!!!!!!!!", j);
144 		}
145 	}
146 
147 	Zdebug("***********building camera table**************\n");
148 }
149 
Object_visible_to_camera(uint32 id)150 bool8 _game_session::Object_visible_to_camera(uint32 id) {
151 	// is id visible to current camera
152 
153 	uint32 j, num_extra;
154 #define FLOOR_NO logic_structs[id]->owner_floor_rect
155 #define MM logic_structs[id]->mega
156 
157 	// Don't want to see held objects
158 	if (logic_structs[id]->ob_status == OB_STATUS_HELD)
159 		return (FALSE8);
160 
161 	// high level user clip
162 	if ((MM) && (!MM->display_me))
163 		return FALSE8;
164 
165 	// check for no camera chosen - 1st cycle
166 	if (cur_camera_number == NO_CAMERA_CHOSEN)
167 		return (FALSE8);
168 
169 	// if following the player then always TRUE8 - need this because of rubber band
170 	if ((!g_mission->camera_follow_id_overide) && (id == player.Fetch_player_id()))
171 		return TRUE8;
172 
173 	if (floor_to_camera_index[FLOOR_NO] == cur_camera_number)
174 		return (TRUE8);
175 
176 	// now check if the current floor is registered as an extra floor viewable from current camera
177 	num_extra = cam_floor_list[cur_camera_number].num_extra_floors;
178 	for (j = 0; j < num_extra; j++)
179 		if (cam_floor_list[cur_camera_number].extra_floors[j] == FLOOR_NO)
180 			return (TRUE8);
181 
182 	// definitely not on screen
183 	return (FALSE8);
184 }
185 
Reset_camera_director()186 void _game_session::Reset_camera_director() {
187 	// by masking this off we force a new camera to be initialised in current view mode
188 	cur_camera_number = NO_CAMERA_CHOSEN;
189 	manual_camera = FALSE8;
190 	wa_camera = FALSE8;
191 	wa_tied_to_pin = FALSE8;
192 	wa_tied_to_exit_pin = FALSE8;
193 	this_rect = 0; // will be legal floor which is all that matters
194 }
195 
Contains(int32 x1,int32 y1,int32 x2,int32 y2,int32 mx,int32 my)196 int32 Contains(int32 x1, int32 y1, int32 x2, int32 y2, int32 mx, int32 my) {
197 	int32 tmp, x3;
198 
199 	x1 = x1 << 1;
200 	y1 = y1 << 1;
201 	x2 = x2 << 1;
202 	y2 = y2 << 1;
203 	mx = (mx << 1) + 1;
204 	my = (my << 1) + 1;
205 
206 	if (((y1 < my) && (y2 > my)) || ((y1 > my) && (y2 < my))) {
207 		if (x1 > x2) {
208 			tmp = x1;
209 			x1 = x2;
210 			x2 = tmp;
211 			tmp = y1;
212 			y1 = y2;
213 			y2 = tmp;
214 		}
215 		if (y1 < y2)
216 			x3 = x1 + (my - y1) * (x2 - x1) / (y2 - y1);
217 		else
218 			x3 = x2 - (my - y2) * (x2 - x1) / (y1 - y2);
219 		if (x3 < mx)
220 			return 1;
221 	}
222 	return 0;
223 }
224 
Camera_director()225 void _game_session::Camera_director() {
226 	// check which object the camera is linked to
227 	// check which floor rect that object is on
228 	// if no camera run a script
229 	// else if new camera switch it in
230 
231 	const __aWalkArea *wa;
232 	uint32 k;
233 	uint32 hit;
234 	PXreal sub1, sub2, len, y;
235 
236 	Prepare_camera_floors();
237 
238 	// a manual script camera completely overides the floor and WA system cameras
239 	if ((manual_camera) || (camera_lock))
240 		return;
241 
242 	// do walkarea stuff first
243 	// are we currently on one?
244 	// if so are we still on it
245 	// if not are we on one in the list
246 	// if so start it
247 	// else go through normal floor system
248 
249 	if (wa_camera) {
250 		// we are currently using a WA camera
251 
252 		wa = MS->wa_list[wa_number];
253 
254 		y = floor_def->Return_true_y((PXreal)wa->y);
255 
256 		if ((y >= obfloor->base_height) && (y < (floor_def->Fetch_floors_volume_height(this_rect)))) {
257 
258 			if (wa_tied_to_pin) {
259 				// we are still tied to the pin point
260 				// if we are within the stretch distance from the pin then we remain on the wa camera - even if we are now
261 				// actually outside of it
262 
263 				sub1 = (PXreal)posi->x - wa_pin_x;
264 				sub2 = (PXreal)posi->z - wa_pin_z;
265 
266 				// dist
267 				len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
268 
269 				Tdebug("rubber.txt", "len %3.2f   pos %3.2f, %3.2f pin %3.2f, %3.2f", len, posi->x, posi->z, wa_pin_x, wa_pin_z);
270 
271 				if (len < (PXreal)(REAL_RUBBER * REAL_RUBBER))
272 					return; // still constrained by the band
273 
274 				// band is broken
275 				wa_tied_to_pin = FALSE8; // release
276 			}
277 
278 			if ((posi->x > wa->x) && (posi->x < (wa->x + wa->w)) && (posi->z > wa->z) && (posi->z < (wa->z + wa->h))) {
279 				// we're inside the rectangle
280 				// are we within the polygon
281 
282 				hit = 0; // result is 0, miss
283 
284 				for (k = 0; k < (wa->noPoints - 1); k++) {
285 					if (Contains(wa->points[k].x, wa->points[k].z, wa->points[k + 1].x, wa->points[k + 1].z, (uint32)posi->x, (uint32)posi->z))
286 						hit = 1 - hit;
287 				}
288 
289 				if (hit) { // hurrah - still hitting
290 					cur_camera_number = floor_to_camera_index[this_rect];
291 					return;
292 				}
293 			}
294 		}
295 
296 		// only stick an off pin in if we're not wandering straight onto another
297 		if (!Process_wa_list()) {
298 			// not hitting anymore
299 			if (!g_mission->camera_follow_id_overide)
300 				this_rect = floor_def->Return_non_rubber_floor_no(logic_structs[player.Fetch_player_id()], this_rect);
301 			else
302 				this_rect = floor_def->Return_non_rubber_floor_no(logic_structs[g_mission->camera_follow_id_overide], this_rect);
303 
304 			// stick a pin in that we must leave before hitting a wa again
305 			wa_pin_x = posi->x;
306 			wa_pin_y = posi->y;
307 			wa_pin_z = posi->z;
308 			wa_tied_to_exit_pin = TRUE8;
309 
310 			cur_camera_number = NO_CAMERA_CHOSEN; // force a choose
311 			wa_camera = FALSE8;                   // not any more
312 		}
313 	}
314 
315 	// not on one so check em all to see if we are
316 	// but first check to see if we are tied to a get-off-wa pin
317 
318 	if (wa_tied_to_exit_pin) {
319 
320 		if (wa_pin_y != posi->y) {            // as soon as we change y the pin breaks
321 			wa_tied_to_exit_pin = FALSE8; // off
322 		} else {
323 			sub1 = (PXreal)posi->x - wa_pin_x;
324 			sub2 = (PXreal)posi->z - wa_pin_z;
325 
326 			// dist
327 			len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
328 
329 			if (len > (PXreal)(REAL_RUBBER * REAL_RUBBER)) {
330 				// band is broken
331 				wa_tied_to_exit_pin = FALSE8; // release
332 			}
333 		}
334 	} else {
335 		Process_wa_list();
336 	}
337 
338 	// not on a WA camera so check via the floor rects
339 
340 	if (this_rect == PXNULL) { // object not on a rect??
341 		// we cant pick a camera set view
342 		// if a set is set up then that will continue to be used
343 		// if there isnt a set at all (this is first cycle) then engine will switch to nethack mode
344 		return;
345 	}
346 
347 	// has player changed camera - work this out from primary camera
348 	if (cur_camera_number != floor_to_camera_index[this_rect]) {
349 		// we are now on a floor with a different camera
350 		// BUT
351 		// we allow some elastic on the old one before switching
352 
353 		// get floor that we last changed camera on
354 		// if we are within its bounds then remain
355 
356 		if (cur_camera_number != NO_CAMERA_CHOSEN) {
357 			_floor *floor;
358 			floor = (_floor *)floor_def->Fetch_floor_number(anchor_floor);
359 
360 			if ((posi->y == (PXreal)(floor->base_height)) && (posi->x >= (PXreal)(floor->rect.x1 - RUBBER)) && (posi->x <= (PXreal)(floor->rect.x2 + RUBBER)) &&
361 			    (posi->z >= (PXreal)(floor->rect.z1 - RUBBER)) && (posi->z <= (PXreal)(floor->rect.z2 + RUBBER)))
362 				return; // still within the rubber banded camera
363 		}
364 
365 		if (floor_to_camera_index[this_rect] == 0xffffffff) { // no named camera so its a more complex logic switch
366 			// ok, lets assume that there was no special camera!
367 			g_px->display_mode = TEMP_NETHACK; // stick us into temporary nethack mode which will bounce out again if it can
368 			Zdebug("no named camera! - entering TEMP_NETHACK");
369 		} else { // ok, there is a named camera! and we know its different from current
370 			// different from current
371 			Zdebug(" make cam=%s %s", camera_name_list[floor_to_camera_index[this_rect]], camera_cluster_list[floor_to_camera_index[this_rect]]);
372 
373 			// set camera number
374 			cur_camera_number = floor_to_camera_index[this_rect];
375 			anchor_floor = this_rect;
376 
377 			// if we're not in NETHACK mode then switch in the set
378 			if (g_px->display_mode != NETHACK) {
379 				g_px->display_mode = THREED; // force back in-case we were in TEMP_NETHACK
380 
381 				// init the new set - Initialise_set will record the name of the new camera
382 				// it will also handle missing set files and bung us into TEMP_NETHACK mode if it has to
383 				Initialise_set(camera_name_list[floor_to_camera_index[this_rect]], camera_cluster_list[floor_to_camera_index[this_rect]]); // name);
384 
385 				// force in the anims of megas not playing stand - techs and the like
386 				MS->One_logic_cycle();
387 			}
388 		}
389 	}
390 }
391 
Prepare_camera_floors()392 void _game_session::Prepare_camera_floors() {
393 	// get floor numbers from followed object
394 	// get a mega class objects world position - can be player or other mega
395 	if (!g_mission->camera_follow_id_overide) {
396 		if (!player.Player_exists())
397 			Fatal_error("camera director cant choose a scene as player object has been shut down");
398 
399 		if (logic_structs[player.Fetch_player_id()]->ob_status == OB_STATUS_HELD)
400 			Fatal_error("camera director cant choose a scene as player object has been shut down");
401 
402 		posi = (PXvector *)&logic_structs[player.Fetch_player_id()]->mega->actor_xyz;
403 
404 		this_rect = logic_structs[player.Fetch_player_id()]->owner_floor_rect;
405 	} else {
406 		// following another mega character
407 
408 		posi = (PXvector *)&logic_structs[g_mission->camera_follow_id_overide]->mega->actor_xyz;
409 
410 		this_rect = logic_structs[g_mission->camera_follow_id_overide]->owner_floor_rect;
411 	}
412 
413 	// fetch the floor
414 	obfloor = (_floor *)floor_def->Fetch_floor_number(this_rect);
415 }
416 
Process_wa_list()417 bool8 _game_session::Process_wa_list() {
418 	const __aWalkArea *wa;
419 	uint32 j, k;
420 	uint32 hit;
421 	char *name;
422 	PXreal y;
423 
424 	for (j = 0; j < MS->total_was; j++) {
425 		wa = MS->wa_list[j];
426 
427 		y = floor_def->Return_true_y((PXreal)wa->y);
428 
429 		if ((y >= obfloor->base_height) && (y < (floor_def->Fetch_floors_volume_height(this_rect)))) {
430 			if ((posi->x > wa->x) && (posi->x < (wa->x + wa->w)) && (posi->z > wa->z) && (posi->z < (wa->z + wa->h))) {
431 				// we're inside the rectangle
432 				// are we within the polygon
433 
434 				hit = 0; // result is 0, miss
435 
436 				for (k = 0; k < (wa->noPoints - 1); k++) {
437 					if (Contains(wa->points[k].x, wa->points[k].z, wa->points[k + 1].x, wa->points[k + 1].z, (uint32)posi->x, (uint32)posi->z))
438 						hit = 1 - hit;
439 				}
440 
441 				if (hit) { // hey we hit the closed poly
442 					name = (char *)const_cast<ICB::__point *>(&wa->points[wa->noPoints]);
443 
444 					Tdebug("cam_changes.txt", " WA camera name %s cluster %s", name, wa->cameraCluster);
445 
446 					Initialise_set(name, wa->cameraCluster); // name, clusterVersion of the name
447 
448 					wa_camera = TRUE8;
449 					wa_number = j;
450 					wa_pin_x = posi->x;
451 					wa_pin_z = posi->z;
452 					wa_tied_to_pin = TRUE8;
453 
454 					// in case we are coming here afresh - from a reset camera director - i.e. from nethack mode
455 					// find the floor and set the current camera
456 					// in other words we fake the system to think that the floor camera is chosen - thats how we decide
457 					// who is on camera.
458 
459 					if (!g_mission->camera_follow_id_overide) {
460 						this_rect = floor_def->Return_floor_rect(posi->x, posi->z, posi->y, 0);
461 					} else {
462 						this_rect = floor_def->Return_non_rubber_floor_no(logic_structs[g_mission->camera_follow_id_overide], this_rect);
463 					}
464 
465 					// set camera number
466 					cur_camera_number = floor_to_camera_index[this_rect];
467 
468 					Tdebug("cam_changes.txt", "  floor %d", cur_camera_number);
469 
470 					return TRUE8;
471 				}
472 			}
473 		}
474 	}
475 
476 	return FALSE8;
477 }
478 
fn_switch_to_manual_camera(int32 &,int32 * params)479 mcodeFunctionReturnCodes _game_session::fn_switch_to_manual_camera(int32 &, int32 *params) {
480 	// switch in a manual camera
481 
482 	//	params   0   stub name of room - CORRIDOR\pc\camera
483 	//				1 stub name of camera - corridor\pc\CAMERA
484 	//				2 name of primary floor
485 	uint32 floor_num;
486 	uint32 len;
487 	char h_buf[8];
488 	const char *room_name = (const char *)MemoryUtil::resolvePtr(params[0]);
489 	const char *camera_name = (const char *)MemoryUtil::resolvePtr(params[1]);
490 	const char *floor_name = (const char *)MemoryUtil::resolvePtr(params[2]);
491 
492 	// get primary floor number
493 	floor_num = floor_def->Fetch_floor_number_by_name(floor_name);
494 
495 	// set camera number
496 	cur_camera_number = floor_to_camera_index[floor_num];
497 
498 	manual_camera = TRUE8;
499 
500 	len = sprintf(manual_camera_name, "%s\\pc\\%s", room_name, camera_name);
501 	if (len > ENGINE_STRING_LEN)
502 		Fatal_error("fn_switch_to_manual_camera string len error");
503 
504 	HashFile(manual_camera_name, h_buf);
505 
506 	Tdebug("cam_changes.txt", " built name %s %s", (const char *)temp_buf, h_buf);
507 	Initialise_set(manual_camera_name, h_buf); // name);
508 
509 	return IR_CONT;
510 }
511 
fn_is_current_camera(int32 & result,int32 * params)512 mcodeFunctionReturnCodes _game_session::fn_is_current_camera(int32 &result, int32 *params) {
513 	// check passed string against current camera
514 	//	params   0   ascii name
515 	const char *camera_name = (const char *)MemoryUtil::resolvePtr(params[0]);
516 
517 	// check for not set initialised yet - 1st game cycle
518 	if (!set.OK()) {
519 		result = 0;
520 		return IR_CONT;
521 	}
522 
523 	if (!strstr(set.GetSetName(), camera_name))
524 		result = 0; // no
525 	else
526 		result = 1;
527 
528 	return IR_CONT;
529 }
530 
fn_is_current_location(int32 & result,int32 * params)531 mcodeFunctionReturnCodes _game_session::fn_is_current_location(int32 &result, int32 *params) {
532 	char h_buf[8];
533 	uint32 len;
534 	const char *location_name = (const char *)MemoryUtil::resolvePtr(params[0]);
535 
536 	// First we need to know which location the player is in, because the information level
537 	// for this is automatically at full.
538 	//uint32 nPlayerFloorIndex = MS->logic_structs[MS->player.Fetch_player_id()]->owner_floor_rect;
539 
540 	Message_box("is %s current location?", location_name);
541 
542 	len = sprintf(manual_camera_name, "%s\\pc\\%s", location_name, set.GetSetName());
543 	if (len > ENGINE_STRING_LEN)
544 		Fatal_error("fn_is_current_location string len error");
545 
546 	HashFile(manual_camera_name, h_buf);
547 	result = 1;
548 	// continue script
549 	return IR_CONT;
550 }
551 
fn_cancel_manual_camera(int32 &,int32 *)552 mcodeFunctionReturnCodes _game_session::fn_cancel_manual_camera(int32 &, int32 *) {
553 	// cancel the manual camera
554 	Tdebug("cam_changes.txt", "Releasing manual camera");
555 
556 	if (!manual_camera)
557 		return IR_CONT;
558 
559 	Reset_camera_director();
560 
561 	return (IR_CONT);
562 }
563 
564 // Compute if mega's are on or off current camera and send events to them to say "ON_CAMERA" / "OFF_CAMERA"
UpdateOnOffCamera()565 void _game_session::UpdateOnOffCamera() {
566 	PXcamera &camera = GetCamera();
567 	_logic *log;
568 
569 	for (uint32 j = 0; j < number_of_voxel_ids; j++) {
570 		// fetch the logic structure for the game object that has a voxel image to render
571 		log = logic_structs[voxel_id_list[j]];
572 
573 		// shift the this cycle camera flag to last cycle camera flag
574 		log->mega->ShiftViewState();
575 
576 		// person owned by current camera floor? TEMP check
577 		if (Object_visible_to_camera(voxel_id_list[j])) {
578 			bool8 result = TRUE8;
579 			PXvector filmPosition;
580 			PXWorldToFilm(log->mega->actor_xyz, camera, result, filmPosition);
581 
582 			// i.e. his feet are visible !
583 			if (result && (filmPosition.z < -g_actor_hither_plane))
584 				log->mega->SetThisViewState(ON_CAMERA);
585 		}
586 
587 		// Now test the view state flags and send an appropriate event
588 		// Just walked on camera
589 		if (log->mega->viewState == OFF_ON_CAMERA) {
590 			g_oEventManager->PostNamedEvent(EVENT_ON_CAMERA, voxel_id_list[j]);
591 		}
592 		// Just walked off camera
593 		else if (log->mega->viewState == ON_OFF_CAMERA) {
594 			g_oEventManager->PostNamedEvent(EVENT_OFF_CAMERA, voxel_id_list[j]);
595 		}
596 	}
597 }
598 
599 } // End of namespace ICB
600