1 /*
2 SDLPoP, a port/conversion of the DOS game Prince of Persia.
3 Copyright (C) 2013-2021 Dávid Nagy
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18 The authors of this program may be contacted at https://forum.princed.org
19 */
20
21 #include "common.h"
22
23 // seg007:0000
process_trobs()24 void __pascal far process_trobs() {
25 word need_delete;
26 word index;
27 word new_index;
28 need_delete = 0;
29 if (trobs_count == 0) return;
30 for (index = 0; index < trobs_count; ++index) {
31 trob = trobs[index];
32 animate_tile();
33 trobs[index].type = trob.type;
34 if (trob.type < 0) {
35 need_delete = 1;
36 }
37 }
38 if (need_delete) {
39 for (index = new_index = 0; index < trobs_count; ++index) {
40 if (trobs[index].type >= 0) {
41 trobs[new_index++] = trobs[index];
42 }
43 }
44 trobs_count = new_index;
45 }
46 }
47
48 // seg007:00AF
animate_tile()49 void __pascal far animate_tile() {
50 get_room_address(trob.room);
51 switch (get_curr_tile(trob.tilepos)) {
52 case tiles_19_torch:
53 case tiles_30_torch_with_debris:
54 animate_torch();
55 break;
56 case tiles_6_closer:
57 case tiles_15_opener:
58 animate_button();
59 break;
60 case tiles_2_spike:
61 animate_spike();
62 break;
63 case tiles_11_loose:
64 animate_loose();
65 break;
66 case tiles_0_empty:
67 animate_empty();
68 break;
69 case tiles_18_chomper:
70 animate_chomper();
71 break;
72 case tiles_4_gate:
73 animate_door();
74 break;
75 case tiles_16_level_door_left:
76 animate_leveldoor();
77 break;
78 case tiles_10_potion:
79 animate_potion();
80 break;
81 case tiles_22_sword:
82 animate_sword();
83 break;
84 default:
85 trob.type = -1;
86 break;
87 }
88 curr_room_modif[trob.tilepos] = curr_modifier;
89 }
90
91 // seg007:0166
is_trob_in_drawn_room()92 short __pascal far is_trob_in_drawn_room() {
93 if (trob.room != drawn_room) {
94 trob.type = -1;
95 return 0;
96 } else {
97 return 1;
98 }
99 }
100
101 // seg007:017E
set_redraw_anim_right()102 void __pascal far set_redraw_anim_right() {
103 set_redraw_anim(get_trob_right_pos_in_drawn_room(), 1);
104 }
105
106 // seg007:018C
set_redraw_anim_curr()107 void __pascal far set_redraw_anim_curr() {
108 set_redraw_anim(get_trob_pos_in_drawn_room(), 1);
109 }
110
111 // seg007:019A
redraw_at_trob()112 void __pascal far redraw_at_trob() {
113 word tilepos;
114 redraw_height = 63;
115 tilepos = get_trob_pos_in_drawn_room();
116 set_redraw_full(tilepos, 1);
117 set_wipe(tilepos, 1);
118 }
119
120 // seg007:01C5
redraw_21h()121 void __pascal far redraw_21h() {
122 redraw_height = 0x21;
123 redraw_tile_height();
124 }
125
126 // seg007:01D0
redraw_11h()127 void __pascal far redraw_11h() {
128 redraw_height = 0x11;
129 redraw_tile_height();
130 }
131
132 // seg007:01DB
redraw_20h()133 void __pascal far redraw_20h() {
134 redraw_height = 0x20;
135 redraw_tile_height();
136 }
137
138 // seg007:01E6
draw_trob()139 void __pascal far draw_trob() {
140 word var_2;
141 var_2 = get_trob_right_pos_in_drawn_room();
142 set_redraw_anim(var_2, 1);
143 set_redraw_fore(var_2, 1);
144 set_redraw_anim(get_trob_right_above_pos_in_drawn_room(), 1);
145 }
146
147 // seg007:0218
redraw_tile_height()148 void __pascal far redraw_tile_height() {
149 short tilepos;
150 tilepos = get_trob_pos_in_drawn_room();
151 set_redraw_full(tilepos, 1);
152 set_wipe(tilepos, 1);
153 tilepos = get_trob_right_pos_in_drawn_room();
154 set_redraw_full(tilepos, 1);
155 set_wipe(tilepos, 1);
156 }
157
158 // seg007:0258
get_trob_pos_in_drawn_room()159 short __pascal far get_trob_pos_in_drawn_room() {
160 short tilepos;
161 tilepos = trob.tilepos;
162 if (trob.room == room_A) {
163 if (tilepos >= 20 && tilepos < 30) {
164 // 20..29 -> -1..-10
165 tilepos = 19 - tilepos;
166 } else {
167 tilepos = 30;
168 }
169 } else {
170 if (trob.room != drawn_room) {
171 tilepos = 30;
172 }
173 }
174 return tilepos;
175 }
176
177 // seg007:029D
get_trob_right_pos_in_drawn_room()178 short __pascal far get_trob_right_pos_in_drawn_room() {
179 word tilepos;
180 tilepos = trob.tilepos;
181 if (trob.room == drawn_room) {
182 if (tilepos % 10 != 9) {
183 ++tilepos;
184 } else {
185 tilepos = 30;
186 }
187 } else if (trob.room == room_L) {
188 if (tilepos % 10 == 9) {
189 tilepos -= 9;
190 } else {
191 tilepos = 30;
192 }
193 } else if (trob.room == room_A) {
194 if (tilepos >= 20 && tilepos < 29) {
195 // 20..28 -> -2..-10
196 tilepos = 18 - tilepos;
197 } else {
198 tilepos = 30;
199 }
200 } else if (trob.room == room_AL && tilepos == 29) {
201 tilepos = -1;
202 } else {
203 tilepos = 30;
204 }
205 return tilepos;
206 }
207
208 // seg007:032C
get_trob_right_above_pos_in_drawn_room()209 short __pascal far get_trob_right_above_pos_in_drawn_room() {
210 word tilepos;
211 tilepos = trob.tilepos;
212 if (trob.room == drawn_room) {
213 if (tilepos % 10 != 9) {
214 if (tilepos < 10) {
215 // 0..8 -> -2..-10
216 tilepos = -(tilepos + 2);
217 } else {
218 tilepos -= 9;
219 }
220 } else {
221 tilepos = 30;
222 }
223 } else if (trob.room == room_L) {
224 if (tilepos == 9) {
225 tilepos = -1;
226 } else {
227 if (tilepos % 10 == 9) {
228 tilepos -= 19;
229 } else {
230 tilepos = 30;
231 }
232 }
233 } else if (trob.room == room_B) {
234 if (tilepos < 9) {
235 tilepos += 21;
236 } else {
237 tilepos = 30;
238 }
239 } else if (trob.room == room_BL && tilepos == 9) {
240 tilepos = 20;
241 } else {
242 tilepos = 30;
243 }
244 return tilepos;
245 }
246
247 // seg007:03CF
animate_torch()248 void __pascal far animate_torch() {
249 //if (is_trob_in_drawn_room()) {
250 // Keep animating torches in the rightmost column of the left-side room as well, because they are visible in the current room.
251 if (trob.room == drawn_room || (trob.room == room_L && (trob.tilepos % 10) == 9) ) {
252 curr_modifier = get_torch_frame(curr_modifier);
253 set_redraw_anim_right();
254 } else {
255 trob.type = -1;
256 }
257 }
258
259 // seg007:03E9
animate_potion()260 void __pascal far animate_potion() {
261 word type;
262 if (trob.type >= 0 && is_trob_in_drawn_room()) {
263 type = curr_modifier & 0xF8;
264 curr_modifier = bubble_next_frame(curr_modifier & 0x07) | type;
265 #ifdef FIX_LOOSE_NEXT_TO_POTION
266 redraw_at_trob();
267 #else
268 set_redraw_anim_curr();
269 #endif
270 }
271 }
272
273 // seg007:0425
animate_sword()274 void __pascal far animate_sword() {
275 if (is_trob_in_drawn_room()) {
276 --curr_modifier;
277 if (curr_modifier == 0) {
278 curr_modifier = (prandom(255) & 0x3F) + 0x28;
279 }
280 #ifdef FIX_LOOSE_NEXT_TO_POTION
281 redraw_at_trob();
282 #else
283 set_redraw_anim_curr();
284 #endif
285 }
286 }
287
288 // seg007:0448
animate_chomper()289 void __pascal far animate_chomper() {
290 word blood;
291 word frame;
292 if (trob.type >= 0) {
293 blood = curr_modifier & 0x80;
294 frame = (curr_modifier & 0x7F) + 1;
295 if (frame > /*15*/ custom->chomper_speed) {
296 frame = 1;
297 }
298 curr_modifier = blood | frame;
299 if (frame == 2) {
300 play_sound(sound_47_chomper); // chomper
301 }
302 // If either:
303 // - Kid left this room
304 // - Kid left this row
305 // - Kid died but not in this chomper
306 // and chomper is past frame 6
307 // then stop.
308 if ((trob.room != drawn_room || trob.tilepos / 10 != Kid.curr_row ||
309 (Kid.alive >= 0 && blood == 0)) && (curr_modifier & 0x7F) >= 6
310 ) {
311 trob.type = -1;
312 }
313 }
314 if ((curr_modifier & 0x7F) < 6) {
315 redraw_at_trob();
316 }
317 }
318
319 // seg007:04D3
animate_spike()320 void __pascal far animate_spike() {
321 if (trob.type >= 0) {
322 // 0xFF means a disabled spike.
323 if (curr_modifier == 0xFF) return;
324 if (curr_modifier & 0x80) {
325 --curr_modifier;
326 if (curr_modifier & 0x7F) return;
327 curr_modifier = 6;
328 } else {
329 ++curr_modifier;
330 if (curr_modifier == 5) {
331 curr_modifier = 0x8F;
332 } else if (curr_modifier == 9) {
333 curr_modifier = 0;
334 trob.type = -1;
335 }
336 }
337 }
338 redraw_21h();
339 }
340
341 // data:27B2
342 const byte gate_close_speeds[] = {0, 0, 0, 20, 40, 60, 80, 100, 120};
343 // data:27C0
344 const byte door_delta[] = {-1, 4, 4};
345 // seg007:0522
animate_door()346 void __pascal far animate_door() {
347 /*
348 Possible values of anim_type:
349 0: closing
350 1: open
351 2: permanent open
352 3,4,5,6,7,8: fast closing with speeds 20,40,60,80,100,120 /4 pixel/frame
353 */
354 sbyte anim_type;
355 anim_type = trob.type;
356 if (anim_type >= 0) {
357 if (anim_type >= 3) {
358 // closing fast
359 if (anim_type < 8) {
360 ++anim_type;
361 trob.type = anim_type;
362 }
363 short new_mod = curr_modifier - gate_close_speeds[anim_type];
364 curr_modifier = new_mod;
365 //if ((sbyte)curr_modifier < 0) {
366 if (new_mod < 0) {
367 //if ((curr_modifier -= gate_close_speeds[anim_type]) < 0) {
368 curr_modifier = 0;
369 trob.type = -1;
370 play_sound(sound_6_gate_closing_fast); // gate closing fast
371 }
372 } else {
373 if (curr_modifier != 0xFF) {
374 // 0xFF means permanently open.
375 curr_modifier += door_delta[anim_type];
376 if (anim_type == 0) {
377 // closing
378 if (curr_modifier != 0) {
379 if (curr_modifier < 188) {
380 if ((curr_modifier & 3) == 3) {
381 play_door_sound_if_visible(sound_4_gate_closing); // gate closing
382 }
383 }
384 } else {
385 gate_stop();
386 }
387 } else {
388 // opening
389 if (curr_modifier < 188) {
390 if ((curr_modifier & 7) == 0) {
391 play_sound(sound_5_gate_opening); // gate opening
392 }
393 } else {
394 // stop
395 if (anim_type < 2) {
396 // after regular open
397 curr_modifier = 238;
398 trob.type = 0; // closing
399 play_sound(sound_7_gate_stop); // gate stop (after opening)
400 } else {
401 // after permanent open
402 curr_modifier = 0xFF; // keep open
403 gate_stop();
404 }
405 }
406 }
407 } else {
408 gate_stop();
409 }
410 }
411 }
412 draw_trob();
413 }
414
415 // seg007:05E3
gate_stop()416 void __pascal far gate_stop() {
417 trob.type = -1;
418 play_door_sound_if_visible(sound_7_gate_stop); // gate stop (after closing)
419 }
420
421 // data:27B8
422 const byte leveldoor_close_speeds[] = {0, 5, 17, 99, 0};
423 // seg007:05F1
animate_leveldoor()424 void __pascal far animate_leveldoor() {
425 /*
426 Possible values of trob_type:
427 0: open
428 1: open (with button)
429 2: open
430 3,4,5,6: fast closing with speeds 0,5,17,99 pixel/frame
431 */
432 word trob_type;
433 trob_type = trob.type;
434 if (trob.type >= 0) {
435 if (trob_type >= 3) {
436 // closing
437 ++trob.type;
438 curr_modifier -= leveldoor_close_speeds[trob.type - 3];
439 if ((sbyte)curr_modifier < 0) {
440 curr_modifier = 0;
441 trob.type = -1;
442 play_sound(sound_14_leveldoor_closing); // level door closing
443 } else {
444 if (trob.type == 4 &&
445 (sound_flags & sfDigi)
446 ) {
447 sound_interruptible[sound_15_leveldoor_sliding] = 1;
448 play_sound(sound_15_leveldoor_sliding); // level door sliding (closing)
449 }
450 }
451 } else {
452 // opening
453 ++curr_modifier;
454 if (curr_modifier >= 43) {
455 trob.type = -1;
456 #ifdef FIX_FEATHER_INTERRUPTED_BY_LEVELDOOR
457 if (!(fixes->fix_feather_interrupted_by_leveldoor && is_feather_fall))
458 #endif
459 stop_sounds();
460 if (leveldoor_open == 0 || leveldoor_open == 2) {
461 leveldoor_open = 1;
462 if (current_level == /*4*/ custom->mirror_level) {
463 // Special event: place mirror
464 get_tile(/*4*/ custom->mirror_room, /*4*/ custom->mirror_column, /*0*/ custom->mirror_row);
465 curr_room_tiles[curr_tilepos] = /*tiles_13_mirror*/ custom->mirror_tile;
466 }
467 }
468 } else {
469 sound_interruptible[sound_15_leveldoor_sliding] = 0;
470 play_sound(sound_15_leveldoor_sliding); // level door sliding (opening)
471 }
472 }
473 }
474 set_redraw_anim_right();
475 }
476
477 // seg007:06AD
bubble_next_frame(short curr)478 short __pascal far bubble_next_frame(short curr) {
479 short next;
480 next = curr + 1;
481 if (next >= 8) next = 1;
482 return next;
483 }
484
485 // seg007:06CD
get_torch_frame(short curr)486 short __pascal far get_torch_frame(short curr) {
487 short next;
488 next = prandom(255);
489 if (next != curr) {
490 if (next < 9) {
491 return next;
492 } else {
493 next = curr;
494 }
495 }
496 ++next;
497 if (next >= 9) next = 0;
498 return next;
499 }
500
501 // seg007:070A
set_redraw_anim(short tilepos,byte frames)502 void __pascal far set_redraw_anim(short tilepos, byte frames) {
503 if (tilepos < 30) {
504 if (tilepos < 0) {
505 ++tilepos;
506 redraw_frames_above[-tilepos] = frames;
507 // or simply: ~tilepos
508 } else {
509 redraw_frames_anim[tilepos] = frames;
510 }
511 }
512 }
513
514 // seg007:0738
set_redraw2(short tilepos,byte frames)515 void __pascal far set_redraw2(short tilepos, byte frames) {
516 if (tilepos < 30) {
517 if (tilepos < 0) {
518 // trying to draw a mob at a negative tilepos, in the range -1 .. -10
519 // used e.g. when the kid is climbing up to the room above
520 // however, loose tiles falling out of the room end up with a negative tilepos {-2 .. -11} !
521 tilepos = (-tilepos) - 1;
522 if (tilepos > 9) tilepos = 9; // prevent array index out of bounds!
523 redraw_frames_above[tilepos] = frames;
524 } else {
525 redraw_frames2[tilepos] = frames;
526 }
527 }
528 }
529
530 // seg007:0766
set_redraw_floor_overlay(short tilepos,byte frames)531 void __pascal far set_redraw_floor_overlay(short tilepos, byte frames) {
532 if (tilepos < 30) {
533 if (tilepos < 0) {
534 ++tilepos;
535 redraw_frames_above[-tilepos] = frames;
536 // or simply: ~tilepos
537 } else {
538 redraw_frames_floor_overlay[tilepos] = frames;
539 }
540 }
541 }
542
543 // seg007:0794
set_redraw_full(short tilepos,byte frames)544 void __pascal far set_redraw_full(short tilepos, byte frames) {
545 if (tilepos < 30) {
546 if (tilepos < 0) {
547 ++tilepos;
548 redraw_frames_above[-tilepos] = frames;
549 // or simply: ~tilepos
550 } else {
551 redraw_frames_full[tilepos] = frames;
552 }
553 }
554 }
555
556 // seg007:07C2
set_redraw_fore(short tilepos,byte frames)557 void __pascal far set_redraw_fore(short tilepos, byte frames) {
558 if (tilepos < 30 && tilepos >= 0) {
559 redraw_frames_fore[tilepos] = frames;
560 }
561 }
562
563 // seg007:07DF
set_wipe(short tilepos,byte frames)564 void __pascal far set_wipe(short tilepos, byte frames) {
565 if (tilepos < 30 && tilepos >= 0) {
566 if (wipe_frames[tilepos] != 0) {
567 redraw_height = MAX(wipe_heights[tilepos], redraw_height);
568 }
569 wipe_heights[tilepos] = redraw_height;
570 wipe_frames[tilepos] = frames;
571 }
572 }
573
574 // seg007:081E
start_anim_torch(short room,short tilepos)575 void __pascal far start_anim_torch(short room,short tilepos) {
576 curr_room_modif[tilepos] = prandom(8);
577 add_trob(room, tilepos, 1);
578 }
579
580 // seg007:0847
start_anim_potion(short room,short tilepos)581 void __pascal far start_anim_potion(short room,short tilepos) {
582 curr_room_modif[tilepos] &= 0xF8;
583 curr_room_modif[tilepos] |= prandom(6) + 1;
584 add_trob(room, tilepos, 1);
585 }
586
587 // seg007:087C
start_anim_sword(short room,short tilepos)588 void __pascal far start_anim_sword(short room,short tilepos) {
589 curr_room_modif[tilepos] = prandom(0xFF) & 0x1F;
590 add_trob(room, tilepos, 1);
591 }
592
593 // seg007:08A7
start_anim_chomper(short room,short tilepos,byte modifier)594 void __pascal far start_anim_chomper(short room,short tilepos, byte modifier) {
595 short old_modifier;
596 old_modifier = curr_room_modif[tilepos];
597 if (old_modifier == 0 || old_modifier >= 6) {
598 curr_room_modif[tilepos] = modifier;
599 add_trob(room, tilepos, 1);
600 }
601 }
602
603 // seg007:08E3
start_anim_spike(short room,short tilepos)604 void __pascal far start_anim_spike(short room,short tilepos) {
605 sbyte old_modifier;
606 old_modifier = curr_room_modif[tilepos];
607 if (old_modifier <= 0) {
608 if (old_modifier == 0) {
609 add_trob(room, tilepos, 1);
610 play_sound(sound_49_spikes); // spikes
611 } else {
612 // 0xFF means a disabled spike.
613 if (old_modifier != (sbyte)0xFF) {
614 curr_room_modif[tilepos] = 0x8F;
615 }
616 }
617 }
618 }
619
620 // seg007:092C
trigger_gate(short room,short tilepos,short button_type)621 short __pascal far trigger_gate(short room,short tilepos,short button_type) {
622 byte modifier;
623 modifier = curr_room_modif[tilepos];
624 if (button_type == tiles_15_opener) {
625 // If the gate is permanently open, don't to anything.
626 if (modifier == 0xFF) return -1;
627 if (modifier >= 188) { // if it's already open
628 curr_room_modif[tilepos] = 238; // keep it open for a while
629 return -1;
630 }
631 curr_room_modif[tilepos] = (modifier + 3) & 0xFC;
632 return 1; // regular open
633 } else if (button_type == tiles_14_debris) {
634 // If it's not fully open:
635 if (modifier < 188) return 2; // permanent open
636 curr_room_modif[tilepos] = 0xFF; // keep open
637 return -1;
638 } else {
639 if (modifier != 0) {
640 return 3; // close fast
641 } else {
642 // already closed
643 return -1;
644 }
645 }
646 }
647
648 // seg007:0999
trigger_1(short target_type,short room,short tilepos,short button_type)649 short __pascal far trigger_1(short target_type,short room,short tilepos,short button_type) {
650 short result;
651 result = -1;
652 if (target_type == tiles_4_gate) {
653 result = trigger_gate(room, tilepos, button_type);
654 } else if (target_type == tiles_16_level_door_left) {
655 if (curr_room_modif[tilepos] != 0) {
656 result = -1;
657 } else {
658 result = 1;
659 }
660 } else if (custom->allow_triggering_any_tile) { //allow_triggering_any_tile hack
661 result = 1;
662 }
663 return result;
664 }
665
666 // seg007:09E5
do_trigger_list(short index,short button_type)667 void __pascal far do_trigger_list(short index,short button_type) {
668 word room;
669 word tilepos;
670 byte target_type;
671 sbyte trigger_result;
672 // while (doorlink1_ad[index] != -1) { // these can't be equal!
673 while (1) { // Same as the above but just a little faster and no compiler warning.
674 room = get_doorlink_room(index);
675 get_room_address(room);
676 tilepos = get_doorlink_tile(index);
677 target_type = curr_room_tiles[tilepos] & 0x1F;
678 trigger_result = trigger_1(target_type, room, tilepos, button_type);
679 if (trigger_result >= 0) {
680 add_trob(room, tilepos, trigger_result);
681 }
682 if (get_doorlink_next(index++) == 0) break;
683 }
684 }
685
686 // seg007:0A5A
add_trob(byte room,byte tilepos,sbyte type)687 void __pascal far add_trob(byte room,byte tilepos,sbyte type) {
688 short found;
689 if (trobs_count >= 30) {
690 show_dialog("Trobs Overflow");
691 return /*0*/; // added
692 }
693 trob.room = room;
694 trob.tilepos = tilepos;
695 trob.type = type;
696 found = find_trob();
697 if (found == -1) {
698 // add new
699 if (trobs_count == 30) return;
700 trobs[trobs_count++] = trob;
701 } else {
702 // change existing
703 trobs[found].type = trob.type;
704 }
705 }
706
707 // seg007:0ACA
find_trob()708 short __pascal far find_trob() {
709 short index;
710 for (index = 0; index < trobs_count; ++index) {
711 if (trobs[index].tilepos == trob.tilepos &&
712 trobs[index].room == trob.room) return index;
713 }
714 return -1;
715 }
716
717 // seg007:0B0A
clear_tile_wipes()718 void __pascal far clear_tile_wipes() {
719 memset_near(redraw_frames_full, 0, sizeof(redraw_frames_full));
720 memset_near(wipe_frames, 0, sizeof(wipe_frames));
721 memset_near(wipe_heights, 0, sizeof(wipe_heights));
722 memset_near(redraw_frames_anim, 0, sizeof(redraw_frames_anim));
723 memset_near(redraw_frames_fore, 0, sizeof(redraw_frames_fore));
724 memset_near(redraw_frames2, 0, sizeof(redraw_frames2));
725 memset_near(redraw_frames_floor_overlay, 0, sizeof(redraw_frames_floor_overlay));
726 memset_near(tile_object_redraw, 0, sizeof(tile_object_redraw));
727 memset_near(redraw_frames_above, 0, sizeof(redraw_frames_above));
728 }
729
730 // seg007:0BB6
get_doorlink_timer(short index)731 short __pascal far get_doorlink_timer(short index) {
732 return doorlink2_ad[index] & 0x1F;
733 }
734
735 // seg007:0BCD
set_doorlink_timer(short index,byte value)736 short __pascal far set_doorlink_timer(short index,byte value) {
737 doorlink2_ad[index] &= 0xE0;
738 doorlink2_ad[index] |= value & 0x1F;
739 return doorlink2_ad[index];
740 }
741
742 // seg007:0BF2
get_doorlink_tile(short index)743 short __pascal far get_doorlink_tile(short index) {
744 return doorlink1_ad[index] & 0x1F;
745 }
746
747 // seg007:0C09
get_doorlink_next(short index)748 short __pascal far get_doorlink_next(short index) {
749 return !(doorlink1_ad[index] & 0x80);
750 }
751
752 // seg007:0C26
get_doorlink_room(short index)753 short __pascal far get_doorlink_room(short index) {
754 return
755 ((doorlink1_ad[index] & 0x60) >> 5) +
756 ((doorlink2_ad[index] & 0xE0) >> 3);
757 }
758
759 // seg007:0C53
trigger_button(int playsound,int button_type,int modifier)760 void __pascal far trigger_button(int playsound,int button_type,int modifier) {
761 sbyte link_timer;
762 get_curr_tile(curr_tilepos);
763 if (button_type == 0) {
764 // 0 means currently selected
765 button_type = curr_tile;
766 }
767 if (modifier == -1) {
768 // -1 means currently selected
769 modifier = curr_modifier;
770 }
771 link_timer = get_doorlink_timer(modifier);
772 // is the event jammed?
773 if (link_timer != 0x1F) {
774 set_doorlink_timer(modifier, 5);
775 if (link_timer < 2) {
776 add_trob(curr_room, curr_tilepos, 1);
777 redraw_11h();
778 is_guard_notice = 1;
779 if (playsound) {
780 play_sound(sound_3_button_pressed); // button pressed
781 }
782 }
783 do_trigger_list(modifier, button_type);
784 }
785 }
786
787 // seg007:0CD9
died_on_button()788 void __pascal far died_on_button() {
789 word button_type;
790 word modifier;
791 button_type = get_curr_tile(curr_tilepos);
792 modifier = curr_modifier;
793 if (curr_tile == tiles_15_opener) {
794 curr_room_tiles[curr_tilepos] = tiles_1_floor;
795 curr_room_modif[curr_tilepos] = 0;
796 button_type = tiles_14_debris; // force permanent open
797 } else {
798 curr_room_tiles[curr_tilepos] = tiles_5_stuck;
799 }
800 trigger_button(1, button_type, modifier);
801 }
802
803 // seg007:0D3A
animate_button()804 void __pascal far animate_button() {
805 word var_2;
806 if (trob.type >= 0) {
807 set_doorlink_timer(curr_modifier, var_2 = get_doorlink_timer(curr_modifier) - 1);
808 if (var_2 < 2) {
809 trob.type = -1;
810 redraw_11h();
811 }
812 }
813 }
814
815 // seg007:0D72
start_level_door(short room,short tilepos)816 void __pascal far start_level_door(short room,short tilepos) {
817 curr_room_modif[tilepos] = 43; // start fully open
818 add_trob(room, tilepos, 3);
819 }
820
821 // seg007:0D93
animate_empty()822 void __pascal far animate_empty() {
823 trob.type = -1;
824 redraw_20h();
825 }
826
827 // data:2284
828 const word y_loose_land[] = {2, 65, 128, 191, 254};
829 // seg007:0D9D
animate_loose()830 void __pascal far animate_loose() {
831 word room;
832 word row;
833 word tilepos;
834 short anim_type;
835 anim_type = trob.type;
836 if (anim_type >= 0) {
837 ++curr_modifier;
838 if (curr_modifier & 0x80) {
839 // just shaking
840 // don't stop on level 13, needed for the auto-falling floors
841 if (current_level == 13) return;
842 if (curr_modifier >= 0x84) {
843 curr_modifier = 0;
844 trob.type = -1;
845 }
846 loose_shake(!curr_modifier);
847 } else {
848 // something is on the floor
849 // should it fall already?
850 if (curr_modifier >= /*11*/ custom->loose_floor_delay) {
851 curr_modifier = remove_loose(room = trob.room, tilepos = trob.tilepos);
852 trob.type = -1;
853 curmob.xh = (tilepos % 10) << 2;
854 row = tilepos / 10;
855 curmob.y = y_loose_land[row + 1];
856 curmob.room = room;
857 curmob.speed = 0;
858 curmob.type = 0;
859 curmob.row = row;
860 add_mob();
861 } else {
862 loose_shake(0);
863 }
864 }
865 }
866 redraw_20h();
867 }
868
869 // data:2734
870 const byte loose_sound[] = {0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0};
871 // seg007:0E55
loose_shake(int arg_0)872 void __pascal far loose_shake(int arg_0) {
873 word sound_id;
874 if (arg_0 || loose_sound[curr_modifier & 0x7F]) {
875 do {
876 // Sounds 20,21,22: loose floor shaking
877 sound_id = prandom(2) + sound_20_loose_shake_1;
878 } while(sound_id == last_loose_sound);
879
880 #ifdef USE_REPLAY
881 // Skip this prandom call if we are replaying, and the replay file was made with an old version of SDLPoP (which didn't have this call).
882 if (!(replaying && g_deprecation_number < 2))
883 #endif
884 {
885 prandom(2); // For vanilla pop compatibility, an RNG cycle is wasted here
886 // Note: In DOS PoP, it's wasted a few lines below.
887 }
888
889 if (sound_flags & sfDigi) {
890 last_loose_sound = sound_id;
891 // random sample rate (10500..11500)
892 //sound_pointers[sound_id]->samplerate = prandom(1000) + 10500;
893 }
894 play_sound(sound_id);
895 }
896 }
897
898 // seg007:0EB8
remove_loose(int room,int tilepos)899 int __pascal far remove_loose(int room, int tilepos) {
900 curr_room_tiles[tilepos] = tiles_0_empty;
901 // note: the level type is used to determine the modifier of the empty space left behind
902 return custom->tbl_level_type[current_level];
903 }
904
905 // seg007:0ED5
make_loose_fall(byte modifier)906 void __pascal far make_loose_fall(byte modifier) {
907 // is it a "solid" loose floor?
908 if ((curr_room_tiles[curr_tilepos] & 0x20) == 0) {
909 if ((sbyte)curr_room_modif[curr_tilepos] <= 0) {
910 curr_room_modif[curr_tilepos] = modifier;
911 add_trob(curr_room, curr_tilepos, 0);
912 redraw_20h();
913 }
914 }
915 }
916
917 // seg007:0F13
start_chompers()918 void __pascal far start_chompers() {
919 short timing;
920 short modifier;
921 short tilepos;
922 short column;
923 timing = 15;
924 if ((byte)Char.curr_row < 3) {
925 get_room_address(Char.room);
926 for (column = 0, tilepos = tbl_line[Char.curr_row];
927 column < 10; ++column, ++tilepos
928 ){
929 if (get_curr_tile(tilepos) == tiles_18_chomper) {
930 modifier = curr_modifier & 0x7F;
931 if (modifier == 0 || modifier >= 6) {
932 start_anim_chomper(Char.room, tilepos, timing | (curr_modifier & 0x80));
933 timing = next_chomper_timing(timing);
934 }
935 }
936 }
937 }
938 }
939
940 // seg007:0F9A
next_chomper_timing(byte timing)941 int __pascal far next_chomper_timing(byte timing) {
942 // 15,12,9,6,13,10,7,14,11,8,repeat
943 timing -= 3;
944 if (timing < 6) {
945 timing += 10;
946 }
947 return timing;
948 }
949
950 // seg007:0FB4
loose_make_shake()951 void __pascal far loose_make_shake() {
952 // don't shake on level 13
953 if (curr_room_modif[curr_tilepos] == 0 && current_level != 13) {
954 curr_room_modif[curr_tilepos] = 0x80;
955 add_trob(curr_room, curr_tilepos, 1);
956 }
957 }
958
959 // seg007:0FE0
do_knock(int room,int tile_row)960 void __pascal far do_knock(int room,int tile_row) {
961 short tile_col;
962 for (tile_col = 0; tile_col < 10; ++tile_col) {
963 if (get_tile(room, tile_col, tile_row) == tiles_11_loose) {
964 loose_make_shake();
965 }
966 }
967 }
968
969 // seg007:1010
add_mob()970 void __pascal far add_mob() {
971 if (mobs_count >= 14) {
972 show_dialog("Mobs Overflow");
973 return /*0*/; // added
974 }
975 mobs[mobs_count++] = curmob;
976 }
977
978 // seg007:1041
get_curr_tile(short tilepos)979 short __pascal far get_curr_tile(short tilepos) {
980 curr_modifier = curr_room_modif[tilepos];
981 return curr_tile = curr_room_tiles[tilepos] & 0x1F;
982 }
983
984 // data:43DC
985 word curmob_index;
986
987 // seg007:1063
do_mobs()988 void __pascal far do_mobs() {
989 short n_mobs;
990 short index;
991 short new_index;
992 n_mobs = mobs_count;
993 for (curmob_index = 0; n_mobs > curmob_index; ++curmob_index) {
994 curmob = mobs[curmob_index];
995 move_mob();
996 check_loose_fall_on_kid();
997 mobs[curmob_index] = curmob;
998 }
999 new_index = 0;
1000 for (index = 0; index < mobs_count; ++index) {
1001 if (mobs[index].speed != -1) {
1002 mobs[new_index++] = mobs[index];
1003 }
1004 }
1005 mobs_count = new_index;
1006 }
1007
1008 // seg007:110F
move_mob()1009 void __pascal far move_mob() {
1010 if (curmob.type == 0) {
1011 move_loose();
1012 }
1013 if (curmob.speed <= 0) {
1014 ++curmob.speed;
1015 }
1016 }
1017
1018 // data:227A
1019 const short y_something[] = {-1, 62, 125, 188, 25};
1020 // data:594A
1021 word curr_tile_temp;
1022 // seg007:1126
move_loose()1023 void __pascal far move_loose() {
1024 if (curmob.speed < 0) return;
1025 if (curmob.speed < 29) curmob.speed += 3;
1026 curmob.y += curmob.speed;
1027 if (curmob.room == 0) {
1028 if (curmob.y < 210) {
1029 return;
1030 } else {
1031 curmob.speed = -2;
1032 return;
1033 }
1034 }
1035 if (curmob.y < 226 && y_something[curmob.row + 1] <= curmob.y) {
1036 // fell into a different row
1037 curr_tile_temp = get_tile(curmob.room, curmob.xh >> 2, curmob.row);
1038 if (curr_tile_temp == tiles_11_loose) {
1039 loose_fall();
1040 }
1041 if (curr_tile_temp == tiles_0_empty ||
1042 curr_tile_temp == tiles_11_loose
1043 ) {
1044 mob_down_a_row();
1045 return;
1046 }
1047 play_sound(sound_2_tile_crashing); // tile crashing
1048 do_knock(curmob.room, curmob.row);
1049 curmob.y = y_something[curmob.row + 1];
1050 curmob.speed = -2;
1051 loose_land();
1052 }
1053 }
1054
1055 // seg007:11E8
loose_land()1056 void __pascal far loose_land() {
1057 short button_type;
1058 short tiletype;
1059 button_type = 0;
1060 tiletype = get_tile(curmob.room, curmob.xh >> 2, curmob.row);
1061 switch (tiletype) {
1062 case tiles_15_opener:
1063 curr_room_tiles[curr_tilepos] = tiles_14_debris;
1064 button_type = tiles_14_debris;
1065 // fallthrough!
1066 case tiles_6_closer:
1067 trigger_button(1, button_type, -1);
1068 tiletype = get_tile(curmob.room, curmob.xh >> 2, curmob.row);
1069 // fallthrough!
1070 case tiles_1_floor:
1071 case tiles_2_spike:
1072 case tiles_10_potion:
1073 case tiles_19_torch:
1074 case tiles_30_torch_with_debris:
1075 if (tiletype == tiles_19_torch ||
1076 tiletype == tiles_30_torch_with_debris
1077 ) {
1078 curr_room_tiles[curr_tilepos] = tiles_30_torch_with_debris;
1079 } else {
1080 curr_room_tiles[curr_tilepos] = tiles_14_debris;
1081 }
1082 redraw_at_cur_mob();
1083 if (tile_col != 0) {
1084 set_redraw_full(curr_tilepos - 1, 1);
1085 }
1086 }
1087 }
1088
1089 // seg007:12CB
loose_fall()1090 void __pascal far loose_fall() {
1091 curr_room_modif[curr_tilepos] = remove_loose(curr_room, curr_tilepos);
1092 curmob.speed >>= 1;
1093 mobs[curmob_index] = curmob;
1094 curmob.y += 6;
1095 mob_down_a_row();
1096 add_mob();
1097 curmob = mobs[curmob_index];
1098 redraw_at_cur_mob();
1099 }
1100
1101 // seg007:132C
redraw_at_cur_mob()1102 void __pascal far redraw_at_cur_mob() {
1103 if (curmob.room == drawn_room) {
1104 redraw_height = 0x20;
1105 set_redraw_full(curr_tilepos, 1);
1106 set_wipe(curr_tilepos, 1);
1107 // Redraw tile to the right only if it's in the same room.
1108 if ((curr_tilepos % 10) + 1 < 10) { // changed
1109 set_redraw_full(curr_tilepos + 1, 1);
1110 set_wipe(curr_tilepos + 1, 1);
1111 }
1112 }
1113 }
1114
1115 // seg007:1387
mob_down_a_row()1116 void __pascal far mob_down_a_row() {
1117 ++curmob.row;
1118 if (curmob.row >= 3) {
1119 curmob.y -= 192;
1120 curmob.row = 0;
1121 curmob.room = level.roomlinks[curmob.room - 1].down;
1122 }
1123 }
1124
1125 // seg007:13AE
draw_mobs()1126 void __pascal far draw_mobs() {
1127 short index;
1128 for (index = 0; index < mobs_count; ++index) {
1129 curmob = mobs[index];
1130 draw_mob();
1131 }
1132 }
1133
1134 // seg007:13E5
draw_mob()1135 void __pascal far draw_mob() {
1136 short tile_row;
1137 short ypos;
1138 short top_row;
1139 short tilepos;
1140 short tile_col;
1141 ypos = curmob.y;
1142 if (curmob.room == drawn_room) {
1143 if (curmob.y >= 210) return;
1144 } else if (curmob.room == room_B) {
1145 if (ABS((sbyte)ypos) >= 18) return;
1146 curmob.y += 192;
1147 ypos = curmob.y;
1148 } else if (curmob.room == room_A) {
1149 if (curmob.y < 174) return;
1150 ypos = curmob.y - 189;
1151 } else {
1152 return;
1153 }
1154 tile_col = curmob.xh >> 2;
1155 tile_row = y_to_row_mod4(ypos);
1156 obj_tilepos = get_tilepos_nominus(tile_col, tile_row);
1157 ++tile_col;
1158 tilepos = get_tilepos(tile_col, tile_row);
1159 set_redraw2(tilepos, 1);
1160 set_redraw_fore(tilepos, 1);
1161 top_row = y_to_row_mod4(ypos - 18);
1162 if (top_row != tile_row) {
1163 tilepos = get_tilepos(tile_col, top_row);
1164 set_redraw2(tilepos, 1);
1165 set_redraw_fore(tilepos, 1);
1166 }
1167 add_mob_to_objtable(ypos);
1168 }
1169
1170 // seg007:14DE
add_mob_to_objtable(int ypos)1171 void __pascal far add_mob_to_objtable(int ypos) {
1172 word index;
1173 objtable_type* curr_obj;
1174 index = objtable_count++;
1175 curr_obj = &objtable[index];
1176 curr_obj->obj_type = curmob.type | 0x80;
1177 curr_obj->xh = curmob.xh;
1178 curr_obj->xl = 0;
1179 curr_obj->y = ypos;
1180 curr_obj->chtab_id = id_chtab_6_environment;
1181 curr_obj->id = 10;
1182 curr_obj->clip.top = 0;
1183 curr_obj->clip.left = 0;
1184 curr_obj->clip.right = 40;
1185 mark_obj_tile_redraw(index);
1186 }
1187
1188 // seg007:153E
sub_9A8E()1189 void __pascal far sub_9A8E() {
1190 // This function is not used.
1191 method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_top, &rect_top, 0);
1192 }
1193
1194 // seg007:1556
is_spike_harmful()1195 int __pascal far is_spike_harmful() {
1196 sbyte modifier;
1197 modifier = curr_room_modif[curr_tilepos];
1198 if (modifier == 0 || modifier == -1) {
1199 return 0;
1200 } else if (modifier < 0) {
1201 return 1;
1202 } else if (modifier < 5) {
1203 return 2;
1204 } else {
1205 return 0;
1206 }
1207 }
1208
1209 // seg007:1591
check_loose_fall_on_kid()1210 void __pascal far check_loose_fall_on_kid() {
1211 loadkid();
1212 if (Char.room == curmob.room &&
1213 Char.curr_col == curmob.xh >> 2 &&
1214 curmob.y < Char.y &&
1215 Char.y - 30 < curmob.y
1216 ) {
1217 fell_on_your_head();
1218 savekid();
1219 }
1220 }
1221
1222 // seg007:15D3
fell_on_your_head()1223 void __pascal far fell_on_your_head() {
1224 short frame;
1225 short action;
1226 frame = Char.frame;
1227 action = Char.action;
1228 // loose floors hurt you in frames 5..14 (running) only on level 13
1229 if (
1230 (current_level == 13 || (frame < frame_5_start_run || frame >= 15)) &&
1231 (action < actions_2_hang_climb || action == actions_7_turn)
1232 ) {
1233 Char.y = y_land[Char.curr_row + 1];
1234 if (take_hp(1)) {
1235 seqtbl_offset_char(seq_22_crushed); // dead (because of loose floor)
1236 if (frame == frame_177_spiked) { // spiked
1237 Char.x = char_dx_forward(-12);
1238 }
1239 } else {
1240 if (frame != frame_109_crouch) { // crouching
1241 if (get_tile_behind_char() == 0) {
1242 Char.x = char_dx_forward(-2);
1243 }
1244 seqtbl_offset_char(seq_52_loose_floor_fell_on_kid); // loose floor fell on Kid
1245 }
1246 }
1247 }
1248 }
1249
1250 // seg007:1669
play_door_sound_if_visible(int sound_id)1251 void __pascal far play_door_sound_if_visible(int sound_id) {
1252 word has_sound;
1253 word tilepos;
1254 word gate_room;
1255 tilepos = trob.tilepos;
1256 gate_room = trob.room;
1257 has_sound = 0;
1258
1259 #ifdef FIX_GATE_SOUNDS
1260 sbyte has_sound_condition;
1261 if (fixes->fix_gate_sounds)
1262 has_sound_condition = (gate_room == room_L && tilepos % 10 == 9) ||
1263 (gate_room == drawn_room && tilepos % 10 != 9);
1264 else
1265 has_sound_condition = gate_room == room_L ? tilepos % 10 == 9 :
1266 (gate_room == drawn_room && tilepos % 10 != 9);
1267 #define GATE_SOUND_CONDITION has_sound_condition
1268 #else
1269 #define GATE_SOUND_CONDITION gate_room == room_L ? tilepos % 10 == 9 : \
1270 (gate_room == drawn_room && tilepos % 10 != 9)
1271 #endif
1272 // Special event: sound of closing gates
1273 if ((current_level == 3 && gate_room == 2) || GATE_SOUND_CONDITION) {
1274 has_sound = 1;
1275 }
1276 if (has_sound) {
1277 play_sound(sound_id);
1278 }
1279 }
1280