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:0E32
  24. const word strikeprob  [] = { 61,100, 61, 61, 61, 40,100,220,  0, 48, 32, 48};
  25. // data:0E4A
  26. const word restrikeprob[] = {  0,  0,  0,  5,  5,175, 16,  8,  0,255,255,150};
  27. // data:0E62
  28. const word blockprob   [] = {  0,150,150,200,200,255,200,250,  0,255,255,255};
  29. // data:0E7A
  30. const word impblockprob[] = {  0, 61, 61,100,100,145,100,250,  0,145,255,175};
  31. // data:0E92
  32. const word advprob     [] = {255,200,200,200,255,255,200,  0,  0,255,100,100};
  33. // data:0EAA
  34. const word refractimer [] = { 16, 16, 16, 16,  8,  8,  8,  8,  0,  8,  0,  0};
  35. // data:0EC2
  36. const word extrastrength[] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
  37.  
  38. // seg002:0000
  39. void __pascal far do_init_shad(const byte *source,int seq_index) {
  40.         memcpy_near(&Char, source, 7);
  41.         seqtbl_offset_char(seq_index);
  42.         Char.charid = charid_1_shadow;
  43.         demo_time = 0;
  44.         guard_skill = 3;
  45.         guardhp_delta = guardhp_curr = guardhp_max = 4;
  46.         saveshad();
  47. }
  48.  
  49. // seg002:0044
  50. void __pascal far get_guard_hp() {
  51.         guardhp_delta = guardhp_curr = guardhp_max = extrastrength[guard_skill] + tbl_guard_hp[current_level];
  52. }
  53.  
  54. // data:0EEA
  55. const byte init_shad_6[] = {0x0F, 0x51, 0x76, 0, 0, 1, 0, 0};
  56. // data:0EF2
  57. const byte init_shad_5[] = {0x0F, 0x37, 0x37, 0, 0xFF, 0, 0, 0};
  58. // data:0EFA
  59. const byte init_shad_12[] = {0x0F, 0x51, 0xE8, 0, 0, 0, 0, 0};
  60.  
  61. // seg002:0064
  62. void __pascal far check_shadow() {
  63.         offguard = 0;
  64.         if (current_level == 12) {
  65.                 // Special event: level 12 shadow
  66.                 if (!united_with_shadow && drawn_room == 15) {
  67.                         Char.room = drawn_room;
  68.                         if (get_tile(15, 1, 0) == tiles_22_sword) {
  69.                                 return;
  70.                         }
  71.                         shadow_initialized = 0;
  72.                         do_init_shad(/*&*/init_shad_12, 7 /*fall*/);
  73.                         return;
  74.                 }
  75.         } else if (current_level == 6) {
  76.                 // Special event: level 6 shadow
  77.                 Char.room = drawn_room;
  78.                 if (Char.room == 1) {
  79.                         if (leveldoor_open != 0x4D) {
  80.                                 play_sound(sound_25_presentation); // presentation (level 6 shadow)
  81.                                 leveldoor_open = 0x4D;
  82.                         }
  83.                         do_init_shad(/*&*/init_shad_6, 2 /*stand*/);
  84.                         return;
  85.                 }
  86.         } else if (current_level == 5) {
  87.                 // Special event: level 5 shadow
  88.                 Char.room = drawn_room;
  89.                 if (Char.room == 24) {
  90.                         if (get_tile(24, 3, 0) != tiles_10_potion) {
  91.                                 return;
  92.                         }
  93.                         do_init_shad(/*&*/init_shad_5, 2 /*stand*/);
  94.                         return;
  95.                 }
  96.         }
  97.         enter_guard();
  98. }
  99.  
  100. // seg002:0112
  101. void __pascal far enter_guard() {
  102.         word room_minus_1;
  103.         word guard_tile;
  104.         word frame;
  105.         byte seq_hi;
  106.         // arrays are indexed 0..23 instead of 1..24
  107.         room_minus_1 = drawn_room - 1;
  108.         frame = Char.frame; // hm?
  109.         guard_tile = level.guards_tile[room_minus_1];
  110.  
  111. #ifndef FIX_OFFSCREEN_GUARDS_DISAPPEARING
  112.         if (guard_tile >= 30) return;
  113. #else
  114.         if (guard_tile >= 30) {
  115.                 if (!fix_offscreen_guards_disappearing) return;
  116.  
  117.                 // try to see if there are offscreen guards in the left and right rooms that might be visible from this room
  118.                 word left_guard_tile = 31;
  119.                 word right_guard_tile = 31;
  120.                 if (room_L > 0) left_guard_tile = level.guards_tile[room_L-1];
  121.                 if (room_R > 0) right_guard_tile = level.guards_tile[room_R-1];
  122.  
  123.                 int other_guard_x;
  124.                 sbyte other_guard_dir;
  125.                 int delta_x;
  126.                 int other_room_minus_1;
  127.                 if (right_guard_tile >= 0 && right_guard_tile < 30) {
  128.                         other_room_minus_1 = room_R - 1;
  129.                         other_guard_x = level.guards_x[other_room_minus_1];
  130.                         other_guard_dir = level.guards_dir[other_room_minus_1];
  131.                         // left edge of the guard matters
  132.                         if (other_guard_dir == dir_0_right) other_guard_x -= 9; // only retrieve a guard if they will be visible
  133.                         if (other_guard_dir == dir_FF_left) other_guard_x += 1; // getting these right was mostly trial and error
  134.                         // only retrieve offscreen guards
  135.                         if (!(other_guard_x < 58 + 4)) return;
  136.                         delta_x = 140; // guard leaves to the left
  137.                         guard_tile = right_guard_tile;
  138.                 }
  139.                 else if (left_guard_tile >= 0 && left_guard_tile < 30) {
  140.                         other_room_minus_1 = room_L - 1;
  141.                         other_guard_x = level.guards_x[other_room_minus_1];
  142.                         other_guard_dir = level.guards_dir[other_room_minus_1];
  143.                         // right edge of the guard matters
  144.                         if (other_guard_dir == dir_0_right) other_guard_x -= 9;
  145.                         if (other_guard_dir == dir_FF_left) other_guard_x += 1;
  146.                         // only retrieve offscreen guards
  147.                         if (!(other_guard_x > 190 - 4)) return;
  148.                         delta_x = -140; // guard leaves to the right
  149.                         guard_tile = left_guard_tile;
  150.                 }
  151.                 else return;
  152.  
  153.                 // retrieve guard from adjacent room
  154.                 level.guards_x[room_minus_1] = level.guards_x[other_room_minus_1] + delta_x;
  155.                 level.guards_color[room_minus_1] = level.guards_color[other_room_minus_1];
  156.                 level.guards_dir[room_minus_1] = level.guards_dir[other_room_minus_1];
  157.                 level.guards_seq_hi[room_minus_1] = level.guards_seq_hi[other_room_minus_1];
  158.                 level.guards_seq_lo[room_minus_1] = level.guards_seq_lo[other_room_minus_1];
  159.                 level.guards_skill[room_minus_1] = level.guards_skill[other_room_minus_1];
  160.  
  161.                 level.guards_tile[other_room_minus_1] = 0xFF;
  162.                 level.guards_seq_hi[other_room_minus_1] = 0;
  163.         }
  164. #endif
  165.  
  166.         Char.room = drawn_room;
  167.         Char.curr_row = guard_tile / 10;
  168.         Char.y = y_land[Char.curr_row + 1];
  169.         Char.x = level.guards_x[room_minus_1];
  170.         Char.curr_col = get_tile_div_mod_m7(Char.x);
  171.         Char.direction = level.guards_dir[room_minus_1];
  172.         // only regular guards have different colors (and only on VGA)
  173.         if (graphics_mode == gmMcgaVga && tbl_guard_type[current_level] == 0) {
  174.                 curr_guard_color = level.guards_color[room_minus_1];
  175.         } else {
  176.                 curr_guard_color = 0;
  177.         }
  178.  
  179.         #ifdef REMEMBER_GUARD_HP
  180.         int remembered_hp = (curr_guard_color & 0xF0) >> 4;
  181.         #endif
  182.         curr_guard_color &= 0x0F; // added; only least significant 4 bits are used for guard color
  183.  
  184.         // level 3 has skeletons with infinite lives
  185.         if (current_level == 3) {
  186.                 Char.charid = charid_4_skeleton;
  187.         } else {
  188.                 Char.charid = charid_2_guard;
  189.         }
  190.         seq_hi = level.guards_seq_hi[room_minus_1];
  191.         if (seq_hi == 0) {
  192.                 if (Char.charid == charid_4_skeleton) {
  193.                         Char.sword = sword_2_drawn;
  194.                         seqtbl_offset_char(seq_63_guard_stand_active); // stand active (when entering room) (skeleton)
  195.                 } else {
  196.                         Char.sword = sword_0_sheathed;
  197.                         seqtbl_offset_char(seq_77_guard_stand_inactive); // stand inactive (when entering room)
  198.                 }
  199.         } else {
  200.                 Char.curr_seq = level.guards_seq_lo[room_minus_1] + (seq_hi << 8);
  201.         }
  202.         play_seq();
  203.         guard_skill = level.guards_skill[room_minus_1];
  204.         if (guard_skill >= 12) {
  205.                 guard_skill = 3;
  206.         }
  207.         frame = Char.frame;
  208.         if (frame == frame_185_dead || frame == frame_177_spiked || frame == frame_178_chomped) {
  209.                 Char.alive = 1;
  210.                 draw_guard_hp(0, guardhp_curr);
  211.                 guardhp_curr = 0;
  212.         } else {
  213.                 Char.alive = -1;
  214.                 justblocked = 0;
  215.                 guard_refrac = 0;
  216.                 is_guard_notice = 0;
  217.                 get_guard_hp();
  218.                 #ifdef REMEMBER_GUARD_HP
  219.                 if (enable_remember_guard_hp && remembered_hp > 0)
  220.                         guardhp_delta = guardhp_curr = (word) remembered_hp;
  221.                 #endif
  222.         }
  223.         Char.fall_y = 0;
  224.         Char.fall_x = 0;
  225.         Char.action = actions_1_run_jump;
  226.         saveshad();
  227. }
  228.  
  229. // seg002:0269
  230. void __pascal far check_guard_fallout() {
  231.         if (Guard.direction == dir_56_none || Guard.y < 211) {
  232.                 return;
  233.         }
  234.         if (Guard.charid == charid_1_shadow) {
  235.                 if (Guard.action != actions_4_in_freefall) {
  236.                         return;
  237.                 }
  238.                 loadshad();
  239.                 clear_char();
  240.                 saveshad();
  241.         } else if (Guard.charid == charid_4_skeleton &&
  242.                 (Guard.room = level.roomlinks[Guard.room - 1].down) == 3) {
  243.                 // if skeleton falls down into room 3
  244.                 Guard.x = 133;
  245.                 Guard.curr_row = 1;
  246.                 Guard.direction = dir_0_right;
  247.                 Guard.alive = -1;
  248.                 leave_guard();
  249.         } else {
  250.                 on_guard_killed();
  251.                 level.guards_tile[drawn_room - 1] = -1;
  252.                 Guard.direction = dir_56_none;
  253.                 draw_guard_hp(0, guardhp_curr);
  254.                 guardhp_curr = 0;
  255.         }
  256. }
  257.  
  258. // seg002:02F5
  259. void __pascal far leave_guard() {
  260.         word room_minus_1;
  261.         if (Guard.direction == dir_56_none || Guard.charid == charid_1_shadow || Guard.charid == charid_24_mouse) {
  262.                 return;
  263.         }
  264.         // arrays are indexed 0..23 instead of 1..24
  265.         room_minus_1 = Guard.room - 1;
  266.         level.guards_tile[room_minus_1] = get_tilepos(0, Guard.curr_row);
  267.  
  268.         level.guards_color[room_minus_1] = curr_guard_color & 0x0F; // restriction to 4 bits added
  269. #ifdef REMEMBER_GUARD_HP
  270.         if (enable_remember_guard_hp && guardhp_curr < 16) // can remember 1..15 hp
  271.                 level.guards_color[room_minus_1] |= (guardhp_curr << 4);
  272. #endif
  273.  
  274.         level.guards_x[room_minus_1] = Guard.x;
  275.         level.guards_dir[room_minus_1] = Guard.direction;
  276.         level.guards_skill[room_minus_1] = guard_skill;
  277.         if (Guard.alive < 0) {
  278.                 level.guards_seq_hi[room_minus_1] = 0;
  279.         } else {
  280.                 level.guards_seq_lo[room_minus_1] = Guard.curr_seq;
  281.                 level.guards_seq_hi[room_minus_1] = Guard.curr_seq >> 8;
  282.         }
  283.         Guard.direction = dir_56_none;
  284.         draw_guard_hp(0, guardhp_curr);
  285.         guardhp_curr = 0;
  286. }
  287.  
  288. // seg002:039E
  289. void __pascal far follow_guard() {
  290.         level.guards_tile[Kid.room - 1] = 0xFF;
  291.         level.guards_tile[Guard.room - 1] = 0xFF;
  292.         loadshad();
  293.         goto_other_room(roomleave_result);
  294.         saveshad();
  295. }
  296.  
  297. // seg002:03C7
  298. void __pascal far exit_room() {
  299.         word leave;
  300.         word kid_room_m1;
  301.         leave = 0;
  302.         if (exit_room_timer != 0) {
  303.                 --exit_room_timer;
  304.                 return;
  305.         }
  306.         loadkid();
  307.         load_frame_to_obj();
  308.         set_char_collision();
  309.         roomleave_result = leave_room();
  310.         if (roomleave_result < 0) {
  311.                 return;
  312.         }
  313.         savekid();
  314.         next_room = Char.room;
  315.         if (Guard.direction == dir_56_none) return;
  316.         if (Guard.alive < 0 && Guard.sword == sword_2_drawn) {
  317.                 kid_room_m1 = Kid.room - 1;
  318.                 if (level.guards_tile[kid_room_m1] >= 30 ||
  319.                         level.guards_seq_hi[kid_room_m1] != 0
  320.                 ) {
  321.                         if (roomleave_result == 0) {
  322.                                 // left
  323.                                 if (Guard.x >= 91) leave = 1;
  324.                                 #ifdef FIX_GUARD_FOLLOWING_THROUGH_CLOSED_GATES
  325.                                 else if (fix_guard_following_through_closed_gates && can_guard_see_kid != 2 &&
  326.                                                 Kid.sword != sword_2_drawn) {
  327.                                         leave = 1;
  328.                                 }
  329.                                 #endif
  330.                         } else if (roomleave_result == 1) {
  331.                                 // right
  332.                                 if (Guard.x < 165) leave = 1;
  333.                                 #ifdef FIX_GUARD_FOLLOWING_THROUGH_CLOSED_GATES
  334.                                 else if (fix_guard_following_through_closed_gates && can_guard_see_kid != 2 &&
  335.                                                  Kid.sword != sword_2_drawn) {
  336.                                         leave = 1;
  337.                                 }
  338.                                 #endif
  339.                         } else if (roomleave_result == 2) {
  340.                                 // up
  341.                                 if (Guard.curr_row >= 0) leave = 1;
  342.                         } else {
  343.                                 // down
  344.                                 if (Guard.curr_row < 3) leave = 1;
  345.                         }
  346.                 } else {
  347.                         leave = 1;
  348.                 }
  349.         } else {
  350.                 leave = 1;
  351.         }
  352.         if (leave) {
  353.                 leave_guard();
  354.         } else {
  355.                 follow_guard();
  356.         }
  357. }
  358.  
  359. // seg002:0486
  360. int __pascal far goto_other_room(short direction) {
  361.         short opposite_dir;
  362.         Char.room = ((byte*)&level.roomlinks[Char.room - 1])[direction];
  363.         if (direction == 0) {
  364.                 // left
  365.                 Char.x += 140;
  366.                 opposite_dir = 1;
  367.         } else if (direction == 1) {
  368.                 // right
  369.                 Char.x -= 140;
  370.                 opposite_dir = 0;
  371.         } else if (direction == 2) {
  372.                 // up
  373.                 Char.y += 189;
  374.                 Char.curr_row = y_to_row_mod4(Char.y);
  375.                 opposite_dir = 3;
  376.         } else {
  377.                 // down
  378.                 Char.y -= 189;
  379.                 Char.curr_row = y_to_row_mod4(Char.y);
  380.                 opposite_dir = 2;
  381.         }
  382.         return opposite_dir;
  383. }
  384.  
  385. // seg002:0504
  386. short __pascal far leave_room() {
  387.         short frame;
  388.         word action;
  389.         short chary;
  390.         short leave_dir;
  391.         chary = Char.y;
  392.         action = Char.action;
  393.         frame = Char.frame;
  394.         if (action != actions_5_bumped &&
  395.                 action != actions_4_in_freefall &&
  396.                 action != actions_3_in_midair &&
  397.                 (sbyte)chary < 10 && (sbyte)chary > -16
  398.         ) {
  399.                 leave_dir = 2; // up
  400.         } else if (chary >= 211) {
  401.                 leave_dir = 3; // down
  402.         } else if (
  403.                 // frames 135..149: climb up
  404.                 (frame >= frame_135_climbing_1 && frame < 150) ||
  405.                 // frames 110..119: standing up from crouch
  406.                 (frame >= frame_110_stand_up_from_crouch_1 && frame < 120) ||
  407.                 // frames 150..162: with sword
  408.                 (frame >= frame_150_parry && frame < 163
  409.  
  410. #ifdef FIX_RETREAT_WITHOUT_LEAVING_ROOM
  411.                         // By repeatedly pressing 'back' in a swordfight, you can retreat out of a room without the room changing. (Trick 35)
  412.  
  413.                         // The game waits for a 'legal frame' (e.g. frame_170_stand_with_sword) until leaving is possible;
  414.                         // However, this frame is never reached if you press 'back' in the correct pattern!
  415.  
  416.                         // Solution: also allow the room to be changed on frame_157_walk_with_sword
  417.                         // Note that this means that the delay for leaving rooms in a swordfight becomes noticably shorter.
  418.  
  419.                         && (frame != frame_157_walk_with_sword || !fix_retreat_without_leaving_room)
  420. #endif
  421.  
  422.                 ) ||
  423.                 // frames 166..168: with sword
  424.                 (frame >= frame_166_stand_inactive && frame < 169) ||
  425.                 action == actions_7_turn // turn
  426.         ) {
  427.                 return -1;
  428.         } else if (Char.direction != dir_0_right) {
  429.                 // looking left
  430.                 if (char_x_left <= 54) {
  431.                         leave_dir = 0; // left
  432.                 } else if (char_x_left >= 198) {
  433.                         leave_dir = 1; // right
  434.                 } else {
  435.                         return -1;
  436.                 }
  437.         } else {
  438.                 // looking right
  439.                 get_tile(Char.room, 9, Char.curr_row);
  440.                 if (curr_tile2 != tiles_7_doortop_with_floor &&
  441.                         curr_tile2 != tiles_12_doortop &&
  442.                         char_x_right >= 201
  443.                 ) {
  444.                         leave_dir = 1; // right
  445.                 } else if (char_x_right <= 57) {
  446.                         leave_dir = 0; // left
  447.                 } else {
  448.                         return -1;
  449.                 }
  450.         }
  451.         switch (leave_dir) {
  452.                 case 0: // left
  453.                         play_mirr_mus();
  454.                         level3_set_chkp();
  455.                         Jaffar_exit();
  456.                 break;
  457.                 case 1: // right
  458.                         sword_disappears();
  459.                         meet_Jaffar();
  460.                 break;
  461.                 //case 2: // up
  462.                 case 3: // down
  463.                         // Special event: falling exit
  464.                         if (current_level == 6 && Char.room == 1) {
  465.                                 return -2;
  466.                         }
  467.                 break;
  468.         }
  469.         goto_other_room(leave_dir);
  470. #ifdef USE_REPLAY
  471.         if (skipping_replay && replay_seek_target == replay_seek_0_next_room) skipping_replay = 0;
  472. #endif
  473.         return leave_dir;
  474. }
  475.  
  476. // seg002:0643
  477. void __pascal far Jaffar_exit() {
  478.         if (leveldoor_open == 2) {
  479.                 get_tile(24, 0, 0);
  480.                 trigger_button(0, 0, -1);
  481.         }
  482. }
  483.  
  484. // seg002:0665
  485. void __pascal far level3_set_chkp() {
  486.         // Special event: set checkpoint
  487.         if (current_level == 3 && Char.room == 7) {
  488.                 checkpoint = 1;
  489.                 hitp_beg_lev = hitp_max;
  490.         }
  491. }
  492.  
  493. // seg002:0680
  494. void __pascal far sword_disappears() {
  495.         // Special event: sword disappears
  496.         if (current_level == 12 && Char.room == 18) {
  497.                 get_tile(15, 1, 0);
  498.                 curr_room_tiles[curr_tilepos] = tiles_1_floor;
  499.                 curr_room_modif[curr_tilepos] = 0; // added, a nonzero modifier may show fake tiles
  500.         }
  501. }
  502.  
  503. // seg002:06AE
  504. void __pascal far meet_Jaffar() {
  505.         // Special event: play music
  506.         if (current_level == 13 && leveldoor_open == 0 && Char.room == 3) {
  507.                 play_sound(sound_29_meet_Jaffar); // meet Jaffar
  508.                 // Special event: Jaffar waits a bit (28/12=2.33 seconds)
  509.                 guard_notice_timer = 28;
  510.         }
  511. }
  512.  
  513. // seg002:06D3
  514. void __pascal far play_mirr_mus() {
  515.         // Special event: mirror music
  516.         if (
  517.                 leveldoor_open != 0 &&
  518.                 leveldoor_open != 0x4D && // was the music played already?
  519.                 current_level == 4 &&
  520.                 Char.curr_row == 0 &&
  521.                 Char.room == 11
  522.         ) {
  523.                 play_sound(sound_25_presentation); // presentation (level 4 mirror)
  524.                 leveldoor_open = 0x4D;
  525.         }
  526. }
  527.  
  528. // seg002:0706
  529. void __pascal far move_0_nothing() {
  530.         control_shift = 0;
  531.         control_y = 0;
  532.         control_x = 0;
  533.         control_shift2 = 0;
  534.         control_down = 0;
  535.         control_up = 0;
  536.         control_backward = 0;
  537.         control_forward = 0;
  538. }
  539.  
  540. // seg002:0721
  541. void __pascal far move_1_forward() {
  542.         control_x = -1;
  543.         control_forward = -1;
  544. }
  545.  
  546. // seg002:072A
  547. void __pascal far move_2_backward() {
  548.         control_backward = -1;
  549.         control_x = 1;
  550. }
  551.  
  552. // seg002:0735
  553. void __pascal far move_3_up() {
  554.         control_y = -1;
  555.         control_up = -1;
  556. }
  557.  
  558. // seg002:073E
  559. void __pascal far move_4_down() {
  560.         control_down = -1;
  561.         control_y = 1;
  562. }
  563.  
  564. // seg002:0749
  565. void __pascal far move_up_back() {
  566.         control_up = -1;
  567.         move_2_backward();
  568. }
  569.  
  570. // seg002:0753
  571. void __pascal far move_down_back() {
  572.         control_down = -1;
  573.         move_2_backward();
  574. }
  575.  
  576. // seg002:075D
  577. void __pascal far move_down_forw() {
  578.         control_down = -1;
  579.         move_1_forward();
  580. }
  581.  
  582. // seg002:0767
  583. void __pascal far move_6_shift() {
  584.         control_shift = -1;
  585.         control_shift2 = -1;
  586. }
  587.  
  588. // seg002:0770
  589. void __pascal far move_7() {
  590.         control_shift = 0;
  591. }
  592.  
  593. // seg002:0776
  594. void __pascal far autocontrol_opponent() {
  595.         word charid;
  596.         move_0_nothing();
  597.         charid = Char.charid;
  598.         if (charid == charid_0_kid) {
  599.                 autocontrol_kid();
  600.         } else {
  601.                 if (justblocked) --justblocked;
  602.                 if (kid_sword_strike) --kid_sword_strike;
  603.                 if (guard_refrac) --guard_refrac;
  604.                 if (charid == charid_24_mouse) {
  605.                         autocontrol_mouse();
  606.                 } else if (charid == charid_4_skeleton) {
  607.                         autocontrol_skeleton();
  608.                 } else if (charid == charid_1_shadow) {
  609.                         autocontrol_shadow();
  610.                 } else if (current_level == 13) {
  611.                         autocontrol_Jaffar();
  612.                 } else {
  613.                         autocontrol_guard();
  614.                 }
  615.         }
  616. }
  617.  
  618. // seg002:07EB
  619. void __pascal far autocontrol_mouse() {
  620.         if (Char.direction == dir_56_none) {
  621.                 return;
  622.         }
  623.         if (Char.action == actions_0_stand) {
  624.                 if (Char.x >= 200) {
  625.                         clear_char();
  626.                 }
  627.         } else {
  628.                 if (Char.x < 166) {
  629.                         seqtbl_offset_char(seq_107_mouse_stand_up_and_go); // mouse
  630.                         play_seq();
  631.                 }
  632.         }
  633. }
  634.  
  635. // seg002:081D
  636. void __pascal far autocontrol_shadow() {
  637.         if (current_level == 4) {
  638.                 autocontrol_shadow_level4();
  639.         } else if (current_level == 5) {
  640.                 autocontrol_shadow_level5();
  641.         } else if (current_level == 6) {
  642.                 autocontrol_shadow_level6();
  643.         } else if (current_level == 12) {
  644.                 autocontrol_shadow_level12();
  645.         }
  646. }
  647.  
  648. // seg002:0850
  649. void __pascal far autocontrol_skeleton() {
  650.         Char.sword = sword_2_drawn;
  651.         autocontrol_guard();
  652. }
  653.  
  654. // seg002:085A
  655. void __pascal far autocontrol_Jaffar() {
  656.         autocontrol_guard();
  657. }
  658.  
  659. // seg002:085F
  660. void __pascal far autocontrol_kid() {
  661.         autocontrol_guard();
  662. }
  663.  
  664. // seg002:0864
  665. void __pascal far autocontrol_guard() {
  666.         if (Char.sword < sword_2_drawn) {
  667.                 autocontrol_guard_inactive();
  668.         } else {
  669.                 autocontrol_guard_active();
  670.         }
  671. }
  672.  
  673. // seg002:0876
  674. void __pascal far autocontrol_guard_inactive() {
  675.         short distance;
  676.         if (Kid.alive >= 0) return;
  677.         distance = char_opp_dist();
  678.         if (Opp.curr_row != Char.curr_row || (word)distance < (word)-8) {
  679.                 // If Kid made a sound ...
  680.                 if (is_guard_notice) {
  681.                         is_guard_notice = 0;
  682.                         if (distance < 0) {
  683.                                 // ... and Kid is behind Guard, Guard turns around.
  684.                                 if ((word)distance < (word)-4) {
  685.                                         move_4_down();
  686.                                 }
  687.                                 return;
  688.                         }
  689.                 } else if (distance < 0) {
  690.                         return;
  691.                 }
  692.         }
  693.         if (can_guard_see_kid) {
  694.                 // If Guard can see Kid, Guard moves to fighting pose.
  695.                 if (current_level != 13 || guard_notice_timer == 0) {
  696.                         move_down_forw();
  697.                 }
  698.         }
  699. }
  700.  
  701. // seg002:08DC
  702. void __pascal far autocontrol_guard_active() {
  703.         short opp_frame;
  704.         short char_frame;
  705.         short distance;
  706.         char_frame = Char.frame;
  707.         if (char_frame != frame_166_stand_inactive && char_frame >= 150 && can_guard_see_kid != 1) {
  708.                 if (can_guard_see_kid == 0) {
  709.                         if (droppedout != 0) {
  710.                                 guard_follows_kid_down();
  711.                                 //return;
  712.                         } else if (Char.charid != charid_4_skeleton) {
  713.                                 move_down_back();
  714.                         }
  715.                         //return;
  716.                 } else { // can_guard_see_kid == 2
  717.                         opp_frame = Opp.frame;
  718.                         distance = char_opp_dist();
  719.                         if (distance >= 12 &&
  720.                                 // frames 102..117: falling and landing
  721.                                 opp_frame >= frame_102_start_fall_1 && opp_frame < frame_118_stand_up_from_crouch_9 &&
  722.                                 Opp.action == actions_5_bumped
  723.                         ) {
  724.                                 return;
  725.                         }
  726.                         if (distance < 35) {
  727.                                 if ((Char.sword < sword_2_drawn && distance < 8) || distance < 12) {
  728.                                         if (Char.direction == Opp.direction) {
  729.                                                 // turn around
  730.                                                 move_2_backward();
  731.                                                 //return;
  732.                                         } else {
  733.                                                 move_1_forward();
  734.                                                 //return;
  735.                                         }
  736.                                 } else {
  737.                                         autocontrol_guard_kid_in_sight(distance);
  738.                                         //return;
  739.                                 }
  740.                         } else {
  741.                                 if (guard_refrac != 0) return;
  742.                                 if (Char.direction != Opp.direction) {
  743.                                         // frames 7..14: running
  744.                                         // frames 34..43: run-jump
  745.                                         if (opp_frame >= frame_7_run && opp_frame < 15) {
  746.                                                 if (distance < 40) move_6_shift();
  747.                                                 return;
  748.                                         } else if (opp_frame >= frame_34_start_run_jump_1 && opp_frame < 44) {
  749.                                                 if (distance < 50) move_6_shift();
  750.                                                 return;
  751.                                                 //return;
  752.                                         }
  753.                                 }
  754.                                 autocontrol_guard_kid_far();
  755.                         }
  756.                         //...
  757.                 }
  758.                 //...
  759.         }
  760. }
  761.  
  762. // seg002:09CB
  763. void __pascal far autocontrol_guard_kid_far() {
  764.         if (tile_is_floor(get_tile_infrontof_char()) ||
  765.                 tile_is_floor(get_tile_infrontof2_char())) {
  766.                 move_1_forward();
  767.         } else {
  768.                 move_2_backward();
  769.         }
  770. }
  771.  
  772. // seg002:09F8
  773. void __pascal far guard_follows_kid_down() {
  774.         // This is called from autocontrol_guard_active, so char=Guard, Opp=Kid
  775.         word opp_action;
  776.         opp_action = Opp.action;
  777.         if (opp_action == actions_2_hang_climb || opp_action == actions_6_hang_straight) {
  778.                 return;
  779.         }
  780.         if (// there is wall in front of Guard
  781.                 wall_type(get_tile_infrontof_char()) != 0 ||
  782.                 (! tile_is_floor(curr_tile2) && (
  783.                         (get_tile(curr_room, tile_col, ++tile_row) == tiles_2_spike ||
  784.                         // Guard would fall on loose floor
  785.                         curr_tile2 == tiles_11_loose ||
  786.                         // ... or wall (?)
  787.                         wall_type(curr_tile2) != 0 ||
  788.                         // ... or into a chasm
  789.                         ! tile_is_floor(curr_tile2)) ||
  790.                         // ... or Kid is not below
  791.                         Char.curr_row + 1 != Opp.curr_row
  792.                 ))
  793.         ) {
  794.                 // don't follow
  795.                 droppedout = 0;
  796.                 move_2_backward();
  797.         } else {
  798.                 // follow
  799.                 move_1_forward();
  800.         }
  801. }
  802.  
  803. // seg002:0A93
  804. void __pascal far autocontrol_guard_kid_in_sight(short distance) {
  805.         if (Opp.sword == sword_2_drawn) {
  806.                 autocontrol_guard_kid_armed(distance);
  807.         } else if (guard_refrac == 0) {
  808.                 if (distance < 29) {
  809.                         move_6_shift();
  810.                 } else {
  811.                         move_1_forward();
  812.                 }
  813.         }
  814. }
  815.  
  816. // seg002:0AC1
  817. void __pascal far autocontrol_guard_kid_armed(short distance) {
  818.         if (distance < 10 || distance >= 29) {
  819.                 guard_advance();
  820.         } else {
  821.                 guard_block();
  822.                 if (guard_refrac == 0) {
  823.                         if (distance < 12 || distance >= 29) {
  824.                                 guard_advance();
  825.                         } else {
  826.                                 guard_strike();
  827.                         }
  828.                 }
  829.         }
  830. }
  831.  
  832. // seg002:0AF5
  833. void __pascal far guard_advance() {
  834.         if (guard_skill == 0 || kid_sword_strike == 0) {
  835.                 if (advprob[guard_skill] > prandom(255)) {
  836.                         move_1_forward();
  837.                 }
  838.         }
  839. }
  840.  
  841. // seg002:0B1D
  842. void __pascal far guard_block() {
  843.         word opp_frame;
  844.         opp_frame = Opp.frame;
  845.         if (opp_frame == frame_152_strike_2 || opp_frame == frame_153_strike_3 || opp_frame == frame_162_block_to_strike) {
  846.                 if (justblocked != 0) {
  847.                         if (impblockprob[guard_skill] > prandom(255)) {
  848.                                 move_3_up();
  849.                         }
  850.                 } else {
  851.                         if (blockprob[guard_skill] > prandom(255)) {
  852.                                 move_3_up();
  853.                         }
  854.                 }
  855.         }
  856. }
  857.  
  858. // seg002:0B73
  859. void __pascal far guard_strike() {
  860.         word opp_frame;
  861.         word char_frame;
  862.         opp_frame = Opp.frame;
  863.         if (opp_frame == frame_169_begin_block || opp_frame == frame_151_strike_1) return;
  864.         char_frame = Char.frame;
  865.         if (char_frame == frame_161_parry || char_frame == frame_150_parry) {
  866.                 if (restrikeprob[guard_skill] > prandom(255)) {
  867.                         move_6_shift();
  868.                 }
  869.         } else {
  870.                 if (strikeprob[guard_skill] > prandom(255)) {
  871.                         move_6_shift();
  872.                 }
  873.         }
  874. }
  875.  
  876. // seg002:0BCD
  877. void __pascal far hurt_by_sword() {
  878.         short distance;
  879.         if (Char.alive >= 0) return;
  880.         if (Char.sword != sword_2_drawn) {
  881.                 // Being hurt when not in fighting pose means death.
  882.                 take_hp(100);
  883.                 seqtbl_offset_char(seq_85_stabbed_to_death); // dying (stabbed unarmed)
  884.                 loc_4276:
  885.                 if (get_tile_behind_char() != 0 ||
  886.                         (distance = distance_to_edge_weight()) < 4
  887.                 ) {
  888.                         seqtbl_offset_char(seq_85_stabbed_to_death); // dying (stabbed)
  889.                         if (Char.charid != charid_0_kid &&
  890.                                 Char.direction < dir_0_right && // looking left
  891.                                 (curr_tile2 == tiles_4_gate || get_tile_at_char() == tiles_4_gate)
  892.                         ) {
  893.                                 Char.x = x_bump[tile_col - (curr_tile2 != tiles_4_gate) + 5] + 7;
  894.                                 Char.x = char_dx_forward(10);
  895.                         }
  896.                         Char.y = y_land[Char.curr_row + 1];
  897.                         Char.fall_y = 0;
  898.                 } else {
  899.                         Char.x = char_dx_forward(distance - 20);
  900.                         load_fram_det_col();
  901.                         inc_curr_row();
  902.                         seqtbl_offset_char(seq_81_kid_pushed_off_ledge); // Kid/Guard is killed and pushed off the ledge
  903.                 }
  904.         } else {
  905.                 // You can't hurt skeletons
  906.                 if (Char.charid != charid_4_skeleton) {
  907.                         if (take_hp(1)) goto loc_4276;
  908.                 }
  909.                 seqtbl_offset_char(seq_74_hit_by_sword); // being hit with sword
  910.                 Char.y = y_land[Char.curr_row + 1];
  911.                 Char.fall_y = 0;
  912.         }
  913.         // sound 13: Kid hurt (by sword), sound 12: Guard hurt (by sword)
  914.         play_sound(Char.charid == charid_0_kid ? sound_13_kid_hurt : sound_12_guard_hurt);
  915.         play_seq();
  916. }
  917.  
  918. // seg002:0CD4
  919. void __pascal far check_sword_hurt() {
  920.         if (Guard.action == actions_99_hurt) {
  921.                 if (Kid.action == actions_99_hurt) {
  922.                         Kid.action = actions_1_run_jump;
  923.                 }
  924.                 loadshad();
  925.                 hurt_by_sword();
  926.                 saveshad();
  927.                 guard_refrac = refractimer[guard_skill];
  928.         } else {
  929.                 if (Kid.action == actions_99_hurt) {
  930.                         loadkid();
  931.                         hurt_by_sword();
  932.                         savekid();
  933.                 }
  934.         }
  935. }
  936.  
  937. // seg002:0D1A
  938. void __pascal far check_sword_hurting() {
  939.         short kid_frame;
  940.         kid_frame = Kid.frame;
  941.         // frames 217..228: go up on stairs
  942.         if (kid_frame != 0 && (kid_frame < frame_219_exit_stairs_3 || kid_frame >= 229)) {
  943.                 loadshad_and_opp();
  944.                 check_hurting();
  945.                 saveshad_and_opp();
  946.                 loadkid_and_opp();
  947.                 check_hurting();
  948.                 savekid_and_opp();
  949.         }
  950. }
  951.  
  952. // seg002:0D56
  953. void __pascal far check_hurting() {
  954.         short opp_frame, char_frame, distance, min_hurt_range;
  955.         if (Char.sword != sword_2_drawn) return;
  956.         if (Char.curr_row != Opp.curr_row) return;
  957.         char_frame = Char.frame;
  958.         // frames 153..154: poking with sword
  959.         if (char_frame != frame_153_strike_3 && char_frame != frame_154_poking) return;
  960.         // If char is poking ...
  961.         distance = char_opp_dist();
  962.         opp_frame = Opp.frame;
  963.         // frames 161 and 150: parrying
  964.         if (distance < 0 || distance >= 29 ||
  965.                 (opp_frame != frame_161_parry && opp_frame != frame_150_parry)
  966.         ) {
  967.                 // ... and Opp is not parrying
  968.                 // frame 154: poking
  969.                 if (Char.frame == frame_154_poking) {
  970.                         if (Opp.sword < sword_2_drawn) {
  971.                                 min_hurt_range = 8;
  972.                         } else {
  973.                                 min_hurt_range = 12;
  974.                         }
  975.                         distance = char_opp_dist();
  976.                         if (distance >= min_hurt_range && distance < 29) {
  977.                                 Opp.action = actions_99_hurt;
  978.                         }
  979.                 }
  980.         } else {
  981.                 Opp.frame = frame_161_parry;
  982.                 if (Char.charid != charid_0_kid) {
  983.                         justblocked = 4;
  984.                 }
  985.                 seqtbl_offset_char(seq_69_attack_was_parried); // attack was parried
  986.                 play_seq();
  987.         }
  988.         // frame 154: poking
  989.         // frame 161: parrying
  990.         if (Char.frame == frame_154_poking && Opp.frame != frame_161_parry && Opp.action != actions_99_hurt) {
  991.                 play_sound(sound_11_sword_moving); // sword moving
  992.         }
  993. }
  994.  
  995. // seg002:0E1F
  996. void __pascal far check_skel() {
  997.         // Special event: skeleton wakes
  998.         if (current_level == 3 &&
  999.                 Guard.direction == dir_56_none &&
  1000.                 drawn_room == 1 &&
  1001.                 leveldoor_open != 0 &&
  1002.                 (Kid.curr_col == 2 || Kid.curr_col == 3)
  1003.         ) {
  1004.                 get_tile(drawn_room, 5, 1);
  1005.                 if (curr_tile2 == tiles_21_skeleton) {
  1006.                         // erase skeleton
  1007.                         curr_room_tiles[curr_tilepos] = tiles_1_floor;
  1008.                         redraw_height = 24;
  1009.                         set_redraw_full(curr_tilepos, 1);
  1010.                         set_wipe(curr_tilepos, 1);
  1011.                         ++curr_tilepos;
  1012.                         set_redraw_full(curr_tilepos, 1);
  1013.                         set_wipe(curr_tilepos, 1);
  1014.                         Char.room = drawn_room;
  1015.                         Char.curr_row = 1;
  1016.                         Char.y = y_land[Char.curr_row + 1];
  1017.                         Char.curr_col = 5;
  1018.                         Char.x = x_bump[Char.curr_col + 5] + 14;
  1019.                         Char.direction = dir_FF_left;
  1020.                         seqtbl_offset_char(seq_88_skel_wake_up); // skel wake up
  1021.                         play_seq();
  1022.                         play_sound(sound_44_skel_alive); // skel alive
  1023.                         guard_skill = 2;
  1024.                         Char.alive = -1;
  1025.                         guardhp_max = guardhp_curr = 3;
  1026.                         Char.fall_x = Char.fall_y = 0;
  1027.                         is_guard_notice = guard_refrac = 0;
  1028.                         Char.sword = sword_2_drawn;
  1029.                         Char.charid = charid_4_skeleton;
  1030.                         saveshad();
  1031.                 }
  1032.         }
  1033. }
  1034.  
  1035. // seg002:0F3F
  1036. void __pascal far do_auto_moves(const auto_move_type *moves_ptr) {
  1037.         short demoindex;
  1038.         short curr_move;
  1039.         if (demo_time >= 0xFE) return;
  1040.         ++demo_time;
  1041.         demoindex = demo_index;
  1042.         if (moves_ptr[demoindex].time <= demo_time) {
  1043.                 ++demo_index;
  1044.         } else {
  1045.                 demoindex = demo_index - 1;
  1046.         }
  1047.         curr_move = moves_ptr[demoindex].move;
  1048.         switch (curr_move) {
  1049.                 case -1:
  1050.                 break;
  1051.                 case 0:
  1052.                         move_0_nothing();
  1053.                 break;
  1054.                 case 1:
  1055.                         move_1_forward();
  1056.                 break;
  1057.                 case 2:
  1058.                         move_2_backward();
  1059.                 break;
  1060.                 case 3:
  1061.                         move_3_up();
  1062.                 break;
  1063.                 case 4:
  1064.                         move_4_down();
  1065.                 break;
  1066.                 case 5:
  1067.                         move_3_up();
  1068.                         move_1_forward();
  1069.                 break;
  1070.                 case 6:
  1071.                         move_6_shift();
  1072.                 break;
  1073.                 case 7:
  1074.                         move_7();
  1075.                 break;
  1076.         }
  1077. }
  1078.  
  1079. // seg002:1000
  1080. void __pascal far autocontrol_shadow_level4() {
  1081.         if (Char.room == 4) {
  1082.                 if (Char.x < 80) {
  1083.                         clear_char();
  1084.                 } else {
  1085.                         move_1_forward();
  1086.                 }
  1087.         }
  1088. }
  1089.  
  1090. // data:0F02
  1091. const auto_move_type shad_drink_move[] = {
  1092. {0x00, 0},
  1093. {0x01, 1},
  1094. {0x0E, 0},
  1095. {0x12, 6},
  1096. {0x1D, 7},
  1097. {0x2D, 2},
  1098. {0x31, 1},
  1099. {0xFF,-2},
  1100. };
  1101.  
  1102. // seg002:101A
  1103. void __pascal far autocontrol_shadow_level5() {
  1104.         if (Char.room == 24) {
  1105.                 if (demo_time == 0) {
  1106.                         get_tile(24, 1, 0);
  1107.                         // is the door open?
  1108.                         if (curr_room_modif[curr_tilepos] < 80) return;
  1109.                         demo_index = 0;
  1110.                 }
  1111.                 do_auto_moves(shad_drink_move);
  1112.                 if (Char.x < 15) {
  1113.                         clear_char();
  1114.                 }
  1115.         }
  1116. }
  1117.  
  1118. // seg002:1064
  1119. void __pascal far autocontrol_shadow_level6() {
  1120.         if (Char.room == 1 &&
  1121.                 Kid.frame == frame_43_running_jump_4 && // a frame in run-jump
  1122.                 Kid.x < 128
  1123.         ) {
  1124.                 move_6_shift();
  1125.                 move_1_forward();
  1126.         }
  1127. }
  1128.  
  1129. // seg002:1082
  1130. void __pascal far autocontrol_shadow_level12() {
  1131.         short opp_frame;
  1132.         short xdiff;
  1133.         if (Char.room == 15 && shadow_initialized == 0) {
  1134.                 if (Opp.x >= 150) {
  1135.                         do_init_shad(/*&*/init_shad_12, 7 /*fall*/);
  1136.                         return;
  1137.                 }
  1138.                 shadow_initialized = 1;
  1139.         }
  1140.         if (Char.sword >= sword_2_drawn) {
  1141.                 // if the Kid puts his sword away, the shadow does the same,
  1142.                 // but only if the shadow was already hurt (?)
  1143.                 if (offguard == 0 || guard_refrac == 0) {
  1144.                         autocontrol_guard_active();
  1145.                 } else {
  1146.                         move_4_down();
  1147.                 }
  1148.                 return;
  1149.         }
  1150.         if (Opp.sword >= sword_2_drawn || offguard == 0) {
  1151.                 xdiff = 0x7000; // bugfix/workaround
  1152.                 // This behavior matches the DOS version but not the Apple II source.
  1153.                 if (can_guard_see_kid < 2 || (xdiff = char_opp_dist()) >= 90) {
  1154.                         if (xdiff < 0) {
  1155.                                 move_2_backward();
  1156.                         }
  1157.                         return;
  1158.                 }
  1159.                 // Shadow draws his sword
  1160.                 if (Char.frame == 15) {
  1161.                         move_down_forw();
  1162.                 }
  1163.                 return;
  1164.         }
  1165.         if (char_opp_dist() < 10) {
  1166.                 // unite with the shadow
  1167.                 flash_color = color_15_brightwhite; // white
  1168.                 flash_time = 18;
  1169.                 // get an extra HP for uniting the shadow
  1170.                 add_life();
  1171.                 // time of Kid-shadow flash
  1172.                 united_with_shadow = 42;
  1173.                 // put the Kid where the shadow was
  1174.                 Char.charid = charid_0_kid;
  1175.                 savekid();
  1176.                 // remove the shadow
  1177.                 clear_char();
  1178.                 return;
  1179.         }
  1180.         if (can_guard_see_kid == 2) {
  1181.                 // If Kid runs to shadow, shadow runs to Kid.
  1182.                 opp_frame = Opp.frame;
  1183.                 // frames 1..14: running
  1184.                 // frames 121..132: stepping
  1185.                 if ((opp_frame >= frame_3_start_run && opp_frame < frame_15_stand) ||
  1186.                         (opp_frame >= frame_127_stepping_7 && opp_frame < 133)
  1187.                 ) {
  1188.                         move_1_forward();
  1189.                 }
  1190.         }
  1191. }
  1192.