Subversion Repositories Games.Prince of Persia

Rev

Blame | Last modification | View Log | Download | RSS feed

  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. }
  2071.