Add support for displaying level names and authors
[numtypysics.git] / App.cpp
blobeb12b3e12cab5bd17a4df3df35c46480500dfe89
1 /*
2 * This file is part of NumptyPhysics
3 * Copyright (C) 2008 Tim Edmonds
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
17 #include "Common.h"
18 #include "Array.h"
19 #include "Config.h"
20 #include "Game.h"
21 #include "Scene.h"
22 #include "Levels.h"
23 #include "Canvas.h"
24 #include "Ui.h"
26 #include "PythonInput.h"
28 #include <cstdio>
29 #include <string>
30 #include <sys/stat.h>
31 #include <SDL/SDL.h>
34 class App : private WidgetParent
36 int m_width;
37 int m_height;
38 bool m_rotate;
39 bool m_fullscreen;
40 bool m_thumbnailMode;
41 bool m_quit;
42 Array<const char*> m_files;
43 Array<Widget *> m_layers;
44 Window *m_window;
45 #ifdef HAVE_PYTHON
46 PythonInput *m_python_input;
47 #endif
48 public:
49 App(int argc, char** argv)
50 : m_width(SCREEN_WIDTH),
51 m_height(SCREEN_HEIGHT),
52 m_rotate(false),
53 m_fullscreen(false),
54 m_thumbnailMode(false),
55 m_quit(false),
56 m_window(NULL)
57 #ifdef HAVE_PYTHON
58 , m_python_input(NULL)
59 #endif
61 for ( int i=1; i<argc; i++ ) {
62 if ( strcmp(argv[i],"-bmp")==0 ) {
63 m_thumbnailMode = true;
64 } else if ( strcmp(argv[i],"-rotate")==0 ) {
65 m_rotate = true;
66 } else if ( strcmp(argv[i],"-fullscreen")==0 ) {
67 m_fullscreen = true;
68 } else if ( strcmp(argv[i],"-geometry")==0 && i<argc-1) {
69 int ww, hh;
70 if ( sscanf(argv[i+1],"%dx%d",&ww,&hh) ==2 ) {
71 m_width = ww;
72 m_height = hh;
74 i++;
75 } else {
76 m_files.append( argv[i] );
79 init();
82 ~App()
84 delete m_window;
85 #ifdef HAVE_PYTHON
86 delete m_python_input;
87 #endif
90 void run()
92 if ( m_thumbnailMode ) {
93 for ( int i=0; i<m_files.size(); i++ ) {
94 renderThumbnail( m_files[i], m_width, m_height );
96 } else {
97 m_window = new Window(m_width,m_height,"Numpty Physics","NPhysics", m_fullscreen),
98 runGame( m_files, m_width, m_height );
102 private:
104 void init()
106 if ( m_thumbnailMode ) {
107 putenv((char*)"SDL_VIDEODRIVER=dummy");
108 } else {
109 putenv((char*)"SDL_VIDEO_X11_WMCLASS=NPhysics");
111 if ( mkdir( (std::string(getenv("HOME"))+"/"USER_BASE_PATH).c_str(),
112 0755)!=0 ) {
113 fprintf(stderr,"failed to create user dir\n");
117 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
118 throw "Couldn't initialize SDL";
121 #ifdef HAVE_PYTHON
122 m_python_input = new PythonInput(m_width, m_height);
123 #endif
127 void renderThumbnail( const char* file, int width, int height )
129 configureScreenTransform( width, height );
130 Scene scene( true );
131 if ( scene.load( file ) ) {
132 printf("generating bmp %s\n", file);
133 Canvas temp( width, height );
134 scene.draw( temp, FULLSCREEN_RECT );
135 std::string bmp( file );
136 bmp += ".bmp";
137 temp.writeBMP( bmp.c_str() );
142 void add( Widget* w )
144 m_layers.append( w );
145 w->setParent( this );
148 void remove( Widget* w )
150 if ( m_layers.indexOf( w ) >= 0 ) {
151 m_layers.erase( m_layers.indexOf( w ) );
152 w->setParent( NULL );
157 void runGame( Array<const char*>& files, int width, int height )
159 Levels* levels = new Levels();
161 if ( files.size() > 0 ) {
162 for ( int i=0; i<files.size(); i++ ) {
163 levels->addPath( files[i] );
165 } else {
166 struct stat st;
167 if ( stat("Game.cpp",&st)==0 ) {
168 levels->addPath( "data" );
169 } else {
170 levels->addPath( DEFAULT_LEVEL_PATH );
172 levels->addPath( Config::userDataDir().c_str() );
175 add( createGameLayer( levels, width, height ) );
176 mainLoop();
179 void renderLayers()
181 Rect area = m_layers[0]->dirtyArea();
183 for ( int i=1; i<m_layers.size(); i++ ) {
184 Rect dirt = m_layers[i]->dirtyArea();
185 if ( !dirt.isEmpty() ) {
186 if ( area.isEmpty() ) {
187 area = dirt;
188 } else {
189 area.expand( dirt );
193 for ( int i=0; i<m_layers.size(); i++ ) {
194 m_layers[i]->draw( *m_window, area );
196 //m_window->drawRect( area, 0x00ff00, false );
197 m_window->update( area );
201 bool handleGameEvent( SDL_Event &ev )
203 switch( ev.type ) {
204 case SDL_QUIT:
205 m_quit = true;
206 return true;
207 case SDL_KEYDOWN:
208 if ( ev.key.keysym.sym == SDLK_q ) {
209 m_quit = true;
210 return true;
213 return false;
216 void dispatchEvents( int lastTick )
218 for ( int i=0; i<m_layers.size(); i++ ) {
219 m_layers[i]->onTick( lastTick );
222 SDL_Event ev;
223 while ( SDL_PollEvent(&ev) ) {
224 if ( !handleGameEvent(ev) ) {
225 for ( int i=m_layers.size()-1; i>=0; i-- ) {
226 if ( m_layers[i]->handleEvent(ev) ) {
227 break;
234 void mainLoop()
236 renderLayers();
238 int renderRate = (MIN_RENDER_RATE+MAX_RENDER_RATE)/2;
239 int iterationRate = ITERATION_RATE;
240 int iterateCounter = 0;
241 int lastTick = SDL_GetTicks();
242 bool isComplete = false;
244 while ( !m_quit ) {
246 //assumes RENDER_RATE <= ITERATION_RATE
247 //TODO dynamic tick scaling for improved sleep
248 while ( iterateCounter < iterationRate ) {
249 dispatchEvents( lastTick );
250 if ( m_quit ) return;
251 iterateCounter += renderRate;
253 iterateCounter -= iterationRate;
255 renderLayers();
257 int sleepMs = lastTick + 1000/renderRate - SDL_GetTicks();
259 if ( sleepMs > 1 && renderRate < MAX_RENDER_RATE ) {
260 renderRate++;
261 // printf("increasing render rate to %dfps\n",renderRate);
262 sleepMs = lastTick + 1000/renderRate - SDL_GetTicks();
265 if ( sleepMs > 0 ) {
266 SDL_Delay( sleepMs );
267 } else {
268 // printf("overrun %dms\n",-sleepMs);
269 if ( renderRate > MIN_RENDER_RATE ) {
270 renderRate--;
271 // printf("decreasing render rate to %dfps\n",renderRate);
272 } else if ( iterationRate > 30 ) {
273 //slow down simulation time to maintain fps??
276 lastTick = SDL_GetTicks();
286 int npmain(int argc, char** argv)
288 try {
289 App app(argc,argv);
290 app.run();
291 } catch ( const char* e ) {
292 fprintf(stderr,"*** CAUGHT: %s",e);
294 return 0;