1
0
forked from 0ad/0ad
0ad/source/main.cpp
janwas dc027002c0 host of changes to accommodate Atlas:
- input.cpp allows feeding in events from the outside (will be used by
main.cpp and Atlas msg loop)
- most of main.cpp split into ps/GameSetup.*
- main.cpp is now a "controller" that drives the game via SDL events
(Atlas is another such controller that can override main.cpp). it calls
GameSetup functions to set up and processes messages.
- add functions to import AtlasUI.dll automatically

also, the usual refactoring ;)

This was SVN commit r2622.
2005-08-14 23:34:37 +00:00

371 lines
7.8 KiB
C++
Executable File

/*
This module drives the game when running without Atlas (our integrated
map editor). It receives input and OS messages via SDL and feeds them
into the input dispatcher, where they are passed on to the game GUI and
simulation.
It also contains main(), which either runs the above controller or
that of Atlas depending on commandline parameters.
*/
#include "precompiled.h"
#ifdef SCED
# include "ui/StdAfx.h"
# undef ERROR
#endif // SCED
#include "lib/input.h"
#include "lib/sdl.h"
#include "lib/timer.h"
#include "lib/res/file/vfs.h"
#include "lib/res/sound/snd.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/Config.h"
#include "ps/Loader.h"
#include "gui/GUI.h"
#include "ps/CConsole.h"
#include "ps/Profile.h"
#include "ps/System.h"
#include "ps/Game.h"
#include "ps/Hotkey.h"
#include "ps/Interact.h"
#include "ps/Network/SessionManager.h"
#include "simulation/Scheduler.h"
#include "sound/CMusicPlayer.h"
#define LOG_CATEGORY "main"
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void kill_mainloop();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// main app message handler
//
bool keys[SDLK_LAST];
bool mouse_buttons[5]; // CAN REMOVE AFTER MOVING RESET TO WSDL
bool g_active = true;
int g_mouse_x = 50, g_mouse_y = 50;
static int MainInputHandler(const SDL_Event* ev)
{
int c;
switch(ev->type)
{
case SDL_ACTIVEEVENT:
g_active = (ev->active.gain != 0);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
c = ev->key.keysym.sym;
keys[c] = (ev->type == SDL_KEYDOWN);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
c = ev->button.button;
if(c < ARRAY_SIZE(mouse_buttons))
mouse_buttons[c] = (ev->type == SDL_MOUSEBUTTONDOWN);
else
debug_warn("MainInputHandler: invalid mouse button");
break;
case SDL_MOUSEMOTION:
g_mouse_x = ev->motion.x;
g_mouse_y = ev->motion.y;
break;
case SDL_HOTKEYDOWN:
switch(ev->user.code)
{
case HOTKEY_EXIT:
kill_mainloop();
break;
case HOTKEY_SCREENSHOT:
WriteScreenshot("png");
break;
case HOTKEY_PLAYMUSIC:
break;
default:
return EV_PASS;
}
return EV_HANDLED;
}
return EV_PASS;
}
// dispatch all pending events to the various receivers.
static void PumpEvents()
{
in_dispatch_recorded_events();
SDL_Event event;
while(SDL_PollEvent(&event))
in_dispatch_event(&event);
}
static int ProgressiveLoad()
{
wchar_t description[100];
int progress_percent;
int ret = LDR_ProgressiveLoad(10e-3, description, ARRAY_SIZE(description), &progress_percent);
switch(ret)
{
// no load active => no-op (skip code below)
case 0:
return 0;
// current task didn't complete. we only care about this insofar as the
// load process is therefore not yet finished.
case ERR_TIMED_OUT:
break;
// just finished loading
case LDR_ALL_FINISHED:
g_Game->ReallyStartGame();
wcscpy_s(description, ARRAY_SIZE(description), L"Game is starting..");
// LDR_ProgressiveLoad returns L""; set to valid text to
// avoid problems in converting to JSString
break;
// error!
default:
CHECK_ERR(ret);
// can't do this above due to legit ERR_TIMED_OUT
break;
}
GUI_DisplayLoadProgress(progress_percent, description);
return 0;
}
CMusicPlayer music_player;
static void Frame()
{
MICROLOG(L"Frame");
oglCheck();
PROFILE_START( "update music" );
music_player.update();
PROFILE_END( "update music" );
static double last_time;
const double time = get_time();
const float TimeSinceLastFrame = (float)(time-last_time);
last_time = time;
ONCE(return);
// first call: set last_time and return
debug_assert(TimeSinceLastFrame >= 0.0f);
PROFILE_START( "reload changed files" );
MICROLOG(L"reload files");
vfs_reload_changed_files();
PROFILE_END( "reload changed files" );
PROFILE_START( "progressive load" );
ProgressiveLoad();
PROFILE_END( "progressive load" );
PROFILE_START( "input" );
MICROLOG(L"input");
PumpEvents();
g_SessionManager.Poll();
PROFILE_END( "input" );
PROFILE_START( "gui tick" );
#ifndef NO_GUI
g_GUI.TickObjects();
#endif
PROFILE_END( "gui tick" );
PROFILE_START( "game logic" );
if (g_Game && g_Game->IsGameStarted())
{
PROFILE_START( "simulation update" );
g_Game->Update(TimeSinceLastFrame);
PROFILE_END( "simulation update" );
if (!g_FixedFrameTiming)
{
PROFILE( "camera update" );
g_Game->GetView()->Update(float(TimeSinceLastFrame));
}
PROFILE_START( "selection and interaction ui" );
// TODO Where does GameView end and other things begin?
g_Mouseover.update( TimeSinceLastFrame );
g_Selection.update();
PROFILE_END( "selection and interaction ui" );
PROFILE_START( "sound update" );
CCamera* camera = g_Game->GetView()->GetCamera();
CMatrix3D& orientation = camera->m_Orientation;
float* pos = &orientation._data[12];
float* dir = &orientation._data[8];
float* up = &orientation._data[4];
if(snd_update(pos, dir, up) < 0)
debug_printf("snd_update failed\n");
PROFILE_END( "sound update" );
}
else
{
// CSimulation would do this with the proper turn length if we were in
// a game. This is basically just to keep script timers running.
uint ms_elapsed = (uint)(TimeSinceLastFrame*1000);
g_Scheduler.update(ms_elapsed);
if(snd_update(0, 0, 0) < 0)
debug_printf("snd_update (pos=0 version) failed\n");
}
PROFILE_END( "game logic" );
PROFILE_START( "update console" );
g_Console->Update(TimeSinceLastFrame);
PROFILE_END( "update console" );
// TODO: ugly, but necessary. these are one-shot events, have to be reset.
// Spoof mousebuttonup events for the hotkey system
SDL_Event spoof;
spoof.type = SDL_MOUSEBUTTONUP;
spoof.button.button = SDL_BUTTON_WHEELUP;
if( mouse_buttons[SDL_BUTTON_WHEELUP] )
hotkeyInputHandler( &spoof );
spoof.button.button = SDL_BUTTON_WHEELDOWN;
if( mouse_buttons[SDL_BUTTON_WHEELDOWN] )
hotkeyInputHandler( &spoof );
mouse_buttons[SDL_BUTTON_WHEELUP] = false;
mouse_buttons[SDL_BUTTON_WHEELDOWN] = false;
PROFILE_START( "render" );
if(g_active)
{
MICROLOG(L"render");
Render();
MICROLOG(L"finished render");
PROFILE_START( "swap buffers" );
SDL_GL_SwapBuffers();
PROFILE_END( "swap buffers" );
}
// inactive; relinquish CPU for a little while
// don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored
else
SDL_Delay(10);
PROFILE_END( "render" );
g_Profiler.Frame();
calc_fps();
if(g_FixedFrameTiming && frameCount==100)
kill_mainloop();
}
static void MainControllerInit()
{
// add additional input handlers only needed by this controller.
// gui_handler needs to be after (i.e. called before!) the hotkey handler
// so that input boxes can be typed in without setting off hotkeys.
#ifndef NO_GUI
in_add_handler(gui_handler);
#endif
// must be after gui_handler. Should mayhap even be last.
in_add_handler(MainInputHandler);
}
static void MainControllerShutdown()
{
music_player.release();
}
#ifndef SCED
static bool quit = false; // break out of main loop
// HACK: Let code from other files (i.e. the scripting system) quit
void kill_mainloop()
{
quit = true;
}
int main(int argc, char* argv[])
{
debug_printf("MAIN &argc=%p &argv=%p\n", &argc, &argv);
ATLAS_RunIfOnCmdLine(argc, argv);
// ELSE
Init(argc, argv, true);
MainControllerInit();
while(!quit)
Frame();
Shutdown();
MainControllerShutdown();
exit(0);
}
// Public functions for Atlas to use:
// TODO: Make this far less hacky
void Init_(int argc, char** argv, bool setup_gfx)
{
g_Quickstart = true;
Init(argc, argv, setup_gfx);
}
void Shutdown_()
{
Shutdown();
}
void Render_()
{
Render();
}
#else // SCED:
void ScEd_Init()
{
g_Quickstart = true;
Init(0, NULL, false);
}
void ScEd_Shutdown()
{
Shutdown();
}
#endif // SCED