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