Fix duplicate creation of window (makes fullscreen mode work on OSX)
[numtypysics.git] / Game.cpp
blob202e9ac094f851d57a3eb03a8070884938cc82aa
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 ( m_createStroke ) {
620 m_scene.deleteStroke( m_createStroke );
621 m_createStroke = NULL;
622 } else if ( m_scene.deleteStroke( m_scene.strokes().at(m_scene.strokes().size()-1) ) ) {
623 m_stats.undoCount++;
625 m_refresh = true;
626 return true;
628 break;
629 default:
630 break;
632 return false;
635 bool startStroke(int index, Uint16 x, Uint16 y)
637 if (m_createStrokes[index])
639 return false;
642 int attrib = 0;
643 if ( m_strokeFixed ) {
644 attrib |= ATTRIB_GROUND;
646 if ( m_strokeSleep ) {
647 attrib |= ATTRIB_SLEEPING;
649 if ( m_strokeDecor ) {
650 attrib |= ATTRIB_DECOR;
652 m_createStrokes[index] = m_scene.newStroke( Path()&cursorPoint(x, y), m_colour, attrib );
654 return true;
657 bool extendStroke(int index, Uint16 x, Uint16 y)
659 if ( m_createStrokes[index] ) {
660 m_scene.extendStroke( m_createStrokes[index], cursorPoint(x, y) );
661 return true;
664 return false;
667 bool finishStroke(int index)
669 if (!m_createStrokes[index]) {
670 return false;
673 if ( m_scene.activate( m_createStrokes[index] ) ) {
674 m_stats.strokeCount++;
675 if ( isPaused() ) {
676 m_stats.pausedStrokes++;
678 } else {
679 m_scene.deleteStroke( m_createStrokes[index] );
681 m_createStrokes[index] = NULL;
683 return true;
686 bool handleEditEvent( SDL_Event &ev )
688 //allow move/delete in normal play!!
689 //if ( !m_edit ) return false;
691 switch( ev.type ) {
692 case SDL_MOUSEBUTTONDOWN:
693 if ( ev.button.button == SDL_BUTTON_RIGHT ) {
695 if (startMoveStroke(0, ev.button.x, ev.button.y)) {
696 return true;
699 if ( ev.button.button == SDL_BUTTON_MIDDLE ) {
700 if (deleteStroke(ev.button.x, ev.button.y)) {
701 return true;
704 break;
705 case SDL_MOUSEBUTTONUP:
706 if ( ev.button.button == SDL_BUTTON_RIGHT ) {
707 if (endMoveStroke(0)) {
708 return true;
711 break;
712 case SDL_MOUSEMOTION:
713 if (moveStroke(0, ev.button.x, ev.button.y)) {
714 return true;
716 break;
717 default:
718 break;
720 return false;
723 bool startMoveStroke(int index, Uint16 x, Uint16 y)
725 if (m_moveStrokes[index]) {
726 return false;
729 m_moveStrokes[index] = m_scene.strokeAtPoint( cursorPoint(x, y),
730 SELECT_TOLERANCE );
732 if (m_moveStrokes[index]) {
733 m_scene.setNoMass(m_moveStrokes[index]);
736 return true;
739 bool endMoveStroke(int index)
741 if (!m_moveStrokes[index]) {
742 return false;
743 } else {
744 m_scene.setDefaultMass(m_moveStrokes[index]);
748 m_moveStrokes[index] = NULL;
749 return true;
752 bool moveStroke(int index, Uint16 x, Uint16 y)
754 if (!m_moveStrokes[index]) {
755 return false;
758 m_scene.moveStroke( m_moveStrokes[index], cursorPoint(x, y) );
759 return true;
763 bool deleteStroke(Uint16 x, Uint16 y)
765 m_scene.deleteStroke( m_scene.strokeAtPoint( cursorPoint(x, y),
766 SELECT_TOLERANCE ) );
767 m_refresh = true;
771 bool handleMultitouchEvent ( SDL_Event &ev)
773 switch( ev.type ) {
774 case SDL_NP_START_STROKE: {
775 DrawEvent* de = (DrawEvent*)ev.user.data1;
776 startStroke(de->cursor_id, de->x, de->y);
777 free(de);
779 break;
781 case SDL_NP_APPEND_STROKE: {
782 DrawEvent* de = (DrawEvent*)ev.user.data1;
783 extendStroke(de->cursor_id, de->x, de->y);
784 free(de);
786 break;
788 case SDL_NP_FINISH_STROKE: {
789 DrawEvent* de = (DrawEvent*)ev.user.data1;
790 finishStroke(de->cursor_id);
791 free(de);
793 break;
795 case SDL_NP_START_ROPE:
796 break;
798 case SDL_NP_APPEND_ROPE:
799 break;
801 case SDL_NP_FINISH_ROPE:
802 break;
804 case SDL_NP_START_DRAG: {
805 DragEvent* de = (DragEvent*)ev.user.data1;
806 startMoveStroke(de->cursor_id, de->x, de->y);
807 free(de);
809 break;
811 case SDL_NP_DRAG: {
812 DragEvent* de = (DragEvent*)ev.user.data1;
813 moveStroke(de->cursor_id, de->x, de->y);
814 free(de);
816 break;
818 case SDL_NP_END_DRAG: {
819 DragEvent* de = (DragEvent*)ev.user.data1;
820 endMoveStroke(de->cursor_id);
821 free(de);
823 break;
825 case SDL_NP_PAN:
826 break;
828 case SDL_NP_ZOOM:
829 break;
831 case SDL_NP_DELETE: {
832 DeleteEvent* de = (DeleteEvent*)ev.user.data1;
833 deleteStroke(de->x, de->y);
834 free(de);
836 break;
840 return false;
844 ////////////////////////////////////////////////////////////////
845 // layer interface
846 ////////////////////////////////////////////////////////////////
848 virtual bool isDirty()
850 //TODO this can be a bit heavyweight
851 return !dirtyArea().isEmpty();
854 virtual Rect dirtyArea()
856 if ( m_refresh ) {
857 m_refresh = false;
858 return FULLSCREEN_RECT;
859 } else {
860 return m_scene.dirtyArea();
864 virtual void onTick( int tick )
866 if ( !isPaused() ) {
867 m_scene.step();
868 m_recorder.tick();
869 m_player.tick();
872 if (m_levelnameHideTime == -1 && m_levelnameOverlay) {
873 showOverlay(m_levelnameOverlay);
874 m_levelnameHideTime = tick + 4000;
875 } else if (m_levelnameHideTime != 0 &&
876 m_levelnameHideTime<tick && m_levelnameOverlay) {
877 hideOverlay(m_levelnameOverlay);
878 m_levelnameHideTime = 0;
881 SDL_Event ev;
882 while ( m_player.fetchEvent(ev) ) {
883 // todo only dispatch play events?
884 handleModEvent(ev)
885 || handleGameEvent(ev)
886 || handleEditEvent(ev)
887 || handlePlayEvent(ev)
888 || handleMultitouchEvent(ev);
891 if ( m_isCompleted && m_edit ) {
892 hideOverlay( m_completedOverlay );
893 m_isCompleted = false;
895 if ( m_scene.isCompleted() != m_isCompleted && !m_edit ) {
896 m_isCompleted = m_scene.isCompleted();
897 if ( m_isCompleted ) {
898 m_stats.endTime = SDL_GetTicks();
899 m_player.stop();
900 m_recorder.stop();
901 m_completedOverlay = createNextLevelOverlay(*this, m_scene.getWinner());
902 showOverlay( m_completedOverlay );
903 } else {
904 hideOverlay( m_completedOverlay );
908 if ( m_os ) {
909 m_os->poll();
910 for ( char *f = m_os->getLaunchFile(); f; f=m_os->getLaunchFile() ) {
911 if ( strstr(f,".npz") ) {
912 //m_levels->empty();
914 m_levels->addPath( f );
915 int l = m_levels->findLevel( f );
916 if ( l >= 0 ) {
917 gotoLevel( l );
918 m_window.raise();
924 virtual void draw( Canvas& screen, const Rect& area )
926 m_scene.draw( screen, area );
927 if ( m_fade ) {
928 screen.fade( area );
932 virtual bool handleEvent( SDL_Event& ev )
934 if ( m_player.isRunning()
935 && ( ev.type==SDL_MOUSEMOTION ||
936 ev.type==SDL_MOUSEBUTTONDOWN ||
937 ev.type==SDL_MOUSEBUTTONUP ) ) {
938 return false;
939 } else if (handleModEvent(ev)
940 || handleGameEvent(ev)
941 || handleEditEvent(ev)
942 || handlePlayEvent(ev)
943 || handleMultitouchEvent(ev)) {
944 //\TODO only record edit,play events etc
945 m_recorder.record( ev );
946 return true;
948 return false;
951 virtual Window* getWindow() {
952 return &m_window;
958 Widget* createGameLayer( Levels* levels, int width, int height, bool fullscreen )
960 return new Game(levels,width,height,fullscreen);