NXEngine v1.0.0.2
[NXEngine.git] / main.cpp
blobb8d6f25ff66bdc9347f21eff6c8780bfb8938e80
2 #include <stdarg.h>
3 #include "nx.h"
4 #include "graphics/safemode.h"
5 #include "main.fdh"
7 const char *data_dir = "data";
8 const char *stage_dir = "data/Stage";
9 const char *pic_dir = "endpic";
10 const char *nxdata_dir = ".";
12 int fps = 0;
13 static int fps_so_far = 0;
14 static uint32_t fpstimer = 0;
16 #define GAME_WAIT (1000/GAME_FPS) // sets framerate
17 int framecount = 0;
18 bool freezeframe = false;
19 int flipacceltime = 0;
21 int main(int argc, char *argv[])
23 bool inhibit_loadfade = false;
24 bool error = false;
25 bool freshstart;
27 SetLogFilename("debug.txt");
28 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
30 staterr("ack, sdl_init failed: %s.", SDL_GetError());
31 return 1;
33 atexit(SDL_Quit);
35 // start up inputs first thing because settings_load may remap them
36 input_init();
38 // load settings, or at least get the defaults,
39 // so we know the initial screen resolution.
40 settings_load();
42 if (Graphics::init(settings->resolution)) { staterr("Failed to initilize graphics."); return 1; }
43 if (font_init()) { staterr("Failed to load font."); return 1; }
45 if (!settings->files_extracted)
47 if (extract_main())
49 Graphics::close();
50 font_close();
51 return 0;
53 else
55 settings->files_extracted = true;
56 settings_save();
60 if (check_data_exists())
62 return 1;
65 //Graphics::ShowLoadingScreen();
66 if (sound_init()) { fatal("Failed to initilize sound."); return 1; }
67 if (trig_init()) { fatal("Failed trig module init."); return 1; }
69 if (tsc_init()) { fatal("Failed to initilize script engine."); return 1; }
70 if (textbox.Init()) { fatal("Failed to initialize textboxes."); return 1; }
71 if (Carets::init()) { fatal("Failed to initialize carets."); return 1; }
73 if (game.init()) return 1;
74 game.setmode(GM_NORMAL);
75 // set null stage just to have something to do while we go to intro
76 game.switchstage.mapno = 0;
78 //#define REPLAY
79 #ifdef REPLAY
80 game.switchstage.mapno = START_REPLAY;
81 Replay::set_ffwd(23400);
82 //Replay::set_stopat(3500);
83 #else
84 //game.switchstage.mapno = LOAD_GAME;
85 //game.pause(GP_OPTIONS);
87 if (settings->skip_intro && file_exists(GetProfileName(settings->last_save_slot)))
88 game.switchstage.mapno = LOAD_GAME;
89 else
90 game.setmode(GM_INTRO);
91 #endif
93 // for debug
94 if (game.paused) { game.switchstage.mapno = 0; game.switchstage.eventonentry = 0; }
95 if (game.switchstage.mapno == LOAD_GAME) inhibit_loadfade = true;
97 game.running = true;
98 freshstart = true;
99 while(game.running)
101 // SSS/SPS persists across stage transitions until explicitly
102 // stopped, or you die & reload. It seems a bit risky to me,
103 // but that's the spec.
104 if (game.switchstage.mapno >= MAPNO_SPECIALS)
106 StopLoopSounds();
109 // enter next stage, whatever it may be
110 if (game.switchstage.mapno == LOAD_GAME || \
111 game.switchstage.mapno == LOAD_GAME_FROM_MENU)
113 if (game.switchstage.mapno == LOAD_GAME_FROM_MENU)
114 freshstart = true;
116 stat("= Loading game =");
117 if (game_load(settings->last_save_slot))
119 fatal("savefile error");
120 goto ingame_error;
123 Replay::OnGameStarting();
125 if (!inhibit_loadfade) fade.Start(FADE_IN, FADE_CENTER);
126 else inhibit_loadfade = false;
128 else if (game.switchstage.mapno == START_REPLAY)
130 stat(">> beginning replay '%s'", GetReplayName(game.switchstage.param));
132 StopScripts();
133 if (Replay::begin_playback(GetReplayName(game.switchstage.param)))
135 fatal("error starting playback");
136 goto ingame_error;
139 else
141 if (game.switchstage.mapno == NEW_GAME || \
142 game.switchstage.mapno == NEW_GAME_FROM_MENU)
144 bool show_intro = (game.switchstage.mapno == NEW_GAME_FROM_MENU);
145 InitNewGame(show_intro);
148 // slide weapon bar on first intro to Start Point
149 if (game.switchstage.mapno == STAGE_START_POINT && \
150 game.switchstage.eventonentry == 91)
152 freshstart = true;
155 // switch maps
156 if (load_stage(game.switchstage.mapno)) goto ingame_error;
158 player->x = (game.switchstage.playerx * TILE_W) << CSF;
159 player->y = (game.switchstage.playery * TILE_H) << CSF;
162 // start the level
163 if (game.initlevel()) return 1;
165 if (freshstart)
166 weapon_introslide();
168 gameloop();
169 game.stageboss.OnMapExit();
170 freshstart = false;
173 shutdown: ;
174 Replay::close();
175 game.close();
176 Carets::close();
178 Graphics::close();
179 input_close();
180 font_close();
181 sound_close();
182 tsc_close();
183 textbox.Deinit();
184 return error;
186 ingame_error: ;
187 stat("");
188 stat(" ************************************************");
189 stat(" * An in-game error occurred. Game shutting down.");
190 stat(" ************************************************");
191 error = true;
192 goto shutdown;
196 void gameloop(void)
198 uint32_t gametimer;
200 gametimer = -GAME_WAIT*10;
201 game.switchstage.mapno = -1;
203 while(game.running && game.switchstage.mapno < 0)
205 uint32_t curtime = SDL_GetTicks();
207 if ((curtime - gametimer >= GAME_WAIT) || game.ffwdtime)
209 run_tick();
211 // try to "catch up" if something else on the system bogs us down for a moment.
212 // but if we get really far behind, it's ok to start dropping frames
213 if (game.ffwdtime)
215 gametimer = curtime;
216 game.ffwdtime--;
218 else if ((curtime - gametimer > (GAME_WAIT * 3)))
220 gametimer = curtime;
222 else
224 gametimer += GAME_WAIT;
231 static inline void run_tick()
233 static bool can_tick = true;
234 static bool last_freezekey = false;
235 static bool last_framekey = false;
236 static int frameskip = 0;
238 input_poll();
240 // input handling for a few global things
241 if (justpushed(ESCKEY))
243 if (settings->instant_quit)
245 game.running = false;
247 else if (!game.paused) // no pause from Options
249 game.pause(GP_PAUSED);
252 else if (justpushed(F3KEY))
254 game.pause(GP_OPTIONS);
257 // freeze frame
258 if (settings->enable_debug_keys)
260 if (inputs[FREEZE_FRAME_KEY] && !last_freezekey)
262 can_tick = true;
263 freezeframe ^= 1;
264 framecount = 0;
267 if (inputs[FRAME_ADVANCE_KEY] && !last_framekey)
269 can_tick = true;
270 if (!freezeframe)
272 freezeframe = 1;
273 framecount = 0;
277 last_freezekey = inputs[FREEZE_FRAME_KEY];
278 last_framekey = inputs[FRAME_ADVANCE_KEY];
281 // fast-forward key (F5)
282 if (inputs[FFWDKEY] && (settings->enable_debug_keys || Replay::IsPlaying()))
284 game.ffwdtime = 2;
287 if (can_tick)
289 game.tick();
291 if (freezeframe)
293 char buf[1024];
294 sprintf(buf, "[] Tick %d", framecount++);
295 font_draw_shaded(4, (SCREEN_HEIGHT-GetFontHeight()-4), buf, 0, &greenfont);
296 can_tick = false;
298 else
300 Replay::DrawStatus();
303 if (settings->show_fps)
305 update_fps();
308 if (!flipacceltime)
310 screen->Flip();
312 else
314 flipacceltime--;
315 if (--frameskip < 0)
317 screen->Flip();
318 frameskip = 256;
322 memcpy(lastinputs, inputs, sizeof(lastinputs));
324 else
325 { // frame is frozen; don't hog CPU
326 SDL_Delay(20);
329 // immediately after a game tick is when we have the most amount of time before
330 // the game needs to run again. so now's as good a time as any for some
331 // BGM audio processing, wouldn't you say?
332 org_run();
335 void update_fps()
337 fps_so_far++;
339 if ((SDL_GetTicks() - fpstimer) >= 500)
341 fpstimer = SDL_GetTicks();
342 fps = (fps_so_far << 1);
343 fps_so_far = 0;
346 char fpstext[64];
347 sprintf(fpstext, "%d fps", fps);
349 int x = (SCREEN_WIDTH - 4) - GetFontWidth(fpstext, 0, true);
350 font_draw_shaded(x, 4, fpstext, 0, &greenfont);
354 void InitNewGame(bool with_intro)
356 stat("= Beginning new game =");
358 memset(game.flags, 0, sizeof(game.flags));
359 memset(game.skipflags, 0, sizeof(game.skipflags));
360 textbox.StageSelect.ClearSlots();
362 game.quaketime = game.megaquaketime = 0;
363 game.showmapnametime = 0;
364 game.debug.god = 0;
365 game.running = true;
366 game.frozen = false;
368 // fully re-init the player object
369 Objects::DestroyAll(true);
370 game.createplayer();
372 player->maxHealth = 3;
373 player->hp = player->maxHealth;
375 game.switchstage.mapno = STAGE_START_POINT;
376 game.switchstage.playerx = 10;
377 game.switchstage.playery = 8;
378 game.switchstage.eventonentry = (with_intro) ? 200 : 91;
380 fade.set_full(FADE_OUT);
384 void c------------------------------() {}
387 static void fatal(const char *str)
389 staterr("fatal: '%s'", str);
391 if (!safemode::init())
393 safemode::moveto(SM_UPPER_THIRD);
394 safemode::print("Fatal Error");
396 safemode::moveto(SM_CENTER);
397 safemode::print("%s", str);
399 safemode::run_until_key();
400 safemode::close();
404 static bool check_data_exists()
406 char fname[MAXPATHLEN];
408 sprintf(fname, "%s/npc.tbl", data_dir);
409 if (file_exists(fname)) return 0;
411 if (!safemode::init())
413 safemode::moveto(SM_UPPER_THIRD);
414 safemode::print("Fatal Error");
416 safemode::moveto(SM_CENTER);
417 safemode::print("Missing \"%s\" directory.", data_dir);
418 safemode::print("Please copy it over from a Doukutsu installation.");
420 safemode::run_until_key();
421 safemode::close();
424 return 1;
427 void visible_warning(const char *fmt, ...)
429 va_list ar;
430 char buffer[80];
432 va_start(ar, fmt);
433 vsnprintf(buffer, sizeof(buffer), fmt, ar);
434 va_end(ar);
436 console.Print(buffer);