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:432F
24
sbyte bump_col_left_of_wall;
25
// data:436E
26
sbyte bump_col_right_of_wall;
27
// data:4C0A
28
sbyte right_checked_col;
29
// data:408A
30
sbyte left_checked_col;
31
 
32
 
33
// data:4C0C
34
short coll_tile_left_xpos;
35
// These two arrays are indexed with the return value of wall_type.
36
// data:24BA
37
const sbyte wall_dist_from_left[] = {0, 10, 0, -1, 0, 0};
38
// data:24C0
39
const sbyte wall_dist_from_right[] = {0, 0, 10, 13, 0, 0};
40
 
41
// seg004:0004
42
void __pascal far check_collisions() {
43
        short column;
44
        bump_col_left_of_wall = bump_col_right_of_wall = -1;
45
        if (Char.action == actions_7_turn) return;
46
        collision_row = Char.curr_row;
47
        move_coll_to_prev();
48
        prev_collision_row = collision_row;
49
        right_checked_col = MIN(get_tile_div_mod_m7(char_x_right_coll) + 2, 11);
50
        left_checked_col = get_tile_div_mod_m7(char_x_left_coll) - 1;
51
        get_row_collision_data(collision_row    , curr_row_coll_room, curr_row_coll_flags);
52
        get_row_collision_data(collision_row + 1, below_row_coll_room, below_row_coll_flags);
53
        get_row_collision_data(collision_row - 1, above_row_coll_room, above_row_coll_flags);
54
        for (column = 9; column >= 0; --column) {
55
                if (curr_row_coll_room[column] >= 0 &&
56
                        prev_coll_room[column] == curr_row_coll_room[column]
57
                ) {
58
                        // char bumps into left of wall
59
                        if (
60
                                (prev_coll_flags[column] & 0x0F) == 0 &&
61
                                (curr_row_coll_flags[column] & 0x0F) != 0
62
                        ) {
63
                                bump_col_left_of_wall = column;
64
                        }
65
                        // char bumps into right of wall
66
                        if (
67
                                (prev_coll_flags[column] & 0xF0) == 0 &&
68
                                (curr_row_coll_flags[column] & 0xF0) != 0
69
                        ) {
70
                                bump_col_right_of_wall = column;
71
                        }
72
                }
73
        }
74
}
75
 
76
// seg004:00DF
77
void __pascal far move_coll_to_prev() {
78
        sbyte* row_coll_room_ptr;
79
        byte* row_coll_flags_ptr;
80
        short column;
81
        if (collision_row     == prev_collision_row ||
82
                collision_row + 3 == prev_collision_row ||
83
                collision_row - 3 == prev_collision_row
84
        ) {
85
                row_coll_room_ptr = curr_row_coll_room;
86
                row_coll_flags_ptr = curr_row_coll_flags;
87
        } else if (
88
                collision_row + 1 == prev_collision_row ||
89
                collision_row - 2 == prev_collision_row
90
        ) {
91
                row_coll_room_ptr = above_row_coll_room;
92
                row_coll_flags_ptr = above_row_coll_flags;
93
        } else {
94
                row_coll_room_ptr = below_row_coll_room;
95
                row_coll_flags_ptr = below_row_coll_flags;
96
        }
97
        for (column = 0; column < 10; ++column) {
98
                prev_coll_room[column] = row_coll_room_ptr[column];
99
                prev_coll_flags[column] = row_coll_flags_ptr[column];
100
                below_row_coll_room[column] = -1;
101
                above_row_coll_room[column] = -1;
102
                curr_row_coll_room[column] = -1;
103
                // bugfix:
104
                curr_row_coll_flags[column] = 0;
105
                below_row_coll_flags[column] = 0;
106
                above_row_coll_flags[column] = 0;
107
        }
108
}
109
 
110
// seg004:0185
111
void __pascal far get_row_collision_data(short row, sbyte *row_coll_room_ptr, byte *row_coll_flags_ptr) {
112
        short right_wall_xpos;
113
        byte curr_flags;
114
        short room;
115
        short column;
116
        short left_wall_xpos;
117
        room = Char.room;
118
        coll_tile_left_xpos = x_bump[left_checked_col + 5] + 7;
119
        for (column = left_checked_col; column <= right_checked_col; ++column) {
120
                left_wall_xpos = get_left_wall_xpos(room, column, row);
121
                right_wall_xpos = get_right_wall_xpos(room, column, row);
122
                // char bumps into left of wall
123
                curr_flags = (left_wall_xpos < char_x_right_coll) * 0x0F;
124
                // char bumps into right of wall
125
                curr_flags |= (right_wall_xpos > char_x_left_coll) * 0xF0;
126
                row_coll_flags_ptr[tile_col] = curr_flags;
127
                row_coll_room_ptr[tile_col] = curr_room;
128
                coll_tile_left_xpos += 14;
129
        }
130
}
131
 
132
// seg004:0226
133
int __pascal far get_left_wall_xpos(int room,int column,int row) {
134
        short type;
135
        type = wall_type(get_tile(room, column, row));
136
        if (type) {
137
                return wall_dist_from_left[type] + coll_tile_left_xpos;
138
        } else {
139
                return 0xFF;
140
        }
141
}
142
 
143
// seg004:025F
144
int __pascal far get_right_wall_xpos(int room,int column,int row) {
145
        short type;
146
        type = wall_type(get_tile(room, column, row));
147
        if (type) {
148
                return coll_tile_left_xpos - wall_dist_from_right[type] + 13;
149
        } else {
150
                return 0;
151
        }
152
}
153
 
154
// seg004:029D
155
void __pascal far check_bumped() {
156
        if (
157
                Char.action != actions_2_hang_climb &&
158
                Char.action != actions_6_hang_straight &&
159
                // frames 135..149: climb up
160
                (Char.frame < frame_135_climbing_1 || Char.frame >= 149)
161
        ) {
162
#ifdef FIX_TWO_COLL_BUG
163
                if (bump_col_left_of_wall >= 0) {
164
                        check_bumped_look_right();
165
                        if (!fix_two_coll_bug) return; // check for the left-oriented collision only with the fix enabled
166
                }
167
                if (bump_col_right_of_wall >= 0) {
168
                        check_bumped_look_left();
169
                }
170
#else
171
                if (bump_col_left_of_wall >= 0) {
172
                        check_bumped_look_right();
173
                }
174
                else
175
                if (bump_col_right_of_wall >= 0) {
176
                        check_bumped_look_left();
177
                }
178
#endif // FIX_TWO_COLL_BUG
179
 
180
        }
181
}
182
 
183
// seg004:02D2
184
void __pascal far check_bumped_look_left() {
185
        if ((Char.sword == sword_2_drawn || Char.direction < dir_0_right) && // looking left
186
                is_obstacle_at_col(bump_col_right_of_wall)
187
        ) {
188
                bumped(get_right_wall_xpos(curr_room, tile_col, tile_row) - char_x_left_coll, dir_0_right);
189
        }
190
}
191
 
192
// seg004:030A
193
void __pascal far check_bumped_look_right() {
194
        if ((Char.sword == sword_2_drawn || Char.direction == dir_0_right) && // looking right
195
                is_obstacle_at_col(bump_col_left_of_wall)
196
        ) {
197
                bumped(get_left_wall_xpos(curr_room, tile_col, tile_row) - char_x_right_coll, dir_FF_left);
198
        }
199
}
200
 
201
// seg004:0343
202
int __pascal far is_obstacle_at_col(int tile_col) {
203
        short tile_row;
204
        tile_row = Char.curr_row;
205
        if (tile_row < 0) {
206
                tile_row += 3;
207
        }
208
        if (tile_row >= 3) {
209
                tile_row -= 3;
210
        }
211
        get_tile(curr_row_coll_room[tile_col], tile_col, tile_row);
212
        return is_obstacle();
213
}
214
 
215
// seg004:037E
216
int __pascal far is_obstacle() {
217
        if (curr_tile2 == tiles_10_potion) {
218
                return 0;
219
        } else if (curr_tile2 == tiles_4_gate) {
220
                if (! can_bump_into_gate()) return 0;
221
        } else if (curr_tile2 == tiles_18_chomper) {
222
                // is the chomper closed?
223
                if (curr_room_modif[curr_tilepos] != 2) return 0;
224
        } else if (
225
                curr_tile2 == tiles_13_mirror &&
226
                Char.charid == charid_0_kid &&
227
                Char.frame >= frame_39_start_run_jump_6 && Char.frame < frame_44_running_jump_5 && // run-jump
228
                Char.direction < dir_0_right // right-to-left only
229
        ) {
230
                curr_room_modif[curr_tilepos] = 0x56; // broken mirror or what?
231
                jumped_through_mirror = -1;
232
                return 0;
233
        }
234
        coll_tile_left_xpos = xpos_in_drawn_room(x_bump[tile_col + 5]) + 7;
235
        return 1;
236
}
237
 
238
// seg004:0405
239
int __pascal far xpos_in_drawn_room(int xpos) {
240
        if (curr_room != drawn_room) {
241
                if (curr_room == room_L || curr_room == room_BL) {
242
                        xpos -= 140;
243
                } else if (curr_room == room_R || curr_room == room_BR) {
244
                        xpos += 140;
245
                }
246
        }
247
        return xpos;
248
}
249
 
250
// seg004:0448
251
void __pascal far bumped(sbyte delta_x,sbyte push_direction) {
252
        // frame 177: spiked
253
        if (Char.alive < 0 && Char.frame != frame_177_spiked) {
254
                Char.x += delta_x;
255
                if (push_direction < dir_0_right) {
256
                        // pushing left
257
                        if (curr_tile2 == tiles_20_wall) {
258
                                get_tile(curr_room, --tile_col, tile_row);
259
                        }
260
                } else {
261
                        // pushing right
262
                        if (curr_tile2 == tiles_12_doortop ||
263
                                curr_tile2 == tiles_7_doortop_with_floor ||
264
                                curr_tile2 == tiles_20_wall
265
                        ) {
266
                                ++tile_col;
267
                                if (curr_room == 0 && tile_col == 10) {
268
                                        curr_room = Char.room;
269
                                        tile_col = 0;
270
                                }
271
                                get_tile(curr_room, tile_col, tile_row);
272
                        }
273
                }
274
                if (tile_is_floor(curr_tile2)) {
275
                        bumped_floor(push_direction);
276
                } else {
277
                        bumped_fall();
278
                }
279
        }
280
}
281
 
282
// seg004:04E4
283
void __pascal far bumped_fall() {
284
        short action;
285
        action = Char.action;
286
        Char.x = char_dx_forward(-4);
287
        if (action == actions_4_in_freefall) {
288
                Char.fall_x = 0;
289
        } else {
290
                seqtbl_offset_char(seq_45_bumpfall); // fall after bumped
291
                play_seq();
292
        }
293
        bumped_sound();
294
}
295
 
296
// seg004:0520
297
void __pascal far bumped_floor(sbyte push_direction) {
298
        short frame;
299
        short seq_index;
300
        if (Char.sword != sword_2_drawn && (word)(y_land[Char.curr_row + 1] - Char.y) >= (word)15) {
301
                bumped_fall();
302
        } else {
303
                Char.y = y_land[Char.curr_row + 1];
304
                if (Char.fall_y >= 22) {
305
                        Char.x = char_dx_forward(-5);
306
                } else {
307
                        Char.fall_y = 0;
308
                        if (Char.alive) {
309
                                if (Char.sword == sword_2_drawn) {
310
                                        if (push_direction == Char.direction) {
311
                                                seqtbl_offset_char(seq_65_bump_forward_with_sword); // pushed forward with sword (Kid)
312
                                                play_seq();
313
                                                Char.x = char_dx_forward(1);
314
                                                return;
315
                                        } else {
316
                                                seq_index = seq_64_pushed_back_with_sword; // pushed back with sword
317
                                        }
318
                                } else {
319
                                        frame = Char.frame;
320
                                        if (frame == 24 || frame == 25 ||
321
                                                (frame >= 40 && frame < 43) ||
322
                                                (frame >= frame_102_start_fall_1 && frame < 107)
323
                                        ) {
324
                                                seq_index = seq_46_hardbump; // bump into wall after run-jump (crouch)
325
                                        } else {
326
                                                seq_index = seq_47_bump; // bump into wall
327
                                        }
328
                                }
329
                                seqtbl_offset_char(seq_index);
330
                                play_seq();
331
                                bumped_sound();
332
                        }
333
                }
334
        }
335
}
336
 
337
// seg004:05F1
338
void __pascal far bumped_sound() {
339
        is_guard_notice = 1;
340
        play_sound(sound_8_bumped); // touching a wall
341
}
342
 
343
// seg004:0601
344
void __pascal far clear_coll_rooms() {
345
        memset_near(prev_coll_room, -1, sizeof(prev_coll_room));
346
        memset_near(curr_row_coll_room, -1, sizeof(curr_row_coll_room));
347
        memset_near(below_row_coll_room, -1, sizeof(below_row_coll_room));
348
        memset_near(above_row_coll_room, -1, sizeof(above_row_coll_room));
349
        // workaround
350
        memset_near(prev_coll_flags, 0, sizeof(prev_coll_flags));
351
        memset_near(curr_row_coll_flags, 0, sizeof(curr_row_coll_flags));
352
        memset_near(below_row_coll_flags, 0, sizeof(below_row_coll_flags));
353
        memset_near(above_row_coll_flags, 0, sizeof(above_row_coll_flags));
354
        prev_collision_row = -1;
355
}
356
 
357
// seg004:0657
358
int __pascal far can_bump_into_gate() {
359
        return (curr_room_modif[curr_tilepos] >> 2) + 6 < char_height;
360
}
361
 
362
// seg004:067C
363
int __pascal far get_edge_distance() {
364
/*
365
Possible results in edge_type:
366
0: closer/sword/potion
367
1: edge
368
2: floor (nothing near char)
369
*/
370
        short distance;
371
        byte tiletype;
372
        determine_col();
373
        load_frame_to_obj();
374
        set_char_collision();
375
        tiletype = get_tile_at_char();
376
        if (wall_type(tiletype) != 0) {
377
                tile_col = Char.curr_col;
378
                distance = dist_from_wall_forward(tiletype);
379
                if (distance >= 0) {
380
                        loc_59DD:
381
                        if (distance < 14) {
382
                                edge_type = 1;
383
                        } else {
384
                                edge_type = 2;
385
                                distance = 11;
386
                        }
387
                } else {
388
                        goto loc_59E8;
389
                }
390
        } else {
391
                loc_59E8:
392
                tiletype = get_tile_infrontof_char();
393
                if (tiletype == tiles_12_doortop && Char.direction >= dir_0_right) {
394
                        loc_59FB:
395
                        edge_type = 0;
396
                        distance = distance_to_edge_weight();
397
                } else {
398
                        if (wall_type(tiletype) != 0) {
399
                                tile_col = infrontx;
400
                                distance = dist_from_wall_forward(tiletype);
401
                                if (distance >= 0) goto loc_59DD;
402
                        }
403
                        if (tiletype == tiles_11_loose) goto loc_59FB;
404
                        if (
405
                                tiletype == tiles_6_closer ||
406
                                tiletype == tiles_22_sword ||
407
                                tiletype == tiles_10_potion
408
                        ) {
409
                                distance = distance_to_edge_weight();
410
                                if (distance != 0) {
411
                                        edge_type = 0;
412
                                } else {
413
                                        edge_type = 2;
414
                                        distance = 11;
415
                                }
416
                        } else {
417
                                if (tile_is_floor(tiletype)) {
418
                                        edge_type = 2;
419
                                        distance = 11;
420
                                } else {
421
                                        goto loc_59FB;
422
                                }
423
                        }
424
                }
425
        }
426
        curr_tile2 = tiletype;
427
        return distance;
428
}
429
 
430
// seg004:076B
431
void __pascal far check_chomped_kid() {
432
        short tile_col;
433
        short tile_row;
434
        tile_row = Char.curr_row;
435
        for (tile_col = 0; tile_col < 10; ++tile_col) {
436
                if (curr_row_coll_flags[tile_col] == 0xFF &&
437
                        get_tile(curr_row_coll_room[tile_col], tile_col, tile_row) == tiles_18_chomper &&
438
                        (curr_room_modif[curr_tilepos] & 0x7F) == 2 // closed chomper
439
                ) {
440
                        chomped();
441
                }
442
        }
443
}
444
 
445
// seg004:07BF
446
void __pascal far chomped() {
447
        #ifdef FIX_SKELETON_CHOMPER_BLOOD
448
        if (!(fix_skeleton_chomper_blood && Char.charid == charid_4_skeleton))
449
        #endif
450
                curr_room_modif[curr_tilepos] |= 0x80; // put blood
451
        if (Char.frame != frame_178_chomped && Char.room == curr_room) {
452
                Char.x = x_bump[tile_col + 5] + 7;
453
                Char.x = char_dx_forward(7 - !Char.direction);
454
                Char.y = y_land[Char.curr_row + 1];
455
                take_hp(100);
456
                play_sound(sound_46_chomped); // something chomped
457
                seqtbl_offset_char(seq_54_chomped); // chomped
458
                play_seq();
459
        }
460
}
461
 
462
// seg004:0833
463
void __pascal far check_gate_push() {
464
        // Closing gate pushes Kid
465
        short frame;
466
        short var_4;
467
        frame = Char.frame;
468
        if (Char.action == actions_7_turn ||
469
                frame == frame_15_stand || // stand
470
                (frame >= frame_108_fall_land_2 && frame < 111) // crouch
471
        ) {
472
                get_tile_at_char();
473
                var_4 = tile_col;
474
                if ((curr_tile2 == tiles_4_gate ||
475
                        get_tile(curr_room, --tile_col, tile_row) == tiles_4_gate) &&
476
                        (curr_row_coll_flags[tile_col] & prev_coll_flags[tile_col]) == 0xFF &&
477
                        can_bump_into_gate()
478
                ) {
479
                        bumped_sound();
480
                        // push Kid left if var_4 <= tile_col, gate at char's tile
481
                        // push Kid right if var_4 > tile_col, gate is left from char's tile
482
                        Char.x += 5 - (var_4 <= tile_col) * 10;
483
                }
484
        }
485
}
486
 
487
// seg004:08C3
488
void __pascal far check_guard_bumped() {
489
        if (
490
                Char.action == actions_1_run_jump &&
491
                Char.alive < 0 &&
492
                Char.sword >= sword_2_drawn
493
        ) {
494
                if (
495
 
496
                        #ifdef FIX_PUSH_GUARD_INTO_WALL
497
                        // Should also check for a wall BEHIND the guard, instead of only the current tile
498
                        (fix_push_guard_into_wall && get_tile_behind_char() == tiles_20_wall) ||
499
                        #endif
500
 
501
                        get_tile_at_char() == tiles_20_wall ||
502
                        curr_tile2 == tiles_7_doortop_with_floor ||
503
                        (curr_tile2 == tiles_4_gate && can_bump_into_gate()) ||
504
                        (Char.direction >= dir_0_right && (
505
                                get_tile(curr_room, --tile_col, tile_row) == tiles_7_doortop_with_floor ||
506
                                (curr_tile2 == tiles_4_gate && can_bump_into_gate())
507
                        ))
508
                ) {
509
                        load_frame_to_obj();
510
                        set_char_collision();
511
                        if (is_obstacle()) {
512
                                short delta_x;
513
                                delta_x = dist_from_wall_behind(curr_tile2);
514
                                if (delta_x < 0 && delta_x > -13) {
515
                                        Char.x = char_dx_forward(-delta_x);
516
                                        seqtbl_offset_char(seq_65_bump_forward_with_sword); // pushed to wall with sword (Guard)
517
                                        play_seq();
518
                                        load_fram_det_col();
519
                                }
520
                        }
521
                }
522
        }
523
}
524
 
525
// seg004:0989
526
void __pascal far check_chomped_guard() {
527
        get_tile_at_char();
528
        if ( ! check_chomped_here()) {
529
                get_tile(curr_room, ++tile_col, tile_row);
530
                check_chomped_here();
531
        }
532
}
533
 
534
// seg004:09B0
535
int __pascal far check_chomped_here() {
536
        if (curr_tile2 == tiles_18_chomper &&
537
                (curr_room_modif[curr_tilepos] & 0x7F) == 2
538
        ) {
539
                coll_tile_left_xpos = x_bump[tile_col + 5] + 7;
540
                if (get_left_wall_xpos(curr_room, tile_col, tile_row) < char_x_right_coll &&
541
                        get_right_wall_xpos(curr_room, tile_col, tile_row) > char_x_left_coll
542
                ) {
543
                        chomped();
544
                        return 1;
545
                } else {
546
                        return 0;
547
                }
548
        } else {
549
                return 0;
550
        }
551
}
552
 
553
// seg004:0A10
554
int __pascal far dist_from_wall_forward(byte tiletype) {
555
        short type;
556
        if (tiletype == tiles_4_gate && ! can_bump_into_gate()) {
557
                return -1;
558
        } else {
559
                coll_tile_left_xpos = x_bump[tile_col + 5] + 7;
560
                type = wall_type(tiletype);
561
                if (type == 0) return -1;
562
                if (Char.direction < dir_0_right) {
563
                        // looking left
564
                        //return wall_dist_from_right[type] + char_x_left_coll - coll_tile_left_xpos - 13;
565
                        return char_x_left_coll - (coll_tile_left_xpos + 13 - wall_dist_from_right[type]);
566
                } else {
567
                        // looking right
568
                        return wall_dist_from_left[type] + coll_tile_left_xpos - char_x_right_coll;
569
                }
570
        }
571
}
572
 
573
// seg004:0A7B
574
int __pascal far dist_from_wall_behind(byte tiletype) {
575
        short type;
576
        type = wall_type(tiletype);
577
        if (type == 0) {
578
                return 99;
579
        } else {
580
                if (Char.direction >= dir_0_right) {
581
                        // looking right
582
                        //return wall_dist_from_right[type] + char_x_left_coll - coll_tile_left_xpos - 13;
583
                        return char_x_left_coll - (coll_tile_left_xpos + 13 - wall_dist_from_right[type]);
584
                } else {
585
                        // looking left
586
                        return wall_dist_from_left[type] + coll_tile_left_xpos - char_x_right_coll;
587
                }
588
        }
589
}