Minor simplification of code in date_to_str().
[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 int   display_info = 0;
224
225 /* Option handling */
226
227 static void parse_args(int argc, char **argv)
228 {
229     char *exec = *(argv++);
230     int missing; /* Argument is missing. */
231
232     const char *usage = _(
233         "Usage: %s [options ...]\n"
234         "-r, --replay file         play the replay 'file'.\n"
235         "-i, --info                display info about level or replay.\n"
236         "    --data dir            use 'dir' as game data directory.\n"
237         "-v, --version             show version.\n"
238         "-h, -?, --help            show this usage message.\n"
239     );
240
241 #define CASE(x) (strcmp(*argv, (x)) == 0)       /* Check current option */
242 #define MAND    !(missing = (argv[1] == NULL))  /* Argument is mandatory */
243
244     while (*argv != NULL)
245     {
246         missing = 0;
247         if (CASE("-h") || CASE("-?") || CASE("--help"))
248         {
249             printf(usage, exec);
250             exit(0);
251         }
252         else if (CASE("-v") || CASE("--version"))
253         {
254             printf("%s %s\n", TITLE, VERSION);
255             exit(0);
256         }
257         else if (CASE("--data") && MAND)
258             data_path = *(++argv);
259         else if ((CASE("-r") || CASE("--replay")) && MAND)
260             replay_path = *(++argv);
261         else if ((CASE("-i") || CASE("--info")))
262             display_info = 1;
263         else if (!missing)
264         {
265             fprintf(stderr, _("%s: unknown option %s\n"), exec, *argv);
266             fprintf(stderr, usage, exec);
267             exit(1);
268         }
269         else
270         {
271             fprintf(stderr, _("%s: option %s requires an argument\n"), exec,
272                     *argv);
273             fprintf(stderr, usage, exec);
274             exit(1);
275         }
276         argv++;
277     }
278     return;
279 }
280
281 int main(int argc, char *argv[])
282 {
283     SDL_Joystick *joy = NULL;
284     SDL_Surface *icon;
285
286     int t1, t0;
287
288     language_init("neverball", CONFIG_LOCALE);
289
290     parse_args(argc, argv);
291
292     if (!config_data_path(data_path, SET_FILE))
293     {
294         fprintf(stderr, _("Failure to establish game data directory\n"));
295         return 1;
296     }
297
298     if (!config_user_path(NULL))
299     {
300         fprintf(stderr, _("Failure to establish config directory\n"));
301         return 1;
302     }
303
304     /* Intitialize the configuration */
305
306     config_init();
307     config_load();
308
309     /* Initialize the language */
310
311     language_set(language_from_code(config_simple_get_s(CONFIG_LANG)));
312
313     /* Prepare run without SDL */
314
315     if (replay_path)
316     {
317         if (!level_replay(replay_path))
318         {
319             fprintf(stderr, _("Replay file '%s': %s\n"), replay_path,
320                     errno ? strerror(errno) : _("Not a replay file"));
321             return 1;
322         }
323         else if (display_info)
324             demo_replay_dump_info();
325     }
326
327     if (display_info)
328     {
329         if (replay_path == NULL)
330         {
331             fprintf(stderr, _("%s: --info requires --replay\n"),
332                     argv[0]);
333             return 1;
334         }
335         return 0;
336     }
337
338     /* Initialize SDL system and subsystems */
339
340     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) == -1)
341     {
342         fprintf(stderr, "%s\n", SDL_GetError());
343         return 1;
344     }
345
346     /* Initialize the joystick. */
347
348     if (SDL_NumJoysticks() > 0)
349     {
350         joy = SDL_JoystickOpen(config_get_d(CONFIG_JOYSTICK_DEVICE));
351         if (joy)
352             SDL_JoystickEventState(SDL_ENABLE);
353     }
354
355     /* Initialize the audio. */
356
357     audio_bind(AUD_MENU,   3, "snd/menu.wav");
358     audio_bind(AUD_START,  1, _("snd/select.ogg"));
359     audio_bind(AUD_READY,  1, _("snd/ready.ogg"));
360     audio_bind(AUD_SET,    1, _("snd/set.ogg"));
361     audio_bind(AUD_GO,     1, _("snd/go.ogg"));
362     audio_bind(AUD_BALL,   2, "snd/ball.ogg");
363     audio_bind(AUD_BUMP,   3, "snd/bump.ogg");
364     audio_bind(AUD_COIN,   2, "snd/coin.wav");
365     audio_bind(AUD_TICK,   4, "snd/tick.ogg");
366     audio_bind(AUD_TOCK,   4, "snd/tock.ogg");
367     audio_bind(AUD_SWITCH, 5, "snd/switch.wav");
368     audio_bind(AUD_JUMP,   5, "snd/jump.ogg");
369     audio_bind(AUD_GOAL,   5, "snd/goal.wav");
370     audio_bind(AUD_SCORE,  1, _("snd/record.ogg"));
371     audio_bind(AUD_FALL,   1, _("snd/fall.ogg"));
372     audio_bind(AUD_TIME,   1, _("snd/time.ogg"));
373     audio_bind(AUD_OVER,   1, _("snd/over.ogg"));
374
375     audio_init();
376
377     /* Require 16-bit double buffer with 16-bit depth buffer. */
378
379     SDL_GL_SetAttribute(SDL_GL_RED_SIZE,     5);
380     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,   5);
381     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,    5);
382     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,  16);
383     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
384
385     /* Set the WM icon */
386
387     icon = IMG_Load(config_data("icon/neverball.png"));
388
389     if (icon)
390     {
391         SDL_WM_SetIcon(icon, NULL);
392         SDL_FreeSurface(icon);
393     }
394
395     /* Initialize the video. */
396
397     if (!config_mode(config_get_d(CONFIG_FULLSCREEN),
398                      config_get_d(CONFIG_WIDTH), config_get_d(CONFIG_HEIGHT)))
399     {
400         fprintf(stderr, "%s\n", SDL_GetError());
401         return 1;
402     }
403
404     SDL_WM_SetCaption(TITLE, TITLE);
405
406     /* Initialize the run state. */
407
408     init_state(&st_null);
409
410     if (replay_path)
411     {
412         level_replay(replay_path);
413         demo_play_goto(1);
414         goto_state(&st_demo_play);
415     }
416     else
417         goto_state(&st_title);
418
419     /* Run the main game loop. */
420
421     t0 = SDL_GetTicks();
422     while (loop())
423         if ((t1 = SDL_GetTicks()) > t0)
424         {
425             if (config_get_pause())
426             {
427                 st_paint();
428                 gui_blank();
429                 SDL_Delay(10);
430             }
431             else
432             {
433                 st_timer((t1 - t0) / 1000.f);
434                 st_paint();
435             }
436             SDL_GL_SwapBuffers();
437
438             t0 = t1;
439
440             if (config_get_d(CONFIG_NICE))
441                 SDL_Delay(1);
442         }
443
444     /* Gracefully close the game */
445
446     if (SDL_JoystickOpened(0))
447         SDL_JoystickClose(joy);
448
449     SDL_Quit();
450
451     config_save();
452
453     return 0;
454 }
455
456 /*---------------------------------------------------------------------------*/
457