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