change distance calculation
[numtypysics.git] / Game.cpp
blob85a200cbdd2a9eca6f29e802f1c5e7f33019748f
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 Stroke *m_ropeParts[MT_MAX_CURSORS][MAX_ROPE_PARTS];
296 int m_numRopeLength[MT_MAX_CURSORS];
297 int m_numRopeParts[MT_MAX_CURSORS];
298 Array<Overlay*> m_overlays;
299 Window m_window;
300 Overlay *m_pauseOverlay;
301 Overlay *m_editOverlay;
302 Overlay *m_completedOverlay;
303 Overlay *m_levelnameOverlay;
304 int m_levelnameHideTime;
305 // DemoOverlay m_demoOverlay;
306 DemoRecorder m_recorder;
307 DemoPlayer m_player;
308 CollectionSelector m_cselector;
309 Os *m_os;
310 GameStats m_stats;
311 bool m_isCompleted;
312 bool m_paused;
313 public:
314 Game( Levels* levels, int width, int height, bool fullscreen )
315 : m_window(width,height,"Numpty Physics","NPhysics", fullscreen),
316 m_pauseOverlay( NULL ),
317 m_editOverlay( NULL ),
318 m_completedOverlay( NULL ),
319 m_levelnameOverlay( NULL ),
320 m_levelnameHideTime( 0 ),
321 m_isCompleted(false),
322 m_cselector( *this ),
323 m_os( Os::get() ),
324 m_paused( false )
325 //,m_demoOverlay( *this )
327 for(int n=0; n<MT_MAX_CURSORS; n++) {
328 m_createStrokes[n] = NULL;
329 m_moveStrokes[n] = NULL;
330 m_numRopeLength[n] = NULL;
331 m_numRopeParts[n] = 0;
333 configureScreenTransform( m_window.width(), m_window.height() );
334 m_levels = levels;
335 gotoLevel(0);
339 virtual bool renderScene( Canvas& c, int level )
341 Scene scene( true );
342 int size = m_levels->load( level, levelbuf, sizeof(levelbuf) );
343 if ( size && scene.load( levelbuf, size ) ) {
344 scene.draw( c, FULLSCREEN_RECT );
345 return true;
347 return false;
350 void gotoLevel( int level, bool replay=false )
352 if ( level >= 0 && level < m_levels->numLevels() ) {
353 int size = m_levels->load( level, levelbuf, sizeof(levelbuf) );
354 if ( size && m_scene.load( levelbuf, size ) ) {
355 m_scene.activateAll();
356 //m_window.setSubName( file );
357 m_refresh = true;
358 if ( m_edit ) {
359 m_scene.protect(0);
361 m_recorder.stop();
362 m_player.stop();
363 if ( replay ) {
364 m_player.start( &m_recorder.getLog() );
365 } else {
366 m_recorder.start();
368 m_level = level;
369 m_stats.reset();
370 if (m_levelnameOverlay && m_levelnameHideTime) {
371 hideOverlay(m_levelnameOverlay);
372 delete m_levelnameOverlay;
373 m_levelnameHideTime = 0;
376 std::string title = m_scene.getTitle(), author = m_scene.getAuthor();
378 /* Only show title if we have at least one of (title, author) specified */
379 if (!title.empty() || !author.empty()) {
380 m_levelnameOverlay = createTextOverlay( *this,
381 ((title.empty())?(std::string("Untitled")):(title)) +
382 std::string(" by ") +
383 ((author.empty())?(std::string("Anonymous")):(author)));
384 m_levelnameHideTime = -1; /* in onTick, -1 means "show the overlay" */
391 bool save( const char *file=NULL )
393 string p;
394 if ( file ) {
395 p = file;
396 } else {
397 p = Config::userDataDir() + Os::pathSep + "L99_saved.nph";
399 if ( m_scene.save( p ) ) {
400 m_levels->addPath( p.c_str() );
401 int l = m_levels->findLevel( p.c_str() );
402 if ( l >= 0 ) {
403 m_level = l;
404 m_window.setSubName( p.c_str() );
406 return true;
408 return false;
411 bool send()
413 if ( save( SEND_TEMP_FILE ) ) {
414 Http h;
415 if ( h.post( Config::planetRoot().c_str(),
416 "upload", SEND_TEMP_FILE, "type=level" ) ) {
417 std::string id = h.getHeader("NP-Upload-Id");
418 if ( id.length() > 0 ) {
419 printf("uploaded as id %s\n",id.c_str());
420 if ( !m_os->openBrowser((Config::planetRoot()+"?level="+id).c_str()) ) {
421 showMessage("Unable to launch browser");
423 } else {
424 showMessage("UploadFailed: unknown error");
426 } else {
427 showMessage(std::string("UploadFailed: ")+h.errorMessage());
430 return false;
433 void setTool( int t )
435 m_colour = t;
438 void editMode( bool set )
440 m_edit = set;
443 void showMessage( const std::string& msg )
445 //todo
446 printf("showMessage \"%s\"\n",msg.c_str());
449 void showOverlay( Overlay* o )
451 parent()->add( o );
452 o->onShow();
455 void hideOverlay( Overlay* o )
457 parent()->remove( o );
458 o->onHide();
459 m_refresh = true;
462 void setPause(bool pause)
464 if (pause == m_paused) {
465 return;
468 if ( pause ) {
469 if ( !m_pauseOverlay ) {
470 m_pauseOverlay = createIconOverlay( *this, "pause.png", 50, 50 );
472 showOverlay( m_pauseOverlay );
473 } else {
474 hideOverlay( m_pauseOverlay );
476 m_paused = pause;
479 void togglePause()
481 setPause( !m_paused );
484 bool isPaused()
486 return m_paused;
489 void edit( bool doEdit )
491 if ( m_edit != doEdit ) {
492 m_edit = doEdit;
493 if ( m_edit ) {
494 if ( !m_editOverlay ) {
495 m_editOverlay = createEditOverlay(*this);
497 showOverlay( m_editOverlay );
498 m_scene.protect(0);
499 } else {
500 hideOverlay( m_editOverlay );
501 m_strokeFixed = false;
502 m_strokeSleep = false;
503 m_strokeDecor = false;
504 if ( m_colour < 2 ) m_colour = 2;
505 m_scene.protect();
510 Vec2 cursorPoint(Uint16 x, Uint16 y)
512 Vec2 pt( x, y );
513 worldToScreen.inverseTransform( pt );
514 return pt;
517 Vec2 mousePoint( SDL_Event ev )
519 Vec2 pt( ev.button.x, ev.button.y );
520 worldToScreen.inverseTransform( pt );
521 return pt;
524 bool handleGameEvent( SDL_Event &ev )
526 switch( ev.type ) {
527 case SDL_KEYDOWN:
528 switch ( ev.key.keysym.sym ) {
529 case SDLK_SPACE:
530 case SDLK_KP_ENTER:
531 case SDLK_RETURN:
532 togglePause();
533 break;
534 case SDLK_s:
535 save();
536 break;
537 case SDLK_F4:
538 showOverlay( createMenuOverlay( *this ) );
539 break;
540 case SDLK_c:
541 showOverlay( m_cselector.overlay() );
542 break;
543 case SDLK_e:
544 case SDLK_F6:
545 edit( !m_edit );
546 break;
547 case SDLK_d:
548 //toggleOverlay( m_demoOverlay );
549 break;
550 case SDLK_r:
551 case SDLK_UP:
552 gotoLevel( m_level );
553 break;
554 case SDLK_n:
555 case SDLK_RIGHT:
556 gotoLevel( m_level+1 );
557 break;
558 case SDLK_p:
559 case SDLK_LEFT:
560 gotoLevel( m_level-1 );
561 break;
562 case SDLK_v:
563 gotoLevel( m_level, true );
564 break;
565 default:
566 break;
568 break;
569 default:
570 break;
572 return false;
575 bool handleModEvent( SDL_Event &ev )
577 static int mod=0;
578 //printf("mod=%d\n",ev.key.keysym.sym,mod);
579 switch( ev.type ) {
580 case SDL_KEYDOWN:
581 //printf("mod key=%x mod=%d\n",ev.key.keysym.sym,mod);
582 if ( ev.key.keysym.sym == SDLK_F8 ) {
583 mod = 1; //zoom- == middle (delete)
584 return true;
585 } else if ( ev.key.keysym.sym == SDLK_F7 ) {
586 mod = 2; //zoom+ == right (move)
587 return true;
589 break;
590 case SDL_KEYUP:
591 if ( ev.key.keysym.sym == SDLK_F7
592 || ev.key.keysym.sym == SDLK_F8 ) {
593 mod = 0;
594 return true;
596 break;
597 case SDL_MOUSEBUTTONDOWN:
598 case SDL_MOUSEBUTTONUP:
599 if ( ev.button.button == SDL_BUTTON_LEFT && mod != 0 ) {
600 ev.button.button = ((mod==1) ? SDL_BUTTON_MIDDLE : SDL_BUTTON_RIGHT);
602 break;
604 return false;
607 bool handlePlayEvent( SDL_Event &ev )
609 switch( ev.type ) {
610 case SDL_MOUSEBUTTONDOWN:
611 if ( ev.button.button == SDL_BUTTON_LEFT ) {
612 if ((SDL_GetModState() & KMOD_SHIFT) > 0)
614 if(startRope(0, ev.button.x, ev.button.y))
616 return true;
619 else
621 if (startStroke(0, ev.button.x, ev.button.y))
623 return true;
627 break;
628 case SDL_MOUSEBUTTONUP:
629 if ( ev.button.button == SDL_BUTTON_LEFT) {
630 if ((SDL_GetModState() & KMOD_SHIFT) > 0)
632 if(finishRope(0))
634 return true;
637 else
639 if (finishStroke(0)) {
640 return true;
644 break;
645 case SDL_MOUSEMOTION:
646 if ( m_createStrokes[0] ) {
647 if ((SDL_GetModState() & KMOD_SHIFT) > 0)
649 if (extendRope(0, ev.button.x, ev.button.y))
651 return true;
654 else
656 if (extendStroke(0, ev.button.x, ev.button.y)) {
657 return true;
661 break;
662 case SDL_KEYDOWN:
663 if ( ev.key.keysym.sym == SDLK_ESCAPE ) {
664 if ( cancelDraw(0) )
666 m_refresh = true;
667 return true;
669 else if ( m_scene.deleteStroke( m_scene.strokes().at(m_scene.strokes().size()-1) ) ) {
670 m_stats.undoCount++;
671 m_refresh = true;
672 return true;
675 break;
676 default:
677 break;
679 return false;
682 bool checkCursorId(int index)
684 if (index >= MT_MAX_CURSORS)
686 fprintf(stderr, "coursor id %d to high (max %d)\n", index, MT_MAX_CURSORS);
687 return false;
690 return true;
693 bool startStroke(int index, Uint16 x, Uint16 y)
695 if (!checkCursorId(index))
697 return false;
700 if (m_createStrokes[index])
702 fprintf(stderr, "startStroke: stroke %d already started\n", index);
703 return false;
706 int attrib = 0;
707 if ( m_strokeFixed ) {
708 attrib |= ATTRIB_GROUND;
710 if ( m_strokeSleep ) {
711 attrib |= ATTRIB_SLEEPING;
713 if ( m_strokeDecor ) {
714 attrib |= ATTRIB_DECOR;
716 m_createStrokes[index] = m_scene.newStroke( Path()&cursorPoint(x, y), m_colour, attrib );
718 return true;
721 bool extendStroke(int index, Uint16 x, Uint16 y)
723 if (!checkCursorId(index))
725 return false;
728 if ( !m_createStrokes[index] ) {
729 fprintf(stderr, "extendStroke: stroke %d not yet started\n", index);
730 return false;
733 m_scene.extendStroke( m_createStrokes[index], cursorPoint(x, y) );
734 return true;
737 bool finishStroke(int index)
739 if (!checkCursorId(index))
741 return false;
744 if (!m_createStrokes[index]) {
745 fprintf(stderr, "finishStroke: stroke %d not yet started\n", index);
746 return false;
749 int numPoints = m_scene.numPoints(m_createStrokes[index]);
750 if (numPoints < FINISH_THRESHOLD_NUM_POINTS)
752 fprintf(stderr, "finishStroke: can't finish stroke with %d < %d points\n", numPoints, FINISH_THRESHOLD_NUM_POINTS);
753 cancelDraw(index);
754 return true;
757 if ( m_scene.activate( m_createStrokes[index] ) ) {
758 m_stats.strokeCount++;
759 if ( isPaused() ) {
760 m_stats.pausedStrokes++;
762 } else {
763 m_scene.deleteStroke( m_createStrokes[index] );
766 fprintf(stderr, "finished stroke with %d points\n", numPoints);
768 m_createStrokes[index] = NULL;
770 return true;
773 bool startRope(int index, Uint16 x, Uint16 y)
775 if(!checkCursorId(index))
777 return false;
780 if (m_createStrokes[index])
782 fprintf(stderr, "startRope: rope %d already started\n", index);
783 return false;
786 int attrib = 0;
787 m_numRopeLength[index] = 0;
788 m_numRopeParts[index] = 0;
789 m_createStrokes[index] = m_scene.newStroke( Path()&cursorPoint(x, y), m_colour, attrib );
790 m_ropeParts[index][0] = m_createStrokes[index];
792 return true;
795 bool extendRope(int index, Uint16 x, Uint16 y)
797 if (!checkCursorId(index))
799 return false;
802 if ( !m_createStrokes[index] ) {
803 fprintf(stderr, "extendRope: rope %d not yet started\n", index);
804 return false;
807 m_numRopeLength[index]++;
808 m_scene.extendStroke( m_createStrokes[index], cursorPoint(x, y) );
810 if (m_numRopeLength[index] == 10)
812 nextRopePart(index, x, y);
814 return true;
817 void nextRopePart(int index, Uint16 x, Uint16 y)
819 if (!checkCursorId(index))
821 return;
824 if (!m_createStrokes[index]) {
825 fprintf(stderr, "nextRopePart: rope %d not yet started\n", index);
826 return;
829 if ( m_scene.activate( m_createStrokes[index] ) ) {
830 m_stats.strokeCount++;
831 if ( isPaused() ) {
832 m_stats.pausedStrokes++;
834 } else {
835 m_scene.deleteStroke( m_createStrokes[index] );
838 m_ropeParts[index][m_numRopeParts[index]++] = m_createStrokes[index];
839 m_scene.setNoMass(m_createStrokes[index]);
841 int attrib = 0;
842 m_numRopeLength[index] = 0;
843 m_createStrokes[index] = m_scene.newStroke( Path()&cursorPoint(x, y), m_colour, attrib );
846 bool finishRope(int index)
848 if (!checkCursorId(index))
850 return false;
853 if (!m_createStrokes[index]) {
854 fprintf(stderr, "finishRope: rope %d not yet started\n", index);
855 return false;
858 if ( m_scene.activate( m_createStrokes[index] ) ) {
859 m_stats.strokeCount++;
860 if ( isPaused() ) {
861 m_stats.pausedStrokes++;
863 } else {
864 m_scene.deleteStroke( m_createStrokes[index] );
866 m_createStrokes[index] = NULL;
868 for(int n=0; n<m_numRopeParts[index]; n++)
870 m_scene.setDefaultMass(m_ropeParts[index][n]);
873 return true;
876 bool cancelDraw(int index)
878 if (!checkCursorId(index))
880 return false;
883 if ( !m_createStrokes[index] )
885 fprintf(stderr, "cancelDraw: line %d not yet started\n", index);
886 return false;
889 int numPoints = m_scene.numPoints(m_createStrokes[index]);
890 if ( numPoints > CANCEL_THRESHOLD_NUM_POINTS)
892 fprintf(stderr, "won't cancel stroke with %d > %d points\n", numPoints, CANCEL_THRESHOLD_NUM_POINTS);
893 return false;
896 m_scene.deleteStroke( m_createStrokes[index] );
897 m_createStrokes[index] = NULL;
898 m_refresh = true;
899 return true;
902 bool handleEditEvent( SDL_Event &ev )
904 //allow move/delete in normal play!!
905 //if ( !m_edit ) return false;
907 switch( ev.type ) {
908 case SDL_MOUSEBUTTONDOWN:
909 if ( ev.button.button == SDL_BUTTON_RIGHT ) {
911 if (startMoveStroke(0, ev.button.x, ev.button.y)) {
912 return true;
915 if ( ev.button.button == SDL_BUTTON_MIDDLE ) {
916 if (deleteStroke(ev.button.x, ev.button.y)) {
917 return true;
920 break;
921 case SDL_MOUSEBUTTONUP:
922 if ( ev.button.button == SDL_BUTTON_RIGHT ) {
923 if (endMoveStroke(0)) {
924 return true;
927 break;
928 case SDL_MOUSEMOTION:
929 if (moveStroke(0, ev.button.x, ev.button.y)) {
930 return true;
932 break;
933 default:
934 break;
936 return false;
939 bool startMoveStroke(int index, Uint16 x, Uint16 y)
941 if (!checkCursorId(index))
943 return false;
946 if (m_moveStrokes[index]) {
947 return false;
950 m_moveStrokes[index] = m_scene.strokeAtPoint( cursorPoint(x, y),
951 SELECT_TOLERANCE );
953 if (m_moveStrokes[index]) {
954 m_scene.setNoMass(m_moveStrokes[index]);
957 return true;
960 bool endMoveStroke(int index)
962 if (!checkCursorId(index))
964 return false;
967 if (!m_moveStrokes[index]) {
968 return false;
969 } else {
970 m_scene.setDefaultMass(m_moveStrokes[index]);
974 m_moveStrokes[index] = NULL;
975 return true;
978 bool moveStroke(int index, Uint16 x, Uint16 y)
980 if (!checkCursorId(index))
982 return false;
985 if (!m_moveStrokes[index]) {
986 return false;
989 m_scene.moveStroke( m_moveStrokes[index], cursorPoint(x, y) );
990 return true;
994 bool deleteStroke(Uint16 x, Uint16 y)
996 m_scene.deleteStroke( m_scene.strokeAtPoint( cursorPoint(x, y),
997 SELECT_TOLERANCE ) );
998 m_refresh = true;
1002 bool handleMultitouchEvent ( SDL_Event &ev)
1004 switch( ev.type ) {
1005 case SDL_NP_START_STROKE: {
1006 DrawEvent* de = (DrawEvent*)ev.user.data1;
1007 if (de == NULL) { return false; }
1009 startStroke(de->cursor_id, de->x, de->y);
1010 free(de);
1011 ev.user.data1 = NULL;
1012 return true;
1015 case SDL_NP_APPEND_STROKE: {
1016 DrawEvent* de = (DrawEvent*)ev.user.data1;
1017 if (de == NULL) { return false; }
1019 extendStroke(de->cursor_id, de->x, de->y);
1020 free(de);
1021 ev.user.data1 = NULL;
1022 return true;
1025 case SDL_NP_FINISH_STROKE: {
1026 DrawEvent* de = (DrawEvent*)ev.user.data1;
1027 if (de == NULL) { return false; }
1029 finishStroke(de->cursor_id);
1030 free(de);
1031 ev.user.data1 = NULL;
1032 return true;
1035 case SDL_NP_START_ROPE: {
1036 DrawEvent* de = (DrawEvent*)ev.user.data1;
1037 if (de == NULL) { return false; }
1039 startRope(de->cursor_id, de->x, de->y);
1040 free(de);
1041 ev.user.data1 = NULL;
1042 return true;
1045 case SDL_NP_APPEND_ROPE: {
1046 DrawEvent* de = (DrawEvent*)ev.user.data1;
1047 if (de == NULL) { return false; }
1049 extendRope(de->cursor_id, de->x, de->y);
1050 free(de);
1051 ev.user.data1 = NULL;
1052 return true;
1055 case SDL_NP_FINISH_ROPE: {
1056 DrawEvent* de = (DrawEvent*)ev.user.data1;
1057 finishRope(de->cursor_id);
1058 free(de);
1059 return true;
1062 case SDL_NP_CANCEL_DRAW: {
1063 CursorEvent* ce = (CursorEvent*)ev.user.data1;
1064 if (ce == NULL) { return false; }
1066 cancelDraw(ce->cursor_id);
1067 free(ce);
1068 ev.user.data1 = NULL;
1069 return true;
1072 case SDL_NP_START_DRAG: {
1073 DragEvent* de = (DragEvent*)ev.user.data1;
1074 if (de == NULL) { return false; }
1076 startMoveStroke(de->cursor_id, de->x, de->y);
1077 free(de);
1078 ev.user.data1 = NULL;
1079 return true;
1082 case SDL_NP_DRAG: {
1083 DragEvent* de = (DragEvent*)ev.user.data1;
1084 if (de == NULL) { return false; }
1086 moveStroke(de->cursor_id, de->x, de->y);
1087 free(de);
1088 ev.user.data1 = NULL;
1089 return true;
1092 case SDL_NP_END_DRAG: {
1093 DragEvent* de = (DragEvent*)ev.user.data1;
1094 if (de == NULL) { return false; }
1096 endMoveStroke(de->cursor_id);
1097 free(de);
1098 ev.user.data1 = NULL;
1099 return true;
1102 case SDL_NP_PAN:
1103 return true;
1105 case SDL_NP_ZOOM:
1106 return true;
1108 case SDL_NP_DELETE: {
1109 DeleteEvent* de = (DeleteEvent*)ev.user.data1;
1110 if (de == NULL) { return false; }
1112 deleteStroke(de->x, de->y);
1113 free(de);
1114 ev.user.data1 = NULL;
1115 return true;
1118 case SDL_NP_PREVIEW_CURSOR: {
1119 DrawEvent* de = (DrawEvent*)ev.user.data1;
1120 if (de == NULL) { return false; }
1122 previewCursor(de->cursor_id, de->x, de->y);
1123 free(de);
1124 ev.user.data1 = NULL;
1125 return true;
1128 case SDL_NP_RESTART_LEVEL: {
1129 gotoLevel( m_level );
1130 return true;
1133 case SDL_NP_NEXT_LEVEL: {
1134 gotoLevel( m_level+1 );
1135 return true;
1138 case SDL_NP_PREV_LEVEL: {
1139 gotoLevel( m_level-1 );
1140 return true;
1143 case SDL_NP_TOGGLE_MENU: {
1144 toggleMenu();
1145 return true;
1148 case SDL_NP_SHOW_MENU: {
1149 showMenu();
1150 return true;
1153 case SDL_NP_HIDE_MENU: {
1154 hideMenu();
1155 return true;
1158 case SDL_NP_TOGGLE_PAUSE: {
1159 togglePause();
1160 return true;
1163 case SDL_NP_PAUSE: {
1164 setPause(true);
1165 return true;
1168 case SDL_NP_RESUME: {
1169 setPause(false);
1170 return true;
1173 case SDL_NP_SAVE: {
1174 save();
1175 return true;
1178 case SDL_NP_TOGGLE_EDITOR: {
1179 edit( !m_edit );
1180 return true;
1183 case SDL_NP_REPLAY: {
1184 gotoLevel( m_level, true );
1185 return true;
1189 return false;
1192 bool previewCursor(int cursor_id, int x, int y)
1194 m_window.previewCursor(cursor_id, x, y);
1195 return true;
1198 ////////////////////////////////////////////////////////////////
1199 // layer interface
1200 ////////////////////////////////////////////////////////////////
1202 virtual bool isDirty()
1204 //TODO this can be a bit heavyweight
1205 return !dirtyArea().isEmpty();
1208 virtual Rect dirtyArea()
1210 if ( m_refresh ) {
1211 m_refresh = false;
1212 return FULLSCREEN_RECT;
1213 } else {
1214 return m_scene.dirtyArea();
1218 virtual void onTick( int tick )
1220 if ( !isPaused() ) {
1221 m_scene.step();
1222 m_recorder.tick();
1223 m_player.tick();
1226 if (m_levelnameHideTime == -1 && m_levelnameOverlay) {
1227 showOverlay(m_levelnameOverlay);
1228 m_levelnameHideTime = tick + 4000;
1229 } else if (m_levelnameHideTime != 0 &&
1230 m_levelnameHideTime<tick && m_levelnameOverlay) {
1231 hideOverlay(m_levelnameOverlay);
1232 m_levelnameHideTime = 0;
1235 SDL_Event ev;
1236 while ( m_player.fetchEvent(ev) ) {
1237 // todo only dispatch play events?
1238 handleModEvent(ev)
1239 || handleGameEvent(ev)
1240 || handleEditEvent(ev)
1241 || handlePlayEvent(ev)
1242 || handleMultitouchEvent(ev);
1245 if ( m_isCompleted && m_edit ) {
1246 hideOverlay( m_completedOverlay );
1247 m_isCompleted = false;
1249 if ( m_scene.isCompleted() != m_isCompleted && !m_edit ) {
1250 m_isCompleted = m_scene.isCompleted();
1251 if ( m_isCompleted ) {
1252 m_stats.endTime = SDL_GetTicks();
1253 m_player.stop();
1254 m_recorder.stop();
1255 m_completedOverlay = createNextLevelOverlay(*this, m_scene.getWinner());
1256 showOverlay( m_completedOverlay );
1257 } else {
1258 hideOverlay( m_completedOverlay );
1262 if ( m_os ) {
1263 m_os->poll();
1264 for ( char *f = m_os->getLaunchFile(); f; f=m_os->getLaunchFile() ) {
1265 if ( strstr(f,".npz") ) {
1266 //m_levels->empty();
1268 m_levels->addPath( f );
1269 int l = m_levels->findLevel( f );
1270 if ( l >= 0 ) {
1271 gotoLevel( l );
1272 m_window.raise();
1278 void toggleMenu()
1280 if ( m_completedOverlay == NULL ) {
1281 showMenu();
1282 } else {
1283 hideMenu();
1287 void showMenu()
1289 m_completedOverlay = createNextLevelOverlay(*this, -2);
1290 showOverlay( m_completedOverlay );
1293 void hideMenu()
1295 m_completedOverlay = NULL;
1296 hideOverlay( m_completedOverlay );
1299 virtual void draw( Canvas& screen, const Rect& area )
1301 m_scene.draw( screen, area );
1302 if ( m_fade ) {
1303 screen.fade( area );
1307 virtual bool handleEvent( SDL_Event& ev )
1309 if ( m_player.isRunning()
1310 && ( ev.type==SDL_MOUSEMOTION ||
1311 ev.type==SDL_MOUSEBUTTONDOWN ||
1312 ev.type==SDL_MOUSEBUTTONUP ) ) {
1313 return false;
1314 } else if (handleModEvent(ev)
1315 || handleGameEvent(ev)
1316 || handleEditEvent(ev)
1317 || handlePlayEvent(ev)
1318 || handleMultitouchEvent(ev)) {
1319 //\TODO only record edit,play events etc
1320 m_recorder.record( ev );
1321 return true;
1323 return false;
1326 virtual Window* getWindow() {
1327 return &m_window;
1333 Widget* createGameLayer( Levels* levels, int width, int height, bool fullscreen )
1335 return new Game(levels,width,height,fullscreen);