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