1 /**************************************************************
2  *         _____    __                       _____            *
3  *        /  _  \  |  |    ____  ___  ___   /  |  |           *
4  *       /  /_\  \ |  |  _/ __ \ \  \/  /  /   |  |_          *
5  *      /    |    \|  |__\  ___/  >    <  /    ^   /          *
6  *      \____|__  /|____/ \___  >/__/\_ \ \____   |           *
7  *              \/            \/       \/      |__|           *
8  *                                                            *
9  **************************************************************
10  *    (c) Free Lunch Design 2003                              *
11  *    Written by Johan Peitz                                  *
12  *    http://www.freelunchdesign.com                          *
13  **************************************************************
14  *    This source code is released under the The GNU          *
15  *    General Public License (GPL). Please refer to the       *
16  *    document license.txt in the source directory or         *
17  *    http://www.gnu.org for license information.             *
18  **************************************************************/
19 
20 
21 
22 #include <allegro.h>
23 #include <aldumb.h>
24 #include <string.h>
25 #include <ctype.h>
26 
27 #include "timer.h"
28 #include "map.h"
29 #include "control.h"
30 #include "actor.h"
31 #include "player.h"
32 #include "particle.h"
33 #include "bullet.h"
34 #include "scroller.h"
35 #include "options.h"
36 #include "hisc.h"
37 #include "script.h"
38 #include "main.h"
39 #include "edit.h"
40 #include "shooter.h"
41 #include "unix.h"
42 
43 #include "../data/data.h"
44 
45 
46 // some game status defines
47 #define GS_OK				1
48 #define GS_GAMEOVER			2
49 #define GS_QUIT_GAME		3
50 #define GS_QUIT_MENU		4
51 #define GS_PLAY				5
52 #define GS_LEVEL_DONE		6
53 #define GS_GAME_COMPLETE	7
54 #define GS_GAME_DIED		8
55 #define GS_SCORES			9
56 #define GS_EDIT			   10
57 
58 int game_status = 0;
59 
60 
61 // globalez
62 DATAFILE *data = NULL;
63 DATAFILE *maps = NULL;
64 DATAFILE *sfx_data = NULL;
65 BITMAP *swap_screen;
66 PALETTE org_pal;
67 Tscroller hscroll;
68 Thisc *hisc_table;
69 Thisc *hisc_table_space;
70 
71 // the map
72 Tmap *map = NULL;
73 
74 // controls
75 Tcontrol ctrl;
76 
77 // actors
78 Tactor actor[MAX_ACTORS];
79 
80 // edit
81 int editing = 0;
82 
83 // music and stuff
84 static AL_DUH_PLAYER *dp = NULL;
85 static DUH_SIGRENDERER *sr = NULL;
86 static DUH *duh = NULL;
87 
88 // mod start patterns
89 #define MOD_INTRO_SONG		 0x00
90 #define MOD_MENU_SONG		 0x08
91 #define MOD_BOSS_SONG		 0x0c
92 #define MOD_OUTRO_SONG		 0x11
93 #define MOD_PLAYER_DIES		 0x1a
94 #define MOD_LEVEL_DONE		 0x1b
95 #define MOD_GAME_OVER		 0x1c
96 #define MOD_LEVEL_SONG		 0x1d
97 
98 
99 // sound fx
100 #define MAX_SOUNDS		32
101 SAMPLE *sfx[MAX_SOUNDS] = { NULL, NULL, NULL, NULL,
102 							NULL, NULL, NULL, NULL,
103 							NULL, NULL, NULL, NULL,
104 							NULL, NULL, NULL, NULL,
105 							NULL, NULL, NULL, NULL,
106 							NULL, NULL, NULL, NULL,
107 							NULL, NULL, NULL, NULL,
108 							NULL, NULL, NULL, NULL };
109 
110 
111 
112 
113 // various
114 char scroller_message[] =
115 	"Free Lunch Design      presents      Alex the Allegator 4      "
116 	"Guide Alex to the exit of each level      Jump or shoot enemies picking up stars and cherries on the way      "
117 	"Use arrows to move Alex, ALT to jump and Left CTRL to shoot, or use a gamepad or joystick      "
118 	"Code and GFX by Johan Peitz      Music and SFX by Anders Svensson       ";
119 char init_string[] = "[YhJPJKUSY`0-'\"7 ";
120 
121 char *level_files[256];
122 int num_levels;
123 int got_sound = 0;
124 Toptions options;
125 int menu_choice = 1;
126 int playing_original_game = 1;
127 int init_ok = 0;
128 
129 static FILE* log_fp = NULL;
130 
131 
132 // // // // // // // // // // // // // // // // // // // // //
133 
134 // returns pointer to the game over bitmap
get_gameover_sign()135 BITMAP *get_gameover_sign() {
136 	return data[GAME_OVER].dat;
137 }
138 
139 // returns pointer to the lets go bitmap
get_letsgo_sign()140 BITMAP *get_letsgo_sign() {
141 	return data[LETSGO].dat;
142 }
143 
144 // returns pointer to the space highscore table
get_space_hisc()145 Thisc *get_space_hisc() {
146 	return hisc_table_space;
147 }
148 
149 // returns the init_string
get_init_string()150 char *get_init_string() {
151 	return init_string;
152 }
153 
154 
155 // loggs the text to the text file
log2file(char * format,...)156 void log2file(char *format, ...) {
157 	va_list ptr; /* get an arg pointer */
158 
159 	if (log_fp) {
160 		/* initialize ptr to point to the first argument after the format string */
161 		va_start(ptr, format);
162 
163 		/* Write to logfile. */
164 		vfprintf(log_fp, format, ptr); // Write passed text.
165 		fprintf(log_fp, "\n"); // New line..
166 
167 		va_end(ptr);
168 
169 		fflush(log_fp);
170 	}
171 
172 }
173 
174 
175 // saves a screenshot
take_screenshot(BITMAP * bmp)176 void take_screenshot(BITMAP *bmp) {
177 	static int number = 0;
178 	PALETTE p;
179 	BITMAP *b;
180 	char buf[256];
181 	int ok = 0;
182 
183 	// check if the file name allready exists
184 	do {
185 		sprintf(buf, "a4_%03d.pcx", number ++);
186 		if (!exists(buf)) ok = 1;
187 		if (number > 999) return;
188 	} while(!ok);
189 
190 	get_palette(p);
191 
192 	b = create_sub_bitmap(bmp, 0, 0, bmp->w, bmp->h);
193 	save_bitmap(buf, b, p);
194 	destroy_bitmap(b);
195 
196 }
197 
198 
199 // garbles a string using a key
garble_string(char * str,int key)200 void garble_string(char *str, int key) {
201 	int i;
202 	int len_i = strlen(str);
203 	for(i = 0; i < len_i; i ++) {
204 		str[i] ^= (key+i);
205 	}
206 }
207 
208 // sets the current map
set_map(Tmap * m)209 void set_map(Tmap *m) {
210 	map = m;
211 }
212 
213 // plays a sample using default settings
play_sound(SAMPLE * s)214 void play_sound(SAMPLE *s) {
215 	if (got_sound && s != NULL) play_sample(s, get_config_int("sound", "sample_volume", 100), 128, 1000, 0);
216 }
217 
218 // plays a sample using default settings
219 // (from an index)
play_sound_id(int id)220 void play_sound_id(int id) {
221 	if (got_sound) play_sound(sfx[id]);
222 }
223 
224 // plays a sample using user settings
play_sound_ex(SAMPLE * s,int vol,int freq,int loop)225 void play_sound_ex(SAMPLE *s, int vol, int freq, int loop) {
226 	if (got_sound && s != NULL) {
227 		int v = (get_config_int("sound", "sample_volume", 100) * (float)(vol))/100.0;
228 		play_sample(s, v, 128, freq, loop);
229 	}
230 }
231 
232 // plays a sample using user settings
233 // (from an index)
play_sound_id_ex(int id,int vol,int freq,int loop)234 void play_sound_id_ex(int id, int vol, int freq, int loop) {
235 	play_sound_ex(sfx[id], vol, freq, loop);
236 }
237 
238 // stops a sample (providing an id)
stop_sound_id(int id)239 void stop_sound_id(int id) {
240 	stop_sample(sfx[id]);
241 }
242 
243 // adjusts a sample (from an index) according to player position
adjust_sound_id_ex(int id,int x)244 void adjust_sound_id_ex(int id, int x) {
245 	if (got_sound) {
246 		int vol = MAX(100 - ABS(player.actor->x - x) / 2, 0);
247 		int v = get_config_int("sound", "sample_volume", 100) * (float)(vol)/100.0;
248 		int pan = MID(0, 128 + x - player.actor->x, 255);
249 		if (sfx[id] != NULL) {
250 			adjust_sample(sfx[id], v, pan, 1000, 1);
251 		}
252 	}
253 }
254 
255 
256 // shows a little message
msg_box(char * str)257 void msg_box(char *str) {
258 	if (got_sound) al_pause_duh(dp);
259 	alert("Alex 4: Message", NULL, str, "OK", NULL, 0, 0);
260 	if (got_sound) al_resume_duh(dp);
261 }
262 
263 // polls the music
poll_music()264 void poll_music() {
265 	if (got_sound && dp != NULL) al_poll_duh(dp);
266 }
267 
268 
269 // waits for user to strike a key, or x seconds
wait_key(int seconds)270 void wait_key(int seconds) {
271 	int t = 0;
272 	int kp = 0;
273 
274 	clear_keybuf();
275 	cycle_count = 0;
276 	while(!kp && t < seconds * 50) {
277 		cycle_count = 0;
278 		poll_control(&ctrl);
279 		t ++;
280 		if (keypressed()) kp = 1;
281 		if (is_fire(&ctrl) || is_jump(&ctrl)) kp = 1;
282 		if (got_sound && dp != NULL) al_poll_duh(dp);
283 		while(!cycle_count);
284 	}
285 }
286 
287 
288 // returns the player actor
get_alex()289 Tactor *get_alex() {
290 	return &actor[0];
291 }
292 
293 // stops any mod playing
stop_music(void)294 static void stop_music(void) {
295 	al_stop_duh(dp);
296 	dp = NULL;
297 }
298 
299 
300 // starts the mod at position x
start_music(int startorder)301 static void start_music(int startorder) {
302 	const int n_channels = 2; /* stereo */
303 
304 	stop_music();
305 
306 	{
307 		sr = dumb_it_start_at_order(duh, n_channels, startorder);
308 		dp = al_duh_encapsulate_sigrenderer(sr,
309 			((float)(get_config_int("sound", "music_volume", 255)) / 255.0),
310 			get_config_int("dumb", "buffer_size", 4096),
311 			get_config_int("dumb", "sound_freq", 44100));
312 		if (!dp) duh_end_sigrenderer(sr); // howto.txt doesn't mention that this is necessary! dumb.txt does ...
313 	}
314 }
315 
316 
317 // delay routine used by the fades
fade_rest(int msec,AL_DUH_PLAYER * duh_player)318 void fade_rest(int msec, AL_DUH_PLAYER *duh_player) {
319 	int i = 0;
320 
321 	while(i < msec / 20) {
322 		cycle_count = 0;
323 		if (got_sound && duh_player != NULL) al_poll_duh(duh_player);
324 		i ++;
325 		while(!cycle_count)	yield_timeslice();
326 	}
327 }
328 
329 
330 // fades in from white to 4 color palette
fade_in_pal(int delay)331 void fade_in_pal(int delay) {
332 	set_color(1, &org_pal[3]);
333 	fade_rest(delay, dp);
334 
335 	set_color(1, &org_pal[2]);
336 	set_color(2, &org_pal[3]);
337 	fade_rest(delay, dp);
338 
339 	set_color(1, &org_pal[1]);
340 	set_color(2, &org_pal[2]);
341 	set_color(3, &org_pal[3]);
342 	fade_rest(delay, dp);
343 }
344 
345 
346 // fades 4 color palette to white
fade_out_pal(int delay)347 void fade_out_pal(int delay) {
348 	set_color(1, &org_pal[2]);
349 	set_color(2, &org_pal[3]);
350 	set_color(3, &org_pal[4]);
351 	fade_rest(delay, dp);
352 	set_color(1, &org_pal[3]);
353 	set_color(2, &org_pal[4]);
354 	fade_rest(delay, dp);
355 	set_color(1, &org_pal[4]);
356 	fade_rest(delay, dp);
357 }
358 
359 
360 
361 // fade in from black to 4 colors pal
fade_in_pal_black(int delay,AL_DUH_PLAYER * duh_player)362 void fade_in_pal_black(int delay, AL_DUH_PLAYER *duh_player) {
363 	set_color(4, &org_pal[2]);
364 	fade_rest(delay, duh_player);
365 
366 	set_color(3, &org_pal[2]);
367 	set_color(4, &org_pal[3]);
368 	fade_rest(delay, duh_player);
369 
370 	set_color(2, &org_pal[2]);
371 	set_color(3, &org_pal[3]);
372 	set_color(4, &org_pal[4]);
373 	fade_rest(delay, duh_player);
374 }
375 
376 
377 // fades 4 color palette to black
fade_out_pal_black(int delay,AL_DUH_PLAYER * duh_player)378 void fade_out_pal_black(int delay, AL_DUH_PLAYER *duh_player) {
379 	set_color(2, &org_pal[1]);
380 	set_color(3, &org_pal[2]);
381 	set_color(4, &org_pal[3]);
382 	fade_rest(delay, duh_player);
383 	set_color(3, &org_pal[1]);
384 	set_color(4, &org_pal[2]);
385 	fade_rest(delay, duh_player);
386 	set_color(4, &org_pal[1]);
387 	fade_rest(delay, duh_player);
388 }
389 
390 
391 
392 // ets all color to white
wipe_pal()393 void wipe_pal() {
394 	set_color(1, &org_pal[4]);
395 	set_color(2, &org_pal[4]);
396 	set_color(3, &org_pal[4]);
397 	set_color(4, &org_pal[4]);
398 }
399 
400 // removes trailing white space from a null terminated string
clear_trailing_whitespace(char * data)401 void clear_trailing_whitespace(char *data) {
402 	unsigned int i;
403 
404 	for(i = 0; i < strlen(data); i++) {
405 		if (data[i] == ' ' || data[i] == '\t' || data[i] == '\n' || data[i] == '\r')
406 			data[i] = 0;
407 	}
408 }
409 
410 
411 /// load the level filenames
load_level_files(const char * filename)412 void load_level_files(const char *filename) {
413 	FILE *fp;
414 	char buf[1024];
415 	int mode = 0;
416 	char *ret;
417 	char path[1024];
418 
419 	// get path to maps
420 	replace_filename(path, filename, "", 1024);
421 
422 	// reset counters
423 	num_levels = 0;
424 
425 	// open level file
426 	fp = fopen(filename, "rt");
427 	if (!fp) {
428 		allegro_message("%s not found", filename);
429 		log2file("  *** %s not found", filename);
430 		return;
431 	}
432 
433 	// start parsing
434     ret = fgets(buf, 1024, fp);  // read a line
435 	while(ret != NULL && mode == 0) {
436 		// read lines until #start# token is found
437 		if (!strncmp("#start#", buf, 7)) {
438 			mode = 1;
439 		}
440 	    ret = fgets(buf, 1024, fp);  // read a line
441 	}
442 	if (!mode) {
443 		mode = 2;
444 		allegro_message("#start# not found");
445 		log2file("  *** #start# not found");
446 	}
447 
448 	// read level lines until #end#
449 	while(ret != NULL && mode == 1) {
450 		// read levels until #end# token is found
451 		if (!strncmp("#end#", buf, 5)) {
452 			mode = 2;
453 		}
454 		else {
455 			char full_path_to_map[1024];
456 
457 			clear_trailing_whitespace(buf);
458 			sprintf(full_path_to_map, "%s%s", path, buf);
459 			level_files[num_levels] = strdup(full_path_to_map);
460 
461 			// test if map exists
462 			if (exists(level_files[num_levels])) {
463 				num_levels ++;
464 				log2file("  <%s> found", buf);
465 			}
466 			else {
467 				allegro_message("ALEX4:\n<%s> not found - skipping", level_files[num_levels]);
468 				log2file("  *** <%s> not found - skipping", level_files[num_levels]);
469 			}
470 		}
471 
472 		ret = fgets(buf, 1024, fp);  // read a line
473 	}
474 
475 	// close file
476 	fclose(fp);
477 
478 	return;
479 }
480 
481 
482 
483 
484 // blits anything to screen
blit_to_screen(BITMAP * bmp)485 void blit_to_screen(BITMAP *bmp) {
486 	acquire_screen();
487 	if (options.use_vsync) vsync();
488 	stretch_blit(bmp, screen, 0, 0, bmp->w, bmp->h, 0, 0, SCREEN_W, SCREEN_H);
489 	release_screen();
490 }
491 
492 
493 // draws the status bar
draw_status_bar(BITMAP * bmp,int y)494 void draw_status_bar(BITMAP *bmp, int y) {
495 	int i;
496 
497 	rectfill(bmp, 0, y, 159, y+9, 1);
498 	draw_sprite_h_flip(bmp, data[HERO_NORM].dat, 0, y+1);
499 	textprintf(bmp, data[THE_FONT].dat, 9, y+1, 4, " :%d", player.lives);
500 
501 	for(i = 0; i < player.health; i ++)
502 		draw_sprite(bmp, data[HEART2].dat, 40 + 10 * i, y-3);
503 
504 	draw_sprite(bmp, data[EGG].dat, 80, y-5);
505 	textprintf(bmp, data[THE_FONT].dat, 85, y+1, 4, " :%d", player.ammo);
506 
507 	textprintf_right(bmp, data[THE_FONT].dat, 158, y+1, 4, "%d", player.score);
508 }
509 
510 
511 // draws a frame of the action
draw_frame(BITMAP * bmp,int _status_bar)512 void draw_frame(BITMAP *bmp, int _status_bar) {
513 	int x0, x1, x2;
514 	int ox = map->offset_x;
515 	int i;
516 
517 	// calc bg pos
518 	x0 = (-ox % 1280) >> 3;
519 	x1 = (-ox % 640) >> 2;
520 	x2 = (-ox % 320) >> 1;
521 
522 	// draw backgrounds
523 	blit(data[BG0].dat, bmp, 0, 0, x0, 0, 160, 120);
524 	blit(data[BG0].dat, bmp, 0, 0, x0 + 160, 0, 160, 120);
525 	draw_sprite(bmp, data[BG1].dat, x1, 120 - ((BITMAP *)data[BG1].dat)->h);
526 	draw_sprite(bmp, data[BG1].dat, x1 + 160, 120 - ((BITMAP *)data[BG1].dat)->h);
527 	draw_sprite(bmp, data[BG2].dat, x2, 120 - ((BITMAP *)data[BG2].dat)->h);
528 	draw_sprite(bmp, data[BG2].dat, x2 + 160, 120 - ((BITMAP *)data[BG2].dat)->h);
529 
530 	// draw frame
531 	draw_map(bmp, map, 0, 0, 160, 120, editing);
532 
533 	if (!editing) {
534 		// draw actors
535 		for(i = 1; i < MAX_ACTORS; i ++)
536 			if (actor[i].active)
537 				draw_actor(bmp, &actor[i], actor[i].x - ox, actor[i].y);
538 
539 		// draw particles
540 		for(i = 0; i < MAX_PARTICLES; i ++)
541 			if (particle[i].life)
542 				draw_particle(bmp, &particle[i], ox, 0);
543 	}
544 
545 	// draw player
546 	draw_player(bmp, &player, player.actor->x - ox, player.actor->y);
547 
548 	// draw bullets
549 	for(i = 0; i < MAX_BULLETS; i ++)
550 		if (bullet[i].exist)
551 			draw_bullet(bmp, &bullet[i], ox, 0);
552 
553 	// draw statusbar
554 	if (!editing) {
555 		if (_status_bar)	draw_status_bar(bmp, 110);
556 		else rectfill(bmp, 0, 110, 159, 119, 1);
557 
558 	}
559 	else { 		/////////////// EDIT stats
560 		int mx = mouse_x / (SCREEN_W / 160);
561 		int my = mouse_y / (SCREEN_H / 120);
562 		draw_edit_mode(bmp, map, mx, my);
563 	}
564 }
565 
566 
567 // loads a sample from disk
load_path_sample(char * fname)568 SAMPLE *load_path_sample(char *fname) {
569 	char buf[1024];
570 	SAMPLE *s;
571 	sprintf(buf, "%s/%s", get_config_string("sound", "sfx_path", "sfx"), fname);
572 	s = load_sample(buf);
573 	if (s == NULL) {
574 		sprintf(buf, "not found: %s/%s", get_config_string("sound", "sfx_path", "sfx"), fname);
575 		log2file(" *** %s", buf);
576 		msg_box(buf);
577 	}
578 	return s;
579 }
580 
581 
582 // counts number of maps
583 // invoked when loading the map datafile
count_maps_callback(DATAFILE * d)584 void count_maps_callback(DATAFILE *d) {
585 	num_levels ++;
586 }
587 
588 
589 // invoked when game looses focus
display_switch_out(void)590 void display_switch_out(void) {
591 	if (got_sound) {
592 		al_pause_duh(dp);
593 		rest(100);
594 	}
595 }
596 
597 // invoked when game regains focus
display_switch_in(void)598 void display_switch_in(void) {
599 	if (got_sound) al_resume_duh(dp);
600 }
601 
602 // sets up the gui colors
fix_gui_colors()603 void fix_gui_colors() {
604 	((RGB *)data[0].dat)[255].r = 0;
605 	((RGB *)data[0].dat)[255].g = 0;
606 	((RGB *)data[0].dat)[255].b = 0;
607 	((RGB *)data[0].dat)[254].r = 63;
608 	((RGB *)data[0].dat)[254].g = 63;
609 	((RGB *)data[0].dat)[254].b = 63;
610 	gui_fg_color = 255;
611 	gui_bg_color = 254;
612 }
613 
614 // init the game
init_game(const char * map_file)615 int init_game(const char *map_file) {
616 	PACKFILE *pf;
617 	BITMAP *bmp;
618 	int i;
619 	int w, h;
620 #ifdef __unix__
621 	char filename[512];
622 	char *homedir = get_homedir();
623 #endif
624 
625 	log2file("\nInit routines:");
626 
627 	// various allegro things
628 	log2file(" initializing allegro");
629 	text_mode(-1);
630 	garble_string(init_string, 53);
631 #ifdef __unix__
632 	snprintf(filename, sizeof(filename), "%s/.alex4/alex4.ini",
633 		homedir? homedir:".");
634 	override_config_file(filename);
635 #else
636 	set_config_file("alex4.ini");
637 #endif
638 	set_window_close_button(FALSE);
639 
640 	// install timers
641 	log2file(" installing timers");
642 	install_timers();
643 
644 	// set color depth to 8bpp
645 	log2file(" setting color depth (8)");
646 	set_color_depth(8);
647 
648 	// allocating memory
649 	log2file(" allocating memory for off screen buffers");
650 	swap_screen = create_bitmap(160, 120);
651 	if (swap_screen == NULL) {
652 		log2file("  *** failed");
653 		allegro_message("ALEX4:\nFailed to allocate memory for swap screen.");
654 		return FALSE;
655 	}
656 
657 	log2file(" allocating memory for high score table 1");
658 	hisc_table = make_hisc_table();
659 	if (hisc_table == NULL) {
660 		log2file("  *** failed");
661 		allegro_message("ALEX4:\nFailed to allocate memory for high score list.");
662 		return FALSE;
663 	}
664 
665 	log2file(" allocating memory for high score table 2");
666 	hisc_table_space = make_hisc_table();
667 	if (hisc_table_space == NULL) {
668 		log2file("  *** failed");
669 		allegro_message("ALEX4:\nFailed to allocate memory for high score list.");
670 		return FALSE;
671 	}
672 
673 
674 	// init gfx
675 	if (get_config_int("graphics", "fullscreen", 0)) {
676 		w = get_config_int("graphics", "f_width", 640);
677 		h = get_config_int("graphics", "f_height", 480);
678 	}
679 	else {
680 		w = get_config_int("graphics", "w_width", 640);
681 		h = get_config_int("graphics", "w_height", 480);
682 	}
683 
684 	log2file(" entering gfx mode set in alex4.ini (%dx%d %s)", w, h, (get_config_int("graphics", "fullscreen", 0) ? "full" : "win"));
685 
686     if (set_gfx_mode(
687 		(get_config_int("graphics", "fullscreen", 0) ? GFX_AUTODETECT_FULLSCREEN : GFX_AUTODETECT_WINDOWED),
688 		w, h, 0, 0)) {
689 		log2file("  *** failed");
690 		log2file(" entering gfx mode (640x480 windowed)");
691 		if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0)) {
692 			log2file("  *** failed");
693 			log2file(" entering gfx mode (640x480 fullscreen)");
694 			if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0)) {
695 				log2file("  *** failed");
696 				allegro_message("ALEX4:\nFailed to enter gfx mode.\nTry setting a custom resolution in alex4.ini.");
697 				return FALSE;
698 			}
699 		}
700 	}
701 
702 	// show initial loading screen
703 	clear(swap_screen);
704 	textout_centre(swap_screen, font, "loading...", 320, 200, 1);
705 	blit_to_screen(swap_screen);
706 
707 #ifndef __unix__
708 	// set switch modes and callbacks
709 	if (set_display_switch_mode(SWITCH_PAUSE) < 0)
710 		log2file("  * display switch mode failed");
711 	if (set_display_switch_callback(SWITCH_IN, display_switch_in) < 0)
712 		log2file("  * display switch in failed");
713 	if (set_display_switch_callback(SWITCH_OUT, display_switch_out) < 0)
714 		log2file("  * display switch out failed");
715 #endif
716 
717 
718 	// set win title (no! really???)
719 	log2file(" setting window title");
720 	set_window_title("Alex 4");
721 
722 	// register dumb
723 	log2file(" registering dumb");
724 	dumb_register_packfiles();
725     dumb_register_dat_mod(DUMB_DAT_MOD);
726 	dumb_resampling_quality = get_config_int("dumb", "dumb_resampling_quality", 4);
727 	dumb_it_max_to_mix = get_config_int("dumb", "dumb_it_max_to_mix", 128);
728 
729 	// load data
730 	log2file(" loading data");
731 	packfile_password(init_string);
732 	data = load_datafile(DATADIR "data.dat");
733 	packfile_password(NULL);
734 	if (data == NULL) {
735     	log2file("  *** failed");
736 		allegro_message("ALEX4:\nFailed to load data.");
737 		return FALSE;
738 	}
739 
740 	// load options
741 	log2file(" loading options");
742 #ifdef __unix__
743 	snprintf(filename, sizeof(filename), "%s/.alex4/alex4.sav",
744 		homedir? homedir:".");
745 	pf = pack_fopen(filename, "rp");
746 #else
747 	pf = pack_fopen("alex4.sav", "rp");
748 #endif
749 	if (pf) {
750 		load_options(&options, pf);
751 		pack_fclose(pf);
752 	}
753 	else {
754 	    log2file("  *** failed, resetting to defaults");
755 		reset_options(&options);
756 	}
757 
758 	// loading highscores
759 	log2file(" loading hiscores");
760 #ifdef __unix__
761 	snprintf(filename, sizeof(filename), "%s/.alex4/alex4.hi",
762 		homedir? homedir:".");
763 	pf = pack_fopen(filename, "rp");
764 #else
765 	pf = pack_fopen("alex4.hi", "rp");
766 #endif
767 	if (pf) {
768 		load_hisc_table(hisc_table, pf);
769 		load_hisc_table(hisc_table_space, pf);
770 		pack_fclose(pf);
771 	}
772 	else {
773 	    log2file("  *** failed, resetting");
774 		reset_hisc_table(hisc_table, "alex", 25000, 5000);
775 		sort_hisc_table(hisc_table);
776 		reset_hisc_table(hisc_table_space, "Lola", 3000000, 600000);
777 		sort_hisc_table(hisc_table_space);
778 	}
779 
780 	// fix some palette entries
781 	((RGB *)data[0].dat)[0].r = 0;
782 	((RGB *)data[0].dat)[0].g = 0;
783 	((RGB *)data[0].dat)[0].b = 0;
784 	fix_gui_colors();
785 	set_palette(data[0].dat);
786 
787 	// show splash screen
788 	clear_to_color(swap_screen, 3);
789 
790 	bmp = data[FLD_LOGO].dat;
791 	draw_character(swap_screen, bmp, 80 - bmp->w / 2 + 0, 50 + 1, 1);
792 	draw_character(swap_screen, bmp, 80 - bmp->w / 2, 50, 4);
793 
794 	blit_to_screen(swap_screen);
795 
796 
797 	// load maps
798 	if (playing_original_game) {
799 		log2file(" loading original maps");
800 		packfile_password(init_string);
801 		num_levels = -1;  // skip end object when counting
802 		maps = load_datafile_callback(DATADIR "maps.dat", count_maps_callback);
803 		packfile_password(NULL);
804 		if (maps == NULL) {
805 	    	log2file("  *** failed");
806 			allegro_message("ALEX4:\nFailed to load original maps.");
807 			return FALSE;
808 		}
809 		log2file(" loaded %d maps", num_levels);
810 	}
811 	else {
812 		log2file(" loading custom maps");
813 		log2file("  reading map file: %s", map_file);
814 		if (exists(map_file)) {
815 			// load level file names
816 			load_level_files(map_file);
817 			log2file("  %d maps loaded", num_levels);
818 			if (num_levels == 0) {
819 				log2file("  *** no maps were loaded");
820 				allegro_message("ALEX4:\nCustom map file must\nhold at least one\nlegal map file.");
821 				return FALSE;
822 			}
823 		}
824 		else {
825 			log2file("  *** file not found: %s", map_file);
826 			allegro_message("ALEX4:\nCustom map file not found:\n%s", map_file);
827 			return FALSE;
828 		}
829 	}
830 
831 
832 	// install some parts of allegro
833 	log2file(" installing keyboard");
834 	install_keyboard();
835 	log2file(" installing mouse");
836 	install_mouse();
837 
838 	// fix palette
839 	for(i = 0; i < 256; i ++) {
840 		org_pal[i].r = ((RGB *)data[0].dat)[i].r;
841 		org_pal[i].g = ((RGB *)data[0].dat)[i].g;
842 		org_pal[i].b = ((RGB *)data[0].dat)[i].b;
843 	}
844 
845 	// init control
846 	log2file(" initializing controls");
847 	init_control(&ctrl);
848 
849 	// initializing joystick
850 	log2file(" initializing joystick/gamepad");
851 	if (install_joystick(JOY_TYPE_AUTODETECT)) {
852     	log2file("  *** failed");
853 	}
854 	else {
855 		ctrl.use_joy = 1;
856 	}
857 
858 	// install sound
859 	log2file(" installing sound");
860    	set_volume_per_voice(0);
861 	switch(get_config_int("sound", "sound_device", 1)) {
862 		case 1:
863 			i = DIGI_AUTODETECT;
864 			log2file("  DIGI_AUTODETECT selected (%d)", i);
865 			break;
866 #ifdef ALLEGRO_WINDOWS
867 		case 2:
868 			i = DIGI_DIRECTX(0);
869 			log2file("  DIGI_DIRECTX(0) selected (%d)", i);
870 			break;
871 		case 3:
872 			i = DIGI_DIRECTAMX(0);
873 			log2file("  DIGI_DIRECTAMX(0) selected (%d)", i);
874 			break;
875 #elif defined ALLEGRO_UNIX
876 #ifdef DIGI_OSS
877 		case 2:
878 			i = DIGI_OSS;
879 			log2file("  DIGI_OSS selected (%d)", i);
880 			break;
881 #endif
882 #ifdef DIGI_ALSA
883 		case 3:
884 			i = DIGI_ALSA;
885 			log2file("  DIGI_ALSA selected (%d)", i);
886 			break;
887 #endif
888 #endif
889 		default:
890 			i = -770405;	// dummy number
891 			got_sound = 0;
892 			log2file("  NO_SOUND selected");
893 			break;
894 	}
895 	if (i != -770405) {
896 		if (install_sound(i, MIDI_NONE, NULL)) {
897 			log2file("  *** failed");
898 			got_sound = 0;
899 		}
900 		else got_sound = 1;
901 	}
902 
903 	if (got_sound) {
904 		int s = 0;
905 		// load music
906         duh = (DUH *)data[MSC_GAME].dat;
907 
908 		if (get_config_int("sound", "use_sound_datafile", 1)) {
909 			log2file(" loading sound datafile");
910 			packfile_password(init_string);
911 			sfx_data = load_datafile(DATADIR "sfx_44.dat");
912 			if (sfx_data == NULL) {
913 				sfx_data = load_datafile(DATADIR "sfx_22.dat");
914 				log2file("  sfx_44.dat not found");
915 				s = 0;
916 			}
917 			else {
918 				s = 1;
919 				log2file("  sfx_44.dat will be used");
920 			}
921 			packfile_password(NULL);
922 			if (sfx_data == NULL) {
923 				log2file("  sfx_22.dat not found");
924 				log2file("  *** failed - no sfx will be available");
925 			}
926 			else {
927 				if (!s) log2file("  sfx_22.dat will be used");
928 
929 				// assign samples
930 				sfx[SMPL_CHERRY]	 = sfx_data[0].dat;
931 				sfx[SMPL_CHOPPER]	 = sfx_data[1].dat;
932 				sfx[SMPL_CRUSH]		 = sfx_data[2].dat;
933 				sfx[SMPL_A_DIE]		 = sfx_data[3].dat;
934 				sfx[SMPL_EAT]		 = sfx_data[4].dat;
935 				sfx[SMPL_BEAM]		 = sfx_data[5].dat;
936 				sfx[SMPL_ENGINE]	 = sfx_data[6].dat;
937 				sfx[SMPL_HEART]		 = sfx_data[7].dat;
938 				sfx[SMPL_HIT]		 = sfx_data[8].dat;
939 				sfx[SMPL_HURT]		 = sfx_data[9].dat;
940 				sfx[SMPL_CRUSH_LAND] = sfx_data[10].dat;
941 				sfx[SMPL_JUMP]		 = sfx_data[11].dat;
942 				sfx[SMPL_E_DIE]		 = sfx_data[12].dat;
943 				sfx[SMPL_MENU]		 = sfx_data[13].dat;
944 				sfx[SMPL_PAUSE]		 = sfx_data[14].dat;
945 				sfx[SMPL_POINT]		 = sfx_data[15].dat;
946 				sfx[SMPL_SHIP]		 = sfx_data[16].dat;
947 				sfx[SMPL_SHOOT]		 = sfx_data[17].dat;
948 				sfx[SMPL_SPIT]		 = sfx_data[18].dat;
949 				sfx[SMPL_STAR]		 = sfx_data[19].dat;
950 				sfx[SMPL_STARTUP]	 = sfx_data[20].dat;
951 				sfx[SMPL_MASH]		 = sfx_data[21].dat;
952 				sfx[SMPL_TALK]		 = sfx_data[22].dat;
953 				sfx[SMPL_SPIN]		 = sfx_data[23].dat;
954 				sfx[SMPL_XTRALIFE]	 = sfx_data[24].dat;
955 			}
956 		}
957 		else {
958 			// load sounds from disk
959 			log2file(" loading sounds from %s", get_config_string("sound", "sfx_path", ""));
960 			sfx[SMPL_STARTUP]	 = load_path_sample("startup.wav");
961 			sfx[SMPL_POINT]		 = load_path_sample("point.wav");
962 			sfx[SMPL_JUMP]		 = load_path_sample("jump.wav");
963 			sfx[SMPL_MASH]		 = load_path_sample("stomp.wav");
964 			sfx[SMPL_EAT]		 = load_path_sample("eat.wav");
965 			sfx[SMPL_SPIT]		 = load_path_sample("spit.wav");
966 			sfx[SMPL_A_DIE]		 = load_path_sample("die.wav");
967 			sfx[SMPL_HIT]		 = load_path_sample("hit.wav");
968 			sfx[SMPL_CRUSH]		 = load_path_sample("crush.wav");
969 			sfx[SMPL_E_DIE]		 = load_path_sample("kill.wav");
970 			sfx[SMPL_HEART]		 = load_path_sample("heart.wav");
971 			sfx[SMPL_HURT]		 = load_path_sample("hurt.wav");
972 			sfx[SMPL_XTRALIFE]	 = load_path_sample("xtralife.wav");
973 			sfx[SMPL_CHERRY]	 = load_path_sample("cherry.wav");
974 			sfx[SMPL_MENU]		 = load_path_sample("menu.wav");
975 			sfx[SMPL_SHOOT]		 = load_path_sample("shoot.wav");
976 			sfx[SMPL_SPIN]		 = load_path_sample("turn.wav");
977 			sfx[SMPL_STAR]		 = load_path_sample("star.wav");
978 			sfx[SMPL_CRUSH_LAND] = load_path_sample("impact.wav");
979 			sfx[SMPL_PAUSE]		 = load_path_sample("pause.wav");
980 			sfx[SMPL_ENGINE]	 = load_path_sample("engine.wav");
981 			sfx[SMPL_CHOPPER]	 = load_path_sample("chopper.wav");
982 			sfx[SMPL_SHIP]		 = load_path_sample("ship.wav");
983 			sfx[SMPL_BEAM]		 = load_path_sample("energy.wav");
984 			sfx[SMPL_TALK]		 = load_path_sample("alex.wav");
985 		}
986 	}
987 
988 
989 	// misc
990 	log2file(" initializing scroller");
991 	init_scroller(&hscroll, data[THE_FONT].dat, scroller_message, 160, 10, TRUE);
992 
993 	options.use_vsync = get_config_int("graphics", "vsync", 0);
994 	log2file(" vsync set to %s", (options.use_vsync ? "ON" : "OFF"));
995 
996 	// done!
997 	play_sound(sfx[SMPL_STARTUP]);
998 	wait_key(2);
999 	fade_out_pal(100);
1000 
1001 
1002 	init_ok = 1;
1003 	log2file(" init OK!");
1004 	return TRUE;
1005 }
1006 
1007 
1008 // uninits the game
uninit_game()1009 void uninit_game() {
1010 	int i;
1011 	PACKFILE *pf;
1012 #ifdef __unix__
1013 	char filename[512];
1014 	char *homedir = get_homedir();
1015 #endif
1016 
1017 	log2file("\nExit routines:");
1018 
1019 	log2file(" unloading datafile");
1020 	if (data != NULL) unload_datafile(data);
1021 
1022 	log2file(" unloading original maps");
1023 	if (maps != NULL) unload_datafile(maps);
1024 
1025 	log2file(" destroying temporary map");
1026 	if (map != NULL) destroy_map(map);
1027 
1028 	log2file(" freeing level names");
1029 	for(i = 0; i < num_levels; i ++) free(level_files[i]);
1030 
1031 	// only save if everything was inited ok!
1032 	if (init_ok) {
1033 		log2file(" saving options");
1034 #ifdef __unix__
1035 		snprintf(filename, sizeof(filename), "%s/.alex4/alex4.sav",
1036 			homedir? homedir:".");
1037 		pf = pack_fopen(filename, "wp");
1038 #else
1039 		pf = pack_fopen("alex4.sav", "wp");
1040 #endif
1041 		if (pf) {
1042 			save_options(&options, pf);
1043 			pack_fclose(pf);
1044 		}
1045 
1046 		log2file(" saving highscores");
1047 #ifdef __unix__
1048 		snprintf(filename, sizeof(filename), "%s/.alex4/alex4.hi",
1049 			homedir? homedir:".");
1050 		pf = pack_fopen(filename, "wp");
1051 #else
1052 		pf = pack_fopen("alex4.hi", "wp");
1053 #endif
1054 		if (pf) {
1055 			save_hisc_table(hisc_table, pf);
1056 			save_hisc_table(hisc_table_space, pf);
1057 			pack_fclose(pf);
1058 		}
1059 	}
1060 
1061 	if (get_config_int("sound", "use_sound_datafile", 1)) {
1062 		log2file(" unloading sound data");
1063 		if (sfx_data != NULL) unload_datafile(sfx_data);
1064 	}
1065 	else {
1066 		log2file(" freeing sounds");
1067 		for(i = 0; i < MAX_SOUNDS; i ++) {
1068 			if (sfx[i] != NULL)	destroy_sample(sfx[i]);
1069 		}
1070 	}
1071 
1072 	log2file(" exiting dumb");
1073 	dumb_exit();
1074 
1075 	log2file(" exiting allegro");
1076 	allegro_exit();
1077 }
1078 
1079 
1080 // inits the player on a map
init_player(Tplayer * p,Tmap * m)1081 void init_player(Tplayer *p, Tmap *m) {
1082 	actor[0].direction = 1;
1083 	actor[0].x = m->start_x << 4;
1084 	if (actor[0].x < 0) {
1085 		actor[0].direction = 0;
1086 		actor[0].x = -actor[0].x;
1087 	}
1088 	actor[0].dy = 0;
1089 	actor[0].y = (m->start_y << 4) + 16;
1090 
1091 	p->jumping = 0;
1092 	p->wounded = 0;
1093 	p->dy = 0;
1094 	p->ammo = 0;
1095 	p->eat_counter = 0;
1096 	p->actor->status = (p->ammo ? AC_FULL : AC_NORM);
1097 	p->health = MAX(p->health, 1);
1098 }
1099 
1100 
1101 // draws text with an outline
textout_outline(BITMAP * bmp,const char * txt,int x,int y)1102 void textout_outline(BITMAP *bmp, const char *txt, int x, int y) {
1103 	textout(bmp, data[THE_FONT].dat, txt, x+1, y, 1);
1104 	textout(bmp, data[THE_FONT].dat, txt, x-1, y, 1);
1105 	textout(bmp, data[THE_FONT].dat, txt, x, y+1, 1);
1106 	textout(bmp, data[THE_FONT].dat, txt, x, y-1, 1);
1107 	textout(bmp, data[THE_FONT].dat, txt, x, y, 4);
1108 }
1109 
1110 
1111 // draws centered text with an outline
textout_outline_center(BITMAP * bmp,const char * txt,int cx,int y)1112 void textout_outline_center(BITMAP *bmp, const char *txt, int cx, int y) {
1113 	int x = cx - text_length(data[THE_FONT].dat, txt) / 2;
1114 	textout_outline(bmp, txt, x, y);
1115 }
1116 
1117 
1118 
1119 // plays the let's go sequence
show_lets_go()1120 void show_lets_go() {
1121 	BITMAP *go = data[LETSGO].dat;
1122 	int x = -go->w;
1123 	int mode = 0;
1124 	int wait = 0;
1125 	int y = 120, ty = 60;
1126 	int dy = 0;
1127 
1128 	cycle_count = 0;
1129 	while(mode != 3) {
1130 		// do logic
1131 		while(cycle_count > 0) {
1132 			logic_count ++;
1133 
1134 			// poll music machine
1135 			if (got_sound) al_poll_duh(dp);
1136 
1137 			// move text
1138 			if (mode == 0 || mode == 2) x += 4;
1139 			if (x >= 80 - go->w/2) mode = 1;
1140 			if (x > 160) mode = 3;
1141 			if (mode == 1) wait ++;
1142 			if (wait > 50 && mode == 1) {
1143 				mode = 2;
1144 				ty = 130;
1145 			}
1146 
1147 			// move level name
1148 			if (y > ty) y -= (y-ty)/8;
1149 			if (y < ty) y += dy++;
1150 
1151 			// move on
1152 			cycle_count --;
1153 		}
1154 
1155 		// let other processes play
1156 		yield_timeslice();
1157 
1158 		// draw stuff
1159 		draw_frame(swap_screen, 1);
1160 		draw_sprite(swap_screen, go, x, 35);
1161 		textout_outline_center(swap_screen, map->name, 80, y);
1162 		blit_to_screen(swap_screen);
1163 
1164 	}
1165 }
1166 
1167 // shows the game over sign sequence
show_game_over()1168 void show_game_over() {
1169 	BITMAP *go = data[GAME_OVER].dat;
1170 
1171 	int x = -go->w;
1172 	int mode = 0;
1173 	int wait = 0;
1174 
1175 	cycle_count = 0;
1176 	while(mode != 3) {
1177 		// do logic
1178 		while(cycle_count > 0) {
1179 			logic_count ++;
1180 
1181 			// poll music machine
1182 			if (got_sound) al_poll_duh(dp);
1183 
1184 			// move text
1185 			if (mode == 0 || mode == 2) x += 4;
1186 			if (x >= 80 - go->w/2) mode = 1;
1187 			if (x > 160) mode = 3;
1188 			if (mode == 1) wait ++;
1189 			if (wait > 120 && mode == 1) mode = 2;
1190 
1191 			if (key[KEY_ESC]) mode = 3;
1192 
1193 			// move on
1194 			cycle_count --;
1195 		}
1196 
1197 		// let other processes play
1198 		yield_timeslice();
1199 
1200 		// draw stuff
1201 		draw_frame(swap_screen, 1);
1202 		draw_sprite(swap_screen, go, x, 35);
1203 		blit_to_screen(swap_screen);
1204 	}
1205 }
1206 
1207 // drawing routine used by
1208 // show_custom_ending()
draw_custom_ending(BITMAP * bmp)1209 void draw_custom_ending(BITMAP *bmp) {
1210 	int i, r;
1211 	BITMAP *head = data[FLD_HEAD].dat;
1212 
1213 	blit(data[INTRO_BG].dat, bmp, 0, 0, 0, 0, 160, 120);
1214 
1215 	r = 70 + fixtoi(20 * fixcos(itofix(game_count >> 1)) + 20 * fixsin(itofix((int)(game_count / 2.7))) );
1216 	for(i = 0; i < 256; i += 32)
1217 		draw_sprite(bmp, head, 80 - head->w/2 + fixtoi(r * fixcos(itofix(game_count + i))), 60 - head->h/2 + fixtoi(r * fixsin(itofix(game_count + i))));
1218 
1219 	draw_sprite_h_flip(bmp, data[ALEX].dat, 60, 40);
1220 	draw_sprite(bmp, data[LOLA].dat, 84, 40);
1221 
1222 	textout_outline_center(bmp, "Congratulations!", 80, 60);
1223 	textout_outline_center(bmp, "A winner is you!", 80, 80);
1224 }
1225 
1226 // show the ending of a custom game
show_custom_ending()1227 void show_custom_ending() {
1228 	int done = 0;
1229 	int wait = 1000; // ten seconds
1230 	int tmp;
1231 
1232 	tmp = game_count;
1233 	draw_custom_ending(swap_screen);
1234 	blit_to_screen(swap_screen);
1235 	fade_in_pal(100);
1236 	game_count = tmp;
1237 
1238 	cycle_count = 0;
1239 	while(!done) {
1240 		// do logic
1241 		while(cycle_count > 0) {
1242 			logic_count ++;
1243 
1244 			// poll music machine
1245 			if (got_sound) al_poll_duh(dp);
1246 
1247 			// poll user
1248 			poll_control(&ctrl);
1249 			if (is_fire(&ctrl) || is_jump(&ctrl) || key[KEY_SPACE] || key[KEY_ENTER] || key[KEY_ESC])
1250 				done = 1;
1251 
1252 			// decrease time
1253 			if (--wait < 0) done = 1;
1254 
1255 			// move on
1256 			cycle_count --;
1257 		}
1258 
1259 		// let other processes play
1260 		yield_timeslice();
1261 
1262 		// draw stuff
1263 		draw_custom_ending(swap_screen);
1264 		blit_to_screen(swap_screen);
1265 	}
1266 
1267 }
1268 
1269 // lighten or darken a 4 color bitmap
transform_bitmap(BITMAP * bmp,int steps)1270 void transform_bitmap(BITMAP *bmp, int steps) {
1271 	int x, y;
1272 	int c;
1273 
1274 	for(x = 0; x < bmp->w; x ++) {
1275 		for(y = 0; y < bmp->h; y ++) {
1276 			c = _getpixel(bmp, x, y);
1277 			_putpixel(bmp, x, y, MIN(MAX(c + steps, 1), 4));
1278 		}
1279 		if (got_sound) al_poll_duh(dp);
1280 	}
1281 }
1282 
1283 // draws the scoring sequence at end of level
1284 // used by show_cutscene(..)
draw_cutscene(BITMAP * bmp,int org_level,int _level,int _lives,int _stars,int _cherries)1285 void draw_cutscene(BITMAP *bmp, int org_level, int _level, int _lives, int _stars, int _cherries) {
1286 	BITMAP *go = data[LEVELCOMPLETE].dat;
1287 	char buf[80];
1288 
1289 	clear_bitmap(bmp);
1290 
1291 	draw_sprite(bmp, go, 80 - go->w/2, 10);
1292 
1293 	sprintf(buf, "Level    %2d*100 = %4d", org_level, _level);
1294 	textout_outline(bmp, buf, 3, 60);
1295 	sprintf(buf, "Lives    %2d*100 = %4d", player.lives, _lives);
1296 	textout_outline(bmp, buf, 3, 72);
1297 	sprintf(buf, "Stars    %2d*100 = %4d", player.stars_taken, _stars);
1298 	textout_outline(bmp, buf, 3, 84);
1299 	sprintf(buf, "Cherries %2d* 10 = %4d", player.cherries_taken, _cherries);
1300 	textout_outline(bmp, buf, 3, 96);
1301 }
1302 
1303 // shows the scoring sequence at end of level
show_cutscene(int level)1304 void show_cutscene(int level) {
1305 	int x = -160;
1306 	BITMAP *bmp = create_bitmap(160, 120);
1307 	BITMAP *swap2 = create_bitmap(160, 120);
1308 	int mode = 0;
1309 	int my_counter = 0;
1310 
1311 	int _lives = player.lives * 100;
1312 	int _level = level * 100;
1313 	int _cherries = player.cherries_taken * 10;
1314 	int _stars = player.stars_taken * 100;
1315 
1316 	if (swap2 == NULL || bmp == NULL) {
1317 		if (swap2 != NULL) destroy_bitmap(swap2);
1318 		if (bmp != NULL) destroy_bitmap(bmp);
1319 		return;
1320 	}
1321 
1322 	// music!
1323 	if (got_sound) start_music(MOD_LEVEL_DONE);
1324 
1325 
1326 	// create cutscene screene
1327 	blit(swap_screen, swap2, 0, 0, 0, 0, 160, 120);
1328 
1329 	transform_bitmap(swap2, -1);
1330 
1331 	draw_cutscene(bmp, level, _level, _lives, _stars, _cherries);
1332 
1333 	// scroll bmp onto swap_screen
1334 	clear_keybuf();
1335 	cycle_count = 0;
1336 	while(mode != 3) {
1337 		// do logic
1338 		while(cycle_count > 0) {
1339 			logic_count ++;
1340 			my_counter ++;
1341 			poll_control(&ctrl);
1342 
1343 			// poll music machine
1344 			if (got_sound) al_poll_duh(dp);
1345 
1346 			if (((mode == 1) && (keypressed() || is_fire(&ctrl) || is_jump(&ctrl))) || (my_counter > 200)) {
1347 				mode = 2;
1348 			}
1349 
1350 			// move text
1351 			if (mode == 0) x += 8;
1352 			if (x == 0 && mode == 0) {
1353 				mode = 1;
1354 				clear_keybuf();
1355 			}
1356 
1357 			// count stats
1358 			if (mode == 2 && !(game_count % 4)) {
1359 				int a = 0;
1360 				if (_level)    { player.score +=  100; _level    -= 100; a++; }
1361 				if (_lives)    { player.score +=  100; _lives    -= 100; a++; }
1362 				if (_stars)    { player.score +=  100; _stars    -= 100; a++; }
1363 				if (_cherries) { player.score +=   10; _cherries -=  10; a++; }
1364 				if (!a) mode = 3;
1365 
1366 				play_sound(sfx[SMPL_POINT]);
1367 
1368 				draw_cutscene(bmp, level, _level, _lives, _stars, _cherries);
1369 			}
1370 
1371 			// move on
1372 			cycle_count --;
1373 		}
1374 
1375 		// let other processes play
1376 		yield_timeslice();
1377 
1378 		// draw stuff
1379 		blit(swap2, swap_screen, 0, 0, 0, 0, 160, 120);
1380 		draw_status_bar(swap_screen, 110);
1381 		draw_sprite(swap_screen, bmp, x, 0);
1382 		blit_to_screen(swap_screen);
1383 	}
1384 
1385 	wait_key(5);
1386 
1387 	if (got_sound) stop_music();
1388 
1389 	destroy_bitmap(bmp);
1390 }
1391 
1392 // shows a highscore table
show_scores(int space,Thisc * table)1393 void show_scores(int space, Thisc *table) {
1394 	DATAFILE *df = NULL;
1395 	BITMAP *bg = NULL;
1396 
1397 	if (space) {
1398 		// get space bg
1399 		packfile_password(init_string);
1400 		df = load_datafile_object(DATADIR "a45.dat", "BG1");
1401 		packfile_password(NULL);
1402 		if (df != NULL)	{
1403 			bg = df->dat;
1404 		}
1405 		else
1406 			msg_box("ooga");
1407 	}
1408 
1409 	if (bg == NULL || !space)
1410 		blit(data[INTRO_BG].dat, swap_screen, 0, 0, 0, 0, 160, 120);
1411 	else {
1412 		clear_to_color(swap_screen, 1);
1413 		blit(bg, swap_screen, 0, 0, 0, 0, 160, 120);
1414 	}
1415 
1416 
1417 	textout_outline_center(swap_screen, "High scores", 80, 8);
1418 	textout_outline_center(swap_screen, "Press any key", 80, 100);
1419 	draw_hisc_table(table, swap_screen, data[THE_FONT].dat, 10, 30, (space ? 4 : 1), !space);
1420 
1421 	blit_to_screen(swap_screen);
1422 	fade_in_pal(100);
1423 
1424 	clear_keybuf();
1425 	poll_control(&ctrl);
1426 	while(!is_jump(&ctrl) && !keypressed()) {
1427 		poll_control(&ctrl);
1428 		if (got_sound) al_poll_duh(dp);
1429 	}
1430 	play_sound(sfx[SMPL_MENU]);
1431 
1432 	fade_out_pal(100);
1433 
1434 	// clean up
1435 	if (df != NULL) unload_datafile_object(df);
1436 
1437 }
1438 
1439 // draws the level selector
draw_select_starting_level(BITMAP * bmp,int level,int min,int max)1440 void draw_select_starting_level(BITMAP *bmp, int level, int min, int max) {
1441 	BITMAP *stuff = create_bitmap(40, 10);
1442 	char buf[80];
1443 	int xpos = 2;
1444 
1445 	blit(data[ALEX_BG].dat, bmp, 0, 0, 0, 0, 160, 112);
1446 	rectfill(bmp, 0, 112, 160, 120, 2);
1447 
1448 	sprintf(buf, "%s %d %s", (level > min ? "<" : " "), level, (level < max ? ">" : " "));
1449 	clear_bitmap(stuff);
1450 	textout_centre(stuff, data[THE_FONT].dat, buf, stuff->w/2 + 1, 1, 2);
1451 	textout_centre(stuff, data[THE_FONT].dat, buf, stuff->w/2, 0, 1);
1452 	stretch_sprite(bmp, stuff, 80 - 4*stuff->w/2, 30, 4*stuff->w, 4*stuff->h);
1453 
1454 	textout_centre(bmp, data[THE_FONT].dat, "SELECT START LEVEL", 80, 90, 1);
1455 	textout_centre(bmp, data[THE_FONT].dat, "SELECT START LEVEL", 79, 89, 4);
1456 
1457 	if (options.one_hundred) {
1458 		if (game_count & 32 || game_count & 16) draw_sprite(bmp, data[SHIP100].dat, xpos, 2);
1459 	}
1460 	else {
1461 		if (options.stars[level - 1]) {
1462 			draw_sprite(bmp, data[STAR].dat, xpos, 2);
1463 			draw_sprite(bmp, data[ALL100].dat, xpos + 4, 14);
1464 			xpos += 20;
1465 		}
1466 		if (options.cherries[level - 1]) {
1467 			draw_sprite(bmp, data[CHERRY].dat, xpos, 2);
1468 			draw_sprite(bmp, data[ALL100].dat, xpos + 4, 14);
1469 		}
1470 	}
1471 
1472 
1473 	destroy_bitmap(stuff);
1474 }
1475 
1476 
1477 // lets the player select starting level
select_starting_level()1478 int select_starting_level() {
1479 	int start_level = 1;
1480 	int done = 0;
1481 	int counter = 0;
1482 
1483 	draw_select_starting_level(swap_screen, start_level, 1, options.max_levels + 1);
1484 	blit_to_screen(swap_screen);
1485 	fade_in_pal(100);
1486 
1487 	clear_keybuf();
1488 
1489 	cycle_count = 0;
1490 	while(!done) {
1491 		// do logic
1492 		while(cycle_count > 0) {
1493 			logic_count ++;
1494 
1495 			// poll music machine
1496 			if (got_sound) al_poll_duh(dp);
1497 
1498 			// check controls
1499 			poll_control(&ctrl);
1500 			if (is_right(&ctrl) && start_level < options.max_levels + 1 && !counter) {
1501 				start_level ++;
1502 				play_sound(sfx[SMPL_MENU]);
1503 			}
1504 
1505 			if (is_left(&ctrl) && start_level > 1 && !counter) {
1506 				start_level --;
1507 				play_sound(sfx[SMPL_MENU]);
1508 			}
1509 
1510 			if (is_jump(&ctrl) || is_fire(&ctrl)) {
1511 				done = 1;
1512 				play_sound(sfx[SMPL_MENU]);
1513 			}
1514 			if (keypressed()) {
1515 				int scancode = readkey() >> 8;
1516 				if (scancode == KEY_SPACE || scancode == KEY_ENTER) {
1517 					done = 1;
1518 					play_sound(sfx[SMPL_MENU]);
1519 				}
1520 				if (scancode == KEY_F1 && options.one_hundred) {
1521 					done = 1;
1522 					start_level = -100;
1523 					play_sound(sfx[SMPL_MENU]);
1524 				}
1525 				if (scancode == KEY_ESC) {
1526 					done = 1;
1527 					start_level = -1;
1528 					play_sound(sfx[SMPL_MENU]);
1529 				}
1530 			}
1531 			if (!is_left(&ctrl) || !is_right(&ctrl)) counter = 0;
1532 			if (is_left(&ctrl) || is_right(&ctrl)) counter = 10;
1533 
1534 			// move on
1535 			cycle_count --;
1536 		}
1537 
1538 		// draw stuff
1539 		if (start_level >= 0) {
1540 			draw_select_starting_level(swap_screen, start_level, 1, options.max_levels + 1);
1541 			blit_to_screen(swap_screen);
1542 		}
1543 	}
1544 
1545 	return start_level;
1546 }
1547 
1548 
1549 
1550 // starts a new game
new_game(int reset_player_data)1551 void new_game(int reset_player_data) {
1552 	// init player
1553 	if (reset_player_data) {
1554 		player.ammo = 0;
1555 		player.lives = 2;
1556 		player.score = 0;
1557 		player.health = 1;
1558 	}
1559 	player.actor = &actor[0];
1560 	player.eat_counter = 0;
1561 	actor[0].active = 1;
1562 	actor[0].data = data;
1563 	actor[0].frames[0] = HERO000;
1564 	actor[0].frames[1] = HERO001;
1565 	actor[0].frames[2] = HERO002;
1566 	actor[0].frames[3] = HERO003;
1567 	actor[0].frames[4] = HERO_NORM;
1568 	actor[0].num_frames = 4;
1569 	actor[0].frame = 0;
1570 	actor[0].anim_counter = 0;
1571 	actor[0].anim_max = 4;
1572 }
1573 
1574 
1575 // tidies up after a map has been used
deinit_map(Tmap * m)1576 void deinit_map(Tmap *m) {
1577 	int i;
1578 
1579 	// stop any playing sounds
1580 	for(i = 1; i < MAX_ACTORS; i ++) {
1581 		if (actor[i].active && actor[i].sound != -1) {
1582 			stop_sound_id(actor[i].sound);
1583 		}
1584 	}
1585 }
1586 
1587 
1588 
1589 
1590 // sets up actors and stuff
init_map(Tmap * m)1591 void init_map(Tmap *m) {
1592 	int x, y, i;
1593 
1594 	m->win_conditions_fullfilled = 0;
1595 	m->num_enemies = 0;
1596 
1597 	for(i = 1; i < MAX_ACTORS; i ++) {
1598 		actor[i].active = 0;
1599 	}
1600 
1601 	// check entire map for enemies and initialize
1602 	// them as they are found
1603 	for(x = 0; x < m->width; x ++) {
1604 		for(y = 0; y < m->height; y ++) {
1605 			if (m->dat[x + y * m->width].type == MAP_ENEMY1) {	// small human
1606 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1607 				if (a != NULL) {
1608 					a->type = MAP_ENEMY1;
1609 					a->num_frames = 4;
1610 					a->frames[0] = ENEMY1_01;
1611 					a->frames[1] = ENEMY1_02;
1612 					a->frames[2] = ENEMY1_03;
1613 					a->frames[3] = ENEMY1_04;
1614 					a->flat_frame = ENEMY1_05;
1615 					a->w = 12; a->h = 14;
1616 					a->ox = 2; a->oy = 2;
1617 					a->flags = ACF_JUMPABLE | ACF_FLATABLE | ACF_HURTS | ACF_SHOOTABLE | ACF_ROLLABLE;
1618 					m->num_enemies ++;
1619 				}
1620 			}
1621 			if (m->dat[x + y * m->width].type == MAP_ENEMY2) { // big human
1622 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1623 				if (a != NULL) {
1624 					a->type = MAP_ENEMY2;
1625 					a->num_frames = 4;
1626 					a->frames[0] = ENEMY2_01;
1627 					a->frames[1] = ENEMY2_02;
1628 					a->frames[2] = ENEMY2_03;
1629 					a->frames[3] = ENEMY2_04;
1630 					a->flat_frame = ENEMY2_05;
1631 					a->w = 12; a->h = 19;
1632 					a->ox = 2; a->oy = 5;
1633 					a->flags = ACF_JUMPABLE | ACF_FLATABLE | ACF_HURTS | ACF_SHOOTABLE | ACF_ROLLABLE;
1634 					m->num_enemies ++;
1635 				}
1636 			}
1637 			if (m->dat[x + y * m->width].type == MAP_ENEMY3) { // crusher
1638 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1639 				if (a != NULL) {
1640 					a->type = MAP_ENEMY3;
1641 					a->num_frames = 1;
1642 					a->frames[0] = ENEMY3;
1643 					a->w = 30; a->h = 16;
1644 					a->ox = 1; a->oy = 112-16;
1645 					a->flags = ACF_HURTS;
1646 				}
1647 			}
1648 			if (m->dat[x + y * m->width].type == MAP_ENEMY4) { // spike fish
1649 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1650 				if (a != NULL) {
1651 					a->type = MAP_ENEMY4;
1652 					a->num_frames = 1;
1653 					a->frames[0] = ENEMY4;
1654 					a->w = 12; a->h = 14;
1655 					a->ox = 2; a->oy = 2;
1656 					a->flags =  ACF_HURTS | ACF_SHOOTABLE | ACF_ROLLABLE;
1657 					m->num_enemies ++;
1658 				}
1659 			}
1660 			if (m->dat[x + y * m->width].type == MAP_ENEMY5) { // yelly fish
1661 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1662 				if (a != NULL) {
1663 					a->type = MAP_ENEMY5;
1664 					a->num_frames = 1;
1665 					a->frames[0] = ENEMY5_01;
1666 					a->w = 12; a->h = 14;
1667 					a->ox = 2; a->oy = 2;
1668 					a->flags = ACF_JUMPABLE | ACF_HURTS | ACF_SHOOTABLE | ACF_ROLLABLE;
1669 					m->num_enemies ++;
1670 				}
1671 			}
1672 			if (m->dat[x + y * m->width].type == MAP_ENEMY6) { // cannon
1673 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1674 				if (a != NULL) {
1675 					a->type = MAP_ENEMY6;
1676 					a->num_frames = 0;
1677 					a->flags = 0;
1678 				}
1679 			}
1680 			if (m->dat[x + y * m->width].type == MAP_GUARD1) { // spike-car
1681 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1682 				if (a != NULL) {
1683 					a->type = MAP_GUARD1;
1684 					a->energy = 100;
1685 					a->num_frames = 4;
1686 					a->hit_offset = 4;
1687 					a->frames[0] = GUARD1_1;
1688 					a->frames[1] = GUARD1_2;
1689 					a->frames[2] = GUARD1_3;
1690 					a->frames[3] = GUARD1_4;
1691 					a->w = 32; a->h = 32;
1692 					a->ox = 0; a->oy = 0;
1693 					a->flags = ACF_HURTS | ACF_ROLLABLE_BACK;
1694 					m->num_enemies ++;
1695 					a->sound = SMPL_ENGINE;
1696 					play_sound_id_ex(a->sound, 100, 1000, 1);
1697 				}
1698 			}
1699 			if (m->dat[x + y * m->width].type == MAP_GUARD2) { // spike-jumper-crusher
1700 				Tactor *a = make_actor(actor, x << 4, (y << 4) + 16, data);
1701 				if (a != NULL) {
1702 					a->type = MAP_GUARD2;
1703 					a->energy = 150;
1704 					a->num_frames = 2;
1705 					a->frames[0] = GUARD2_1a;
1706 					a->frames[1] = GUARD2_1b;
1707 					a->w = 32; a->h = 32;
1708 					a->ox = 0; a->oy = 16;
1709 					a->flags = ACF_HURTS | ACF_JUMPABLE | ACF_BOUNCEABLE;
1710 					m->num_enemies ++;
1711 				}
1712 			}
1713 		}
1714 	}
1715 }
1716 
1717 
1718 // starts a new level
1719 // level_id < 0 -> load fname
1720 // uses datafile map o/w
new_level(char * fname,int level_id,int draw)1721 void new_level(char *fname, int level_id, int draw) {
1722 	int tox;
1723 	int i;
1724 	int x, y;
1725 
1726 	if (map != NULL) destroy_map(map);
1727 	map = NULL;
1728 
1729 	if (level_id < 0) {
1730 		if (level_id == -1) {
1731 			// get map from file
1732 			log2file(" loading map <%s>", fname);
1733 			map = load_map(fname);
1734 			if (!map) {
1735 				msg_box("failed to load map!!!");
1736     			log2file(" *** failed!");
1737 				return;
1738 			}
1739 		}
1740 		else {
1741 			// get map from data file
1742 			log2file(" getting map %d from datafile", atoi(fname));
1743 			map = load_map_from_memory((void *)data[SCRIPTMAP0 + atoi(fname)].dat);
1744 		}
1745 	}
1746 	else {
1747 		// get map from MAP data file
1748 		log2file(" getting map %d from map-datafile", level_id);
1749 		map = load_map_from_memory((void *)maps[level_id].dat);
1750 	}
1751 
1752 
1753 
1754 	map->data = data;
1755 
1756 	init_player(&player, map);
1757 
1758 	if (player.actor->direction) tox = MAX(0, MIN(player.actor->x - 60, map->width * 16 - 160));
1759 	else tox = MAX(0, MIN(player.actor->x - 85, map->width * 16 - 160));
1760 	map->offset_x = tox;
1761 
1762 	init_map(map);
1763 
1764 	reset_particles(particle, MAX_PARTICLES);
1765 	reset_bullets(bullet, MAX_BULLETS);
1766 
1767 	for(i = 1; i < MAX_ACTORS; i++)
1768 		if (actor[i].active)
1769 			update_actor_with_map(&actor[i], map);
1770 
1771 	// calc pickup frequency
1772 	player.cherries = player.cherries_taken = 0;
1773 	player.stars = player.stars_taken = 0;
1774 	for(x = 0; x < map->width; x ++) {
1775 		for(y = 0; y < map->height; y ++) {
1776 			if (map->dat[x + y * map->width].item == MAP_STAR) player.stars ++;
1777 			if (map->dat[x + y * map->width].item == MAP_CHERRY) player.cherries ++;
1778 		}
1779 	}
1780 
1781 	if (draw) {
1782 		draw_frame(swap_screen, 1);
1783 		blit_to_screen(swap_screen);
1784 		fade_in_pal(100);
1785 	}
1786 }
1787 
1788 
1789 
1790 
1791 // updates player movement
update_player()1792 void update_player() {
1793 	int old_x = player.actor->x;
1794 	int x1, x2;
1795 	Tmappos *mp;
1796 	int tx, ty;
1797 
1798 	if (player.actor->direction) {
1799 		x1 = 2;
1800 		x2 = 10;
1801 	}
1802 	else {
1803 		x1 = 13;
1804 		x2 = 6;
1805 	}
1806 
1807 	poll_control(&ctrl);
1808 
1809 	if (player.wounded) player.wounded --;
1810 
1811 	// most stuff can only be done when the player is alive
1812 	if (player.actor->status != AC_DEAD) {
1813 
1814 		// handle eating animation
1815 		if (player.actor->status == AC_EAT) {
1816 			player.eat_counter ++;
1817 			if (player.eat_counter == 8) {
1818 				player.actor->status = AC_FULL;
1819 				player.eat_counter = 0;
1820 			}
1821 		}
1822 
1823 		// handle firing
1824 		if (is_fire(&ctrl) && player.actor->status == AC_FULL && !player.eat_pressed) {
1825 			player.actor->status = AC_SPIT;
1826 			player.eat_pressed = 1;
1827 			if (player.ammo) {
1828 				Tbullet *b = get_free_bullet(bullet, MAX_BULLETS);
1829 				if (b != NULL) {
1830 					set_bullet(b, (int)player.actor->x + 7, (int)player.actor->y - 14, (player.actor->direction ? 4 : -4), 0, data[EGG2].dat, 0);
1831 					player.ammo --;
1832 					play_sound(sfx[SMPL_SPIT]);
1833 				}
1834 			}
1835 
1836 
1837 		}
1838 
1839 		// return to normal
1840 		if (!is_fire(&ctrl)) {
1841 			player.eat_pressed = 0;
1842 			if (player.eat_counter == 0 && player.actor->status != AC_BALL) {
1843 				if (player.ammo) player.actor->status = AC_FULL;
1844 				else player.actor->status = AC_NORM;
1845 			}
1846 		}
1847 
1848 
1849 		// horizontal movement
1850 		if (is_left(&ctrl) && (player.actor->status != AC_BALL)) {
1851 			player.actor->x -= 1;
1852 			if (player.actor->direction) player.actor->x -= 3;
1853 			player.actor->direction = 0;
1854 			animate_actor(&actor[0]);
1855 		}
1856 		else if (is_right(&ctrl) && (player.actor->status != AC_BALL)) {
1857 			player.actor->x += 1;
1858 			if (!player.actor->direction) player.actor->x += 3;
1859 			player.actor->direction = 1;
1860 			animate_actor(&actor[0]);
1861 		}
1862 
1863 		// rolling?
1864 		if (player.actor->status == AC_BALL) {
1865 			if (player.actor->direction) {
1866 				player.angle += 8;
1867 				player.actor->x += 2;
1868 			}
1869 			else {
1870 				player.angle -= 8;
1871 				player.actor->x -= 2;
1872 			}
1873 		}
1874 
1875 		// horizontal tile collision
1876 		if (is_ground(map, player.actor->x + x1, player.actor->y - 1))  { player.actor->x = old_x; if (player.actor->status == AC_BALL) play_sound(sfx[SMPL_HIT]); player.actor->status = (player.actor->status == AC_BALL ? AC_NORM : player.actor->status); };
1877 		if (is_ground(map, player.actor->x + x2, player.actor->y - 1))  { player.actor->x = old_x; if (player.actor->status == AC_BALL) play_sound(sfx[SMPL_HIT]); player.actor->status = (player.actor->status == AC_BALL ? AC_NORM : player.actor->status); };
1878 		if (is_ground(map, player.actor->x + x1, player.actor->y - 15)) { player.actor->x = old_x; if (player.actor->status == AC_BALL) play_sound(sfx[SMPL_HIT]); player.actor->status = (player.actor->status == AC_BALL ? AC_NORM : player.actor->status); };
1879 		if (is_ground(map, player.actor->x + x2, player.actor->y - 15)) { player.actor->x = old_x; if (player.actor->status == AC_BALL) play_sound(sfx[SMPL_HIT]); player.actor->status = (player.actor->status == AC_BALL ? AC_NORM : player.actor->status); };
1880 
1881 
1882 		// vertical movement
1883 		if (!is_ground(map, player.actor->x + x1, player.actor->y)) {
1884 			if (!is_ground(map, player.actor->x + x2, player.actor->y)) {
1885 				player.actor->dy ++;
1886 				player.jumping = 1;
1887 			}
1888 		}
1889 		else {
1890 			player.jumping = 0;
1891 			player.actor->dy = 0;
1892 		}
1893 
1894 		// jumping?
1895 		if (is_jump(&ctrl) && !player.jumping && !player.jump_pressed) {
1896 			player.actor->dy = -16;
1897 			player.jumping = 1;
1898 			player.jump_pressed = 1;
1899 			play_sound(sfx[SMPL_JUMP]);
1900 		}
1901 		if (!is_jump(&ctrl)) {
1902 			if (player.actor->dy < 0) player.actor->dy >>= 1;
1903 			player.jump_pressed = 0;
1904 		}
1905 
1906 		// apply vertical force
1907 		player.actor->y += player.actor->dy >> 2;
1908 
1909 		// check floor
1910 		while (is_ground(map, player.actor->x + x1, player.actor->y - 1)) { player.actor->y --; player.jumping = 0; }
1911 		while (is_ground(map, player.actor->x + x2, player.actor->y - 1)) { player.actor->y --; player.jumping = 0; }
1912 
1913 		// check roof
1914 		while (is_ground(map, player.actor->x + x1, player.actor->y - 15)) { player.actor->y ++; player.actor->dy = 0; }
1915 		while (is_ground(map, player.actor->x + x2, player.actor->y - 15)) { player.actor->y ++; player.actor->dy = 0; }
1916 
1917 		// check for map types
1918 		////// PICKUPS / EATS
1919 		tx = (player.actor->x + (player.actor->direction ? 10 : 6)) >> 4;
1920 		ty = (player.actor->y - 10) >> 4;
1921 		mp = get_mappos(map, tx, ty);
1922 
1923 		if (mp != NULL) {
1924 			if (mp->item == MAP_EGG) {
1925 				mp->item = 0;
1926 				if (player.actor->status != AC_BALL) player.actor->status = AC_EAT;
1927 				player.eat_counter = 0;
1928 				player.ammo ++;
1929 				play_sound(sfx[SMPL_EAT]);
1930 			}
1931 			if (mp->item == MAP_HEART) {
1932 				mp->item = 0;
1933 				if (player.actor->status != AC_BALL) player.actor->status = AC_EAT;
1934 				player.eat_counter = 0;
1935 				if (player.health >= 2) {
1936 					player.score += 100;
1937 				}
1938 				else {
1939 					player.health ++;
1940 				}
1941 				play_sound(sfx[SMPL_HEART]);
1942 			}
1943 			if (mp->item == MAP_STAR) {
1944 				mp->item = 0;
1945 				if (player.actor->status != AC_BALL) player.actor->status = AC_EAT;
1946 				player.eat_counter = 0;
1947 				player.score += 100;
1948 				player.stars_taken ++;
1949 				play_sound(sfx[SMPL_STAR]);
1950 
1951 				if (player.stars == player.stars_taken) {
1952 					Tparticle *p;
1953 					p = get_free_particle(particle, MAX_PARTICLES);
1954 					if (p != NULL) {
1955 						set_particle(p, (tx << 4) + 4, ty << 4, 0, -0.5, 0, 50, ALL100);
1956 					}
1957 				}
1958 			}
1959 			if (mp->item == MAP_CHERRY) {
1960 				mp->item = 0;
1961 				if (player.actor->status != AC_BALL) player.actor->status = AC_EAT;
1962 				player.eat_counter = 0;
1963 				player.score += 10;
1964 				player.cherries_taken ++;
1965 				play_sound(sfx[SMPL_CHERRY]);
1966 
1967 				if (player.cherries == player.cherries_taken) {
1968 					Tparticle *p;
1969 					p = get_free_particle(particle, MAX_PARTICLES);
1970 					if (p != NULL) {
1971 						set_particle(p, (tx << 4) + 8, ty << 4, 0, -0.5, 0, 50, ALL100);
1972 					}
1973 				}
1974 			}
1975 			else if (mp->item == MAP_1UP) {
1976 				create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 4, 32, 0, -1);
1977 				mp->item = 0;
1978 				if (player.actor->status != AC_BALL) player.actor->status = AC_EAT;
1979 				player.eat_counter = 0;
1980 				player.lives ++;
1981 				play_sound(sfx[SMPL_XTRALIFE]);
1982 			}
1983 		}
1984 
1985 
1986 		////// MIDDLE STUFF (door, spikes)
1987 		tx = (player.actor->x + 7) >> 4;
1988 		ty = (player.actor->y - 7) >> 4;
1989 		mp = get_mappos(map, tx, ty);
1990 
1991 		// turn off exit flag
1992 		map->win_conditions_fullfilled &= ~MAP_WIN_EXIT;
1993 		// check tile for death or door
1994 		if (mp != NULL) {
1995 			if (mp->type == MAP_DEAD) {
1996 				kill_player(&player);
1997 				play_sound(sfx[SMPL_A_DIE]);
1998 			}
1999 			if (mp->type == MAP_EXIT) {
2000 				map->win_conditions_fullfilled |=  map->win_conditions & MAP_WIN_EXIT;
2001 			}
2002 		}
2003 
2004 
2005 		////// BREAKABLES
2006 		tx = (player.actor->x + (player.actor->direction ? 15 : 0)) >> 4;
2007 		ty = (player.actor->y - 7) >> 4;
2008 		mp = get_mappos(map, tx, ty);
2009 
2010 		if (mp != NULL) {
2011 			if (mp->type == MAP_BRK && player.actor->status == AC_BALL) {
2012 				mp->tile = mp->type = mp->mask = 0;
2013 				create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 16, 64, 0, -1);
2014 				create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 16, 64, 0, -1);
2015 				play_sound(sfx[SMPL_CRUSH]);
2016 			}
2017 		}
2018 
2019 
2020 		////// FLOORS
2021 		tx = (player.actor->x + 7) >> 4;
2022 		ty = (player.actor->y + 4) >> 4;
2023 		mp = get_mappos(map, tx, ty);
2024 
2025 		if (mp != NULL) {
2026 			if (mp->type == MAP_SPIN && player.actor->status != AC_BALL) {
2027 				Tmappos *m = get_mappos(map, tx + (player.actor->direction ? 1 : -1), ty - 1);
2028 				if (m != NULL) {
2029 					if (!m->mask) {
2030 						player.actor->status = AC_BALL;
2031 						play_sound(sfx[SMPL_SPIN]);
2032 					}
2033 				}
2034 			}
2035 		}
2036 
2037 	}
2038 	else {	// player is dead
2039 		player.actor->y += player.actor->dy >> 2;
2040 		player.actor->dy ++;
2041 		player.actor->x += (player.actor->direction ? 1 : -1);
2042 	}
2043 }
2044 
2045 
2046 
2047 // checks and acts upone collisions between
2048 // bullets and enemies
check_bullets_with_enemies()2049 void check_bullets_with_enemies() {
2050 	int b, e;
2051 
2052 	for(b = 0; b < MAX_BULLETS; b ++) {
2053 		if (bullet[b].exist && bullet[b].bad == 0) {
2054 			for(e = 1; e < MAX_ACTORS; e ++) {
2055 				if (actor[e].active && actor[e].status == AC_NORM && (actor[e].flags & ACF_SHOOTABLE)) {
2056 					if (check_bb_collision(bullet[b].x, bullet[b].y, 7, 6, actor[e].x, actor[e].y-actor[e].h, 16, actor[e].h)) {
2057 						kill_actor(&actor[e]);
2058 						map->num_enemies --;
2059 						actor[e].direction = (bullet[b].dx > 0 ? 0 : 1);
2060 						bullet[b].exist = 0;
2061 						create_burst(particle, (int)bullet[b].x+4, (int)bullet[b].y+3, 4, 8, 0, -1);
2062 						play_sound(sfx[SMPL_E_DIE]);
2063 					}
2064 				}
2065 			}
2066 		}
2067 	}
2068 }
2069 
2070 
2071 // checks and acts upon collisions between
2072 // bullets and alex
check_bullets_with_alex()2073 void check_bullets_with_alex() {
2074 	int b;
2075 
2076 	for(b = 0; b < MAX_BULLETS; b ++) {
2077 		if (bullet[b].exist && bullet[b].bad) {
2078 			if (actor[0].status != AC_DEAD) {
2079 				if (check_bb_collision(bullet[b].x+1, bullet[b].y+1, bullet[b].bmp->w-1, bullet[b].bmp->h-1, actor[0].x+4, actor[0].y-16, 8, 16)) {
2080 					if (!player.wounded) {
2081 						wound_player(&player);
2082 						if (player.actor->status == AC_DEAD) play_sound(sfx[SMPL_A_DIE]);
2083 						else play_sound(sfx[SMPL_HURT]);
2084 					}
2085 					//actor[0].direction = (bullet[b].dx > 0 ? 1 : 0);
2086 					bullet[b].exist = 0;
2087 					create_burst(particle, (int)bullet[b].x+4, (int)bullet[b].y+3, 4, 16, 0, -1);
2088 				}
2089 			}
2090 		}
2091 	}
2092 }
2093 
2094 
2095 
2096 // checks and acts upon collisions between
2097 // enemies and alex
check_alex_with_enemies()2098 void check_alex_with_enemies() {
2099 	int e;
2100 
2101 	if (actor[0].status != AC_DEAD) {
2102 		for(e = 1; e < MAX_ACTORS; e ++) {
2103 			actor[e].is_hit = 0;
2104 			if (actor[e].active && actor[0].status != AC_DEAD && actor[e].flags & ACF_HURTS) {
2105 				// first check if alex jumped on the enemy
2106 				if (actor[0].dy >= 0 && actor[e].status == AC_NORM && player.jumping && actor[0].status != AC_BALL && actor[e].flags & ACF_JUMPABLE) {
2107 					if (check_bb_collision(actor[0].x+2, actor[0].y+12-16, 12, 8, actor[e].x, actor[e].y-actor[e].h, actor[e].w, 4)) {
2108 						Tparticle *p;
2109 						p = get_free_particle(particle, MAX_PARTICLES);
2110 						if (p != NULL) {
2111 							set_particle(p, actor[e].x + (actor[e].w >> 1), actor[0].y, 0, 0, 0, 5, PARTICLE_BOPP);
2112 						}
2113 
2114 						if (actor[e].flags & ACF_FLATABLE) {
2115 							// flatten
2116 							actor[e].status = AC_FLAT;
2117 							actor[e].counter = 0;
2118 						}
2119 						else if (!(actor[e].flags & ACF_BOUNCEABLE)) {
2120 							// kill
2121 							kill_actor(&actor[e]);
2122 							map->num_enemies --;
2123 							actor[e].direction = (actor[0].direction ? 0 : 1);
2124 						}
2125 
2126 						if (actor[e].flags & ACF_BOUNCEABLE) {
2127 							// bounce
2128 							player.actor->y = actor[e].y - actor[e].h - 6;
2129 							player.dy = -15;	// jump!
2130 							player.jumping = TRUE;
2131 							play_sound(sfx[SMPL_JUMP]);
2132 							if (actor[e].mode == 0) {
2133 								actor[e].mode = 4;
2134 								actor[e].counter = 0;
2135 							}
2136 						}
2137 						else {
2138 							player.dy = -5;
2139 							//play_sound((actor[e].type == MAP_ENEMY5 ? sfx[SMPL_E_DIE] : sfx[SMPL_MASH]));
2140 							play_sound(sfx[SMPL_MASH]);
2141 							if (is_jump(&ctrl) ) {
2142 								player.dy = -20;	// jump!
2143 								player.jumping = TRUE;
2144 								play_sound(sfx[SMPL_JUMP]);
2145 							}
2146 						}
2147 					}
2148 				}
2149 				else {
2150 					// now check if alex dies or knocks them over
2151 					if (actor[e].status == AC_NORM || (actor[e].status == AC_FLAT && actor[0].status == AC_BALL)) {
2152 						if (check_bb_collision(actor[0].x + (actor[0].direction ? 0 : 8), actor[0].y-16, 8, 16, actor[e].x + actor[e].ox, actor[e].y - actor[e].h, actor[e].w, actor[e].h)) {
2153 							int wounded = 0;
2154 							if (actor[0].status == AC_BALL) {
2155 								if (actor[e].flags & ACF_ROLLABLE) {
2156 									kill_actor(&actor[e]);
2157 									map->num_enemies --;
2158 									actor[e].direction = (actor[0].direction ? 0 : 1);
2159 									play_sound(sfx[SMPL_E_DIE]);
2160 								}
2161 								else if (actor[e].flags & ACF_ROLLABLE_BACK) {
2162 									if (actor[e].direction == actor[0].direction) {
2163 										actor[e].energy --;
2164 										actor[e].is_hit = 1;
2165 										play_sound(sfx[SMPL_HIT]);
2166 									}
2167 									else wounded = 1;
2168 								}
2169 								else wounded = 1;
2170 							}
2171 							else wounded = 1;
2172 
2173 							if (!player.wounded && wounded) {
2174 								clear(swap_screen);
2175 								wound_player(&player);
2176 								if (player.actor->status == AC_DEAD) {
2177 									actor[0].direction = actor[e].direction;
2178 									play_sound(sfx[SMPL_A_DIE]);
2179 								}
2180 								else {
2181 									play_sound(sfx[SMPL_HURT]);
2182 								}
2183 							}
2184 						}
2185 					}
2186 					// maybe he can eat them?
2187 					else if (actor[e].status == AC_FLAT && actor[0].status != AC_BALL) {
2188 						if (check_bb_collision(actor[0].x + (actor[0].direction ? 16 : -8), actor[0].y, 8, 16, actor[e].x + 2, actor[e].y + 8, 12, 8)) {
2189 							actor[e].active = 0;
2190 							map->num_enemies --;
2191 							actor[0].status = AC_EAT;
2192 							player.score += 50;
2193 							player.eat_counter = 0;
2194 							play_sound(sfx[SMPL_EAT]);
2195 						}
2196 					}
2197 				}
2198 			}
2199 		}
2200 	}
2201 }
2202 
2203 
2204 // calculates camera pos for map m considering player p
calculate_camera_pos(Tplayer * p,Tmap * m)2205 void calculate_camera_pos(Tplayer *p, Tmap *m) {
2206 	static int camera_type = 1;
2207 
2208 	if (p->actor->status == AC_BALL) {
2209 		camera_type = 2;
2210 	}
2211 	else {
2212 		camera_type = 1;
2213 	}
2214 
2215 
2216 	if (camera_type == 0) { // centered camera, doesn't happen a lot
2217 		static int target_x;
2218 		target_x = MAX(0, MIN(p->actor->x + 8 - 80, m->width * 16 - 160));
2219 		if (m->offset_x < target_x) m->offset_x ++;
2220 		if (m->offset_x > target_x) m->offset_x --;
2221 	}
2222 	else if (camera_type == 1) { // walk camera
2223 		int x = p->actor->x - m->offset_x;
2224 		int pan = 1;
2225 		int tox, d_tox, step;
2226 		static int side = 1;
2227 		static int d_step = 0;
2228 		static int count = 0;
2229 
2230 		if (side) {
2231 			if (x < 60 && x > 40 ) pan = 0;
2232 		}
2233 		else {
2234 			if (x < 105 && x > 85 ) pan = 0;
2235 		}
2236 
2237 		if (pan) {
2238 			side = p->actor->direction;
2239 			count = 0;
2240 		}
2241 		else {
2242 			d_step = 0;
2243 			if (++ count > 50) pan = 1; // pan anyway
2244 		}
2245 
2246 
2247 
2248 		if (pan) {
2249 			if (p->actor->direction) tox = MAX(0, MIN(p->actor->x - 60, m->width * 16 - 160));
2250 			else tox = MAX(0, MIN(p->actor->x - 85, m->width * 16 - 160));
2251 
2252 			d_tox = tox - m->offset_x;
2253 			if (ABS(d_tox) > 1) step = 2;
2254 			else if (ABS(d_tox) > 0) step = 1;
2255 			else step = 0;
2256 			if (p->actor->status != AC_BALL) step = MIN(step, 3);
2257 			step *= SGN(d_tox);
2258 
2259 			if (d_step > step) d_step --;
2260 			if (d_step < step) d_step ++;
2261 
2262 			m->offset_x = MAX(0, MIN(m->offset_x + d_step, m->width * 16 - 160));
2263 		}
2264 	}
2265 	else if (camera_type == 2) { // roll camera
2266 		int x = p->actor->x - m->offset_x;
2267 		int pan = 1;
2268 		int tox, d_tox, step;
2269 		static int side = 1;
2270 		static int d_step = 0;
2271 		static int count = 0;
2272 
2273 		if (side) {
2274 			if (x < 34 && x > 8 ) pan = 0;
2275 		}
2276 		else {
2277 			if (x < 136 && x > 111 ) pan = 0;
2278 		}
2279 
2280 		if (pan) {
2281 			side = p->actor->direction;
2282 			count = 0;
2283 		}
2284 		else {
2285 			d_step = 0;
2286 			if (++ count > 50) pan = 1; // pan anyway
2287 		}
2288 
2289 
2290 
2291 		if (pan) {
2292 			if (p->actor->direction) tox = MAX(0, MIN(p->actor->x - 34, m->width * 16 - 160));
2293 			else tox = MAX(0, MIN(p->actor->x - 112, m->width * 16 - 160));
2294 
2295 			d_tox = tox - m->offset_x;
2296 			if (ABS(d_tox) > 70) step = 8;
2297 			else if (ABS(d_tox) > 60) step = 7;
2298 			else if (ABS(d_tox) > 50) step = 6;
2299 			else if (ABS(d_tox) > 40) step = 5;
2300 			else if (ABS(d_tox) > 30) step = 4;
2301 			else if (ABS(d_tox) > 20) step = 3;
2302 			else if (ABS(d_tox) > 1) step = 2;
2303 			else if (ABS(d_tox) > 0) step = 1;
2304 			else step = 0;
2305 			if (p->actor->status != AC_BALL) step = MIN(step, 3);
2306 			step *= SGN(d_tox);
2307 
2308 			if (d_step > step) d_step --;
2309 			if (d_step < step) d_step ++;
2310 
2311 			m->offset_x = MAX(0, MIN(m->offset_x + d_step, m->width * 16 - 160));
2312 		}
2313 	}
2314 }
2315 
2316 
2317 // shows the pause menu
do_pause_menu(BITMAP * bg)2318 int do_pause_menu(BITMAP *bg) {
2319 	int done = 0;
2320 
2321 	// pause sound
2322 	if (got_sound && !editing) al_pause_duh(dp);
2323 	play_sound_id(SMPL_PAUSE);
2324 
2325 	// darken screen
2326 	transform_bitmap(bg, -1);
2327 	blit_to_screen(bg);
2328 
2329 	// show text
2330 	textout_outline_center(bg, (editing ? "<<< EDITOR PAUSED >>>" : "<<< GAME PAUSED >>>"), 80, 40);
2331 	textout_outline_center(bg, "esc to quit", 80, 60);
2332 	textout_outline_center(bg, "any key to resume", 80, 70);
2333 	blit_to_screen(bg);
2334 
2335 	// wait to release esc
2336 	while(key[KEY_ESC]);
2337 
2338 	// wait for user input
2339 	clear_keybuf();
2340 	while(!done) {
2341 		if (got_sound) al_poll_duh(dp);
2342 		poll_control(&ctrl);
2343 		if (is_fire(&ctrl) || is_jump(&ctrl)) done = 1;
2344 		if (keypressed()) done = 1;
2345 		if (key[KEY_ESC]) done = -1;
2346 		yield_timeslice();
2347 	}
2348 
2349 	if (done == -1) {
2350 		game_status = GS_QUIT_GAME;
2351 		play_sound_id(SMPL_MENU);
2352 	}
2353 	else {
2354 		play_sound_id(SMPL_PAUSE);
2355 	}
2356 
2357 	if (got_sound && !editing) al_resume_duh(dp);
2358 
2359 	return done;
2360 }
2361 
2362 
2363 // play the game!
play(int level)2364 int play(int level) {
2365 	int i;
2366 	int playing_go_music = 0;
2367 
2368 
2369 	game_status = GS_OK;
2370 	cycle_count = 0;
2371 	while(game_status == GS_OK) {
2372 
2373 		//  do logic
2374 		while(cycle_count > 0) {
2375 			logic_count ++;
2376 
2377 			// poll music machine
2378 			if (got_sound) {
2379 				al_poll_duh(dp);
2380 			}
2381 
2382 			// check if user wants to enter edit mode
2383 			if (!playing_original_game && !editing) {
2384 				if (key[KEY_F1]) {		// toggle edit on/off
2385 					editing = 1;
2386 					if (got_sound) stop_music();
2387 					set_edit_mode(EDIT_MODE_DRAW);
2388 					log2file("Entering EDIT MODE");
2389 				}
2390 			}
2391 
2392 			// check player moves
2393 			if (!editing) {
2394 				update_player();
2395 
2396 				// dead?
2397 				if (player.actor->status == AC_DEAD && !playing_go_music) {
2398 					if (got_sound) start_music((player.lives ? MOD_PLAYER_DIES : MOD_GAME_OVER));
2399 					playing_go_music = 1;
2400 				}
2401 
2402 				if (player.actor->y > 160) {
2403 					if (player.actor->status != AC_DEAD) {
2404 						kill_player(&player);
2405 						player.actor->y = 150;
2406 						player.actor->dy = -20;
2407 						play_sound(sfx[SMPL_A_DIE]);
2408 					}
2409 					else
2410 						game_status = GS_GAME_DIED;
2411 
2412 				}
2413 
2414 				// camera
2415 				calculate_camera_pos(&player, map);
2416 
2417 				// actors
2418 				for(i = 1; i < MAX_ACTORS; i ++) {
2419 					if (actor[i].active) {
2420 						// always update guard actors
2421 						if (actor[i].type == MAP_GUARD1 || actor[i].type == MAP_GUARD2) {
2422 							update_actor_with_map(&actor[i], map);
2423 						}
2424 						// update other actors only if on screen
2425 						else if (actor[i].x > map->offset_x - 32 && actor[i].x < map->offset_x + 160 + 32)
2426 							update_actor_with_map(&actor[i], map);
2427 					}
2428 				}
2429 				if (player.dy) {
2430 					player.actor->dy = player.dy;
2431 					player.dy = 0;
2432 				}
2433 
2434 				// particles
2435 				for(i = 0; i < MAX_PARTICLES; i ++) {
2436 					if (particle[i].life) {
2437 						//update_particle(&particle[i]);
2438 						update_particle_with_map(&particle[i], map);
2439 					}
2440 				}
2441 
2442 				// bullets
2443 				for(i = 0; i < MAX_BULLETS; i ++) {
2444 					if (bullet[i].exist) {
2445 						update_bullet_with_map(&bullet[i], map);
2446 					}
2447 				}
2448 
2449 				// collision checking
2450 				// bullets vs enemies!
2451 				check_bullets_with_enemies();
2452 				// alex vs enemies!
2453 				check_alex_with_enemies();
2454 				// alex vs enemy bullets!
2455 				check_bullets_with_alex();
2456 
2457 
2458 				// update and check win conditions
2459 				if (map->num_enemies == 0) map->win_conditions_fullfilled |= map->win_conditions & MAP_WIN_KILL_ALL;
2460 				if (map->win_conditions_fullfilled == map->win_conditions)
2461 					game_status = GS_LEVEL_DONE;
2462 
2463 
2464 			}
2465 			else { 			// edit stuff
2466 				int mx = mouse_x / (SCREEN_W / 160);
2467 				int my = mouse_y / (SCREEN_H / 120);
2468 
2469 				player.actor->x = map->start_x * 16;
2470 				player.actor->y = map->start_y * 16 + 16;
2471 				if (player.actor->x < 0) {
2472 					player.actor->x = -map->start_x * 16;
2473 					player.actor->direction = 0;
2474 				}
2475 				else {
2476 					player.actor->direction = 1;
2477 				}
2478 				update_edit_mode(map, swap_screen, mx, my, mouse_b);
2479 			}
2480 
2481 			// COMMON STUFF
2482 			if (key[KEY_ESC]) {
2483 				do_pause_menu(swap_screen);
2484 				cycle_count = 0;
2485 			}
2486 			if (key[KEY_F12]) {
2487 				take_screenshot(swap_screen);
2488 				while(key[KEY_F12])	if (got_sound) al_poll_duh(dp);
2489 				cycle_count = 0;
2490 			}
2491 
2492 			cycle_count --;
2493 		}
2494 
2495 		// let other processes play
2496 		yield_timeslice();
2497 
2498 		// draw
2499 		frame_count ++;
2500 		draw_frame(swap_screen, 1);
2501 		blit_to_screen(swap_screen);
2502 	}
2503 
2504 	return game_status;
2505 }
2506 
2507 
2508 // draws the title
draw_title(BITMAP * bmp,int tick)2509 void draw_title(BITMAP *bmp, int tick) {
2510 	int w, h;
2511 	char start_string[128] = "START GAME";
2512 	int y, x, step = 12;
2513 
2514 	if (!playing_original_game) strcpy(start_string, "CUSTOM GAME");
2515 
2516 	blit(data[ALEX_BG].dat, bmp, 0, 0, 0, 0, 160, 112);
2517 	rectfill(bmp, 0, 112, 160, 120, 2);
2518 
2519 	w = ((BITMAP *)data[ALEX_LOGO].dat)->w;
2520 	h = ((BITMAP *)data[ALEX_LOGO].dat)->h;
2521 	draw_sprite(bmp, data[ALEX_LOGO].dat, 80 - w/2, 30 - h/2);
2522 
2523 	draw_scroller(&hscroll, bmp, 0, 110);
2524 
2525 	y = 60;
2526 	x = 50;
2527 	textout(bmp, data[THE_FONT].dat, start_string, x+1, y+1, 1);
2528 	textout(bmp, data[THE_FONT].dat, start_string, x, y, 4);
2529 
2530 	y += step;
2531 	textout(bmp, data[THE_FONT].dat, "HIGH SCORES", x+1, y+1, 1);
2532 	textout(bmp, data[THE_FONT].dat, "HIGH SCORES", x, y, 4);
2533 
2534 	y += step;
2535 	textout(bmp, data[THE_FONT].dat, "EDITOR", x+1, y+1, 1);
2536 	textout(bmp, data[THE_FONT].dat, "EDITOR", x, y, 4);
2537 
2538 	y += step;
2539 	textout(bmp, data[THE_FONT].dat, "QUIT", x+1, y+1, 1);
2540 	textout(bmp, data[THE_FONT].dat, "QUIT", x, y, 4);
2541 
2542 	draw_sprite(bmp, data[POINTER].dat, x - 25 + fixtoi(3 * fixcos(itofix(tick << 2))), 44 + menu_choice * step);
2543 }
2544 
2545 
2546 // switches gfx mode
switch_gfx_mode(int mode,int w,int h)2547 void switch_gfx_mode(int mode, int w, int h) {
2548 	log2file(" switching to %d x %d (%s)", w, h, (mode == GFX_AUTODETECT_WINDOWED ? "w" : "f"));
2549 	if (set_gfx_mode(mode, w, h, 0, 0)) {
2550 		log2file("  *** failed");
2551 		log2file("  trying default mode (640x480 win)");
2552 		if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0)) {
2553 			log2file("  *** failed");
2554 			log2file("  trying default mode (640x480 full)");
2555 			if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0)) {
2556 				log2file("  *** failed");
2557 				log2file("  exiting program...");
2558 				uninit_game();
2559 				exit(0);
2560 				log2file("\nDone...\n");
2561 			}
2562 		}
2563 	}
2564 
2565 	set_palette(data[0].dat);
2566 }
2567 
2568 
2569 
2570 
2571 // gets a string from the user
2572 // used for getting highscore names
get_string(BITMAP * bmp,char * string,int max_size,FONT * f,int pos_x,int pos_y,int colour,Tcontrol * pad)2573 int get_string(BITMAP *bmp, char *string, int max_size, FONT *f, int pos_x, int pos_y, int colour, Tcontrol *pad) {
2574     int i = 0, c;
2575     BITMAP *block = create_bitmap(text_length(f, "w")*max_size + 2, text_height(f) + 2);
2576 	char letters[] = "_abcdefghijklmnopqrstuvwxyz {}";
2577 	int current_letter = 0;
2578 	int max_letter = strlen(letters) - 1;
2579 	int print_delay = 0;
2580 
2581     if (block == NULL)
2582         return 1;
2583 
2584 	blit(bmp, block, pos_x - 1, pos_y - 1, 0, 0, block->w, block->h);
2585 
2586     clear_keybuf();
2587     while(1) {
2588 		cycle_count = 0;
2589         string[i] = letters[current_letter];
2590 		string[i + 1] = '\0';
2591         blit(block, bmp, 0, 0, pos_x - 1, pos_y - 1, block->w, block->h);
2592         textout(bmp, f, string, pos_x, pos_y, colour);
2593 		blit_to_screen(bmp);
2594 
2595 		if (pad != NULL) {
2596 			poll_control(pad);
2597 			if (print_delay > 0) print_delay --;
2598 			if (!is_any(pad)) print_delay = 0;
2599 		}
2600 		// get input
2601 		if (keypressed()) {
2602 			c = readkey();
2603 			switch((c >> 8)) {
2604 	            case KEY_BACKSPACE :
2605 					i--;
2606 					i = (i < 0)?0 :i;
2607 					break;
2608 
2609 				case KEY_ENTER :
2610 					string[i] = '\0';
2611 					destroy_bitmap(block);
2612 					return 0;
2613 					break;
2614 
2615 				default :
2616 	                if (i < max_size - 2 && isprint(c & 0xff)) {
2617 						string[i] = c & 0xff;
2618 						i++;
2619 						current_letter = 0;
2620 					}
2621 					break;
2622 			}
2623 		}
2624 		else if (pad != NULL && !print_delay) {
2625 			// check pad
2626 			if (is_up(pad) || is_right(pad)) {
2627 				current_letter ++;
2628 				if (current_letter > max_letter) current_letter = 0;
2629 				print_delay = 8;
2630 			}
2631 			if (is_down(pad) || is_left(pad)) {
2632 				current_letter --;
2633 				if (current_letter < 0) current_letter = max_letter;
2634 				print_delay = 8;
2635 			}
2636 
2637 			if (is_jump(pad) || is_fire(pad)) {
2638 				print_delay = 50;
2639 				switch(letters[current_letter]) {
2640 					case '{':
2641 						i--;
2642 						i = (i < 0)?0 :i;
2643 						break;
2644 					case '}':
2645 						string[i] = '\0';
2646 						destroy_bitmap(block);
2647 						return 0;
2648 						break;
2649 
2650 					default :
2651 			            if (i < max_size - 2) {
2652 							string[i] = letters[current_letter];
2653 							i++;
2654 						}
2655 						break;
2656 				}
2657 			}
2658 
2659 		}
2660 
2661 		while(!cycle_count);
2662 
2663     }
2664 }
2665 
2666 
2667 // lets the player enter a name for highscore use (or what ever)
get_player_name(char * name)2668 void get_player_name(char *name) {
2669 	blit(data[INTRO_BG].dat, swap_screen, 0, 0, 0, 0, 160, 120);
2670 	transform_bitmap(swap_screen, -1);
2671 	textout_outline_center(swap_screen, "Congratulations,", 80, 8);
2672 	textout_outline_center(swap_screen, "You've got", 80, 19);
2673 	textout_outline_center(swap_screen, "a high score!", 80, 30);
2674 	textout_outline_center(swap_screen, "Enter your name:", 80, 55);
2675 	blit_to_screen(swap_screen);
2676 	fade_in_pal(100);
2677 	get_string(swap_screen, name, 10, data[THE_FONT].dat, 50, 80, 4, &ctrl);
2678 }
2679 
2680 
2681 // title and menu
do_main_menu()2682 int do_main_menu() {
2683 	int status = GS_OK;
2684 	int count = 0;
2685 	int tick = 0;
2686 
2687 	log2file("\nRunning main menu:");
2688 
2689 	draw_title(swap_screen, tick);
2690 	blit_to_screen(swap_screen);
2691 	fade_in_pal(100);
2692 
2693 	clear_keybuf();
2694 	cycle_count = 0;
2695 	while(status == GS_OK) {
2696 
2697 		//  do logic
2698 		while(cycle_count > 0) {
2699 			logic_count ++;
2700 			tick ++;
2701 
2702 			// poll music
2703 			if (got_sound) al_poll_duh(dp);
2704 
2705 			scroll_scroller(&hscroll, -1);
2706 			if (!scroller_is_visible(&hscroll)) restart_scroller(&hscroll);
2707 
2708 			poll_control(&ctrl);
2709 			if (count) count --;
2710 
2711 			// is it ok to check for actions?
2712 			if (!count) {
2713 				// if the user has pressed an action button,
2714 				// handle the different alternatives
2715 				if (key[KEY_SPACE] || key[KEY_ENTER] || is_fire(&ctrl) || is_jump(&ctrl)) {
2716 					if (menu_choice == 1) {
2717 						log2file(" play selected");
2718 						status = GS_PLAY;
2719 						play_sound(sfx[SMPL_MENU]);
2720 					}
2721 					if (menu_choice == 2) {
2722 						log2file(" scores selected");
2723 						status = GS_SCORES;
2724 						play_sound(sfx[SMPL_MENU]);
2725 					}
2726 					if (menu_choice == 3) {
2727 						log2file(" edit selected");
2728 						status = GS_EDIT;
2729 						play_sound(sfx[SMPL_MENU]);
2730 					}
2731 					if (menu_choice == 4) {
2732 						log2file(" quit selected");
2733 						status = GS_QUIT_MENU;
2734 						play_sound(sfx[SMPL_MENU]);
2735 					}
2736 					count = 10;
2737 				}
2738 
2739 				// esc is always a shortcut to quit
2740 				if (key[KEY_ESC]) {
2741 					if (menu_choice == 4) {
2742 						log2file(" quit selected");
2743 						status = GS_QUIT_MENU;
2744 					}
2745 					else menu_choice = 4;
2746 					play_sound(sfx[SMPL_MENU]);
2747 					count = 10;
2748 				}
2749 
2750 				// movements in the menu
2751 				if (key[KEY_UP] || is_up(&ctrl)) {
2752 					menu_choice --;
2753 					if (menu_choice < 1) menu_choice = 4;
2754 					play_sound(sfx[SMPL_MENU]);
2755 					count = 10;
2756 				}
2757 				if (key[KEY_DOWN] || is_down(&ctrl)) {
2758 					menu_choice ++;
2759 					if (menu_choice > 4) menu_choice = 1;
2760 					play_sound(sfx[SMPL_MENU]);
2761 					count = 10;
2762 				}
2763 			}
2764 
2765 			// releasing all these keys, and you cen press them again
2766 			if (!is_any(&ctrl) && !key[KEY_UP] && !key[KEY_ESC] && !key[KEY_DOWN] && !key[KEY_SPACE] && !key[KEY_ENTER]) count = 0;
2767 
2768 			// shortcuts to gfx modes
2769 			if (key[KEY_1]) { while(key[KEY_1]); switch_gfx_mode(GFX_AUTODETECT_WINDOWED, 160, 120); }
2770 			if (key[KEY_2]) { while(key[KEY_2]); switch_gfx_mode(GFX_AUTODETECT_WINDOWED, 320, 240); }
2771 			if (key[KEY_3]) { while(key[KEY_3]); switch_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480); }
2772 			if (key[KEY_4]) { while(key[KEY_4]); switch_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480); }
2773 
2774 			cycle_count --;
2775 		}
2776 
2777 		// let other processes play
2778 		yield_timeslice();
2779 
2780 		// draw
2781 		frame_count ++;
2782 		draw_title(swap_screen, tick);
2783 		blit_to_screen(swap_screen);
2784 	}
2785 
2786 	// user selected EDIT
2787 	if (status == GS_EDIT) {
2788 		stop_music();
2789 		fix_gui_colors();
2790 		log2file("\nEntering editor:");
2791 		fade_out_pal(100);
2792 
2793 		// create an empty map for the user
2794 		log2file(" creating empty map");
2795 		if (map != NULL) destroy_map(map);
2796 		map = create_map(50, 7);
2797 
2798 		// if all went well, start editing
2799 		if (map != NULL) {
2800 			map->data = data;
2801 
2802 			new_game(1);
2803 			init_player(&player, map);
2804 			init_map(map);
2805 
2806 			reset_particles(particle, MAX_PARTICLES);
2807 			reset_bullets(bullet, MAX_BULLETS);
2808 
2809 			editing = TRUE;
2810 			set_edit_mode(EDIT_MODE_DRAW);
2811 			set_edit_path_and_file("new.map");
2812 
2813 			draw_frame(swap_screen, 1);
2814 			blit_to_screen(swap_screen);
2815 			fade_in_pal(100);
2816 			status = play(-1);
2817 			deinit_map(map);
2818 		}
2819 		else {
2820 			log2file(" *** failed");
2821 		}
2822 		if (got_sound) start_music(MOD_MENU_SONG);
2823 	}
2824 	else if (status == GS_PLAY) {  //// user selected PLAY
2825 		int level = 0;
2826 
2827 		log2file("\nStarting new game:");
2828 		fade_out_pal(100);
2829 
2830 		game_status = GS_OK;
2831 		editing = FALSE;
2832 		new_game(1);
2833 
2834 		// select starting level
2835 		if (options.max_levels && playing_original_game) {
2836 			level = select_starting_level() - 1;
2837 			fade_out_pal(100);
2838 
2839 			if (level == -2) {  // esc
2840 				log2file(" cancelled");
2841 				game_status = status = GS_QUIT_GAME;
2842 			}
2843 
2844 			if (level == -101) {  // shooter
2845 				log2file(" shooter selected");
2846 				stop_music();
2847 				start_shooter(&ctrl, got_sound);
2848 				game_status = status = GS_QUIT_GAME;
2849 				start_music(MOD_MENU_SONG);
2850 			}
2851 
2852 		}
2853 
2854 		// start playing
2855 		while(game_status == GS_OK || game_status == GS_LEVEL_DONE) {
2856 			// select level
2857 			if (playing_original_game) {
2858 				log2file(" starting level <%d>", level);
2859 				new_level("datafile", level, 1);
2860 			}
2861 			else {
2862 				log2file(" starting level <%s>", level_files[level]);
2863 				set_edit_path_and_file(level_files[level]);
2864 				new_level(level_files[level], -1, 1);
2865 			}
2866 
2867 			if (got_sound) {
2868 				if (map->boss_level) start_music(MOD_BOSS_SONG);
2869 				else start_music(MOD_LEVEL_SONG);
2870 			}
2871 
2872 			// actual game starts here
2873 			show_lets_go();
2874 			status = play(level);
2875 			// done playing level
2876 
2877 			deinit_map(map);
2878 
2879 			// act on different outcomes
2880 			if (status == GS_GAME_DIED) {
2881 				log2file(" player died");
2882 				if (player.lives == 0) {
2883 					game_status = GS_GAMEOVER;
2884 				}
2885 				else {
2886 					game_status = GS_OK;
2887 					fade_out_pal(200);
2888 				}
2889 			}
2890 			else if (status == GS_QUIT_GAME) {
2891 				log2file(" player quit");
2892 				if (got_sound) stop_music();
2893 				fade_out_pal(100);
2894 				if (got_sound) start_music(MOD_MENU_SONG);
2895 			}
2896 			else {
2897 				PACKFILE *pf;
2898 #ifdef __unix__
2899 				char filename[512];
2900 				char *homedir = get_homedir();
2901 #endif
2902 				log2file(" level complete");
2903 				if (got_sound) stop_music();
2904 				if (level < MAX_LEVELS && playing_original_game) {
2905 					int i;
2906 					int ace = 1;
2907 					if (player.cherries == player.cherries_taken) {
2908 						options.cherries[level] = 100;
2909 						log2file("   all cherries taken");
2910 					}
2911 					if (player.stars == player.stars_taken) {
2912 						options.stars[level] = 100;
2913 						log2file("   all stars taken");
2914 					}
2915 
2916 					// check if all levels are aced
2917 					for(i = 0; i < num_levels; i ++) {
2918 						if (options.cherries[i] != 100) ace = 0;
2919 						if (options.stars[i] != 100) ace = 0;
2920 					}
2921 					if (ace) options.one_hundred = 1;
2922 				}
2923 				level ++;
2924 				show_cutscene(level);
2925 				if (level == num_levels) {
2926 					log2file(" game completed");
2927 					game_status = GS_GAME_COMPLETE;
2928 				}
2929 				if (level > options.max_levels && game_status != GS_GAME_COMPLETE) {
2930 					options.max_levels = level;
2931 				}
2932 				fade_out_pal(100);
2933 
2934 				// save options
2935 				log2file(" saving options");
2936 #ifdef __unix__
2937 				snprintf(filename, sizeof(filename),
2938 					"%s/.alex4/alex4.sav",
2939 					homedir? homedir:".");
2940 				pf = pack_fopen(filename, "wp");
2941 #else
2942 				pf = pack_fopen("alex4.sav", "wp");
2943 #endif
2944 				if (pf) {
2945 					save_options(&options, pf);
2946 					pack_fclose(pf);
2947 				}
2948 			}
2949 		}
2950 
2951 		// are we done or dead?
2952 		if (game_status == GS_GAMEOVER || game_status == GS_GAME_COMPLETE) {
2953 			Thisc post;
2954 
2955 			if (game_status == GS_GAMEOVER) {
2956 				log2file(" game over");
2957 				show_game_over();
2958 			}
2959 			else { // game complete
2960 				if (playing_original_game) {
2961 					start_music(MOD_OUTRO_SONG);
2962 					run_script((char *)data[SCR_OUTRO].dat, data);
2963 					stop_music();
2964 				}
2965 				else {
2966 					show_custom_ending();
2967 				}
2968 				clear_to_color(swap_screen, 4);
2969 				blit_to_screen(swap_screen);
2970 			}
2971 
2972 			stop_music();
2973 
2974 			// build post
2975 			post.level = level + 1;
2976 			post.score = player.score;
2977 
2978 			// check if player got a high score
2979 			if (qualify_hisc_table(hisc_table, post)) {
2980 				fade_out_pal(100);
2981 
2982 				log2file(" player qualified for highscore (%d, %d)", post.level, post.score);
2983 
2984 				// get player name
2985 				get_player_name(post.name);
2986 				log2file(" score logged as <%s>", post.name);
2987 
2988 				enter_hisc_table(hisc_table, post);
2989 				sort_hisc_table(hisc_table);
2990 			}
2991 
2992 			if (got_sound) start_music(MOD_MENU_SONG);
2993 			status = GS_SCORES;
2994 		}
2995 
2996 	}
2997 
2998 	// show highscores?
2999 	// can be entered from both menu and end of game
3000 	if (status == GS_SCORES) {
3001 		fade_out_pal(100);
3002 		log2file(" showing scores");
3003 		show_scores(0, hisc_table);
3004 		if (options.one_hundred) {
3005 			show_scores(1, hisc_table_space);
3006 		}
3007 	}
3008 
3009 	// user quit
3010 	if (status == GS_QUIT_MENU) {
3011 		stop_music();
3012 		fade_out_pal(100);
3013 		// show bye bye screen
3014 		blit(data[INTRO_BG].dat, swap_screen, 0, 0, 0, 0, 160, 120);
3015 		transform_bitmap(swap_screen, -1);
3016 		textout_outline_center(swap_screen, "Thanks for playing!", 80, 20);
3017 		textout_outline_center(swap_screen, "Design, Code, GFX:", 80, 48);
3018 		textout_outline_center(swap_screen, "Johan Peitz", 80, 60);
3019 		textout_outline_center(swap_screen, "MUSIC, SFX:", 80, 78);
3020 		textout_outline_center(swap_screen, "Anders Svensson", 80, 90);
3021 		blit_to_screen(swap_screen);
3022 		fade_in_pal(100);
3023 		cycle_count = 0;
3024 		while(!key[KEY_ESC] && cycle_count < 200);
3025 		fade_out_pal(100);
3026 		clear(screen);
3027 	}
3028 
3029 	return status;
3030 }
3031 
3032 
3033 
3034 
3035 // main
main(int argc,char ** argv)3036 int main(int argc, char **argv) {
3037 	int i;
3038 	char full_path[1024];
3039 #ifndef __unix__
3040 	char working_directory[1024];
3041 #else
3042 	char *homedir = get_homedir();
3043 #endif
3044 
3045 	// init allegro
3046 	allegro_init();
3047 
3048 #ifdef __unix__
3049 	// start logfile
3050 	snprintf(full_path, sizeof(full_path), "%s/.alex4",
3051 		homedir? homedir:".");
3052 	check_and_create_dir(full_path);
3053 	snprintf(full_path, sizeof(full_path), "%s/.alex4/log.txt",
3054 		homedir? homedir:".");
3055 	log_fp = fopen(full_path, "wt");
3056 #else
3057 	// get working directory
3058 	get_executable_name(full_path, 1024);
3059 	replace_filename(working_directory, full_path, "", 1024);
3060 	chdir(working_directory);
3061 
3062 	// start logfile
3063 	log_fp = fopen("log.txt", "wt");
3064 #endif
3065 	if (log_fp) {
3066 		fprintf(log_fp, "Alex 4 (%s) - log file\n-------------------\n", GAME_VERSION_STR);
3067 	}
3068 
3069 	// log program arguments
3070 	log2file("Game started with the following commands:");
3071 	for(i = 0; i < argc; i ++) {
3072 		log2file("   %s", argv[i]);
3073 	}
3074 #ifndef __unix__
3075 	log2file("Working directory is:\n   %s", working_directory);
3076 #endif
3077 
3078 	// test wether to play real game
3079 	// or custom levels
3080 	if (argc == 1) playing_original_game = TRUE;
3081 	else playing_original_game = FALSE;
3082 
3083 	// init game
3084 	if (init_game((playing_original_game ? "maps from datafile please" : argv[1]))) {
3085 		if (playing_original_game) {
3086 			start_music(MOD_INTRO_SONG);
3087 			if (run_script((char *)data[SCR_INTRO].dat, data) < 0) {
3088 				fade_out_pal(100);
3089 			}
3090 			stop_music();
3091 		}
3092 		if (got_sound) start_music(MOD_MENU_SONG);
3093 		while(do_main_menu() != GS_QUIT_MENU);
3094 	}
3095 	else {
3096 		log2file("*** init failed!");
3097 		allegro_message("ALEX4:\nFailed to start game.");
3098 	}
3099 
3100 	// tidy up
3101 	uninit_game();
3102 	allegro_exit();
3103 	log2file("\nDone...\n");
3104 	if (log_fp)
3105 		fclose(log_fp);
3106 
3107 	return 0;
3108 } END_OF_MAIN();
3109 
3110 
3111 
3112 
3113 
3114 
3115