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.  
  23. // data:3D1A
  24. sbyte distance_mirror;
  25.  
  26. // seg003:0000
  27. void __pascal far init_game(int level) {
  28.         if(offscreen_surface) {
  29.                 free_surface(offscreen_surface); // missing in original
  30.                 offscreen_surface = NULL;
  31.         }
  32.         offscreen_surface = make_offscreen_buffer(&rect_top);
  33.         load_kid_sprite();
  34.         text_time_remaining = 0;
  35.         text_time_total = 0;
  36.         is_show_time = 0;
  37.         checkpoint = 0;
  38.         upside_down = 0; // N.B. upside_down is also reset in set_start_pos()
  39.         resurrect_time = 0;
  40.         if (!dont_reset_time) {
  41.                 rem_min = start_minutes_left;   // 60
  42.                 rem_tick = start_ticks_left;    // 719
  43.                 hitp_beg_lev = start_hitp;      // 3
  44.         }
  45.         need_level1_music = (level == 1);
  46.         play_level(level);
  47. }
  48.  
  49. // seg003:005C
  50. void __pascal far play_level(int level_number) {
  51.         cutscene_ptr_type cutscene_func;
  52. #ifdef USE_COPYPROT
  53.         if (enable_copyprot && level_number == copyprot_level) {
  54.                 level_number = 15;
  55.         }
  56. #endif
  57.         for (;;) {
  58.                 if (demo_mode && level_number > 2) {
  59.                         start_level = -1;
  60.                         need_quotes = 1;
  61.                         start_game();
  62.                 }
  63.                 if (level_number != current_level) {
  64.                         if (level_number <0 || level_number >15) {
  65.                                 printf("Tried to load cutscene for level %d, not in 0..15\n", level_number);
  66.                                 quit(1);
  67.                         }
  68.                         cutscene_func = tbl_cutscenes[level_number];
  69.                         if (cutscene_func != NULL
  70.  
  71.                                 #ifdef USE_REPLAY
  72.                                 && !(recording || replaying)
  73.                                 #endif
  74. #ifdef USE_SCREENSHOT
  75.                                 && !want_auto_screenshot()
  76. #endif
  77.                                         ) {
  78.                                 load_intro(level_number > 2, cutscene_func, 1);
  79.                         }
  80.                 }
  81.                 if (level_number != current_level) {
  82.                         load_lev_spr(level_number);
  83.                 }
  84.                 load_level();
  85.                 pos_guards();
  86.                 clear_coll_rooms();
  87.                 clear_saved_ctrl();
  88.                 drawn_room = 0;
  89.                 mobs_count = 0;
  90.                 trobs_count = 0;
  91.                 next_sound = -1;
  92.                 holding_sword = 0;
  93.                 grab_timer = 0;
  94.                 can_guard_see_kid = 0;
  95.                 united_with_shadow = 0;
  96.                 flash_time = 0;
  97.                 leveldoor_open = 0;
  98.                 demo_index = 0;
  99.                 demo_time = 0;
  100.                 guardhp_curr = 0;
  101.                 hitp_delta = 0;
  102.                 Guard.charid = charid_2_guard;
  103.                 Guard.direction = dir_56_none;
  104.                 do_startpos();
  105.                 have_sword = (level_number != 1);
  106.                 find_start_level_door();
  107.                 // busy waiting?
  108.                 while (check_sound_playing() && !do_paused()) idle();
  109.                 stop_sounds();
  110.                 #ifdef USE_REPLAY
  111.                 if (replaying) replay_restore_level();
  112.                 if (skipping_replay) {
  113.                         if (replay_seek_target == replay_seek_0_next_room ||
  114.                                 replay_seek_target == replay_seek_1_next_level
  115.                         )
  116.                                 skipping_replay = 0; // resume replay from here
  117.                 }
  118.                 #endif
  119.                 draw_level_first();
  120.                 show_copyprot(0);
  121.                 level_number = play_level_2();
  122.                 // hacked...
  123. #ifdef USE_COPYPROT
  124.                 if (enable_copyprot && level_number == copyprot_level && !demo_mode) {
  125.                         level_number = 15;
  126.                 } else {
  127.                         if (level_number == 16) {
  128.                                 level_number = copyprot_level;
  129.                                 copyprot_level = -1;
  130.                         }
  131.                 }
  132. #endif
  133.                 free_peels();
  134.         }
  135. }
  136.  
  137. // seg003:01A3
  138. void __pascal far do_startpos() {
  139.         word x;
  140.         // Special event: start at checkpoint
  141.         if (current_level == 3 && checkpoint) {
  142.                 level.start_dir = dir_FF_left;
  143.                 level.start_room = 2;
  144.                 level.start_pos = 6;
  145.                 // Special event: remove loose floor
  146.                 get_tile(7, 4, 0);
  147.                 curr_room_tiles[curr_tilepos] = tiles_0_empty;
  148.         }
  149.         next_room = Char.room = level.start_room;
  150.         x = level.start_pos;
  151.         Char.curr_col = x % 10;
  152.         Char.curr_row = x / 10;
  153.         Char.x = x_bump[Char.curr_col + 5] + 14;
  154.         // Start in the opposite direction (and turn into the correct one).
  155.         Char.direction = ~ level.start_dir;
  156.         if (seamless == 0) {
  157.                 if (current_level != 0) {
  158.                         x = hitp_beg_lev;
  159.                 } else {
  160.                         // HP on demo level
  161.                         x = 4;
  162.                 }
  163.                 hitp_max = hitp_curr = x;
  164.         }
  165.         if (current_level == 1) {
  166.                 // Special event: press button + falling entry
  167.                 get_tile(5, 2, 0);
  168.                 trigger_button(0, 0, -1);
  169.                 seqtbl_offset_char(seq_7_fall); // fall
  170.         } else if (current_level == 13) {
  171.                 // Special event: running entry
  172.                 seqtbl_offset_char(seq_84_run); // run
  173.         } else {
  174.                 seqtbl_offset_char(seq_5_turn); // turn
  175.         }
  176.         set_start_pos();
  177. }
  178.  
  179. // seg003:028A
  180. void __pascal far set_start_pos() {
  181.         Char.y = y_land[Char.curr_row + 1];
  182.         Char.alive = -1;
  183.         Char.charid = charid_0_kid;
  184.         is_screaming = 0;
  185.         knock = 0;
  186.         upside_down = start_upside_down; // 0
  187.         is_feather_fall = 0;
  188.         Char.fall_y = 0;
  189.         Char.fall_x = 0;
  190.         offguard = 0;
  191.         Char.sword = sword_0_sheathed;
  192.         droppedout = 0;
  193.         play_seq();
  194.         if (current_level == 7 && Char.room == 17) {
  195.                 // Special event: level 7 falling entry
  196.                 // level 7, room 17: show room below
  197.                 goto_other_room(3);
  198.         }
  199.         savekid();
  200. }
  201.  
  202. // seg003:02E6
  203. void __pascal far find_start_level_door() {
  204.         short tilepos;
  205.         get_room_address(Kid.room);
  206.         for (tilepos = 0; tilepos < 30; ++tilepos) {
  207.                 if ((curr_room_tiles[tilepos] & 0x1F) == tiles_16_level_door_left) {
  208.                         start_level_door(Kid.room, tilepos);
  209.                 }
  210.         }
  211. }
  212.  
  213. // seg003:0326
  214. void __pascal far draw_level_first() {
  215.         screen_updates_suspended = 1;
  216.  
  217.         next_room = Kid.room;
  218.         check_the_end();
  219.         if (tbl_level_type[current_level]) {
  220.                 gen_palace_wall_colors();
  221.         }
  222.         draw_rect(&screen_rect, 0);
  223.         show_level();
  224.         redraw_screen(0);
  225.         draw_kid_hp(hitp_curr, hitp_max);
  226.  
  227.         screen_updates_suspended = 0;
  228.         request_screen_update();
  229.  
  230. #ifdef USE_SCREENSHOT
  231.         auto_screenshot();
  232. #endif
  233.  
  234.         // Busy waiting!
  235.         start_timer(timer_1, 5);
  236.         do_simple_wait(1);
  237. }
  238.  
  239. // seg003:037B
  240. void __pascal far redraw_screen(int drawing_different_room) {
  241.         //remove_flash();
  242.         if (drawing_different_room) {
  243.                 draw_rect(&rect_top, 0);
  244.         }
  245.  
  246.         different_room = 0;
  247.         if (is_blind_mode) {
  248.                 draw_rect(&rect_top, 0);
  249.         } else {
  250.                 if (curr_guard_color) {
  251.                         // Moved *before* drawings.
  252.                         set_chtab_palette(chtab_addrs[id_chtab_5_guard], &guard_palettes[0x30 * curr_guard_color - 0x30], 0x10);
  253.                 }
  254.                 need_drects = 0;
  255.                 redraw_room();
  256. #ifdef USE_LIGHTING
  257.         redraw_lighting();
  258. #endif
  259.                 if (is_keyboard_mode) {
  260.                         clear_kbd_buf();
  261.                 }
  262.                 is_blind_mode = 1;
  263.                 draw_tables();
  264.                 if (is_keyboard_mode) {
  265.                         clear_kbd_buf();
  266.                 }
  267. #ifdef USE_COPYPROT
  268.                 if (current_level == 15) {
  269.                         // letters on potions level
  270.                         current_target_surface = offscreen_surface;
  271.                         short var_2;
  272.                         for (var_2 = 0; var_2 < 14; ++var_2) {
  273.                                 if (copyprot_room[var_2] == drawn_room) {
  274.                                         set_curr_pos((copyprot_tile[var_2] % 10 << 5) + 24, copyprot_tile[var_2] / 10 * 63 + 38);
  275.                                         draw_text_character(copyprot_letter[cplevel_entr[var_2]]);
  276.                                 }
  277.                         }
  278.                         current_target_surface = onscreen_surface_;
  279.                 }
  280. #endif
  281.                 is_blind_mode = 0;
  282.                 memset_near(table_counts, 0, sizeof(table_counts));
  283.                 draw_moving();
  284.                 draw_tables();
  285.                 if (is_keyboard_mode) {
  286.                         clear_kbd_buf();
  287.                 }
  288.                 need_drects = 1;
  289.                 if (curr_guard_color) {
  290.                         //set_pal_arr(0x80, 0x10, &guard_palettes[0x30 * curr_guard_color - 0x30], 1);
  291.                 }
  292.                 if (upside_down) {
  293.                         flip_screen(offscreen_surface);
  294.                 }
  295.                 copy_screen_rect(&rect_top);
  296.                 if (upside_down) {
  297.                         flip_screen(offscreen_surface);
  298.                 }
  299.                 if (is_keyboard_mode) {
  300.                         clear_kbd_buf();
  301.                 }
  302.         }
  303.         exit_room_timer = 2;
  304.  
  305. }
  306.  
  307. // seg003:04F8
  308. // Returns a level number:
  309. // - The current level if it was restarted.
  310. // - The next level if the level was completed.
  311. int __pascal far play_level_2() {
  312.         while (1) { // main loop
  313. #ifdef USE_QUICKSAVE
  314.                 check_quick_op();
  315. #endif
  316.  
  317. #ifdef USE_REPLAY
  318.                 if (need_replay_cycle) replay_cycle();
  319. #endif
  320.  
  321.                 if (Kid.sword == sword_2_drawn) {
  322.                         // speed when fighting (smaller is faster)
  323.                         start_timer(timer_1, 6);
  324.                 } else {
  325.                         // speed when not fighting (smaller is faster)
  326.                         start_timer(timer_1, 5);
  327.                 }
  328.                 guardhp_delta = 0;
  329.                 hitp_delta = 0;
  330.                 timers();
  331.                 play_frame();
  332.  
  333. #ifdef USE_REPLAY
  334.                 // At the exact "end of level" frame, preserve the seed to ensure reproducibility,
  335.                 // regardless of how long the sound is still playing *after* this frame (torch animation modifies the seed!)
  336.                 if (keep_last_seed == 1) {
  337.                         preserved_seed = random_seed;
  338.                         keep_last_seed = -1; // disable repeat
  339.                 }
  340. #endif
  341.  
  342.                 if (is_restart_level) {
  343.                         is_restart_level = 0;
  344.                         return current_level;
  345.                 } else {
  346.                         if (next_level == current_level || check_sound_playing()) {
  347.                                 screen_updates_suspended = 1;
  348.                                 draw_game_frame();
  349.                                 if (flash_if_hurt()) {
  350.                                         screen_updates_suspended = 0;
  351.                                         request_screen_update(); // display the flash
  352.                                         delay_ticks(2);          // and add a short delay
  353.                                 }
  354.                                 screen_updates_suspended = 0;
  355.                                 remove_flash_if_hurt();
  356.  
  357.                                 #ifdef USE_DEBUG_CHEATS
  358.                                 if (debug_cheats_enabled && is_timer_displayed) {
  359.                                         char timer_text[16];
  360.                                         if (rem_min < 0) {
  361.                                                 snprintf(timer_text, 16, "%02d:%02d:%02d", -(rem_min + 1), (719 - rem_tick) / 12, (719 - rem_tick) % 12);
  362.                                         } else {
  363.                                                 snprintf(timer_text, 16, "%02d:%02d:%02d", rem_min - 1, rem_tick / 12, rem_tick % 12);
  364.                                         }
  365.                                         screen_updates_suspended = 1;
  366.                                         draw_rect(&timer_rect, color_0_black);
  367.                                         show_text(&timer_rect, -1, -1, timer_text);
  368.                                         screen_updates_suspended = 0;
  369.                                 }
  370.                                 #endif
  371.  
  372.                                 request_screen_update(); // request screen update manually
  373.                                 do_simple_wait(1);
  374.                         } else {
  375.                                 stop_sounds();
  376.                                 hitp_beg_lev = hitp_max;
  377.                                 checkpoint = 0;
  378.  
  379.                                 #ifdef USE_REPLAY
  380.                                 if (keep_last_seed == -1) {
  381.                                         random_seed = preserved_seed; // Ensure reproducibility in the next level.
  382.                                         keep_last_seed = 0;
  383.                                 }
  384.                                 #endif
  385.  
  386.                                 return next_level;
  387.                         }
  388.                 }
  389.         }
  390. }
  391.  
  392. // seg003:0576
  393. void __pascal far redraw_at_char() {
  394.         short x_top_row;
  395.         short tile_col;
  396.         short tile_row;
  397.         short x_col_left;
  398.         short x_col_right;
  399.         if (Char.sword >= sword_2_drawn) {
  400.                 // If char is holding sword, it makes redraw-area bigger.
  401.                 if (Char.direction >= dir_0_right) {
  402.                         if (++char_col_right > 9) char_col_right = 9;
  403.                         // char_col_right = MIN(char_col_right + 1, 9);
  404.                 } else {
  405.                         if (--char_col_left < 0) char_col_left = 0;
  406.                         // char_col_left = MAX(char_col_left - 1, 0);
  407.                 }
  408.         }
  409.         if (Char.charid == charid_0_kid) {
  410.                 x_top_row = MIN(char_top_row, prev_char_top_row);
  411.                 x_col_right = MAX(char_col_right, prev_char_col_right);
  412.                 x_col_left = MIN(char_col_left, prev_char_col_left);
  413.         } else {
  414.                 x_top_row = char_top_row;
  415.                 x_col_right = char_col_right;
  416.                 x_col_left = char_col_left;
  417.         }
  418.         for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row) {
  419.                 for (tile_col = x_col_left; tile_col <= x_col_right; ++tile_col) {
  420.                         set_redraw_fore(get_tilepos(tile_col, tile_row), 1);
  421.                 }
  422.         }
  423.         if (Char.charid == charid_0_kid) {
  424.                 prev_char_top_row = char_top_row;
  425.                 prev_char_col_right = char_col_right;
  426.                 prev_char_col_left = char_col_left;
  427.         }
  428. }
  429.  
  430. // seg003:0645
  431. void __pascal far redraw_at_char2() {
  432.         short char_action;
  433.         short char_frame;
  434.         void __pascal (* redraw_func)(short, byte);
  435.         char_action = Char.action;
  436.         char_frame = Char.frame;
  437.         redraw_func = &set_redraw2;
  438.         // frames 78..80: grab
  439.         if (char_frame < frame_78_jumphang || char_frame >= frame_80_jumphang) {
  440.                 // frames 135..149: climb up
  441.                 if (char_frame >= frame_137_climbing_3 && char_frame < frame_145_climbing_11) {
  442.                         redraw_func = &set_redraw_floor_overlay;
  443.                 } else {
  444.                         // frames 102..106: fall
  445.                         if (char_action != actions_2_hang_climb && char_action != actions_3_in_midair &&
  446.                                         char_action != actions_4_in_freefall && char_action != actions_6_hang_straight &&
  447.                                         (char_action != actions_5_bumped || char_frame < frame_102_start_fall_1 || char_frame > frame_106_fall)) {
  448.                                 return;
  449.                         }
  450.                 }
  451.         }
  452.         for (tile_col = char_col_right; tile_col >= char_col_left; --tile_col) {
  453.                 if (char_action != 2) {
  454.                         redraw_func(get_tilepos(tile_col, char_bottom_row), 1);
  455.                 }
  456.                 if (char_top_row != char_bottom_row) {
  457.                         redraw_func(get_tilepos(tile_col, char_top_row), 1);
  458.                 }
  459.         }
  460. }
  461.  
  462. // seg003:0706
  463. void __pascal far check_knock() {
  464.         if (knock) {
  465.                 do_knock(Char.room, Char.curr_row - (knock>0));
  466.                 knock = 0;
  467.         }
  468. }
  469.  
  470. // seg003:0735
  471. void __pascal far timers() {
  472.         if (united_with_shadow > 0) {
  473.                 --united_with_shadow;
  474.                 if (united_with_shadow == 0) {
  475.                         --united_with_shadow;
  476.                 }
  477.         }
  478.         if (guard_notice_timer > 0) {
  479.                 --guard_notice_timer;
  480.         }
  481.         if (resurrect_time > 0) {
  482.                 --resurrect_time;
  483.         }
  484.         if (is_feather_fall && !check_sound_playing()) {
  485. #ifdef USE_REPLAY
  486.                 if (recording) special_move = MOVE_EFFECT_END;
  487.                 if (!replaying) // during replays, feather effect gets cancelled in do_replay_move()
  488. #endif
  489.                 is_feather_fall = 0;
  490.         }
  491.         // Special event: mouse
  492.         if (current_level == 8 && Char.room == 16 && leveldoor_open) {
  493.                 ++leveldoor_open;
  494.                 // time before mouse comes: 150/12=12.5 seconds
  495.                 if (leveldoor_open == 150) {
  496.                         do_mouse();
  497.                 }
  498.         }
  499. }
  500.  
  501. // seg003:0798
  502. void __pascal far check_mirror() {
  503.         word clip_top;
  504.         if (jumped_through_mirror == -1) {
  505.                 jump_through_mirror();
  506.         } else {
  507.                 if (get_tile_at_char() == tiles_13_mirror) {
  508.                         loadkid();
  509.                         load_frame();
  510.                         check_mirror_image();
  511.                         if (distance_mirror >= 0) {
  512.                                 load_frame_to_obj();
  513.                                 reset_obj_clip();
  514.                                 clip_top = y_clip[Char.curr_row + 1];
  515.                                 if (clip_top < obj_y) {
  516.                                         obj_clip_top = clip_top;
  517.                                         obj_clip_left = (Char.curr_col << 5) + 9;
  518.                                         add_objtable(4); // mirror image
  519.                                 }
  520.                         }
  521.                 }
  522.         }
  523. }
  524.  
  525. // seg003:080A
  526. void __pascal far jump_through_mirror() {
  527.         loadkid();
  528.         load_frame();
  529.         check_mirror_image();
  530.         jumped_through_mirror = 0;
  531.         Char.charid = charid_1_shadow;
  532.         play_sound(sound_45_jump_through_mirror); // jump through mirror
  533.         saveshad();
  534.         guardhp_max = guardhp_curr = hitp_max;
  535.         hitp_curr = 1;
  536.         draw_kid_hp(1, hitp_max);
  537.         draw_guard_hp(guardhp_curr, guardhp_max);
  538. }
  539.  
  540. // seg003:085B
  541. void __pascal far check_mirror_image() {
  542.         short distance;
  543.         short xpos;
  544.         xpos = x_bump[Char.curr_col + 5] + 10;
  545.         distance = distance_to_edge_weight();
  546.         if (Char.direction >= dir_0_right) {
  547.                 distance = (~distance) + 14;
  548.         }
  549.         distance_mirror = distance - 2;
  550.         Char.x = (xpos << 1) - Char.x;
  551.         Char.direction = ~Char.direction;
  552. }
  553.  
  554. // seg003:08AA
  555. void __pascal far bump_into_opponent() {
  556.         // This is called from play_kid_frame, so char=Kid, Opp=Guard
  557.         short distance;
  558.         if (can_guard_see_kid >= 2 &&
  559.                 Char.sword == sword_0_sheathed && // Kid must not be in fighting pose
  560.                 Opp.sword != sword_0_sheathed && // but Guard must
  561.                 Opp.action < 2 &&
  562.                 Char.direction != Opp.direction // must be facing toward each other
  563.         ) {
  564.                 distance = char_opp_dist();
  565.                 if (ABS(distance) <= 15) {
  566.  
  567.                         #ifdef FIX_PAINLESS_FALL_ON_GUARD
  568.                         if (fix_painless_fall_on_guard) {
  569.                                 if (Char.fall_y >= 33) return; // don't bump; dead
  570.                                 else if (Char.fall_y >= 22) { // medium land
  571.                                         take_hp(1);
  572.                                         play_sound(sound_16_medium_land);
  573.                                 }
  574.                         }
  575.                         #endif
  576.  
  577.                         Char.y = y_land[Char.curr_row + 1];
  578.                         Char.fall_y = 0;
  579.                         seqtbl_offset_char(seq_47_bump); // bump into opponent
  580.                         play_seq();
  581.                 }
  582.         }
  583. }
  584.  
  585. // seg003:0913
  586. void __pascal far pos_guards() {
  587.         short guard_tile;
  588.         short room1;
  589.         for (room1 = 0; room1 < 24; ++room1) {
  590.                 guard_tile = level.guards_tile[room1];
  591.                 if (guard_tile < 30) {
  592.                         level.guards_x[room1] = x_bump[guard_tile % 10 + 5] + 14;
  593.                         level.guards_seq_hi[room1] = 0;
  594.                 }
  595.         }
  596. }
  597.  
  598. // seg003:0959
  599. void __pascal far check_can_guard_see_kid() {
  600. /*
  601. Possible results in can_guard_see_kid:
  602. 0: Guard can't see Kid
  603. 1: Guard can see Kid, but won't come
  604. 2: Guard can see Kid, and will come
  605. */
  606.         short kid_frame;
  607.         short left_pos;
  608.         short temp;
  609.         short right_pos;
  610.         kid_frame = Kid.frame;
  611.         if (Guard.charid == charid_24_mouse) {
  612.                 return;
  613.         }
  614.         if ((Guard.charid != charid_1_shadow || current_level == 12) &&
  615.                 // frames 217..228: going up on stairs
  616.                 kid_frame != 0 && (kid_frame < frame_219_exit_stairs_3 || kid_frame >= 229) &&
  617.                 Guard.direction != dir_56_none && Kid.alive < 0 && Guard.alive < 0 && Kid.room == Guard.room && Kid.curr_row == Guard.curr_row
  618.         ) {
  619.                 can_guard_see_kid = 2;
  620.                 left_pos = x_bump[Kid.curr_col + 5] + 7;
  621.                 right_pos = x_bump[Guard.curr_col + 5] + 7;
  622.                 if (left_pos > right_pos) {
  623.                         temp = left_pos;
  624.                         left_pos = right_pos;
  625.                         right_pos = temp;
  626.                 }
  627.                 // A chomper is on the left side of a tile, so it doesn't count.
  628.                 if (get_tile_at_kid(left_pos) == tiles_18_chomper) {
  629.                         left_pos += 14;
  630.                 }
  631.                 // A gate is on the right side of a tile, so it doesn't count.
  632.                 if (get_tile_at_kid(right_pos) == tiles_4_gate) {
  633.                         right_pos -= 14;
  634.                 }
  635.                 if (right_pos >= left_pos) {
  636.                         while (left_pos <= right_pos) {
  637.                                 // Can't see through these tiles.
  638.                                 if (get_tile_at_kid(left_pos) == tiles_20_wall ||
  639.                                         curr_tile2 == tiles_7_doortop_with_floor ||
  640.                                         curr_tile2 == tiles_12_doortop
  641.                                 ) {
  642.                                         can_guard_see_kid = 0; return;
  643.                                 }
  644.                                 // Can see through these, but won't go through them.
  645.                                 if (curr_tile2 == tiles_11_loose ||
  646.                                         curr_tile2 == tiles_18_chomper ||
  647.                                         (curr_tile2 == tiles_4_gate && curr_room_modif[curr_tilepos] < 112) ||
  648.                                         !tile_is_floor(curr_tile2)
  649.                                 ) {
  650.                                         can_guard_see_kid = 1;
  651.                                 }
  652.                                 left_pos += 14;
  653.                         }
  654.                 }
  655.         } else {
  656.                 can_guard_see_kid = 0;
  657.         }
  658. }
  659.  
  660. // seg003:0A99
  661. byte __pascal far get_tile_at_kid(int xpos) {
  662.         return get_tile(Kid.room, get_tile_div_mod_m7(xpos), Kid.curr_row);
  663. }
  664.  
  665. // seg003:0ABA
  666. void __pascal far do_mouse() {
  667.         loadkid();
  668.         Char.charid = charid_24_mouse;
  669.         Char.x = 200;
  670.         Char.curr_row = 0;
  671.         Char.y = y_land[Char.curr_row + 1];
  672.         Char.alive = -1;
  673.         Char.direction = dir_FF_left;
  674.         guardhp_curr = 1;
  675.         seqtbl_offset_char(seq_105_mouse_forward); // mouse forward
  676.         play_seq();
  677.         saveshad();
  678. }
  679.  
  680. // seg003:0AFC
  681. int __pascal far flash_if_hurt() {
  682.         if (flash_time != 0) {
  683.                 do_flash_no_delay(flash_color); // don't add delay to the flash
  684.                 return 1;
  685.         } else if (hitp_delta < 0) {
  686.                 if (is_joyst_mode && enable_controller_rumble && sdl_haptic != NULL) {
  687.                         SDL_HapticRumblePlay(sdl_haptic, 1.0, 100); // rumble at full strength for 100 milliseconds
  688.                 }
  689.                 do_flash_no_delay(color_12_brightred); // red
  690.                 return 1;
  691.         }
  692.         return 0; // not flashed
  693. }
  694.  
  695. // seg003:0B1A
  696. void __pascal far remove_flash_if_hurt() {
  697.         if (flash_time != 0) {
  698.                 --flash_time;
  699.         } else {
  700.                 if (hitp_delta >= 0) return;
  701.         }
  702.         remove_flash();
  703. }
  704.