global level and set loading at the begin of the game
[neverball] / ball / main.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 /*---------------------------------------------------------------------------*/
16
17 #ifdef WIN32
18 #pragma comment(lib, "SDL_ttf.lib")
19 #pragma comment(lib, "SDL_mixer.lib")
20 #pragma comment(lib, "SDL_image.lib")
21 #pragma comment(lib, "SDL.lib")
22 #pragma comment(lib, "SDLmain.lib")
23 #pragma comment(lib, "opengl32.lib")
24 #endif
25
26 /*---------------------------------------------------------------------------*/
27
28 #include <SDL.h>
29 #include <SDL_image.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "glext.h"
35 #include "config.h"
36 #include "image.h"
37 #include "audio.h"
38 #include "demo.h"
39 #include "levels.h"
40 #include "game.h"
41 #include "gui.h"
42 #include "set.h"
43
44 #include "st_conf.h"
45 #include "st_title.h"
46 #include "st_demo.h"
47 #include "st_level.h"
48
49 #define TITLE "Neverball"
50
51 /*---------------------------------------------------------------------------*/
52
53 static void shot(void)
54 {
55     static char filename[MAXSTR];
56     static int  num = 0;
57
58     sprintf(filename, _("screen%02d.png"), num++);
59
60     image_snap(filename);
61 }
62
63 /*---------------------------------------------------------------------------*/
64
65 static void toggle_wire(void)
66 {
67     static int wire = 0;
68
69     if (wire)
70     {
71         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
72         glEnable(GL_TEXTURE_2D);
73         glEnable(GL_LIGHTING);
74         wire = 0;
75     }
76     else
77     {
78         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
79         glDisable(GL_TEXTURE_2D);
80         glDisable(GL_LIGHTING);
81         wire = 1;
82     }
83 }
84
85 static void toggle_fullscreen(void)
86 {
87     int x, y;
88
89     SDL_GetMouseState(&x, &y);
90     config_mode(!config_get_d(CONFIG_FULLSCREEN), config_get_d(CONFIG_WIDTH),
91                 config_get_d(CONFIG_HEIGHT));
92     SDL_WarpMouse(x, y);
93 }
94
95 /*---------------------------------------------------------------------------*/
96
97 static int loop(void)
98 {
99     SDL_Event e;
100     int d = 1;
101
102     while (d && SDL_PollEvent(&e))
103     {
104         if (e.type == SDL_QUIT)
105             return 0;
106
107         if (e.type == SDL_KEYDOWN)
108             switch (e.key.keysym.sym)
109             {
110             case SDLK_SPACE: config_tgl_pause();        break;
111             case SDLK_F11:   toggle_fullscreen();       break;
112             case SDLK_F10:   shot();                    break;
113             case SDLK_F9:    config_tgl_d(CONFIG_FPS);  break;
114             case SDLK_F8:    config_tgl_d(CONFIG_NICE); break;
115             case SDLK_F7:    toggle_wire();             break;
116             default: break;
117             }
118
119         if (!config_get_pause())
120             switch (e.type)
121             {
122             case SDL_MOUSEMOTION:
123                 st_point(+e.motion.x,
124                          -e.motion.y + config_get_d(CONFIG_HEIGHT),
125                          +e.motion.xrel,
126                          config_get_d(CONFIG_MOUSE_INVERT)
127                          ? +e.motion.yrel : -e.motion.yrel);
128                 break;
129
130             case SDL_MOUSEBUTTONDOWN:
131                 d = st_click((e.button.button == SDL_BUTTON_LEFT) ? -1 : 1, 1);
132                 break;
133
134             case SDL_MOUSEBUTTONUP:
135                 d = st_click((e.button.button == SDL_BUTTON_LEFT) ? -1 : 1, 0);
136                 break;
137
138             case SDL_KEYDOWN:
139
140                 switch (e.key.keysym.sym)
141                 {
142
143                 case SDLK_RETURN:
144                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 1);
145                     break;
146                 case SDLK_ESCAPE:
147                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT), 1);
148                     break;
149                 case SDLK_LEFT:
150                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), -JOY_MAX);
151                     break;
152                 case SDLK_RIGHT:
153                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), +JOY_MAX);
154                     break;
155                 case SDLK_UP:
156                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), -JOY_MAX);
157                     break;
158                 case SDLK_DOWN:
159                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), +JOY_MAX);
160                     break;
161
162                 default:
163                     if (SDL_EnableUNICODE(-1))
164                         d = st_keybd(e.key.keysym.unicode, 1);
165                     else
166                         d = st_keybd(e.key.keysym.sym, 1);
167                 }
168                 break;
169
170             case SDL_KEYUP:
171
172                 switch (e.key.keysym.sym)
173                 {
174                 case SDLK_RETURN:
175                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 0);
176                     break;
177                 case SDLK_ESCAPE:
178                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT), 0);
179                     break;
180                 case SDLK_LEFT:
181                 case SDLK_RIGHT:
182                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), 1);
183                     break;
184                 case SDLK_DOWN:
185                 case SDLK_UP:
186                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), 1);
187                     break;
188
189                 default:
190                     d = st_keybd(e.key.keysym.sym, 0);
191                 }
192
193                 break;
194
195             case SDL_ACTIVEEVENT:
196                 if (e.active.state == SDL_APPINPUTFOCUS)
197                     if (e.active.gain == 0 && config_get_grab())
198                         config_set_pause();
199                 break;
200
201             case SDL_JOYAXISMOTION:
202                 st_stick(e.jaxis.axis, e.jaxis.value);
203                 break;
204
205             case SDL_JOYBUTTONDOWN:
206                 d = st_buttn(e.jbutton.button, 1);
207                 break;
208
209             case SDL_JOYBUTTONUP:
210                 d = st_buttn(e.jbutton.button, 0);
211                 break;
212             }
213     }
214     return d;
215 }
216
217 /*---------------------------------------------------------------------------*/
218
219 /* Option values */
220
221 static char *data_path    = NULL;
222 static char *replay_path  = NULL;
223 static char *level_path   = NULL;
224 static int   display_info = 0;
225
226 /* Option handling */
227
228 static void parse_args(int argc, char **argv)
229 {
230     char *exec = *(argv++);
231     int missing; /* Argument is missing. */
232
233     const char *usage = _(
234         "Usage: %s [options ...]\n"
235         "-r, --replay file         play the replay 'file'.\n"
236         "-l, --level file.sol      play the level 'file.sol'.\n"
237         "-i, --info                display info about level or replay.\n"
238         "    --data dir            use 'dir' as game data directory.\n"
239         "-v, --version             show version.\n"
240         "-h, -?, --help            show this usage message.\n"
241     );
242
243 #define CASE(x) (strcmp(*argv, (x)) == 0)       /* Check current option */
244 #define MAND    !(missing = (argv[1] == NULL))  /* Argument is mandatory */
245
246     while (*argv != NULL)
247     {
248         missing = 0;
249         if (CASE("-h") || CASE("-?") || CASE("--help"))
250         {
251             printf(usage, exec);
252             exit(0);
253         }
254         else if (CASE("-v") || CASE("--version"))
255         {
256             printf("%s %s\n", TITLE, VERSION);
257             exit(0);
258         }
259         else if (CASE("--data") && MAND)
260             data_path = *(++argv);
261         else if ((CASE("-r") || CASE("--replay")) && MAND)
262             replay_path = *(++argv);
263         else if ((CASE("-l") || CASE("--level")) && MAND)
264             level_path = *(++argv);
265         else if ((CASE("-i") || CASE("--info")))
266             display_info = 1;
267         else if (!missing)
268         {
269             fprintf(stderr, _("%s: unknown option %s\n"), exec, *argv);
270             fprintf(stderr, usage, exec);
271             exit(1);
272         }
273         else
274         {
275             fprintf(stderr, _("%s: option %s requires an argument\n"), exec,
276                     *argv);
277             fprintf(stderr, usage, exec);
278             exit(1);
279         }
280         argv++;
281     }
282     return;
283 }
284
285 int main(int argc, char *argv[])
286 {
287     SDL_Joystick *joy = NULL;
288     SDL_Surface *icon;
289
290     int t1, t0;
291
292     language_init("neverball", CONFIG_LOCALE);
293
294     parse_args(argc, argv);
295
296     if (!config_data_path(data_path, SET_FILE))
297     {
298         fprintf(stderr, _("Failure to establish game data directory\n"));
299         return 1;
300     }
301
302     if (!config_user_path(NULL))
303     {
304         fprintf(stderr, _("Failure to establish config directory\n"));
305         return 1;
306     }
307
308     /* Intitialize the configuration */
309
310     config_init();
311     config_load();
312
313     /* Initialize the language */
314
315     language_set(language_from_code(config_simple_get_s(CONFIG_LANG)));
316
317     /* Prepare run without SDL */
318
319     if (replay_path)
320     {
321         if (!level_replay(replay_path))
322         {
323             fprintf(stderr, _("Replay file '%s': %s\n"), replay_path,
324                     errno ? strerror(errno) : _("Not a replay file"));
325             return 1;
326         }
327         else if (display_info)
328             demo_replay_dump_info();
329     }
330
331     if (level_path != NULL)
332     {
333         struct level l;
334         if (!level_load(level_path, &l))
335             return 1;
336         else if (display_info)
337             level_dump_info(&l);
338     }
339
340     if (display_info)
341     {
342         if (replay_path == NULL && level_path == NULL)
343         {
344             fprintf(stderr, _("%s: --info requires --replay or --level\n"),
345                     argv[0]);
346             return 1;
347         }
348         return 0;
349     }
350
351     /* Initialize SDL system and subsystems */
352
353     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) == -1)
354     {
355         fprintf(stderr, "%s\n", SDL_GetError());
356         return 1;
357     }
358
359     /* Initialize the joystick. */
360
361     if (SDL_NumJoysticks() > 0)
362     {
363         joy = SDL_JoystickOpen(config_get_d(CONFIG_JOYSTICK_DEVICE));
364         if (joy)
365             SDL_JoystickEventState(SDL_ENABLE);
366     }
367
368     /* Initialize the audio. */
369
370     audio_bind(AUD_MENU,   3, "snd/menu.wav");
371     audio_bind(AUD_START,  1, _("snd/select.ogg"));
372     audio_bind(AUD_READY,  1, _("snd/ready.ogg"));
373     audio_bind(AUD_SET,    1, _("snd/set.ogg"));
374     audio_bind(AUD_GO,     1, _("snd/go.ogg"));
375     audio_bind(AUD_BALL,   2, "snd/ball.ogg");
376     audio_bind(AUD_BUMP,   3, "snd/bump.ogg");
377     audio_bind(AUD_COIN,   2, "snd/coin.wav");
378     audio_bind(AUD_TICK,   4, "snd/tick.ogg");
379     audio_bind(AUD_TOCK,   4, "snd/tock.ogg");
380     audio_bind(AUD_SWITCH, 5, "snd/switch.wav");
381     audio_bind(AUD_JUMP,   5, "snd/jump.ogg");
382     audio_bind(AUD_GOAL,   5, "snd/goal.wav");
383     audio_bind(AUD_SCORE,  1, _("snd/record.ogg"));
384     audio_bind(AUD_FALL,   1, _("snd/fall.ogg"));
385     audio_bind(AUD_TIME,   1, _("snd/time.ogg"));
386     audio_bind(AUD_OVER,   1, _("snd/over.ogg"));
387
388     audio_init();
389
390     /* Require 16-bit double buffer with 16-bit depth buffer. */
391
392     SDL_GL_SetAttribute(SDL_GL_RED_SIZE,     5);
393     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,   5);
394     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,    5);
395     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,  16);
396     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
397
398     /* Set the WM icon */
399
400     icon = IMG_Load(config_data("icon/neverball.png"));
401
402     if (icon)
403     {
404         SDL_WM_SetIcon(icon, NULL);
405         SDL_FreeSurface(icon);
406     }
407
408     /* Initialize the video. */
409
410     if (!config_mode(config_get_d(CONFIG_FULLSCREEN),
411                      config_get_d(CONFIG_WIDTH), config_get_d(CONFIG_HEIGHT)))
412     {
413         fprintf(stderr, "%s\n", SDL_GetError());
414         return 1;
415     }
416
417     SDL_WM_SetCaption(TITLE, TITLE);
418
419     /* Load levels and sets. */
420
421     set_init();
422     
423     /* Initialize the run state. */
424
425     init_state(&st_null);
426
427     if (replay_path)
428     {
429         level_replay(replay_path);
430         demo_play_goto(1);
431         goto_state(&st_demo_play);
432     }
433     else if (level_path != NULL)
434     {
435         level_play_single(level_path);
436         goto_state(&st_level);
437     }
438     else
439         goto_state(&st_title);
440
441     /* Run the main game loop. */
442
443     t0 = SDL_GetTicks();
444     while (loop())
445         if ((t1 = SDL_GetTicks()) > t0)
446         {
447             if (config_get_pause())
448             {
449                 st_paint();
450                 gui_blank();
451                 SDL_Delay(10);
452             }
453             else
454             {
455                 st_timer((t1 - t0) / 1000.f);
456                 st_paint();
457             }
458             SDL_GL_SwapBuffers();
459
460             t0 = t1;
461
462             if (config_get_d(CONFIG_NICE))
463                 SDL_Delay(1);
464         }
465
466     /* Gracefully close the game */
467
468     if (SDL_JoystickOpened(0))
469         SDL_JoystickClose(joy);
470
471     SDL_Quit();
472
473     config_save();
474
475     return 0;
476 }
477
478 /*---------------------------------------------------------------------------*/
479