Merged changesets [1048] and [1049] from source:branches/config-fix into trunk (chang...
[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
116             case SDLK_F7:
117                 if (config_get_cheat())
118                     toggle_wire();
119                 break;
120
121             default: break;
122             }
123
124         if (!config_get_pause())
125             switch (e.type)
126             {
127             case SDL_MOUSEMOTION:
128                 st_point(+e.motion.x,
129                          -e.motion.y + config_get_d(CONFIG_HEIGHT),
130                          +e.motion.xrel,
131                          config_get_d(CONFIG_MOUSE_INVERT)
132                          ? +e.motion.yrel : -e.motion.yrel);
133                 break;
134
135             case SDL_MOUSEBUTTONDOWN:
136                 d = st_click((e.button.button == SDL_BUTTON_LEFT) ? -1 : 1, 1);
137                 break;
138
139             case SDL_MOUSEBUTTONUP:
140                 d = st_click((e.button.button == SDL_BUTTON_LEFT) ? -1 : 1, 0);
141                 break;
142
143             case SDL_KEYDOWN:
144
145                 switch (e.key.keysym.sym)
146                 {
147
148                 case SDLK_RETURN:
149                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 1);
150                     break;
151                 case SDLK_ESCAPE:
152                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT), 1);
153                     break;
154                 case SDLK_LEFT:
155                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), -JOY_MAX);
156                     break;
157                 case SDLK_RIGHT:
158                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), +JOY_MAX);
159                     break;
160                 case SDLK_UP:
161                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), -JOY_MAX);
162                     break;
163                 case SDLK_DOWN:
164                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), +JOY_MAX);
165                     break;
166
167                 default:
168                     if (SDL_EnableUNICODE(-1))
169                         d = st_keybd(e.key.keysym.unicode, 1);
170                     else
171                         d = st_keybd(e.key.keysym.sym, 1);
172                 }
173                 break;
174
175             case SDL_KEYUP:
176
177                 switch (e.key.keysym.sym)
178                 {
179                 case SDLK_RETURN:
180                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 0);
181                     break;
182                 case SDLK_ESCAPE:
183                     d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT), 0);
184                     break;
185                 case SDLK_LEFT:
186                 case SDLK_RIGHT:
187                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), 1);
188                     break;
189                 case SDLK_DOWN:
190                 case SDLK_UP:
191                     st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), 1);
192                     break;
193
194                 default:
195                     d = st_keybd(e.key.keysym.sym, 0);
196                 }
197
198                 break;
199
200             case SDL_ACTIVEEVENT:
201                 if (e.active.state == SDL_APPINPUTFOCUS)
202                     if (e.active.gain == 0 && config_get_grab())
203                         config_set_pause();
204                 break;
205
206             case SDL_JOYAXISMOTION:
207                 st_stick(e.jaxis.axis, e.jaxis.value);
208                 break;
209
210             case SDL_JOYBUTTONDOWN:
211                 d = st_buttn(e.jbutton.button, 1);
212                 break;
213
214             case SDL_JOYBUTTONUP:
215                 d = st_buttn(e.jbutton.button, 0);
216                 break;
217             }
218     }
219     return d;
220 }
221
222 /*---------------------------------------------------------------------------*/
223
224 /* Option values */
225
226 static char *data_path    = NULL;
227 static char *replay_path  = NULL;
228 static int   display_info = 0;
229
230 /* Option handling */
231
232 static void parse_args(int argc, char **argv)
233 {
234     char *exec = *(argv++);
235     int missing;
236
237     const char *usage = _(
238         "Usage: %s [options ...]\n"
239         "-r, --replay file         play the replay 'file'.\n"
240         "-i, --info                display info about a replay.\n"
241         "    --data dir            use 'dir' as game data directory.\n"
242         "-v, --version             show version.\n"
243         "-h, -?, --help            show this usage message.\n"
244     );
245
246 #   define CASE(x) (strcmp(*argv, (x)) == 0)       /* Check current option */
247 #   define MAND    !(missing = (argv[1] == NULL))  /* Argument is mandatory */
248
249     while (*argv != NULL)
250     {
251         missing = 0;
252         if (CASE("-h") || CASE("-?") || CASE("--help"))
253         {
254             printf(usage, exec);
255             exit(0);
256         }
257         else if (CASE("-v") || CASE("--version"))
258         {
259             printf("%s\n", VERSION);
260             exit(0);
261         }
262         else if (CASE("--data") && MAND)
263             data_path = *(++argv);
264         else if ((CASE("-r") || CASE("--replay")) && MAND)
265             replay_path = *(++argv);
266         else if ((CASE("-i") || CASE("--info")))
267             display_info = 1;
268         else if (!missing)
269         {
270             fprintf(stderr, _("%s: unknown option %s\n"), exec, *argv);
271             fprintf(stderr, usage, exec);
272             exit(1);
273         }
274         else
275         {
276             fprintf(stderr, _("%s: option %s requires an argument\n"), exec,
277                     *argv);
278             fprintf(stderr, usage, exec);
279             exit(1);
280         }
281         argv++;
282     }
283
284 #   undef CASE
285 #   undef MAND
286
287     return;
288 }
289
290 int main(int argc, char *argv[])
291 {
292     SDL_Joystick *joy = NULL;
293     SDL_Surface *icon;
294
295     int t1, t0;
296
297     language_init("neverball", CONFIG_LOCALE);
298
299     parse_args(argc, argv);
300
301     if (!config_data_path(data_path, SET_FILE))
302     {
303         fprintf(stderr, _("Failure to establish game data directory\n"));
304         return 1;
305     }
306
307     if (!config_user_path(NULL))
308     {
309         fprintf(stderr, _("Failure to establish config directory\n"));
310         return 1;
311     }
312
313     /* Initialize SDL system and subsystems */
314
315     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) == -1)
316     {
317         fprintf(stderr, "%s\n", SDL_GetError());
318         return 1;
319     }
320
321     /* Intitialize the configuration */
322
323     config_init();
324     config_load();
325
326     /* Initialize the language */
327
328     language_set(language_from_code(config_simple_get_s(CONFIG_LANG)));
329
330     /* Prepare run without GUI */
331
332     if (replay_path)
333     {
334         if (!level_replay(replay_path))
335         {
336             fprintf(stderr, _("Replay file '%s': %s\n"), replay_path,
337                     errno ? strerror(errno) : _("Not a replay file"));
338             return 1;
339         }
340         else if (display_info)
341             demo_replay_dump_info();
342     }
343
344     if (display_info)
345     {
346         if (replay_path == NULL)
347         {
348             fprintf(stderr, _("%s: --info requires --replay\n"),
349                     argv[0]);
350             return 1;
351         }
352         return 0;
353     }
354
355     /* Initialize the joystick. */
356
357     if (SDL_NumJoysticks() > 0)
358     {
359         joy = SDL_JoystickOpen(config_get_d(CONFIG_JOYSTICK_DEVICE));
360         if (joy)
361             SDL_JoystickEventState(SDL_ENABLE);
362     }
363
364     /* Initialize the audio. */
365
366     audio_bind(AUD_MENU,   3, "snd/menu.wav");
367     audio_bind(AUD_START,  1, _("snd/select.ogg"));
368     audio_bind(AUD_READY,  1, _("snd/ready.ogg"));
369     audio_bind(AUD_SET,    1, _("snd/set.ogg"));
370     audio_bind(AUD_GO,     1, _("snd/go.ogg"));
371     audio_bind(AUD_BALL,   2, "snd/ball.ogg");
372     audio_bind(AUD_BUMPS,  3, "snd/bumplil.ogg");
373     audio_bind(AUD_BUMPM,  3, "snd/bump.ogg");
374     audio_bind(AUD_BUMPL,  3, "snd/bumpbig.ogg");
375     audio_bind(AUD_COIN,   2, "snd/coin.wav");
376     audio_bind(AUD_TICK,   4, "snd/tick.ogg");
377     audio_bind(AUD_TOCK,   4, "snd/tock.ogg");
378     audio_bind(AUD_SWITCH, 5, "snd/switch.wav");
379     audio_bind(AUD_JUMP,   5, "snd/jump.ogg");
380     audio_bind(AUD_GOAL,   5, "snd/goal.wav");
381     audio_bind(AUD_SCORE,  1, _("snd/record.ogg"));
382     audio_bind(AUD_FALL,   1, _("snd/fall.ogg"));
383     audio_bind(AUD_TIME,   1, _("snd/time.ogg"));
384     audio_bind(AUD_OVER,   1, _("snd/over.ogg"));
385     audio_bind(AUD_GROW,   5, "snd/grow.ogg");
386     audio_bind(AUD_SHRINK, 5, "snd/shrink.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     /* Initialize the run state. */
420
421     init_state(&st_null);
422
423     if (replay_path)
424     {
425         level_replay(replay_path);
426         demo_play_goto(1);
427         goto_state(&st_demo_play);
428     }
429     else
430         goto_state(&st_title);
431
432     /* Run the main game loop. */
433
434     t0 = SDL_GetTicks();
435     while (loop())
436         if ((t1 = SDL_GetTicks()) > t0)
437         {
438             if (config_get_pause())
439             {
440                 st_paint();
441                 gui_blank();
442                 SDL_Delay(10);
443             }
444             else
445             {
446                 st_timer((t1 - t0) / 1000.f);
447                 st_paint();
448             }
449             SDL_GL_SwapBuffers();
450
451             t0 = t1;
452
453             if (config_get_d(CONFIG_NICE))
454                 SDL_Delay(1);
455         }
456
457     /* Gracefully close the game */
458
459     if (SDL_JoystickOpened(0))
460         SDL_JoystickClose(joy);
461
462     SDL_Quit();
463
464     config_save();
465
466     return 0;
467 }
468
469 /*---------------------------------------------------------------------------*/
470