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/common/px_common.h"
29 #include "engines/icb/mission.h"
30 #include "engines/icb/global_objects.h"
31 #include "engines/icb/global_switches.h"
32 #include "engines/icb/direct_input.h"
33 #include "engines/icb/p4.h"
34 #include "engines/icb/remora.h"
35 #include "engines/icb/icon_list_manager.h"
36 #include "engines/icb/sound.h"
37 #include "engines/icb/string_vest.h"
38 #include "engines/icb/sound_logic.h"
39 #include "engines/icb/timer_func.h"
40 #include "engines/icb/mission_functions.h"
41 #include "engines/icb/common/datapacker.h"
42 #include "engines/icb/res_man.h"
43 #include "engines/icb/options_manager_pc.h"
44 #include "engines/icb/cluster_manager_pc.h"
45 
46 #include "common/util.h"
47 #include "common/textconsole.h"
48 
49 namespace ICB {
50 
51 // Short term global until we stop supporting old save game format
52 uint32 packData;
53 
Setup_camtest_mission()54 bool8 Setup_camtest_mission() {
55 	// create a null mission and session for camtest viewer mode
56 
57 	return FALSE8;
58 }
59 
Set_new_session_name(const char * ascii)60 void _mission::Set_new_session_name(const char *ascii) {
61 	// record the name of the next session
62 
63 /*	// name to lower
64 	for (uint32 j = 0; j < strlen(ascii); j++)
65 		if (Common::isUpper(*(ascii + j)))
66 			*(ascii + j) = tolower(*(ascii + j));*/
67 
68 	Set_string(const_cast<char *>(ascii), new_session_name, TINY_NAME_LEN);
69 
70 	new_session = TRUE8; // trigger a new session
71 }
72 
Set_init_nico_name(const char * ascii)73 void _mission::Set_init_nico_name(const char *ascii) {
74 	// record the name of the init nico
75 
76 	Set_string(const_cast<char *>(ascii), init_nico_name, TINY_NAME_LEN);
77 
78 	init_nico = TRUE8; // trigger a new session
79 }
80 
Is_there_init_nico()81 bool8 _mission::Is_there_init_nico() {
82 	// return init nico status
83 
84 	return (init_nico);
85 }
86 
Reset_init_nico()87 void _mission::Reset_init_nico() {
88 	// remove the name of the init nico
89 
90 	init_nico = FALSE8; // reset
91 }
92 
Return_init_nico_name()93 const char *_mission::Return_init_nico_name() { return ((const char *)init_nico_name); }
94 
Setup_new_mission(const char * mission_name,const char * session_name)95 bool8 Setup_new_mission(const char *mission_name, const char *session_name) {
96 	// Just to be tidy
97 
98 	// Stop any previous sounds
99 	StopAllSoundsNow();
100 
101 	// Pause the sound engine
102 	PauseSounds();
103 
104 	// create a new mission object deleting any current one
105 
106 	// fix names to lowercase on the PC ONLY
107 /*	uint32 j;
108 	for (j = 0; j < strlen(mission_name); j++)
109 		if (Common::isUpper(*(mission_name + j)))
110 			*(mission_name + j) = tolower(*(mission_name + j));
111 
112 	for (j = 0; j < strlen(session_name); j++)
113 		if (Common::isUpper(*(session_name + j)))
114 			*(session_name + j) = tolower(*(session_name + j));*/
115 
116 	// check if session exists
117 
118 	// If we are using the straight paths check for the cluster
119 	// Otherwise make up the path to the psx name format cluster
120 
121 	// Make the filename equivalent of the hash'ed version of mission name
122 	char h_mission_name[8];
123 	HashFile(mission_name, h_mission_name);
124 	char h_session_name[8];
125 	HashFile(session_name, h_session_name);
126 
127 	// convert hashes to lower-case for FS operations
128 	for (int32 i = 0; i < 8; i++) {
129 		h_mission_name[i] = (char)tolower(h_mission_name[i]);
130 		h_session_name[i] = (char)tolower(h_session_name[i]);
131 	}
132 
133 	sprintf(temp_buf, SESSION_TEST_PATH, h_mission_name, h_session_name);
134 
135 #if 1 // was #ifdef FROM_PC_CD
136 	// Need the mission data present on hard-disk for it to destruct properly
137 	// so do this here for the pc before we shunt stuff about
138 	if (g_mission)
139 		g_icb_mission->___delete_mission();
140 
141 	// Call the runtime cluster manager to install mission data on minimum install
142 
143 	// Need a mission id number fist of all
144 	MISSION_ID m = (MISSION_ID)FindMissionNumber(mission_name);
145 
146 	// Initialise the install for this mission
147 	// This function also checks that the correct CD is in the drive too
148 	if (g_theClusterManager->StartMissionInstall(m)) {
149 		// Keep calling this until it's done.  I know this prevents windows
150 		// from doing anything but that's a useful thing for installing stuff.
151 		while (g_theClusterManager->InstallMission())
152 			;
153 	}
154 #endif
155 
156 	if (rs_bg->Test_file(temp_buf)) {
157 		if (g_mission)
158 			g_icb_mission->___delete_mission();
159 
160 		g_icb_mission->___init_mission(mission_name, session_name);
161 		MS->Pre_initialise_objects();
162 		MS->Init_objects();
163 
164 		return (TRUE8);
165 	}
166 
167 	return (FALSE8);
168 }
169 
___init_mission(const char * new_mission_name,const char * session_name)170 void _mission::___init_mission(const char *new_mission_name, const char *session_name) {
171 	// mission object constructor
172 	// creates a session too
173 
174 	Zdebug("___init_mission %s %s", new_mission_name, session_name);
175 
176 	// camview mode
177 	if (camera_hack)
178 		return;
179 
180 	chi_following = FALSE8; // store a mission wide session independent record of whether or not chi has been initialised
181 	num_bullets = 0;
182 	num_clips = 0;
183 	num_medi = 0;
184 	inited_globals = FALSE8;
185 
186 	if (new_mission_name == NULL)
187 		Fatal_error("new mission no mission name");
188 	if (session_name == NULL)
189 		Fatal_error("new mission no session name");
190 
191 	// Work out which CD we should be using
192 	g_px->current_cd = WhichCD(new_mission_name);
193 
194 	// Need a mission id number fist of all
195 	MISSION_ID m = (MISSION_ID)FindMissionNumber(new_mission_name);
196 
197 	// Initialise the install for this mission
198 	// This function also checks that the correct CD is in the drive too
199 	if (g_theClusterManager->StartMissionInstall(m)) {
200 		// Keep calling this until it's done.  I know this prevents windows
201 		// from doing anything but that's a useful thing for installing stuff.
202 		while (g_theClusterManager->InstallMission())
203 			;
204 	}
205 
206 	// When using clusters keep items withouth the root so the correct hashing
207 	// start point can be maintained
208 	strcpy(mission_name, new_mission_name);
209 
210 	Set_string(new_mission_name, tiny_mission_name, TINY_NAME_LEN);
211 	Set_string(session_name, tiny_session_name, TINY_NAME_LEN);
212 
213 	// Make the filename equivalent of the hash'ed version of mission name
214 	HashFile(new_mission_name, h_mission_name);
215 
216 	Zdebug("-- Begin new mission - %s", (const char *)new_mission_name);
217 
218 	//  setup mission sound
219 
220 	LoadMissionSounds(new_mission_name);
221 
222 	// Reset the widescreen and fade effects
223 	surface_manager->Reset_Effects();
224 	// set pointer :this is so things inside session->__init can use MS e.g. remora & inventory initialisation
225 	g_mission = g_icb_mission;
226 
227 	// set sessions private resource manager to have no defragging - will fail as soon as a file cannot fit
228 	private_session_resman->Set_to_no_defrag();
229 
230 	// setup the starting session
231 	// note - objects will need their initialise-objects call making outside of this because mission->session-> must be inited
232 	session = g_icb_session;
233 	session->___init(mission_name, session_name);
234 
235 	// set mission status to ok - a script can reset to !0 to end the entire mission
236 	mission_status = 0;
237 
238 	// camera will follow the player when this is 0 - change to another id to follow that instead
239 	camera_follow_id_overide = 0;
240 
241 	// Don't think this matters but better be safe.
242 	remora_save_mode = -1;
243 
244 	// total micro sessions saved
245 	number_sessions_saved = 0;
246 
247 	// reset new session flag
248 	new_session = FALSE8;
249 	init_nico = FALSE8;
250 
251 	Zdebug("~___init_mission %s %s", new_mission_name, session_name);
252 }
253 
___delete_mission()254 void _mission::___delete_mission() {
255 	// mission object deconstructor
256 
257 	Zdebug("deleting mission");
258 
259 	// kill the pointer which doubles as an inited yes/no flag
260 	g_mission = NULL;
261 
262 	session->___destruct();
263 
264 	// Close any existing menu
265 	g_oIconMenu->CloseDownIconMenu();
266 
267 	// This kills any lists that are lying about (that script writers may have created but forgotten to destroy).  It
268 	// won't kill the inventory though, because that has GAME_WIDE scope and we are only killing to MISSION_WIDE.
269 	g_oIconListManager->ResetToScopeLevel(MISSION_WIDE);
270 
271 	// This is the call that removes all the icons in the inventory list WITHOUT destroying the list itself.
272 	g_oIconListManager->ResetList(ICON_LIST_INVENTORY);
273 
274 	// Turn the in-game timer off
275 	StopTimer();
276 }
277 
End_mission()278 void _mission::End_mission() {
279 	// cause the mission to end and shutdown
280 	mission_status = 1;
281 }
282 
_mission()283 _mission::_mission()
284 		: session(NULL), camera_follow_id_overide(0), remora_save_mode(0), ad_time(0), lt_time(0), set_time(0), flip_time(0), cycle_time(0), logic_time(0), resman_logic_time(0),
285 		los_time(0), event_time(0), sound_time(0), xtra_mega_time(0), nActorsDrawn(0), nActorsConsidered(0), old_hits_value(0), chi_following(0), num_bullets(0), num_clips(0),
286 		num_medi(0), inited_globals(FALSE8), mission_terminate(0), mission_status(0), number_sessions_saved(0), new_session(FALSE8), init_nico(FALSE8) {
287 	memset(new_session_name, '\0', TINY_NAME_LEN);
288 	memset(init_nico_name, '\0', TINY_NAME_LEN);
289 	memset(mission_name, '\0', ENGINE_STRING_LEN);
290 	memset(tiny_mission_name, '\0', TINY_NAME_LEN);
291 	memset(tiny_session_name, '\0', TINY_NAME_LEN);
292 	memset(h_mission_name, '\0', 8);
293 	for (int32 i = 0; i < MAX_sessions; i++) {
294 		memset(micro_sessions[i].session__name, '\0', ENGINE_STRING_LEN);
295 		micro_sessions[i].number_of_micro_objects = 0;
296 		for (int32 j = 0; j < MAX_session_objects; j++) {
297 			memset(micro_sessions[i].micro_objects[j].lvar_value, 0, MAX_lvars * sizeof(int32));
298 			micro_sessions[i].micro_objects[j].total_lvars = 0;
299 			micro_sessions[i].micro_objects[j].status_flag = OB_STATUS_NOT_HELD;
300 		}
301 		memset(micro_sessions[i].fvars, 0, MAX_fvars * sizeof(int32));
302 	}
303 }
304 
~_mission()305 _mission::~_mission() { Zdebug("*mission destructing*"); }
306 
Game_cycle()307 uint32 _mission::Game_cycle() {
308 	// run a cycle for the current session in the current mission
309 
310 	// returns   0 ok
311 	//				1 finish the mission
312 
313 	// safety check for no session
314 	if (session == NULL)
315 		Fatal_error("no session");
316 
317 	if (new_session == TRUE8) { // a new session has been requested
318 		PauseSounds();
319 
320 		// save the session data to a micro session
321 		Save_micro_session();
322 
323 		// remove existing session - taking the view with it
324 		session->___destruct();
325 
326 		// set sessions private resource manager to have no defragging - will fail as soon as a file cannot fit
327 		private_session_resman->Set_to_no_defrag();
328 
329 		// initialise the new one
330 		session->___init((const char *)mission_name, (const char *)new_session_name);
331 
332 		// set the tiny session name
333 		Set_string(new_session_name, tiny_session_name, TINY_NAME_LEN);
334 
335 		// create gameworld and structs
336 		session->Pre_initialise_objects();
337 
338 		// reload object values if the session has been saved
339 		Restore_micro_session_vars();
340 
341 		// init the objects
342 		session->Init_objects();
343 
344 		// reload object coordinates
345 		Restore_micro_session_coords(FALSE8);
346 
347 		// must reset to NULL
348 		new_session = FALSE8;
349 
350 		// cancel init nico
351 		g_mission->Reset_init_nico();
352 
353 		camera_follow_id_overide = 0; // cancel previous watch
354 
355 		remora_save_mode = -1;
356 
357 		UnpauseSounds();
358 	}
359 
360 	// help out rs_anims
361 
362 	rs_anims->Garbage_removal();
363 
364 	// update the sound engine
365 	UpdateHearableSounds();
366 
367 	// now do a loop of logic
368 	// get start time
369 	MS->prev_save_state = MS->Can_save(); // get previous state - used by lifts to see if player is active
370 	MS->Set_can_save(FALSE8);             // cant save as default - this is reversed by states that allow save this game cycle
371 
372 	g_px->logic_timing = TRUE8;
373 	logic_time = GetMicroTimer();
374 	resman_logic_time = 0; // reset
375 	xtra_mega_time = 0;
376 	session->One_logic_cycle();
377 	// work out overall time
378 	logic_time = GetMicroTimer() - logic_time;
379 	g_px->logic_timing = FALSE8;
380 
381 	// cancel SAVE if watching another mega
382 	if (g_mission->camera_follow_id_overide)
383 		MS->Set_can_save(FALSE8);
384 
385 	session->Process_conveyors(); // conveyor belts
386 
387 	// Update global timer by a tick
388 	g_globalScriptVariables->SetVariable("missionelapsedtime", g_globalScriptVariables->GetVariable("missionelapsedtime") + 1);
389 
390 	// call the camera director which will pick a view depending upon the player objects position
391 	session->Camera_director();
392 
393 	return (mission_status);
394 }
395 
Save_micro_session()396 void _mission::Save_micro_session() {
397 	// save all object lvars to a micro session
398 	c_game_object *object;
399 	uint32 j = 0;
400 	uint32 i, k;
401 	uint32 total_fvars = 0;
402 
403 	Tdebug("micro_session.txt", "\n\nSAVING session %s", Fetch_tiny_session_name());
404 
405 	// see if this session has ever been saved
406 	for (j = 0; j < number_sessions_saved; j++) {
407 		if (!strcmp((const char *)(micro_sessions[j].session__name), Fetch_tiny_session_name())) {
408 			Tdebug("micro_session.txt", " session found - slot %d", j);
409 			break;
410 		}
411 	}
412 
413 	// are we creating a new one
414 	if (j == number_sessions_saved) {
415 		number_sessions_saved++;
416 	}
417 
418 	// j is slot number
419 	Set_string(Fetch_tiny_session_name(), micro_sessions[j].session__name);
420 
421 	Tdebug("micro_session.txt", " saving in slot %d", j);
422 
423 	// save number of em
424 	micro_sessions[j].number_of_micro_objects = session->Fetch_number_of_objects();
425 
426 	for (i = 0; i < session->Fetch_number_of_objects(); i++) {
427 		object = (c_game_object *)session->objects->Fetch_item_by_number(i);
428 
429 		Tdebug("micro_session.txt", "\n  object %d  %s, %d vars - status %d", i, object->GetName(), object->GetNoLvars(), session->Fetch_object_status(i));
430 		micro_sessions[j].micro_objects[i].status_flag = session->Fetch_object_status(i);
431 
432 		// if mega then save coord
433 		if (session->logic_structs[i]->image_type == VOXEL) {
434 			if (!session->logic_structs[i]->mega->pushed) {
435 				Tdebug("micro_session.txt", "  mega");
436 				micro_sessions[j].fvars[total_fvars++] = ((int32)session->logic_structs[i]->mega->actor_xyz.x);
437 				micro_sessions[j].fvars[total_fvars++] = ((int32)session->logic_structs[i]->mega->actor_xyz.y);
438 				micro_sessions[j].fvars[total_fvars++] = ((int32)session->logic_structs[i]->mega->actor_xyz.z);
439 			} else {
440 				Tdebug("micro_session.txt", "  mega   *pushed*");
441 				micro_sessions[j].fvars[total_fvars++] = ((int32)session->logic_structs[i]->mega->pushed_actor_xyz.x);
442 				micro_sessions[j].fvars[total_fvars++] = ((int32)session->logic_structs[i]->mega->pushed_actor_xyz.y);
443 				micro_sessions[j].fvars[total_fvars++] = ((int32)session->logic_structs[i]->mega->pushed_actor_xyz.z);
444 			}
445 			micro_sessions[j].fvars[total_fvars++] = ((int32)(session->logic_structs[i]->pan * PAN_SCALE_FACTOR));
446 		}
447 
448 		micro_sessions[j].micro_objects[i].total_lvars = 0;
449 
450 		if (object->GetNoLvars() > MAX_lvars)
451 			Fatal_error("object [%s] has too many lvars - has %d, only %d allowed", object->GetName(), object->GetNoLvars(), MAX_lvars);
452 
453 		for (k = 0; k < object->GetNoLvars(); k++) {
454 			if (!object->IsVariableString(k)) {
455 				Tdebug("micro_session.txt", "   saving lvar %d %s value %d", k, object->GetScriptVariableName(k), object->GetIntegerVariable(k));
456 
457 				int32 value = object->GetIntegerVariable(k);
458 
459 				// Using 14-bits to pack lvar's
460 				int32 packMin = -(1 << 13);
461 				int32 packMax = +((1 << 13) - 1);
462 
463 				if ((value < packMin) || (value > packMax)) {
464 					// Don't do a message box for a CD build of the game!
465 					Message_box("Object '%s' lvar %d '%s' is too big to pack please try and reduce %d range is %d->%d", object->GetName(), k,
466 					            object->GetScriptVariableName(k), value, packMin, packMax);
467 					packData = 0;
468 				}
469 
470 				// Using 16-bits as a maximum to store lvar's
471 				int32 lvarMin = -(1 << 15);
472 				int32 lvarMax = +((1 << 15) - 1);
473 
474 				if ((value < lvarMin) || (value > lvarMax)) {
475 					Fatal_error("Object '%s' lvar %d '%s' is too big to save %d range is %d->%d", object->GetName(), k, object->GetScriptVariableName(k), value,
476 					            packMin, packMax);
477 				}
478 
479 				micro_sessions[j].micro_objects[i].lvar_value[micro_sessions[j].micro_objects[i].total_lvars++] = value;
480 			}
481 		}
482 	}
483 }
484 
Restore_micro_session_vars()485 void _mission::Restore_micro_session_vars() {
486 	// reload all object lvars from a micro session
487 	c_game_object *object;
488 	uint32 j = 0;
489 	uint32 i, k;
490 	uint32 lvar;
491 
492 	Tdebug("micro_session.txt", "\n\nRestore_micro_session_vars session %s", Fetch_tiny_session_name());
493 
494 	// see if this session has ever been saved
495 	for (j = 0; j < number_sessions_saved; j++)
496 		if (!strcmp((const char *)(micro_sessions[j].session__name), Fetch_tiny_session_name())) {
497 			Tdebug("micro_session.txt", " session found - slot %d", j);
498 
499 			// restore lvars
500 			for (i = 0; i < session->Fetch_number_of_objects(); i++) {
501 				object = (c_game_object *)session->objects->Fetch_item_by_number(i);
502 
503 				Tdebug("micro_session.txt", "\n  object %d  %s, %d vars - status %d", i, object->GetName(), object->GetNoLvars(),
504 				       micro_sessions[j].micro_objects[i].status_flag);
505 				session->Set_object_status(i, micro_sessions[j].micro_objects[i].status_flag);
506 
507 				lvar = 0;
508 				for (k = 0; k < object->GetNoLvars(); k++) {
509 					if (!object->IsVariableString(k)) {
510 						Tdebug("micro_session.txt", "   restoring lvar %d %s to %d", k, object->GetScriptVariableName(k),
511 						       micro_sessions[j].micro_objects[i].lvar_value[lvar]);
512 						// reset lvar value
513 						object->SetIntegerVariable(k, micro_sessions[j].micro_objects[i].lvar_value[lvar++]);
514 					}
515 				}
516 			}
517 
518 			return;
519 		}
520 
521 	Tdebug("micro_session.txt", " session NOT found", j);
522 }
523 
Restore_micro_session_coords(bool8 from_disk)524 void _mission::Restore_micro_session_coords(bool8 from_disk) {
525 	// reload all object lvars from a micro session
526 	c_game_object *object;
527 	uint32 j = 0;
528 	uint32 i;
529 	uint32 index = 0;
530 
531 	Tdebug("micro_session.txt", "\n\nRestore_micro_session_coords session %s", Fetch_tiny_session_name());
532 
533 	// see if this session has ever been saved
534 	for (j = 0; j < number_sessions_saved; j++)
535 		if (!strcmp((const char *)(micro_sessions[j].session__name), Fetch_tiny_session_name())) {
536 			Tdebug("micro_session.txt", " session found - slot %d", j);
537 
538 			// restore lvars
539 			for (i = 0; i < session->Fetch_number_of_objects(); i++) {
540 				object = (c_game_object *)session->objects->Fetch_item_by_number(i);
541 
542 				Tdebug("micro_session.txt", "\n  object %d  %s, %d vars - status %d", i, object->GetName(), object->GetNoLvars(),
543 				       micro_sessions[j].micro_objects[i].status_flag);
544 				session->Set_object_status(i, micro_sessions[j].micro_objects[i].status_flag);
545 
546 				// if mega then restore coord
547 				if (session->logic_structs[i]->image_type == VOXEL) {
548 					// from disk, or doesnt have exclusives or (does have exclusives) but is chi and chi is not
549 					// following
550 					if ((from_disk) || (!session->logic_structs[i]->mega->has_exclusive_coords) ||
551 					    ((session->chi_id == i) && (session->chi_think_mode != __FOLLOWING))) {
552 						session->logic_structs[i]->mega->actor_xyz.x = (PXreal)micro_sessions[j].fvars[index++];
553 						session->logic_structs[i]->mega->actor_xyz.y = (PXreal)micro_sessions[j].fvars[index++];
554 						session->logic_structs[i]->mega->actor_xyz.z = (PXreal)micro_sessions[j].fvars[index++];
555 						session->logic_structs[i]->pan = (PXreal)(micro_sessions[j].fvars[index++] / (PXreal)PAN_SCALE_FACTOR);
556 					} else { // skip as the data IS saved regardless
557 						index += 4;
558 					}
559 				}
560 			}
561 			return;
562 		}
563 
564 	Tdebug("micro_session.txt", " session NOT found", j);
565 }
566 
Save_game_position(const char * filename,const char * slot_label,uint32 timeplayed)567 void _mission::Save_game_position(const char *filename, const char *slot_label, uint32 timeplayed) {
568 	// save the current game position to disk
569 
570 	//	save mission/session names
571 	//			globals
572 	//			micro-sessions
573 
574 	uint32 i, j, k;
575 	int32 avalue, fval;
576 	int16 lval;
577 	uint8 atinyvalue;
578 	int32 l;
579 	uint32 numIcons;
580 	char *iconnames[ICON_LIST_MAX_ICONS];
581 	uint32 iconHashes[ICON_LIST_MAX_ICONS];
582 	uint32 iconCounts[ICON_LIST_MAX_ICONS];
583 
584 	Tdebug("save_restore.txt", "\nsaving file [%s]", filename);
585 	Tdebug("save_restore.txt", " mission [%s]", Fetch_tiny_mission_name());
586 
587 	// As a default we are trying to pack the data
588 	packData = 1;
589 
590 	// save the CURRENT session data to a micro session
591 	Save_micro_session();
592 
593 	// Check the lvar's to see if we can pack them or not
594 	uint32 nlvars = 0;
595 	for (j = 0; j < number_sessions_saved; j++) {
596 		for (i = 0; i < micro_sessions[j].number_of_micro_objects; i++) {
597 			nlvars = (uint8)micro_sessions[j].micro_objects[i].total_lvars;
598 
599 			for (k = 0; k < nlvars; k++) {
600 				lval = (int16)micro_sessions[j].micro_objects[i].lvar_value[k];
601 
602 				// Using 14-bits to pack lvar's
603 				int32 packMin = -(1 << 13);
604 				int32 packMax = +((1 << 13) - 1);
605 
606 				if ((lval < packMin) || (lval > packMax)) {
607 					Message_box("Jake says : packData = 0");
608 					packData = 0;
609 				}
610 			}
611 		}
612 	}
613 
614 	// first save the index file which contains the session name and mission name that we're currently running - and hence want to
615 	// restore to later
616 	Common::WriteStream *stream = openDiskWriteStream(filename); // attempt to open the file for writing
617 	if (stream == NULL)
618 		Fatal_error("Save_game_position cannot *OPEN* [%s]", (const char *)filename);
619 
620 	// specific stuff for pc save game menu
621 	stream->write(slot_label, MAX_LABEL_LENGTH);
622 	stream->writeSint32LE(timeplayed);
623 
624 	atinyvalue = SR_VERSION;
625 	stream->writeByte(atinyvalue);
626 
627 	avalue = strlen(Fetch_tiny_mission_name()) + 1;
628 	stream->writeSint32LE(avalue);
629 	stream->write((const char *)Fetch_tiny_mission_name(), avalue);
630 
631 	avalue = strlen(Fetch_tiny_session_name()) + 1;
632 	stream->writeSint32LE(avalue);
633 	stream->write((const char *)Fetch_tiny_session_name(), avalue);
634 
635 	// now write the globals out
636 	atinyvalue = (uint8)g_globalScriptVariables->GetNoItems();
637 	Tdebug("save_restore.txt", " %d globals", atinyvalue);
638 	stream->writeByte(atinyvalue);
639 
640 	for (j = 0; j < atinyvalue; j++) {
641 		avalue = (int32)g_globalScriptVariables->GetVariable((*g_globalScriptVariables)[j].hash, 0, 0);
642 		stream->writeSint32LE(avalue);
643 		Tdebug("save_restore.txt", "  %d 0x%08x = %d", j, (*g_globalScriptVariables)[j].hash, avalue);
644 	}
645 
646 	// get the icon information
647 	numIcons = g_oIconListManager->GetList(ICON_LIST_INVENTORY, iconnames, iconHashes, iconCounts);
648 
649 	stream->writeUint32LE(numIcons);
650 	Tdebug("save_restore.txt", "  %d unique icons", numIcons);
651 
652 	for (j = 0; j < numIcons; j++) {
653 		// write length of icon name then the string
654 		avalue = strlen(iconnames[j]) + 1;
655 		stream->writeSint32LE(avalue);
656 		stream->write((const void *)iconnames[j], avalue);
657 
658 		// write the hash value
659 		avalue = (uint32)iconHashes[j];
660 		stream->writeUint32LE(avalue);
661 
662 		// write the count value
663 		atinyvalue = (uint8)iconCounts[j];
664 		stream->writeByte(atinyvalue);
665 	}
666 
667 	// save the micro-sessions
668 
669 	// save number of micro-sessions
670 	avalue = number_sessions_saved;
671 	stream->writeSint32LE(avalue);
672 	Tdebug("save_restore.txt", " %d sessions", avalue);
673 
674 	// The DataPacker object
675 	DataPacker dpack;
676 
677 	for (j = 0; j < number_sessions_saved; j++) {
678 		// write the session name out
679 		Tdebug("save_restore.txt", "  save session [%s]", (const char *)micro_sessions[j].session__name);
680 		avalue = strlen(micro_sessions[j].session__name) + 1;
681 		stream->writeSint32LE(avalue);
682 		Tdebug("save_restore.txt", "  name len %d", avalue);
683 		stream->write((const void *)micro_sessions[j].session__name, avalue);
684 
685 		for (l = 0; l < MAX_fvars; l++) {
686 			fval = micro_sessions[j].fvars[l];
687 			stream->writeSint32LE(fval);
688 			Tdebug("save_restore.txt", "   %d", fval);
689 		}
690 
691 		stream->writeSint32LE(micro_sessions[j].number_of_micro_objects);
692 		Tdebug("save_restore.txt", "  %d objects in session", avalue);
693 
694 		for (i = 0; i < micro_sessions[j].number_of_micro_objects; i++) {
695 			Tdebug("save_restore.txt", "  \nobject %d", i);
696 
697 			// first write state flag
698 			atinyvalue = (uint8)micro_sessions[j].micro_objects[i].status_flag;
699 			Tdebug("save_restore.txt", "   status %d", atinyvalue);
700 			stream->writeByte(atinyvalue);
701 
702 			// save out lvars
703 			atinyvalue = (uint8)micro_sessions[j].micro_objects[i].total_lvars;
704 			stream->writeByte(atinyvalue);
705 			Tdebug("save_restore.txt", "   %d lvars", atinyvalue);
706 		}
707 
708 		// Pack the lvar data using the data packer object
709 		// Initialise the data packer object for writing
710 		DataPacker::ReturnCodes dret = DataPacker::OK;
711 
712 		// Are we storing the raw data or using the packer ?
713 		DataPacker::PackModeEnum packMode = DataPacker::PACK;
714 		atinyvalue = 0;
715 		if (packData == 1) {
716 			packMode = DataPacker::PACK;
717 			atinyvalue = 1;
718 		} else {
719 			packMode = DataPacker::DONT_PACK;
720 			atinyvalue = 0;
721 		}
722 
723 		stream->writeByte(atinyvalue);
724 
725 		dret = dpack.open(DataPacker::WRITE, packMode);
726 
727 		if (dret != DataPacker::OK) {
728 			Real_Fatal_error("DataPacker::Open failed dret %d", (int32)dret);
729 		}
730 		for (i = 0; i < micro_sessions[j].number_of_micro_objects; i++) {
731 			nlvars = micro_sessions[j].micro_objects[i].total_lvars;
732 			for (k = 0; k < nlvars; k++) {
733 				lval = (int16)micro_sessions[j].micro_objects[i].lvar_value[k];
734 				Tdebug("save_restore.txt", "   lvar %d = %d", k, lval);
735 
736 				dret = dpack.put(lval, stream);
737 				if (dret != DataPacker::OK) {
738 					Real_Fatal_error("DataPacker::Put failed dret %d", (int32)dret);
739 				}
740 			}
741 		}
742 		dret = dpack.close(stream);
743 		if (dret != DataPacker::OK) {
744 			Real_Fatal_error("DataPacker::Close failed dret %d", (int32)dret);
745 		}
746 	}
747 
748 	// now save special chi follow information
749 
750 	if ((session->is_there_a_chi) && (session->chi_think_mode != __NOTHING) && (session->chi_history != session->cur_history)) {
751 		// there is a chi and she is following and not on our floor
752 
753 		atinyvalue = 1; // yes
754 		Tdebug("save_restore.txt", "\nsaving CHI data");
755 		stream->writeByte(atinyvalue);
756 
757 		// save x,y and z to restart chi @
758 		fval = (uint32)session->hist_pin_x;
759 		stream->writeSint32LE(fval);
760 		Tdebug("save_restore.txt", " %d", fval);
761 
762 		fval = (uint32)session->hist_pin_y;
763 		stream->writeSint32LE(fval);
764 		Tdebug("save_restore.txt", " %d", fval);
765 
766 		fval = (uint32)session->hist_pin_z;
767 		stream->writeSint32LE(fval);
768 		Tdebug("save_restore.txt", " %d", fval);
769 	} else {
770 		// no chi
771 		atinyvalue = 0; // no
772 		Tdebug("save_restore.txt", "\nNOT saving CHI data");
773 		stream->writeByte(atinyvalue);
774 	}
775 
776 	// save ammo, bullets, medis
777 	atinyvalue = (uint8)g_mission->num_bullets;
778 	stream->writeByte(atinyvalue);
779 	atinyvalue = (uint8)g_mission->num_clips;
780 	stream->writeByte(atinyvalue);
781 	atinyvalue = (uint8)g_mission->num_medi;
782 	stream->writeByte(atinyvalue);
783 
784 	// manual cameras
785 	if (session->manual_camera) {
786 		atinyvalue = 1; // yes
787 		Tdebug("save_restore.txt", "\nsaving manual camera");
788 		stream->writeByte(atinyvalue);
789 
790 		// save name
791 		stream->write(session->manual_camera_name, ENGINE_STRING_LEN);
792 		// cam number
793 		stream->writeUint32LE(session->cur_camera_number);
794 	} else {
795 		// no manual camera
796 		atinyvalue = 0; // no
797 		Tdebug("save_restore.txt", "\nNOT saving manual camera");
798 		stream->writeByte(atinyvalue);
799 	}
800 
801 	// game script pc
802 	avalue = gs.pc;
803 	stream->writeSint32LE(avalue);
804 	Tdebug("save_restore.txt", " gamescript pc %d", avalue);
805 
806 	// save timed events
807 	g_oEventManager->Save(stream);
808 
809 	// Save the Remora's locations-visited information.
810 	g_oRemora->Save(stream);
811 
812 	// save gfx init info for initing a set...
813 	surface_manager->SaveGFXInfo(stream);
814 	SavePlatformSpecific(stream);
815 
816 	delete stream;
817 }
818 
Restore_micro_session_from_save_game(Common::SeekableReadStream * stream)819 void _mission::Restore_micro_session_from_save_game(Common::SeekableReadStream *stream) {
820 	// restore the micro-sessions for the mission
821 	uint32 i, j, k;
822 	int32 avalue;
823 	uint8 atinyvalue;
824 	char buf[256];
825 	uint32 total_sessions;
826 	uint8 total_lvars = 0;
827 
828 	total_sessions = stream->readUint32LE();
829 	Tdebug("save_restore.txt", " %d sessions", total_sessions);
830 
831 	// set the total saved flag
832 	number_sessions_saved = total_sessions;
833 
834 	// The DataPacker object
835 	DataPacker dpack;
836 
837 	for (j = 0; j < total_sessions; j++) {
838 		// read the session name
839 		// length of name
840 		avalue = stream->readSint32LE();
841 
842 		// name
843 		stream->read(buf, avalue);
844 
845 		Set_string(buf, micro_sessions[j].session__name);
846 		Tdebug("save_restore.txt", "  load session [%s]", (const char *)micro_sessions[j].session__name);
847 
848 		for (k = 0; k < MAX_fvars; k++) {
849 			avalue = stream->readSint32LE();
850 			Tdebug("save_restore.txt", "   %d", avalue);
851 			micro_sessions[j].fvars[k] = avalue;
852 		}
853 
854 		micro_sessions[j].number_of_micro_objects = stream->readUint32LE();
855 
856 		Tdebug("save_restore.txt", "  %d objects in session", micro_sessions[j].number_of_micro_objects);
857 
858 		DataPacker::PackModeEnum packMode = DataPacker::PACK;
859 
860 		// The new way
861 		for (i = 0; i < micro_sessions[j].number_of_micro_objects; i++) {
862 			Tdebug("save_restore.txt", "  \nobject %d", i);
863 
864 			// read in state flag
865 			atinyvalue = stream->readByte();
866 			micro_sessions[j].micro_objects[i].status_flag = (_object_status)atinyvalue;
867 			Tdebug("save_restore.txt", "   status %d", atinyvalue);
868 
869 			// read in lvars
870 			total_lvars = stream->readByte();
871 
872 			Tdebug("save_restore.txt", "   %d lvars", total_lvars);
873 			micro_sessions[j].micro_objects[i].total_lvars = (uint32)total_lvars;
874 		}
875 
876 		atinyvalue = stream->readByte();
877 		if (atinyvalue == 1) {
878 			packMode = DataPacker::PACK;
879 		} else {
880 			packMode = DataPacker::DONT_PACK;
881 		}
882 		// read in info for all objects
883 
884 		// Unpack the lvar data using the data packer object
885 		// Initialise the data packer object for reading
886 		DataPacker::ReturnCodes dret = DataPacker::OK;
887 
888 		dret = dpack.open(DataPacker::READ, packMode);
889 
890 		if (dret != DataPacker::OK) {
891 			Real_Fatal_error("DataPacker::Open failed dret %d", (int32)dret);
892 		}
893 
894 		for (i = 0; i < micro_sessions[j].number_of_micro_objects; i++) {
895 			total_lvars = (uint8)micro_sessions[j].micro_objects[i].total_lvars;
896 			for (k = 0; k < total_lvars; k++) {
897 				dret = dpack.Get(avalue, stream);
898 				if (dret != DataPacker::OK) {
899 					Real_Fatal_error("DataPacker::Get failed dret %d", (int32)dret);
900 				}
901 				micro_sessions[j].micro_objects[i].lvar_value[k] = avalue;
902 				Tdebug("save_restore.txt", "   lvar %d = %d", k, avalue);
903 			}
904 		}
905 
906 		dret = dpack.close(stream);
907 		if (dret != DataPacker::OK) {
908 			Real_Fatal_error("DataPacker::Close failed dret %d", (int32)dret);
909 		}
910 	}
911 }
912 
Load_game(const char * filename)913 __load_result Load_game(const char *filename) {
914 	// load a save game
915 	Common::SeekableReadStream *stream = 0; // file pointer
916 	uint32 avalue;
917 	uint8 atinyvalue;
918 	char mission_name[64];
919 	char session_name[64];
920 	char icon_name[MAXLEN_ICON_NAME];
921 	uint32 icon_hash;
922 	uint32 j;
923 	uint32 num_icons;
924 	int32 fvar;
925 
926 	Tdebug("save_restore.txt", "\nLoad game [%s]", filename);
927 
928 	// open the index file
929 	stream = openDiskFileForBinaryStreamRead(filename); // attempt to open the file for reading
930 
931 	if (stream == NULL)
932 		return __NO_SUCH_FILE;
933 
934 	char label[MAX_LABEL_LENGTH];           // load into here cause i'm too thick to know how to skip it (tw)
935 	fvar = stream->readUint32LE();          // load and discard time played
936 	stream->read(&label, MAX_LABEL_LENGTH); // load and discard user label name
937 
938 	// load schema and check
939 	atinyvalue = stream->readByte();
940 
941 	if (atinyvalue != SR_VERSION) {
942 		delete stream;
943 		Real_Message_box("Old version save games are not supported file:%d code:%d", atinyvalue, SR_VERSION);
944 		return __WRONG_VERSION;
945 	}
946 
947 	// read the mission name
948 	// length of name
949 	avalue = stream->readUint32LE();
950 	// name
951 	stream->read(mission_name, avalue);
952 
953 	// read the session name
954 	// length of name
955 	avalue = stream->readUint32LE();
956 	// name
957 	stream->read(session_name, avalue);
958 
959 	Tdebug("save_restore.txt", "mission [%s] session [%s]", mission_name, session_name);
960 
961 	// number of globals
962 	atinyvalue = stream->readByte();
963 	Tdebug("save_restore.txt", " %d globals", atinyvalue);
964 	if (atinyvalue != (uint8)g_globalScriptVariables->GetNoItems()) {
965 		Tdebug("save_restore.txt", " globals mismatch");
966 		return __GLOBAL_MISMATCH;
967 	}
968 
969 	for (j = 0; j < atinyvalue; j++) {
970 		avalue = stream->readSint32LE();
971 		g_globalScriptVariables->SetVariable((*g_globalScriptVariables)[j].hash, avalue);
972 		Tdebug("save_restore.txt", "  %d = %d", j, avalue);
973 	}
974 
975 	// delete any existing mission
976 	if (g_mission)
977 		g_icb_mission->___delete_mission();
978 	g_icb_mission->___init_mission(mission_name, session_name);
979 
980 	// read number of icons
981 	num_icons = stream->readUint32LE();
982 	Tdebug("save_restore.txt", "  %d icons", num_icons);
983 
984 	for (j = 0; j < num_icons; j++) {
985 		// string length
986 		avalue = stream->readSint32LE();
987 		stream->read(icon_name, avalue);
988 
989 		// read hash value
990 		icon_hash = stream->readUint32LE();
991 
992 		// read count
993 		atinyvalue = stream->readByte();
994 
995 		Tdebug("save_restore.txt", "  [%s] hash %X %d", icon_name, icon_hash, atinyvalue);
996 
997 		while (atinyvalue--)
998 			g_oIconListManager->AddIconToList(ICON_LIST_INVENTORY, icon_name, icon_hash);
999 	}
1000 
1001 	// setup the major session components
1002 	MS->Pre_initialise_objects();
1003 
1004 	// rebuild the micro-session
1005 	g_mission->Restore_micro_session_from_save_game(stream);
1006 
1007 	Tdebug("save_restore.txt", "restored micro session");
1008 
1009 	// reload object values if the session has been saved
1010 	g_mission->Restore_micro_session_vars();
1011 
1012 	Tdebug("save_restore.txt", "restored vars");
1013 
1014 	// run the init scripts
1015 	MS->Init_objects();
1016 
1017 	Tdebug("save_restore.txt", "initialised objects");
1018 
1019 	// reload object coordinates
1020 	g_mission->Restore_micro_session_coords(TRUE8);
1021 
1022 	MS->Set_init_voxel_floors(); // v important for some logics that will begin by checking a floor number
1023 	// setup player history to new restored coordinate/floor
1024 	MS->pre_interact_floor = MS->logic_structs[MS->player.Fetch_player_id()]->owner_floor_rect;
1025 
1026 	// restore chi if she was following
1027 	atinyvalue = stream->readByte();
1028 	if (atinyvalue) {
1029 		Tdebug("save_restore.txt", "\nrestore CHI");
1030 
1031 		// chi x
1032 		fvar = stream->readSint32LE();
1033 		Tdebug("save_restore.txt", " %d", fvar);
1034 		MS->logic_structs[MS->chi_id]->mega->actor_xyz.x = (PXreal)fvar;
1035 
1036 		// y
1037 		fvar = stream->readSint32LE();
1038 		Tdebug("save_restore.txt", " %d", fvar);
1039 		MS->logic_structs[MS->chi_id]->mega->actor_xyz.y = (PXreal)fvar;
1040 
1041 		// z
1042 		fvar = stream->readSint32LE();
1043 		Tdebug("save_restore.txt", " %d", fvar);
1044 		MS->logic_structs[MS->chi_id]->mega->actor_xyz.z = (PXreal)fvar;
1045 
1046 		g_mission->chi_following = TRUE8; // she was following so set the master flag - may or may not be required but lets go for safety
1047 
1048 		Tdebug("save_restore.txt", " %3.1f %3.1f %3.1f", MS->logic_structs[MS->chi_id]->mega->actor_xyz.x, MS->logic_structs[MS->chi_id]->mega->actor_xyz.y,
1049 		       MS->logic_structs[MS->chi_id]->mega->actor_xyz.z);
1050 	} else {
1051 		Tdebug("save_restore.txt", "\nNOT restoring CHI");
1052 	}
1053 
1054 	// save ammo, bullets, medis
1055 	atinyvalue = stream->readByte();
1056 	g_mission->num_bullets = (uint32)atinyvalue;
1057 
1058 	atinyvalue = stream->readByte();
1059 	g_mission->num_clips = (uint32)atinyvalue;
1060 
1061 	atinyvalue = stream->readByte();
1062 	g_mission->num_medi = (uint32)atinyvalue;
1063 
1064 	// manual camera?
1065 	atinyvalue = stream->readByte();
1066 	if (atinyvalue) {
1067 		Tdebug("save_restore.txt", "\nrestoring manual camera");
1068 
1069 		// save name
1070 		stream->read(MS->manual_camera_name, ENGINE_STRING_LEN);
1071 		// cam number
1072 		MS->cur_camera_number = stream->readUint32LE();
1073 
1074 		MS->manual_camera = TRUE8;
1075 
1076 		char h_buf[8];
1077 		HashFile(MS->manual_camera_name, h_buf);
1078 		MS->Initialise_set(MS->manual_camera_name, h_buf);
1079 	} else {
1080 		Tdebug("save_restore.txt", "\nNOT restoring manual camera");
1081 	}
1082 
1083 	// gamescript pc
1084 	gs.pc = stream->readUint32LE();
1085 
1086 	// timed events
1087 	g_oEventManager->Restore(stream);
1088 
1089 	// Restore the Remora's knowledge about where the player has been.
1090 	g_oRemora->Restore(stream);
1091 
1092 	// load gfx init info for initing a set...
1093 	surface_manager->LoadGFXInfo(stream);
1094 	g_mission->LoadPlatformSpecific(stream);
1095 
1096 	delete stream;
1097 
1098 	// run a logic cycle to get those anims caching!
1099 	MS->One_logic_cycle();
1100 	MS->Camera_director();
1101 	MS->One_logic_cycle();
1102 
1103 	return __LOAD_OK;
1104 }
1105 
Create_display()1106 void _mission::Create_display() {
1107 	switch (g_px->display_mode) {
1108 	case THREED:
1109 		// Need this for development safey - but is redundant in final (console-less) game
1110 		if (!session->SetOK()) {
1111 			g_px->display_mode = TEMP_NETHACK; // so we can bounce out again if a real camera/set is found
1112 
1113 			// Save the actor's control mode
1114 			session->player.Push_control_mode(ACTOR_RELATIVE);
1115 		} else {
1116 			// Check if we have just regain the focus after task switching
1117 			if (gRegainedFocus) {
1118 				session->set.ReInit();
1119 
1120 				if (g_oIconMenu->IsActive())
1121 					g_oIconMenu->ReActivate();
1122 
1123 				gRegainedFocus = false;
1124 			}
1125 			// To get on,off camera events in REMORA mode
1126 			// this function is less accurate than stage_draw computation of same events
1127 			session->UpdateOnOffCamera();
1128 
1129 			// The Remora has a function which gets called every cycle when it is active.  This is because game
1130 			// logic continues to run when the Remora is up.
1131 			// But, note the background is NOT drawn whilst in REMORA mode
1132 			if (g_oRemora->IsActive()) {
1133 				g_oRemora->DrawRemora();
1134 			} else {
1135 //  full 3d stage draw NOT in REMORA mode
1136 				session->Stage_draw_poly();
1137 
1138 				// Only render speech when not in REMORA mode
1139 				// (as REMORA uses speech system to draw its own text)
1140 				if (g_px->on_screen_text) {
1141 					session->Render_speech(session->text_speech_bloc);
1142 
1143 					// If there is currently a SFX subtitle active then display it.
1144 					if (g_oSoundLogicEngine->SubtitleActive())
1145 						g_oSoundLogicEngine->DrawSubtitle();
1146 				}
1147 			}
1148 
1149 			// draw a timer if we have one...
1150 			DrawTimer();
1151 
1152 			// If the icon menu is active, draw it.
1153 			if (g_oIconMenu->IsActive()) {
1154 				g_oIconMenu->DrawIconMenu();
1155 
1156 				// If not in the REMORA then draw the armed menu & health bar as well
1157 				if ((g_oRemora->IsActive() == FALSE8) && (session->logic_structs[session->player.Fetch_player_id()]->mega->Fetch_armed_status())) {
1158 					int32 nBullets = session->player.GetNoBullets();
1159 					int32 nClips = session->player.GetNoAmmoClips();
1160 					int32 maxBullets = session->player.GetBulletsPerClip();
1161 					int32 maxClips = session->player.GetMaxClips();
1162 					g_oIconMenu->DrawArmedMenu(nBullets, maxBullets, nClips, maxClips);
1163 
1164 					session->Draw_health_bar();
1165 					session->health_time = 0; // cancel the health bar timer
1166 				}
1167 			} else if (session->logic_structs[session->player.Fetch_player_id()]->mega->Fetch_armed_status()) { // if player armed
1168 				session->Draw_health_bar();
1169 				session->health_time = 0; // cancel the health bar timer
1170 
1171 				int32 nBullets = session->player.GetNoBullets();
1172 				int32 nClips = session->player.GetNoAmmoClips();
1173 				int32 maxBullets = session->player.GetBulletsPerClip();
1174 				int32 maxClips = session->player.GetMaxClips();
1175 				g_oIconMenu->DrawArmedMenu(nBullets, maxBullets, nClips, maxClips);
1176 			} else if (session->health_time) {
1177 				// Draw the health bar if unarmed and recently taken damage
1178 				session->health_time--;
1179 				session->Draw_health_bar();
1180 			}
1181 
1182 			// If the icon menu is currently flashing added medipacks or clips draw it (but not in Remora).
1183 			if (!g_oRemora->IsActive() && g_oIconMenu->IsAdding())
1184 				g_oIconMenu->DrawAdding();
1185 
1186 			if (g_px->mega_timer)
1187 				session->Display_mega_times();
1188 
1189 			// Crude Interaction Highlight
1190 			session->player.Render_crude_interact_highlight();
1191 			session->Show_lit_unlit_diagnostics();
1192 			session->player.DrawCompass();
1193 		}
1194 		break;
1195 
1196 	default:
1197 		Fatal_error("unknown game display mode");
1198 		break;
1199 	}
1200 }
1201 
1202 } // End of namespace ICB
1203