Subversion Repositories Games.Prince of Persia

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
SDLPoP, a port/conversion of the DOS game Prince of Persia.
3
Copyright (C) 2013-2018  Dávid Nagy
4
 
5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
 
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14
 
15
You should have received a copy of the GNU General Public License
16
along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
The authors of this program may be contacted at http://forum.princed.org
19
*/
20
 
21
#include "common.h"
22
#include <setjmp.h>
23
#include <math.h>
24
 
25
// data:461E
26
dat_type * dathandle;
27
 
28
// data:4C08
29
word need_redraw_because_flipped;
30
 
31
// seg000:0000
32
void far pop_main() {
33
        const char* temp = check_param("seed=");
34
        if (temp != NULL) {
35
                random_seed = atoi(temp+5);
36
                seed_was_init = 1;
37
        }
38
 
39
        // debug only: check that the sequence table deobfuscation did not mess things up
40
        #ifdef CHECK_SEQTABLE_MATCHES_ORIGINAL
41
        check_seqtable_matches_original();
42
        #endif
43
 
44
        load_global_options();
45
 
46
#ifdef USE_REPLAY
47
        if (g_argc > 1) {
48
                char *filename = g_argv[1]; // file dragged on top of executable or double clicked
49
                char *e = strrchr(filename, '.');
50
                if (e != NULL && strcasecmp(e, ".P1R") == 0) { // valid replay filename passed as first arg
51
                        start_with_replay_file(filename);
52
                }
53
        }
54
 
55
        temp = check_param("validate");
56
        if (temp != NULL) {
57
                is_validate_mode = 1;
58
                start_with_replay_file(temp);
59
        }
60
#endif
61
 
62
        check_mod_param();
63
        load_mod_options();
64
 
65
        // CusPop option
66
        is_blind_mode = start_in_blind_mode;
67
        // Bug: with start_in_blind_mode enabled, moving objects are not displayed until blind mode is toggled off+on??
68
 
69
        apply_seqtbl_patches();
70
 
71
        char sprintf_temp[100];
72
        int i;
73
 
74
        dathandle = open_dat("PRINCE.DAT", 0);
75
 
76
        /*video_mode =*/ parse_grmode();
77
 
78
        init_timer(60);
79
        parse_cmdline_sound();
80
 
81
        set_hc_pal();
82
 
83
        current_target_surface = rect_sthg(onscreen_surface_, &screen_rect);
84
        show_loading();
85
        set_joy_mode();
86
        cheats_enabled = check_param("megahit") != NULL;
87
#ifdef USE_DEBUG_CHEATS
88
        debug_cheats_enabled = check_param("debug") != NULL;
89
        if (debug_cheats_enabled) cheats_enabled = 1; // param 'megahit' not necessary if 'debug' is used
90
#endif
91
        draw_mode = check_param("draw") != NULL && cheats_enabled;
92
        demo_mode = check_param("demo") != NULL;
93
 
94
        init_copyprot_dialog();
95
#ifdef USE_REPLAY
96
        init_record_replay();
97
#endif
98
 
99
        if (cheats_enabled
100
                #ifdef USE_REPLAY
101
                || recording
102
                #endif
103
        ) {
104
                for (i = 15; i >= 0; --i) {
105
                        snprintf(sprintf_temp, sizeof(sprintf_temp), "%d", i);
106
                        if (check_param(sprintf_temp)) {
107
                                start_level = i;
108
                                break;
109
                        }
110
                }
111
        }
112
#ifdef USE_SCREENSHOT
113
        init_screenshot();
114
#endif
115
 
116
        init_game_main();
117
}
118
 
119
byte* level_var_palettes;
120
 
121
// seg000:024F
122
void __pascal far init_game_main() {
123
        doorlink1_ad = /*&*/level.doorlinks1;
124
        doorlink2_ad = /*&*/level.doorlinks2;
125
        prandom(1);
126
        if (graphics_mode == gmMcgaVga) {
127
                // Guard palettes
128
                guard_palettes = (byte*) load_from_opendats_alloc(10, "bin", NULL, NULL);
129
                // (blood, hurt flash) #E00030 = red
130
                set_pal(12, 0x38, 0x00, 0x0C, 1);
131
                // (palace wall pattern) #C09850 = light brown
132
                set_pal( 6, 0x30, 0x26, 0x14, 0);
133
 
134
                // Level color variations (1.3)
135
                level_var_palettes = load_from_opendats_alloc(20, "bin", NULL, NULL);
136
        }
137
        // PRINCE.DAT: sword
138
        chtab_addrs[id_chtab_0_sword] = load_sprites_from_file(700, 1<<2, 1);
139
        // PRINCE.DAT: flame, sword on floor, potion
140
        chtab_addrs[id_chtab_1_flameswordpotion] = load_sprites_from_file(150, 1<<3, 1);
141
        close_dat(dathandle);
142
#ifdef USE_LIGHTING
143
        init_lighting();
144
#endif
145
        load_sounds(0, 43);
146
        load_opt_sounds(43, 56); //added
147
        hof_read();
148
        show_splash(); // added
149
        show_use_fixes_and_enhancements_prompt(); // added
150
        start_game();
151
}
152
 
153
 
154
// data:02C2
155
word first_start = 1;
156
// data:4C38
157
jmp_buf setjmp_buf;
158
// seg000:0358
159
void __pascal far start_game() {
160
#ifdef USE_COPYPROT
161
        word which_entry;
162
        word pos;
163
        word entry_used[40];
164
        byte letts_used[26];
165
#endif
166
        screen_updates_suspended = 0;
167
        // Prevent filling of stack.
168
        // start_game is called from many places to restart the game, for example:
169
        // process_key, play_frame, draw_game_frame, play_level, control_kid, end_sequence, expired
170
        if (first_start) {
171
                first_start = 0;
172
                setjmp(/*&*/setjmp_buf);
173
        } else {
174
                draw_rect(&screen_rect, 0);
175
                show_quotes();
176
                clear_screen_and_sounds();
177
                longjmp(/*&*/setjmp_buf,-1);
178
        }
179
        release_title_images(); // added
180
        free_optsnd_chtab(); // added
181
#ifdef USE_COPYPROT
182
        copyprot_plac = prandom(13);
183
        memset(&entry_used, 0, sizeof(entry_used));
184
        memset(&letts_used, 0, sizeof(letts_used));
185
        for (pos = 0; pos < 14; ++pos) {
186
                do {
187
                        if (pos == copyprot_plac) {
188
                                which_entry = copyprot_idx = prandom(39);
189
                        } else {
190
                                which_entry = prandom(39);
191
                        }
192
                } while (entry_used[which_entry] || letts_used[copyprot_letter[which_entry]-'A']);
193
                cplevel_entr[pos] = which_entry;
194
                entry_used[which_entry] = 1;
195
                letts_used[copyprot_letter[which_entry]-'A'] = 1;
196
        }
197
#endif
198
        if (skip_title) { // CusPop option: skip the title sequence (level loads instantly)
199
                int level_number = (start_level >= 0) ? start_level : first_level;
200
                init_game(level_number);
201
                return;
202
        }
203
 
204
        if (start_level < 0) {
205
                show_title();
206
        } else {
207
                init_game(start_level);
208
        }
209
}
210
 
211
#ifdef USE_QUICKSAVE
212
// All these functions return true on success, false otherwise.
213
 
214
FILE* quick_fp;
215
 
216
int process_save(void* data, size_t data_size) {
217
        return fwrite(data, data_size, 1, quick_fp) == 1;
218
}
219
 
220
int process_load(void* data, size_t data_size) {
221
        return fread(data, data_size, 1, quick_fp) == 1;
222
}
223
 
224
typedef int process_func_type(void* data, size_t data_size);
225
 
226
int quick_process(process_func_type process_func) {
227
        int ok = 1;
228
#define process(x) ok = ok && process_func(&(x), sizeof(x))
229
        // level
230
        process(level);
231
        process(checkpoint);
232
        process(upside_down);
233
        process(drawn_room);
234
        process(current_level);
235
        process(next_level);
236
        process(mobs_count);
237
        process(mobs);
238
        process(trobs_count);
239
        process(trobs);
240
        process(leveldoor_open);
241
        //process(exit_room_timer);
242
        // kid
243
        process(Kid);
244
        process(hitp_curr);
245
        process(hitp_max);
246
        process(hitp_beg_lev);
247
        process(grab_timer);
248
        process(holding_sword);
249
        process(united_with_shadow);
250
        process(have_sword);
251
        /*process(ctrl1_forward);
252
        process(ctrl1_backward);
253
        process(ctrl1_up);
254
        process(ctrl1_down);
255
        process(ctrl1_shift2);*/
256
        process(kid_sword_strike);
257
        process(pickup_obj_type);
258
        process(offguard);
259
        // guard
260
        process(Guard);
261
        process(Char);
262
        process(Opp);
263
        process(guardhp_curr);
264
        process(guardhp_max);
265
        process(demo_index);
266
        process(demo_time);
267
        process(curr_guard_color);
268
        process(guard_notice_timer);
269
        process(guard_skill);
270
        process(shadow_initialized);
271
        process(guard_refrac);
272
        process(justblocked);
273
        process(droppedout);
274
        // collision
275
        process(curr_row_coll_room);
276
        process(curr_row_coll_flags);
277
        process(below_row_coll_room);
278
        process(below_row_coll_flags);
279
        process(above_row_coll_room);
280
        process(above_row_coll_flags);
281
        process(prev_collision_row);
282
        // flash
283
        process(flash_color);
284
        process(flash_time);
285
        // sounds
286
        process(need_level1_music);
287
        process(is_screaming);
288
        process(is_feather_fall);
289
        process(last_loose_sound);
290
        //process(next_sound);
291
        //process(current_sound);
292
        // random
293
        process(random_seed);
294
        // remaining time
295
        process(rem_min);
296
        process(rem_tick);
297
        // saved controls
298
        process(control_x);
299
        process(control_y);
300
        process(control_shift);
301
        process(control_forward);
302
        process(control_backward);
303
        process(control_up);
304
        process(control_down);
305
        process(control_shift2);
306
        process(ctrl1_forward);
307
        process(ctrl1_backward);
308
        process(ctrl1_up);
309
        process(ctrl1_down);
310
        process(ctrl1_shift2);
311
#undef process
312
        return ok;
313
}
314
 
315
const char* quick_file = "QUICKSAVE.SAV";
316
const char quick_version[] = "V1.16b4 ";
317
char quick_control[] = "........";
318
#ifdef OSX
319
#include <CoreServices/CoreServices.h>
320
#endif // OSX
321
 
322
const char* get_quick_path(char* custom_path_buffer, size_t max_len) {
323
        if (!use_custom_levelset) {
324
#ifdef OSX
325
                static char path[PATH_MAX] = "";
326
                if (path[0] == 0)
327
                {
328
                        FSRef ref;
329
                        FSFindFolder (kUserDomain, kApplicationSupportFolderType, kCreateFolder, &ref);
330
                        FSRefMakePath (&ref, (UInt8 *) &path, PATH_MAX);
331
                        strcat (path, "/Prince of Persia");
332
                        mkdir (path, 0755);
333
                        strcat (path, "/QUICKSAVE.SAV");
334
                }
335
                return (path);
336
#else // !OSX
337
                return quick_file;
338
#endif // OSX
339
        }
340
        // if playing a custom levelset, try to use the mod folder
341
        snprintf(custom_path_buffer, max_len, "mods/%s/%s", levelset_name, quick_file /*QUICKSAVE.SAV*/ );
342
        return custom_path_buffer;
343
}
344
 
345
int quick_save() {
346
        int ok = 0;
347
        char custom_quick_path[POP_MAX_PATH];
348
        const char* path = get_quick_path(custom_quick_path, sizeof(custom_quick_path));
349
        quick_fp = fopen(path, "wb");
350
        if (quick_fp != NULL) {
351
                process_save((void*) quick_version, COUNT(quick_version));
352
                ok = quick_process(process_save);
353
                fclose(quick_fp);
354
                quick_fp = NULL;
355
        }
356
        return ok;
357
}
358
 
359
void restore_room_after_quick_load() {
360
        int temp1 = curr_guard_color;
361
        int temp2 = next_level;
362
        reset_level_unused_fields(false);
363
        load_lev_spr(current_level);
364
        curr_guard_color = temp1;
365
        next_level = temp2;
366
 
367
        //need_full_redraw = 1;
368
        different_room = 1;
369
        next_room = drawn_room;
370
        load_room_links();
371
        //draw_level_first();
372
        //gen_palace_wall_colors();
373
        is_guard_notice = 0; // prevent guard turning around immediately
374
        draw_game_frame(); // for falling
375
        //redraw_screen(1); // for room_L
376
 
377
        hitp_delta = guardhp_delta = 1; // force HP redraw
378
        draw_hp();
379
        loadkid_and_opp();
380
        // Get rid of "press button" message if kid was dead before quickload.
381
        text_time_total = text_time_remaining = 0;
382
        //next_sound = current_sound = -1;
383
        exit_room_timer = 0;
384
}
385
 
386
int quick_load() {
387
        int ok = 0;
388
        char custom_quick_path[POP_MAX_PATH];
389
        const char* path = get_quick_path(custom_quick_path, sizeof(custom_quick_path));
390
        quick_fp = fopen(path, "rb");
391
        if (quick_fp != NULL) {
392
                // check quicksave version is compatible
393
                process_load(quick_control, COUNT(quick_control));
394
                if (strcmp(quick_control, quick_version) != 0) {
395
                        fclose(quick_fp);
396
                        quick_fp = NULL;
397
                        return 0;
398
                }
399
 
400
                stop_sounds();
401
                start_timer(timer_0, 5); // briefly display a black screen as a visual cue
402
                draw_rect(&screen_rect, 0);
403
                screen_updates_suspended = 0;
404
                request_screen_update();
405
                screen_updates_suspended = 1;
406
                short old_rem_min = rem_min;
407
                word old_rem_tick = rem_tick;
408
 
409
                ok = quick_process(process_load);
410
                fclose(quick_fp);
411
                quick_fp = NULL;
412
 
413
                restore_room_after_quick_load();
414
 
415
                do_wait(timer_0);
416
                screen_updates_suspended = 0;
417
                request_screen_update();
418
 
419
                #ifdef USE_QUICKLOAD_PENALTY
420
                // Subtract one minute from the remaining time (if it is above 5 minutes)
421
                if (enable_quicksave_penalty &&
422
                        // don't apply the penalty after time has already stopped!
423
                        (current_level < 13 || (current_level == 13 && leveldoor_open < 2))
424
                ) {
425
                        int ticks_elapsed = 720 * (rem_min - old_rem_min) + (rem_tick - old_rem_tick);
426
                        // don't restore time at all if the elapsed time is between 0 and 1 minutes
427
                        if (ticks_elapsed > 0 && ticks_elapsed < 720) {
428
                                rem_min = old_rem_min;
429
                                rem_tick = old_rem_tick;
430
                        }
431
                        else {
432
                                if (rem_min == 6) rem_tick = 719; // crop to "5 minutes" exactly, if hitting the threshold in <1 minute
433
                                if (rem_min > 5 /*be lenient, not much time left*/ || rem_min < 0 /*time runs 'forward' if < 0*/) {
434
                                        --rem_min;
435
                                }
436
                        }
437
 
438
                }
439
                #endif
440
        }
441
        return ok;
442
}
443
 
444
int need_quick_save = 0;
445
int need_quick_load = 0;
446
 
447
void check_quick_op() {
448
        if (!enable_quicksave) return;
449
        if (need_quick_save) {
450
                if (!is_feather_fall && quick_save()) {
451
                        display_text_bottom("GAME SAVED");
452
                } else {
453
                        display_text_bottom("UNABLE TO SAVE GAME");
454
                }
455
                need_quick_save = 0;
456
                text_time_total = 24;
457
                text_time_remaining = 24;
458
        }
459
        if (need_quick_load) {
460
#ifdef USE_REPLAY
461
                if (recording) {
462
                        stop_recording(); // quickloading would mess up the replay!
463
                }
464
#endif
465
                if (quick_load()) {
466
                        display_text_bottom("GAME LOADED");
467
                } else {
468
                        display_text_bottom("NO SAVEGAME AVAILABLE");
469
                }
470
                need_quick_load = 0;
471
                text_time_total = 24;
472
                text_time_remaining = 24;
473
        }
474
}
475
 
476
 
477
#endif // USE_QUICKSAVE
478
 
479
Uint32 temp_shift_release_callback(Uint32 interval, void *param) {
480
        const Uint8* state = SDL_GetKeyboardState(NULL);
481
        if (state[SDL_SCANCODE_LSHIFT]) key_states[SDL_SCANCODE_LSHIFT] = 1;
482
        if (state[SDL_SCANCODE_RSHIFT]) key_states[SDL_SCANCODE_RSHIFT] = 1;
483
        return 0; // causes the timer to be removed
484
}
485
 
486
// seg000:04CD
487
int __pascal far process_key() {
488
        char sprintf_temp[80];
489
        int key;
490
        const char* answer_text = NULL;
491
        word need_show_text;
492
        need_show_text = 0;
493
        key = key_test_quit();
494
 
495
        if (start_level < 0) {
496
                if (key || control_shift) {
497
                        #ifdef USE_QUICKSAVE
498
                        if (key == SDL_SCANCODE_F9) need_quick_load = 1;
499
                        #endif
500
                        #ifdef USE_REPLAY
501
                        if (key == SDL_SCANCODE_TAB || need_start_replay) {
502
                                start_replay();
503
                        }
504
                        else if (key == (SDL_SCANCODE_TAB | WITH_CTRL)) {
505
                                start_level = first_level;
506
                                start_recording();
507
                        } else
508
                        #endif
509
                        if (key == (SDL_SCANCODE_L | WITH_CTRL)) { // ctrl-L
510
                                if (!load_game()) return 0;
511
                        } else {
512
                                start_level = first_level; // 1
513
                        }
514
                        draw_rect(&screen_rect, 0);
515
#ifdef USE_FADE
516
                        if (is_global_fading) {
517
                                fade_palette_buffer->proc_restore_free(fade_palette_buffer);
518
                                is_global_fading = 0;
519
                        }
520
#endif
521
                        start_game();
522
                }
523
        }
524
        // If the Kid died, enter or shift will restart the level.
525
        if (rem_min != 0 && Kid.alive > 6 && (control_shift || key == SDL_SCANCODE_RETURN)) {
526
                key = SDL_SCANCODE_A | WITH_CTRL; // ctrl-a
527
        }
528
#ifdef USE_REPLAY
529
        if (recording) key_press_while_recording(&key);
530
        else if (replaying) key_press_while_replaying(&key);
531
#endif
532
        if (key == 0) return 0;
533
        if (is_keyboard_mode) clear_kbd_buf();
534
 
535
        if (!cheats_enabled) // Pierre-Marie Baty -- allow typing 'megahit' in game to enable cheats
536
        {
537
                static const int megahit_chars[] = { SDL_SCANCODE_M, SDL_SCANCODE_E, SDL_SCANCODE_G, SDL_SCANCODE_A, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_T };
538
                static int megahit_index = 0;
539
                megahit_index = (key == megahit_chars[megahit_index] ? megahit_index + 1 : 0);
540
                if (megahit_index == sizeof (megahit_chars) / sizeof (megahit_chars[0]))
541
                {
542
                        answer_text = "CHEATS ENABLED";
543
                        need_show_text = 1;
544
                        cheats_enabled = 1;
545
                }
546
        }
547
 
548
        switch(key) {
549
                case SDL_SCANCODE_ESCAPE: // esc
550
                case SDL_SCANCODE_ESCAPE | WITH_SHIFT: // allow pause while grabbing
551
                        is_paused = 1;
552
                break;
553
                case SDL_SCANCODE_SPACE: // space
554
                        is_show_time = 1;
555
                break;
556
                case SDL_SCANCODE_A | WITH_CTRL: // ctrl-a
557
                        if (current_level != 15) {
558
                                stop_sounds();
559
                                is_restart_level = 1;
560
                        }
561
                break;
562
                case SDL_SCANCODE_G | WITH_CTRL: // ctrl-g
563
                        // CusPoP: first and last level where saving is allowed
564
//                      if (current_level > 2 && current_level < 14) { // original
565
                        if (current_level >= saving_allowed_first_level && current_level <= saving_allowed_last_level) {
566
                                save_game();
567
                        }
568
                break;
569
                case SDL_SCANCODE_J | WITH_CTRL: // ctrl-j
570
                        if ((sound_flags & sfDigi) && sound_mode == smTandy) {
571
                                answer_text = "JOYSTICK UNAVAILABLE";
572
                        } else {
573
                                if (set_joy_mode()) {
574
                                        answer_text = "JOYSTICK MODE";
575
                                } else {
576
                                        answer_text = "JOYSTICK NOT FOUND";
577
                                }
578
                        }
579
                        need_show_text = 1;
580
                break;
581
                case SDL_SCANCODE_K | WITH_CTRL: // ctrl-k
582
                        answer_text = "KEYBOARD MODE";
583
                        is_joyst_mode = 0;
584
                        is_keyboard_mode = 1;
585
                        need_show_text = 1;
586
                break;
587
                case SDL_SCANCODE_R | WITH_CTRL: // ctrl-r
588
                        start_level = -1;
589
                        start_game();
590
                break;
591
                case SDL_SCANCODE_S | WITH_CTRL: // ctrl-s
592
                        turn_sound_on_off((!is_sound_on) * 15);
593
                        answer_text = "SOUND OFF";
594
                        if (is_sound_on) {
595
                                answer_text = "SOUND ON";
596
                        }
597
                        //
598
                        need_show_text = 1;
599
                break;
600
                case SDL_SCANCODE_V | WITH_CTRL: // ctrl-v
601
                        //answer_text = "PRINCE OF PERSIA  V1.0";
602
                        snprintf(sprintf_temp, sizeof(sprintf_temp), "Prince of Persia v%s-SDL\n", SDLPOP_VERSION);
603
                        answer_text = sprintf_temp;
604
                        need_show_text = 1;
605
                break;
606
                case SDL_SCANCODE_L | WITH_SHIFT: // shift-l
607
                        if (current_level < shift_L_allowed_until_level /* 4 */ || cheats_enabled) {
608
                                // if shift is not released within the delay, the cutscene is skipped
609
                                Uint32 delay = 250;
610
                                key_states[SDL_SCANCODE_LSHIFT] = 0;
611
                                key_states[SDL_SCANCODE_RSHIFT] = 0;
612
                                SDL_TimerID timer;
613
                                timer = SDL_AddTimer(delay, temp_shift_release_callback, NULL);
614
                                if (timer == 0) {
615
                                        sdlperror("SDL_AddTimer");
616
                                        quit(1);
617
                                }
618
                                if (current_level == 14) {
619
                                        next_level = 1;
620
                                } else {
621
                                        if (current_level == 15 && cheats_enabled) {
622
#ifdef USE_COPYPROT
623
                                                if (enable_copyprot) {
624
                                                        next_level = copyprot_level;
625
                                                        copyprot_level = -1;
626
                                                }
627
#endif
628
                                        } else {
629
                                                next_level = current_level + 1;
630
                                                if (!cheats_enabled && rem_min > shift_L_reduced_minutes /* 15 */) {
631
                                                        rem_min = shift_L_reduced_minutes; // 15
632
                                                        rem_tick = shift_L_reduced_ticks; // 719
633
                                                }
634
                                        }
635
                                }
636
                                stop_sounds();
637
                        }
638
                break;
639
#ifdef USE_QUICKSAVE
640
                case SDL_SCANCODE_F6:
641
                case SDL_SCANCODE_F6 | WITH_SHIFT:
642
                        if (Kid.alive < 0) need_quick_save = 1;
643
                break;
644
                case SDL_SCANCODE_F9:
645
                case SDL_SCANCODE_F9 | WITH_SHIFT:
646
                        need_quick_load = 1;
647
                break;
648
#ifdef USE_REPLAY
649
                case SDL_SCANCODE_TAB | WITH_CTRL:
650
                case SDL_SCANCODE_TAB | WITH_CTRL | WITH_SHIFT:
651
                        if (recording) { // finished recording
652
                                stop_recording();
653
                        }
654
                        else { // should start recording
655
                                start_recording();
656
                        }
657
                break;
658
#endif // USE_REPLAY
659
#endif // USE_QUICKSAVE
660
        }
661
        if (cheats_enabled) {
662
                switch (key) {
663
                        case SDL_SCANCODE_C: // c
664
                                snprintf(sprintf_temp, sizeof(sprintf_temp), "S%d L%d R%d A%d B%d", drawn_room, room_L, room_R, room_A, room_B);
665
                                answer_text = /*&*/sprintf_temp;
666
                                need_show_text = 1;
667
                        break;
668
                        case SDL_SCANCODE_C | WITH_SHIFT: // shift-c
669
                                snprintf(sprintf_temp, sizeof(sprintf_temp), "AL%d AR%d BL%d BR%d", room_AL, room_AR, room_BL, room_BR);
670
                                answer_text = /*&*/sprintf_temp;
671
                                need_show_text = 1;
672
                        break;
673
                        case SDL_SCANCODE_MINUS:
674
                        case SDL_SCANCODE_KP_MINUS:             // '-' --> subtract time cheat
675
                                if (rem_min > 1) --rem_min;
676
 
677
#ifdef ALLOW_INFINITE_TIME
678
                                else if (rem_min < -1) ++rem_min; // if negative/infinite, time runs 'forward'
679
                                else if (rem_min == -1) rem_tick = 720; // resets the timer to 00:00:00
680
#endif
681
 
682
                                text_time_total = 0;
683
                                text_time_remaining = 0;
684
                                is_show_time = 1;
685
                        break;
686
                        case SDL_SCANCODE_EQUALS | WITH_SHIFT: // '+'
687
                        case SDL_SCANCODE_KP_PLUS:         // '+' --> add time cheat
688
 
689
#ifdef ALLOW_INFINITE_TIME
690
                                if (rem_min < 0) { // if negative/infinite, time runs 'forward'
691
                                        if (rem_min > INT16_MIN) --rem_min;
692
                                }
693
                                else ++rem_min;
694
#else
695
                                ++rem_min;
696
#endif
697
 
698
                                text_time_total = 0;
699
                                text_time_remaining = 0;
700
                                is_show_time = 1;
701
                        break;
702
                        case SDL_SCANCODE_R: // R --> revive kid cheat
703
                                if (Kid.alive > 0) {
704
                                        resurrect_time = 20;
705
                                        Kid.alive = -1;
706
                                        erase_bottom_text(1);
707
                                }
708
                        break;
709
                        case SDL_SCANCODE_K: // K --> kill guard cheat
710
                                guardhp_delta = -guardhp_curr;
711
                                Guard.alive = 0;
712
                        break;
713
                        case SDL_SCANCODE_I | WITH_SHIFT: // shift+I --> invert cheat
714
                                toggle_upside();
715
                        break;
716
                        case SDL_SCANCODE_W | WITH_SHIFT: // shift+W --> feather fall cheat
717
                                feather_fall();
718
                        break;
719
                        case SDL_SCANCODE_H: // H --> view room to the left
720
                                draw_guard_hp(0, 10);
721
                                next_room = room_L;
722
                        break;
723
                        case SDL_SCANCODE_J: // J --> view room to the right
724
                                draw_guard_hp(0, 10);
725
                                next_room = room_R;
726
                        break;
727
                        case SDL_SCANCODE_U: // U --> view room above
728
                                draw_guard_hp(0, 10);
729
                                next_room = room_A;
730
                        break;
731
                        case SDL_SCANCODE_N: // N --> view room below
732
                                draw_guard_hp(0, 10);
733
                                next_room = room_B;
734
                        break;
735
                        case SDL_SCANCODE_B | WITH_SHIFT: // shift-b
736
                                is_blind_mode = !is_blind_mode;
737
                                if (is_blind_mode) {
738
                                        draw_rect(&rect_top, 0);
739
                                } else {
740
                                        need_full_redraw = 1;
741
                                }
742
                        break;
743
                        case SDL_SCANCODE_S | WITH_SHIFT: // shift-s
744
                                if (hitp_curr != hitp_max) {
745
                                        play_sound(sound_33_small_potion); // small potion (cheat)
746
                                        hitp_delta = 1;
747
                                        flash_color = 4; // red
748
                                        flash_time = 2;
749
                                }
750
                        break;
751
                        case SDL_SCANCODE_T | WITH_SHIFT: // shift-t
752
                                play_sound(sound_30_big_potion); // big potion (cheat)
753
                                flash_color = 4; // red
754
                                flash_time = 4;
755
                                add_life();
756
                        break;
757
                        #ifdef USE_DEBUG_CHEATS
758
                        case SDL_SCANCODE_T:
759
                                is_timer_displayed = 1 - is_timer_displayed; // toggle
760
                                if (!is_timer_displayed) {
761
                                        need_full_redraw = 1;
762
                                }
763
                        break;
764
                        #endif
765
                }
766
        }
767
 
768
        if (need_show_text) {
769
                display_text_bottom(answer_text);
770
                text_time_total = 24;
771
                text_time_remaining = 24;
772
        }
773
        return 1;
774
}
775
 
776
// seg000:08EB
777
void __pascal far play_frame() {
778
        do_mobs();
779
        process_trobs();
780
        check_skel();
781
        check_can_guard_see_kid();
782
        // if level is restarted, return immediately
783
        if (play_kid_frame()) return;
784
        play_guard_frame();
785
        if (0 == resurrect_time) {
786
                check_sword_hurting();
787
                check_sword_hurt();
788
        }
789
        check_sword_vs_sword();
790
        do_delta_hp();
791
        exit_room();
792
        check_the_end();
793
        check_guard_fallout();
794
        if (current_level == 0) {
795
                // Special event: level 0 running exit
796
                if (Kid.room == 24) {
797
                        draw_rect(&screen_rect, 0);
798
                        start_level = -1;
799
                        need_quotes = 1;
800
                        start_game();
801
                }
802
        } else if(current_level == 6) {
803
                // Special event: level 6 falling exit
804
                if (roomleave_result == -2) {
805
                        Kid.y = -1;
806
                        stop_sounds();
807
                        ++next_level;
808
                }
809
        } else if(current_level == 12) {
810
                // Special event: level 12 running exit
811
                if (Kid.room == 23) {
812
                        ++next_level;
813
// Sounds must be stopped, because play_level_2() checks next_level only if there are no sounds playing.
814
                        stop_sounds();
815
                        seamless = 1;
816
                }
817
        }
818
        show_time();
819
        // expiring doesn't count on Jaffar/princess level
820
        if (current_level < 13 && rem_min == 0) {
821
                expired();
822
        }
823
}
824
 
825
// seg000:09B6
826
void __pascal far draw_game_frame() {
827
        short var_2;
828
        if (need_full_redraw) {
829
                redraw_screen(0);
830
                need_full_redraw = 0;
831
        } else {
832
                if (different_room) {
833
                        drawn_room = next_room;
834
                        if (tbl_level_type[current_level]) {
835
                                gen_palace_wall_colors();
836
                        }
837
                        redraw_screen(1);
838
                } else {
839
                        if (need_redraw_because_flipped) {
840
                                need_redraw_because_flipped = 0;
841
                                redraw_screen(0);
842
                        } else {
843
                                memset_near(&table_counts, 0, sizeof(table_counts));
844
                                draw_moving();
845
                                draw_tables();
846
                                if (is_blind_mode) {
847
                                        draw_rect(&rect_top, 0);
848
                                }
849
                                if (upside_down) {
850
                                        flip_screen(offscreen_surface);
851
                                }
852
                                while (drects_count--) {
853
                                        copy_screen_rect(&drects[drects_count]);
854
                                }
855
                                if (upside_down) {
856
                                        flip_screen(offscreen_surface);
857
                                }
858
                                drects_count = 0;
859
                        }
860
                }
861
        }
862
 
863
        play_next_sound();
864
        // Note: texts are identified by their total time!
865
        if (text_time_remaining == 1) {
866
                // If the text's is about to expire:
867
                if (text_time_total == 36 || text_time_total == 288) {
868
                        // 36: died on demo/potions level
869
                        // 288: press button to continue
870
                        // In this case, restart the game.
871
                        start_level = -1;
872
                        need_quotes = 1;
873
 
874
#ifdef USE_REPLAY
875
                        if (recording) stop_recording();
876
                        if (replaying) end_replay();
877
#endif
878
 
879
                        start_game();
880
                } else {
881
                        // Otherwise, just clear it.
882
                        erase_bottom_text(1);
883
                }
884
        } else {
885
                if (text_time_remaining != 0 && text_time_total != 1188) {
886
                        // 1188: potions level (page/line/word) -- this one does not disappear
887
                        --text_time_remaining;
888
                        if (text_time_total == 288 && text_time_remaining < 72) {
889
                                // 288: press button to continue
890
                                // Blink the message:
891
                                var_2 = text_time_remaining % 12;
892
                                if (var_2 > 3) {
893
                                        erase_bottom_text(0);
894
                                } else {
895
                                        if (var_2 == 3) {
896
                                                display_text_bottom("Press Button to Continue");
897
                                                play_sound_from_buffer(sound_pointers[sound_38_blink]); // press button blink
898
                                        }
899
                                }
900
                        }
901
                }
902
        }
903
}
904
 
905
// seg000:0B12
906
void __pascal far anim_tile_modif() {
907
        word tilepos;
908
        for (tilepos = 0; tilepos < 30; ++tilepos) {
909
                switch (get_curr_tile(tilepos)) {
910
                        case tiles_10_potion:
911
                                start_anim_potion(drawn_room, tilepos);
912
                        break;
913
                        case tiles_19_torch:
914
                        case tiles_30_torch_with_debris:
915
                                start_anim_torch(drawn_room, tilepos);
916
                        break;
917
                        case tiles_22_sword:
918
                                start_anim_sword(drawn_room, tilepos);
919
                        break;
920
                }
921
        }
922
}
923
 
924
// seg000:0B72
925
void __pascal far load_sounds(int first,int last) {
926
        dat_type* ibm_dat = NULL;
927
        dat_type* digi1_dat = NULL;
928
//      dat_type* digi2_dat = NULL;
929
        dat_type* digi3_dat = NULL;
930
        dat_type* midi_dat = NULL;
931
        short current;
932
        ibm_dat = open_dat("IBM_SND1.DAT", 0);
933
        if (sound_flags & sfDigi) {
934
                digi1_dat = open_dat("DIGISND1.DAT", 0);
935
//              digi2_dat = open_dat("DIGISND2.DAT", 0);
936
                digi3_dat = open_dat("DIGISND3.DAT", 0);
937
        }
938
        if (sound_flags & sfMidi) {
939
                midi_dat = open_dat("MIDISND1.DAT", 0);
940
        }
941
 
942
        #ifdef USE_MIXER
943
        load_sound_names();
944
        #endif
945
 
946
        for (current = first; current <= last; ++current) {
947
                if (sound_pointers[current] != NULL) continue;
948
                /*if (demo_mode) {
949
                        sound_pointers[current] = decompress_sound((sound_buffer_type*) load_from_opendats_alloc(current + 10000));
950
                } else*/ {
951
                        //sound_pointers[current] = (sound_buffer_type*) load_from_opendats_alloc(current + 10000, "bin", NULL, NULL);
952
                        //printf("overwriting sound_pointers[%d] = %p\n", current, sound_pointers[current]);
953
 
954
 
955
                        sound_pointers[current] = load_sound(current);
956
                }
957
        }
958
        if (midi_dat) close_dat(midi_dat);
959
        if (digi1_dat) close_dat(digi1_dat);
960
//      if (digi2_dat) close_dat(digi2_dat);
961
        if (digi3_dat) close_dat(digi3_dat);
962
        close_dat(ibm_dat);
963
}
964
 
965
// seg000:0C5E
966
void __pascal far load_opt_sounds(int first,int last) {
967
        // stub
968
        dat_type* ibm_dat = NULL;
969
        dat_type* digi_dat = NULL;
970
        dat_type* midi_dat = NULL;
971
        short current;
972
        ibm_dat = open_dat("IBM_SND2.DAT", 0);
973
        if (sound_flags & sfDigi) {
974
                digi_dat = open_dat("DIGISND2.DAT", 0);
975
        }
976
        if (sound_flags & sfMidi) {
977
                midi_dat = open_dat("MIDISND2.DAT", 0);
978
        }
979
        for (current = first; current <= last; ++current) {
980
                //We don't free sounds, so load only once.
981
                if (sound_pointers[current] != NULL) continue;
982
                /*if (demo_mode) {
983
                        sound_pointers[current] = decompress_sound((sound_buffer_type*) load_from_opendats_alloc(current + 10000));
984
                } else*/ {
985
                        //sound_pointers[current] = (sound_buffer_type*) load_from_opendats_alloc(current + 10000, "bin", NULL, NULL);
986
                        //printf("overwriting sound_pointers[%d] = %p\n", current, sound_pointers[current]);
987
                        sound_pointers[current] = load_sound(current);
988
                }
989
        }
990
        if (midi_dat) close_dat(midi_dat);
991
        if (digi_dat) close_dat(digi_dat);
992
        close_dat(ibm_dat);
993
}
994
 
995
// data:03BA
996
const char*const tbl_guard_dat[] = {"GUARD.DAT", "FAT.DAT", "SKEL.DAT", "VIZIER.DAT", "SHADOW.DAT"};
997
// data:03C4
998
const char*const tbl_envir_gr[] = {"", "C", "C", "E", "E", "V"};
999
// data:03D0
1000
const char*const tbl_envir_ki[] = {"DUNGEON", "PALACE"};
1001
// seg000:0D20
1002
void __pascal far load_lev_spr(int level) {
1003
        dat_type* dathandle;
1004
        short guardtype;
1005
        char filename[20];
1006
        dathandle = NULL;
1007
        current_level = next_level = level;
1008
        draw_rect(&screen_rect, 0);
1009
        free_optsnd_chtab();
1010
        snprintf(filename, sizeof(filename), "%s%s.DAT",
1011
                tbl_envir_gr[graphics_mode],
1012
                tbl_envir_ki[tbl_level_type[current_level]]
1013
        );
1014
        load_chtab_from_file(id_chtab_6_environment, 200, filename, 1<<5);
1015
        load_more_opt_graf(filename);
1016
        guardtype = tbl_guard_type[current_level];
1017
        if (guardtype != -1) {
1018
                if (guardtype == 0) {
1019
                        dathandle = open_dat(tbl_level_type[current_level] ? "GUARD1.DAT" : "GUARD2.DAT", 0);
1020
                }
1021
                load_chtab_from_file(id_chtab_5_guard, 750, tbl_guard_dat[guardtype], 1<<8);
1022
                if (dathandle) {
1023
                        close_dat(dathandle);
1024
                }
1025
        }
1026
        curr_guard_color = 0;
1027
        load_chtab_from_file(id_chtab_7_environmentwall, 360, filename, 1<<6);
1028
 
1029
        // Level colors (1.3)
1030
        if (graphics_mode == gmMcgaVga && level_var_palettes != NULL) {
1031
                int level_color = tbl_level_color[current_level];
1032
                if (level_color != 0) {
1033
                        byte* env_pal = level_var_palettes + 0x30*(level_color-1);
1034
                        byte* wall_pal = env_pal + 0x30 * tbl_level_type[current_level];
1035
                        set_pal_arr(0x50, 0x10, (rgb_type*)env_pal, 1);
1036
                        set_pal_arr(0x60, 0x10, (rgb_type*)wall_pal, 1);
1037
                        set_chtab_palette(chtab_addrs[id_chtab_6_environment], env_pal, 0x10);
1038
                        set_chtab_palette(chtab_addrs[id_chtab_7_environmentwall], wall_pal, 0x10);
1039
                }
1040
        }
1041
 
1042
        /*if (comp_skeleton[current_level])*/ {
1043
                load_opt_sounds(44, 44); // skel alive
1044
        }
1045
        /*if (comp_mirror[current_level])*/ {
1046
                load_opt_sounds(45, 45); // mirror
1047
        }
1048
        /*if (comp_chomper[current_level])*/ {
1049
                load_opt_sounds(46, 47); // something chopped, chomper
1050
        }
1051
        /*if (comp_spike[current_level])*/ {
1052
                load_opt_sounds(48, 49); // something spiked, spikes
1053
        }
1054
}
1055
 
1056
// seg000:0E6C
1057
void __pascal far load_level() {
1058
        dat_type* dathandle;
1059
        dathandle = open_dat("LEVELS.DAT", 0);
1060
        load_from_opendats_to_area(current_level + 2000, &level, sizeof(level), "bin");
1061
        close_dat(dathandle);
1062
 
1063
        alter_mods_allrm();
1064
        reset_level_unused_fields(true); // added
1065
}
1066
 
1067
void reset_level_unused_fields(bool loading_clean_level) {
1068
        // Entirely unused fields in the level format: reset to zero for now
1069
        // They can be repurposed to add new stuff to the level format in the future
1070
        memset(level.roomxs, 0, sizeof(level.roomxs));
1071
        memset(level.roomys, 0, sizeof(level.roomys));
1072
        memset(level.fill_1, 0, sizeof(level.fill_1));
1073
        memset(level.fill_2, 0, sizeof(level.fill_2));
1074
        memset(level.fill_3, 0, sizeof(level.fill_3));
1075
 
1076
        // For these fields, only use the bits that are actually used, and set the rest to zero.
1077
        // Good for repurposing the unused bits in the future.
1078
        int i;
1079
        for (i = 0; i < level.used_rooms; ++i) {
1080
                //level.guards_dir[i]   &= 0x01; // 1 bit in use
1081
                level.guards_skill[i] &= 0x0F; // 4 bits in use
1082
        }
1083
 
1084
        // In savestates, additional information may be stored (e.g. remembered guard hp) - should not reset this then!
1085
        if (loading_clean_level) {
1086
                for (i = 0; i < level.used_rooms; ++i) {
1087
                        level.guards_color[i] &= 0x0F; // 4 bits in use (other 4 bits repurposed as remembered guard hp)
1088
                }
1089
        }
1090
 
1091
}
1092
 
1093
// seg000:0EA8
1094
// returns 1 if level is restarted, 0 otherwise
1095
int __pascal far play_kid_frame() {
1096
        loadkid_and_opp();
1097
        load_fram_det_col();
1098
        check_killed_shadow();
1099
        play_kid();
1100
        if (upside_down && Char.alive >= 0) {
1101
                upside_down = 0;
1102
                need_redraw_because_flipped = 1;
1103
        }
1104
        if (is_restart_level) {
1105
                return 1;
1106
        }
1107
        if (Char.room != 0) {
1108
                play_seq();
1109
                fall_accel();
1110
                fall_speed();
1111
                load_frame_to_obj();
1112
                load_fram_det_col();
1113
                set_char_collision();
1114
                bump_into_opponent();
1115
                check_collisions();
1116
                check_bumped();
1117
                check_gate_push();
1118
                check_action();
1119
                check_press();
1120
                check_spike_below();
1121
                if (resurrect_time == 0) {
1122
                        check_spiked();
1123
                        check_chomped_kid();
1124
                }
1125
                check_knock();
1126
        }
1127
        savekid();
1128
        return 0;
1129
}
1130
 
1131
// seg000:0F48
1132
void __pascal far play_guard_frame() {
1133
        if (Guard.direction != dir_56_none) {
1134
                loadshad_and_opp();
1135
                load_fram_det_col();
1136
                check_killed_shadow();
1137
                play_guard();
1138
                if (Char.room == drawn_room) {
1139
                        play_seq();
1140
                        if (Char.x >= 44 && Char.x < 211) {
1141
                                fall_accel();
1142
                                fall_speed();
1143
                                load_frame_to_obj();
1144
                                load_fram_det_col();
1145
                                set_char_collision();
1146
                                check_guard_bumped();
1147
                                check_action();
1148
                                check_press();
1149
                                check_spike_below();
1150
                                check_spiked();
1151
                                check_chomped_guard();
1152
                        }
1153
                }
1154
                saveshad();
1155
        }
1156
}
1157
 
1158
// seg000:0FBD
1159
void __pascal far check_the_end() {
1160
        if (next_room != 0 && next_room != drawn_room) {
1161
                drawn_room = next_room;
1162
                load_room_links();
1163
                if (current_level == 14 && drawn_room == 5) {
1164
#ifdef USE_REPLAY
1165
                        if (recording) stop_recording();
1166
                        if (replaying) end_replay();
1167
#endif
1168
                        // Special event: end of game
1169
                        end_sequence();
1170
                }
1171
                different_room = 1;
1172
                loadkid();
1173
                anim_tile_modif();
1174
                start_chompers();
1175
                check_fall_flo();
1176
                check_shadow();
1177
        }
1178
}
1179
 
1180
// seg000:1009
1181
void __pascal far check_fall_flo() {
1182
        // Special event: falling floors
1183
        if (current_level == 13 && (drawn_room == 23 || drawn_room == 16)) {
1184
                get_room_address(curr_room = room_A);
1185
                for (curr_tilepos = 22; curr_tilepos <= 27; ++curr_tilepos) {
1186
                        make_loose_fall(-(prandom(0xFF) & 0x0F));
1187
                }
1188
        }
1189
}
1190
 
1191
void get_joystick_state(int raw_x, int raw_y, int axis_state[2]) {
1192
 
1193
#define DEGREES_TO_RADIANS (M_PI/180.0)
1194
 
1195
        // check if the X/Y position is within the 'dead zone' of the joystick
1196
        int dist_squared = raw_x*raw_x + raw_y*raw_y;
1197
        if (dist_squared < joystick_threshold*joystick_threshold) {
1198
                axis_state[0] = 0;
1199
                axis_state[1] = 0;
1200
        } else {
1201
                double angle = atan2(raw_y, raw_x); // angle of the joystick: 0 = right, >0 = downward, <0 = upward
1202
                //printf("Joystick angle is %f degrees\n", angle/DEGREES_TO_RADIANS);
1203
 
1204
                if (fabs(angle) < (60*DEGREES_TO_RADIANS)) // 120 degree range facing right
1205
                        axis_state[0] = 1;
1206
 
1207
                else if (fabs(angle) > (120*DEGREES_TO_RADIANS)) // 120 degree range facing left
1208
                        axis_state[0] = -1;
1209
 
1210
                else {
1211
                        // joystick is neutral horizontally, so the control should be released
1212
                        // however: prevent stop running if the Kid was already running / trying to do a running-jump
1213
                        // (this tweak makes it a bit easier to do (multiple) running jumps)
1214
                        if (!(angle < 0 /*facing upward*/ && Kid.action == actions_1_run_jump)) {
1215
                                axis_state[0] = 0;
1216
                        }
1217
                }
1218
 
1219
                if (angle < (-30*DEGREES_TO_RADIANS) && angle > (-150*DEGREES_TO_RADIANS)) // 120 degree range facing up
1220
                        axis_state[1] = -1;
1221
 
1222
                // down slightly less sensitive than up (prevent annoyance when your thumb slips down a bit by accident)
1223
                // (not sure if this adjustment is really necessary)
1224
                else if (angle > (35*DEGREES_TO_RADIANS) && angle < (145*DEGREES_TO_RADIANS)) // 110 degree range facing down
1225
                        axis_state[1] = 1;
1226
 
1227
                else {
1228
                        // joystick is neutral vertically, so the control should be released
1229
                        // however: should prevent unintended standing up when attempting to crouch-hop
1230
                        if (!((Kid.frame >= frame_108_fall_land_2 && Kid.frame <= frame_112_stand_up_from_crouch_3)
1231
                                  && angle > 0 /*facing downward*/))
1232
                        {
1233
                                axis_state[1] = 0;
1234
                        }
1235
                }
1236
        }
1237
}
1238
 
1239
void get_joystick_state_hor_only(int raw_x, int axis_state[2]) {
1240
        if (raw_x > joystick_threshold) {
1241
                axis_state[0] = 1;
1242
        } else if (raw_x < -joystick_threshold) {
1243
                axis_state[0] = -1;
1244
        } else axis_state[0] = 0;
1245
 
1246
        // disregard all vertical input from the joystick controls (only use Y and A buttons or D-pad for up/down)
1247
        axis_state[1] = 0;
1248
}
1249
 
1250
// seg000:1051
1251
void __pascal far read_joyst_control() {
1252
 
1253
        if (joystick_only_horizontal) {
1254
                get_joystick_state_hor_only(joy_axis[SDL_CONTROLLER_AXIS_LEFTX], joy_left_stick_states);
1255
                get_joystick_state_hor_only(joy_axis[SDL_CONTROLLER_AXIS_RIGHTX], joy_right_stick_states);
1256
        } else {
1257
                get_joystick_state(joy_axis[SDL_CONTROLLER_AXIS_LEFTX], joy_axis[SDL_CONTROLLER_AXIS_LEFTY], joy_left_stick_states);
1258
                get_joystick_state(joy_axis[SDL_CONTROLLER_AXIS_RIGHTX], joy_axis[SDL_CONTROLLER_AXIS_RIGHTY], joy_right_stick_states);
1259
        }
1260
 
1261
        if (joy_left_stick_states[0] == -1 || joy_right_stick_states[0] == -1 || joy_hat_states[0] == -1)
1262
                control_x = -1;
1263
 
1264
        if (joy_left_stick_states[0] == 1 || joy_right_stick_states[0] == 1 || joy_hat_states[0] == 1)
1265
                control_x = 1;
1266
 
1267
        if (joy_left_stick_states[1] == -1 || joy_right_stick_states[1] == -1 || joy_hat_states[1] == -1 || joy_AY_buttons_state == -1)
1268
                control_y = -1;
1269
 
1270
        if (joy_left_stick_states[1] == 1 || joy_right_stick_states[1] == 1 || joy_hat_states[1] == 1 || joy_AY_buttons_state == 1)
1271
                control_y = 1;
1272
 
1273
        if (joy_X_button_state == 1 ||
1274
                        joy_axis[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > 8000 ||
1275
                        joy_axis[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > 8000)
1276
        {
1277
                control_shift = -1;
1278
        }
1279
 
1280
}
1281
 
1282
// seg000:10EA
1283
void __pascal far draw_kid_hp(short curr_hp,short max_hp) {
1284
        short drawn_hp_index;
1285
        for (drawn_hp_index = curr_hp; drawn_hp_index < max_hp; ++drawn_hp_index) {
1286
                // empty HP
1287
                method_6_blit_img_to_scr(get_image(id_chtab_2_kid, 217), drawn_hp_index * 7, 194, blitters_0_no_transp);
1288
        }
1289
        for (drawn_hp_index = 0; drawn_hp_index < curr_hp; ++drawn_hp_index) {
1290
                // full HP
1291
                method_6_blit_img_to_scr(get_image(id_chtab_2_kid, 216), drawn_hp_index * 7, 194, blitters_0_no_transp);
1292
        }
1293
}
1294
 
1295
// seg000:1159
1296
void __pascal far draw_guard_hp(short curr_hp,short max_hp) {
1297
        short drawn_hp_index;
1298
        short guard_charid;
1299
        if (chtab_addrs[id_chtab_5_guard] == NULL) return;
1300
        guard_charid = Guard.charid;
1301
        if (guard_charid != charid_4_skeleton &&
1302
                guard_charid != charid_24_mouse &&
1303
                // shadow has HP only on level 12
1304
                (guard_charid != charid_1_shadow || current_level == 12)
1305
        ) {
1306
                for (drawn_hp_index = curr_hp; drawn_hp_index < max_hp; ++drawn_hp_index) {
1307
                        method_6_blit_img_to_scr(chtab_addrs[id_chtab_5_guard]->images[0], 314 - drawn_hp_index * 7, 194, blitters_9_black);
1308
                }
1309
                for (drawn_hp_index = 0; drawn_hp_index < curr_hp; ++drawn_hp_index) {
1310
                        method_6_blit_img_to_scr(chtab_addrs[id_chtab_5_guard]->images[0], 314 - drawn_hp_index * 7, 194, blitters_0_no_transp);
1311
                }
1312
        }
1313
}
1314
 
1315
// seg000:11EC
1316
void __pascal far add_life() {
1317
        short hpmax = hitp_max;
1318
        ++hpmax;
1319
        // CusPop: set maximum number of hitpoints (max_hitp_allowed, default = 10)
1320
//      if (hpmax > 10) hpmax = 10; // original
1321
        if (hpmax > max_hitp_allowed) hpmax = max_hitp_allowed;
1322
        hitp_max = hpmax;
1323
        set_health_life();
1324
}
1325
 
1326
// seg000:1200
1327
void __pascal far set_health_life() {
1328
        hitp_delta = hitp_max - hitp_curr;
1329
}
1330
 
1331
// seg000:120B
1332
void __pascal far draw_hp() {
1333
        if (hitp_delta) {
1334
                draw_kid_hp(hitp_curr, hitp_max);
1335
        }
1336
        if (hitp_curr == 1 && current_level != 15) {
1337
                // blinking hitpoint
1338
                if (rem_tick & 1) {
1339
                        draw_kid_hp(1, 0);
1340
                } else {
1341
                        draw_kid_hp(0, 1);
1342
                }
1343
        }
1344
        if (guardhp_delta) {
1345
                draw_guard_hp(guardhp_curr, guardhp_max);
1346
        }
1347
        if (guardhp_curr == 1) {
1348
                if (rem_tick & 1) {
1349
                        draw_guard_hp(1, 0);
1350
                } else {
1351
                        draw_guard_hp(0, 1);
1352
                }
1353
        }
1354
}
1355
 
1356
// seg000:127B
1357
void __pascal far do_delta_hp() {
1358
        // level 12: if the shadow is hurt, Kid is also hurt
1359
        if (Opp.charid == charid_1_shadow &&
1360
                current_level == 12 &&
1361
                guardhp_delta != 0
1362
        ) {
1363
                hitp_delta = guardhp_delta;
1364
        }
1365
        hitp_curr = MIN(MAX(hitp_curr + hitp_delta, 0), hitp_max);
1366
        guardhp_curr = MIN(MAX(guardhp_curr + guardhp_delta, 0), guardhp_max);
1367
}
1368
 
1369
byte sound_prio_table[] = {
1370
        0x14, 0x1E, 0x23, 0x66, 0x32, 0x37, 0x30, 0x30, 0x4B, 0x50, 0x0A,
1371
        0x12, 0x0C, 0x0B, 0x69, 0x6E, 0x73, 0x78, 0x7D, 0x82, 0x91, 0x96,
1372
        0x9B, 0xA0, 1, 1, 1, 1, 1, 0x13, 1, 1, 1, 1, 1, 1, 1,
1373
        1, 1, 0, 1, 1, 1, 1, 0x87, 0x8C, 0x0F, 0x10, 0x19, 0x16, 1,
1374
        0, 1, 1, 1, 1, 1, 0
1375
};
1376
byte sound_pcspeaker_exists[] = {
1377
        1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1378
        1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1379
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1380
        1, 1, 1, 0
1381
};
1382
 
1383
// seg000:12C5
1384
void __pascal far play_sound(int sound_id) {
1385
        //printf("Would play sound %d\n", sound_id);
1386
        if (next_sound < 0 || sound_prio_table[sound_id] <= sound_prio_table[next_sound]) {
1387
                if (NULL == sound_pointers[sound_id]) return;
1388
                if (sound_pcspeaker_exists[sound_id] != 0 || sound_pointers[sound_id]->type != sound_speaker) {
1389
                        next_sound = sound_id;
1390
                }
1391
        }
1392
}
1393
 
1394
// seg000:1304
1395
void __pascal far play_next_sound() {
1396
        if (next_sound >= 0) {
1397
                if (!check_sound_playing() ||
1398
                        (sound_interruptible[current_sound] != 0 && sound_prio_table[next_sound] <= sound_prio_table[current_sound])
1399
                ) {
1400
                        current_sound = next_sound;
1401
                        play_sound_from_buffer(sound_pointers[current_sound]);
1402
                }
1403
        }
1404
        next_sound = -1;
1405
}
1406
 
1407
// seg000:1353
1408
void __pascal far check_sword_vs_sword() {
1409
        if (Kid.frame == 167 || Guard.frame == 167) {
1410
                play_sound(sound_10_sword_vs_sword); // sword vs. sword
1411
        }
1412
}
1413
 
1414
// seg000:136A
1415
void __pascal far load_chtab_from_file(int chtab_id,int resource,const char near *filename,int palette_bits) {
1416
        //printf("Loading chtab %d, id %d from %s\n",chtab_id,resource,filename);
1417
        dat_type* dathandle;
1418
        if (chtab_addrs[chtab_id] != NULL) return;
1419
        dathandle = open_dat(filename, 0);
1420
        chtab_addrs[chtab_id] = load_sprites_from_file(resource, palette_bits, 1);
1421
        close_dat(dathandle);
1422
}
1423
 
1424
// seg000:13BA
1425
void __pascal far free_all_chtabs_from(int first) {
1426
        word chtab_id;
1427
        free_peels();
1428
        for (chtab_id = first; chtab_id < 10; ++chtab_id) {
1429
                if (chtab_addrs[chtab_id]) {
1430
                        free_chtab(chtab_addrs[chtab_id]);
1431
                        chtab_addrs[chtab_id] = NULL;
1432
                }
1433
        }
1434
}
1435
 
1436
// seg009:12EF
1437
void __pascal far load_one_optgraf(chtab_type* chtab_ptr,dat_pal_type far *pal_ptr,int base_id,int min_index,int max_index) {
1438
        short index;
1439
        for (index = min_index; index <= max_index; ++index) {
1440
                image_type* image = load_image(base_id + index + 1, pal_ptr);
1441
                if (image != NULL) chtab_ptr->images[index] = image;
1442
        }
1443
}
1444
 
1445
byte optgraf_min[] = {0x01, 0x1E, 0x4B, 0x4E, 0x56, 0x65, 0x7F, 0x0A};
1446
byte optgraf_max[] = {0x09, 0x1F, 0x4D, 0x53, 0x5B, 0x7B, 0x8F, 0x0D};
1447
// seg000:13FC
1448
void __pascal far load_more_opt_graf(const char *filename) {
1449
        // stub
1450
        dat_type* dathandle;
1451
        dat_shpl_type area;
1452
        short graf_index;
1453
        dathandle = NULL;
1454
        for (graf_index = 0; graf_index < 8; ++graf_index) {
1455
                /*if (...) */ {
1456
                        if (dathandle == NULL) {
1457
                                dathandle = open_dat(filename, 0);
1458
                                load_from_opendats_to_area(200, &area, sizeof(area), "pal");
1459
                                area.palette.row_bits = 0x20;
1460
                        }
1461
                        load_one_optgraf(chtab_addrs[id_chtab_6_environment], &area.palette, 1200, optgraf_min[graf_index] - 1, optgraf_max[graf_index] - 1);
1462
                }
1463
        }
1464
        if (dathandle != NULL) {
1465
                close_dat(dathandle);
1466
        }
1467
}
1468
 
1469
// seg000:148D
1470
int __pascal far do_paused() {
1471
#ifdef USE_REPLAY
1472
        if (replaying && skipping_replay) return 0;
1473
#endif
1474
 
1475
        word key;
1476
        key = 0;
1477
        next_room = 0;
1478
        control_shift = 0;
1479
        control_y = 0;
1480
        control_x = 0;
1481
        if (is_joyst_mode) {
1482
                read_joyst_control();
1483
        } else {
1484
                read_keyb_control();
1485
        }
1486
        key = process_key();
1487
        if (is_paused) {
1488
                is_paused = 0;
1489
                display_text_bottom("GAME PAUSED");
1490
                // busy waiting?
1491
                do {
1492
                        idle();
1493
                        //request_screen_update();
1494
                } while (! process_key());
1495
                erase_bottom_text(1);
1496
        }
1497
        return key || control_shift;
1498
}
1499
 
1500
// seg000:1500
1501
void __pascal far read_keyb_control() {
1502
 
1503
        if (key_states[SDL_SCANCODE_UP] || key_states[SDL_SCANCODE_HOME] || key_states[SDL_SCANCODE_PAGEUP]
1504
            || key_states[SDL_SCANCODE_KP_8] || key_states[SDL_SCANCODE_KP_7] || key_states[SDL_SCANCODE_KP_9]
1505
        ) {
1506
                control_y = -1;
1507
        } else if (key_states[SDL_SCANCODE_CLEAR] || key_states[SDL_SCANCODE_DOWN]
1508
                   || key_states[SDL_SCANCODE_KP_5] || key_states[SDL_SCANCODE_KP_2]
1509
        ) {
1510
                control_y = 1;
1511
        }
1512
        if (key_states[SDL_SCANCODE_LEFT] || key_states[SDL_SCANCODE_HOME]
1513
            || key_states[SDL_SCANCODE_KP_4] || key_states[SDL_SCANCODE_KP_7]
1514
        ) {
1515
                control_x = -1;
1516
        } else if (key_states[SDL_SCANCODE_RIGHT] || key_states[SDL_SCANCODE_PAGEUP]
1517
                   || key_states[SDL_SCANCODE_KP_6] || key_states[SDL_SCANCODE_KP_9]
1518
        ) {
1519
                control_x = 1;
1520
        }
1521
        control_shift = -(key_states[SDL_SCANCODE_LSHIFT] || key_states[SDL_SCANCODE_RSHIFT]);
1522
 
1523
        #ifdef USE_DEBUG_CHEATS
1524
        if (cheats_enabled && debug_cheats_enabled) {
1525
                if (key_states[SDL_SCANCODE_RIGHTBRACKET]) ++Char.x;
1526
                else if (key_states[SDL_SCANCODE_LEFTBRACKET]) --Char.x;
1527
        }
1528
        #endif
1529
}
1530
 
1531
// seg000:156D
1532
void __pascal far copy_screen_rect(const rect_type far *source_rect_ptr) {
1533
        const rect_type* far target_rect_ptr;
1534
        rect_type target_rect;
1535
        if (upside_down) {
1536
                target_rect_ptr = &target_rect;
1537
                /**target_rect_ptr*/target_rect = *source_rect_ptr;
1538
                /*target_rect_ptr->*/target_rect.top = 192 - source_rect_ptr->bottom;
1539
                /*target_rect_ptr->*/target_rect.bottom = 192 - source_rect_ptr->top;
1540
        } else {
1541
                target_rect_ptr = source_rect_ptr;
1542
        }
1543
        method_1_blit_rect(onscreen_surface_, offscreen_surface, target_rect_ptr, target_rect_ptr, 0);
1544
#ifdef USE_LIGHTING
1545
        update_lighting(target_rect_ptr);
1546
#endif
1547
}
1548
 
1549
// seg000:15E9
1550
void __pascal far toggle_upside() {
1551
        upside_down = ~ upside_down;
1552
        need_redraw_because_flipped = 1;
1553
}
1554
 
1555
// seg000:15F8
1556
void __pascal far feather_fall() {
1557
        is_feather_fall = 1;
1558
        flash_color = 2; // green
1559
        flash_time = 3;
1560
        stop_sounds();
1561
        play_sound(sound_39_low_weight); // low weight
1562
}
1563
 
1564
// seg000:1618
1565
int __pascal far parse_grmode() {
1566
        // stub
1567
        set_gr_mode(gmMcgaVga);
1568
        return gmMcgaVga;
1569
}
1570
 
1571
// seg000:172C
1572
void __pascal far gen_palace_wall_colors() {
1573
        dword old_randseed;
1574
        word prev_color;
1575
        short row;
1576
        short subrow;
1577
        word color_base;
1578
        short column;
1579
        word color;
1580
 
1581
        old_randseed = random_seed;
1582
        random_seed = drawn_room;
1583
        prandom(1); // discard
1584
        for (row = 0; row < 3; row++) {
1585
                for (subrow = 0; subrow < 4; subrow++) {
1586
                        if (subrow % 2) {
1587
                                color_base = 0x61; // 0x61..0x64 in subrow 1 and 3
1588
                        } else {
1589
                                color_base = 0x66; // 0x66..0x69 in subrow 0 and 2
1590
                        }
1591
                        prev_color = -1;
1592
                        for (column = 0; column <= 10; ++column) {
1593
                                do {
1594
                                        color = color_base + prandom(3);
1595
                                } while (color == prev_color);
1596
                                palace_wall_colors[44 * row + 11 * subrow + column] = color;
1597
                                //palace_wall_colors[row][subrow][column] = color;
1598
                                prev_color = color;
1599
                        }
1600
                }
1601
        }
1602
        random_seed = old_randseed;
1603
}
1604
 
1605
// data:042E
1606
const rect_type rect_titles = {106,24,195,296};
1607
 
1608
// seg000:17E6
1609
void __pascal far show_title() {
1610
        word textcolor;
1611
        load_opt_sounds(sound_50_story_2_princess, sound_55_story_1_absence); // main theme, story, princess door
1612
        textcolor = get_text_color(15, color_15_brightwhite, 0x800);
1613
        dont_reset_time = 0;
1614
        if(offscreen_surface) free_surface(offscreen_surface); // missing in original
1615
        offscreen_surface = make_offscreen_buffer(&screen_rect);
1616
        load_title_images(1);
1617
        current_target_surface = offscreen_surface;
1618
        do_wait(timer_0);
1619
 
1620
        draw_image_2(0 /*main title image*/, chtab_title50, 0, 0, blitters_0_no_transp);
1621
        fade_in_2(offscreen_surface, 0x1000); //STUB
1622
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, blitters_0_no_transp);
1623
        play_sound_from_buffer(sound_pointers[54]); // main theme
1624
        start_timer(timer_0, 0x82);
1625
        draw_image_2(1 /*Broderbund Software presents*/, chtab_title50, 96, 106, blitters_0_no_transp);
1626
        do_wait(timer_0);
1627
 
1628
        start_timer(timer_0,0xCD);
1629
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1630
        draw_image_2(0 /*main title image*/, chtab_title50, 0, 0, blitters_0_no_transp);
1631
        do_wait(timer_0);
1632
 
1633
        start_timer(timer_0,0x41);
1634
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1635
        draw_image_2(0 /*main title image*/, chtab_title50, 0, 0, blitters_0_no_transp);
1636
        draw_image_2(2 /*a game by Jordan Mechner*/, chtab_title50, 96, 122, blitters_0_no_transp);
1637
        do_wait(timer_0);
1638
 
1639
        start_timer(timer_0,0x10E);
1640
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1641
        draw_image_2(0 /*main title image*/, chtab_title50, 0, 0, blitters_0_no_transp);
1642
        do_wait(timer_0);
1643
 
1644
        start_timer(timer_0,0xEB);
1645
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1646
        draw_image_2(0 /*main title image*/, chtab_title50, 0, 0, blitters_0_no_transp);
1647
        draw_image_2(3 /*Prince Of Persia*/, chtab_title50, 24, 107, blitters_10h_transp);
1648
        draw_image_2(4 /*Copyright 1990 Jordan Mechner*/, chtab_title50, 48, 184, blitters_0_no_transp);
1649
        do_wait(timer_0);
1650
 
1651
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1652
        draw_image_2(0 /*story frame*/, chtab_title40, 0, 0, blitters_0_no_transp);
1653
        draw_image_2(1 /*In the Sultan's absence*/, chtab_title40, 24, 25, textcolor);
1654
        current_target_surface = onscreen_surface_;
1655
        while (check_sound_playing()) {
1656
                idle();
1657
                do_paused();
1658
        }
1659
//      method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, blitters_0_no_transp);
1660
        play_sound_from_buffer(sound_pointers[sound_55_story_1_absence]); // story 1: In the absence
1661
        transition_ltr();
1662
        pop_wait(timer_0, 0x258);
1663
        fade_out_2(0x800);
1664
        release_title_images();
1665
 
1666
        load_intro(0, &pv_scene, 0);
1667
 
1668
        load_title_images(1);
1669
        current_target_surface = offscreen_surface;
1670
        draw_image_2(0 /*story frame*/, chtab_title40, 0, 0, blitters_0_no_transp);
1671
        draw_image_2(2 /*Marry Jaffar*/, chtab_title40, 24, 25, textcolor);
1672
        fade_in_2(offscreen_surface, 0x800);
1673
        draw_image_2(0 /*main title image*/, chtab_title50, 0, 0, blitters_0_no_transp);
1674
        draw_image_2(3 /*Prince Of Persia*/, chtab_title50, 24, 107, blitters_10h_transp);
1675
        draw_image_2(4 /*Copyright 1990 Jordan Mechner*/, chtab_title50, 48, 184, blitters_0_no_transp);
1676
        while (check_sound_playing()) {
1677
                idle();
1678
                do_paused();
1679
        }
1680
        transition_ltr();
1681
        pop_wait(timer_0, 0x78);
1682
        draw_image_2(0 /*story frame*/, chtab_title40, 0, 0, blitters_0_no_transp);
1683
        draw_image_2(4 /*credits*/, chtab_title40, 24, 26, textcolor);
1684
        transition_ltr();
1685
        pop_wait(timer_0, 0x168);
1686
        if (hof_count) {
1687
                draw_image_2(0 /*story frame*/, chtab_title40, 0, 0, blitters_0_no_transp);
1688
                draw_image_2(3 /*Prince Of Persia*/, chtab_title50, 24, 24, blitters_10h_transp);
1689
                show_hof();
1690
                transition_ltr();
1691
                pop_wait(timer_0, 0xF0);
1692
        }
1693
        current_target_surface = onscreen_surface_;
1694
        while (check_sound_playing()) {
1695
                idle();
1696
                do_paused();
1697
        }
1698
        fade_out_2(0x1800);
1699
        free_surface(offscreen_surface);
1700
        offscreen_surface = NULL; // added
1701
        release_title_images();
1702
        init_game(0);
1703
}
1704
 
1705
// seg000:1BB3
1706
void __pascal far transition_ltr() {
1707
        short position;
1708
        rect_type rect;
1709
        rect.top = 0;
1710
        rect.bottom = 200;
1711
        rect.left = 0;
1712
        rect.right = 2;
1713
        for (position = 0; position < 320; position += 2) {
1714
                method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect, &rect, 0);
1715
                rect.left += 2;
1716
                rect.right += 2;
1717
                pop_wait(timer_1, 0);
1718
        }
1719
}
1720
 
1721
// seg000:1C0F
1722
void __pascal far release_title_images() {
1723
        if (chtab_title50) {
1724
                free_chtab(chtab_title50);
1725
                chtab_title50 = NULL;
1726
        }
1727
        if (chtab_title40) {
1728
                free_chtab(chtab_title40);
1729
                chtab_title40 = NULL;
1730
        }
1731
}
1732
 
1733
// seg000:1C3A
1734
void __pascal far draw_image_2(int id, chtab_type* chtab_ptr, int xpos, int ypos, int blit) {
1735
        image_type* source;
1736
        image_type* decoded_image;
1737
        image_type* mask;
1738
        mask = NULL;
1739
        if (NULL == chtab_ptr) return;
1740
        source = chtab_ptr->images[id];
1741
        decoded_image = source;
1742
        if (blit != blitters_0_no_transp && blit != blitters_10h_transp) {
1743
                method_3_blit_mono(decoded_image, xpos, ypos, blitters_0_no_transp, blit);
1744
        } else if (blit == blitters_10h_transp) {
1745
                if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
1746
                        //...
1747
                } else {
1748
                        mask = decoded_image;
1749
                }
1750
                draw_image_transp(decoded_image, mask, xpos, ypos);
1751
                if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
1752
                        free_far(mask);
1753
                }
1754
        } else {
1755
                method_6_blit_img_to_scr(decoded_image, xpos, ypos, blit);
1756
        }
1757
}
1758
 
1759
// seg000:1D2C
1760
void __pascal far load_kid_sprite() {
1761
        load_chtab_from_file(id_chtab_2_kid, 400, "KID.DAT", 1<<7);
1762
}
1763
 
1764
const char* save_file = "PRINCE.SAV";
1765
 
1766
const char* get_save_path(char* custom_path_buffer, size_t max_len) {
1767
        if (!use_custom_levelset) {
1768
                return save_file;
1769
        }
1770
        // if playing a custom levelset, try to use the mod folder
1771
        snprintf(custom_path_buffer, max_len, "mods/%s/%s", levelset_name, save_file /*PRINCE.SAV*/ );
1772
        return custom_path_buffer;
1773
}
1774
 
1775
// seg000:1D45
1776
void __pascal far save_game() {
1777
        word success;
1778
        int handle;
1779
        success = 0;
1780
        char custom_save_path[POP_MAX_PATH];
1781
        const char* save_path = get_save_path(custom_save_path, sizeof(custom_save_path));
1782
        // no O_TRUNC
1783
        handle = open(save_path, O_WRONLY | O_CREAT | O_BINARY, 0600);
1784
        if (handle == -1) goto loc_1DB8;
1785
        if (write(handle, &rem_min, 2) == 2) goto loc_1DC9;
1786
        loc_1D9B:
1787
        close(handle);
1788
        if (!success) {
1789
                unlink(save_path);
1790
        }
1791
        loc_1DB8:
1792
        if (!success) goto loc_1E18;
1793
        display_text_bottom("GAME SAVED");
1794
        goto loc_1E2E;
1795
        loc_1DC9:
1796
        if (write(handle, &rem_tick, 2) != 2) goto loc_1D9B;
1797
        if (write(handle, &current_level, 2) != 2) goto loc_1D9B;
1798
        if (write(handle, &hitp_beg_lev, 2) != 2) goto loc_1D9B;
1799
        success = 1;
1800
        goto loc_1D9B;
1801
        loc_1E18:
1802
        display_text_bottom("UNABLE TO SAVE GAME");
1803
        //play_sound_from_buffer(&sound_cant_save);
1804
        loc_1E2E:
1805
        text_time_remaining = 24;
1806
}
1807
 
1808
// seg000:1E38
1809
short __pascal far load_game() {
1810
        int handle;
1811
        word success;
1812
        success = 0;
1813
        char custom_save_path[POP_MAX_PATH];
1814
        const char* save_path = get_save_path(custom_save_path, sizeof(custom_save_path));
1815
        handle = open(save_path, O_RDONLY | O_BINARY);
1816
        if (handle == -1) goto loc_1E99;
1817
        if (read(handle, &rem_min, 2) == 2) goto loc_1E9E;
1818
        loc_1E8E:
1819
        close(handle);
1820
        loc_1E99:
1821
        return success;
1822
        loc_1E9E:
1823
        if (read(handle, &rem_tick, 2) != 2) goto loc_1E8E;
1824
        if (read(handle, &start_level, 2) != 2) goto loc_1E8E;
1825
        if (read(handle, &hitp_beg_lev, 2) != 2) goto loc_1E8E;
1826
#ifdef USE_COPYPROT
1827
        if (enable_copyprot && copyprot_level > 0) {
1828
                copyprot_level = start_level;
1829
        }
1830
#endif
1831
        success = 1;
1832
        dont_reset_time = 1;
1833
        goto loc_1E8E;
1834
}
1835
 
1836
// seg000:1F02
1837
void __pascal far clear_screen_and_sounds() {
1838
        short index;
1839
        stop_sounds();
1840
        current_target_surface = rect_sthg(onscreen_surface_, &screen_rect);
1841
 
1842
        is_cutscene = 0;
1843
        peels_count = 0;
1844
        // should these be freed?
1845
        for (index = 2; index < 10; ++index) {
1846
                if (chtab_addrs[index]) {
1847
                        // Original code does not free these?
1848
                        free_chtab(chtab_addrs[index]);
1849
                        chtab_addrs[index] = NULL;
1850
                }
1851
        }
1852
        /* //Don't free sounds.
1853
        for (index = 44; index < 57; ++index) {
1854
                //continue; // don't release sounds? modern machines have enough memory
1855
                free_sound(sound_pointers[index]); // added
1856
                sound_pointers[index] = NULL;
1857
        }
1858
        */
1859
        current_level = -1;
1860
}
1861
 
1862
// seg000:1F7B
1863
void __pascal far parse_cmdline_sound() {
1864
        // stub
1865
        sound_flags |= sfDigi;
1866
}
1867
 
1868
// seg000:226D
1869
void __pascal far free_optional_sounds() {
1870
        /* //Don't free sounds.
1871
        int sound_id;
1872
        for (sound_id = 44; sound_id < 57; ++sound_id) {
1873
                free_sound(sound_pointers[sound_id]);
1874
                sound_pointers[sound_id] = NULL;
1875
        }
1876
        */
1877
        // stub
1878
}
1879
 
1880
const byte tbl_snd_is_music[] = {
1881
                0,0,0,0,0,0,0,0,0,0, //9
1882
                0,0,0,0,0,0,0,0,0,0, //19
1883
                0,0,0,0,1,1,1,1,1,1, //29
1884
                1,0,1,1,1,1,1,1,0,1, //39
1885
                1,1,0,1,0,0,0,0,0,0, //49
1886
                1,0,1,1,1,1,1
1887
};
1888
 
1889
void reload_non_music_sounds() {
1890
        int i;
1891
        for (i = 0; i < COUNT(tbl_snd_is_music); ++i) {
1892
                if (!tbl_snd_is_music[i]) {
1893
                        free_sound(sound_pointers[i]);
1894
                        sound_pointers[i] = NULL;
1895
                }
1896
        }
1897
        load_sounds(0, 43);
1898
        load_opt_sounds(44, 56);
1899
}
1900
 
1901
// seg000:22BB
1902
void __pascal far free_optsnd_chtab() {
1903
        free_optional_sounds();
1904
        free_all_chtabs_from(id_chtab_3_princessinstory);
1905
}
1906
 
1907
// seg000:22C8
1908
void __pascal far load_title_images(int bgcolor) {
1909
        dat_type* dathandle;
1910
        dathandle = open_dat("TITLE.DAT", 0);
1911
        chtab_title40 = load_sprites_from_file(40, 1<<11, 1);
1912
        chtab_title50 = load_sprites_from_file(50, 1<<12, 1);
1913
        close_dat(dathandle);
1914
        if (graphics_mode == gmMcgaVga) {
1915
                // background of text frame
1916
                SDL_Color color;
1917
                if (bgcolor) {
1918
                        // RGB(4,0,18h) = #100060 = dark blue
1919
                        set_pal((find_first_pal_row(1<<11) << 4) + 14, 0x04, 0x00, 0x18, 1);
1920
                        color.r = 0x10;
1921
                        color.g = 0x00;
1922
                        color.b = 0x60;
1923
                        color.a = 0xFF;
1924
                } else {
1925
                        // RGB(20h,0,0) = #800000 = dark red
1926
                        set_pal((find_first_pal_row(1<<11) << 4) + 14, 0x20, 0x00, 0x00, 1);
1927
                        color.r = 0x80;
1928
                        color.g = 0x00;
1929
                        color.b = 0x00;
1930
                        color.a = 0xFF;
1931
                }
1932
                if (NULL != chtab_title40) {
1933
                        SDL_SetPaletteColors(chtab_title40->images[0]->format->palette, &color, 14, 1);
1934
                }
1935
        } else if (graphics_mode == gmEga || graphics_mode == gmTga) {
1936
                // ...
1937
        }
1938
}
1939
 
1940
#ifdef USE_COPYPROT
1941
// data:017A
1942
const word copyprot_word[] = {9, 1, 6, 4, 5, 3, 6, 3, 4, 4, 3, 2,12, 5,13, 1, 9, 2, 2, 4, 9, 4,11, 8, 5, 4, 1, 6, 2, 4, 6, 8, 4, 2, 7,11, 5, 4, 1, 2};
1943
// data:012A
1944
const word copyprot_line[] = {2, 1, 5, 4, 3, 5, 1, 3, 7, 2, 2, 4, 6, 6, 2, 6, 3, 1, 2, 3, 2, 2, 3,10, 5, 6, 5, 6, 3, 5, 7, 2, 2, 4, 5, 7, 2, 6, 5, 5};
1945
// data:00DA
1946
const word copyprot_page[] = {5, 3, 7, 3, 3, 4, 1, 5,12, 5,11,10, 1, 2, 8, 8, 2, 4, 6, 1, 4, 7, 3, 2, 1, 7,10, 1, 4, 3, 4, 1, 4, 1, 8, 1, 1,10, 3, 3};
1947
#endif
1948
 
1949
// seg000:23F4
1950
void __pascal far show_copyprot(int where) {
1951
#ifdef USE_COPYPROT
1952
        char sprintf_temp[140];
1953
        if (current_level != 15) return;
1954
        if (where) {
1955
                if (text_time_remaining || is_cutscene) return;
1956
                text_time_total = 1188;
1957
                text_time_remaining = 1188;
1958
                is_show_time = 0;
1959
                snprintf(sprintf_temp, sizeof(sprintf_temp),
1960
                        "WORD %d LINE %d PAGE %d",
1961
                        copyprot_word[copyprot_idx], copyprot_line[copyprot_idx], copyprot_page[copyprot_idx]);
1962
                display_text_bottom(sprintf_temp);
1963
        } else {
1964
                snprintf(sprintf_temp, sizeof(sprintf_temp),
1965
                        "Drink potion matching the first letter of Word %d on Line %d\n"
1966
                        "of Page %d of the manual.",
1967
                        copyprot_word[copyprot_idx], copyprot_line[copyprot_idx], copyprot_page[copyprot_idx]);
1968
                show_dialog(sprintf_temp);
1969
        }
1970
#endif
1971
}
1972
 
1973
// seg000:2489
1974
void __pascal far show_loading() {
1975
        show_text(&screen_rect, 0, 0, "Loading. . . .");
1976
}
1977
 
1978
// data:42C4
1979
word which_quote;
1980
 
1981
char const * const tbl_quotes[2] = {
1982
"\"(****/****) Incredibly realistic. . . The "
1983
"adventurer character actually looks human as he "
1984
"runs, jumps, climbs, and hangs from ledges.\"\n"
1985
"\n"
1986
"                                  Computer Entertainer\n"
1987
"\n"
1988
"\n"
1989
"\n"
1990
"\n"
1991
"\"A tremendous achievement. . . Mechner has crafted "
1992
"the smoothest animation ever seen in a game of this "
1993
"type.\n"
1994
"\n"
1995
"\"PRINCE OF PERSIA is the STAR WARS of its field.\"\n"
1996
"\n"
1997
"                                  Computer Gaming World",
1998
"\"An unmitigated delight. . . comes as close to "
1999
"(perfection) as any arcade game has come in a long, "
2000
"long time. . . what makes this game so wonderful (am "
2001
"I gushing?) is that the little onscreen character "
2002
"does not move like a little onscreen character -- he "
2003
"moves like a person.\"\n"
2004
"\n"
2005
"                                      Nibble"
2006
};
2007
 
2008
// seg000:249D
2009
void __pascal far show_quotes() {
2010
        //start_timer(timer_0,0);
2011
        //remove_timer(timer_0);
2012
        if (demo_mode && need_quotes) {
2013
                draw_rect(&screen_rect, 0);
2014
                show_text(&screen_rect, -1, 0, tbl_quotes[which_quote]);
2015
                which_quote = !which_quote;
2016
                start_timer(timer_0,0x384);
2017
        }
2018
        need_quotes = 0;
2019
}
2020
 
2021
const rect_type splash_text_1_rect = {0, 0, 50, 320};
2022
const rect_type splash_text_2_rect = {50, 0, 200, 320};
2023
 
2024
const char* splash_text_1 = "SDLPoP " SDLPOP_VERSION;
2025
const char* splash_text_2 =
2026
                "To quick save/load, press F6/F9 in-game.\n"
2027
                "\n"
2028
#ifdef USE_REPLAY
2029
                "To record replays, press Ctrl+Tab in-game.\n"
2030
                "To view replays, press Tab on the title screen.\n"
2031
                "\n"
2032
#endif
2033
                "Edit game.ini to customize SDLPoP.\n"
2034
                "Mods also work with SDLPoP.\n"
2035
                "\n"
2036
                "For more information, read doc/Readme.txt.\n"
2037
                "Questions? Visit http://forum.princed.org\n"
2038
                "\n"
2039
                "Press any key to continue...";
2040
 
2041
void show_splash() {
2042
        if (!enable_info_screen || start_level >= 0) return;
2043
        screen_updates_suspended = 0;
2044
        current_target_surface = onscreen_surface_;
2045
        draw_rect(&screen_rect, 0);
2046
        show_text_with_color(&splash_text_1_rect, 0, 0, splash_text_1, color_15_brightwhite);
2047
        show_text_with_color(&splash_text_2_rect, 0, -1, splash_text_2, color_7_lightgray);
2048
 
2049
        int key = 0;
2050
        do {
2051
                idle();
2052
                key = key_test_quit();
2053
 
2054
                if (joy_hat_states[0] != 0 || joy_X_button_state != 0 || joy_AY_buttons_state != 0 || joy_B_button_state != 0) {
2055
                        joy_hat_states[0] = 0;
2056
                        joy_AY_buttons_state = 0;
2057
                        joy_X_button_state = 0;
2058
                        joy_B_button_state = 0;
2059
                        key_states[SDL_SCANCODE_LSHIFT] = 1; // close the splash screen using the gamepad
2060
                }
2061
 
2062
        } while(key == 0 && !(key_states[SDL_SCANCODE_LSHIFT] || key_states[SDL_SCANCODE_RSHIFT]));
2063
 
2064
        if ((key & WITH_CTRL) || (enable_quicksave && key == SDL_SCANCODE_F9) || (enable_replay && key == SDL_SCANCODE_TAB)) {
2065
                extern int last_key_scancode; // defined in seg009.c
2066
                last_key_scancode = key; // can immediately do Ctrl+L, etc from the splash screen
2067
        }
2068
        key_states[SDL_SCANCODE_LSHIFT] = 0; // don't immediately start the game if shift was pressed!
2069
        key_states[SDL_SCANCODE_RSHIFT] = 0;
2070
}