1 /*
2 SDLPoP, a port/conversion of the DOS game Prince of Persia.
3 Copyright (C) 2013-2021 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 <https://www.gnu.org/licenses/>.
17
18 The authors of this program may be contacted at https://forum.princed.org
19 */
20
21 #include "common.h"
22
23 // data:3D1A
24 sbyte distance_mirror;
25
26 // seg003:0000
init_game(int level)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 = custom->start_minutes_left; // 60
42 rem_tick = custom->start_ticks_left; // 719
43 hitp_beg_lev = custom->start_hitp; // 3
44 }
45 need_level1_music = (level == /*1*/ custom->intro_music_level);
46 play_level(level);
47 }
48
49 // seg003:005C
play_level(int level_number)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 == custom->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[custom->tbl_cutscenes_by_index[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)*/ (level_number == 0 || level_number >= custom->have_sword_from_level);
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 == custom->copyprot_level && !demo_mode) {
125 level_number = 15;
126 } else {
127 if (level_number == 16) {
128 level_number = custom->copyprot_level;
129 custom->copyprot_level = -1;
130 }
131 }
132 #endif
133 free_peels();
134 }
135 }
136
137 // seg003:01A3
do_startpos()138 void __pascal far do_startpos() {
139 word x;
140 // Special event: start at checkpoint
141 if (current_level == /*3*/ custom->checkpoint_level && checkpoint) {
142 level.start_dir = /*dir_FF_left*/ custom->checkpoint_respawn_dir;
143 level.start_room = /*2*/ custom->checkpoint_respawn_room;
144 level.start_pos = /*6*/ custom->checkpoint_respawn_tilepos;
145 // Special event: remove loose floor
146 get_tile(/*7*/ custom->checkpoint_clear_tile_room,
147 /*4*/ custom->checkpoint_clear_tile_col,
148 /*0*/ custom->checkpoint_clear_tile_row);
149 curr_room_tiles[curr_tilepos] = tiles_0_empty;
150 }
151 next_room = Char.room = level.start_room;
152 x = level.start_pos;
153 Char.curr_col = x % 10;
154 Char.curr_row = x / 10;
155 Char.x = x_bump[Char.curr_col + 5] + 14;
156 // Start in the opposite direction (and turn into the correct one).
157 Char.direction = ~ level.start_dir;
158 if (seamless == 0) {
159 if (current_level != 0) {
160 x = hitp_beg_lev;
161 } else {
162 // HP on demo level
163 x = /*4*/ custom->demo_hitp;
164 }
165 hitp_max = hitp_curr = x;
166 }
167 if (/*current_level == 1*/ custom->tbl_entry_pose[current_level] == 1) {
168 // Special event: press button + falling entry
169 get_tile(5, 2, 0);
170 trigger_button(0, 0, -1);
171 seqtbl_offset_char(seq_7_fall); // fall
172 } else if (/*current_level == 13*/ custom->tbl_entry_pose[current_level] == 2) {
173 // Special event: running entry
174 seqtbl_offset_char(seq_84_run); // run
175 } else {
176 seqtbl_offset_char(seq_5_turn); // turn
177 }
178 set_start_pos();
179 }
180
181 // seg003:028A
set_start_pos()182 void __pascal far set_start_pos() {
183 Char.y = y_land[Char.curr_row + 1];
184 Char.alive = -1;
185 Char.charid = charid_0_kid;
186 is_screaming = 0;
187 knock = 0;
188 upside_down = custom->start_upside_down; // 0
189 is_feather_fall = 0;
190 Char.fall_y = 0;
191 Char.fall_x = 0;
192 offguard = 0;
193 Char.sword = sword_0_sheathed;
194 droppedout = 0;
195 play_seq();
196 if (current_level == /*7*/ custom->falling_entry_level && Char.room == /*17*/ custom->falling_entry_room) {
197 // Special event: level 7 falling entry
198 // level 7, room 17: show room below
199 goto_other_room(3);
200 }
201 savekid();
202 }
203
204 // seg003:02E6
find_start_level_door()205 void __pascal far find_start_level_door() {
206 short tilepos;
207 get_room_address(Kid.room);
208 for (tilepos = 0; tilepos < 30; ++tilepos) {
209 if ((curr_room_tiles[tilepos] & 0x1F) == tiles_16_level_door_left) {
210 start_level_door(Kid.room, tilepos);
211 }
212 }
213 }
214
215 // seg003:0326
draw_level_first()216 void __pascal far draw_level_first() {
217 next_room = Kid.room;
218 check_the_end();
219 if (custom->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 #ifdef USE_QUICKSAVE
228 check_quick_op();
229 #endif
230
231 #ifdef USE_SCREENSHOT
232 auto_screenshot();
233 #endif
234
235 // Busy waiting!
236 start_timer(timer_1, 5);
237 do_simple_wait(1);
238 }
239
240 // seg003:037B
redraw_screen(int drawing_different_room)241 void __pascal far redraw_screen(int drawing_different_room) {
242 //remove_flash();
243 if (drawing_different_room) {
244 draw_rect(&rect_top, 0);
245 }
246
247 different_room = 0;
248 if (is_blind_mode) {
249 draw_rect(&rect_top, 0);
250 } else {
251 if (curr_guard_color) {
252 // Moved *before* drawings.
253 set_chtab_palette(chtab_addrs[id_chtab_5_guard], &guard_palettes[0x30 * curr_guard_color - 0x30], 0x10);
254 }
255 need_drects = 0;
256 redraw_room();
257 #ifdef USE_LIGHTING
258 redraw_lighting();
259 #endif
260 if (is_keyboard_mode) {
261 clear_kbd_buf();
262 }
263 is_blind_mode = 1;
264 draw_tables();
265 if (is_keyboard_mode) {
266 clear_kbd_buf();
267 }
268 #ifdef USE_COPYPROT
269 if (current_level == 15) {
270 // letters on potions level
271 current_target_surface = offscreen_surface;
272 short var_2;
273 for (var_2 = 0; var_2 < 14; ++var_2) {
274 if (copyprot_room[var_2] == drawn_room) {
275 set_curr_pos((copyprot_tile[var_2] % 10 << 5) + 24, copyprot_tile[var_2] / 10 * 63 + 38);
276 draw_text_character(copyprot_letter[cplevel_entr[var_2]]);
277 }
278 }
279 current_target_surface = onscreen_surface_;
280 }
281 #endif
282 is_blind_mode = 0;
283 memset_near(table_counts, 0, sizeof(table_counts));
284 draw_moving();
285 draw_tables();
286 if (is_keyboard_mode) {
287 clear_kbd_buf();
288 }
289 need_drects = 1;
290 if (curr_guard_color) {
291 //set_pal_arr(0x80, 0x10, &guard_palettes[0x30 * curr_guard_color - 0x30], 1);
292 }
293 if (upside_down) {
294 flip_screen(offscreen_surface);
295 }
296 copy_screen_rect(&rect_top);
297 if (upside_down) {
298 flip_screen(offscreen_surface);
299 }
300 if (is_keyboard_mode) {
301 clear_kbd_buf();
302 }
303 }
304 exit_room_timer = 2;
305
306 }
307
308 #ifdef CHECK_TIMING
309 typedef struct test_timing_state_type {
310 bool already_had_first_frame;
311 Uint64 level_start_counter;
312 int ticks_left_at_level_start;
313 float seconds_left_at_level_start;
314 } test_timing_state_type;
315
test_timings(test_timing_state_type * state)316 void test_timings(test_timing_state_type* state) {
317
318 if (!state->already_had_first_frame) {
319 state->level_start_counter = SDL_GetPerformanceCounter();
320 state->ticks_left_at_level_start = (rem_min-1)*720 + rem_tick;
321 state->seconds_left_at_level_start = (1.0f / 60.0f) * (5 * state->ticks_left_at_level_start);
322 printf("Seconds left = %f\n", state->seconds_left_at_level_start);
323 state->already_had_first_frame = true;
324 } else if (rem_tick % 12 == 11) {
325 Uint64 current_counter = SDL_GetPerformanceCounter();
326 float actual_seconds_elapsed = (float)(current_counter - state->level_start_counter) / (float)SDL_GetPerformanceFrequency();
327
328 int ticks_left = (rem_min-1)*720 + rem_tick;
329 float game_seconds_left = (1.0f / 60.0f) * (5 * ticks_left);
330 float game_seconds_elapsed = state->seconds_left_at_level_start - game_seconds_left;
331
332 printf("rem_min: %d game elapsed (s): %.2f actual elapsed (s): %.2f delta: %.2f\n",
333 rem_min, game_seconds_elapsed, actual_seconds_elapsed, actual_seconds_elapsed - game_seconds_elapsed);
334 }
335
336 }
337 #endif
338
339 // seg003:04F8
340 // Returns a level number:
341 // - The current level if it was restarted.
342 // - The next level if the level was completed.
play_level_2()343 int __pascal far play_level_2() {
344 reset_timer(timer_1);
345 #ifdef CHECK_TIMING
346 test_timing_state_type test_timing_state = {0};
347 #endif
348 while (1) { // main loop
349 #ifdef USE_QUICKSAVE
350 check_quick_op();
351 #endif
352 #ifdef CHECK_TIMING
353 test_timings(&test_timing_state);
354 #endif
355
356 #ifdef USE_REPLAY
357 if (need_replay_cycle) replay_cycle();
358 #endif
359 if (Kid.sword == sword_2_drawn) {
360 // speed when fighting (smaller is faster)
361 set_timer_length(timer_1, /*6*/ custom->fight_speed);
362 } else {
363 // speed when not fighting (smaller is faster)
364 set_timer_length(timer_1, /*5*/ custom->base_speed);
365 }
366 guardhp_delta = 0;
367 hitp_delta = 0;
368 timers();
369 play_frame();
370
371 #ifdef USE_REPLAY
372 // At the exact "end of level" frame, preserve the seed to ensure reproducibility,
373 // regardless of how long the sound is still playing *after* this frame (torch animation modifies the seed!)
374 if (keep_last_seed == 1) {
375 preserved_seed = random_seed;
376 keep_last_seed = -1; // disable repeat
377 }
378 #endif
379
380 if (is_restart_level) {
381 is_restart_level = 0;
382 return current_level;
383 } else {
384 if (next_level == current_level || check_sound_playing()) {
385 draw_game_frame();
386 flash_if_hurt();
387 remove_flash_if_hurt();
388 do_simple_wait(timer_1);
389 } else {
390 stop_sounds();
391 hitp_beg_lev = hitp_max;
392 checkpoint = 0;
393
394 #ifdef USE_REPLAY
395 if (keep_last_seed == -1) {
396 random_seed = preserved_seed; // Ensure reproducibility in the next level.
397 keep_last_seed = 0;
398 }
399 #endif
400
401 return next_level;
402 }
403 }
404 }
405 }
406
407 // seg003:0576
redraw_at_char()408 void __pascal far redraw_at_char() {
409 short x_top_row;
410 short tile_col;
411 short tile_row;
412 short x_col_left;
413 short x_col_right;
414 if (Char.sword >= sword_2_drawn) {
415 // If char is holding sword, it makes redraw-area bigger.
416 if (Char.direction >= dir_0_right) {
417 if (++char_col_right > 9) char_col_right = 9;
418 // char_col_right = MIN(char_col_right + 1, 9);
419 } else {
420 if (--char_col_left < 0) char_col_left = 0;
421 // char_col_left = MAX(char_col_left - 1, 0);
422 }
423 }
424 if (Char.charid == charid_0_kid) {
425 x_top_row = MIN(char_top_row, prev_char_top_row);
426 x_col_right = MAX(char_col_right, prev_char_col_right);
427 x_col_left = MIN(char_col_left, prev_char_col_left);
428 } else {
429 x_top_row = char_top_row;
430 x_col_right = char_col_right;
431 x_col_left = char_col_left;
432 }
433 for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row) {
434 for (tile_col = x_col_left; tile_col <= x_col_right; ++tile_col) {
435 set_redraw_fore(get_tilepos(tile_col, tile_row), 1);
436 }
437 }
438 if (Char.charid == charid_0_kid) {
439 prev_char_top_row = char_top_row;
440 prev_char_col_right = char_col_right;
441 prev_char_col_left = char_col_left;
442 }
443 }
444
445 // seg003:0645
redraw_at_char2()446 void __pascal far redraw_at_char2() {
447 short char_action;
448 short char_frame;
449 void __pascal (* redraw_func)(short, byte);
450 char_action = Char.action;
451 char_frame = Char.frame;
452 redraw_func = &set_redraw2;
453 // frames 78..80: grab
454 if (char_frame < frame_78_jumphang || char_frame >= frame_80_jumphang) {
455 // frames 135..149: climb up
456 if (char_frame >= frame_137_climbing_3 && char_frame < frame_145_climbing_11) {
457 redraw_func = &set_redraw_floor_overlay;
458 } else {
459 // frames 102..106: fall
460 if (char_action != actions_2_hang_climb && char_action != actions_3_in_midair &&
461 char_action != actions_4_in_freefall && char_action != actions_6_hang_straight &&
462 (char_action != actions_5_bumped || char_frame < frame_102_start_fall_1 || char_frame > frame_106_fall)) {
463 return;
464 }
465 }
466 }
467 for (tile_col = char_col_right; tile_col >= char_col_left; --tile_col) {
468 if (char_action != 2) {
469 redraw_func(get_tilepos(tile_col, char_bottom_row), 1);
470 }
471 if (char_top_row != char_bottom_row) {
472 redraw_func(get_tilepos(tile_col, char_top_row), 1);
473 }
474 }
475 }
476
477 // seg003:0706
check_knock()478 void __pascal far check_knock() {
479 if (knock) {
480 do_knock(Char.room, Char.curr_row - (knock>0));
481 knock = 0;
482 }
483 }
484
485 // seg003:0735
timers()486 void __pascal far timers() {
487 if (united_with_shadow > 0) {
488 --united_with_shadow;
489 if (united_with_shadow == 0) {
490 --united_with_shadow;
491 }
492 }
493 if (guard_notice_timer > 0) {
494 --guard_notice_timer;
495 }
496 if (resurrect_time > 0) {
497 --resurrect_time;
498 }
499
500 if (fixes->fix_quicksave_during_feather) {
501 if (is_feather_fall > 0) {
502 --is_feather_fall;
503 if (is_feather_fall == 0) {
504 if (check_sound_playing()) {
505 stop_sounds();
506 }
507
508 //printf("slow fall ended at: rem_min = %d, rem_tick = %d\n", rem_min, rem_tick);
509 //printf("length = %d ticks\n", is_feather_fall);
510 #ifdef USE_REPLAY
511 if (recording) special_move = MOVE_EFFECT_END;
512 #endif
513 }
514 }
515 } else {
516 if (is_feather_fall) is_feather_fall++;
517
518 if (is_feather_fall && (!check_sound_playing() || is_feather_fall > 225)) {
519 //printf("slow fall ended at: rem_min = %d, rem_tick = %d\n", rem_min, rem_tick);
520 //printf("length = %d ticks\n", is_feather_fall);
521 #ifdef USE_REPLAY
522 if (recording) special_move = MOVE_EFFECT_END;
523 if (!replaying) // during replays, feather effect gets cancelled in do_replay_move()
524 #endif
525 is_feather_fall = 0;
526 }
527 }
528
529 // Special event: mouse
530 if (current_level == /*8*/ custom->mouse_level && Char.room == /*16*/ custom->mouse_room && leveldoor_open) {
531 ++leveldoor_open;
532 // time before mouse comes: 150/12=12.5 seconds
533 if (leveldoor_open == /*150*/ custom->mouse_delay) {
534 do_mouse();
535 }
536 }
537 }
538
539 // seg003:0798
check_mirror()540 void __pascal far check_mirror() {
541 word clip_top;
542 if (jumped_through_mirror == -1) {
543 jump_through_mirror();
544 } else {
545 if (get_tile_at_char() == tiles_13_mirror) {
546 loadkid();
547 load_frame();
548 check_mirror_image();
549 if (distance_mirror >= 0 && custom->show_mirror_image && Char.room == drawn_room) {
550 load_frame_to_obj();
551 reset_obj_clip();
552 clip_top = y_clip[Char.curr_row + 1];
553 if (clip_top < obj_y) {
554 obj_clip_top = clip_top;
555 obj_clip_left = (Char.curr_col << 5) + 9;
556 add_objtable(4); // mirror image
557 }
558 }
559 }
560 }
561 }
562
563 // seg003:080A
jump_through_mirror()564 void __pascal far jump_through_mirror() {
565 loadkid();
566 load_frame();
567 check_mirror_image();
568 jumped_through_mirror = 0;
569 Char.charid = charid_1_shadow;
570 play_sound(sound_45_jump_through_mirror); // jump through mirror
571 saveshad();
572 guardhp_max = guardhp_curr = hitp_max;
573 hitp_curr = 1;
574 draw_kid_hp(1, hitp_max);
575 draw_guard_hp(guardhp_curr, guardhp_max);
576 }
577
578 // seg003:085B
check_mirror_image()579 void __pascal far check_mirror_image() {
580 short distance;
581 short xpos;
582 xpos = x_bump[Char.curr_col + 5] + 10;
583 distance = distance_to_edge_weight();
584 if (Char.direction >= dir_0_right) {
585 distance = (~distance) + 14;
586 }
587 distance_mirror = distance - 2;
588 Char.x = (xpos << 1) - Char.x;
589 Char.direction = ~Char.direction;
590 }
591
592 // seg003:08AA
bump_into_opponent()593 void __pascal far bump_into_opponent() {
594 // This is called from play_kid_frame, so char=Kid, Opp=Guard
595 short distance;
596 if (can_guard_see_kid >= 2 &&
597 Char.sword == sword_0_sheathed && // Kid must not be in fighting pose
598 Opp.sword != sword_0_sheathed && // but Guard must
599 Opp.action < 2 &&
600 Char.direction != Opp.direction // must be facing toward each other
601 ) {
602 distance = char_opp_dist();
603 if (ABS(distance) <= 15) {
604
605 #ifdef FIX_PAINLESS_FALL_ON_GUARD
606 if (fixes->fix_painless_fall_on_guard) {
607 if (Char.fall_y >= 33) return; // don't bump; dead
608 else if (Char.fall_y >= 22) { // medium land
609 take_hp(1);
610 play_sound(sound_16_medium_land);
611 }
612 }
613 #endif
614
615 Char.y = y_land[Char.curr_row + 1];
616 Char.fall_y = 0;
617 seqtbl_offset_char(seq_47_bump); // bump into opponent
618 play_seq();
619 }
620 }
621 }
622
623 // seg003:0913
pos_guards()624 void __pascal far pos_guards() {
625 short guard_tile;
626 short room1;
627 for (room1 = 0; room1 < 24; ++room1) {
628 guard_tile = level.guards_tile[room1];
629 if (guard_tile < 30) {
630 level.guards_x[room1] = x_bump[guard_tile % 10 + 5] + 14;
631 level.guards_seq_hi[room1] = 0;
632 }
633 }
634 }
635
636 // seg003:0959
check_can_guard_see_kid()637 void __pascal far check_can_guard_see_kid() {
638 /*
639 Possible results in can_guard_see_kid:
640 0: Guard can't see Kid
641 1: Guard can see Kid, but won't come
642 2: Guard can see Kid, and will come
643 */
644 short kid_frame;
645 short left_pos;
646 short temp;
647 short right_pos;
648 kid_frame = Kid.frame;
649 if (Guard.charid == charid_24_mouse) {
650 // If the prince is fighting a guard, and the player does a quickload to a state where the prince is near the mouse, the prince would draw the sword.
651 // The following line prevents this.
652 can_guard_see_kid = 0;
653 return;
654 }
655 if ((Guard.charid != charid_1_shadow || current_level == 12) &&
656 // frames 217..228: going up on stairs
657 kid_frame != 0 && (kid_frame < frame_219_exit_stairs_3 || kid_frame >= 229) &&
658 Guard.direction != dir_56_none && Kid.alive < 0 && Guard.alive < 0 && Kid.room == Guard.room && Kid.curr_row == Guard.curr_row
659 ) {
660 can_guard_see_kid = 2;
661 left_pos = x_bump[Kid.curr_col + 5] + 7;
662 #ifdef FIX_DOORTOP_DISABLING_GUARD
663 if (fixes->fix_doortop_disabling_guard) {
664 // When the kid is hanging on the right side of a doortop, Kid.curr_col points at the doortop tile and a guard on the left side will see the prince.
665 // This fixes that.
666 if (Kid.action == actions_2_hang_climb || Kid.action == actions_6_hang_straight) left_pos += 14;
667 }
668 #endif
669 //printf("Kid.curr_col = %d, Kid.action = %d\n", Kid.curr_col, Kid.action);
670 right_pos = x_bump[Guard.curr_col + 5] + 7;
671 if (left_pos > right_pos) {
672 temp = left_pos;
673 left_pos = right_pos;
674 right_pos = temp;
675 }
676 // A chomper is on the left side of a tile, so it doesn't count.
677 if (get_tile_at_kid(left_pos) == tiles_18_chomper) {
678 left_pos += 14;
679 }
680 // A gate is on the right side of a tile, so it doesn't count.
681 if (get_tile_at_kid(right_pos) == tiles_4_gate
682 #ifdef FIX_DOORTOP_DISABLING_GUARD
683 || (fixes->fix_doortop_disabling_guard && (get_tile_at_kid(right_pos) == tiles_7_doortop_with_floor || get_tile_at_kid(right_pos) == tiles_12_doortop))
684 #endif
685 ) {
686 right_pos -= 14;
687 }
688 if (right_pos >= left_pos) {
689 while (left_pos <= right_pos) {
690 // Can't see through these tiles.
691 if (get_tile_at_kid(left_pos) == tiles_20_wall ||
692 curr_tile2 == tiles_7_doortop_with_floor ||
693 curr_tile2 == tiles_12_doortop
694 ) {
695 can_guard_see_kid = 0; return;
696 }
697 // Can see through these, but won't go through them.
698 if (curr_tile2 == tiles_11_loose ||
699 curr_tile2 == tiles_18_chomper ||
700 (curr_tile2 == tiles_4_gate && curr_room_modif[curr_tilepos] < 112) ||
701 !tile_is_floor(curr_tile2)
702 ) {
703 can_guard_see_kid = 1;
704 }
705 left_pos += 14;
706 }
707 }
708 } else {
709 can_guard_see_kid = 0;
710 }
711 }
712
713 // seg003:0A99
get_tile_at_kid(int xpos)714 byte __pascal far get_tile_at_kid(int xpos) {
715 return get_tile(Kid.room, get_tile_div_mod_m7(xpos), Kid.curr_row);
716 }
717
718 // seg003:0ABA
do_mouse()719 void __pascal far do_mouse() {
720 loadkid();
721 Char.charid = /*charid_24_mouse*/ custom->mouse_object;
722 Char.x = /*200*/ custom->mouse_start_x;
723 Char.curr_row = 0;
724 Char.y = y_land[Char.curr_row + 1];
725 Char.alive = -1;
726 Char.direction = dir_FF_left;
727 guardhp_curr = 1;
728 seqtbl_offset_char(seq_105_mouse_forward); // mouse forward
729 play_seq();
730 saveshad();
731 }
732
733 // seg003:0AFC
flash_if_hurt()734 int __pascal far flash_if_hurt() {
735 if (flash_time != 0) {
736 do_flash(flash_color);
737 return 1;
738 } else if (hitp_delta < 0) {
739 if (is_joyst_mode && enable_controller_rumble) {
740 if (sdl_haptic != NULL) {
741 SDL_HapticRumblePlay(sdl_haptic, 1.0, 100); // rumble at full strength for 100 milliseconds
742 #if SDL_VERSION_ATLEAST(2,0,9)
743 } else if (sdl_controller_ != NULL) {
744 SDL_GameControllerRumble(sdl_controller_, 0xFFFF, 0xFFFF, 100);
745 } else {
746 SDL_JoystickRumble(sdl_joystick_, 0xFFFF, 0xFFFF, 100);
747 #endif
748 }
749 }
750 do_flash(color_12_brightred); // red
751 return 1;
752 }
753 return 0; // not flashed
754 }
755
756 // seg003:0B1A
remove_flash_if_hurt()757 void __pascal far remove_flash_if_hurt() {
758 if (flash_time != 0) {
759 --flash_time;
760 } else {
761 if (hitp_delta >= 0) return;
762 }
763 remove_flash();
764 }
765