Subversion Repositories Games.Prince of Persia

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
}