NXEngine v1.0.0.6
[NXEngine.git] / main.cpp
blob3a12bf34356b14e002e55c142bfc4d29d226e8c6
2 #include "nx.h"
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include "graphics/safemode.h"
6 #include "main.fdh"
8 const char *data_dir = "data";
9 const char *stage_dir = "data/Stage";
10 const char *pic_dir = "endpic";
11 const char *nxdata_dir = ".";
13 int fps = 0;
14 static int fps_so_far = 0;
15 static uint32_t fpstimer = 0;
17 #define GAME_WAIT (1000/GAME_FPS) // sets framerate
18 #define VISFLAGS (SDL_APPACTIVE | SDL_APPINPUTFOCUS)
19 int framecount = 0;
20 bool freezeframe = false;
21 int flipacceltime = 0;
23 int main(int argc, char *argv[])
25 bool inhibit_loadfade = false;
26 bool error = false;
27 bool freshstart;
29 SetLogFilename("debug.txt");
30 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
32 staterr("ack, sdl_init failed: %s.", SDL_GetError());
33 return 1;
35 atexit(SDL_Quit);
37 // start up inputs first thing because settings_load may remap them
38 input_init();
40 // load settings, or at least get the defaults,
41 // so we know the initial screen resolution.
42 settings_load();
44 if (Graphics::init(settings->resolution)) { staterr("Failed to initialize graphics."); return 1; }
45 if (font_init()) { staterr("Failed to load font."); return 1; }
47 //speed_test();
48 //return 1;
50 #ifdef CONFIG_DATA_EXTRACTOR
51 if (!settings->files_extracted)
53 if (extract_main())
55 Graphics::close();
56 font_close();
57 return 0;
59 else
61 settings->files_extracted = true;
62 settings_save();
65 #endif
67 if (check_data_exists())
69 return 1;
72 //Graphics::ShowLoadingScreen();
73 if (sound_init()) { fatal("Failed to initialize sound."); return 1; }
74 if (trig_init()) { fatal("Failed trig module init."); return 1; }
76 if (tsc_init()) { fatal("Failed to initialize script engine."); return 1; }
77 if (textbox.Init()) { fatal("Failed to initialize textboxes."); return 1; }
78 if (Carets::init()) { fatal("Failed to initialize carets."); return 1; }
80 if (game.init()) return 1;
81 game.setmode(GM_NORMAL);
82 // set null stage just to have something to do while we go to intro
83 game.switchstage.mapno = 0;
85 //#define REPLAY
86 #ifdef REPLAY
87 game.switchstage.mapno = START_REPLAY;
88 //Replay::set_ffwd(6000);
89 //Replay::set_stopat(3500);
90 game.switchstage.param = 1;
91 #else
92 //game.switchstage.mapno = LOAD_GAME;
93 //game.pause(GP_OPTIONS);
95 if (settings->skip_intro && file_exists(GetProfileName(settings->last_save_slot)))
96 game.switchstage.mapno = LOAD_GAME;
97 else
98 game.setmode(GM_INTRO);
99 #endif
101 // for debug
102 if (game.paused) { game.switchstage.mapno = 0; game.switchstage.eventonentry = 0; }
103 if (game.switchstage.mapno == LOAD_GAME) inhibit_loadfade = true;
105 game.running = true;
106 freshstart = true;
108 stat("Entering main loop...");
109 #ifdef __SDLSHIM__
110 set_console_visible(false);
111 #endif
113 //speed_test();
114 //return 1;
116 while(game.running)
118 // SSS/SPS persists across stage transitions until explicitly
119 // stopped, or you die & reload. It seems a bit risky to me,
120 // but that's the spec.
121 if (game.switchstage.mapno >= MAPNO_SPECIALS)
123 StopLoopSounds();
126 // enter next stage, whatever it may be
127 if (game.switchstage.mapno == LOAD_GAME || \
128 game.switchstage.mapno == LOAD_GAME_FROM_MENU)
130 if (game.switchstage.mapno == LOAD_GAME_FROM_MENU)
131 freshstart = true;
133 stat("= Loading game =");
134 if (game_load(settings->last_save_slot))
136 fatal("savefile error");
137 goto ingame_error;
140 Replay::OnGameStarting();
142 if (!inhibit_loadfade) fade.Start(FADE_IN, FADE_CENTER);
143 else inhibit_loadfade = false;
145 else if (game.switchstage.mapno == START_REPLAY)
147 stat(">> beginning replay '%s'", GetReplayName(game.switchstage.param));
149 StopScripts();
150 if (Replay::begin_playback(GetReplayName(game.switchstage.param)))
152 fatal("error starting playback");
153 goto ingame_error;
156 else
158 if (game.switchstage.mapno == NEW_GAME || \
159 game.switchstage.mapno == NEW_GAME_FROM_MENU)
161 bool show_intro = (game.switchstage.mapno == NEW_GAME_FROM_MENU);
162 InitNewGame(show_intro);
165 // slide weapon bar on first intro to Start Point
166 if (game.switchstage.mapno == STAGE_START_POINT && \
167 game.switchstage.eventonentry == 91)
169 freshstart = true;
172 // switch maps
173 if (load_stage(game.switchstage.mapno)) goto ingame_error;
175 player->x = (game.switchstage.playerx * TILE_W) << CSF;
176 player->y = (game.switchstage.playery * TILE_H) << CSF;
179 // start the level
180 if (game.initlevel()) return 1;
182 if (freshstart)
183 weapon_introslide();
185 gameloop();
186 game.stageboss.OnMapExit();
187 freshstart = false;
190 shutdown: ;
191 Replay::close();
192 game.close();
193 Carets::close();
195 Graphics::close();
196 input_close();
197 font_close();
198 sound_close();
199 tsc_close();
200 textbox.Deinit();
201 return error;
203 ingame_error: ;
204 stat("");
205 stat(" ************************************************");
206 stat(" * An in-game error occurred. Game shutting down.");
207 stat(" ************************************************");
208 error = true;
209 goto shutdown;
213 void gameloop(void)
215 int32_t nexttick = 0;
217 game.switchstage.mapno = -1;
219 while(game.running && game.switchstage.mapno < 0)
221 // get time until next tick
222 int32_t curtime = SDL_GetTicks();
223 int32_t timeRemaining = nexttick - curtime;
225 if (timeRemaining <= 0 || game.ffwdtime)
227 run_tick();
229 // try to "catch up" if something else on the system bogs us down for a moment.
230 // but if we get really far behind, it's ok to start dropping frames
231 if (game.ffwdtime)
232 game.ffwdtime--;
234 nexttick = curtime + GAME_WAIT;
236 // pause game if window minimized
237 if ((SDL_GetAppState() & VISFLAGS) != VISFLAGS)
239 AppMinimized();
240 nexttick = 0;
243 else
245 // don't needlessly hog CPU, but don't sleep for entire
246 // time left, some CPU's/kernels will fall asleep for
247 // too long and cause us to run slower than we should
248 timeRemaining /= 2;
249 if (timeRemaining)
250 SDL_Delay(timeRemaining);
255 static inline void run_tick()
257 static bool can_tick = true;
258 static bool last_freezekey = false;
259 static bool last_framekey = false;
260 static int frameskip = 0;
262 input_poll();
264 // input handling for a few global things
265 if (justpushed(ESCKEY))
267 if (settings->instant_quit)
269 game.running = false;
271 else if (!game.paused) // no pause from Options
273 game.pause(GP_PAUSED);
276 else if (justpushed(F3KEY))
278 game.pause(GP_OPTIONS);
281 // freeze frame
282 if (settings->enable_debug_keys)
284 if (inputs[FREEZE_FRAME_KEY] && !last_freezekey)
286 can_tick = true;
287 freezeframe ^= 1;
288 framecount = 0;
291 if (inputs[FRAME_ADVANCE_KEY] && !last_framekey)
293 can_tick = true;
294 if (!freezeframe)
296 freezeframe = 1;
297 framecount = 0;
301 last_freezekey = inputs[FREEZE_FRAME_KEY];
302 last_framekey = inputs[FRAME_ADVANCE_KEY];
305 // fast-forward key (F5)
306 if (inputs[FFWDKEY] && (settings->enable_debug_keys || Replay::IsPlaying()))
308 game.ffwdtime = 2;
311 if (can_tick)
313 game.tick();
315 if (freezeframe)
317 char buf[1024];
318 sprintf(buf, "[] Tick %d", framecount++);
319 font_draw_shaded(4, (SCREEN_HEIGHT-GetFontHeight()-4), buf, 0, &greenfont);
320 can_tick = false;
322 else
324 Replay::DrawStatus();
327 if (settings->show_fps)
329 update_fps();
332 if (!flipacceltime)
334 //platform_sync_to_vblank();
335 screen->Flip();
337 else
339 flipacceltime--;
340 if (--frameskip < 0)
342 screen->Flip();
343 frameskip = 256;
347 memcpy(lastinputs, inputs, sizeof(lastinputs));
349 else
350 { // frame is frozen; don't hog CPU
351 SDL_Delay(20);
354 // immediately after a game tick is when we have the most amount of time before
355 // the game needs to run again. so now's as good a time as any for some
356 // BGM audio processing, wouldn't you say?
357 org_run();
360 void update_fps()
362 fps_so_far++;
364 if ((SDL_GetTicks() - fpstimer) >= 500)
366 fpstimer = SDL_GetTicks();
367 fps = (fps_so_far << 1);
368 fps_so_far = 0;
371 char fpstext[64];
372 sprintf(fpstext, "%d fps", fps);
374 int x = (SCREEN_WIDTH - 4) - GetFontWidth(fpstext, 0, true);
375 font_draw_shaded(x, 4, fpstext, 0, &greenfont);
379 void InitNewGame(bool with_intro)
381 stat("= Beginning new game =");
383 memset(game.flags, 0, sizeof(game.flags));
384 memset(game.skipflags, 0, sizeof(game.skipflags));
385 textbox.StageSelect.ClearSlots();
387 game.quaketime = game.megaquaketime = 0;
388 game.showmapnametime = 0;
389 game.debug.god = 0;
390 game.running = true;
391 game.frozen = false;
393 // fully re-init the player object
394 Objects::DestroyAll(true);
395 game.createplayer();
397 player->maxHealth = 3;
398 player->hp = player->maxHealth;
400 game.switchstage.mapno = STAGE_START_POINT;
401 game.switchstage.playerx = 10;
402 game.switchstage.playery = 8;
403 game.switchstage.eventonentry = (with_intro) ? 200 : 91;
405 fade.set_full(FADE_OUT);
409 void AppMinimized(void)
411 stat("Game minimized or lost focus--pausing...");
412 SDL_PauseAudio(1);
414 for(;;)
416 if ((SDL_GetAppState() & VISFLAGS) == VISFLAGS)
418 break;
421 input_poll();
422 SDL_Delay(20);
425 SDL_PauseAudio(0);
426 stat("Focus regained, resuming play...");
431 void c------------------------------() {}
434 static void fatal(const char *str)
436 staterr("fatal: '%s'", str);
438 if (!safemode::init())
440 safemode::moveto(SM_UPPER_THIRD);
441 safemode::print("Fatal Error");
443 safemode::moveto(SM_CENTER);
444 safemode::print("%s", str);
446 safemode::run_until_key();
447 safemode::close();
451 static bool check_data_exists()
453 char fname[MAXPATHLEN];
455 sprintf(fname, "%s/npc.tbl", data_dir);
456 if (file_exists(fname)) return 0;
458 if (!safemode::init())
460 safemode::moveto(SM_UPPER_THIRD);
461 safemode::print("Fatal Error");
463 safemode::moveto(SM_CENTER);
464 safemode::print("Missing \"%s\" directory.", data_dir);
465 safemode::print("Please copy it over from a Doukutsu installation.");
467 safemode::run_until_key();
468 safemode::close();
471 return 1;
474 void visible_warning(const char *fmt, ...)
476 va_list ar;
477 char buffer[80];
479 va_start(ar, fmt);
480 vsnprintf(buffer, sizeof(buffer), fmt, ar);
481 va_end(ar);
483 console.Print(buffer);
487 void c------------------------------() {}
490 #ifdef __SDLSHIM__
492 void speed_test(void)
494 SDL_Rect textrect;
496 uint32_t end = 0;
497 fps = 0;
499 ClearScreen(255, 0, 0);
501 game.running = true;
502 while(game.running)
504 if (SDL_GetTicks() >= end)
506 SDLS_VRAMSurface->h = 320;
507 SDLS_VRAMSurface->cliprect.h = 320;
508 textrect.x = 5;
509 textrect.y = 250;
510 textrect.w = 100;
511 textrect.h = 10;
512 SDL_FillRect(SDLS_VRAMSurface, &textrect, 0);
514 char buffer[80];
515 sprintf(buffer, "%d fps ", fps);
516 direct_text_draw(5, 250, buffer);
517 SDLS_VRAMSurface->h = 240;
518 SDLS_VRAMSurface->cliprect.h = 240;
520 input_poll();
522 fps = 0;
523 end = SDL_GetTicks() + 1000;
526 screen->Flip();
527 fps++;
531 #else
533 void speed_test(void)
535 SDL_Rect textrect;
536 SDL_Surface *vram = screen->GetSDLSurface();
537 int click = 0;
539 uint32_t end = 0;
540 fps = 0;
542 SDL_FillRect(vram, NULL, SDL_MapRGB(vram->format, 255, 0, 0));
543 int c = 0;
545 game.running = true;
546 while(game.running)
548 //SDL_FillRect(vram, NULL, c ^= 255);
550 if (SDL_GetTicks() >= end)
552 stat("%d fps", fps);
553 fps = 0;
554 end = SDL_GetTicks() + 1000;
556 if (++click > 3)
557 break;
560 screen->Flip();
561 fps++;
565 #endif
569 void org_test_miniloop(void)
571 uint32_t start = 0, curtime;
572 uint32_t counter;
574 stat("Starting org test");
576 font_draw(5, 5, "ORG test in progress...", 0, &greenfont);
577 font_draw(5, 15, "Logging statistics to nx.log", 0, &greenfont);
578 font_draw(5, 25, "Press any button to quit", 0, &greenfont);
579 screen->Flip();
581 music_set_enabled(1);
582 music(32);
584 last_sdl_key = -1;
586 for(;;)
588 org_run();
590 if (++counter > 1024)
592 counter = 0;
594 curtime = SDL_GetTicks();
595 if ((curtime - start) >= 100)
597 start = curtime;
598 input_poll();
600 if (last_sdl_key != -1)
601 return;
607 void SDL_Delay(int ms)
609 usleep(ms * 1000);