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/debug.h"
30 #include "engines/icb/p4_generic.h"
31 #include "engines/icb/res_man.h"
32 #include "engines/icb/common/px_common.h"
33 #include "engines/icb/common/px_game_object.h"
34 #include "engines/icb/common/px_scriptengine.h"
35 #include "engines/icb/common/px_prop_anims.h"
36 #include "engines/icb/common/px_walkarea_integer.h"
37 #include "engines/icb/object_structs.h"
38 #include "engines/icb/session.h"
39 #include "engines/icb/mission.h"
40 #include "engines/icb/common/px_linkeddatafile.h"
41 #include "engines/icb/floors.h"
42 #include "engines/icb/barriers.h"
43 #include "engines/icb/global_objects.h"
44 #include "engines/icb/global_switches.h"
45 #include "engines/icb/remora.h"
46 #include "engines/icb/sound_logic.h"
47 #include "engines/icb/loadscrn.h"
48 #include "engines/icb/sound.h"
49 #include "engines/icb/sound_lowlevel.h"
50 
51 namespace ICB {
52 
53 
54 // Translation tweaks
55 
56 _linked_data_file *LoadTranslatedFile(const char *session, const char *mission);
57 
58 
59 // prototypes
60 int32 Fetch_token_value(uint8 *file, uint32 length, uint8 *token);
61 
62 void ClearTextures();
63 
___init(const char * mission,const char * new_session_name)64 void _game_session::___init(const char *mission, const char *new_session_name) {
65 	// session object constructor
66 	// set up a game_session object from a session name
67 
68 	uint32 buf_hash;
69 
70 	// begin with no set object
71 	// a camera will be choosen after the first logic cycle based upon the player objects position
72 	set.Reset();
73 
74 	// no special footsteps set
75 
76 	numFloorFootSfx = 0;
77 	specialFootSfx = 0;
78 	ladderFootSfx = 0;
79 	defaultFootSfx = 0;
80 
81 	// setup speech text block pointer
82 	text_bloc = g_text_bloc1;
83 	text_speech_bloc = g_text_bloc2; // pc has second bloc
84 
85 	// If you die when you have an unread email and restart the mission, the email icon continues
86 	// to flash.  The Remora should reset this when it initialises but since that happens after all
87 	// the objects have been loaded, I decided it would be safest to reset the flag here.  This is
88 	// very much an 11th-hour hack.
89 	g_oRemora->MarkEmailRead();
90 
91 	// first clear out private_session_resman
92 	private_session_resman->Reset();
93 	// tell it that the resources cannot be moved about
94 	private_session_resman->Set_to_no_defrag();
95 
96 	ClearTextures();
97 
98 	if (camera_hack == TRUE8) {
99 		total_objects = 0;
100 		return;
101 	}
102 
103 	// Make the filename equivalent of the hash'ed version of session name
104 	HashFile(new_session_name, session_h_name);
105 
106 	// Put the hash mission and hash session filenames together
107 	char h_mission_name[8];
108 	HashFile(mission, h_mission_name);
109 
110 	sprintf(speech_font_one, FONT_PATH, "font.pcfont");
111 	sprintf(remora_font, FONT_PATH, "futura.pcfont");
112 
113 	if (sprintf(session_name, "%s\\%s\\", mission, new_session_name) > ENGINE_STRING_LEN)
114 		Fatal_error("_game_session::_game_session [%s] string overflow", session_name);
115 
116 	if (sprintf(h_session_name, "%s\\%s", h_mission_name, session_h_name) > ENGINE_STRING_LEN)
117 		Fatal_error("_game_session::_game_session [%s] string overflow", h_session_name);
118 
119 	if (sprintf(session_cluster, SESSION_CLUSTER_PATH, h_mission_name, session_h_name) > ENGINE_STRING_LEN)
120 		Fatal_error("_game_session::_game_session [%s] string overflow", session_cluster);
121 
122 	session_cluster_hash = HashString(session_cluster);
123 	speech_font_one_hash = HashString(speech_font_one);
124 	remora_font_hash = HashString(remora_font);
125 	Zdebug("_game_session %s", (const char *)session_name);
126 
127 // now setup the session
128 // load all the fixed name files
129 
130 //	mission<
131 //		session<
132 //			objects.linked
133 //			scripts.linked
134 //			walkgrid.grid
135 //			session.RVanims
136 //			camera-name<
137 
138 	// Jake : so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
139 	StartLoading(new_session_name);
140 
141 	LoadMsg("Session Cluster");
142 
143 	// right, on the psx for the between session sound we need to make sure the
144 	// resman has the mission sound stuff in memory BEFORE we do the
145 	// StartLoading...
146 
147 	LoadMsg("Session Sound");
148 
149 	// setup sound data cluster on psx...
150 
151 	LoadSessionSounds(session_cluster);
152 
153 
154 	// initialise the session game objects
155 	// we can assume all of these in here will be of the game object class!
156 
157 	// When clustered the session files have the base stripped
158 	strcpy(temp_buf, "objects");
159 
160 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
161 	LoadMsg("Session Objects");
162 
163 	// Make Res_open compute the hash value
164 	buf_hash = NULL_HASH;
165 	objects = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, session_cluster, session_cluster_hash);
166 
167 	//	set this for convenience
168 	total_objects = objects->Fetch_number_of_items();
169 	Zdebug("total objects %d", total_objects);
170 
171 	if (total_objects >= MAX_session_objects)
172 		Fatal_error("too many objects! max available %d", MAX_session_objects);
173 
174 	// create the prop table
175 	// each object has a prop state - though only objects that are linked to background props will use them
176 	// an objects number maps to the state table
177 	//	prop_state_table = new   uint32[total_objects+10];
178 	uint32 j;
179 	for (j = 0; j < total_objects; j++)
180 		prop_state_table[j] = 0;
181 
182 	// inititialise the session scripts
183 
184 	// When clustered the session files have the base stripped
185 	strcpy(temp_buf, "scripts");
186 
187 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
188 	LoadMsg("Session Scripts");
189 	buf_hash = NULL_HASH;
190 	scripts = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, session_cluster, session_cluster_hash);
191 
192 	// display script version info
193 	// also available on console
194 	Script_version_check();
195 
196 	// initialise prop animation file
197 
198 	// When clustered the session files have the base stripped
199 	strcpy(temp_buf, PX_FILENAME_PROPANIMS);
200 
201 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
202 	LoadMsg("Session PropAnims");
203 	buf_hash = NULL_HASH;
204 	prop_anims = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, session_cluster, session_cluster_hash);
205 
206 	// Check file version is correct.
207 	if (prop_anims->GetHeaderVersion() != VERSION_PXWGPROPANIMS)
208 		Fatal_error("%s version check failed (file has %d, engine has %d)", temp_buf, prop_anims->GetHeaderVersion(), VERSION_PXWGPROPANIMS);
209 
210 	// init features file
211 	// we stick this in the private cache so it hangs around and later in-game references wont cause a main pool reload
212 
213 	// When clustered the session files have the base stripped
214 	strcpy(temp_buf, "pxwgfeatures");
215 
216 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
217 	LoadMsg("Session Features");
218 	buf_hash = NULL_HASH;
219 	features = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, session_cluster, session_cluster_hash);
220 
221 	// engine knows no set/camera chosen
222 	Reset_camera_director();
223 	camera_lock = FALSE8; // move to camera director
224 
225 	// reset the route manager service
226 	Reset_route_manager();
227 
228 	text_bloc->please_render = FALSE8;
229 	text_speech_bloc->please_render = FALSE8;
230 	conv_focus = 0; // no conversation is in focus
231 	total_convs = 0; // no conversations
232 
233 	Tdebug("text_lines.txt", "\n\n---Text Lines---\n");
234 
235 	// text
236 	text = NULL; // game can exist with this file
237 
238 	char textFileName[100];
239 
240 	// When clustered the session files have the base stripped
241 	strcpy(temp_buf, "text");
242 
243 	buf_hash = HashString(temp_buf);
244 	if (private_session_resman->Test_file(temp_buf, buf_hash, session_cluster, session_cluster_hash)) {
245 		// Jake : so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
246 		LoadMsg("Session Text");
247 
248 		// Special text loading code so the translators can test their stuff
249 
250 		if (tt) {
251 			// Ok, translators mode has been activated
252 			text = LoadTranslatedFile(mission, session_name);
253 		} else
254 			text = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, session_cluster, session_cluster_hash);
255 	} else
256 		Fatal_error("Missing Text File \"%s\"", temp_buf);
257 
258 	Tdebug("session.txt", "text lines END");
259 
260 	// A copy of the code above to open the global text file.  Feel free to edit this if I've ballsed up
261 	// anywhere.
262 	global_text = NULL;
263 
264 	char global_cluster[ENGINE_STRING_LEN];
265 
266 	strcpy(global_cluster, GLOBAL_CLUSTER_PATH);
267 
268 	uint32 global_cluster_hash = HashString(global_cluster);
269 
270 	sprintf(textFileName, GLOBAL_TEXT_FILE);
271 
272 	buf_hash = HashString(textFileName);
273 
274 	if (private_session_resman->Test_file(textFileName, buf_hash, global_cluster, global_cluster_hash)) {
275 		LoadMsg(temp_buf);
276 
277 		if (tt) {
278 			// Ok, translators mode has been activated
279 			global_text = LoadTranslatedFile("global", "global\\global\\");
280 		} else
281 			global_text = (_linked_data_file *)private_session_resman->Res_open(textFileName, buf_hash, global_cluster, global_cluster_hash);
282 
283 	} else {
284 		Fatal_error("Failed to find global text file [%s][%s]", textFileName, global_cluster);
285 	}
286 
287 	// The surface manager needs to know what colour to use for transparency.  This comes from a fixed
288 	// reference file which is opened here so the global reference can be set.
289 	g_oIconMenu->SetTransparencyColourKey();
290 
291 	// Initialise the remora
292 	g_oRemora->InitialiseRemora();
293 
294 	// Set the default colour for voice over text.
295 	voice_over_red = VOICE_OVER_DEFAULT_RED;
296 	voice_over_green = VOICE_OVER_DEFAULT_GREEN;
297 	voice_over_blue = VOICE_OVER_DEFAULT_BLUE;
298 
299 	Tdebug("session.txt", "CHI START");
300 	// chi
301 	// players movement history
302 	cur_history = 0;
303 	chi_think_mode = __NOTHING;
304 
305 	is_there_a_chi = FALSE8;
306 
307 	Tdebug("session.txt", "walkareas START");
308 
309 	// setup walkareas
310 	total_was = 0;
311 
312 	// When clustered the session files have the base stripped
313 	strcpy(temp_buf, "walkarea");
314 
315 	buf_hash = HashString(temp_buf);
316 
317 	// Jake : so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
318 	LoadMsg("Session Walkareas");
319 	uint32 len = private_session_resman->Check_file_size(temp_buf, buf_hash, session_cluster, session_cluster_hash);
320 
321 	if (len) {
322 		walk_areas = (_linked_data_file *)private_session_resman->Res_open(temp_buf, buf_hash, session_cluster, session_cluster_hash);
323 		Tdebug("walkareas.txt", "%d top level walkareas\n", walk_areas->Fetch_number_of_items());
324 
325 		int32 nMissing = 0;
326 		for (uint32 k = 0; k < walk_areas->Fetch_number_of_items(); k++) {
327 			INTEGER_WalkAreaFile *inner_wa;
328 			inner_wa = (INTEGER_WalkAreaFile *)walk_areas->Fetch_item_by_number(k);
329 
330 			Tdebug("walkareas.txt", "\nclump %d has %d inner items", k, inner_wa->GetNoAreas());
331 
332 			for (j = 0; j < inner_wa->GetNoAreas(); j++) {
333 				const __aWalkArea *wa;
334 
335 				wa = inner_wa->GetWalkArea(j);
336 
337 				wa_list[total_was++] = wa; // write entry to individual item list
338 
339 				if (total_was == MAX_was)
340 					Fatal_error("total number of walk-areas exceeded - %d", MAX_was);
341 			}
342 		}
343 		if (nMissing > 0)
344 			Fatal_error("%d missing cameras : Game must terminate", nMissing);
345 
346 		Tdebug("walkareas.txt", "\n%d individual walk areas found", total_was);
347 
348 	} else
349 		Tdebug("walkareas.txt", "no walkarea file");
350 
351 	number_of_missing_objects = 0; // start with no missing objects
352 
353 	// structure assignment counters - see fn_create_mega
354 	num_megas = 0;
355 	num_vox_images = 0;
356 
357 	// init conveyors
358 	for (j = 0; j < MAX_conveyors; j++)
359 		conveyors[j].moving = FALSE8;
360 
361 	// init auto interact
362 	for (j = 0; j < MAX_auto_interact; j++)
363 		auto_interact_list[j] = 0; // no entry
364 
365 	// stairs
366 	num_stairs = 0;
367 
368 	// lifts
369 	num_lifts = 0;
370 
371 	// turn off health bar
372 	health_time = 0;
373 
374 	// setup generic asyncer
375 	player_stat_was = __TOTAL_PLAYER_MODES; // for 'prev' check
376 	player_stat_use = __TOTAL_PLAYER_MODES; // for 'prev' check
377 	async_counter = 0; // counts up each frame
378 	async_off = 0;     // on by default
379 
380 	// first cycle indicator
381 	first_session_cycle = TRUE8;
382 
383 	Tdebug("session.txt", "session constructor END");
384 }
385 
Script_version_check()386 void _game_session::Script_version_check() {
387 	if (FN_ROUTINES_DATA_VERSION != scripts->GetHeaderVersion())
388 		Fatal_error("WARNING! SCRIPTS AND ENGINE ARE NOT SAME VERSION");
389 }
390 
___destruct()391 void _game_session::___destruct() {
392 	// session object deconstructor
393 	// kills the current session and removes its resources
394 
395 	Zdebug("*session destructing*");
396 
397 	// turn off all sounds
398 	StopAllSoundsNow();
399 
400 	Zdebug("sounds stopped");
401 
402 	// camview mode
403 	if (camera_hack == TRUE8) {
404 		SetReset();
405 		return;
406 	}
407 
408 	// trash resources
409 	private_session_resman->Reset();
410 
411 	// remove diag bars that have been newed
412 	for (uint32 j = 0; j < total_objects; j++)
413 		if (logic_structs[j]->mega)
414 			logic_structs[j]->mega->m_main_route.___init(); // delete diag bars
415 
416 	// delete current set view
417 	SetReset();
418 
419 	// delete player object
420 	//	delete   player;
421 
422 	// speech
423 	//	if   (text_bloc)
424 	//		delete  text_bloc;  //in-case quit during speech
425 }
426 
Initialise_set(const char * name,const char * cluster_name)427 void _game_session::Initialise_set(const char *name, const char *cluster_name) {
428 	// initialise a set object for the current session
429 	// stick us into TEMP_NETHACK mode if the set does not physically exist
430 
431 	// place a split point/boundary into the resource loading
432 	rs_bg->Advance_time_stamp();
433 
434 	// init the set
435 	set.Init(name, cluster_name);
436 
437 	// decide which props to sleep and which to wake
438 	Setup_prop_sleep_states();
439 }
440 
Setup_prop_sleep_states()441 void _game_session::Setup_prop_sleep_states() {
442 	// initialise the new set object
443 
444 	for (uint32 j = 0; j < total_objects; j++)
445 		if (!logic_structs[j]->mega) { // props
446 			logic_structs[j]->prop_on_this_screen = set.DoesPropExist((const char *)logic_structs[j]->GetName());
447 
448 			if (logic_structs[j]->hold_mode == prop_camera_hold) {
449 				if (!logic_structs[j]->prop_on_this_screen) {
450 					logic_structs[j]->camera_held = TRUE8; // not on screen
451 					logic_structs[j]->cycle_time = 0; // accurate for displays
452 				} else {
453 					logic_structs[j]->camera_held = FALSE8; // on screen
454 				}
455 			} else { // not an auto sleep item, but...
456 				//				check for sleeping items that are now on screen
457 				if ((logic_structs[j]->camera_held) && (logic_structs[j]->prop_on_this_screen)) { // held AND on screen (a door)
458 					logic_structs[j]->camera_held = FALSE8; // on screen and not a camera_hold_mode item so wake it up - i.e. doors
459 				}
460 			}
461 		}
462 }
463 
Awaken_doors()464 void _game_session::Awaken_doors() {
465 	// called to release doors when entering remora mode
466 
467 	for (uint32 j = 0; j < total_objects; j++)
468 		if ((logic_structs[j]->big_mode == __CUSTOM_BUTTON_OPERATED_DOOR) || (logic_structs[j]->big_mode == __CUSTOM_AUTO_DOOR)) {
469 			logic_structs[j]->camera_held = FALSE8; // awake!
470 			logic_structs[j]->prop_on_this_screen = TRUE8;
471 		}
472 }
473 
474 __mega_set_names player_startup_anims[] = {__STAND, __STAND_TO_WALK, __WALK, __WALK_TO_STAND, __TURN_ON_THE_SPOT_CLOCKWISE};
475 #define NUMBER_player_startup_anims 5
476 
Init_objects()477 void _game_session::Init_objects() {
478 	char buf[ENGINE_STRING_LEN];
479 	uint32 j, id;
480 
481 	if (!g_mission->inited_globals) {
482 		// init local globals
483 		// only do this at start of mission - never again afterward - i.e. not when returning to first session from another
484 		uint32 script_hash;
485 
486 		id = objects->Fetch_item_number_by_name("player"); // returns -1 if object not in existence
487 		if (id == 0xffffffff)
488 			Fatal_error("Init_objects cant find 'player'");
489 		script_hash = HashString("player::globals");
490 		const char *pc = (const char *)scripts->Try_fetch_item_by_hash(script_hash);
491 		if (pc) {
492 			object = (c_game_object *)objects->Fetch_item_by_number(id);
493 			Tdebug("objects_init.txt", " initialising globals", (const char *)buf);
494 			RunScript(pc, object);
495 		}
496 
497 		g_mission->inited_globals = TRUE8;
498 	}
499 
500 	Zdebug("\nInitialise_objects");
501 
502 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
503 	// this stuff moved here when ammo, bullets etc moved into player struct
504 	InitMsg("Player");
505 	// create a player object
506 	player.___init();
507 
508 	// Now run the InitScript for each object.  This has to be done after the line-of-sight end
509 	// event manager have been initialised in case calls get made to these services in any of the
510 	// objects' InitScripts.
511 	for (j = 0; ((j < total_objects)); j++) {
512 		object = (c_game_object *)objects->Fetch_item_by_number(j);
513 		Tdebug("objects_init.txt", "\n\n---------------------------------------------------\n%d  initialising object '%s'", j, object->GetName());
514 		Zdebug("\n\n---------------------------------------------------\n%d  initialising object '%s'", j, object->GetName());
515 
516 		Zdebug("[%d]", num_megas);
517 
518 		// fast reference for engine functions
519 		// needed incase structs are referenced by FN_ functions
520 		cur_id = j;
521 
522 		// set L for FN_ functions that may be called
523 		L = logic_structs[j];
524 		I = L->voxel_info;
525 		M = L->mega;
526 
527 		// possibly run the init OR logic context script to kick in a base script ready for running in the logic loop
528 		// the init script is always script 0 for the object
529 		// the init script may or may not be overiden
530 		// get the address of the script we want to run
531 		const char *pc = (const char *)scripts->Try_fetch_item_by_hash(object->GetScriptNameFullHash(OB_INIT_SCRIPT)); // run init script
532 
533 		if (pc) {
534 			RunScript(pc, object);
535 
536 			strcpy(buf, object->GetName());
537 			strcat(buf, "::local_init");
538 
539 			uint32 script_hash;
540 
541 			script_hash = HashString(buf);
542 
543 			// Jso PSX can have nice session loading screen and details (for timing and to stop player getting bored)
544 			InitMsg(object->GetName());
545 
546 			Tdebug("objects_init.txt", "search for [%s]", (const char *)buf);
547 
548 			pc = (const char *)scripts->Try_fetch_item_by_hash(script_hash);
549 
550 			if (pc) {
551 				//              set M and I for FN_ functions that may be called
552 
553 				I = L->voxel_info;
554 				M = L->mega;
555 
556 				Tdebug("objects_init.txt", " running optional = [%s]", (const char *)buf);
557 				RunScript(pc, object);
558 			} else
559 				Tdebug("objects_init.txt", " no [%s] found", (const char *)buf);
560 
561 			//          setup logic context
562 			//          set to start on level 0
563 			logic_structs[j]->logic_level = 0;
564 
565 			//          set base logic to logic context script
566 			logic_structs[j]->logic[0] = (char *)scripts->Try_fetch_item_by_hash(object->GetScriptNameFullHash(OB_LOGIC_CONTEXT));
567 			//          **note, we dont need to set up the script reference (logic_ref) for level 0
568 		} else
569 			Shut_down_object("by initialise - no init script");
570 
571 		L = logic_structs[j];
572 		I = L->voxel_info;
573 		if (L->image_type == VOXEL) {
574 			for (uint32 i = 0; i < __NON_GENERIC; i++) {
575 				if (I->IsAnimTable(i)) {
576 #ifdef PRELOAD
577 					rs_anims->Res_open(I->info_name[i], I->info_name_hash[i], I->base_path, I->base_path_hash);
578 #endif
579 				}
580 			}
581 		}
582 
583 	}
584 
585 	Tdebug("objects_init.txt", "\n\n\ncreating mega list");
586 	// create voxel id list
587 	number_of_voxel_ids = 0;
588 	for (j = 0; j < total_objects; j++) { // object 0 is used
589 		//		object must be alive and interactable and a mega
590 		if ((logic_structs[j]->image_type == VOXEL) && (logic_structs[j]->ob_status != OB_STATUS_HELD)) { // not if the object has been manually switched out
591 			Tdebug("objects_init.txt", "%s", (const char *)logic_structs[j]->GetName());
592 			voxel_id_list[number_of_voxel_ids++] = (uint8)j;
593 		}
594 	}
595 
596 	if (number_of_voxel_ids >= MAX_voxel_list)
597 		Fatal_error("Initialise_objects, the voxel id list is too small");
598 
599 	Tdebug("objects_init.txt", "\n\nfound %d voxel characters", number_of_voxel_ids);
600 
601 	// init the player object number
602 	// get id
603 	id = objects->Fetch_item_number_by_name("player"); // returns -1 if object not in existence
604 
605 	if (id != 0xffffffff) {
606 		L = logic_structs[id]; // fetch logic struct for player object
607 		I = L->voxel_info;
608 		M = L->mega;
609 
610 		object = (c_game_object *)objects->Fetch_item_by_number(id);
611 
612 		//		not if this object has been shut-down - for not having a map marker for example
613 		if (L->ob_status != OB_STATUS_HELD)
614 			player.Set_player_id(id);
615 
616 		// Preload the player animation to make PSX jerking better
617 		for (uint32 i = 0; i < NUMBER_player_startup_anims; i++)
618 			rs_anims->Res_open(I->get_anim_name(player_startup_anims[i]), I->anim_name_hash[player_startup_anims[i]], I->base_path, I->base_path_hash);
619 	}
620 
621 	// done
622 	Zdebug("Init session finished\n");
623 
624 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
625 	EndLoading();
626 }
627 
Pre_initialise_objects()628 void _game_session::Pre_initialise_objects() {
629 	// prepare gameworld and objects but dont run init scripts yet
630 
631 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
632 	StartInit(total_objects + 6); // +6 because also floors, barriers, markers, camera_table, plan_view, player
633 
634 	Zdebug("\nPre_Initialise_objects");
635 
636 	Zdebug("[%d]", num_megas);
637 
638 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
639 	InitMsg("Floors");
640 
641 	// initialise the floor area definition file
642 	// uses the private resman so mission->session-> need to be initialised so this cant be on session contructor
643 	floor_def = g_icb_session_floors;
644 	g_icb_session_floors->___init();
645 
646 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
647 	InitMsg("Barriers");
648 
649 	// initialise the route barriers
650 	session_barriers = &g_icb_session_barriers;
651 	g_icb_session_barriers.___init();
652 
653 	Zdebug("A[%d]", num_megas);
654 
655 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
656 	InitMsg("Markers");
657 
658 	// init engine markers
659 	markers.___init();
660 
661 	// so PSX can have nice session loading screen and details (for timing and to stop player getting bored)
662 	InitMsg("Cameras");
663 
664 	// setup the camera system
665 	Build_camera_table();
666 
667 	// First set up the logic structures for the objects.  Each begins with a NULL pointer for its vox image.
668 	uint32 j;
669 	for (j = 0; ((j < total_objects)); ++j) {
670 
671 		Zdebug("%d -[%d]", j, num_megas);
672 
673 		object = (c_game_object *)objects->Fetch_item_by_number(j);
674 
675 		logic_structs[j] = g_logics[j];
676 		logic_structs[j]->___init((const char *)object->GetName());
677 	}
678 
679 	// Set up the event manager for this session.  This has to be done after the barrier handler
680 	// has been set up and after the objects have been set up because it relies on information
681 	// from both.
682 	PXTRY
683 
684 	g_oEventManager->Initialise();
685 
686 	PXCATCH
687 
688 	Fatal_error("Exception in _event_manager::Initialise()");
689 
690 	PXENDCATCH
691 
692 	// And set a duty cycle for the line-of-sight manager.  Just set default for now.
693 
694 	PXTRY
695 
696 	Zdebug("duty");
697 	g_oLineOfSight->SetDutyCycle(1);
698 	Zdebug("~duty");
699 
700 	PXCATCH
701 
702 	Fatal_error("Exception in g_oLineOfSight->SetDutyCycle()");
703 
704 	PXENDCATCH
705 
706 	// Initialise the sound logic engine.
707 	g_oSoundLogicEngine->Initialise();
708 
709 	player.has_weapon = TRUE8; // called before players init script
710 }
711 
712 extern int32 john_number_traces;
713 extern int32 john_total_traces;
714 
715 extern int32 fnTimer;
716 int32 logicTimer;
717 uint32 script_cycleTimer;
718 
One_logic_cycle()719 void _game_session::One_logic_cycle() {
720 	// process all the game objects
721 
722 	uint32 j;
723 
724 	// Wind the line-of-sight engine on one position.  Note that there is a variable in the line-of-sight engine
725 	// which can be used to make this call return without doing anything a certain percentage of the time.
726 
727 	john_number_traces = 0;
728 	john_total_traces = 0;
729 
730 	uint32 time = GetMicroTimer();
731 	PXTRY
732 	g_oLineOfSight->DutyCycle();
733 	PXCATCH
734 	Fatal_error("Exception in g_oLineOfSight->DutyCycle()");
735 	PXENDCATCH
736 	time = GetMicroTimer() - time;
737 	g_mission->los_time = time;
738 
739 	time = GetMicroTimer();
740 	PXTRY
741 	g_oSoundLogicEngine->Cycle();
742 	PXCATCH
743 	Fatal_error("Exception in g_oSoundLogicEngine->Cycle()");
744 	PXENDCATCH
745 	time = GetMicroTimer() - time;
746 	g_mission->sound_time = time;
747 
748 	time = GetMicroTimer();
749 
750 	// service the speech driver
751 	Service_speech();
752 
753 	// reset route manager
754 	Start_new_router_game_cycle();
755 
756 	// Tell the event manager to process its event timers if it is currently maintaining any.
757 	g_oEventManager->CycleEventManager();
758 
759 	// If the icon menu currently has a holding icon, service its logic.
760 	if (g_oIconListManager->IsHolding())
761 		g_oIconListManager->CycleHoldingLogic();
762 
763 	// If the icon menu is currently flashing added medipacks or clips run the logic for it.
764 	if (g_oIconMenu->IsAdding())
765 		g_oIconMenu->CycleAddingLogic();
766 
767 	time = GetMicroTimer() - time;
768 	g_mission->event_time = time;
769 
770 	// run through all the objects calling their logic
771 	for (j = 0; j < total_objects; j++) { // object 0 is used
772 
773 		//		fetch the engine created logic structure for this object
774 		L = logic_structs[j];
775 
776 		if ((L->ob_status != OB_STATUS_HELD) && (!L->camera_held)) { // not if the object has been manually switched out
777 			I = L->voxel_info;
778 			M = L->mega;
779 			cur_id = j; // fast reference for engine functions
780 			// fetch the object that is our current object
781 			// 'object' needed as logic code may ask it for the objects name, etc.
782 			object = (c_game_object *)objects->Fetch_item_by_number(j);
783 
784 			// run appropriate logic
785 			switch (L->big_mode) {
786 			case __SCRIPT: // just running full scripts
787 				if (g_px->mega_timer)
788 					script_cycleTimer = GetMicroTimer();
789 				Pre_logic_event_check();
790 				Script_cycle();
791 				if (g_px->mega_timer) {
792 					script_cycleTimer = GetMicroTimer() - script_cycleTimer;
793 					L->cycle_time = script_cycleTimer;
794 				}
795 				break;
796 
797 			case __NO_LOGIC: // do nothing
798 				break;
799 
800 			case __CUSTOM_SIMPLE_ANIMATE: // special simple animator logic
801 				if (g_px->mega_timer)
802 					script_cycleTimer = GetMicroTimer();
803 				Custom_simple_animator();
804 				if (g_px->mega_timer) {
805 					script_cycleTimer = GetMicroTimer() - script_cycleTimer;
806 					L->cycle_time = script_cycleTimer;
807 				}
808 
809 				break;
810 
811 			case __CUSTOM_BUTTON_OPERATED_DOOR: // special button operated door
812 				if (g_px->mega_timer)
813 					script_cycleTimer = GetMicroTimer();
814 				Custom_button_operated_door();
815 				if (g_px->mega_timer) {
816 					script_cycleTimer = GetMicroTimer() - script_cycleTimer;
817 					L->cycle_time = script_cycleTimer;
818 				}
819 
820 				break;
821 
822 			case __CUSTOM_AUTO_DOOR:
823 				if (g_px->mega_timer)
824 					script_cycleTimer = GetMicroTimer();
825 				Custom_auto_door();
826 				if (g_px->mega_timer) {
827 					script_cycleTimer = GetMicroTimer() - script_cycleTimer;
828 					L->cycle_time = script_cycleTimer;
829 				}
830 				break;
831 
832 			case __MEGA_SLICE_HELD:
833 				if (M->on_players_floor) {
834 					L->big_mode = __SCRIPT; // in view - alive
835 					g_oEventManager->ClearAllEventsForObject(cur_id);
836 					g_oSoundLogicEngine->ClearHeardFlag(cur_id);
837 					Script_cycle();
838 				}
839 
840 				if (PXfabs(M->actor_xyz.y - logic_structs[player.Fetch_player_id()]->mega->actor_xyz.y) < (int32)(M->slice_hold_tolerance)) {
841 					L->big_mode = __SCRIPT;
842 					g_oEventManager->ClearAllEventsForObject(cur_id);
843 					g_oSoundLogicEngine->ClearHeardFlag(cur_id);
844 					Script_cycle();
845 				}
846 				break;
847 			case __MEGA_PLAYER_FLOOR_HELD:
848 			case __MEGA_INITIAL_FLOOR_HELD:
849 				//					waiting for player - release when on our floor
850 				if (M->on_players_floor) {
851 					L->big_mode = __SCRIPT;
852 					g_oEventManager->ClearAllEventsForObject(cur_id);
853 					g_oSoundLogicEngine->ClearHeardFlag(cur_id);
854 					Script_cycle();
855 				} else if (first_session_cycle)
856 					Script_cycle(); // no camera yet
857 				break;
858 			}
859 
860 			// if the character is voxel based then add it to the list of voxel characters that is used by stage draw
861 			if ((L->image_type == VOXEL) && ((L->ob_status != OB_STATUS_HELD))) {
862 				time = GetMicroTimer();
863 
864 				// update some mega related stuff if mega is not sleeping
865 				if (L->big_mode == __SCRIPT) {
866 					//              process outstanding auto pan remaining
867 					if (L->auto_panning)
868 						Advance_auto_pan();
869 
870 					Idle_manager(); // heh heh, check for mega just stood around
871 
872 					UpdateFootstep();
873 					UpdateMegaFX();
874 				}
875 
876 				// set floor rect value - used by stage draw to find indexed camera name
877 				floor_def->Set_floor_rect_flag(L);
878 
879 				// check player floor status
880 				if (!first_session_cycle)
881 					Process_player_floor_status(); // sends events when object gains same floor as player
882 
883 				// process hold mode options
884 				// has on-camera-only mode and is not on screen - then sleep again
885 				if ((L->hold_mode == mega_player_floor_hold) && (!M->on_players_floor))
886 					L->big_mode = __MEGA_PLAYER_FLOOR_HELD;
887 
888 				// has slice hold mode and is now off camera - check for shut off tolerance
889 				if ((L->hold_mode == mega_slice_hold) && (!M->on_players_floor)) {
890 					if (PXfabs(M->actor_xyz.y - logic_structs[player.Fetch_player_id()]->mega->actor_xyz.y) > (int32)(M->slice_hold_tolerance))
891 						L->big_mode = __MEGA_SLICE_HELD;
892 				}
893 
894 				// if on screen and stood then lets maybe do a scratch anim
895 
896 				time = GetMicroTimer() - time;
897 				g_mission->xtra_mega_time += time;
898 			}
899 		}
900 	}
901 
902 	// set not first cycle
903 	first_session_cycle = FALSE8;
904 }
905 
Pre_logic_event_check()906 void _game_session::Pre_logic_event_check() {
907 	//  Check if there any events pending for the object.
908 	// if  so we re-run the logic context which may or may not decide to change the current script
909 
910 	if (((L->do_not_disturb == 1)) || (L->do_not_disturb == 2))
911 		return; // 1 or 2 == fn-do-not-disturb + speech
912 
913 	if (L->do_not_disturb == 3) { // socket_force_new_logic
914 		// clear events
915 		g_oEventManager->ClearAllEventsForObject(cur_id);
916 
917 		L->do_not_disturb = 0; // reset for next cycle
918 		return;
919 	}
920 
921 	// Added call to the sound logic engine to see if any sound events are pending.
922 	if (L->context_request || g_oEventManager->HasEventPending(cur_id) || g_oSoundLogicEngine->SoundEventPendingForID(cur_id)) {
923 		//      Yes, the object has an event pending, so rerun its logic context.
924 		if (L->context_request)
925 			Zdebug("[%s] internal request to rerun logic context", object->GetName());
926 
927 		else
928 			Zdebug("[%s] event means rerun logic context", object->GetName());
929 
930 		if ((L->image_type == VOXEL) && (M->interacting)) { // check for megas who are interacting
931 			// interacting, so ignoring LOS event
932 			Zdebug("interacting, so ignoring LOS event");
933 		} else {
934 			L->logic[0] = (char *)scripts->Try_fetch_item_by_hash((object->GetScriptNameFullHash(OB_LOGIC_CONTEXT)));
935 
936 			// run script - context chooser MAY pick a new L 1 logic
937 			// we call this now so the new script will be setup and ready to run
938 			RunScript(const_cast<const char *&>(L->logic[0]), object);
939 
940 			// reset the crude switch
941 			L->context_request = FALSE8;
942 		}
943 	}
944 }
945 
Script_cycle()946 void _game_session::Script_cycle() {
947 	int32 ret;
948 	c_game_object *script_owner;
949 	uint32 inner_cycles;
950 
951 	inner_cycles = 0; // to catch infnite_loops
952 
953 	// inner logic loop
954 	do {
955 		// sort out which object we should run the script with
956 		// this can change within a cycle
957 		if ((L->image_type == VOXEL) && (M->interacting)) { // check for megas who are interacting
958 			// object is running someone elses interaction script
959 			// so get their object and pass to interpretter so that local vars can be accessed correctly
960 			script_owner = (c_game_object *)objects->Fetch_item_by_number(M->target_id);
961 		} else {
962 			script_owner = object; // object running its own script
963 		}
964 
965 		ret = RunScript(const_cast<const char *&>(L->logic[L->logic_level]), script_owner);
966 
967 		// ret is:
968 		// 0 done enough this cycle
969 		// 1 current script has finished and hit closing brace
970 		// 2 FN_ returned an IR_TERMINATE to interpretter so we just go around - new script or gosub
971 
972 		if (ret == IR_RET_SCRIPT_FINISHED) { // script has finished so drop down a level
973 			if (L->logic_level) { // not on base, so we can just drop down to the script below
974 				L->logic_level--;
975 
976 				// in-case we were running an interaction script then cancel interaction
977 				if (L->image_type == VOXEL) {
978 					M->target_id = 0;
979 					M->interacting = FALSE8;
980 					L->looping = L->old_looping; // safe for return
981 				}
982 			}
983 
984 			if (!L->logic_level) { // restart the object
985 				L->logic_ref[1] = 0; // completely reset L 1 so that context choose will select ok
986 				// it is acceptable to choose the logic that had previously been running
987 
988 				// temp reset PC the hard way
989 				L->logic[0] = (char *)scripts->Try_fetch_item_by_hash(object->GetScriptNameFullHash(OB_LOGIC_CONTEXT));
990 
991 				// run script - context chooser will pick a new L 1 logic
992 				RunScript(const_cast<const char *&>(L->logic[0]), object);
993 
994 				// if still on base then nothing chosen - shut down the object and log a warning somewhere
995 				if (!L->logic_level) {
996 					Shut_down_object("by One_logic_cycle - logic context failed to choose");
997 					ret = 0;
998 				}
999 			}
1000 		}
1001 
1002 		//		catch infinite loops
1003 		inner_cycles++;
1004 
1005 		// I upped this from 5 to 1000 because the Remora relies on fn_new_script to jump around
1006 		// it's menu structure and this was tripping the limit.  The player will have to spend ages
1007 		// in the Remora's menu's now for it to trip this limit, and genuine infinite loops will
1008 		// still be caught.
1009 		if (inner_cycles == 1000)
1010 			Fatal_error("object [%s] is in an infinite script loop!", object->GetName());
1011 
1012 	} while (ret); // ret==0 means quit for this object
1013 }
1014 
Fetch_prop_state(char * prop_name)1015 uint32 _game_session::Fetch_prop_state(char *prop_name) {
1016 	// return a props state
1017 	// if the prop object doesnt exist we create a dummy - the system continues regardless - which is nice
1018 
1019 	uint32 prop_number;
1020 	uint32 j;
1021 
1022 	if (camera_hack == FALSE8) {
1023 		prop_number = objects->Fetch_item_number_by_name(prop_name);
1024 
1025 		if (prop_number != 0xffffffff)
1026 			return (prop_state_table[prop_number]); // get prop state (pc)
1027 	}
1028 
1029 	// prop does not have a owner object - so we create a dummy for it if we havent already
1030 	// so, have we already created a dummy?
1031 	// search for dummy
1032 
1033 	// is our object already here?
1034 	j = 0;
1035 	while ((j < number_of_missing_objects) && (strcmp(missing_obs[j], prop_name)))
1036 		++j;
1037 
1038 	// didnt find the object
1039 	if (j == number_of_missing_objects) {
1040 		// create entry for the object
1041 		if (strcmp(prop_name, "not a prop") && (camera_hack == FALSE8)) // dont report dummy lights
1042 			Message_box("object missing for prop [%s]", prop_name);
1043 
1044 		Set_string(prop_name, missing_obs[number_of_missing_objects], MAX_missing_object_name_length);
1045 
1046 		Tdebug("missing_objects.txt", "%d [%s]", number_of_missing_objects, missing_obs[number_of_missing_objects]);
1047 		missing_ob_prop_states[number_of_missing_objects++] = 0;
1048 
1049 		return (0);
1050 	}
1051 
1052 	// dummy did exist so return the current dummy prop value
1053 	return (missing_ob_prop_states[j]);
1054 }
1055 
Set_prop_state(char * prop_name,uint32 value)1056 void _game_session::Set_prop_state(char *prop_name, uint32 value) {
1057 	// set a prop state
1058 	// if the prop doesnt exist we skip it - and assume it will soon be built
1059 	// there is no scope checking
1060 
1061 	uint32 prop_number;
1062 	uint32 j;
1063 
1064 	if (camera_hack == FALSE8) {
1065 		prop_number = objects->Fetch_item_number_by_name(prop_name);
1066 
1067 		if (prop_number != 0xffffffff)
1068 			prop_state_table[prop_number] = value; // set prop state (pc)
1069 	}
1070 
1071 	// have we already created a dummy?
1072 	// search for dummy
1073 
1074 	// is our object already here?
1075 	j = 0;
1076 	while ((j < number_of_missing_objects) && (strcmp(missing_obs[j], prop_name)))
1077 		++j;
1078 
1079 	// didnt find the object
1080 	if (j == number_of_missing_objects)
1081 		return;
1082 
1083 	// found the dummy so set its value
1084 	missing_ob_prop_states[j] = (uint8)value;
1085 }
1086 
Fetch_named_objects_id(const char * name) const1087 uint32 _game_session::Fetch_named_objects_id(const char *name) const {
1088 	uint32 i;
1089 
1090 	for (i = 0; i < total_objects; ++i) {
1091 		if (strcmp(name, logic_structs[i]->GetName()) == 0)
1092 			return (i);
1093 	}
1094 
1095 	// The object wasn't found.
1096 	Fatal_error("Object %s not found in _game_session::Fetch_named_objects_id()", name);
1097 
1098 	// This stops a compiler error.
1099 	return (0xffffffff);
1100 }
1101 
Process_player_floor_status()1102 void _game_session::Process_player_floor_status() {
1103 	// work out if this object
1104 	bool8 result = FALSE8;
1105 	uint32 num_extra, j, cam, player_floor;
1106 
1107 	player_floor = logic_structs[player.Fetch_player_id()]->owner_floor_rect;
1108 
1109 	// dont need to tell the player he's on the players floor
1110 	if (player.Fetch_player_id() == cur_id)
1111 		return;
1112 
1113 	if (floor_to_camera_index[L->owner_floor_rect] == cur_camera_number)
1114 		result = TRUE8; //
1115 	else { // no exact same
1116 		// ok, but is our floor linked to theirs?
1117 		cam = floor_to_camera_index[player_floor];
1118 		num_extra = cam_floor_list[cam].num_extra_floors;
1119 
1120 		for (j = 0; j < num_extra; j++)
1121 			if (cam_floor_list[cam].extra_floors[j] == L->owner_floor_rect) { // our floor one of players extras?
1122 				result = TRUE8; // yes - the floors are linked
1123 				break;
1124 			}
1125 	}
1126 
1127 	if ((!M->on_players_floor) && (result)) {
1128 		g_oEventManager->PostNamedEventToObject("on_floor", cur_id, player.Fetch_player_id()); // send as if from player
1129 	}
1130 
1131 	M->on_players_floor = result; // set to current state
1132 }
1133 
Idle_manager()1134 void _game_session::Idle_manager() {
1135 	// megas only
1136 	// is the character idling?
1137 	// if so and for a int32 time gosub a script that will do something
1138 
1139 	uint32 k;
1140 	char *ad;
1141 	uint32 script_hash;
1142 
1143 	if ((L->pause) && (L->cur_anim_type == __STAND) && (L->conversation_uid == NO_SPEECH_REQUEST) && (!M->Is_crouched()) && (Object_visible_to_camera(cur_id))) {
1144 
1145 		M->idle_count++;
1146 
1147 		if ((M->idle_count > 24) && (L->logic_level == 1)) {
1148 			M->idle_count = 0;
1149 
1150 			script_hash = HashString("idle");
1151 
1152 			// try and find a script with the passed extention i.e. ???::looping
1153 			for (k = 0; k < object->GetNoScripts(); k++) {
1154 				if (script_hash == object->GetScriptNamePartHash(k)) {
1155 					//          script k is the one to run
1156 					//          get the address of the script we want to run
1157 					ad = (char *)scripts->Try_fetch_item_by_hash(object->GetScriptNameFullHash(k));
1158 
1159 					//          write actual offset
1160 					L->logic[2] = ad;
1161 
1162 					L->logic_level = 2; //
1163 
1164 					L->old_looping = L->looping; // safe for return
1165 
1166 					L->looping = 0; // reset to 0 for new logics
1167 
1168 					M->custom = FALSE8; // reset
1169 
1170 					return; // done it
1171 				}
1172 			}
1173 		}
1174 	}
1175 }
1176 
Set_init_voxel_floors()1177 void _game_session::Set_init_voxel_floors() {
1178 	// set all mega characters floors - called after game restore because logics may begin by checking the floor number but it wont be set until end of first cycle
1179 	uint32 j;
1180 
1181 	for (j = 0; j < number_of_voxel_ids; j++)
1182 		floor_def->Set_floor_rect_flag(logic_structs[voxel_id_list[j]]);
1183 
1184 	// setup the players route barriers
1185 
1186 	MS->M = MS->logic_structs[MS->player.Fetch_player_id()]->mega;
1187 	MS->L = MS->logic_structs[MS->player.Fetch_player_id()];
1188 	Prepare_megas_route_barriers(TRUE8); // update barriers
1189 }
1190 
LoadTranslatedFile(const char * mission,const char * session)1191 _linked_data_file *LoadTranslatedFile(const char *mission, const char *session) {
1192 	// Get the actual session name
1193 	const char *sessionstart = session + strlen(mission) + 1;
1194 	pxString actsession;
1195 	actsession.SetString(sessionstart, strlen(sessionstart) - 1);
1196 
1197 	// Make up the name for the file to be loaded up
1198 	pxString fname = pxVString("%s\\data\\%s%s.ttrans", tt_text, mission, (const char *)actsession);
1199 
1200 	if (!checkFileExists(fname))
1201 		Fatal_error("Unable to load file %s", (const char *)fname);
1202 
1203 	// Load in this file
1204 	Common::SeekableReadStream *stream = openDiskFileForBinaryStreamRead(fname.c_str());
1205 
1206 	if (stream == NULL) // if it could not be opened
1207 		Fatal_error("Unable to load file %s", (const char *)fname);
1208 
1209 	uint32 len = stream->size();
1210 
1211 	//      make space for file
1212 	char *memPtr = new char[len + 1];
1213 
1214 	//      read it in
1215 	stream->read(memPtr, len);
1216 	delete stream; // close the file
1217 
1218 	//      0 terminate the string
1219 	memPtr[len] = 0;
1220 
1221 	return ((_linked_data_file *)memPtr);
1222 }
1223 
1224 } // End of namespace ICB
1225