New levels, re-structuring for testing
[numtypysics.git] / Game.cpp
blob7949d53ecf6ee02e0cb65fb088cf67a9f3d314a1
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 "Overlay.h"
22 #include "Path.h"
23 #include "Canvas.h"
24 #include "Font.h"
25 #include "Levels.h"
26 #include "Http.h"
27 #include "Os.h"
28 #include "Scene.h"
30 #include "Multitouch.h"
32 #include <SDL/SDL.h>
33 #include <SDL/SDL_image.h>
35 #include <cstdio>
36 #include <iostream>
37 #include <sstream>
38 #include <fstream>
39 #include <memory.h>
40 #include <errno.h>
41 #include <sys/stat.h>
43 using namespace std;
45 unsigned char levelbuf[64*1024];
49 struct DemoEntry {
50 DemoEntry( int _t, int _o, SDL_Event& _e ) : t(_t), o(_o), e(_e) {}
51 int t,o;
52 SDL_Event e;
55 class DemoLog : public Array<DemoEntry>
57 public:
58 std::string asString( int i )
60 if ( i < size() ) {
61 DemoEntry& e = at(i);
62 stringstream s;
63 s << "E:" << e.t << " ";
64 switch( e.e.type ) {
65 case SDL_KEYDOWN:
66 s << "K" << e.e.key.keysym.sym;
67 break;
68 case SDL_KEYUP:
69 s << "k" << e.e.key.keysym.sym;
70 break;
71 case SDL_MOUSEBUTTONDOWN:
72 s << "B" << '0'+e.e.button.button;
73 s << "," << e.e.button.x << "," << e.e.button.y;
74 break;
75 case SDL_MOUSEBUTTONUP:
76 s << "b" << '0'+e.e.button.button;
77 s << "," << e.e.button.x << "," << e.e.button.y;
78 break;
79 case SDL_MOUSEMOTION:
80 s << "M" << e.e.button.x << "," << e.e.button.y;
81 break;
82 default:
83 return std::string();
85 return s.str();
87 return std::string();
90 void append( int tick, int offset, SDL_Event& ev )
92 Array<DemoEntry>::append( DemoEntry( tick, offset, ev ) );
95 void appendFromString( const std::string& str )
97 const char *s = str.c_str();
98 int t, o, v1, v2, v3;
99 char c;
100 SDL_Event ev = {0};
101 ev.type = 0xff;
102 if ( sscanf(s,"E:%d %c%d",&t,&c,&v1)==3 ) { //1 arg
103 switch ( c ) {
104 case 'K': ev.type = SDL_KEYDOWN; break;
105 case 'k': ev.type = SDL_KEYUP; break;
107 ev.key.keysym.sym = (SDLKey)v1;
108 } else if ( sscanf(s,"E:%d %c%d,%d",&t,&c,&v1,&v2)==4 ) { //2 args
109 switch ( c ) {
110 case 'M': ev.type = SDL_MOUSEMOTION; break;
112 ev.button.x = v1;
113 ev.button.y = v2;
114 } else if ( sscanf(s,"E:%d %c%d,%d,%d",&t,&c,&v1,&v2,&v3)==5 ) { //3 args
115 switch ( c ) {
116 case 'B': ev.type = SDL_MOUSEBUTTONDOWN; break;
117 case 'b': ev.type = SDL_MOUSEBUTTONUP; break;
119 ev.button.button = v1;
120 ev.button.x = v2;
121 ev.button.y = v3;
123 if ( ev.type != 0xff ) {
124 append( t, o, ev );
129 class DemoRecorder
131 public:
133 void start()
135 m_running = true;
136 m_log.empty();
137 m_log.capacity(512);
138 m_lastTick = 0;
139 m_lastTickTime = SDL_GetTicks();
140 m_pressed = 0;
143 void stop()
145 printf("stop recording: %d events:\n", m_log.size());
146 for ( int i=0; i<m_log.size(); i++ ) {
147 std::string e = m_log.asString(i);
148 if ( e.length() > 0 ) {
149 printf(" %s\n",e.c_str());
152 m_running = false;
155 void tick()
157 if ( m_running ) {
158 m_lastTick++;
159 m_lastTickTime = SDL_GetTicks();
163 void record( SDL_Event& ev )
165 if ( m_running ) {
166 switch( ev.type ) {
167 case SDL_MOUSEBUTTONDOWN:
168 m_pressed |= 1<<ev.button.button;
169 break;
170 case SDL_MOUSEBUTTONUP:
171 m_pressed &= ~(1<<ev.button.button);
172 break;
173 case SDL_MOUSEMOTION:
174 if ( m_pressed == 0 ) {
175 return;
178 m_log.append( m_lastTick, SDL_GetTicks()-m_lastTickTime, ev );
182 DemoLog& getLog() { return m_log; }
184 private:
185 bool m_running;
186 DemoLog m_log;
187 int m_lastTick;
188 int m_lastTickTime;
189 int m_pressed;
193 class DemoPlayer
195 public:
197 void start( const DemoLog* log )
199 m_playing = true;
200 m_log = log;
201 m_index = 0;
202 m_lastTick = 0;
203 printf("start playback: %d events\n",m_log->size());
206 bool isRunning() { return m_playing; }
208 void stop()
210 m_playing = false;
211 m_log = NULL;
214 void tick()
216 if ( m_playing ) {
217 m_lastTick++;
221 bool fetchEvent( SDL_Event& ev )
223 if ( m_playing ) {
224 if ( m_index < m_log->size()
225 && m_log->at(m_index).t <= m_lastTick ) {
226 printf("demo event at t=%d\n",m_lastTick);
227 ev = m_log->at(m_index).e;
228 m_index++;
229 return true;
232 return false;
235 private:
236 bool m_playing;
237 const DemoLog* m_log;
238 int m_index;
239 int m_lastTick;
243 class CollectionSelector : public ListProvider
245 Overlay* m_list;
246 public:
247 CollectionSelector( GameControl& game )
249 m_list = createListOverlay( game, this );
251 Overlay* overlay() { return m_list; }
253 virtual int countItems() {
254 return 58;
256 virtual Canvas* provideItem( int i, Canvas* old ) {
257 delete old;
258 char buf[18];
259 sprintf(buf,"%d. Item",i);
260 Canvas* c = new Canvas( 100, 32 );
261 c->setBackground(0xffffff);
262 c->clear();
263 Font::headingFont()->drawLeft( c, Vec2(3,3), buf, i<<3 );
264 return c;
266 virtual void releaseItem( Canvas* old ) {
267 delete old;
269 virtual void onSelection( int i, int ix, int iy ) {
270 printf("Selected: %d (%d,%d)\n",i,ix,iy);
275 struct GameStats
277 int startTime;
278 int endTime;
279 int strokeCount;
280 int pausedStrokes;
281 int undoCount;
282 void reset() {
283 startTime = SDL_GetTicks();
284 strokeCount = 0;
285 pausedStrokes = 0;
286 undoCount = 0;
290 class Game : public GameControl, public Widget
292 Scene m_scene;
293 Stroke *m_createStrokes[MT_MAX_CURSORS];
294 Stroke *m_moveStrokes[MT_MAX_CURSORS];
295 Array<Overlay*> m_overlays;
296 Window m_window;
297 Overlay *m_pauseOverlay;
298 Overlay *m_editOverlay;
299 Overlay *m_completedOverlay;
300 Overlay *m_levelnameOverlay;
301 int m_levelnameHideTime;
302 // DemoOverlay m_demoOverlay;
303 DemoRecorder m_recorder;
304 DemoPlayer m_player;
305 CollectionSelector m_cselector;
306 Os *m_os;
307 GameStats m_stats;
308 bool m_isCompleted;
309 bool m_paused;
310 public:
311 Game( Levels* levels, int width, int height, bool fullscreen )
312 : m_window(width,height,"Numpty Physics","NPhysics", fullscreen),
313 m_pauseOverlay( NULL ),
314 m_editOverlay( NULL ),
315 m_completedOverlay( NULL ),
316 m_levelnameOverlay( NULL ),
317 m_levelnameHideTime( 0 ),
318 m_isCompleted(false),
319 m_cselector( *this ),
320 m_os( Os::get() ),
321 m_paused( false )
322 //,m_demoOverlay( *this )
324 for(int n=0; n<MT_MAX_CURSORS; n++) {
325 m_createStrokes[n] = NULL;
326 m_moveStrokes[n] = NULL;
328 configureScreenTransform( m_window.width(), m_window.height() );
329 m_levels = levels;
330 gotoLevel(0);
334 virtual bool renderScene( Canvas& c, int level )
336 Scene scene( true );
337 int size = m_levels->load( level, levelbuf, sizeof(levelbuf) );
338 if ( size && scene.load( levelbuf, size ) ) {
339 scene.draw( c, FULLSCREEN_RECT );
340 return true;
342 return false;
345 void gotoLevel( int level, bool replay=false )
347 if ( level >= 0 && level < m_levels->numLevels() ) {
348 int size = m_levels->load( level, levelbuf, sizeof(levelbuf) );
349 if ( size && m_scene.load( levelbuf, size ) ) {
350 m_scene.activateAll();
351 //m_window.setSubName( file );
352 m_refresh = true;
353 if ( m_edit ) {
354 m_scene.protect(0);
356 m_recorder.stop();
357 m_player.stop();
358 if ( replay ) {
359 m_player.start( &m_recorder.getLog() );
360 } else {
361 m_recorder.start();
363 m_level = level;
364 m_stats.reset();
365 if (m_levelnameOverlay && m_levelnameHideTime) {
366 hideOverlay(m_levelnameOverlay);
367 delete m_levelnameOverlay;
368 m_levelnameHideTime = 0;
371 std::string title = m_scene.getTitle(), author = m_scene.getAuthor();
373 /* Only show title if we have at least one of (title, author) specified */
374 if (!title.empty() || !author.empty()) {
375 m_levelnameOverlay = createTextOverlay( *this,
376 ((title.empty())?(std::string("Untitled")):(title)) +
377 std::string(" by ") +
378 ((author.empty())?(std::string("Anonymous")):(author)));
379 m_levelnameHideTime = -1; /* in onTick, -1 means "show the overlay" */
386 bool save( const char *file=NULL )
388 string p;
389 if ( file ) {
390 p = file;
391 } else {
392 p = Config::userDataDir() + Os::pathSep + "L99_saved.nph";
394 if ( m_scene.save( p ) ) {
395 m_levels->addPath( p.c_str() );
396 int l = m_levels->findLevel( p.c_str() );
397 if ( l >= 0 ) {
398 m_level = l;
399 m_window.setSubName( p.c_str() );
401 return true;
403 return false;
406 bool send()
408 if ( save( SEND_TEMP_FILE ) ) {
409 Http h;
410 if ( h.post( Config::planetRoot().c_str(),
411 "upload", SEND_TEMP_FILE, "type=level" ) ) {
412 std::string id = h.getHeader("NP-Upload-Id");
413 if ( id.length() > 0 ) {
414 printf("uploaded as id %s\n",id.c_str());
415 if ( !m_os->openBrowser((Config::planetRoot()+"?level="+id).c_str()) ) {
416 showMessage("Unable to launch browser");
418 } else {
419 showMessage("UploadFailed: unknown error");
421 } else {
422 showMessage(std::string("UploadFailed: ")+h.errorMessage());
425 return false;
428 void setTool( int t )
430 m_colour = t;
433 void editMode( bool set )
435 m_edit = set;
438 void showMessage( const std::string& msg )
440 //todo
441 printf("showMessage \"%s\"\n",msg.c_str());
444 void showOverlay( Overlay* o )
446 parent()->add( o );
447 o->onShow();
450 void hideOverlay( Overlay* o )
452 parent()->remove( o );
453 o->onHide();
454 m_refresh = true;
457 void togglePause()
459 if ( !m_paused ) {
460 if ( !m_pauseOverlay ) {
461 m_pauseOverlay = createIconOverlay( *this, "pause.png", 50, 50 );
463 showOverlay( m_pauseOverlay );
464 m_paused = true;
465 } else {
466 hideOverlay( m_pauseOverlay );
467 m_paused = false;
471 bool isPaused()
473 return m_paused;
476 void edit( bool doEdit )
478 if ( m_edit != doEdit ) {
479 m_edit = doEdit;
480 if ( m_edit ) {
481 if ( !m_editOverlay ) {
482 m_editOverlay = createEditOverlay(*this);
484 showOverlay( m_editOverlay );
485 m_scene.protect(0);
486 } else {
487 hideOverlay( m_editOverlay );
488 m_strokeFixed = false;
489 m_strokeSleep = false;
490 m_strokeDecor = false;
491 if ( m_colour < 2 ) m_colour = 2;
492 m_scene.protect();
497 Vec2 cursorPoint(Uint16 x, Uint16 y)
499 Vec2 pt( x, y );
500 worldToScreen.inverseTransform( pt );
501 return pt;
504 Vec2 mousePoint( SDL_Event ev )
506 Vec2 pt( ev.button.x, ev.button.y );
507 worldToScreen.inverseTransform( pt );
508 return pt;
511 bool handleGameEvent( SDL_Event &ev )
513 switch( ev.type ) {
514 case SDL_KEYDOWN:
515 switch ( ev.key.keysym.sym ) {
516 case SDLK_SPACE:
517 case SDLK_KP_ENTER:
518 case SDLK_RETURN:
519 togglePause();
520 break;
521 case SDLK_s:
522 save();
523 break;
524 case SDLK_F4:
525 showOverlay( createMenuOverlay( *this ) );
526 break;
527 case SDLK_c:
528 showOverlay( m_cselector.overlay() );
529 break;
530 case SDLK_e:
531 case SDLK_F6:
532 edit( !m_edit );
533 break;
534 case SDLK_d:
535 //toggleOverlay( m_demoOverlay );
536 break;
537 case SDLK_r:
538 case SDLK_UP:
539 gotoLevel( m_level );
540 break;
541 case SDLK_n:
542 case SDLK_RIGHT:
543 gotoLevel( m_level+1 );
544 break;
545 case SDLK_p:
546 case SDLK_LEFT:
547 gotoLevel( m_level-1 );
548 break;
549 case SDLK_v:
550 gotoLevel( m_level, true );
551 break;
552 default:
553 break;
555 break;
556 default:
557 break;
559 return false;
562 bool handleModEvent( SDL_Event &ev )
564 static int mod=0;
565 //printf("mod=%d\n",ev.key.keysym.sym,mod);
566 switch( ev.type ) {
567 case SDL_KEYDOWN:
568 //printf("mod key=%x mod=%d\n",ev.key.keysym.sym,mod);
569 if ( ev.key.keysym.sym == SDLK_F8 ) {
570 mod = 1; //zoom- == middle (delete)
571 return true;
572 } else if ( ev.key.keysym.sym == SDLK_F7 ) {
573 mod = 2; //zoom+ == right (move)
574 return true;
576 break;
577 case SDL_KEYUP:
578 if ( ev.key.keysym.sym == SDLK_F7
579 || ev.key.keysym.sym == SDLK_F8 ) {
580 mod = 0;
581 return true;
583 break;
584 case SDL_MOUSEBUTTONDOWN:
585 case SDL_MOUSEBUTTONUP:
586 if ( ev.button.button == SDL_BUTTON_LEFT && mod != 0 ) {
587 ev.button.button = ((mod==1) ? SDL_BUTTON_MIDDLE : SDL_BUTTON_RIGHT);
589 break;
591 return false;
594 bool handlePlayEvent( SDL_Event &ev )
596 switch( ev.type ) {
597 case SDL_MOUSEBUTTONDOWN:
598 if ( ev.button.button == SDL_BUTTON_LEFT ) {
599 if (startStroke(0, ev.button.x, ev.button.y))
601 return true;
604 break;
605 case SDL_MOUSEBUTTONUP:
606 if ( ev.button.button == SDL_BUTTON_LEFT) {
607 if (finishStroke(0)) {
608 return true;
611 break;
612 case SDL_MOUSEMOTION:
613 if (extendStroke(0, ev.button.x, ev.button.y)) {
614 return true;
616 break;
617 case SDL_KEYDOWN:
618 if ( ev.key.keysym.sym == SDLK_ESCAPE ) {
619 if ( cancelDraw(0) )
621 m_refresh = true;
622 return true;
624 else if ( m_scene.deleteStroke( m_scene.strokes().at(m_scene.strokes().size()-1) ) ) {
625 m_stats.undoCount++;
626 m_refresh = true;
627 return true;
630 break;
631 default:
632 break;
634 return false;
637 bool startStroke(int index, Uint16 x, Uint16 y)
639 if (m_createStrokes[index])
641 return false;
644 int attrib = 0;
645 if ( m_strokeFixed ) {
646 attrib |= ATTRIB_GROUND;
648 if ( m_strokeSleep ) {
649 attrib |= ATTRIB_SLEEPING;
651 if ( m_strokeDecor ) {
652 attrib |= ATTRIB_DECOR;
654 m_createStrokes[index] = m_scene.newStroke( Path()&cursorPoint(x, y), m_colour, attrib );
656 return true;
659 bool extendStroke(int index, Uint16 x, Uint16 y)
661 if ( m_createStrokes[index] ) {
662 m_scene.extendStroke( m_createStrokes[index], cursorPoint(x, y) );
663 return true;
666 return false;
669 bool finishStroke(int index)
671 if (!m_createStrokes[index]) {
672 return false;
675 if ( m_scene.activate( m_createStrokes[index] ) ) {
676 m_stats.strokeCount++;
677 if ( isPaused() ) {
678 m_stats.pausedStrokes++;
680 } else {
681 m_scene.deleteStroke( m_createStrokes[index] );
683 m_createStrokes[index] = NULL;
685 return true;
688 bool cancelDraw(int index)
690 if ( m_createStrokes[index] )
692 m_scene.deleteStroke( m_createStrokes[index] );
693 m_createStrokes[index] = NULL;
694 return true;
696 return false;
699 bool handleEditEvent( SDL_Event &ev )
701 //allow move/delete in normal play!!
702 //if ( !m_edit ) return false;
704 switch( ev.type ) {
705 case SDL_MOUSEBUTTONDOWN:
706 if ( ev.button.button == SDL_BUTTON_RIGHT ) {
708 if (startMoveStroke(0, ev.button.x, ev.button.y)) {
709 return true;
712 if ( ev.button.button == SDL_BUTTON_MIDDLE ) {
713 if (deleteStroke(ev.button.x, ev.button.y)) {
714 return true;
717 break;
718 case SDL_MOUSEBUTTONUP:
719 if ( ev.button.button == SDL_BUTTON_RIGHT ) {
720 if (endMoveStroke(0)) {
721 return true;
724 break;
725 case SDL_MOUSEMOTION:
726 if (moveStroke(0, ev.button.x, ev.button.y)) {
727 return true;
729 break;
730 default:
731 break;
733 return false;
736 bool startMoveStroke(int index, Uint16 x, Uint16 y)
738 if (m_moveStrokes[index]) {
739 return false;
742 m_moveStrokes[index] = m_scene.strokeAtPoint( cursorPoint(x, y),
743 SELECT_TOLERANCE );
745 if (m_moveStrokes[index]) {
746 m_scene.setNoMass(m_moveStrokes[index]);
749 return true;
752 bool endMoveStroke(int index)
754 if (!m_moveStrokes[index]) {
755 return false;
756 } else {
757 m_scene.setDefaultMass(m_moveStrokes[index]);
761 m_moveStrokes[index] = NULL;
762 return true;
765 bool moveStroke(int index, Uint16 x, Uint16 y)
767 if (!m_moveStrokes[index]) {
768 return false;
771 m_scene.moveStroke( m_moveStrokes[index], cursorPoint(x, y) );
772 return true;
776 bool deleteStroke(Uint16 x, Uint16 y)
778 m_scene.deleteStroke( m_scene.strokeAtPoint( cursorPoint(x, y),
779 SELECT_TOLERANCE ) );
780 m_refresh = true;
784 bool handleMultitouchEvent ( SDL_Event &ev)
786 switch( ev.type ) {
787 case SDL_NP_START_STROKE: {
788 DrawEvent* de = (DrawEvent*)ev.user.data1;
789 startStroke(de->cursor_id, de->x, de->y);
790 free(de);
792 break;
794 case SDL_NP_APPEND_STROKE: {
795 DrawEvent* de = (DrawEvent*)ev.user.data1;
796 extendStroke(de->cursor_id, de->x, de->y);
797 free(de);
799 break;
801 case SDL_NP_FINISH_STROKE: {
802 DrawEvent* de = (DrawEvent*)ev.user.data1;
803 finishStroke(de->cursor_id);
804 free(de);
806 break;
808 case SDL_NP_START_ROPE:
809 break;
811 case SDL_NP_APPEND_ROPE:
812 break;
814 case SDL_NP_FINISH_ROPE:
815 break;
817 case SDL_NP_CANCEL_DRAW: {
818 CursorEvent* ce = (CursorEvent*)ev.user.data1;
819 cancelDraw(ce->cursor_id);
820 free(ce);
823 case SDL_NP_START_DRAG: {
824 DragEvent* de = (DragEvent*)ev.user.data1;
825 startMoveStroke(de->cursor_id, de->x, de->y);
826 free(de);
828 break;
830 case SDL_NP_DRAG: {
831 DragEvent* de = (DragEvent*)ev.user.data1;
832 moveStroke(de->cursor_id, de->x, de->y);
833 free(de);
835 break;
837 case SDL_NP_END_DRAG: {
838 DragEvent* de = (DragEvent*)ev.user.data1;
839 endMoveStroke(de->cursor_id);
840 free(de);
842 break;
844 case SDL_NP_PAN:
845 break;
847 case SDL_NP_ZOOM:
848 break;
850 case SDL_NP_DELETE: {
851 DeleteEvent* de = (DeleteEvent*)ev.user.data1;
852 deleteStroke(de->x, de->y);
853 free(de);
855 break;
857 case SDL_NP_PREVIEW_CURSOR: {
858 DrawEvent* de = (DrawEvent*)ev.user.data1;
859 previewCursor(de->cursor_id, de->x, de->y);
860 free(de);
864 return false;
867 bool previewCursor(int cursor_id, int x, int y)
869 m_window.previewCursor(cursor_id, x, y);
870 return true;
873 ////////////////////////////////////////////////////////////////
874 // layer interface
875 ////////////////////////////////////////////////////////////////
877 virtual bool isDirty()
879 //TODO this can be a bit heavyweight
880 return !dirtyArea().isEmpty();
883 virtual Rect dirtyArea()
885 if ( m_refresh ) {
886 m_refresh = false;
887 return FULLSCREEN_RECT;
888 } else {
889 return m_scene.dirtyArea();
893 virtual void onTick( int tick )
895 if ( !isPaused() ) {
896 m_scene.step();
897 m_recorder.tick();
898 m_player.tick();
901 if (m_levelnameHideTime == -1 && m_levelnameOverlay) {
902 showOverlay(m_levelnameOverlay);
903 m_levelnameHideTime = tick + 4000;
904 } else if (m_levelnameHideTime != 0 &&
905 m_levelnameHideTime<tick && m_levelnameOverlay) {
906 hideOverlay(m_levelnameOverlay);
907 m_levelnameHideTime = 0;
910 SDL_Event ev;
911 while ( m_player.fetchEvent(ev) ) {
912 // todo only dispatch play events?
913 handleModEvent(ev)
914 || handleGameEvent(ev)
915 || handleEditEvent(ev)
916 || handlePlayEvent(ev)
917 || handleMultitouchEvent(ev);
920 if ( m_isCompleted && m_edit ) {
921 hideOverlay( m_completedOverlay );
922 m_isCompleted = false;
924 if ( m_scene.isCompleted() != m_isCompleted && !m_edit ) {
925 m_isCompleted = m_scene.isCompleted();
926 if ( m_isCompleted ) {
927 m_stats.endTime = SDL_GetTicks();
928 m_player.stop();
929 m_recorder.stop();
930 m_completedOverlay = createNextLevelOverlay(*this, m_scene.getWinner());
931 showOverlay( m_completedOverlay );
932 } else {
933 hideOverlay( m_completedOverlay );
937 if ( m_os ) {
938 m_os->poll();
939 for ( char *f = m_os->getLaunchFile(); f; f=m_os->getLaunchFile() ) {
940 if ( strstr(f,".npz") ) {
941 //m_levels->empty();
943 m_levels->addPath( f );
944 int l = m_levels->findLevel( f );
945 if ( l >= 0 ) {
946 gotoLevel( l );
947 m_window.raise();
953 virtual void draw( Canvas& screen, const Rect& area )
955 m_scene.draw( screen, area );
956 if ( m_fade ) {
957 screen.fade( area );
961 virtual bool handleEvent( SDL_Event& ev )
963 if ( m_player.isRunning()
964 && ( ev.type==SDL_MOUSEMOTION ||
965 ev.type==SDL_MOUSEBUTTONDOWN ||
966 ev.type==SDL_MOUSEBUTTONUP ) ) {
967 return false;
968 } else if (handleModEvent(ev)
969 || handleGameEvent(ev)
970 || handleEditEvent(ev)
971 || handlePlayEvent(ev)
972 || handleMultitouchEvent(ev)) {
973 //\TODO only record edit,play events etc
974 m_recorder.record( ev );
975 return true;
977 return false;
980 virtual Window* getWindow() {
981 return &m_window;
987 Widget* createGameLayer( Levels* levels, int width, int height, bool fullscreen )
989 return new Game(levels,width,height,fullscreen);