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:1888
24
extern const word seqtbl_offsets[];
25
 
26
// seg005:000A
27
void __pascal far seqtbl_offset_char(short seq_index) {
28
        Char.curr_seq = seqtbl_offsets[seq_index];
29
}
30
 
31
// seg005:001D
32
void __pascal far seqtbl_offset_opp(int seq_index) {
33
        Opp.curr_seq = seqtbl_offsets[seq_index];
34
}
35
 
36
// seg005:0030
37
void __pascal far do_fall() {
38
        if (is_screaming == 0 && Char.fall_y >= 31) {
39
                play_sound(sound_1_falling); // falling
40
                is_screaming = 1;
41
        }
42
        if ((word)y_land[Char.curr_row + 1] > (word)Char.y) {
43
                check_grab();
44
 
45
                #ifdef FIX_GLIDE_THROUGH_WALL
46
                if (fix_glide_through_wall) {
47
                        // Fix for the kid falling through walls after turning around while running (especially when weightless)
48
                        determine_col();
49
                        get_tile_at_char();
50
                        if (curr_tile2 == tiles_20_wall ||
51
                                        ((curr_tile2 == tiles_12_doortop || curr_tile2 == tiles_7_doortop_with_floor) &&
52
                                         Char.direction == dir_FF_left)
53
                        ) {
54
                                int delta_x = distance_to_edge_weight();
55
                                //printf("delta_x = %d\n", delta_x);
56
                                // When falling into a wall or doortop after turning or running, delta_x is likely to be either 8, 11 or 12
57
                                // Calling in_wall() here produces the desired fix (no glitching through wall), but only when delta_x >= 10
58
                                // The code below "emulates" in_wall() to always get the same effect as if delta_x == 10
59
                                // (which distance actually looks/behaves best is a matter of preference)
60
                                #define delta_x_reference 10
61
                                if (delta_x >= 8) {
62
                                        delta_x = -5 + delta_x - delta_x_reference;
63
                                        Char.x = (byte) char_dx_forward(delta_x);
64
 
65
                                        Char.fall_x = 0; // not in in_wall(), but we do need to cancel horizontal movement
66
                                }
67
                        }
68
                }
69
                #endif
70
 
71
        } else {
72
 
73
                #ifdef FIX_JUMP_THROUGH_WALL_ABOVE_GATE
74
                if (fix_jump_through_wall_above_gate) {
75
                        // At this point, Char.curr_col has not yet been updated since check_bumped()
76
                        // Basically, Char.curr_col is still set to the column of the wall itself (even though the kid
77
                        // may have 'bumped' against the wall, with Char.x being offset)
78
 
79
                        // To prevent in_wall() from being called we need to update Char.curr_col here.
80
                        // (in_wall() only makes things worse, because it tries to 'eject' the kid from the wall the wrong way.
81
                        // For this reason, the kid can end up behind a closed gate below, like is possible in Level 7)
82
 
83
                        // The fix is calling determine_col() here.
84
 
85
                        // One caveat: the kid can also land on the rightmost edge of a closed gate tile, when doing a running jump
86
                        // to the left from two floors up. This 'trick' can be used in original PoP, but is 'fixed' by this change.
87
                        // To still allow this trick to be possible, we can check that we not jumping into a gate tile.
88
                        // (By the way, strangely enough, in unmodified PoP the trick even works with a tapestry + floor tile...)
89
 
90
                        if (get_tile_at_char() != tiles_4_gate)
91
                                determine_col();
92
                }
93
                #endif
94
 
95
                if (get_tile_at_char() == tiles_20_wall) {
96
                        in_wall();
97
                }
98
                #ifdef FIX_DROP_THROUGH_TAPESTRY
99
                else if (fix_drop_through_tapestry && get_tile_at_char() == tiles_12_doortop && Char.direction == dir_FF_left) {
100
                        if (distance_to_edge_weight() >= 8) // only intervene if the kid is actually IN FRONT of the tapestry
101
                                in_wall();
102
                }
103
                #endif
104
 
105
                if (tile_is_floor(curr_tile2)) {
106
                        land();
107
                } else {
108
                        inc_curr_row();
109
                }
110
        }
111
}
112
 
113
// seg005:0090
114
void __pascal far land() {
115
        word seq_id;
116
        is_screaming = 0;
117
        Char.y = y_land[Char.curr_row + 1];
118
        if (get_tile_at_char() != tiles_2_spike) {
119
 
120
 
121
                if (! tile_is_floor(get_tile_infrontof_char()) &&
122
                        distance_to_edge_weight() < 3
123
                ) {
124
                        Char.x = char_dx_forward(-3);
125
                }
126
 
127
                #ifdef FIX_LAND_AGAINST_GATE_OR_TAPESTRY
128
                else if (fix_land_against_gate_or_tapestry) {
129
                        // A closed gate right in front of the landing spot should not behave like an open floor tile, but like a wall
130
                        // Similar for a tapestry tile (with floor)
131
                        get_tile_infrontof_char();
132
                        if (Char.direction == dir_FF_left && (
133
                                                        (curr_tile2 == tiles_4_gate && can_bump_into_gate()) ||
134
                                                        (curr_tile2 == tiles_7_doortop_with_floor))
135
                                        && distance_to_edge_weight() < 3
136
                        ) {
137
                                Char.x = char_dx_forward(-3);
138
                        }
139
                }
140
                #endif
141
                start_chompers();
142
        } else {
143
                // fell on spikes
144
                goto loc_5EE6;
145
        }
146
        if (Char.alive < 0) {
147
                // alive
148
                if ((distance_to_edge_weight() >= 12 &&
149
                        get_tile_behind_char() == tiles_2_spike) ||
150
                        get_tile_at_char() == tiles_2_spike
151
                ) {
152
                        // fell on spikes
153
                        loc_5EE6:
154
                        if (is_spike_harmful()) {
155
                                spiked();
156
                                return;
157
                        }
158
                        #ifdef FIX_SAFE_LANDING_ON_SPIKES
159
                        else if (fix_safe_landing_on_spikes && curr_room_modif[curr_tilepos] == 0) {
160
                                // spikes ARE dangerous, just not out yet.
161
                                spiked();
162
                                return;
163
                        }
164
                        #endif // FIX_SAFE_LANDING_ON_SPIKES
165
                }
166
                {
167
                        if (Char.fall_y < 22) {
168
                                // fell 1 row
169
                                loc_5EFD:
170
                                if (Char.charid >= charid_2_guard || Char.sword == sword_2_drawn) {
171
                                        Char.sword = sword_2_drawn;
172
                                        seq_id = seq_63_guard_stand_active; // stand active after landing
173
                                } else {
174
                                        seq_id = seq_17_soft_land; // crouch (soft land)
175
                                }
176
                                if (Char.charid == charid_0_kid) {
177
                                        play_sound(sound_17_soft_land); // soft land (crouch)
178
                                        is_guard_notice = 1;
179
                                }
180
                        } else if (Char.fall_y < 33) {
181
                                // fell 2 rows
182
                                if (Char.charid == charid_1_shadow) goto loc_5EFD;
183
                                if (Char.charid == charid_2_guard) goto loc_5F6C;
184
                                // kid (or skeleton (bug!))
185
                                if (! take_hp(1)) {
186
                                        // still alive
187
                                        play_sound(sound_16_medium_land); // medium land
188
                                        is_guard_notice = 1;
189
                                        seq_id = seq_20_medium_land; // medium land (lose 1 HP, crouch)
190
                                } else {
191
                                        // dead (this was the last HP)
192
                                        goto loc_5F75;
193
                                }
194
                        } else {
195
                                // fell 3 or more rows
196
                                goto loc_5F6C;
197
                        }
198
                }
199
        } else {
200
                // dead
201
                loc_5F6C:
202
                take_hp(100);
203
                loc_5F75:
204
                play_sound(sound_0_fell_to_death); // prince crashing into the floor
205
                seq_id = seq_22_crushed; // dead (after falling)
206
        }
207
        seqtbl_offset_char(seq_id);
208
        play_seq();
209
        Char.fall_y = 0;
210
}
211
 
212
// seg005:01B7
213
void __pascal far spiked() {
214
        // If someone falls into spikes, those spikes become harmless (to others).
215
        curr_room_modif[curr_tilepos] = 0xFF;
216
        Char.y = y_land[Char.curr_row + 1];
217
        Char.x = x_bump[tile_col + 5] + 10;
218
        Char.x = char_dx_forward(8);
219
        Char.fall_y = 0;
220
        play_sound(sound_48_spiked); // something spiked
221
        take_hp(100);
222
        seqtbl_offset_char(seq_51_spiked); // spiked
223
        play_seq();
224
}
225
 
226
// seg005:0213
227
void __pascal far control() {
228
        short char_frame;
229
        short char_action;
230
        char_frame = Char.frame;
231
        if (Char.alive >= 0) {
232
                if (char_frame == frame_15_stand || // stand
233
                        char_frame == frame_166_stand_inactive || // stand
234
                        char_frame == frame_158_stand_with_sword || // stand with sword
235
                        char_frame == frame_171_stand_with_sword // stand with sword
236
                ) {
237
                        seqtbl_offset_char(seq_71_dying); // dying (not stabbed)
238
                }
239
        } else {
240
                char_action = Char.action;
241
                if (char_action == actions_5_bumped ||
242
                        char_action == actions_4_in_freefall
243
                ) {
244
                        release_arrows();
245
                } else if (Char.sword == sword_2_drawn) {
246
                        control_with_sword();
247
                } else if (Char.charid >= charid_2_guard) {
248
                        control_guard_inactive();
249
                } else if (char_frame == frame_15_stand || // standing
250
                        (char_frame>= frame_50_turn && char_frame<53) // end of turning
251
                ) {
252
                        control_standing();
253
                } else if (char_frame == frame_48_turn) { // a frame in turning
254
                        control_turning();
255
                } else if (char_frame < 4) { // start run
256
                        control_startrun();
257
                } else if (char_frame >= frame_67_start_jump_up_1 && char_frame < frame_70_jumphang) { // start jump up
258
                        control_jumpup();
259
                } else if (char_frame < 15) { // running
260
                        control_running();
261
                } else if (char_frame >= frame_87_hanging_1 && char_frame < 100) { // hanging
262
                        control_hanging();
263
                } else if (char_frame == frame_109_crouch) { // crouching
264
                        control_crouched();
265
                }
266
 
267
                #ifdef ALLOW_CROUCH_AFTER_CLIMBING
268
                // When ducking with down+forward, give time to release the forward control (prevents unintended crouch-hops)
269
                else if (enable_crouch_after_climbing && Char.curr_seq >= seqtbl_offsets[seq_50_crouch] &&
270
                                Char.curr_seq < seqtbl_offsets[seq_49_stand_up_from_crouch]) // while stooping
271
                        if (control_forward < 1) control_forward = 0;
272
                #endif
273
 
274
                #ifdef FIX_MOVE_AFTER_DRINK
275
                if (fix_move_after_drink && char_frame >= frame_191_drink && char_frame <= frame_205_drink)
276
                        release_arrows();
277
                #endif
278
 
279
                #ifdef FIX_MOVE_AFTER_SHEATHE
280
                if (fix_move_after_sheathe &&
281
                                Char.curr_seq >= seqtbl_offsets[seq_92_put_sword_away] &&
282
                                Char.curr_seq < seqtbl_offsets[seq_93_put_sword_away_fast]
283
                )
284
                        release_arrows();
285
                #endif
286
        }
287
}
288
 
289
// seg005:02EB
290
void __pascal far control_crouched() {
291
        if (need_level1_music != 0 && current_level == 1) {
292
                // Special event: music when crouching
293
                if (! check_sound_playing()) {
294
                        if (need_level1_music == 1) {
295
                                play_sound(sound_25_presentation); // presentation (level 1 start)
296
                                need_level1_music = 2;
297
                        } else {
298
#ifdef USE_REPLAY
299
                                if (recording) special_move = MOVE_EFFECT_END;
300
                                if (!replaying) // during replays, crouch immobilization gets cancelled in do_replay_move()
301
#endif
302
                                need_level1_music = 0;
303
                        }
304
                }
305
        } else {
306
                need_level1_music = 0;
307
                if (control_shift2 < 0 && check_get_item()) return;
308
                if (control_y != 1) {
309
                        seqtbl_offset_char(seq_49_stand_up_from_crouch); // stand up from crouch
310
                } else {
311
                        if (control_forward < 0) {
312
                                control_forward = 1; // disable automatic repeat
313
                                seqtbl_offset_char(seq_79_crouch_hop); // crouch-hop
314
                        }
315
                }
316
        }
317
}
318
 
319
// seg005:0358
320
void __pascal far control_standing() {
321
        short var_2;
322
        if (control_shift2 < 0 && control_shift < 0 && check_get_item()) {
323
                return;
324
        }
325
        if (Char.charid != charid_0_kid && control_down < 0 && control_forward < 0) {
326
                draw_sword();
327
                return;
328
        } //else
329
        if (have_sword) {
330
                if (offguard != 0 && control_shift >= 0) goto loc_6213;
331
                if (can_guard_see_kid >= 2) {
332
                        var_2 = char_opp_dist();
333
                        if (var_2 >= -10 && var_2 < 90) {
334
                                holding_sword = 1;
335
                                if ((word)var_2 < (word)-6) {
336
                                        if (Opp.charid == charid_1_shadow &&
337
                                                (Opp.action == actions_3_in_midair || (Opp.frame >= frame_107_fall_land_1 && Opp.frame < 118))
338
                                        ) {
339
                                                offguard = 0;
340
                                        } else {
341
                                                draw_sword();
342
                                                return;
343
                                        }
344
                                } else {
345
                                        back_pressed();
346
                                        return;
347
                                }
348
                        }
349
                } else {
350
                        offguard = 0;
351
                }
352
        }
353
        if (control_shift < 0) {
354
                if (control_backward < 0) {
355
                        back_pressed();
356
                } else if (control_up < 0) {
357
                        up_pressed();
358
                } else if (control_down < 0) {
359
                        down_pressed();
360
                } else if (control_x < 0 && control_forward < 0) {
361
                        safe_step();
362
                }
363
        } else loc_6213: if (control_forward < 0) {
364
                if (is_keyboard_mode && control_up < 0) {
365
                        standing_jump();
366
                } else {
367
                        forward_pressed();
368
                }
369
        } else if (control_backward < 0) {
370
                back_pressed();
371
        } else if (control_up < 0) {
372
                if (is_keyboard_mode && control_forward < 0) {
373
                        standing_jump();
374
                } else {
375
                        up_pressed();
376
                }
377
        } else if (control_down < 0) {
378
                down_pressed();
379
        } else if (control_x < 0) {
380
                forward_pressed();
381
        }
382
}
383
 
384
// seg005:0482
385
void __pascal far up_pressed() {
386
        int leveldoor_tilepos = -1;
387
        if (get_tile_at_char() == tiles_16_level_door_left) leveldoor_tilepos = curr_tilepos;
388
        else if (get_tile_behind_char() == tiles_16_level_door_left) leveldoor_tilepos = curr_tilepos;
389
        else if (get_tile_infrontof_char() == tiles_16_level_door_left) leveldoor_tilepos = curr_tilepos;
390
        if ((leveldoor_tilepos != -1) &&
391
                level.start_room != drawn_room &&
392
                curr_room_modif[leveldoor_tilepos] >= 42 // this door must be fully open
393
        ){
394
                go_up_leveldoor();
395
        } else {
396
                if (control_x < 0) {
397
                        standing_jump();
398
                } else {
399
                        check_jump_up();
400
                }
401
        }
402
}
403
 
404
// seg005:04C7
405
void __pascal far down_pressed() {
406
        control_down = 1; // disable automatic repeat
407
        if (! tile_is_floor(get_tile_infrontof_char()) &&
408
                distance_to_edge_weight() < 3
409
        ) {
410
                Char.x = char_dx_forward(5);
411
                load_fram_det_col();
412
        } else {
413
                if (! tile_is_floor(get_tile_behind_char()) &&
414
                        distance_to_edge_weight() >= 8
415
                ) {
416
                        through_tile = get_tile_behind_char();
417
                        get_tile_at_char();
418
                        if (can_grab() &&
419
                                #ifdef ALLOW_CROUCH_AFTER_CLIMBING
420
                                (!(enable_crouch_after_climbing && control_forward == -1)) &&
421
                                #endif
422
                                (Char.direction >= dir_0_right ||
423
                                get_tile_at_char() != tiles_4_gate ||
424
                                curr_room_modif[curr_tilepos] >> 2 >= 6)
425
                        ) {
426
                                Char.x = char_dx_forward(distance_to_edge_weight() - 9);
427
                                seqtbl_offset_char(seq_68_climb_down); // climb down
428
                        } else {
429
                                crouch();
430
                        }
431
                } else {
432
                        crouch();
433
                }
434
        }
435
}
436
 
437
// seg005:0574
438
void __pascal far go_up_leveldoor() {
439
        Char.x = x_bump[tile_col + 5] + 10;
440
        Char.direction = dir_FF_left; // right
441
        seqtbl_offset_char(seq_70_go_up_on_level_door); // go up on level door
442
}
443
 
444
// seg005:058F
445
void __pascal far control_turning() {
446
        if (control_shift >= 0 && control_x < 0 && control_y >= 0) {
447
                seqtbl_offset_char(seq_43_start_run_after_turn); // start run and run (after turning)
448
        }
449
 
450
        // Added:
451
        // When using a joystick, the kid may sometimes jump/duck/turn unintendedly after turning around.
452
        // To prevent this: clear the remembered controls, so that if the stick has already moved to another/neutral position,
453
        // the kid will not jump, duck, or turn again.
454
        if (is_joyst_mode) {
455
                if (control_up < 0 && control_y >= 0) {
456
                        control_up = 0;
457
                }
458
                if (control_down < 0 && control_y <= 0) {
459
                        control_down = 0;
460
                }
461
                if (control_backward < 0 && control_x == 0) {
462
                        control_backward = 0;
463
                }
464
        }
465
}
466
 
467
// seg005:05AD
468
void __pascal far crouch() {
469
        seqtbl_offset_char(seq_50_crouch); // crouch
470
        control_down = release_arrows();
471
}
472
 
473
// seg005:05BE
474
void __pascal far back_pressed() {
475
        word seq_id;
476
        control_backward = release_arrows();
477
        // After turn, Kid will draw sword if ...
478
        if (have_sword == 0 || // if Kid has sword
479
                can_guard_see_kid < 2 || // and can see Guard
480
                char_opp_dist() > 0 || // and Guard was behind him
481
                distance_to_edge_weight() < 2
482
        ) {
483
                seq_id = seq_5_turn; // turn
484
        } else {
485
                Char.sword = sword_2_drawn;
486
                offguard = 0;
487
                seq_id = seq_89_turn_draw_sword; // turn and draw sword
488
        }
489
        seqtbl_offset_char(seq_id);
490
}
491
 
492
// seg005:060F
493
void __pascal far forward_pressed() {
494
        short distance;
495
        distance = get_edge_distance();
496
        #ifdef ALLOW_CROUCH_AFTER_CLIMBING
497
        if (enable_crouch_after_climbing && control_down < 0) {
498
                down_pressed();
499
                control_forward = 0;
500
                return;
501
        }
502
        #endif
503
 
504
        if (edge_type == 1 && curr_tile2 != tiles_18_chomper && distance < 8) {
505
                // If char is near a wall, step instead of run.
506
                if (control_forward < 0) {
507
                        safe_step();
508
                }
509
        } else {
510
                seqtbl_offset_char(seq_1_start_run); // start run and run
511
        }
512
}
513
 
514
// seg005:0649
515
void __pascal far control_running() {
516
        if (control_x == 0 && (Char.frame == frame_7_run || Char.frame == frame_11_run)) {
517
                control_forward = release_arrows();
518
                seqtbl_offset_char(seq_13_stop_run); // stop run
519
        } else if (control_x > 0) {
520
                control_backward = release_arrows();
521
                seqtbl_offset_char(seq_6_run_turn); // run-turn
522
        } else if (control_y < 0 && control_up < 0) {
523
                run_jump();
524
        } else if (control_down < 0) {
525
                control_down = 1; // disable automatic repeat
526
                seqtbl_offset_char(seq_26_crouch_while_running); // crouch while running
527
        }
528
}
529
 
530
// seg005:06A8
531
void __pascal far safe_step() {
532
        short distance;
533
        control_shift2 = 1; // disable automatic repeat
534
        control_forward = 1; // disable automatic repeat
535
        distance = get_edge_distance();
536
        if (distance) {
537
                Char.repeat = 1;
538
                seqtbl_offset_char(distance + 28); // 29..42: safe step to edge
539
        } else if (edge_type != 1 && Char.repeat != 0) {
540
                Char.repeat = 0;
541
                seqtbl_offset_char(seq_44_step_on_edge); // step on edge
542
        } else {
543
                seqtbl_offset_char(seq_39_safe_step_11); // unsafe step (off ledge)
544
        }
545
}
546
 
547
// seg005:06F0
548
int __pascal far check_get_item() {
549
        if (get_tile_at_char() == tiles_10_potion ||
550
                curr_tile2 == tiles_22_sword
551
        ) {
552
                if (! tile_is_floor(get_tile_behind_char())) {
553
                        return 0;
554
                }
555
                Char.x = char_dx_forward(-14);
556
                load_fram_det_col();
557
        }
558
        if (get_tile_infrontof_char() == tiles_10_potion ||
559
                curr_tile2 == tiles_22_sword
560
        ) {
561
                get_item();
562
                return 1;
563
        }
564
        return 0;
565
}
566
 
567
// seg005:073E
568
void __pascal far get_item() {
569
        short distance;
570
        if (Char.frame != frame_109_crouch) { // crouching
571
                distance = get_edge_distance();
572
                if (edge_type != 2) {
573
                        Char.x = char_dx_forward(distance);
574
                }
575
                if (Char.direction >= dir_0_right) {
576
                        Char.x = char_dx_forward((curr_tile2 == tiles_10_potion) - 2);
577
                }
578
                crouch();
579
        } else if (curr_tile2 == tiles_22_sword) {
580
                do_pickup(-1);
581
                seqtbl_offset_char(seq_91_get_sword); // get sword
582
        } else { // potion
583
                do_pickup(curr_room_modif[curr_tilepos] >> 3);
584
                seqtbl_offset_char(seq_78_drink); // drink
585
#ifdef USE_COPYPROT
586
                if (current_level == 15) {
587
                        short index;
588
                        for (index = 0; index < 14; ++index) {
589
                                // remove letter on potions level
590
                                if (copyprot_room[index] == curr_room &&
591
                                        copyprot_tile[index] == curr_tilepos
592
                                ) {
593
                                        copyprot_room[index] = 0;
594
                                        break;
595
                                }
596
                        }
597
                }
598
#endif
599
        }
600
}
601
 
602
// seg005:07FF
603
void __pascal far control_startrun() {
604
        if (control_y < 0 && control_x < 0) {
605
                standing_jump();
606
        }
607
}
608
 
609
// seg005:0812
610
void __pascal far control_jumpup() {
611
        if (control_x < 0 || control_forward < 0) {
612
                standing_jump();
613
        }
614
}
615
 
616
// seg005:0825
617
void __pascal far standing_jump() {
618
        control_up = control_forward = 1; // disable automatic repeat
619
        seqtbl_offset_char(seq_3_standing_jump); // standing jump
620
}
621
 
622
// seg005:0836
623
void __pascal far check_jump_up() {
624
        control_up = release_arrows();
625
        through_tile = get_tile_above_char();
626
        get_tile_front_above_char();
627
        if (can_grab()) {
628
                grab_up_with_floor_behind();
629
        } else {
630
                through_tile = get_tile_behind_above_char();
631
                get_tile_above_char();
632
                if (can_grab()) {
633
                        jump_up_or_grab();
634
                } else {
635
                        jump_up();
636
                }
637
        }
638
}
639
 
640
// seg005:087B
641
void __pascal far jump_up_or_grab() {
642
        short distance;
643
        distance = distance_to_edge_weight();
644
        if (distance < 6) {
645
                jump_up();
646
        } else if (! tile_is_floor(get_tile_behind_char())) {
647
                // There is not floor behind char.
648
                grab_up_no_floor_behind();
649
        } else {
650
                // There is floor behind char, go back a bit.
651
                Char.x = char_dx_forward(distance - 14);
652
                load_fram_det_col();
653
                grab_up_with_floor_behind();
654
        }
655
}
656
 
657
// seg005:08C7
658
void __pascal far grab_up_no_floor_behind() {
659
        get_tile_above_char();
660
        Char.x = char_dx_forward(distance_to_edge_weight() - 10);
661
        seqtbl_offset_char(seq_16_jump_up_and_grab); // jump up and grab (no floor behind)
662
}
663
 
664
// seg005:08E6
665
void __pascal far jump_up() {
666
        short distance;
667
        control_up = release_arrows();
668
        distance = get_edge_distance();
669
        if (distance < 4 && edge_type == 1) {
670
                Char.x = char_dx_forward(distance - 3);
671
        }
672
        #ifdef FIX_JUMP_DISTANCE_AT_EDGE
673
        // When climbing up two floors, turning around and jumping upward, the kid falls down.
674
        // This fix makes the workaround of Trick 25 unnecessary.
675
        if (fix_jump_distance_at_edge && distance == 3 && edge_type == 0) {
676
                Char.x = char_dx_forward(-1);
677
        }
678
        #endif
679
        get_tile(Char.room, get_tile_div_mod(back_delta_x(0) + dx_weight() - 6), Char.curr_row - 1);
680
        if (curr_tile2 != tiles_20_wall && ! tile_is_floor(curr_tile2)) {
681
                seqtbl_offset_char(seq_28_jump_up_with_nothing_above); // jump up with nothing above
682
        } else {
683
                seqtbl_offset_char(seq_14_jump_up_into_ceiling); // jump up with wall or floor above
684
        }
685
}
686
 
687
// seg005:0968
688
void __pascal far control_hanging() {
689
        if (Char.alive < 0) {
690
                if (grab_timer == 0 && control_y < 0) {
691
                        can_climb_up();
692
                } else if (control_shift < 0) {
693
                        // hanging against a wall or a doortop
694
                        if (Char.action != actions_6_hang_straight &&
695
                                (get_tile_at_char() == tiles_20_wall ||
696
                                (Char.direction == dir_FF_left && ( // facing left
697
                                        curr_tile2 == tiles_7_doortop_with_floor ||
698
                                        curr_tile2 == tiles_12_doortop
699
                                )))
700
                        ) {
701
                                if (grab_timer == 0) {
702
                                        play_sound(sound_8_bumped); // touching a wall (hang against wall)
703
                                }
704
                                seqtbl_offset_char(seq_25_hang_against_wall); // hang against wall (straight)
705
                        } else {
706
                                if (! tile_is_floor(get_tile_above_char())) {
707
                                        hang_fall();
708
                                }
709
                        }
710
                } else {
711
                        hang_fall();
712
                }
713
        } else {
714
                hang_fall();
715
        }
716
}
717
 
718
// seg005:09DF
719
void __pascal far can_climb_up() {
720
        short seq_id;
721
        seq_id = seq_10_climb_up; // climb up
722
        control_up = control_shift2 = release_arrows();
723
        get_tile_above_char();
724
        if (((curr_tile2 == tiles_13_mirror || curr_tile2 == tiles_18_chomper) &&
725
                Char.direction == dir_0_right) ||
726
                (curr_tile2 == tiles_4_gate && Char.direction != dir_0_right &&
727
                curr_room_modif[curr_tilepos] >> 2 < 6)
728
        ) {
729
                seq_id = seq_73_climb_up_to_closed_gate; // climb up to closed gate and down
730
        }
731
        seqtbl_offset_char(seq_id);
732
}
733
 
734
// seg005:0A46
735
void __pascal far hang_fall() {
736
        control_down = release_arrows();
737
        if (! tile_is_floor(get_tile_behind_char()) &&
738
                ! tile_is_floor(get_tile_at_char())
739
        ) {
740
                seqtbl_offset_char(seq_23_release_ledge_and_fall); // release ledge and fall
741
        } else {
742
                if (get_tile_at_char() == tiles_20_wall ||
743
                        (Char.direction < dir_0_right && ( // looking left
744
                                curr_tile2 == tiles_7_doortop_with_floor ||
745
                                curr_tile2 == tiles_12_doortop
746
                        ))
747
                ) {
748
                        Char.x = char_dx_forward(-7);
749
                }
750
                seqtbl_offset_char(seq_11_release_ledge_and_land); // end of climb down
751
        }
752
}
753
 
754
// seg005:0AA8
755
void __pascal far grab_up_with_floor_behind() {
756
        short distance;
757
        distance = distance_to_edge_weight();
758
 
759
        // The global variable edge_type (which we need!) gets set as a side effect of get_edge_distance()
760
        short edge_distance = get_edge_distance();
761
        //printf("Distance to edge weight: %d\tedge type: %d\tedge distance: %d\n", distance, edge_type, edge_distance);
762
 
763
        #ifdef FIX_EDGE_DISTANCE_CHECK_WHEN_CLIMBING
764
        // When climbing to a higher floor, the game unnecessarily checks how far away the edge below is;
765
        // This contributes to sometimes "teleporting" considerable distances when climbing from firm ground
766
        #define JUMP_STRAIGHT_CONDITION (fix_edge_distance_check_when_climbing)                                         \
767
                                                                        ? (distance < 4 && edge_type != 1)                                                                      \
768
                                                                        : (distance < 4 && edge_distance < 4 && edge_type != 1)
769
        #else
770
        #define JUMP_STRAIGHT_CONDITION distance < 4 && edge_distance < 4 && edge_type != 1
771
        #endif
772
 
773
        if (JUMP_STRAIGHT_CONDITION) {
774
                Char.x = char_dx_forward(distance);
775
                seqtbl_offset_char(seq_8_jump_up_and_grab_straight); // jump up and grab (when?)
776
        } else {
777
                Char.x = char_dx_forward(distance - 4);
778
                seqtbl_offset_char(seq_24_jump_up_and_grab_forward); // jump up and grab (with floor behind)
779
        }
780
}
781
 
782
// seg005:0AF7
783
void __pascal far run_jump() {
784
        short var_2;
785
        short xpos;
786
        short col;
787
        short var_8;
788
        if (Char.frame >= frame_7_run) {
789
                // Align Kid to edge of floor.
790
                xpos = char_dx_forward(4);
791
                col = get_tile_div_mod_m7(xpos);
792
                for (var_2 = 0; var_2 < 2; ++var_2) {
793
                        col += dir_front[Char.direction + 1];
794
                        get_tile(Char.room, col, Char.curr_row);
795
                        if (curr_tile2 == tiles_2_spike || ! tile_is_floor(curr_tile2)) {
796
                                var_8 = distance_to_edge(xpos) + 14 * var_2 - 14;
797
                                if ((word)var_8 < (word)-8 || var_8 >= 2) {
798
                                        if (var_8 < 128) return;
799
                                        var_8 = -3;
800
                                }
801
                                Char.x = char_dx_forward(var_8 + 4);
802
                                break;
803
                        }
804
                }
805
                control_up = release_arrows(); // disable automatic repeat
806
                seqtbl_offset_char(seq_4_run_jump); // run-jump
807
        }
808
}
809
 
810
// sseg005:0BB5
811
void __pascal far back_with_sword() {
812
        short frame;
813
        frame = Char.frame;
814
        if (frame == frame_158_stand_with_sword || frame == frame_170_stand_with_sword || frame == frame_171_stand_with_sword) {
815
                control_backward = 1; // disable automatic repeat
816
                seqtbl_offset_char(seq_57_back_with_sword); // back with sword
817
        }
818
}
819
 
820
// seg005:0BE3
821
void __pascal far forward_with_sword() {
822
        short frame;
823
        frame = Char.frame;
824
        if (frame == frame_158_stand_with_sword || frame == frame_170_stand_with_sword || frame == frame_171_stand_with_sword) {
825
                control_forward = 1; // disable automatic repeat
826
                if (Char.charid != charid_0_kid) {
827
                        seqtbl_offset_char(seq_56_guard_forward_with_sword); // forward with sword (Guard)
828
                } else {
829
                        seqtbl_offset_char(seq_86_forward_with_sword); // forward with sword (Kid)
830
                }
831
        }
832
}
833
 
834
// seg005:0C1D
835
void __pascal far draw_sword() {
836
        word seq_id;
837
        seq_id = seq_55_draw_sword; // draw sword
838
        control_forward = control_shift2 = release_arrows();
839
#ifdef FIX_UNINTENDED_SWORD_STRIKE
840
        if (fix_unintended_sword_strike) {
841
                ctrl1_shift2 = 1; // prevent restoring control_shift2 to -1 in rest_ctrl_1()
842
        }
843
#endif
844
        if (Char.charid == charid_0_kid) {
845
                play_sound(sound_19_draw_sword); // taking out the sword
846
                offguard = 0;
847
        } else if (Char.charid != charid_1_shadow) {
848
                seq_id = seq_90_en_garde; // stand active
849
        }
850
        Char.sword = sword_2_drawn;
851
        seqtbl_offset_char(seq_id);
852
}
853
 
854
// seg005:0C67
855
void __pascal far control_with_sword() {
856
        short distance;
857
        if (Char.action < actions_2_hang_climb) {
858
                if (get_tile_at_char() == tiles_11_loose || can_guard_see_kid >= 2) {
859
                        distance = char_opp_dist();
860
                        if ((word)distance < (word)90) {
861
                                swordfight();
862
                                return;
863
                        } else if (distance < 0) {
864
                                if ((word)distance < (word)-4) {
865
                                        seqtbl_offset_char(seq_60_turn_with_sword); // turn with sword (after switching places)
866
                                        return;
867
                                } else {
868
                                        swordfight();
869
                                        return;
870
                                }
871
                        }
872
                } /*else*/ {
873
                        if (Char.charid == charid_0_kid && Char.alive < 0) {
874
                                holding_sword = 0;
875
                        }
876
                        if (Char.charid < charid_2_guard) {
877
                                // frame 171: stand with sword
878
                                if (Char.frame == frame_171_stand_with_sword) {
879
                                        Char.sword = sword_0_sheathed;
880
                                        seqtbl_offset_char(seq_92_put_sword_away); // put sword away (Guard died)
881
                                }
882
                        } else {
883
                                swordfight();
884
                        }
885
                }
886
        }
887
}
888
 
889
// seg005:0CDB
890
void __pascal far swordfight() {
891
        short frame;
892
        short seq_id;
893
        short charid;
894
        frame = Char.frame;
895
        charid = Char.charid;
896
        // frame 161: parry
897
        if (frame == frame_161_parry && control_shift2 >= 0) {
898
                seqtbl_offset_char(seq_57_back_with_sword); // back with sword (when parrying)
899
                return;
900
        } else if (control_shift2 < 0) {
901
                if (charid == charid_0_kid) {
902
                        kid_sword_strike = 15;
903
                }
904
                sword_strike();
905
                if (control_shift2 == 1) return;
906
        }
907
        if (control_down < 0) {
908
                if (frame == frame_158_stand_with_sword || frame == frame_170_stand_with_sword || frame == frame_171_stand_with_sword) {
909
                        control_down = 1; // disable automatic repeat
910
                        Char.sword = sword_0_sheathed;
911
                        if (charid == charid_0_kid) {
912
                                offguard = 1;
913
                                guard_refrac = 9;
914
                                holding_sword = 0;
915
                                seq_id = seq_93_put_sword_away_fast; // put sword away fast (down pressed)
916
                        } else if (charid == charid_1_shadow) {
917
                                seq_id = seq_92_put_sword_away; // put sword away
918
                        } else {
919
                                seq_id = seq_87_guard_become_inactive; // stand inactive (when Kid leaves sight)
920
                        }
921
                        seqtbl_offset_char(seq_id);
922
                }
923
        } else if (control_up < 0) {
924
                parry();
925
        } else if (control_forward < 0) {
926
                forward_with_sword();
927
        } else if (control_backward < 0) {
928
                back_with_sword();
929
        }
930
}
931
 
932
// seg005:0DB0
933
void __pascal far sword_strike() {
934
        short frame;
935
        short seq_id;
936
        frame = Char.frame;
937
        if (frame == frame_157_walk_with_sword || // walk with sword
938
                frame == frame_158_stand_with_sword || // stand with sword
939
                frame == frame_170_stand_with_sword || // stand with sword
940
                frame == frame_171_stand_with_sword || // stand with sword
941
                frame == frame_165_walk_with_sword // walk with sword
942
        ) {
943
                if (Char.charid == charid_0_kid) {
944
                        seq_id = seq_75_strike; // strike with sword (Kid)
945
                } else {
946
                        seq_id = seq_58_guard_strike; // strike with sword (Guard)
947
                }
948
        } else if (frame == frame_150_parry || frame == frame_161_parry) { // parry
949
                seq_id = seq_66_strike_after_parry; // strike with sword after parrying
950
        } else {
951
                return;
952
        }
953
        control_shift2 = 1; // disable automatic repeat
954
        seqtbl_offset_char(seq_id);
955
}
956
 
957
// seg005:0E0F
958
void __pascal far parry() {
959
        short opp_frame;
960
        short char_frame;
961
        short var_6;
962
        short seq_id;
963
        short char_charid;
964
        char_frame = Char.frame;
965
        opp_frame = Opp.frame;
966
        char_charid = Char.charid;
967
        seq_id = seq_62_parry; // defend (parry) with sword
968
        var_6 = 0;
969
        if (
970
                char_frame == frame_158_stand_with_sword || // stand with sword
971
                char_frame == frame_170_stand_with_sword || // stand with sword
972
                char_frame == frame_171_stand_with_sword || // stand with sword
973
                char_frame == frame_168_back || // back?
974
                char_frame == frame_165_walk_with_sword // walk with sword
975
        ) {
976
                if (char_opp_dist() >= 32 && char_charid != charid_0_kid) {
977
                        back_with_sword();
978
                        return;
979
                } else if (char_charid == charid_0_kid) {
980
                        if (opp_frame == frame_168_back) return;
981
                        if (opp_frame != frame_151_strike_1 &&
982
                                opp_frame != frame_152_strike_2 &&
983
                                opp_frame != frame_162_block_to_strike
984
                        ) {
985
                                if (opp_frame == frame_153_strike_3) { // strike
986
                                        var_6 = 1;
987
                                } else
988
                                if (char_charid != charid_0_kid) {
989
                                        back_with_sword();
990
                                        return;
991
                                }
992
                        }
993
                } else {
994
                        if (opp_frame != frame_152_strike_2) return;
995
                }
996
        } else {
997
                if (char_frame != frame_167_blocked) return;
998
                seq_id = seq_61_parry_after_strike; // parry after striking with sword
999
        }
1000
        control_up = 1; // disable automatic repeat
1001
        seqtbl_offset_char(seq_id);
1002
        if (var_6) {
1003
                play_seq();
1004
        }
1005
}