/*
SDLPoP, a port/conversion of the DOS game Prince of Persia.
Copyright (C) 2013-2018 Dávid Nagy
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
The authors of this program may be contacted at http://forum.princed.org
*/
#include "common.h"
// data:0E32
const word strikeprob [] = { 61,100, 61, 61, 61, 40,100,220, 0, 48, 32, 48};
// data:0E4A
const word restrikeprob[] = { 0, 0, 0, 5, 5,175, 16, 8, 0,255,255,150};
// data:0E62
const word blockprob [] = { 0,150,150,200,200,255,200,250, 0,255,255,255};
// data:0E7A
const word impblockprob[] = { 0, 61, 61,100,100,145,100,250, 0,145,255,175};
// data:0E92
const word advprob [] = {255,200,200,200,255,255,200, 0, 0,255,100,100};
// data:0EAA
const word refractimer [] = { 16, 16, 16, 16, 8, 8, 8, 8, 0, 8, 0, 0};
// data:0EC2
const word extrastrength[] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
// seg002:0000
void __pascal far do_init_shad(const byte *source,int seq_index) {
memcpy_near(&Char, source, 7);
seqtbl_offset_char(seq_index);
Char.charid = charid_1_shadow;
demo_time = 0;
guard_skill = 3;
guardhp_delta = guardhp_curr = guardhp_max = 4;
saveshad();
}
// seg002:0044
void __pascal far get_guard_hp() {
guardhp_delta = guardhp_curr = guardhp_max = extrastrength[guard_skill] + tbl_guard_hp[current_level];
}
// data:0EEA
const byte init_shad_6[] = {0x0F, 0x51, 0x76, 0, 0, 1, 0, 0};
// data:0EF2
const byte init_shad_5[] = {0x0F, 0x37, 0x37, 0, 0xFF, 0, 0, 0};
// data:0EFA
const byte init_shad_12[] = {0x0F, 0x51, 0xE8, 0, 0, 0, 0, 0};
// seg002:0064
void __pascal far check_shadow() {
offguard = 0;
if (current_level == 12) {
// Special event: level 12 shadow
if (!united_with_shadow && drawn_room == 15) {
Char.room = drawn_room;
if (get_tile(15, 1, 0) == tiles_22_sword) {
return;
}
shadow_initialized = 0;
do_init_shad(/*&*/init_shad_12, 7 /*fall*/);
return;
}
} else if (current_level == 6) {
// Special event: level 6 shadow
Char.room = drawn_room;
if (Char.room == 1) {
if (leveldoor_open != 0x4D) {
play_sound(sound_25_presentation); // presentation (level 6 shadow)
leveldoor_open = 0x4D;
}
do_init_shad(/*&*/init_shad_6, 2 /*stand*/);
return;
}
} else if (current_level == 5) {
// Special event: level 5 shadow
Char.room = drawn_room;
if (Char.room == 24) {
if (get_tile(24, 3, 0) != tiles_10_potion) {
return;
}
do_init_shad(/*&*/init_shad_5, 2 /*stand*/);
return;
}
}
enter_guard();
}
// seg002:0112
void __pascal far enter_guard() {
word room_minus_1;
word guard_tile;
word frame;
byte seq_hi;
// arrays are indexed 0..23 instead of 1..24
room_minus_1 = drawn_room - 1;
frame = Char.frame; // hm?
guard_tile = level.guards_tile[room_minus_1];
#ifndef FIX_OFFSCREEN_GUARDS_DISAPPEARING
if (guard_tile >= 30) return;
#else
if (guard_tile >= 30) {
if (!fix_offscreen_guards_disappearing) return;
// try to see if there are offscreen guards in the left and right rooms that might be visible from this room
word left_guard_tile = 31;
word right_guard_tile = 31;
if (room_L > 0) left_guard_tile = level.guards_tile[room_L-1];
if (room_R > 0) right_guard_tile = level.guards_tile[room_R-1];
int other_guard_x;
sbyte other_guard_dir;
int delta_x;
int other_room_minus_1;
if (right_guard_tile >= 0 && right_guard_tile < 30) {
other_room_minus_1 = room_R - 1;
other_guard_x = level.guards_x[other_room_minus_1];
other_guard_dir = level.guards_dir[other_room_minus_1];
// left edge of the guard matters
if (other_guard_dir == dir_0_right) other_guard_x -= 9; // only retrieve a guard if they will be visible
if (other_guard_dir == dir_FF_left) other_guard_x += 1; // getting these right was mostly trial and error
// only retrieve offscreen guards
if (!(other_guard_x < 58 + 4)) return;
delta_x = 140; // guard leaves to the left
guard_tile = right_guard_tile;
}
else if (left_guard_tile >= 0 && left_guard_tile < 30) {
other_room_minus_1 = room_L - 1;
other_guard_x = level.guards_x[other_room_minus_1];
other_guard_dir = level.guards_dir[other_room_minus_1];
// right edge of the guard matters
if (other_guard_dir == dir_0_right) other_guard_x -= 9;
if (other_guard_dir == dir_FF_left) other_guard_x += 1;
// only retrieve offscreen guards
if (!(other_guard_x > 190 - 4)) return;
delta_x = -140; // guard leaves to the right
guard_tile = left_guard_tile;
}
else return;
// retrieve guard from adjacent room
level.guards_x[room_minus_1] = level.guards_x[other_room_minus_1] + delta_x;
level.guards_color[room_minus_1] = level.guards_color[other_room_minus_1];
level.guards_dir[room_minus_1] = level.guards_dir[other_room_minus_1];
level.guards_seq_hi[room_minus_1] = level.guards_seq_hi[other_room_minus_1];
level.guards_seq_lo[room_minus_1] = level.guards_seq_lo[other_room_minus_1];
level.guards_skill[room_minus_1] = level.guards_skill[other_room_minus_1];
level.guards_tile[other_room_minus_1] = 0xFF;
level.guards_seq_hi[other_room_minus_1] = 0;
}
#endif
Char.room = drawn_room;
Char.curr_row = guard_tile / 10;
Char.y = y_land[Char.curr_row + 1];
Char.x = level.guards_x[room_minus_1];
Char.curr_col = get_tile_div_mod_m7(Char.x);
Char.direction = level.guards_dir[room_minus_1];
// only regular guards have different colors (and only on VGA)
if (graphics_mode == gmMcgaVga && tbl_guard_type[current_level] == 0) {
curr_guard_color = level.guards_color[room_minus_1];
} else {
curr_guard_color = 0;
}
#ifdef REMEMBER_GUARD_HP
int remembered_hp = (curr_guard_color & 0xF0) >> 4;
#endif
curr_guard_color &= 0x0F; // added; only least significant 4 bits are used for guard color
// level 3 has skeletons with infinite lives
if (current_level == 3) {
Char.charid = charid_4_skeleton;
} else {
Char.charid = charid_2_guard;
}
seq_hi = level.guards_seq_hi[room_minus_1];
if (seq_hi == 0) {
if (Char.charid == charid_4_skeleton) {
Char.sword = sword_2_drawn;
seqtbl_offset_char(seq_63_guard_stand_active); // stand active (when entering room) (skeleton)
} else {
Char.sword = sword_0_sheathed;
seqtbl_offset_char(seq_77_guard_stand_inactive); // stand inactive (when entering room)
}
} else {
Char.curr_seq = level.guards_seq_lo[room_minus_1] + (seq_hi << 8);
}
play_seq();
guard_skill = level.guards_skill[room_minus_1];
if (guard_skill >= 12) {
guard_skill = 3;
}
frame = Char.frame;
if (frame == frame_185_dead || frame == frame_177_spiked || frame == frame_178_chomped) {
Char.alive = 1;
draw_guard_hp(0, guardhp_curr);
guardhp_curr = 0;
} else {
Char.alive = -1;
justblocked = 0;
guard_refrac = 0;
is_guard_notice = 0;
get_guard_hp();
#ifdef REMEMBER_GUARD_HP
if (enable_remember_guard_hp && remembered_hp > 0)
guardhp_delta = guardhp_curr = (word) remembered_hp;
#endif
}
Char.fall_y = 0;
Char.fall_x = 0;
Char.action = actions_1_run_jump;
saveshad();
}
// seg002:0269
void __pascal far check_guard_fallout() {
if (Guard.direction == dir_56_none || Guard.y < 211) {
return;
}
if (Guard.charid == charid_1_shadow) {
if (Guard.action != actions_4_in_freefall) {
return;
}
loadshad();
clear_char();
saveshad();
} else if (Guard.charid == charid_4_skeleton &&
(Guard.room = level.roomlinks[Guard.room - 1].down) == 3) {
// if skeleton falls down into room 3
Guard.x = 133;
Guard.curr_row = 1;
Guard.direction = dir_0_right;
Guard.alive = -1;
leave_guard();
} else {
on_guard_killed();
level.guards_tile[drawn_room - 1] = -1;
Guard.direction = dir_56_none;
draw_guard_hp(0, guardhp_curr);
guardhp_curr = 0;
}
}
// seg002:02F5
void __pascal far leave_guard() {
word room_minus_1;
if (Guard.direction == dir_56_none || Guard.charid == charid_1_shadow || Guard.charid == charid_24_mouse) {
return;
}
// arrays are indexed 0..23 instead of 1..24
room_minus_1 = Guard.room - 1;
level.guards_tile[room_minus_1] = get_tilepos(0, Guard.curr_row);
level.guards_color[room_minus_1] = curr_guard_color & 0x0F; // restriction to 4 bits added
#ifdef REMEMBER_GUARD_HP
if (enable_remember_guard_hp && guardhp_curr < 16) // can remember 1..15 hp
level.guards_color[room_minus_1] |= (guardhp_curr << 4);
#endif
level.guards_x[room_minus_1] = Guard.x;
level.guards_dir[room_minus_1] = Guard.direction;
level.guards_skill[room_minus_1] = guard_skill;
if (Guard.alive < 0) {
level.guards_seq_hi[room_minus_1] = 0;
} else {
level.guards_seq_lo[room_minus_1] = Guard.curr_seq;
level.guards_seq_hi[room_minus_1] = Guard.curr_seq >> 8;
}
Guard.direction = dir_56_none;
draw_guard_hp(0, guardhp_curr);
guardhp_curr = 0;
}
// seg002:039E
void __pascal far follow_guard() {
level.guards_tile[Kid.room - 1] = 0xFF;
level.guards_tile[Guard.room - 1] = 0xFF;
loadshad();
goto_other_room(roomleave_result);
saveshad();
}
// seg002:03C7
void __pascal far exit_room() {
word leave;
word kid_room_m1;
leave = 0;
if (exit_room_timer != 0) {
--exit_room_timer;
return;
}
loadkid();
load_frame_to_obj();
set_char_collision();
roomleave_result = leave_room();
if (roomleave_result < 0) {
return;
}
savekid();
next_room = Char.room;
if (Guard.direction == dir_56_none) return;
if (Guard.alive < 0 && Guard.sword == sword_2_drawn) {
kid_room_m1 = Kid.room - 1;
if (level.guards_tile[kid_room_m1] >= 30 ||
level.guards_seq_hi[kid_room_m1] != 0
) {
if (roomleave_result == 0) {
// left
if (Guard.x >= 91) leave = 1;
#ifdef FIX_GUARD_FOLLOWING_THROUGH_CLOSED_GATES
else if (fix_guard_following_through_closed_gates && can_guard_see_kid != 2 &&
Kid.sword != sword_2_drawn) {
leave = 1;
}
#endif
} else if (roomleave_result == 1) {
// right
if (Guard.x < 165) leave = 1;
#ifdef FIX_GUARD_FOLLOWING_THROUGH_CLOSED_GATES
else if (fix_guard_following_through_closed_gates && can_guard_see_kid != 2 &&
Kid.sword != sword_2_drawn) {
leave = 1;
}
#endif
} else if (roomleave_result == 2) {
// up
if (Guard.curr_row >= 0) leave = 1;
} else {
// down
if (Guard.curr_row < 3) leave = 1;
}
} else {
leave = 1;
}
} else {
leave = 1;
}
if (leave) {
leave_guard();
} else {
follow_guard();
}
}
// seg002:0486
int __pascal far goto_other_room(short direction) {
short opposite_dir;
Char.room = ((byte*)&level.roomlinks[Char.room - 1])[direction];
if (direction == 0) {
// left
Char.x += 140;
opposite_dir = 1;
} else if (direction == 1) {
// right
Char.x -= 140;
opposite_dir = 0;
} else if (direction == 2) {
// up
Char.y += 189;
Char.curr_row = y_to_row_mod4(Char.y);
opposite_dir = 3;
} else {
// down
Char.y -= 189;
Char.curr_row = y_to_row_mod4(Char.y);
opposite_dir = 2;
}
return opposite_dir;
}
// seg002:0504
short __pascal far leave_room() {
short frame;
word action;
short chary;
short leave_dir;
chary = Char.y;
action = Char.action;
frame = Char.frame;
if (action != actions_5_bumped &&
action != actions_4_in_freefall &&
action != actions_3_in_midair &&
(sbyte)chary < 10 && (sbyte)chary > -16
) {
leave_dir = 2; // up
} else if (chary >= 211) {
leave_dir = 3; // down
} else if (
// frames 135..149: climb up
(frame >= frame_135_climbing_1 && frame < 150) ||
// frames 110..119: standing up from crouch
(frame >= frame_110_stand_up_from_crouch_1 && frame < 120) ||
// frames 150..162: with sword
(frame >= frame_150_parry && frame < 163
#ifdef FIX_RETREAT_WITHOUT_LEAVING_ROOM
// By repeatedly pressing 'back' in a swordfight, you can retreat out of a room without the room changing. (Trick 35)
// The game waits for a 'legal frame' (e.g. frame_170_stand_with_sword) until leaving is possible;
// However, this frame is never reached if you press 'back' in the correct pattern!
// Solution: also allow the room to be changed on frame_157_walk_with_sword
// Note that this means that the delay for leaving rooms in a swordfight becomes noticably shorter.
&& (frame != frame_157_walk_with_sword || !fix_retreat_without_leaving_room)
#endif
) ||
// frames 166..168: with sword
(frame >= frame_166_stand_inactive && frame < 169) ||
action == actions_7_turn // turn
) {
return -1;
} else if (Char.direction != dir_0_right) {
// looking left
if (char_x_left <= 54) {
leave_dir = 0; // left
} else if (char_x_left >= 198) {
leave_dir = 1; // right
} else {
return -1;
}
} else {
// looking right
get_tile(Char.room, 9, Char.curr_row);
if (curr_tile2 != tiles_7_doortop_with_floor &&
curr_tile2 != tiles_12_doortop &&
char_x_right >= 201
) {
leave_dir = 1; // right
} else if (char_x_right <= 57) {
leave_dir = 0; // left
} else {
return -1;
}
}
switch (leave_dir) {
case 0: // left
play_mirr_mus();
level3_set_chkp();
Jaffar_exit();
break;
case 1: // right
sword_disappears();
meet_Jaffar();
break;
//case 2: // up
case 3: // down
// Special event: falling exit
if (current_level == 6 && Char.room == 1) {
return -2;
}
break;
}
goto_other_room(leave_dir);
#ifdef USE_REPLAY
if (skipping_replay && replay_seek_target == replay_seek_0_next_room) skipping_replay = 0;
#endif
return leave_dir;
}
// seg002:0643
void __pascal far Jaffar_exit() {
if (leveldoor_open == 2) {
get_tile(24, 0, 0);
trigger_button(0, 0, -1);
}
}
// seg002:0665
void __pascal far level3_set_chkp() {
// Special event: set checkpoint
if (current_level == 3 && Char.room == 7) {
checkpoint = 1;
hitp_beg_lev = hitp_max;
}
}
// seg002:0680
void __pascal far sword_disappears() {
// Special event: sword disappears
if (current_level == 12 && Char.room == 18) {
get_tile(15, 1, 0);
curr_room_tiles[curr_tilepos] = tiles_1_floor;
curr_room_modif[curr_tilepos] = 0; // added, a nonzero modifier may show fake tiles
}
}
// seg002:06AE
void __pascal far meet_Jaffar() {
// Special event: play music
if (current_level == 13 && leveldoor_open == 0 && Char.room == 3) {
play_sound(sound_29_meet_Jaffar); // meet Jaffar
// Special event: Jaffar waits a bit (28/12=2.33 seconds)
guard_notice_timer = 28;
}
}
// seg002:06D3
void __pascal far play_mirr_mus() {
// Special event: mirror music
if (
leveldoor_open != 0 &&
leveldoor_open != 0x4D && // was the music played already?
current_level == 4 &&
Char.curr_row == 0 &&
Char.room == 11
) {
play_sound(sound_25_presentation); // presentation (level 4 mirror)
leveldoor_open = 0x4D;
}
}
// seg002:0706
void __pascal far move_0_nothing() {
control_shift = 0;
control_y = 0;
control_x = 0;
control_shift2 = 0;
control_down = 0;
control_up = 0;
control_backward = 0;
control_forward = 0;
}
// seg002:0721
void __pascal far move_1_forward() {
control_x = -1;
control_forward = -1;
}
// seg002:072A
void __pascal far move_2_backward() {
control_backward = -1;
control_x = 1;
}
// seg002:0735
void __pascal far move_3_up() {
control_y = -1;
control_up = -1;
}
// seg002:073E
void __pascal far move_4_down() {
control_down = -1;
control_y = 1;
}
// seg002:0749
void __pascal far move_up_back() {
control_up = -1;
move_2_backward();
}
// seg002:0753
void __pascal far move_down_back() {
control_down = -1;
move_2_backward();
}
// seg002:075D
void __pascal far move_down_forw() {
control_down = -1;
move_1_forward();
}
// seg002:0767
void __pascal far move_6_shift() {
control_shift = -1;
control_shift2 = -1;
}
// seg002:0770
void __pascal far move_7() {
control_shift = 0;
}
// seg002:0776
void __pascal far autocontrol_opponent() {
word charid;
move_0_nothing();
charid = Char.charid;
if (charid == charid_0_kid) {
autocontrol_kid();
} else {
if (justblocked) --justblocked;
if (kid_sword_strike) --kid_sword_strike;
if (guard_refrac) --guard_refrac;
if (charid == charid_24_mouse) {
autocontrol_mouse();
} else if (charid == charid_4_skeleton) {
autocontrol_skeleton();
} else if (charid == charid_1_shadow) {
autocontrol_shadow();
} else if (current_level == 13) {
autocontrol_Jaffar();
} else {
autocontrol_guard();
}
}
}
// seg002:07EB
void __pascal far autocontrol_mouse() {
if (Char.direction == dir_56_none) {
return;
}
if (Char.action == actions_0_stand) {
if (Char.x >= 200) {
clear_char();
}
} else {
if (Char.x < 166) {
seqtbl_offset_char(seq_107_mouse_stand_up_and_go); // mouse
play_seq();
}
}
}
// seg002:081D
void __pascal far autocontrol_shadow() {
if (current_level == 4) {
autocontrol_shadow_level4();
} else if (current_level == 5) {
autocontrol_shadow_level5();
} else if (current_level == 6) {
autocontrol_shadow_level6();
} else if (current_level == 12) {
autocontrol_shadow_level12();
}
}
// seg002:0850
void __pascal far autocontrol_skeleton() {
Char.sword = sword_2_drawn;
autocontrol_guard();
}
// seg002:085A
void __pascal far autocontrol_Jaffar() {
autocontrol_guard();
}
// seg002:085F
void __pascal far autocontrol_kid() {
autocontrol_guard();
}
// seg002:0864
void __pascal far autocontrol_guard() {
if (Char.sword < sword_2_drawn) {
autocontrol_guard_inactive();
} else {
autocontrol_guard_active();
}
}
// seg002:0876
void __pascal far autocontrol_guard_inactive() {
short distance;
if (Kid.alive >= 0) return;
distance = char_opp_dist();
if (Opp.curr_row != Char.curr_row || (word)distance < (word)-8) {
// If Kid made a sound ...
if (is_guard_notice) {
is_guard_notice = 0;
if (distance < 0) {
// ... and Kid is behind Guard, Guard turns around.
if ((word)distance < (word)-4) {
move_4_down();
}
return;
}
} else if (distance < 0) {
return;
}
}
if (can_guard_see_kid) {
// If Guard can see Kid, Guard moves to fighting pose.
if (current_level != 13 || guard_notice_timer == 0) {
move_down_forw();
}
}
}
// seg002:08DC
void __pascal far autocontrol_guard_active() {
short opp_frame;
short char_frame;
short distance;
char_frame = Char.frame;
if (char_frame != frame_166_stand_inactive && char_frame >= 150 && can_guard_see_kid != 1) {
if (can_guard_see_kid == 0) {
if (droppedout != 0) {
guard_follows_kid_down();
//return;
} else if (Char.charid != charid_4_skeleton) {
move_down_back();
}
//return;
} else { // can_guard_see_kid == 2
opp_frame = Opp.frame;
distance = char_opp_dist();
if (distance >= 12 &&
// frames 102..117: falling and landing
opp_frame >= frame_102_start_fall_1 && opp_frame < frame_118_stand_up_from_crouch_9 &&
Opp.action == actions_5_bumped
) {
return;
}
if (distance < 35) {
if ((Char.sword < sword_2_drawn && distance < 8) || distance < 12) {
if (Char.direction == Opp.direction) {
// turn around
move_2_backward();
//return;
} else {
move_1_forward();
//return;
}
} else {
autocontrol_guard_kid_in_sight(distance);
//return;
}
} else {
if (guard_refrac != 0) return;
if (Char.direction != Opp.direction) {
// frames 7..14: running
// frames 34..43: run-jump
if (opp_frame >= frame_7_run && opp_frame < 15) {
if (distance < 40) move_6_shift();
return;
} else if (opp_frame >= frame_34_start_run_jump_1 && opp_frame < 44) {
if (distance < 50) move_6_shift();
return;
//return;
}
}
autocontrol_guard_kid_far();
}
//...
}
//...
}
}
// seg002:09CB
void __pascal far autocontrol_guard_kid_far() {
if (tile_is_floor(get_tile_infrontof_char()) ||
tile_is_floor(get_tile_infrontof2_char())) {
move_1_forward();
} else {
move_2_backward();
}
}
// seg002:09F8
void __pascal far guard_follows_kid_down() {
// This is called from autocontrol_guard_active, so char=Guard, Opp=Kid
word opp_action;
opp_action = Opp.action;
if (opp_action == actions_2_hang_climb || opp_action == actions_6_hang_straight) {
return;
}
if (// there is wall in front of Guard
wall_type(get_tile_infrontof_char()) != 0 ||
(! tile_is_floor(curr_tile2) && (
(get_tile(curr_room, tile_col, ++tile_row) == tiles_2_spike ||
// Guard would fall on loose floor
curr_tile2 == tiles_11_loose ||
// ... or wall (?)
wall_type(curr_tile2) != 0 ||
// ... or into a chasm
! tile_is_floor(curr_tile2)) ||
// ... or Kid is not below
Char.curr_row + 1 != Opp.curr_row
))
) {
// don't follow
droppedout = 0;
move_2_backward();
} else {
// follow
move_1_forward();
}
}
// seg002:0A93
void __pascal far autocontrol_guard_kid_in_sight(short distance) {
if (Opp.sword == sword_2_drawn) {
autocontrol_guard_kid_armed(distance);
} else if (guard_refrac == 0) {
if (distance < 29) {
move_6_shift();
} else {
move_1_forward();
}
}
}
// seg002:0AC1
void __pascal far autocontrol_guard_kid_armed(short distance) {
if (distance < 10 || distance >= 29) {
guard_advance();
} else {
guard_block();
if (guard_refrac == 0) {
if (distance < 12 || distance >= 29) {
guard_advance();
} else {
guard_strike();
}
}
}
}
// seg002:0AF5
void __pascal far guard_advance() {
if (guard_skill == 0 || kid_sword_strike == 0) {
if (advprob[guard_skill] > prandom(255)) {
move_1_forward();
}
}
}
// seg002:0B1D
void __pascal far guard_block() {
word opp_frame;
opp_frame = Opp.frame;
if (opp_frame == frame_152_strike_2 || opp_frame == frame_153_strike_3 || opp_frame == frame_162_block_to_strike) {
if (justblocked != 0) {
if (impblockprob[guard_skill] > prandom(255)) {
move_3_up();
}
} else {
if (blockprob[guard_skill] > prandom(255)) {
move_3_up();
}
}
}
}
// seg002:0B73
void __pascal far guard_strike() {
word opp_frame;
word char_frame;
opp_frame = Opp.frame;
if (opp_frame == frame_169_begin_block || opp_frame == frame_151_strike_1) return;
char_frame = Char.frame;
if (char_frame == frame_161_parry || char_frame == frame_150_parry) {
if (restrikeprob[guard_skill] > prandom(255)) {
move_6_shift();
}
} else {
if (strikeprob[guard_skill] > prandom(255)) {
move_6_shift();
}
}
}
// seg002:0BCD
void __pascal far hurt_by_sword() {
short distance;
if (Char.alive >= 0) return;
if (Char.sword != sword_2_drawn) {
// Being hurt when not in fighting pose means death.
take_hp(100);
seqtbl_offset_char(seq_85_stabbed_to_death); // dying (stabbed unarmed)
loc_4276:
if (get_tile_behind_char() != 0 ||
(distance = distance_to_edge_weight()) < 4
) {
seqtbl_offset_char(seq_85_stabbed_to_death); // dying (stabbed)
if (Char.charid != charid_0_kid &&
Char.direction < dir_0_right && // looking left
(curr_tile2 == tiles_4_gate || get_tile_at_char() == tiles_4_gate)
) {
Char.x = x_bump[tile_col - (curr_tile2 != tiles_4_gate) + 5] + 7;
Char.x = char_dx_forward(10);
}
Char.y = y_land[Char.curr_row + 1];
Char.fall_y = 0;
} else {
Char.x = char_dx_forward(distance - 20);
load_fram_det_col();
inc_curr_row();
seqtbl_offset_char(seq_81_kid_pushed_off_ledge); // Kid/Guard is killed and pushed off the ledge
}
} else {
// You can't hurt skeletons
if (Char.charid != charid_4_skeleton) {
if (take_hp(1)) goto loc_4276;
}
seqtbl_offset_char(seq_74_hit_by_sword); // being hit with sword
Char.y = y_land[Char.curr_row + 1];
Char.fall_y = 0;
}
// sound 13: Kid hurt (by sword), sound 12: Guard hurt (by sword)
play_sound(Char.charid == charid_0_kid ? sound_13_kid_hurt : sound_12_guard_hurt);
play_seq();
}
// seg002:0CD4
void __pascal far check_sword_hurt() {
if (Guard.action == actions_99_hurt) {
if (Kid.action == actions_99_hurt) {
Kid.action = actions_1_run_jump;
}
loadshad();
hurt_by_sword();
saveshad();
guard_refrac = refractimer[guard_skill];
} else {
if (Kid.action == actions_99_hurt) {
loadkid();
hurt_by_sword();
savekid();
}
}
}
// seg002:0D1A
void __pascal far check_sword_hurting() {
short kid_frame;
kid_frame = Kid.frame;
// frames 217..228: go up on stairs
if (kid_frame != 0 && (kid_frame < frame_219_exit_stairs_3 || kid_frame >= 229)) {
loadshad_and_opp();
check_hurting();
saveshad_and_opp();
loadkid_and_opp();
check_hurting();
savekid_and_opp();
}
}
// seg002:0D56
void __pascal far check_hurting() {
short opp_frame, char_frame, distance, min_hurt_range;
if (Char.sword != sword_2_drawn) return;
if (Char.curr_row != Opp.curr_row) return;
char_frame = Char.frame;
// frames 153..154: poking with sword
if (char_frame != frame_153_strike_3 && char_frame != frame_154_poking) return;
// If char is poking ...
distance = char_opp_dist();
opp_frame = Opp.frame;
// frames 161 and 150: parrying
if (distance < 0 || distance >= 29 ||
(opp_frame != frame_161_parry && opp_frame != frame_150_parry)
) {
// ... and Opp is not parrying
// frame 154: poking
if (Char.frame == frame_154_poking) {
if (Opp.sword < sword_2_drawn) {
min_hurt_range = 8;
} else {
min_hurt_range = 12;
}
distance = char_opp_dist();
if (distance >= min_hurt_range && distance < 29) {
Opp.action = actions_99_hurt;
}
}
} else {
Opp.frame = frame_161_parry;
if (Char.charid != charid_0_kid) {
justblocked = 4;
}
seqtbl_offset_char(seq_69_attack_was_parried); // attack was parried
play_seq();
}
// frame 154: poking
// frame 161: parrying
if (Char.frame == frame_154_poking && Opp.frame != frame_161_parry && Opp.action != actions_99_hurt) {
play_sound(sound_11_sword_moving); // sword moving
}
}
// seg002:0E1F
void __pascal far check_skel() {
// Special event: skeleton wakes
if (current_level == 3 &&
Guard.direction == dir_56_none &&
drawn_room == 1 &&
leveldoor_open != 0 &&
(Kid.curr_col == 2 || Kid.curr_col == 3)
) {
get_tile(drawn_room, 5, 1);
if (curr_tile2 == tiles_21_skeleton) {
// erase skeleton
curr_room_tiles[curr_tilepos] = tiles_1_floor;
redraw_height = 24;
set_redraw_full(curr_tilepos, 1);
set_wipe(curr_tilepos, 1);
++curr_tilepos;
set_redraw_full(curr_tilepos, 1);
set_wipe(curr_tilepos, 1);
Char.room = drawn_room;
Char.curr_row = 1;
Char.y = y_land[Char.curr_row + 1];
Char.curr_col = 5;
Char.x = x_bump[Char.curr_col + 5] + 14;
Char.direction = dir_FF_left;
seqtbl_offset_char(seq_88_skel_wake_up); // skel wake up
play_seq();
play_sound(sound_44_skel_alive); // skel alive
guard_skill = 2;
Char.alive = -1;
guardhp_max = guardhp_curr = 3;
Char.fall_x = Char.fall_y = 0;
is_guard_notice = guard_refrac = 0;
Char.sword = sword_2_drawn;
Char.charid = charid_4_skeleton;
saveshad();
}
}
}
// seg002:0F3F
void __pascal far do_auto_moves(const auto_move_type *moves_ptr) {
short demoindex;
short curr_move;
if (demo_time >= 0xFE) return;
++demo_time;
demoindex = demo_index;
if (moves_ptr
[demoindex
].
time <= demo_time
) {
++demo_index;
} else {
demoindex = demo_index - 1;
}
curr_move = moves_ptr[demoindex].move;
switch (curr_move) {
case -1:
break;
case 0:
move_0_nothing();
break;
case 1:
move_1_forward();
break;
case 2:
move_2_backward();
break;
case 3:
move_3_up();
break;
case 4:
move_4_down();
break;
case 5:
move_3_up();
move_1_forward();
break;
case 6:
move_6_shift();
break;
case 7:
move_7();
break;
}
}
// seg002:1000
void __pascal far autocontrol_shadow_level4() {
if (Char.room == 4) {
if (Char.x < 80) {
clear_char();
} else {
move_1_forward();
}
}
}
// data:0F02
const auto_move_type shad_drink_move[] = {
{0x00, 0},
{0x01, 1},
{0x0E, 0},
{0x12, 6},
{0x1D, 7},
{0x2D, 2},
{0x31, 1},
{0xFF,-2},
};
// seg002:101A
void __pascal far autocontrol_shadow_level5() {
if (Char.room == 24) {
if (demo_time == 0) {
get_tile(24, 1, 0);
// is the door open?
if (curr_room_modif[curr_tilepos] < 80) return;
demo_index = 0;
}
do_auto_moves(shad_drink_move);
if (Char.x < 15) {
clear_char();
}
}
}
// seg002:1064
void __pascal far autocontrol_shadow_level6() {
if (Char.room == 1 &&
Kid.frame == frame_43_running_jump_4 && // a frame in run-jump
Kid.x < 128
) {
move_6_shift();
move_1_forward();
}
}
// seg002:1082
void __pascal far autocontrol_shadow_level12() {
short opp_frame;
short xdiff;
if (Char.room == 15 && shadow_initialized == 0) {
if (Opp.x >= 150) {
do_init_shad(/*&*/init_shad_12, 7 /*fall*/);
return;
}
shadow_initialized = 1;
}
if (Char.sword >= sword_2_drawn) {
// if the Kid puts his sword away, the shadow does the same,
// but only if the shadow was already hurt (?)
if (offguard == 0 || guard_refrac == 0) {
autocontrol_guard_active();
} else {
move_4_down();
}
return;
}
if (Opp.sword >= sword_2_drawn || offguard == 0) {
xdiff = 0x7000; // bugfix/workaround
// This behavior matches the DOS version but not the Apple II source.
if (can_guard_see_kid < 2 || (xdiff = char_opp_dist()) >= 90) {
if (xdiff < 0) {
move_2_backward();
}
return;
}
// Shadow draws his sword
if (Char.frame == 15) {
move_down_forw();
}
return;
}
if (char_opp_dist() < 10) {
// unite with the shadow
flash_color = color_15_brightwhite; // white
flash_time = 18;
// get an extra HP for uniting the shadow
add_life();
// time of Kid-shadow flash
united_with_shadow = 42;
// put the Kid where the shadow was
Char.charid = charid_0_kid;
savekid();
// remove the shadow
clear_char();
return;
}
if (can_guard_see_kid == 2) {
// If Kid runs to shadow, shadow runs to Kid.
opp_frame = Opp.frame;
// frames 1..14: running
// frames 121..132: stepping
if ((opp_frame >= frame_3_start_run && opp_frame < frame_15_stand) ||
(opp_frame >= frame_127_stepping_7 && opp_frame < 133)
) {
move_1_forward();
}
}
}