Remove "--info" command line option
[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 #include <SDL.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <errno.h>
21
22 #include "glext.h"
23 #include "config.h"
24 #include "video.h"
25 #include "image.h"
26 #include "audio.h"
27 #include "demo.h"
28 #include "progress.h"
29 #include "gui.h"
30 #include "set.h"
31 #include "tilt.h"
32 #include "fs.h"
33 #include "common.h"
34
35 #include "st_conf.h"
36 #include "st_title.h"
37 #include "st_demo.h"
38 #include "st_level.h"
39 #include "st_pause.h"
40
41 const char TITLE[] = "Neverball " VERSION;
42 const char ICON[] = "icon/neverball.png";
43
44 /*---------------------------------------------------------------------------*/
45
46 static void shot(void)
47 {
48     static char filename[MAXSTR];
49
50     sprintf(filename, "Screenshots/screen%05d.png", config_screenshot());
51     image_snap(filename);
52 }
53
54 /*---------------------------------------------------------------------------*/
55
56 static void toggle_wire(void)
57 {
58     static int wire = 0;
59
60     if (wire)
61     {
62         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
63         glEnable(GL_TEXTURE_2D);
64         glEnable(GL_LIGHTING);
65         wire = 0;
66     }
67     else
68     {
69         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
70         glDisable(GL_TEXTURE_2D);
71         glDisable(GL_LIGHTING);
72         wire = 1;
73     }
74 }
75
76 /*---------------------------------------------------------------------------*/
77
78 static int loop(void)
79 {
80     SDL_Event e;
81     int d = 1;
82     int c;
83
84     /* Process SDL events. */
85
86     while (d && SDL_PollEvent(&e))
87     {
88         switch (e.type)
89         {
90         case SDL_QUIT:
91             return 0;
92
93         case SDL_MOUSEMOTION:
94             st_point(+e.motion.x,
95                      -e.motion.y + config_get_d(CONFIG_HEIGHT),
96                      +e.motion.xrel,
97                      config_get_d(CONFIG_MOUSE_INVERT)
98                      ? +e.motion.yrel : -e.motion.yrel);
99             break;
100
101         case SDL_MOUSEBUTTONDOWN:
102             d = st_click(e.button.button, 1);
103             break;
104
105         case SDL_MOUSEBUTTONUP:
106             d = st_click(e.button.button, 0);
107             break;
108
109         case SDL_KEYDOWN:
110
111             c = e.key.keysym.sym;
112
113             if (config_tst_d(CONFIG_KEY_FORWARD, c))
114                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), -JOY_MAX);
115
116             else if (config_tst_d(CONFIG_KEY_BACKWARD, c))
117                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), +JOY_MAX);
118
119             else if (config_tst_d(CONFIG_KEY_LEFT, c))
120                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), -JOY_MAX);
121
122             else if (config_tst_d(CONFIG_KEY_RIGHT, c))
123                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), +JOY_MAX);
124
125             else switch (c)
126             {
127             case SDLK_F10:   shot();                    break;
128             case SDLK_F9:    config_tgl_d(CONFIG_FPS);  break;
129             case SDLK_F8:    config_tgl_d(CONFIG_NICE); break;
130
131             case SDLK_F7:
132                 if (config_cheat())
133                     toggle_wire();
134                 break;
135
136             case SDLK_RETURN:
137                 d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 1);
138                 break;
139             case SDLK_ESCAPE:
140                 d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT), 1);
141                 break;
142
143             default:
144                 if (SDL_EnableUNICODE(-1))
145                     d = st_keybd(e.key.keysym.unicode, 1);
146                 else
147                     d = st_keybd(e.key.keysym.sym, 1);
148             }
149
150             break;
151
152         case SDL_KEYUP:
153
154             c = e.key.keysym.sym;
155
156             if      (config_tst_d(CONFIG_KEY_FORWARD, c))
157                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), 1);
158
159             else if (config_tst_d(CONFIG_KEY_BACKWARD, c))
160                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_Y), 1);
161
162             else if (config_tst_d(CONFIG_KEY_LEFT, c))
163                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), 1);
164
165             else if (config_tst_d(CONFIG_KEY_RIGHT, c))
166                 st_stick(config_get_d(CONFIG_JOYSTICK_AXIS_X), 1);
167
168             else switch (c)
169             {
170             case SDLK_RETURN:
171                 d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 0);
172                 break;
173             case SDLK_ESCAPE:
174                 d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT), 0);
175                 break;
176
177             default:
178                 d = st_keybd(e.key.keysym.sym, 0);
179             }
180
181         case SDL_ACTIVEEVENT:
182             if (e.active.state == SDL_APPINPUTFOCUS)
183                 if (e.active.gain == 0 && video_get_grab())
184                     goto_pause();
185             break;
186
187         case SDL_JOYAXISMOTION:
188             st_stick(e.jaxis.axis, e.jaxis.value);
189             break;
190
191         case SDL_JOYBUTTONDOWN:
192             d = st_buttn(e.jbutton.button, 1);
193             break;
194
195         case SDL_JOYBUTTONUP:
196             d = st_buttn(e.jbutton.button, 0);
197             break;
198         }
199     }
200
201     /* Process events via the tilt sensor API. */
202
203     if (tilt_stat())
204     {
205         int b;
206         int s;
207
208         st_angle((int) tilt_get_x(),
209                  (int) tilt_get_z());
210
211         while (tilt_get_button(&b, &s))
212         {
213             const int X = config_get_d(CONFIG_JOYSTICK_AXIS_X);
214             const int Y = config_get_d(CONFIG_JOYSTICK_AXIS_Y);
215             const int L = config_get_d(CONFIG_JOYSTICK_DPAD_L);
216             const int R = config_get_d(CONFIG_JOYSTICK_DPAD_R);
217             const int U = config_get_d(CONFIG_JOYSTICK_DPAD_U);
218             const int D = config_get_d(CONFIG_JOYSTICK_DPAD_D);
219
220             if (b == L || b == R || b == U || b == D)
221             {
222                 static int pad[4] = { 0, 0, 0, 0 };
223
224                 /* Track the state of the D-pad buttons. */
225
226                 if      (b == L) pad[0] = s;
227                 else if (b == R) pad[1] = s;
228                 else if (b == U) pad[2] = s;
229                 else if (b == D) pad[3] = s;
230
231                 /* Convert D-pad button events into joystick axis motion. */
232
233                 if      (pad[0] && !pad[1]) st_stick(X, -JOY_MAX);
234                 else if (pad[1] && !pad[0]) st_stick(X, +JOY_MAX);
235                 else                        st_stick(X,        1);
236
237                 if      (pad[2] && !pad[3]) st_stick(Y, -JOY_MAX);
238                 else if (pad[3] && !pad[2]) st_stick(Y, +JOY_MAX);
239                 else                        st_stick(Y,        1);
240             }
241             else d = st_buttn(b, s);
242         }
243     }
244
245     return d;
246 }
247
248 /*---------------------------------------------------------------------------*/
249
250 static char *data_path = NULL;
251 static char *demo_path = NULL;
252
253 #define usage \
254     L_(                                                                   \
255         "Usage: %s [options ...]\n"                                       \
256         "Options:\n"                                                      \
257         "  -h, --help                show this usage message.\n"          \
258         "  -v, --version             show version.\n"                     \
259         "  -d, --data <dir>          use 'dir' as game data directory.\n" \
260         "  -r, --replay <file>       play the replay 'file'.\n"           \
261     )
262
263 #define argument_error(option) { \
264     fprintf(stderr, L_("Option '%s' requires an argument.\n"),  option); \
265 }
266
267 static void parse_args(int argc, char **argv)
268 {
269     int i;
270
271     /* Scan argument list. */
272
273     for (i = 1; i < argc; i++)
274     {
275         if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help")    == 0)
276         {
277             printf(usage, argv[0]);
278             exit(EXIT_SUCCESS);
279         }
280
281         if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0)
282         {
283             printf("%s\n", VERSION);
284             exit(EXIT_SUCCESS);
285         }
286
287         if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--data")    == 0)
288         {
289             if (i + 1 == argc)
290             {
291                 argument_error(argv[i]);
292                 exit(EXIT_FAILURE);
293             }
294             data_path = argv[++i];
295             continue;
296         }
297
298         if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--replay")  == 0)
299         {
300             if (i + 1 == argc)
301             {
302                 argument_error(argv[i]);
303                 exit(EXIT_FAILURE);
304             }
305             demo_path = argv[++i];
306             continue;
307         }
308
309         /* Assume a single unrecognised argument is a replay name. */
310
311         if (argc == 2)
312         {
313             demo_path = argv[i];
314             break;
315         }
316     }
317 }
318
319 #undef usage
320 #undef argument_error
321
322 /*---------------------------------------------------------------------------*/
323
324 static int is_replay(struct dir_item *item)
325 {
326     return strcmp(item->path + strlen(item->path) - 4, ".nbr") == 0;
327 }
328
329 static int is_score(struct dir_item *item)
330 {
331     return strncmp(item->path, "neverballhs-", sizeof ("neverballhs-") - 1) == 0;
332 }
333
334 static void make_dirs_and_migrate(void)
335 {
336     Array items;
337     int i;
338
339     const char *src;
340     char *dst;
341
342     if (fs_mkdir("Replays"))
343     {
344         if ((items = fs_dir_scan("", is_replay)))
345         {
346             for (i = 0; i < array_len(items); i++)
347             {
348                 src = DIR_ITEM_GET(items, i)->path;
349                 dst = concat_string("Replays/", src, NULL);
350                 fs_rename(src, dst);
351                 free(dst);
352             }
353
354             fs_dir_free(items);
355         }
356     }
357
358     if (fs_mkdir("Scores"))
359     {
360         if ((items = fs_dir_scan("", is_score)))
361         {
362             for (i = 0; i < array_len(items); i++)
363             {
364                 src = DIR_ITEM_GET(items, i)->path;
365                 dst = concat_string("Scores/",
366                                     src + sizeof ("neverballhs-") - 1,
367                                     ".txt",
368                                     NULL);
369                 fs_rename(src, dst);
370                 free(dst);
371             }
372
373             fs_dir_free(items);
374         }
375     }
376
377     fs_mkdir("Screenshots");
378 }
379
380 int main(int argc, char *argv[])
381 {
382     SDL_Joystick *joy = NULL;
383     int t1, t0, uniform;
384     Uint32 flags = 0;
385
386     if (!fs_init(argv[0]))
387     {
388         fputs("Failure to initialize virtual file system\n", stderr);
389         return 1;
390     }
391
392     lang_init("neverball");
393
394     parse_args(argc, argv);
395
396     config_paths(data_path);
397     make_dirs_and_migrate();
398
399     /* Initialize SDL system and subsystems */
400
401     flags |= SDL_INIT_VIDEO;
402     flags |= SDL_INIT_AUDIO;
403     flags |= config_get_d(CONFIG_JOYSTICK) ? SDL_INIT_JOYSTICK : 0;
404
405     if (SDL_Init(flags) == -1)
406     {
407         fprintf(stderr, "%s\n", SDL_GetError());
408         return 1;
409     }
410
411     /* Intitialize the configuration */
412
413     config_init();
414     config_load();
415
416     /* Initialize the joystick. */
417
418     if (SDL_WasInit(SDL_INIT_JOYSTICK) && SDL_NumJoysticks() > 0)
419     {
420         joy = SDL_JoystickOpen(config_get_d(CONFIG_JOYSTICK_DEVICE));
421         if (joy)
422             SDL_JoystickEventState(SDL_ENABLE);
423     }
424
425     /* Initialize the audio. */
426
427     audio_init();
428     tilt_init();
429
430     /* Initialize the video. */
431
432     if (!video_init(TITLE, ICON))
433         return 1;
434
435     init_state(&st_null);
436
437     /* Initialise demo playback. */
438
439     if (demo_path && fs_add_path(dir_name(demo_path)) &&
440         progress_replay(base_name(demo_path, NULL)))
441     {
442         demo_play_goto(1);
443         goto_state(&st_demo_play);
444     }
445     else
446         goto_state(&st_title);
447
448     /* Run the main game loop. */
449
450     uniform = config_get_d(CONFIG_UNIFORM);
451     t0 = SDL_GetTicks();
452
453     while (loop())
454     {
455         t1 = SDL_GetTicks();
456
457         if (uniform)
458         {
459             /* Step the game uniformly, as configured. */
460
461             int u;
462
463             for (u = 0; u < abs(uniform); ++u)
464             {
465                 st_timer(DT);
466                 t0 += (int) (DT * 1000);
467             }
468         }
469         else
470         {
471             /* Step the game state at least up to the current time. */
472
473             while (t1 > t0)
474             {
475                 st_timer(DT);
476                 t0 += (int) (DT * 1000);
477             }
478         }
479
480         /* Render. */
481
482         st_paint(0.001f * t0);
483         video_swap();
484
485         if (uniform < 0)
486             shot();
487
488         if (config_get_d(CONFIG_NICE))
489             SDL_Delay(1);
490     }
491
492     /* Gracefully close the game */
493
494     if (joy)
495         SDL_JoystickClose(joy);
496
497     tilt_free();
498     SDL_Quit();
499
500     config_save();
501
502     return 0;
503 }
504
505 /*---------------------------------------------------------------------------*/
506