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 #include <setjmp.h>
23 #include <math.h>
24 
25 // data:461E
26 dat_type * dathandle;
27 
28 // data:4C08
29 word need_redraw_because_flipped;
30 
31 void fix_sound_priorities();
32 
33 // seg000:0000
pop_main()34 void far pop_main() {
35 	if (check_param("--version") || check_param("-v")) {
36 		printf ("SDLPoP v%s\n", SDLPOP_VERSION);
37 		exit(0);
38 	}
39 
40 	if (check_param("--help") || check_param("-h") || check_param("-?")) {
41 		printf ("See doc/Readme.txt\n");
42 		exit(0);
43 	}
44 
45 	const char* temp = check_param("seed=");
46 	if (temp != NULL) {
47 		random_seed = atoi(temp+5);
48 		seed_was_init = 1;
49 	}
50 
51 	// debug only: check that the sequence table deobfuscation did not mess things up
52 	#ifdef CHECK_SEQTABLE_MATCHES_ORIGINAL
53 	check_seqtable_matches_original();
54 	#endif
55 
56 #ifdef FIX_SOUND_PRIORITIES
57 	fix_sound_priorities();
58 #endif
59 
60 	load_global_options();
61 	check_mod_param();
62 #ifdef USE_MENU
63 	load_ingame_settings();
64 #endif
65 	if (check_param("mute")) is_sound_on = 0;
66 	turn_sound_on_off((is_sound_on != 0) * 15); // Turn off sound/music if those options were set.
67 
68 #ifdef USE_REPLAY
69 	if (g_argc > 1) {
70 		char *filename = g_argv[1]; // file dragged on top of executable or double clicked
71 		char *e = strrchr(filename, '.');
72 		if (e != NULL && strcasecmp(e, ".P1R") == 0) { // valid replay filename passed as first arg
73 			start_with_replay_file(filename);
74 		}
75 	}
76 
77 	temp = check_param("validate");
78 	if (temp != NULL) {
79 		is_validate_mode = 1;
80 		start_with_replay_file(temp);
81 	}
82 #endif
83 
84 	load_mod_options();
85 
86 	// CusPop option
87 	is_blind_mode = custom->start_in_blind_mode;
88 	// Fix bug: with start_in_blind_mode enabled, moving objects are not displayed until blind mode is toggled off+on??
89 	need_drects = 1;
90 
91 	apply_seqtbl_patches();
92 
93 	char sprintf_temp[100];
94 	int i;
95 
96 	dathandle = open_dat("PRINCE.DAT", 0);
97 
98 	/*video_mode =*/ parse_grmode();
99 
100 	init_timer(BASE_FPS);
101 	parse_cmdline_sound();
102 
103 	set_hc_pal();
104 
105 	current_target_surface = rect_sthg(onscreen_surface_, &screen_rect);
106 	show_loading();
107 	set_joy_mode();
108 	cheats_enabled = check_param("megahit") != NULL;
109 #ifdef USE_DEBUG_CHEATS
110 	debug_cheats_enabled = check_param("debug") != NULL;
111 	if (debug_cheats_enabled) cheats_enabled = 1; // param 'megahit' not necessary if 'debug' is used
112 #endif
113 	draw_mode = check_param("draw") != NULL && cheats_enabled;
114 	demo_mode = check_param("demo") != NULL;
115 
116 	init_copyprot_dialog();
117 #ifdef USE_REPLAY
118 	init_record_replay();
119 #endif
120 
121 	if (cheats_enabled
122 		#ifdef USE_REPLAY
123 		|| recording
124 		#endif
125 	) {
126 		for (i = 15; i >= 0; --i) {
127 			snprintf(sprintf_temp, sizeof(sprintf_temp), "%d", i);
128 			if (check_param(sprintf_temp)) {
129 				start_level = i;
130 				break;
131 			}
132 		}
133 	}
134 
135 	play_demo_level = (check_param("playdemo") != NULL);
136 
137 #ifdef USE_SCREENSHOT
138 	init_screenshot();
139 #endif
140 
141 #ifdef USE_MENU
142 	init_menu();
143 #endif
144 
145 	init_game_main();
146 }
147 
148 byte* level_var_palettes;
149 
150 // seg000:024F
init_game_main()151 void __pascal far init_game_main() {
152 	doorlink1_ad = /*&*/level.doorlinks1;
153 	doorlink2_ad = /*&*/level.doorlinks2;
154 	prandom(1);
155 	if (graphics_mode == gmMcgaVga) {
156 		// Guard palettes
157 		guard_palettes = (byte*) load_from_opendats_alloc(10, "bin", NULL, NULL);
158 		// (blood, hurt flash) #E00030 = red
159 		set_pal(12, 0x38, 0x00, 0x0C, 1);
160 		// (palace wall pattern) #C09850 = light brown
161 		set_pal( 6, 0x30, 0x26, 0x14, 0);
162 
163 		// Level color variations (1.3)
164 		level_var_palettes = load_from_opendats_alloc(20, "bin", NULL, NULL);
165 	}
166 	// PRINCE.DAT: sword
167 	chtab_addrs[id_chtab_0_sword] = load_sprites_from_file(700, 1<<2, 1);
168 	// PRINCE.DAT: flame, sword on floor, potion
169 	chtab_addrs[id_chtab_1_flameswordpotion] = load_sprites_from_file(150, 1<<3, 1);
170 	close_dat(dathandle);
171 #ifdef USE_LIGHTING
172 	init_lighting();
173 #endif
174 	load_all_sounds();
175 
176 	hof_read();
177 	show_splash(); // added
178 	start_game();
179 }
180 
181 
182 // data:02C2
183 word first_start = 1;
184 // data:4C38
185 jmp_buf setjmp_buf;
186 // seg000:0358
start_game()187 void __pascal far start_game() {
188 #ifdef USE_COPYPROT
189 	word which_entry;
190 	word pos;
191 	word entry_used[40];
192 	byte letts_used[26];
193 #endif
194 	// Prevent filling of stack.
195 	// start_game is called from many places to restart the game, for example:
196 	// process_key, play_frame, draw_game_frame, play_level, control_kid, end_sequence, expired
197 	if (first_start) {
198 		first_start = 0;
199 		setjmp(/*&*/setjmp_buf);
200 	} else {
201 		draw_rect(&screen_rect, 0);
202 		show_quotes();
203 		clear_screen_and_sounds();
204 		longjmp(/*&*/setjmp_buf,-1);
205 	}
206 	release_title_images(); // added
207 	free_optsnd_chtab(); // added
208 #ifdef USE_COPYPROT
209 	copyprot_plac = prandom(13);
210 	memset(&entry_used, 0, sizeof(entry_used));
211 	memset(&letts_used, 0, sizeof(letts_used));
212 	for (pos = 0; pos < 14; ++pos) {
213 		do {
214 			if (pos == copyprot_plac) {
215 				which_entry = copyprot_idx = prandom(39);
216 			} else {
217 				which_entry = prandom(39);
218 			}
219 		} while (entry_used[which_entry] || letts_used[copyprot_letter[which_entry]-'A']);
220 		cplevel_entr[pos] = which_entry;
221 		entry_used[which_entry] = 1;
222 		letts_used[copyprot_letter[which_entry]-'A'] = 1;
223 	}
224 #endif
225 	if (custom->skip_title) { // CusPop option: skip the title sequence (level loads instantly)
226 		int level_number = (start_level >= 0) ? start_level : custom->first_level;
227 		init_game(level_number);
228 		return;
229 	}
230 
231 	if (start_level < 0) {
232 		show_title();
233 	} else {
234 		init_game(start_level);
235 	}
236 }
237 
238 #ifdef USE_QUICKSAVE
239 // All these functions return true on success, false otherwise.
240 
241 FILE* quick_fp;
242 
process_save(void * data,size_t data_size)243 int process_save(void* data, size_t data_size) {
244 	return fwrite(data, data_size, 1, quick_fp) == 1;
245 }
246 
process_load(void * data,size_t data_size)247 int process_load(void* data, size_t data_size) {
248 	return fread(data, data_size, 1, quick_fp) == 1;
249 }
250 
251 typedef int process_func_type(void* data, size_t data_size);
252 
quick_process(process_func_type process_func)253 int quick_process(process_func_type process_func) {
254 	int ok = 1;
255 #define process(x) ok = ok && process_func(&(x), sizeof(x))
256 	// level
257 #ifdef USE_DEBUG_CHEATS
258 	// Don't load the level if the user holds either Shift key while pressing F9.
259 	if (debug_cheats_enabled && (key_states[SDL_SCANCODE_LSHIFT] || key_states[SDL_SCANCODE_RSHIFT])) {
260 		fseek(quick_fp, sizeof(level), SEEK_CUR);
261 	} else
262 #endif
263 	{
264 		process(level);
265 	}
266 	process(checkpoint);
267 	process(upside_down);
268 	process(drawn_room);
269 	process(current_level);
270 	process(next_level);
271 	process(mobs_count);
272 	process(mobs);
273 	process(trobs_count);
274 	process(trobs);
275 	process(leveldoor_open);
276 	//process(exit_room_timer);
277 	// kid
278 	process(Kid);
279 	process(hitp_curr);
280 	process(hitp_max);
281 	process(hitp_beg_lev);
282 	process(grab_timer);
283 	process(holding_sword);
284 	process(united_with_shadow);
285 	process(have_sword);
286 	/*process(ctrl1_forward);
287 	process(ctrl1_backward);
288 	process(ctrl1_up);
289 	process(ctrl1_down);
290 	process(ctrl1_shift2);*/
291 	process(kid_sword_strike);
292 	process(pickup_obj_type);
293 	process(offguard);
294 	// guard
295 	process(Guard);
296 	process(Char);
297 	process(Opp);
298 	process(guardhp_curr);
299 	process(guardhp_max);
300 	process(demo_index);
301 	process(demo_time);
302 	process(curr_guard_color);
303 	process(guard_notice_timer);
304 	process(guard_skill);
305 	process(shadow_initialized);
306 	process(guard_refrac);
307 	process(justblocked);
308 	process(droppedout);
309 	// collision
310 	process(curr_row_coll_room);
311 	process(curr_row_coll_flags);
312 	process(below_row_coll_room);
313 	process(below_row_coll_flags);
314 	process(above_row_coll_room);
315 	process(above_row_coll_flags);
316 	process(prev_collision_row);
317 	// flash
318 	process(flash_color);
319 	process(flash_time);
320 	// sounds
321 	process(need_level1_music);
322 	process(is_screaming);
323 	process(is_feather_fall);
324 	process(last_loose_sound);
325 	//process(next_sound);
326 	//process(current_sound);
327 	// random
328 	process(random_seed);
329 	// remaining time
330 	process(rem_min);
331 	process(rem_tick);
332 	// saved controls
333 	process(control_x);
334 	process(control_y);
335 	process(control_shift);
336 	process(control_forward);
337 	process(control_backward);
338 	process(control_up);
339 	process(control_down);
340 	process(control_shift2);
341 	process(ctrl1_forward);
342 	process(ctrl1_backward);
343 	process(ctrl1_up);
344 	process(ctrl1_down);
345 	process(ctrl1_shift2);
346 	// replay recording state
347 #ifdef USE_REPLAY
348 	process(curr_tick);
349 #endif
350 #ifdef USE_COLORED_TORCHES
351 	process(torch_colors);
352 #endif
353 #undef process
354 	return ok;
355 }
356 
357 const char* quick_file = "QUICKSAVE.SAV";
358 const char quick_version[] = "V1.16b4 ";
359 char quick_control[] = "........";
360 
get_quick_path(char * custom_path_buffer,size_t max_len)361 const char* get_quick_path(char* custom_path_buffer, size_t max_len) {
362 	if (!use_custom_levelset) {
363 		return quick_file;
364 	}
365 	// if playing a custom levelset, try to use the mod folder
366 	snprintf_check(custom_path_buffer, max_len, "%s/%s", mod_data_path, quick_file /*QUICKSAVE.SAV*/ );
367 	return custom_path_buffer;
368 }
369 
quick_save()370 int quick_save() {
371 	int ok = 0;
372 	char custom_quick_path[POP_MAX_PATH];
373 	const char* path = get_quick_path(custom_quick_path, sizeof(custom_quick_path));
374 	quick_fp = fopen(path, "wb");
375 	if (quick_fp != NULL) {
376 		process_save((void*) quick_version, COUNT(quick_version));
377 		ok = quick_process(process_save);
378 		fclose(quick_fp);
379 		quick_fp = NULL;
380 	}
381 	return ok;
382 }
383 
restore_room_after_quick_load()384 void restore_room_after_quick_load() {
385 	int temp1 = curr_guard_color;
386 	int temp2 = next_level;
387 	reset_level_unused_fields(false);
388 	load_lev_spr(current_level);
389 	curr_guard_color = temp1;
390 	next_level = temp2;
391 
392 	// feather fall can only get restored if the fix enabled
393 	if (!fixes->fix_quicksave_during_feather && is_feather_fall > 0) {
394 		is_feather_fall = 0;
395 		stop_sounds();
396 	}
397 
398 	//need_full_redraw = 1;
399 	different_room = 1;
400 	// Show the room where the prince is, even if the player moved the view away from it (with the H,J,U,N keys).
401 	next_room = drawn_room = Kid.room;
402 	load_room_links();
403 	//draw_level_first();
404 	//gen_palace_wall_colors();
405 	is_guard_notice = 0; // prevent guard turning around immediately
406 	draw_game_frame(); // for falling
407 	//redraw_screen(1); // for room_L
408 
409 	hitp_delta = guardhp_delta = 1; // force HP redraw
410 	// Don't draw guard HP if a previously viewed room (with the H,J,U,N keys) had a guard but the current room doesn't have one.
411 	if (Guard.room != drawn_room) {
412 		// Like in clear_char().
413 		Guard.direction = dir_56_none;
414 		guardhp_curr = 0;
415 	}
416 
417 	draw_hp();
418 	loadkid_and_opp();
419 	// Get rid of "press button" message if kid was dead before quickload.
420 	text_time_total = text_time_remaining = 0;
421 	//next_sound = current_sound = -1;
422 	exit_room_timer = 0;
423 }
424 
quick_load()425 int quick_load() {
426 	int ok = 0;
427 	char custom_quick_path[POP_MAX_PATH];
428 	const char* path = get_quick_path(custom_quick_path, sizeof(custom_quick_path));
429 	quick_fp = fopen(path, "rb");
430 	if (quick_fp != NULL) {
431 		// check quicksave version is compatible
432 		process_load(quick_control, COUNT(quick_control));
433 		if (strcmp(quick_control, quick_version) != 0) {
434 			fclose(quick_fp);
435 			quick_fp = NULL;
436 			return 0;
437 		}
438 
439 		stop_sounds();
440 		draw_rect(&screen_rect, 0);
441 		update_screen();
442 		delay_ticks(5); // briefly display a black screen as a visual cue
443 
444 		short old_rem_min = rem_min;
445 		word old_rem_tick = rem_tick;
446 
447 		ok = quick_process(process_load);
448 		fclose(quick_fp);
449 		quick_fp = NULL;
450 
451 		restore_room_after_quick_load();
452 		update_screen();
453 
454 		#ifdef USE_QUICKLOAD_PENALTY
455 		// Subtract one minute from the remaining time (if it is above 5 minutes)
456 		if (enable_quicksave_penalty &&
457 			// don't apply the penalty after time has already stopped!
458 			(current_level < /*13*/ custom->victory_stops_time_level || (current_level == /*13*/ custom->victory_stops_time_level && leveldoor_open < 2))
459 		) {
460 			int ticks_elapsed = 720 * (rem_min - old_rem_min) + (rem_tick - old_rem_tick);
461 			// don't restore time at all if the elapsed time is between 0 and 1 minutes
462 			if (ticks_elapsed > 0 && ticks_elapsed < 720) {
463 				rem_min = old_rem_min;
464 				rem_tick = old_rem_tick;
465 			}
466 			else {
467 				if (rem_min == 6) rem_tick = 719; // crop to "5 minutes" exactly, if hitting the threshold in <1 minute
468 				if (rem_min > 5 /*be lenient, not much time left*/ || rem_min < 0 /*time runs 'forward' if < 0*/) {
469 					--rem_min;
470 				}
471 			}
472 
473 		}
474 		#endif
475 	}
476 	return ok;
477 }
478 
check_quick_op()479 void check_quick_op() {
480 	if (!enable_quicksave) return;
481 	if (need_quick_save) {
482 		if ((!is_feather_fall || fixes->fix_quicksave_during_feather) && quick_save()) {
483 			display_text_bottom("QUICKSAVE");
484 		} else {
485 			display_text_bottom("NO QUICKSAVE");
486 		}
487 		need_quick_save = 0;
488 		text_time_total = 24;
489 		text_time_remaining = 24;
490 	}
491 	if (need_quick_load) {
492 /*
493 #ifdef USE_REPLAY
494 		if (recording) {
495 			stop_recording(); // quickloading would mess up the replay!
496 		}
497 #endif
498 */
499 		if (quick_load()) {
500 			display_text_bottom("QUICKLOAD");
501 		} else {
502 			display_text_bottom("NO QUICKLOAD");
503 		}
504 		need_quick_load = 0;
505 		text_time_total = 24;
506 		text_time_remaining = 24;
507 	}
508 }
509 
510 
511 #endif // USE_QUICKSAVE
512 
temp_shift_release_callback(Uint32 interval,void * param)513 Uint32 temp_shift_release_callback(Uint32 interval, void *param) {
514 	const Uint8* state = SDL_GetKeyboardState(NULL);
515 	if (state[SDL_SCANCODE_LSHIFT]) key_states[SDL_SCANCODE_LSHIFT] = 1;
516 	if (state[SDL_SCANCODE_RSHIFT]) key_states[SDL_SCANCODE_RSHIFT] = 1;
517 	return 0; // causes the timer to be removed
518 }
519 
520 // seg000:04CD
process_key()521 int __pascal far process_key() {
522 	char sprintf_temp[80];
523 	int key;
524 	const char* answer_text = NULL;
525 	word need_show_text;
526 	need_show_text = 0;
527 	key = key_test_quit();
528 
529 #ifdef USE_MENU
530 	if (is_paused && is_menu_shown) {
531 		key = key_test_paused_menu(key);
532 		if (key == 0) return 0;
533 	}
534 #endif
535 
536 	if (start_level < 0) {
537 		if (key || control_shift) {
538 			#ifdef USE_QUICKSAVE
539 			if (key == SDL_SCANCODE_F9) need_quick_load = 1;
540 			#endif
541 			#ifdef USE_REPLAY
542 			if (key == SDL_SCANCODE_TAB || need_start_replay) {
543 				start_replay();
544 			}
545 			else if (key == (SDL_SCANCODE_TAB | WITH_CTRL)) {
546 				start_level = custom->first_level;
547 				start_recording();
548 			} else
549 			#endif
550 			if (key == (SDL_SCANCODE_L | WITH_CTRL)) { // Ctrl+L
551 				if (!load_game()) return 0;
552 			} else {
553 				start_level = custom->first_level; // 1
554 			}
555 			draw_rect(&screen_rect, 0);
556 #ifdef USE_FADE
557 			if (is_global_fading) {
558 				fade_palette_buffer->proc_restore_free(fade_palette_buffer);
559 				is_global_fading = 0;
560 			}
561 #endif
562 			start_game();
563 		}
564 	}
565 	// If the Kid died, Enter or Shift will restart the level.
566 	if (rem_min != 0 && Kid.alive > 6 && (control_shift || key == SDL_SCANCODE_RETURN)) {
567 		key = SDL_SCANCODE_A | WITH_CTRL; // Ctrl+A
568 	}
569 #ifdef USE_REPLAY
570 	if (recording) key_press_while_recording(&key);
571 	else if (replaying) key_press_while_replaying(&key);
572 #endif
573 	if (key == 0) return 0;
574 	if (is_keyboard_mode) clear_kbd_buf();
575 
576 	switch(key) {
577 		case SDL_SCANCODE_ESCAPE: // Esc
578 		case SDL_SCANCODE_ESCAPE | WITH_SHIFT: // allow pause while grabbing
579 			is_paused = 1;
580 #ifdef USE_MENU
581 			if (enable_pause_menu && !is_cutscene && !is_ending_sequence) {
582 				is_menu_shown = 1;
583 			}
584 		break;
585 		case SDL_SCANCODE_BACKSPACE:
586 			if (!is_cutscene && !is_ending_sequence) {
587 				is_paused = 1;
588 				is_menu_shown = 1;
589 			}
590 #endif
591 		break;
592 		case SDL_SCANCODE_SPACE: // Space
593 			is_show_time = 1;
594 		break;
595 		case SDL_SCANCODE_A | WITH_CTRL: // Ctrl+A
596 			if (current_level != 15) {
597 				stop_sounds();
598 				is_restart_level = 1;
599 			}
600 		break;
601 		case SDL_SCANCODE_G | WITH_CTRL: // Ctrl+G
602 			// CusPoP: first and last level where saving is allowed
603 //			if (current_level > 2 && current_level < 14) { // original
604 			if (current_level >= custom->saving_allowed_first_level && current_level <= custom->saving_allowed_last_level) {
605 				save_game();
606 			}
607 		break;
608 		case SDL_SCANCODE_J | WITH_CTRL: // Ctrl+J
609 			if ((sound_flags & sfDigi) && sound_mode == smTandy) {
610 				answer_text = "JOYSTICK UNAVAILABLE";
611 			} else {
612 				if (set_joy_mode()) {
613 					answer_text = "JOYSTICK MODE";
614 				} else {
615 					answer_text = "JOYSTICK NOT FOUND";
616 				}
617 			}
618 			need_show_text = 1;
619 		break;
620 		case SDL_SCANCODE_K | WITH_CTRL: // Ctrl+K
621 			answer_text = "KEYBOARD MODE";
622 			is_joyst_mode = 0;
623 			is_keyboard_mode = 1;
624 			need_show_text = 1;
625 		break;
626 		case SDL_SCANCODE_R | WITH_CTRL: // Ctrl+R
627 			start_level = -1;
628 #ifdef USE_MENU
629 			if (is_menu_shown) menu_was_closed(); // Do necessary cleanup.
630 #endif
631 			start_game();
632 		break;
633 		case SDL_SCANCODE_S | WITH_CTRL: // Ctrl+S
634 			turn_sound_on_off((!is_sound_on) * 15);
635 			answer_text = "SOUND OFF";
636 			if (is_sound_on) {
637 				answer_text = "SOUND ON";
638 			}
639 			//
640 			need_show_text = 1;
641 		break;
642 		case SDL_SCANCODE_V | WITH_CTRL: // Ctrl+V
643 			//answer_text = "PRINCE OF PERSIA  V1.0";
644 			snprintf(sprintf_temp, sizeof(sprintf_temp), "SDLPoP v%s\n", SDLPOP_VERSION);
645 			answer_text = sprintf_temp;
646 			need_show_text = 1;
647 		break;
648 		case SDL_SCANCODE_C | WITH_CTRL: // Ctrl+C
649 		{
650 			SDL_version verc, verl;
651 			SDL_VERSION (&verc);
652 			SDL_GetVersion (&verl);
653 			snprintf (sprintf_temp, sizeof (sprintf_temp),
654 				"SDL COMP v%u.%u.%u LINK v%u.%u.%u",
655 				verc.major, verc.minor, verc.patch,
656 				verl.major, verl.minor, verl.patch);
657 			answer_text = sprintf_temp;
658 			need_show_text = 1;
659 		}
660 		break;
661 		case SDL_SCANCODE_L | WITH_SHIFT: // Shift+L
662 			if (current_level < custom->shift_L_allowed_until_level /* 4 */ || cheats_enabled) {
663 				// if Shift is not released within the delay, the cutscene is skipped
664 				Uint32 delay = 250;
665 				key_states[SDL_SCANCODE_LSHIFT] = 0;
666 				key_states[SDL_SCANCODE_RSHIFT] = 0;
667 				SDL_TimerID timer;
668 				timer = SDL_AddTimer(delay, temp_shift_release_callback, NULL);
669 				if (timer == 0) {
670 					sdlperror("process_key: SDL_AddTimer");
671 					quit(1);
672 				}
673 				if (current_level == 14) {
674 					next_level = 1;
675 				} else {
676 					if (current_level == 15 && cheats_enabled) {
677 #ifdef USE_COPYPROT
678 						if (enable_copyprot) {
679 							next_level = custom->copyprot_level;
680 							custom->copyprot_level = -1;
681 						}
682 #endif
683 					} else {
684 						next_level = current_level + 1;
685 						if (!cheats_enabled && rem_min > custom->shift_L_reduced_minutes /* 15 */) {
686 							rem_min = custom->shift_L_reduced_minutes; // 15
687 							rem_tick = custom->shift_L_reduced_ticks; // 719
688 						}
689 					}
690 				}
691 				stop_sounds();
692 			}
693 		break;
694 #ifdef USE_QUICKSAVE
695 		case SDL_SCANCODE_F6:
696 		case SDL_SCANCODE_F6 | WITH_SHIFT:
697 			if (Kid.alive < 0) need_quick_save = 1;
698 		break;
699 		case SDL_SCANCODE_F9:
700 		case SDL_SCANCODE_F9 | WITH_SHIFT:
701 			need_quick_load = 1;
702 		break;
703 #ifdef USE_REPLAY
704 		case SDL_SCANCODE_TAB | WITH_CTRL:
705 		case SDL_SCANCODE_TAB | WITH_CTRL | WITH_SHIFT:
706 			if (recording) { // finished recording
707 				stop_recording();
708 			}
709 			else { // should start recording
710 				start_recording();
711 			}
712 		break;
713 #endif // USE_REPLAY
714 #endif // USE_QUICKSAVE
715 	}
716 	if (cheats_enabled) {
717 		switch (key) {
718 			case SDL_SCANCODE_C: // c
719 				snprintf(sprintf_temp, sizeof(sprintf_temp), "S%d L%d R%d A%d B%d", drawn_room, room_L, room_R, room_A, room_B);
720 				answer_text = /*&*/sprintf_temp;
721 				need_show_text = 1;
722 			break;
723 			case SDL_SCANCODE_C | WITH_SHIFT: // Shift+C
724 				snprintf(sprintf_temp, sizeof(sprintf_temp), "AL%d AR%d BL%d BR%d", room_AL, room_AR, room_BL, room_BR);
725 				answer_text = /*&*/sprintf_temp;
726 				need_show_text = 1;
727 			break;
728 			case SDL_SCANCODE_MINUS:
729 			case SDL_SCANCODE_KP_MINUS:		// '-' --> subtract time cheat
730 				if (rem_min > 1) --rem_min;
731 
732 #ifdef ALLOW_INFINITE_TIME
733 				else if (rem_min < -1) ++rem_min; // if negative/infinite, time runs 'forward'
734 				else if (rem_min == -1) rem_tick = 720; // resets the timer to 00:00:00
735 #endif
736 
737 				text_time_total = 0;
738 				text_time_remaining = 0;
739 				is_show_time = 1;
740 			break;
741 			case SDL_SCANCODE_EQUALS | WITH_SHIFT: // '+'
742 			case SDL_SCANCODE_KP_PLUS:	   // '+' --> add time cheat
743 
744 #ifdef ALLOW_INFINITE_TIME
745 				if (rem_min < 0) { // if negative/infinite, time runs 'forward'
746 					if (rem_min > INT16_MIN) --rem_min;
747 				}
748 				else ++rem_min;
749 #else
750 				++rem_min;
751 #endif
752 
753 				text_time_total = 0;
754 				text_time_remaining = 0;
755 				is_show_time = 1;
756 			break;
757 			case SDL_SCANCODE_R: // R --> revive kid cheat
758 				if (Kid.alive > 0) {
759 					resurrect_time = 20;
760 					Kid.alive = -1;
761 					erase_bottom_text(1);
762 				}
763 			break;
764 			case SDL_SCANCODE_K: // K --> kill guard cheat
765 				if (Guard.charid != charid_4_skeleton) {
766 					guardhp_delta = -guardhp_curr;
767 					Guard.alive = 0;
768 				}
769 			break;
770 			case SDL_SCANCODE_I | WITH_SHIFT: // Shift+I --> invert cheat
771 				toggle_upside();
772 			break;
773 			case SDL_SCANCODE_W | WITH_SHIFT: // Shift+W --> feather fall cheat
774 				feather_fall();
775 			break;
776 			case SDL_SCANCODE_H: // H --> view room to the left
777 				draw_guard_hp(0, 10);
778 				next_room = room_L;
779 			break;
780 			case SDL_SCANCODE_J: // J --> view room to the right
781 				draw_guard_hp(0, 10);
782 				next_room = room_R;
783 			break;
784 			case SDL_SCANCODE_U: // U --> view room above
785 				draw_guard_hp(0, 10);
786 				next_room = room_A;
787 			break;
788 			case SDL_SCANCODE_N: // N --> view room below
789 				draw_guard_hp(0, 10);
790 				next_room = room_B;
791 			break;
792 			case SDL_SCANCODE_B | WITH_CTRL: // Ctrl+B --> Go back to the room where the prince is. (Undo H,J,U,N.)
793 				draw_guard_hp(0, 10);
794 				next_room = Kid.room;
795 			break;
796 			case SDL_SCANCODE_B | WITH_SHIFT: // Shift+B
797 				is_blind_mode = !is_blind_mode;
798 				if (is_blind_mode) {
799 					draw_rect(&rect_top, 0);
800 				} else {
801 					need_full_redraw = 1;
802 				}
803 			break;
804 			case SDL_SCANCODE_S | WITH_SHIFT: // Shift+S
805 				if (hitp_curr != hitp_max) {
806 					play_sound(sound_33_small_potion); // small potion (cheat)
807 					hitp_delta = 1;
808 					flash_color = 4; // red
809 					flash_time = 2;
810 				}
811 			break;
812 			case SDL_SCANCODE_T | WITH_SHIFT: // Shift+T
813 				play_sound(sound_30_big_potion); // big potion (cheat)
814 				flash_color = 4; // red
815 				flash_time = 4;
816 				add_life();
817 			break;
818 			#ifdef USE_DEBUG_CHEATS
819 			case SDL_SCANCODE_T:
820 				is_timer_displayed = 1 - is_timer_displayed; // toggle
821 			break;
822 			case SDL_SCANCODE_F:
823 				if (fixes->fix_quicksave_during_feather) {
824 					is_feather_timer_displayed = 1 - is_feather_timer_displayed; // toggle
825 				} else {
826 					is_feather_timer_displayed = 0;
827 				}
828 			break;
829 			#endif
830 		}
831 	}
832 
833 	if (need_show_text) {
834 		display_text_bottom(answer_text);
835 		text_time_total = 24;
836 		text_time_remaining = 24;
837 	}
838 	return 1;
839 }
840 
841 // seg000:08EB
play_frame()842 void __pascal far play_frame() {
843 	// play feather fall music if there is more than 1 second of feather fall left
844 	if (fixes->fix_quicksave_during_feather && is_feather_fall >= 10 && !check_sound_playing()) {
845 		play_sound(sound_39_low_weight);
846 	}
847 	do_mobs();
848 	process_trobs();
849 	check_skel();
850 	check_can_guard_see_kid();
851 	// if level is restarted, return immediately
852 	if (play_kid_frame()) return;
853 	play_guard_frame();
854 	if (0 == resurrect_time) {
855 		check_sword_hurting();
856 		check_sword_hurt();
857 	}
858 	check_sword_vs_sword();
859 	do_delta_hp();
860 	exit_room();
861 	check_the_end();
862 	check_guard_fallout();
863 	if (current_level == 0) {
864 		// Special event: level 0 running exit
865 		if (Kid.room == /*24*/ custom->demo_end_room) {
866 			draw_rect(&screen_rect, 0);
867 			start_level = -1;
868 			need_quotes = 1;
869 			start_game();
870 		}
871 	} else if(current_level == /*6*/ custom->falling_exit_level) {
872 		// Special event: level 6 falling exit
873 		if (roomleave_result == -2) {
874 			Kid.y = -1;
875 			stop_sounds();
876 			++next_level;
877 		}
878 	} else if(/*current_level == 12*/ custom->tbl_seamless_exit[current_level] >= 0) {
879 		// Special event: level 12 running exit
880 		if (Kid.room == /*23*/ custom->tbl_seamless_exit[current_level]) {
881 			++next_level;
882 // Sounds must be stopped, because play_level_2() checks next_level only if there are no sounds playing.
883 			stop_sounds();
884 			seamless = 1;
885 		}
886 	}
887 	show_time();
888 	// expiring doesn't count on Jaffar/princess level
889 	if (current_level < 13 && rem_min == 0) {
890 		expired();
891 	}
892 }
893 
894 // seg000:09B6
draw_game_frame()895 void __pascal far draw_game_frame() {
896 	short var_2;
897 	if (need_full_redraw) {
898 		redraw_screen(0);
899 		need_full_redraw = 0;
900 	} else {
901 		if (different_room) {
902 			drawn_room = next_room;
903 			if (custom->tbl_level_type[current_level]) {
904 				gen_palace_wall_colors();
905 			}
906 			redraw_screen(1);
907 		} else {
908 			if (need_redraw_because_flipped) {
909 				need_redraw_because_flipped = 0;
910 				redraw_screen(0);
911 			} else {
912 				memset_near(&table_counts, 0, sizeof(table_counts));
913 				draw_moving();
914 				draw_tables();
915 				if (is_blind_mode) {
916 					draw_rect(&rect_top, 0);
917 				}
918 				if (upside_down) {
919 					flip_screen(offscreen_surface);
920 				}
921 				while (drects_count--) {
922 					copy_screen_rect(&drects[drects_count]);
923 				}
924 				if (upside_down) {
925 					flip_screen(offscreen_surface);
926 				}
927 				drects_count = 0;
928 			}
929 		}
930 	}
931 
932 	play_next_sound();
933 	// Note: texts are identified by their total time!
934 	if (text_time_remaining == 1) {
935 		// If the text's is about to expire:
936 		if (text_time_total == 36 || text_time_total == 288) {
937 			// 36: died on demo/potions level
938 			// 288: press button to continue
939 			// In this case, restart the game.
940 			start_level = -1;
941 			need_quotes = 1;
942 
943 #ifdef USE_REPLAY
944 			if (recording) stop_recording();
945 			if (replaying) end_replay();
946 #endif
947 
948 			start_game();
949 		} else {
950 			// Otherwise, just clear it.
951 			erase_bottom_text(1);
952 		}
953 	} else {
954 		if (text_time_remaining != 0 && text_time_total != 1188) {
955 			// 1188: potions level (page/line/word) -- this one does not disappear
956 			--text_time_remaining;
957 			if (text_time_total == 288 && text_time_remaining < 72) {
958 				// 288: press button to continue
959 				// Blink the message:
960 				var_2 = text_time_remaining % 12;
961 				if (var_2 > 3) {
962 					erase_bottom_text(0);
963 				} else {
964 					if (var_2 == 3) {
965 						display_text_bottom("Press Button to Continue");
966 						play_sound_from_buffer(sound_pointers[sound_38_blink]); // press button blink
967 					}
968 				}
969 			}
970 		}
971 	}
972 }
973 
974 // seg000:0B12
anim_tile_modif()975 void __pascal far anim_tile_modif() {
976 	word tilepos;
977 	for (tilepos = 0; tilepos < 30; ++tilepos) {
978 		switch (get_curr_tile(tilepos)) {
979 			case tiles_10_potion:
980 				start_anim_potion(drawn_room, tilepos);
981 			break;
982 			case tiles_19_torch:
983 			case tiles_30_torch_with_debris:
984 				start_anim_torch(drawn_room, tilepos);
985 			break;
986 			case tiles_22_sword:
987 				start_anim_sword(drawn_room, tilepos);
988 			break;
989 		}
990 	}
991 
992 	// Animate torches in the rightmost column of the left-side room as well, because they are visible in the current room.
993 	for (int row = 0; row <= 2; row++) {
994 		switch (get_tile(room_L, 9, row)) {
995 			case tiles_19_torch:
996 			case tiles_30_torch_with_debris:
997 				start_anim_torch(room_L, row * 10 + 9);
998 			break;
999 		}
1000 	}
1001 }
1002 
1003 // seg000:0B72
load_sounds(int first,int last)1004 void __pascal far load_sounds(int first,int last) {
1005 	dat_type* ibm_dat = NULL;
1006 	dat_type* digi1_dat = NULL;
1007 //	dat_type* digi2_dat = NULL;
1008 	dat_type* digi3_dat = NULL;
1009 	dat_type* midi_dat = NULL;
1010 	short current;
1011 	ibm_dat = open_dat("IBM_SND1.DAT", 0);
1012 	if (sound_flags & sfDigi) {
1013 		digi1_dat = open_dat("DIGISND1.DAT", 0);
1014 //		digi2_dat = open_dat("DIGISND2.DAT", 0);
1015 		digi3_dat = open_dat("DIGISND3.DAT", 0);
1016 	}
1017 	if (sound_flags & sfMidi) {
1018 		midi_dat = open_dat("MIDISND1.DAT", 0);
1019 	}
1020 
1021 	load_sound_names();
1022 
1023 	for (current = first; current <= last; ++current) {
1024 		if (sound_pointers[current] != NULL) continue;
1025 		/*if (demo_mode) {
1026 			sound_pointers[current] = decompress_sound((sound_buffer_type*) load_from_opendats_alloc(current + 10000));
1027 		} else*/ {
1028 			//sound_pointers[current] = (sound_buffer_type*) load_from_opendats_alloc(current + 10000, "bin", NULL, NULL);
1029 			//printf("overwriting sound_pointers[%d] = %p\n", current, sound_pointers[current]);
1030 
1031 
1032 			sound_pointers[current] = load_sound(current);
1033 		}
1034 	}
1035 	if (midi_dat) close_dat(midi_dat);
1036 	if (digi1_dat) close_dat(digi1_dat);
1037 //	if (digi2_dat) close_dat(digi2_dat);
1038 	if (digi3_dat) close_dat(digi3_dat);
1039 	close_dat(ibm_dat);
1040 }
1041 
1042 // seg000:0C5E
load_opt_sounds(int first,int last)1043 void __pascal far load_opt_sounds(int first,int last) {
1044 	// stub
1045 	dat_type* ibm_dat = NULL;
1046 	dat_type* digi_dat = NULL;
1047 	dat_type* midi_dat = NULL;
1048 	short current;
1049 	ibm_dat = open_dat("IBM_SND2.DAT", 0);
1050 	if (sound_flags & sfDigi) {
1051 		digi_dat = open_dat("DIGISND2.DAT", 0);
1052 	}
1053 	if (sound_flags & sfMidi) {
1054 		midi_dat = open_dat("MIDISND2.DAT", 0);
1055 	}
1056 	for (current = first; current <= last; ++current) {
1057 		//We don't free sounds, so load only once.
1058 		if (sound_pointers[current] != NULL) continue;
1059 		/*if (demo_mode) {
1060 			sound_pointers[current] = decompress_sound((sound_buffer_type*) load_from_opendats_alloc(current + 10000));
1061 		} else*/ {
1062 			//sound_pointers[current] = (sound_buffer_type*) load_from_opendats_alloc(current + 10000, "bin", NULL, NULL);
1063 			//printf("overwriting sound_pointers[%d] = %p\n", current, sound_pointers[current]);
1064 			sound_pointers[current] = load_sound(current);
1065 		}
1066 	}
1067 	if (midi_dat) close_dat(midi_dat);
1068 	if (digi_dat) close_dat(digi_dat);
1069 	close_dat(ibm_dat);
1070 }
1071 
1072 // data:03BA
1073 const char*const tbl_guard_dat[] = {"GUARD.DAT", "FAT.DAT", "SKEL.DAT", "VIZIER.DAT", "SHADOW.DAT"};
1074 // data:03C4
1075 const char*const tbl_envir_gr[] = {"", "C", "C", "E", "E", "V"};
1076 // data:03D0
1077 const char*const tbl_envir_ki[] = {"DUNGEON", "PALACE"};
1078 // seg000:0D20
load_lev_spr(int level)1079 void __pascal far load_lev_spr(int level) {
1080 	dat_type* dathandle;
1081 	short guardtype;
1082 	char filename[20];
1083 	dathandle = NULL;
1084 	current_level = next_level = level;
1085 	draw_rect(&screen_rect, 0);
1086 	free_optsnd_chtab();
1087 	snprintf(filename, sizeof(filename), "%s%s.DAT",
1088 		tbl_envir_gr[graphics_mode],
1089 		tbl_envir_ki[custom->tbl_level_type[current_level]]
1090 	);
1091 	load_chtab_from_file(id_chtab_6_environment, 200, filename, 1<<5);
1092 	load_more_opt_graf(filename);
1093 	guardtype = custom->tbl_guard_type[current_level];
1094 	if (guardtype != -1) {
1095 		if (guardtype == 0) {
1096 			dathandle = open_dat(custom->tbl_level_type[current_level] ? "GUARD1.DAT" : "GUARD2.DAT", 0);
1097 		}
1098 		load_chtab_from_file(id_chtab_5_guard, 750, tbl_guard_dat[guardtype], 1<<8);
1099 		if (dathandle) {
1100 			close_dat(dathandle);
1101 		}
1102 	}
1103 	curr_guard_color = 0;
1104 	load_chtab_from_file(id_chtab_7_environmentwall, 360, filename, 1<<6);
1105 
1106 	// Level colors (1.3)
1107 	if (graphics_mode == gmMcgaVga && level_var_palettes != NULL) {
1108 		int level_color = custom->tbl_level_color[current_level];
1109 		if (level_color != 0) {
1110 			byte* env_pal = level_var_palettes + 0x30*(level_color-1);
1111 			byte* wall_pal = env_pal + 0x30 * custom->tbl_level_type[current_level];
1112 			set_pal_arr(0x50, 0x10, (rgb_type*)env_pal, 1);
1113 			set_pal_arr(0x60, 0x10, (rgb_type*)wall_pal, 1);
1114 			set_chtab_palette(chtab_addrs[id_chtab_6_environment], env_pal, 0x10);
1115 			set_chtab_palette(chtab_addrs[id_chtab_7_environmentwall], wall_pal, 0x10);
1116 		}
1117 	}
1118 
1119 	/*if (comp_skeleton[current_level])*/ {
1120 		load_opt_sounds(44, 44); // skel alive
1121 	}
1122 	/*if (comp_mirror[current_level])*/ {
1123 		load_opt_sounds(45, 45); // mirror
1124 	}
1125 	/*if (comp_chomper[current_level])*/ {
1126 		load_opt_sounds(46, 47); // something chopped, chomper
1127 	}
1128 	/*if (comp_spike[current_level])*/ {
1129 		load_opt_sounds(48, 49); // something spiked, spikes
1130 	}
1131 }
1132 
1133 // seg000:0E6C
load_level()1134 void __pascal far load_level() {
1135 	dat_type* dathandle;
1136 	dathandle = open_dat("LEVELS.DAT", 0);
1137 	load_from_opendats_to_area(current_level + 2000, &level, sizeof(level), "bin");
1138 	close_dat(dathandle);
1139 
1140 	alter_mods_allrm();
1141 	reset_level_unused_fields(true); // added
1142 }
1143 
reset_level_unused_fields(bool loading_clean_level)1144 void reset_level_unused_fields(bool loading_clean_level) {
1145 	// Entirely unused fields in the level format: reset to zero for now
1146 	// They can be repurposed to add new stuff to the level format in the future
1147 	memset(level.roomxs, 0, sizeof(level.roomxs));
1148 	memset(level.roomys, 0, sizeof(level.roomys));
1149 	memset(level.fill_1, 0, sizeof(level.fill_1));
1150 	memset(level.fill_2, 0, sizeof(level.fill_2));
1151 	memset(level.fill_3, 0, sizeof(level.fill_3));
1152 
1153 	// level.used_rooms is 25 on some levels. Limit it to the actual number of rooms.
1154 	if (level.used_rooms > 24) level.used_rooms = 24;
1155 
1156 	// For these fields, only use the bits that are actually used, and set the rest to zero.
1157 	// Good for repurposing the unused bits in the future.
1158 	int i;
1159 	for (i = 0; i < level.used_rooms; ++i) {
1160 		//level.guards_dir[i]   &= 0x01; // 1 bit in use
1161 		level.guards_skill[i] &= 0x0F; // 4 bits in use
1162 	}
1163 
1164 	// In savestates, additional information may be stored (e.g. remembered guard hp) - should not reset this then!
1165 	if (loading_clean_level) {
1166 		for (i = 0; i < level.used_rooms; ++i) {
1167 			level.guards_color[i] &= 0x0F; // 4 bits in use (other 4 bits repurposed as remembered guard hp)
1168 		}
1169 	}
1170 
1171 }
1172 
1173 // seg000:0EA8
1174 // returns 1 if level is restarted, 0 otherwise
play_kid_frame()1175 int __pascal far play_kid_frame() {
1176 	loadkid_and_opp();
1177 	load_fram_det_col();
1178 	check_killed_shadow();
1179 	play_kid();
1180 	if (upside_down && Char.alive >= 0) {
1181 		upside_down = 0;
1182 		need_redraw_because_flipped = 1;
1183 	}
1184 	if (is_restart_level) {
1185 		return 1;
1186 	}
1187 	if (Char.room != 0) {
1188 		play_seq();
1189 		fall_accel();
1190 		fall_speed();
1191 		load_frame_to_obj();
1192 		load_fram_det_col();
1193 		set_char_collision();
1194 		bump_into_opponent();
1195 		check_collisions();
1196 		check_bumped();
1197 		check_gate_push();
1198 		check_action();
1199 		check_press();
1200 		check_spike_below();
1201 		if (resurrect_time == 0) {
1202 			check_spiked();
1203 			check_chomped_kid();
1204 		}
1205 		check_knock();
1206 	}
1207 	savekid();
1208 	return 0;
1209 }
1210 
1211 // seg000:0F48
play_guard_frame()1212 void __pascal far play_guard_frame() {
1213 	if (Guard.direction != dir_56_none) {
1214 		loadshad_and_opp();
1215 		load_fram_det_col();
1216 		check_killed_shadow();
1217 		play_guard();
1218 		if (Char.room == drawn_room) {
1219 			play_seq();
1220 			if (Char.x >= 44 && Char.x < 211) {
1221 				fall_accel();
1222 				fall_speed();
1223 				load_frame_to_obj();
1224 				load_fram_det_col();
1225 				set_char_collision();
1226 				check_guard_bumped();
1227 				check_action();
1228 				check_press();
1229 				check_spike_below();
1230 				check_spiked();
1231 				check_chomped_guard();
1232 			}
1233 		}
1234 		saveshad();
1235 	}
1236 }
1237 
1238 // seg000:0FBD
check_the_end()1239 void __pascal far check_the_end() {
1240 	if (next_room != 0 && next_room != drawn_room) {
1241 		drawn_room = next_room;
1242 		load_room_links();
1243 		if (current_level == /*14*/ custom->win_level && drawn_room == /*5*/ custom->win_room) {
1244 #ifdef USE_REPLAY
1245 			if (recording) stop_recording();
1246 			if (replaying) end_replay();
1247 #endif
1248 			// Special event: end of game
1249 			end_sequence();
1250 		}
1251 		different_room = 1;
1252 		loadkid();
1253 		anim_tile_modif();
1254 		start_chompers();
1255 		check_fall_flo();
1256 		check_shadow();
1257 	}
1258 }
1259 
1260 // seg000:1009
check_fall_flo()1261 void __pascal far check_fall_flo() {
1262 	// Special event: falling floors
1263 	if (current_level == /*13*/ custom->loose_tiles_level &&
1264 			(drawn_room == /*23*/ custom->loose_tiles_room_1 || drawn_room == /*16*/ custom->loose_tiles_room_2)
1265 	) {
1266 		get_room_address(curr_room = room_A);
1267 		for (curr_tilepos = /*22*/ custom->loose_tiles_first_tile;
1268 		     curr_tilepos <= /*27*/ custom->loose_tiles_last_tile; ++curr_tilepos) {
1269 			make_loose_fall(-(prandom(0xFF) & 0x0F));
1270 		}
1271 	}
1272 }
1273 
get_joystick_state(int raw_x,int raw_y,int axis_state[2])1274 void get_joystick_state(int raw_x, int raw_y, int axis_state[2]) {
1275 
1276 #define DEGREES_TO_RADIANS (M_PI/180.0)
1277 
1278 	// check if the X/Y position is within the 'dead zone' of the joystick
1279 	int dist_squared = raw_x*raw_x + raw_y*raw_y;
1280 	// FIXED: Left jump (top-left) didn't work on some gamepads.
1281 	// On some gamepads, raw_x = raw_y = -32768 in the top-left corner.
1282 	// In this case, dist_squared is calculated as -32768 * -32768 + -32768 * -32768 = 2147483648.
1283 	// But dist_squared is a 32-bit signed integer, which cannot store that number, so it overflows to -2147483648.
1284 	// Therefore, dist_squared < joystick_threshold*joystick_threshold becomes true, and the game thinks the stick is centered.
1285 	// To fix this, we cast both sides of the comparison to an unsigned 32-bit type.
1286 	if ((dword)dist_squared < (dword)(joystick_threshold*joystick_threshold)) {
1287 		axis_state[0] = 0;
1288 		axis_state[1] = 0;
1289 	} else {
1290 		double angle = atan2(raw_y, raw_x); // angle of the joystick: 0 = right, >0 = downward, <0 = upward
1291 		//printf("Joystick angle is %f degrees\n", angle/DEGREES_TO_RADIANS);
1292 
1293 		if (fabs(angle) < (60*DEGREES_TO_RADIANS)) // 120 degree range facing right
1294 			axis_state[0] = 1;
1295 
1296 		else if (fabs(angle) > (120*DEGREES_TO_RADIANS)) // 120 degree range facing left
1297 			axis_state[0] = -1;
1298 
1299 		else {
1300 			// joystick is neutral horizontally, so the control should be released
1301 			// however: prevent stop running if the Kid was already running / trying to do a running-jump
1302 			// (this tweak makes it a bit easier to do (multiple) running jumps)
1303 			if (!(angle < 0 /*facing upward*/ && Kid.action == actions_1_run_jump)) {
1304 				axis_state[0] = 0;
1305 			}
1306 		}
1307 
1308 		if (angle < (-30*DEGREES_TO_RADIANS) && angle > (-150*DEGREES_TO_RADIANS)) // 120 degree range facing up
1309 			axis_state[1] = -1;
1310 
1311 		// down slightly less sensitive than up (prevent annoyance when your thumb slips down a bit by accident)
1312 		// (not sure if this adjustment is really necessary)
1313 		else if (angle > (35*DEGREES_TO_RADIANS) && angle < (145*DEGREES_TO_RADIANS)) // 110 degree range facing down
1314 			axis_state[1] = 1;
1315 
1316 		else {
1317 			// joystick is neutral vertically, so the control should be released
1318 			// however: should prevent unintended standing up when attempting to crouch-hop
1319 			if (!((Kid.frame >= frame_108_fall_land_2 && Kid.frame <= frame_112_stand_up_from_crouch_3)
1320 				  && angle > 0 /*facing downward*/))
1321 			{
1322 				axis_state[1] = 0;
1323 			}
1324 		}
1325 	}
1326 }
1327 
get_joystick_state_hor_only(int raw_x,int axis_state[2])1328 void get_joystick_state_hor_only(int raw_x, int axis_state[2]) {
1329 	if (raw_x > joystick_threshold) {
1330 		axis_state[0] = 1;
1331 	} else if (raw_x < -joystick_threshold) {
1332 		axis_state[0] = -1;
1333 	} else axis_state[0] = 0;
1334 
1335 	// disregard all vertical input from the joystick controls (only use Y and A buttons or D-pad for up/down)
1336 	axis_state[1] = 0;
1337 }
1338 
1339 // seg000:1051
read_joyst_control()1340 void __pascal far read_joyst_control() {
1341 
1342 	if (joystick_only_horizontal) {
1343 		get_joystick_state_hor_only(joy_axis[SDL_CONTROLLER_AXIS_LEFTX], joy_left_stick_states);
1344 		get_joystick_state_hor_only(joy_axis[SDL_CONTROLLER_AXIS_RIGHTX], joy_right_stick_states);
1345 	} else {
1346 		get_joystick_state(joy_axis[SDL_CONTROLLER_AXIS_LEFTX], joy_axis[SDL_CONTROLLER_AXIS_LEFTY], joy_left_stick_states);
1347 		get_joystick_state(joy_axis[SDL_CONTROLLER_AXIS_RIGHTX], joy_axis[SDL_CONTROLLER_AXIS_RIGHTY], joy_right_stick_states);
1348 	}
1349 
1350 	if (joy_left_stick_states[0] == -1 || joy_right_stick_states[0] == -1 || joy_hat_states[0] == -1)
1351 		control_x = -1;
1352 
1353 	if (joy_left_stick_states[0] == 1 || joy_right_stick_states[0] == 1 || joy_hat_states[0] == 1)
1354 		control_x = 1;
1355 
1356 	if (joy_left_stick_states[1] == -1 || joy_right_stick_states[1] == -1 || joy_hat_states[1] == -1 || joy_AY_buttons_state == -1)
1357 		control_y = -1;
1358 
1359 	if (joy_left_stick_states[1] == 1 || joy_right_stick_states[1] == 1 || joy_hat_states[1] == 1 || joy_AY_buttons_state == 1)
1360 		control_y = 1;
1361 
1362 	if (joy_X_button_state == 1 ||
1363 			joy_axis[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > 8000 ||
1364 			joy_axis[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > 8000)
1365 	{
1366 		control_shift = -1;
1367 	}
1368 
1369 }
1370 
1371 // seg000:10EA
draw_kid_hp(short curr_hp,short max_hp)1372 void __pascal far draw_kid_hp(short curr_hp,short max_hp) {
1373 	short drawn_hp_index;
1374 	for (drawn_hp_index = curr_hp; drawn_hp_index < max_hp; ++drawn_hp_index) {
1375 		// empty HP
1376 		method_6_blit_img_to_scr(get_image(id_chtab_2_kid, 217), drawn_hp_index * 7, 194, blitters_0_no_transp);
1377 	}
1378 	for (drawn_hp_index = 0; drawn_hp_index < curr_hp; ++drawn_hp_index) {
1379 		// full HP
1380 		method_6_blit_img_to_scr(get_image(id_chtab_2_kid, 216), drawn_hp_index * 7, 194, blitters_0_no_transp);
1381 	}
1382 }
1383 
1384 // seg000:1159
draw_guard_hp(short curr_hp,short max_hp)1385 void __pascal far draw_guard_hp(short curr_hp,short max_hp) {
1386 	short drawn_hp_index;
1387 	short guard_charid;
1388 	if (chtab_addrs[id_chtab_5_guard] == NULL) return;
1389 	guard_charid = Guard.charid;
1390 	if (guard_charid != charid_4_skeleton &&
1391 		guard_charid != charid_24_mouse &&
1392 		// shadow has HP only on level 12
1393 		(guard_charid != charid_1_shadow || current_level == 12)
1394 	) {
1395 		for (drawn_hp_index = curr_hp; drawn_hp_index < max_hp; ++drawn_hp_index) {
1396 			method_6_blit_img_to_scr(chtab_addrs[id_chtab_5_guard]->images[0], 314 - drawn_hp_index * 7, 194, blitters_9_black);
1397 		}
1398 		for (drawn_hp_index = 0; drawn_hp_index < curr_hp; ++drawn_hp_index) {
1399 			method_6_blit_img_to_scr(chtab_addrs[id_chtab_5_guard]->images[0], 314 - drawn_hp_index * 7, 194, blitters_0_no_transp);
1400 		}
1401 	}
1402 }
1403 
1404 // seg000:11EC
add_life()1405 void __pascal far add_life() {
1406 	short hpmax = hitp_max;
1407 	++hpmax;
1408 	// CusPop: set maximum number of hitpoints (max_hitp_allowed, default = 10)
1409 //	if (hpmax > 10) hpmax = 10; // original
1410 	if (hpmax > custom->max_hitp_allowed) hpmax = custom->max_hitp_allowed;
1411 	hitp_max = hpmax;
1412 	set_health_life();
1413 }
1414 
1415 // seg000:1200
set_health_life()1416 void __pascal far set_health_life() {
1417 	hitp_delta = hitp_max - hitp_curr;
1418 }
1419 
1420 // seg000:120B
draw_hp()1421 void __pascal far draw_hp() {
1422 	if (hitp_delta) {
1423 		draw_kid_hp(hitp_curr, hitp_max);
1424 	}
1425 	if (hitp_curr == 1 && current_level != 15) {
1426 		// blinking hitpoint
1427 		if (rem_tick & 1) {
1428 			draw_kid_hp(1, 0);
1429 		} else {
1430 			draw_kid_hp(0, 1);
1431 		}
1432 	}
1433 	if (guardhp_delta) {
1434 		draw_guard_hp(guardhp_curr, guardhp_max);
1435 	}
1436 	if (guardhp_curr == 1) {
1437 		if (rem_tick & 1) {
1438 			draw_guard_hp(1, 0);
1439 		} else {
1440 			draw_guard_hp(0, 1);
1441 		}
1442 	}
1443 }
1444 
1445 // seg000:127B
do_delta_hp()1446 void __pascal far do_delta_hp() {
1447 	// level 12: if the shadow is hurt, Kid is also hurt
1448 	if (Opp.charid == charid_1_shadow &&
1449 		current_level == 12 &&
1450 		guardhp_delta != 0
1451 	) {
1452 		hitp_delta = guardhp_delta;
1453 	}
1454 	hitp_curr = MIN(MAX(hitp_curr + hitp_delta, 0), hitp_max);
1455 	guardhp_curr = MIN(MAX(guardhp_curr + guardhp_delta, 0), guardhp_max);
1456 }
1457 
1458 byte sound_prio_table[] = {
1459 	0x14, // sound_0_fell_to_death
1460 	0x1E, // sound_1_falling
1461 	0x23, // sound_2_tile_crashing
1462 	0x66, // sound_3_button_pressed
1463 	0x32, // sound_4_gate_closing
1464 	0x37, // sound_5_gate_opening
1465 	0x30, // sound_6_gate_closing_fast
1466 	0x30, // sound_7_gate_stop
1467 	0x4B, // sound_8_bumped
1468 	0x50, // sound_9_grab
1469 	0x0A, // sound_10_sword_vs_sword
1470 	0x12, // sound_11_sword_moving
1471 	0x0C, // sound_12_guard_hurt
1472 	0x0B, // sound_13_kid_hurt
1473 	0x69, // sound_14_leveldoor_closing
1474 	0x6E, // sound_15_leveldoor_sliding
1475 	0x73, // sound_16_medium_land
1476 	0x78, // sound_17_soft_land
1477 	0x7D, // sound_18_drink
1478 	0x82, // sound_19_draw_sword
1479 	0x91, // sound_20_loose_shake_1
1480 	0x96, // sound_21_loose_shake_2
1481 	0x9B, // sound_22_loose_shake_3
1482 	0xA0, // sound_23_footstep
1483 	0x01, // sound_24_death_regular
1484 	0x01, // sound_25_presentation
1485 	0x01, // sound_26_embrace
1486 	0x01, // sound_27_cutscene_2_4_6_12
1487 	0x01, // sound_28_death_in_fight
1488 	0x13, // sound_29_meet_Jaffar
1489 	0x01, // sound_30_big_potion
1490 	0x01, // sound_31
1491 	0x01, // sound_32_shadow_music
1492 	0x01, // sound_33_small_potion
1493 	0x01, // sound_34
1494 	0x01, // sound_35_cutscene_8_9
1495 	0x01, // sound_36_out_of_time
1496 	0x01, // sound_37_victory
1497 	0x01, // sound_38_blink
1498 	0x00, // sound_39_low_weight
1499 	0x01, // sound_40_cutscene_12_short_time
1500 	0x01, // sound_41_end_level_music
1501 	0x01, // sound_42
1502 	0x01, // sound_43_victory_Jaffar
1503 	0x87, // sound_44_skel_alive
1504 	0x8C, // sound_45_jump_through_mirror
1505 	0x0F, // sound_46_chomped
1506 	0x10, // sound_47_chomper
1507 	0x19, // sound_48_spiked
1508 	0x16, // sound_49_spikes
1509 	0x01, // sound_50_story_2_princess
1510 	0x00, // sound_51_princess_door_opening
1511 	0x01, // sound_52_story_4_Jaffar_leaves
1512 	0x01, // sound_53_story_3_Jaffar_comes
1513 	0x01, // sound_54_intro_music
1514 	0x01, // sound_55_story_1_absence
1515 	0x01, // sound_56_ending_music
1516 	0x00
1517 };
1518 byte sound_pcspeaker_exists[] = {
1519 	1, // sound_0_fell_to_death
1520 	0, // sound_1_falling
1521 	1, // sound_2_tile_crashing
1522 	1, // sound_3_button_pressed
1523 	1, // sound_4_gate_closing
1524 	1, // sound_5_gate_opening
1525 	1, // sound_6_gate_closing_fast
1526 	1, // sound_7_gate_stop
1527 	1, // sound_8_bumped
1528 	1, // sound_9_grab
1529 	1, // sound_10_sword_vs_sword
1530 	0, // sound_11_sword_moving
1531 	1, // sound_12_guard_hurt
1532 	1, // sound_13_kid_hurt
1533 	1, // sound_14_leveldoor_closing
1534 	1, // sound_15_leveldoor_sliding
1535 	1, // sound_16_medium_land
1536 	1, // sound_17_soft_land
1537 	1, // sound_18_drink
1538 	0, // sound_19_draw_sword
1539 	0, // sound_20_loose_shake_1
1540 	0, // sound_21_loose_shake_2
1541 	0, // sound_22_loose_shake_3
1542 	1, // sound_23_footstep
1543 	1, // sound_24_death_regular
1544 	1, // sound_25_presentation
1545 	1, // sound_26_embrace
1546 	1, // sound_27_cutscene_2_4_6_12
1547 	1, // sound_28_death_in_fight
1548 	1, // sound_29_meet_Jaffar
1549 	1, // sound_30_big_potion
1550 	1, // sound_31
1551 	1, // sound_32_shadow_music
1552 	1, // sound_33_small_potion
1553 	1, // sound_34
1554 	1, // sound_35_cutscene_8_9
1555 	1, // sound_36_out_of_time
1556 	1, // sound_37_victory
1557 	1, // sound_38_blink
1558 	1, // sound_39_low_weight
1559 	1, // sound_40_cutscene_12_short_time
1560 	1, // sound_41_end_level_music
1561 	1, // sound_42
1562 	1, // sound_43_victory_Jaffar
1563 	1, // sound_44_skel_alive
1564 	1, // sound_45_jump_through_mirror
1565 	1, // sound_46_chomped
1566 	1, // sound_47_chomper
1567 	1, // sound_48_spiked
1568 	1, // sound_49_spikes
1569 	1, // sound_50_story_2_princess
1570 	1, // sound_51_princess_door_opening
1571 	1, // sound_52_story_4_Jaffar_leaves
1572 	1, // sound_53_story_3_Jaffar_comes
1573 	1, // sound_54_intro_music
1574 	1, // sound_55_story_1_absence
1575 	1, // sound_56_ending_music
1576 	0
1577 };
1578 
fix_sound_priorities()1579 void fix_sound_priorities() {
1580 	// Change values to match those in PoP 1.3.
1581 
1582 	// The "spiked" sound didn't interrupt the normal spikes sound when the prince ran into spikes.
1583 	sound_interruptible[sound_49_spikes] = 1;
1584 	sound_prio_table[sound_48_spiked] = 0x15; // moved above spikes
1585 
1586 	// With PoP 1.3 sounds, the "guard hurt" sound didn't play when you hit a guard directly after parrying.
1587 	sound_prio_table[sound_10_sword_vs_sword] = 0x0D; // moved below hit_user/hit_guard
1588 }
1589 
1590 // seg000:12C5
play_sound(int sound_id)1591 void __pascal far play_sound(int sound_id) {
1592 	//printf("Would play sound %d\n", sound_id);
1593 	if (next_sound < 0 || sound_prio_table[sound_id] <= sound_prio_table[next_sound]) {
1594 		if (NULL == sound_pointers[sound_id]) return;
1595 		if (sound_pcspeaker_exists[sound_id] != 0 || sound_pointers[sound_id]->type != sound_speaker) {
1596 			next_sound = sound_id;
1597 		}
1598 	}
1599 }
1600 
1601 // seg000:1304
play_next_sound()1602 void __pascal far play_next_sound() {
1603 	if (next_sound >= 0) {
1604 		if (!check_sound_playing() ||
1605 			(sound_interruptible[current_sound] != 0 && sound_prio_table[next_sound] <= sound_prio_table[current_sound])
1606 		) {
1607 			current_sound = next_sound;
1608 			play_sound_from_buffer(sound_pointers[current_sound]);
1609 		}
1610 	}
1611 	next_sound = -1;
1612 }
1613 
1614 // seg000:1353
check_sword_vs_sword()1615 void __pascal far check_sword_vs_sword() {
1616 	if (Kid.frame == 167 || Guard.frame == 167) {
1617 		play_sound(sound_10_sword_vs_sword); // sword vs. sword
1618 	}
1619 }
1620 
1621 // seg000:136A
load_chtab_from_file(int chtab_id,int resource,const char near * filename,int palette_bits)1622 void __pascal far load_chtab_from_file(int chtab_id,int resource,const char near *filename,int palette_bits) {
1623 	//printf("Loading chtab %d, id %d from %s\n",chtab_id,resource,filename);
1624 	dat_type* dathandle;
1625 	if (chtab_addrs[chtab_id] != NULL) return;
1626 	dathandle = open_dat(filename, 0);
1627 	chtab_addrs[chtab_id] = load_sprites_from_file(resource, palette_bits, 1);
1628 	close_dat(dathandle);
1629 }
1630 
1631 // seg000:13BA
free_all_chtabs_from(int first)1632 void __pascal far free_all_chtabs_from(int first) {
1633 	word chtab_id;
1634 	free_peels();
1635 	for (chtab_id = first; chtab_id < 10; ++chtab_id) {
1636 		if (chtab_addrs[chtab_id]) {
1637 			free_chtab(chtab_addrs[chtab_id]);
1638 			chtab_addrs[chtab_id] = NULL;
1639 		}
1640 	}
1641 }
1642 
1643 // seg009:12EF
load_one_optgraf(chtab_type * chtab_ptr,dat_pal_type far * pal_ptr,int base_id,int min_index,int max_index)1644 void __pascal far load_one_optgraf(chtab_type* chtab_ptr,dat_pal_type far *pal_ptr,int base_id,int min_index,int max_index) {
1645 	short index;
1646 	for (index = min_index; index <= max_index; ++index) {
1647 		image_type* image = load_image(base_id + index + 1, pal_ptr);
1648 		if (image != NULL) chtab_ptr->images[index] = image;
1649 	}
1650 }
1651 
1652 byte optgraf_min[] = {0x01, 0x1E, 0x4B, 0x4E, 0x56, 0x65, 0x7F, 0x0A};
1653 byte optgraf_max[] = {0x09, 0x1F, 0x4D, 0x53, 0x5B, 0x7B, 0x8F, 0x0D};
1654 // seg000:13FC
load_more_opt_graf(const char * filename)1655 void __pascal far load_more_opt_graf(const char *filename) {
1656 	// stub
1657 	dat_type* dathandle;
1658 	dat_shpl_type area;
1659 	short graf_index;
1660 	dathandle = NULL;
1661 	for (graf_index = 0; graf_index < 8; ++graf_index) {
1662 		/*if (...) */ {
1663 			if (dathandle == NULL) {
1664 				dathandle = open_dat(filename, 0);
1665 				load_from_opendats_to_area(200, &area, sizeof(area), "pal");
1666 				area.palette.row_bits = 0x20;
1667 			}
1668 			load_one_optgraf(chtab_addrs[id_chtab_6_environment], &area.palette, 1200, optgraf_min[graf_index] - 1, optgraf_max[graf_index] - 1);
1669 		}
1670 	}
1671 	if (dathandle != NULL) {
1672 		close_dat(dathandle);
1673 	}
1674 }
1675 
1676 // seg000:148D
do_paused()1677 int __pascal far do_paused() {
1678 #ifdef USE_REPLAY
1679 	if (replaying && skipping_replay) return 0;
1680 #endif
1681 
1682 	word key;
1683 	key = 0;
1684 	next_room = 0;
1685 	control_shift = 0;
1686 	control_y = 0;
1687 	control_x = 0;
1688 	if (is_joyst_mode) {
1689 		read_joyst_control();
1690 	} else {
1691 		read_keyb_control();
1692 	}
1693 	key = process_key();
1694 	if (is_ending_sequence && is_paused) {
1695 		is_paused = 0; // fix being able to pause the game during the ending sequence
1696 	}
1697 	if (is_paused) {
1698 		// feather fall gets interrupted by pause
1699 		if (fixes->fix_quicksave_during_feather &&
1700 				is_feather_fall > 0 &&
1701 				check_sound_playing()) {
1702 			stop_sounds();
1703 		}
1704 		display_text_bottom("GAME PAUSED");
1705 #ifdef USE_MENU
1706 		if (enable_pause_menu || is_menu_shown) {
1707 			draw_menu();
1708 			menu_was_closed();
1709 		} else
1710 #endif
1711 		{
1712 			is_paused = 0;
1713 			// busy waiting?
1714 			do {
1715 				idle();
1716 				delay_ticks(1);
1717 			} while (! process_key());
1718 		}
1719 		erase_bottom_text(1);
1720 	}
1721 	return key || control_shift;
1722 }
1723 
1724 // seg000:1500
read_keyb_control()1725 void __pascal far read_keyb_control() {
1726 
1727 	if (key_states[SDL_SCANCODE_UP] || key_states[SDL_SCANCODE_HOME] || key_states[SDL_SCANCODE_PAGEUP]
1728 	    || key_states[SDL_SCANCODE_KP_8] || key_states[SDL_SCANCODE_KP_7] || key_states[SDL_SCANCODE_KP_9]
1729 	) {
1730 		control_y = -1;
1731 	} else if (key_states[SDL_SCANCODE_CLEAR] || key_states[SDL_SCANCODE_DOWN]
1732 	           || key_states[SDL_SCANCODE_KP_5] || key_states[SDL_SCANCODE_KP_2]
1733 	) {
1734 		control_y = 1;
1735 	}
1736 	if (key_states[SDL_SCANCODE_LEFT] || key_states[SDL_SCANCODE_HOME]
1737 	    || key_states[SDL_SCANCODE_KP_4] || key_states[SDL_SCANCODE_KP_7]
1738 	) {
1739 		control_x = -1;
1740 	} else if (key_states[SDL_SCANCODE_RIGHT] || key_states[SDL_SCANCODE_PAGEUP]
1741 	           || key_states[SDL_SCANCODE_KP_6] || key_states[SDL_SCANCODE_KP_9]
1742 	) {
1743 		control_x = 1;
1744 	}
1745 	control_shift = -(key_states[SDL_SCANCODE_LSHIFT] || key_states[SDL_SCANCODE_RSHIFT]);
1746 
1747 	#ifdef USE_DEBUG_CHEATS
1748 	if (cheats_enabled && debug_cheats_enabled) {
1749 		if (key_states[SDL_SCANCODE_RIGHTBRACKET]) ++Char.x;
1750 		else if (key_states[SDL_SCANCODE_LEFTBRACKET]) --Char.x;
1751 	}
1752 	#endif
1753 }
1754 
1755 // seg000:156D
copy_screen_rect(const rect_type far * source_rect_ptr)1756 void __pascal far copy_screen_rect(const rect_type far *source_rect_ptr) {
1757 	const rect_type* far target_rect_ptr;
1758 	rect_type target_rect;
1759 	if (upside_down) {
1760 		target_rect_ptr = &target_rect;
1761 		/**target_rect_ptr*/target_rect = *source_rect_ptr;
1762 		/*target_rect_ptr->*/target_rect.top = 192 - source_rect_ptr->bottom;
1763 		/*target_rect_ptr->*/target_rect.bottom = 192 - source_rect_ptr->top;
1764 	} else {
1765 		target_rect_ptr = source_rect_ptr;
1766 	}
1767 	method_1_blit_rect(onscreen_surface_, offscreen_surface, target_rect_ptr, target_rect_ptr, 0);
1768 #ifdef USE_LIGHTING
1769 	update_lighting(target_rect_ptr);
1770 #endif
1771 }
1772 
1773 // seg000:15E9
toggle_upside()1774 void __pascal far toggle_upside() {
1775 	upside_down = ~ upside_down;
1776 	need_redraw_because_flipped = 1;
1777 }
1778 
1779 // seg000:15F8
feather_fall()1780 void __pascal far feather_fall() {
1781 	//printf("slow fall started at: rem_min = %d, rem_tick = %d\n", rem_min, rem_tick);
1782 	if (fixes->fix_quicksave_during_feather) {
1783 		// feather fall is treated as a timer
1784 		is_feather_fall = FEATHER_FALL_LENGTH * get_ticks_per_sec(timer_1);
1785 	} else {
1786 		is_feather_fall = 1;
1787 	}
1788 	flash_color = 2; // green
1789 	flash_time = 3;
1790 	stop_sounds();
1791 	play_sound(sound_39_low_weight); // low weight
1792 }
1793 
1794 // seg000:1618
parse_grmode()1795 int __pascal far parse_grmode() {
1796 	// stub
1797 	set_gr_mode(gmMcgaVga);
1798 	return gmMcgaVga;
1799 }
1800 
1801 // seg000:172C
gen_palace_wall_colors()1802 void __pascal far gen_palace_wall_colors() {
1803 	dword old_randseed;
1804 	word prev_color;
1805 	short row;
1806 	short subrow;
1807 	word color_base;
1808 	short column;
1809 	word color;
1810 
1811 	old_randseed = random_seed;
1812 	random_seed = drawn_room;
1813 	prandom(1); // discard
1814 	for (row = 0; row < 3; row++) {
1815 		for (subrow = 0; subrow < 4; subrow++) {
1816 			if (subrow % 2) {
1817 				color_base = 0x61; // 0x61..0x64 in subrow 1 and 3
1818 			} else {
1819 				color_base = 0x66; // 0x66..0x69 in subrow 0 and 2
1820 			}
1821 			prev_color = -1;
1822 			for (column = 0; column <= 10; ++column) {
1823 				do {
1824 					color = color_base + prandom(3);
1825 				} while (color == prev_color);
1826 				palace_wall_colors[44 * row + 11 * subrow + column] = color;
1827 				//palace_wall_colors[row][subrow][column] = color;
1828 				prev_color = color;
1829 			}
1830 		}
1831 	}
1832 	random_seed = old_randseed;
1833 }
1834 
1835 // data:042E
1836 const rect_type rect_titles = {106,24,195,296};
1837 
1838 // seg000:17E6
show_title()1839 void __pascal far show_title() {
1840 	load_opt_sounds(sound_50_story_2_princess, sound_55_story_1_absence); // main theme, story, princess door
1841 	dont_reset_time = 0;
1842 	if(offscreen_surface) free_surface(offscreen_surface); // missing in original
1843 	offscreen_surface = make_offscreen_buffer(&screen_rect);
1844 	load_title_images(1);
1845 	current_target_surface = offscreen_surface;
1846 	idle(); // modified
1847 	do_paused();
1848 
1849 	draw_full_image(TITLE_MAIN);
1850 	fade_in_2(offscreen_surface, 0x1000); //STUB
1851 	method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, blitters_0_no_transp);
1852 	current_sound = sound_54_intro_music; // added
1853 	play_sound_from_buffer(sound_pointers[sound_54_intro_music]); // main theme
1854 	start_timer(timer_0, 0x82);
1855 	draw_full_image(TITLE_PRESENTS);
1856 	do_wait(timer_0);
1857 
1858 	start_timer(timer_0, 0xCD);
1859 	method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1860 	draw_full_image(TITLE_MAIN);
1861 	do_wait(timer_0);
1862 
1863 	start_timer(timer_0, 0x41);
1864 	method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1865 	draw_full_image(TITLE_MAIN);
1866 	draw_full_image(TITLE_GAME);
1867 	do_wait(timer_0);
1868 
1869 	start_timer(timer_0, 0x10E);
1870 	method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1871 	draw_full_image(TITLE_MAIN);
1872 	do_wait(timer_0);
1873 
1874 	start_timer(timer_0, 0xEB);
1875 	method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1876 	draw_full_image(TITLE_MAIN);
1877 	draw_full_image(TITLE_POP);
1878 	draw_full_image(TITLE_MECHNER);
1879 	do_wait(timer_0);
1880 
1881 	method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect_titles, &rect_titles, blitters_0_no_transp);
1882 	draw_full_image(STORY_FRAME);
1883 	draw_full_image(STORY_ABSENCE);
1884 	current_target_surface = onscreen_surface_;
1885 	while (check_sound_playing()) {
1886 		idle();
1887 		do_paused();
1888 		delay_ticks(1);
1889 	}
1890 //	method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, blitters_0_no_transp);
1891 	play_sound_from_buffer(sound_pointers[sound_55_story_1_absence]); // story 1: In the absence
1892 	transition_ltr();
1893 	pop_wait(timer_0, 0x258);
1894 	fade_out_2(0x800);
1895 	release_title_images();
1896 
1897 	load_intro(0, &pv_scene, 0);
1898 
1899 	load_title_images(1);
1900 	current_target_surface = offscreen_surface;
1901 	draw_full_image(STORY_FRAME);
1902 	draw_full_image(STORY_MARRY);
1903 	fade_in_2(offscreen_surface, 0x800);
1904 	draw_full_image(TITLE_MAIN);
1905 	draw_full_image(TITLE_POP);
1906 	draw_full_image(TITLE_MECHNER);
1907 	while (check_sound_playing()) {
1908 		idle();
1909 		do_paused();
1910 		delay_ticks(1);
1911 	}
1912 	transition_ltr();
1913 	pop_wait(timer_0, 0x78);
1914 	draw_full_image(STORY_FRAME);
1915 	draw_full_image(STORY_CREDITS);
1916 	transition_ltr();
1917 	pop_wait(timer_0, 0x168);
1918 	if (hof_count) {
1919 		draw_full_image(STORY_FRAME);
1920 		draw_full_image(HOF_POP);
1921 		show_hof();
1922 		transition_ltr();
1923 		pop_wait(timer_0, 0xF0);
1924 	}
1925 	current_target_surface = onscreen_surface_;
1926 	while (check_sound_playing()) {
1927 		idle();
1928 		do_paused();
1929 		delay_ticks(1);
1930 	}
1931 	fade_out_2(0x1800);
1932 	free_surface(offscreen_surface);
1933 	offscreen_surface = NULL; // added
1934 	release_title_images();
1935 	init_game(0);
1936 }
1937 
1938 Uint64 last_transition_counter;
1939 
1940 // seg000:1BB3
transition_ltr()1941 void __pascal far transition_ltr() {
1942 	short position;
1943 	rect_type rect;
1944 	rect.top = 0;
1945 	rect.bottom = 200;
1946 	rect.left = 0;
1947 	rect.right = 2;
1948 	// Estimated transition fps based on the speed of the transition on an Apple IIe.
1949 	// See: https://www.youtube.com/watch?v=7m7j2VuWhQ0
1950 	int transition_fps = 120;
1951 #ifdef USE_FAST_FORWARD
1952 	extern int audio_speed;
1953 	transition_fps *= audio_speed;
1954 #endif
1955 	Uint64 counters_per_frame = perf_frequency / transition_fps;
1956 	last_transition_counter = SDL_GetPerformanceCounter();
1957 	int overshoot = 0;
1958 	for (position = 0; position < 320; position += 2) {
1959 		method_1_blit_rect(onscreen_surface_, offscreen_surface, &rect, &rect, 0);
1960 		rect.left += 2;
1961 		rect.right += 2;
1962 		if (overshoot > 0 && overshoot < 10) {
1963 			--overshoot;
1964 			continue; // On slow systems (e.g. Raspberry Pi), allow the animation to catch up, before refreshing the screen.
1965 		}
1966 		idle(); // modified
1967 		do_paused();
1968 		// Add an appropriate delay until the next frame, so that the animation isn't instantaneous on fast CPUs.
1969 		for (;;) {
1970 			Uint64 current_counter = SDL_GetPerformanceCounter();
1971 			int frametimes_elapsed = (int)((current_counter / counters_per_frame) - (last_transition_counter / counters_per_frame));
1972 			if (frametimes_elapsed > 0) {
1973 				overshoot = frametimes_elapsed - 1;
1974 				last_transition_counter = current_counter;
1975 				break; // Proceed to the next frame.
1976 			} else {
1977 				SDL_Delay(1);
1978 			}
1979 		}
1980 
1981 	}
1982 }
1983 
1984 // seg000:1C0F
release_title_images()1985 void __pascal far release_title_images() {
1986 	if (chtab_title50) {
1987 		free_chtab(chtab_title50);
1988 		chtab_title50 = NULL;
1989 	}
1990 	if (chtab_title40) {
1991 		free_chtab(chtab_title40);
1992 		chtab_title40 = NULL;
1993 	}
1994 }
1995 
1996 // seg000:1C3A
draw_full_image(enum full_image_id id)1997 void __pascal far draw_full_image(enum full_image_id id) {
1998 	image_type* decoded_image;
1999 	image_type* mask = NULL;
2000 	int xpos, ypos, blit;
2001 
2002 	if (id >= MAX_FULL_IMAGES) return;
2003 	if (NULL == *full_image[id].chtab) return;
2004 	decoded_image = (*full_image[id].chtab)->images[full_image[id].id];
2005 	blit = full_image[id].blitter;
2006 	xpos = full_image[id].xpos;
2007 	ypos = full_image[id].ypos;
2008 
2009 	switch (blit) {
2010 	case blitters_white:
2011 		blit = get_text_color(15, color_15_brightwhite, 0x800);
2012 		/* fall through */
2013 	default:
2014 		method_3_blit_mono(decoded_image, xpos, ypos, blitters_0_no_transp, blit);
2015 		break;
2016 	case blitters_10h_transp:
2017 		if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
2018 			//...
2019 		} else {
2020 			mask = decoded_image;
2021 		}
2022 		draw_image_transp(decoded_image, mask, xpos, ypos);
2023 		if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
2024 			free_far(mask);
2025 		}
2026 		break;
2027 	case blitters_0_no_transp:
2028 		method_6_blit_img_to_scr(decoded_image, xpos, ypos, blit);
2029 		break;
2030 	}
2031 }
2032 
2033 // seg000:1D2C
load_kid_sprite()2034 void __pascal far load_kid_sprite() {
2035 	load_chtab_from_file(id_chtab_2_kid, 400, "KID.DAT", 1<<7);
2036 }
2037 
2038 const char* save_file = "PRINCE.SAV";
2039 
get_save_path(char * custom_path_buffer,size_t max_len)2040 const char* get_save_path(char* custom_path_buffer, size_t max_len) {
2041 	if (!use_custom_levelset) {
2042 		return save_file;
2043 	}
2044 	// if playing a custom levelset, try to use the mod folder
2045 	snprintf_check(custom_path_buffer, max_len, "%s/%s", mod_data_path, save_file /*PRINCE.SAV*/ );
2046 	return custom_path_buffer;
2047 }
2048 
2049 // seg000:1D45
save_game()2050 void __pascal far save_game() {
2051 	word success;
2052 	FILE* handle;
2053 	success = 0;
2054 	char custom_save_path[POP_MAX_PATH];
2055 	const char* save_path = get_save_path(custom_save_path, sizeof(custom_save_path));
2056 	handle = fopen(save_path, "wb");
2057 	if (handle == NULL) goto loc_1DB8;
2058 	if (fwrite(&rem_min, 1, 2, handle) == 2) goto loc_1DC9;
2059 	loc_1D9B:
2060 	fclose(handle);
2061 	if (!success) {
2062 		remove(save_path);
2063 	}
2064 	loc_1DB8:
2065 	if (!success) goto loc_1E18;
2066 	display_text_bottom("GAME SAVED");
2067 	goto loc_1E2E;
2068 	loc_1DC9:
2069 	if (fwrite(&rem_tick, 1, 2, handle) != 2) goto loc_1D9B;
2070 	if (fwrite(&current_level, 1, 2, handle) != 2) goto loc_1D9B;
2071 	if (fwrite(&hitp_beg_lev, 1, 2, handle) != 2) goto loc_1D9B;
2072 	success = 1;
2073 	goto loc_1D9B;
2074 	loc_1E18:
2075 	display_text_bottom("UNABLE TO SAVE GAME");
2076 	//play_sound_from_buffer(&sound_cant_save);
2077 	loc_1E2E:
2078 	text_time_remaining = 24;
2079 }
2080 
2081 // seg000:1E38
load_game()2082 short __pascal far load_game() {
2083 	word success;
2084 	FILE* handle;
2085 	success = 0;
2086 	char custom_save_path[POP_MAX_PATH];
2087 	const char* save_path = get_save_path(custom_save_path, sizeof(custom_save_path));
2088 	handle = fopen(save_path, "rb");
2089 	if (handle == NULL) goto loc_1E99;
2090 	if (fread(&rem_min, 1, 2, handle) == 2) goto loc_1E9E;
2091 	loc_1E8E:
2092 	fclose(handle);
2093 	loc_1E99:
2094 	return success;
2095 	loc_1E9E:
2096 	if (fread(&rem_tick, 1, 2, handle) != 2) goto loc_1E8E;
2097 	if (fread(&start_level, 1, 2, handle) != 2) goto loc_1E8E;
2098 	if (fread(&hitp_beg_lev, 1, 2, handle) != 2) goto loc_1E8E;
2099 #ifdef USE_COPYPROT
2100 	if (enable_copyprot && custom->copyprot_level > 0) {
2101 		custom->copyprot_level = start_level;
2102 	}
2103 #endif
2104 	success = 1;
2105 	dont_reset_time = 1;
2106 	goto loc_1E8E;
2107 }
2108 
2109 // seg000:1F02
clear_screen_and_sounds()2110 void __pascal far clear_screen_and_sounds() {
2111 	short index;
2112 	stop_sounds();
2113 	current_target_surface = rect_sthg(onscreen_surface_, &screen_rect);
2114 
2115 	is_cutscene = 0;
2116 	is_ending_sequence = false; // added
2117 	peels_count = 0;
2118 	// should these be freed?
2119 	for (index = 2; index < 10; ++index) {
2120 		if (chtab_addrs[index]) {
2121 			// Original code does not free these?
2122 			free_chtab(chtab_addrs[index]);
2123 			chtab_addrs[index] = NULL;
2124 		}
2125 	}
2126 	/* //Don't free sounds.
2127 	for (index = 44; index < 57; ++index) {
2128 		//continue; // don't release sounds? modern machines have enough memory
2129 		free_sound(sound_pointers[index]); // added
2130 		sound_pointers[index] = NULL;
2131 	}
2132 	*/
2133 	current_level = -1;
2134 }
2135 
2136 // seg000:1F7B
parse_cmdline_sound()2137 void __pascal far parse_cmdline_sound() {
2138 	// stub
2139 	if (check_param("stdsnd")) {
2140 		// Use PC Speaker sounds and music.
2141 	} else {
2142 		// Use digi (wave) sounds and MIDI music.
2143 		sound_flags |= sfDigi;
2144 		sound_flags |= sfMidi;
2145 		sound_mode = smSblast;
2146 	}
2147 }
2148 
2149 // seg000:226D
free_optional_sounds()2150 void __pascal far free_optional_sounds() {
2151 	/* //Don't free sounds.
2152 	int sound_id;
2153 	for (sound_id = 44; sound_id < 57; ++sound_id) {
2154 		free_sound(sound_pointers[sound_id]);
2155 		sound_pointers[sound_id] = NULL;
2156 	}
2157 	*/
2158 	// stub
2159 }
2160 
free_all_sounds()2161 void free_all_sounds() {
2162 	for (int i = 0; i < 58; ++i) {
2163 		free_sound(sound_pointers[i]);
2164 		sound_pointers[i] = NULL;
2165 	}
2166 }
2167 
load_all_sounds()2168 void load_all_sounds() {
2169 	if (!use_custom_levelset) {
2170 		load_sounds(0, 43);
2171 		load_opt_sounds(43, 56); //added
2172 	} else {
2173 		// First load any sounds included in the mod folder...
2174 		skip_normal_data_files = true;
2175 		load_sounds(0, 43);
2176 		load_opt_sounds(43, 56);
2177 		skip_normal_data_files = false;
2178 		// ... then load any missing sounds from SDLPoP's own resources.
2179 		skip_mod_data_files = true;
2180 		load_sounds(0, 43);
2181 		load_opt_sounds(43, 56);
2182 		skip_mod_data_files = false;
2183 	}
2184 }
2185 
2186 // seg000:22BB
free_optsnd_chtab()2187 void __pascal far free_optsnd_chtab() {
2188 	free_optional_sounds();
2189 	free_all_chtabs_from(id_chtab_3_princessinstory);
2190 }
2191 
2192 // seg000:22C8
load_title_images(int bgcolor)2193 void __pascal far load_title_images(int bgcolor) {
2194 	dat_type* dathandle;
2195 	dathandle = open_dat("TITLE.DAT", 0);
2196 	chtab_title40 = load_sprites_from_file(40, 1<<11, 1);
2197 	chtab_title50 = load_sprites_from_file(50, 1<<12, 1);
2198 	close_dat(dathandle);
2199 	if (graphics_mode == gmMcgaVga) {
2200 		// background of text frame
2201 		SDL_Color color;
2202 		if (bgcolor) {
2203 			// RGB(4,0,18h) = #100060 = dark blue
2204 			set_pal((find_first_pal_row(1<<11) << 4) + 14, 0x04, 0x00, 0x18, 1);
2205 			color.r = 0x10;
2206 			color.g = 0x00;
2207 			color.b = 0x60;
2208 			color.a = 0xFF;
2209 		} else {
2210 			// RGB(20h,0,0) = #800000 = dark red
2211 			set_pal((find_first_pal_row(1<<11) << 4) + 14, 0x20, 0x00, 0x00, 1);
2212 			color.r = 0x80;
2213 			color.g = 0x00;
2214 			color.b = 0x00;
2215 			color.a = 0xFF;
2216 		}
2217 		if (NULL != chtab_title40) {
2218 			SDL_SetPaletteColors(chtab_title40->images[0]->format->palette, &color, 14, 1);
2219 		}
2220 	} else if (graphics_mode == gmEga || graphics_mode == gmTga) {
2221 		// ...
2222 	}
2223 }
2224 
2225 #ifdef USE_COPYPROT
2226 // data:017A
2227 const word copyprot_word[] = {9, 1, 6, 4, 5, 3, 6, 3, 4, 4, 3, 2,12, 5,13, 1, 9, 2, 2, 4, 9, 4,11, 8, 5, 4, 1, 6, 2, 4, 6, 8, 4, 2, 7,11, 5, 4, 1, 2};
2228 // data:012A
2229 const word copyprot_line[] = {2, 1, 5, 4, 3, 5, 1, 3, 7, 2, 2, 4, 6, 6, 2, 6, 3, 1, 2, 3, 2, 2, 3,10, 5, 6, 5, 6, 3, 5, 7, 2, 2, 4, 5, 7, 2, 6, 5, 5};
2230 // data:00DA
2231 const word copyprot_page[] = {5, 3, 7, 3, 3, 4, 1, 5,12, 5,11,10, 1, 2, 8, 8, 2, 4, 6, 1, 4, 7, 3, 2, 1, 7,10, 1, 4, 3, 4, 1, 4, 1, 8, 1, 1,10, 3, 3};
2232 #endif
2233 
2234 // seg000:23F4
show_copyprot(int where)2235 void __pascal far show_copyprot(int where) {
2236 #ifdef USE_COPYPROT
2237 	char sprintf_temp[140];
2238 	if (current_level != 15) return;
2239 	if (where) {
2240 		if (text_time_remaining || is_cutscene) return;
2241 		text_time_total = 1188;
2242 		text_time_remaining = 1188;
2243 		is_show_time = 0;
2244 		snprintf(sprintf_temp, sizeof(sprintf_temp),
2245 			"WORD %d LINE %d PAGE %d",
2246 			copyprot_word[copyprot_idx], copyprot_line[copyprot_idx], copyprot_page[copyprot_idx]);
2247 		display_text_bottom(sprintf_temp);
2248 	} else {
2249 		snprintf(sprintf_temp, sizeof(sprintf_temp),
2250 			"Drink potion matching the first letter of Word %d on Line %d\n"
2251 			"of Page %d of the manual.",
2252 			copyprot_word[copyprot_idx], copyprot_line[copyprot_idx], copyprot_page[copyprot_idx]);
2253 		show_dialog(sprintf_temp);
2254 	}
2255 #endif
2256 }
2257 
2258 // seg000:2489
show_loading()2259 void __pascal far show_loading() {
2260 	show_text(&screen_rect, 0, 0, "Loading. . . .");
2261 	update_screen();
2262 }
2263 
2264 // data:42C4
2265 word which_quote;
2266 
2267 char const * const tbl_quotes[2] = {
2268 "\"(****/****) Incredibly realistic. . . The "
2269 "adventurer character actually looks human as he "
2270 "runs, jumps, climbs, and hangs from ledges.\"\n"
2271 "\n"
2272 "                                  Computer Entertainer\n"
2273 "\n"
2274 "\n"
2275 "\n"
2276 "\n"
2277 "\"A tremendous achievement. . . Mechner has crafted "
2278 "the smoothest animation ever seen in a game of this "
2279 "type.\n"
2280 "\n"
2281 "\"PRINCE OF PERSIA is the STAR WARS of its field.\"\n"
2282 "\n"
2283 "                                  Computer Gaming World",
2284 "\"An unmitigated delight. . . comes as close to "
2285 "(perfection) as any arcade game has come in a long, "
2286 "long time. . . what makes this game so wonderful (am "
2287 "I gushing?) is that the little onscreen character "
2288 "does not move like a little onscreen character -- he "
2289 "moves like a person.\"\n"
2290 "\n"
2291 "                                      Nibble"
2292 };
2293 
2294 // seg000:249D
show_quotes()2295 void __pascal far show_quotes() {
2296 	//start_timer(timer_0,0);
2297 	//remove_timer(timer_0);
2298 	if (demo_mode && need_quotes) {
2299 		draw_rect(&screen_rect, 0);
2300 		show_text(&screen_rect, -1, 0, tbl_quotes[which_quote]);
2301 		which_quote = !which_quote;
2302 		start_timer(timer_0, 0x384);
2303 	}
2304 	need_quotes = 0;
2305 }
2306 
2307 const rect_type splash_text_1_rect = {0, 0, 50, 320};
2308 const rect_type splash_text_2_rect = {50, 0, 200, 320};
2309 
2310 const char* splash_text_1 = "SDLPoP " SDLPOP_VERSION;
2311 const char* splash_text_2 =
2312 #ifdef USE_QUICKSAVE
2313 		"To quick save/load, press F6/F9 in-game.\n"
2314 		"\n"
2315 #endif
2316 #ifdef USE_REPLAY
2317 		"To record replays, press Ctrl+Tab in-game.\n"
2318 		"To view replays, press Tab on the title screen.\n"
2319 		"\n"
2320 #endif
2321 		"Edit SDLPoP.ini to customize SDLPoP.\n"
2322 		"Mods also work with SDLPoP.\n"
2323 		"\n"
2324 		"For more information, read doc/Readme.txt.\n"
2325 		"Questions? Visit https://forum.princed.org\n"
2326 		"\n"
2327 		"Press any key to continue...";
2328 
show_splash()2329 void show_splash() {
2330 	if (!enable_info_screen || start_level >= 0) return;
2331 	current_target_surface = onscreen_surface_;
2332 	draw_rect(&screen_rect, 0);
2333 	show_text_with_color(&splash_text_1_rect, 0, 0, splash_text_1, color_15_brightwhite);
2334 	show_text_with_color(&splash_text_2_rect, 0, -1, splash_text_2, color_7_lightgray);
2335 
2336 #ifdef USE_TEXT // Don't wait for a keypress if there is no text for the user to read.
2337 	int key = 0;
2338 	do {
2339 		idle();
2340 		key = key_test_quit();
2341 
2342 		if (joy_hat_states[0] != 0 || joy_X_button_state != 0 || joy_AY_buttons_state != 0 || joy_B_button_state != 0) {
2343 			joy_hat_states[0] = 0;
2344 			joy_AY_buttons_state = 0;
2345 			joy_X_button_state = 0;
2346 			joy_B_button_state = 0;
2347 			key_states[SDL_SCANCODE_LSHIFT] = 1; // close the splash screen using the gamepad
2348 		}
2349 		delay_ticks(1);
2350 
2351 	} while(key == 0 && !(key_states[SDL_SCANCODE_LSHIFT] || key_states[SDL_SCANCODE_RSHIFT]));
2352 
2353 	if ((key & WITH_CTRL) || (enable_quicksave && key == SDL_SCANCODE_F9) || (enable_replay && key == SDL_SCANCODE_TAB)) {
2354 		extern int last_key_scancode; // defined in seg009.c
2355 		last_key_scancode = key; // can immediately do Ctrl+L, etc from the splash screen
2356 	}
2357 	key_states[SDL_SCANCODE_LSHIFT] = 0; // don't immediately start the game if Shift was pressed!
2358 	key_states[SDL_SCANCODE_RSHIFT] = 0;
2359 #endif
2360 }
2361 
2362