2 * This file is part of NumptyPhysics
3 * Copyright (C) 2008 Tim Edmonds
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.
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.
30 #include "Multitouch.h"
33 #include <SDL/SDL_image.h>
45 unsigned char levelbuf
[64*1024];
52 DemoEntry( int _t
, int _o
, SDL_Event
& _e
) : t(_t
), o(_o
), e(_e
) {}
57 class DemoLog
: public Array
<DemoEntry
>
60 std::string
asString( int i
)
65 s
<< "E:" << e
.t
<< " ";
68 s
<< "K" << e
.e
.key
.keysym
.sym
;
71 s
<< "k" << e
.e
.key
.keysym
.sym
;
73 case SDL_MOUSEBUTTONDOWN
:
74 s
<< "B" << '0'+e
.e
.button
.button
;
75 s
<< "," << e
.e
.button
.x
<< "," << e
.e
.button
.y
;
77 case SDL_MOUSEBUTTONUP
:
78 s
<< "b" << '0'+e
.e
.button
.button
;
79 s
<< "," << e
.e
.button
.x
<< "," << e
.e
.button
.y
;
82 s
<< "M" << e
.e
.button
.x
<< "," << e
.e
.button
.y
;
92 void append( int tick
, int offset
, SDL_Event
& ev
)
94 Array
<DemoEntry
>::append( DemoEntry( tick
, offset
, ev
) );
97 void appendFromString( const std::string
& str
)
99 const char *s
= str
.c_str();
100 int t
, o
, v1
, v2
, v3
;
104 if ( sscanf(s
,"E:%d %c%d",&t
,&c
,&v1
)==3 ) { //1 arg
106 case 'K': ev
.type
= SDL_KEYDOWN
; break;
107 case 'k': ev
.type
= SDL_KEYUP
; break;
109 ev
.key
.keysym
.sym
= (SDLKey
)v1
;
110 } else if ( sscanf(s
,"E:%d %c%d,%d",&t
,&c
,&v1
,&v2
)==4 ) { //2 args
112 case 'M': ev
.type
= SDL_MOUSEMOTION
; break;
116 } else if ( sscanf(s
,"E:%d %c%d,%d,%d",&t
,&c
,&v1
,&v2
,&v3
)==5 ) { //3 args
118 case 'B': ev
.type
= SDL_MOUSEBUTTONDOWN
; break;
119 case 'b': ev
.type
= SDL_MOUSEBUTTONUP
; break;
121 ev
.button
.button
= v1
;
125 if ( ev
.type
!= 0xff ) {
141 m_lastTickTime
= SDL_GetTicks();
147 printf("stop recording: %d events:\n", m_log
.size());
148 for ( int i
=0; i
<m_log
.size(); i
++ ) {
149 std::string e
= m_log
.asString(i
);
150 if ( e
.length() > 0 ) {
151 printf(" %s\n",e
.c_str());
161 m_lastTickTime
= SDL_GetTicks();
165 void record( SDL_Event
& ev
)
169 case SDL_MOUSEBUTTONDOWN
:
170 m_pressed
|= 1<<ev
.button
.button
;
172 case SDL_MOUSEBUTTONUP
:
173 m_pressed
&= ~(1<<ev
.button
.button
);
175 case SDL_MOUSEMOTION
:
176 if ( m_pressed
== 0 ) {
180 m_log
.append( m_lastTick
, SDL_GetTicks()-m_lastTickTime
, ev
);
184 DemoLog
& getLog() { return m_log
; }
199 void start( const DemoLog
* log
)
205 printf("start playback: %d events\n",m_log
->size());
208 bool isRunning() { return m_playing
; }
223 bool fetchEvent( SDL_Event
& ev
)
226 if ( m_index
< m_log
->size()
227 && m_log
->at(m_index
).t
<= m_lastTick
) {
228 printf("demo event at t=%d\n",m_lastTick
);
229 ev
= m_log
->at(m_index
).e
;
239 const DemoLog
* m_log
;
245 class CollectionSelector
: public ListProvider
249 CollectionSelector( GameControl
& game
)
251 m_list
= createListOverlay( game
, this );
253 Overlay
* overlay() { return m_list
; }
255 virtual int countItems() {
258 virtual Canvas
* provideItem( int i
, Canvas
* old
) {
261 sprintf(buf
,"%d. Item",i
);
262 Canvas
* c
= new Canvas( 100, 32 );
263 c
->setBackground(0xffffff);
265 Font::headingFont()->drawLeft( c
, Vec2(3,3), buf
, i
<<3 );
268 virtual void releaseItem( Canvas
* old
) {
271 virtual void onSelection( int i
, int ix
, int iy
) {
272 printf("Selected: %d (%d,%d)\n",i
,ix
,iy
);
285 startTime
= SDL_GetTicks();
292 class Game
: public GameControl
, public Widget
295 Stroke
*m_createStrokes
[MT_MAX_CURSORS
];
296 Stroke
*m_moveStrokes
[MT_MAX_CURSORS
];
297 Array
<Overlay
*> m_overlays
;
299 Overlay
*m_pauseOverlay
;
300 Overlay
*m_editOverlay
;
301 Overlay
*m_completedOverlay
;
302 // DemoOverlay m_demoOverlay;
303 DemoRecorder m_recorder
;
305 CollectionSelector m_cselector
;
311 Game( Levels
* levels
, int width
, int height
)
312 : m_window(width
,height
,"Numpty Physics","NPhysics"),
313 m_pauseOverlay( NULL
),
314 m_editOverlay( NULL
),
315 m_completedOverlay( NULL
),
316 m_isCompleted(false),
317 m_cselector( *this ),
320 //,m_demoOverlay( *this )
322 for(int n
=0; n
<MT_MAX_CURSORS
; n
++) {
323 m_createStrokes
[n
] = NULL
;
324 m_moveStrokes
[n
] = NULL
;
326 configureScreenTransform( m_window
.width(), m_window
.height() );
332 virtual bool renderScene( Canvas
& c
, int level
)
335 int size
= m_levels
->load( level
, levelbuf
, sizeof(levelbuf
) );
336 if ( size
&& scene
.load( levelbuf
, size
) ) {
337 scene
.draw( c
, FULLSCREEN_RECT
);
343 void gotoLevel( int level
, bool replay
=false )
345 if ( level
>= 0 && level
< m_levels
->numLevels() ) {
346 int size
= m_levels
->load( level
, levelbuf
, sizeof(levelbuf
) );
347 if ( size
&& m_scene
.load( levelbuf
, size
) ) {
348 m_scene
.activateAll();
349 //m_window.setSubName( file );
357 m_player
.start( &m_recorder
.getLog() );
368 bool save( const char *file
=NULL
)
374 p
= Config::userDataDir() + Os::pathSep
+ "L99_saved.nph";
376 if ( m_scene
.save( p
) ) {
377 m_levels
->addPath( p
.c_str() );
378 int l
= m_levels
->findLevel( p
.c_str() );
381 m_window
.setSubName( p
.c_str() );
390 if ( save( SEND_TEMP_FILE
) ) {
392 if ( h
.post( Config::planetRoot().c_str(),
393 "upload", SEND_TEMP_FILE
, "type=level" ) ) {
394 std::string id
= h
.getHeader("NP-Upload-Id");
395 if ( id
.length() > 0 ) {
396 printf("uploaded as id %s\n",id
.c_str());
397 if ( !m_os
->openBrowser((Config::planetRoot()+"?level="+id
).c_str()) ) {
398 showMessage("Unable to launch browser");
401 showMessage("UploadFailed: unknown error");
404 showMessage(std::string("UploadFailed: ")+h
.errorMessage());
410 void setTool( int t
)
415 void editMode( bool set
)
420 void showMessage( const std::string
& msg
)
423 printf("showMessage \"%s\"\n",msg
.c_str());
426 void showOverlay( Overlay
* o
)
432 void hideOverlay( Overlay
* o
)
434 parent()->remove( o
);
442 if ( !m_pauseOverlay
) {
443 m_pauseOverlay
= createIconOverlay( *this, "pause.png", 50, 50 );
445 showOverlay( m_pauseOverlay
);
448 hideOverlay( m_pauseOverlay
);
458 void edit( bool doEdit
)
460 if ( m_edit
!= doEdit
) {
463 if ( !m_editOverlay
) {
464 m_editOverlay
= createEditOverlay(*this);
466 showOverlay( m_editOverlay
);
469 hideOverlay( m_editOverlay
);
470 m_strokeFixed
= false;
471 m_strokeSleep
= false;
472 m_strokeDecor
= false;
473 if ( m_colour
< 2 ) m_colour
= 2;
479 Vec2
cursorPoint(Uint16 x
, Uint16 y
)
482 worldToScreen
.inverseTransform( pt
);
486 Vec2
mousePoint( SDL_Event ev
)
488 Vec2
pt( ev
.button
.x
, ev
.button
.y
);
489 worldToScreen
.inverseTransform( pt
);
493 bool handleGameEvent( SDL_Event
&ev
)
497 switch ( ev
.key
.keysym
.sym
) {
507 showOverlay( createMenuOverlay( *this ) );
510 showOverlay( m_cselector
.overlay() );
517 //toggleOverlay( m_demoOverlay );
521 gotoLevel( m_level
);
525 gotoLevel( m_level
+1 );
529 gotoLevel( m_level
-1 );
532 gotoLevel( m_level
, true );
544 bool handleModEvent( SDL_Event
&ev
)
547 //printf("mod=%d\n",ev.key.keysym.sym,mod);
550 //printf("mod key=%x mod=%d\n",ev.key.keysym.sym,mod);
551 if ( ev
.key
.keysym
.sym
== SDLK_F8
) {
552 mod
= 1; //zoom- == middle (delete)
554 } else if ( ev
.key
.keysym
.sym
== SDLK_F7
) {
555 mod
= 2; //zoom+ == right (move)
560 if ( ev
.key
.keysym
.sym
== SDLK_F7
561 || ev
.key
.keysym
.sym
== SDLK_F8
) {
566 case SDL_MOUSEBUTTONDOWN
:
567 case SDL_MOUSEBUTTONUP
:
568 if ( ev
.button
.button
== SDL_BUTTON_LEFT
&& mod
!= 0 ) {
569 ev
.button
.button
= ((mod
==1) ? SDL_BUTTON_MIDDLE
: SDL_BUTTON_RIGHT
);
576 bool handlePlayEvent( SDL_Event
&ev
)
579 case SDL_MOUSEBUTTONDOWN
:
580 if ( ev
.button
.button
== SDL_BUTTON_LEFT
) {
581 if (startStroke(0, ev
.button
.x
, ev
.button
.y
))
587 case SDL_MOUSEBUTTONUP
:
588 if ( ev
.button
.button
== SDL_BUTTON_LEFT
) {
589 if (finishStroke(0)) {
594 case SDL_MOUSEMOTION
:
595 if (extendStroke(0, ev
.button
.x
, ev
.button
.y
)) {
600 if ( ev
.key
.keysym
.sym
== SDLK_ESCAPE
) {
601 if ( m_createStroke
) {
602 m_scene
.deleteStroke( m_createStroke
);
603 m_createStroke
= NULL
;
604 } else if ( m_scene
.deleteStroke( m_scene
.strokes().at(m_scene
.strokes().size()-1) ) ) {
617 bool startStroke(int index
, Uint16 x
, Uint16 y
)
619 if (m_createStrokes
[index
])
625 if ( m_strokeFixed
) {
626 attrib
|= ATTRIB_GROUND
;
628 if ( m_strokeSleep
) {
629 attrib
|= ATTRIB_SLEEPING
;
631 if ( m_strokeDecor
) {
632 attrib
|= ATTRIB_DECOR
;
634 m_createStrokes
[index
] = m_scene
.newStroke( Path()&cursorPoint(x
, y
), m_colour
, attrib
);
639 bool extendStroke(int index
, Uint16 x
, Uint16 y
)
641 if ( m_createStrokes
[index
] ) {
642 m_scene
.extendStroke( m_createStrokes
[index
], cursorPoint(x
, y
) );
649 bool finishStroke(int index
)
651 if (!m_createStrokes
[index
]) {
655 if ( m_scene
.activate( m_createStrokes
[index
] ) ) {
656 m_stats
.strokeCount
++;
658 m_stats
.pausedStrokes
++;
661 m_scene
.deleteStroke( m_createStrokes
[index
] );
663 m_createStrokes
[index
] = NULL
;
668 bool handleEditEvent( SDL_Event
&ev
)
670 //allow move/delete in normal play!!
671 //if ( !m_edit ) return false;
674 case SDL_MOUSEBUTTONDOWN
:
675 if ( ev
.button
.button
== SDL_BUTTON_RIGHT
) {
677 if (startMoveStroke(0, ev
.button
.x
, ev
.button
.y
)) {
681 if ( ev
.button
.button
== SDL_BUTTON_MIDDLE
) {
682 if (deleteStroke(ev
.button
.x
, ev
.button
.y
)) {
687 case SDL_MOUSEBUTTONUP
:
688 if ( ev
.button
.button
== SDL_BUTTON_RIGHT
) {
689 if (endMoveStroke(0)) {
694 case SDL_MOUSEMOTION
:
695 if (moveStroke(0, ev
.button
.x
, ev
.button
.y
)) {
705 bool startMoveStroke(int index
, Uint16 x
, Uint16 y
)
707 if (m_moveStrokes
[index
]) {
711 m_moveStrokes
[index
] = m_scene
.strokeAtPoint( cursorPoint(x
, y
),
717 bool endMoveStroke(int index
)
719 if (!m_moveStrokes
[index
]) {
723 m_moveStrokes
[index
] = NULL
;
727 bool moveStroke(int index
, Uint16 x
, Uint16 y
)
729 if (!m_moveStrokes
[index
]) {
733 m_scene
.moveStroke( m_moveStrokes
[index
], cursorPoint(x
, y
) );
738 bool deleteStroke(Uint16 x
, Uint16 y
)
740 m_scene
.deleteStroke( m_scene
.strokeAtPoint( cursorPoint(x
, y
),
741 SELECT_TOLERANCE
) );
746 bool handleMultitouchEvent ( SDL_Event
&ev
)
749 case SDL_NP_START_STROKE
: {
750 DrawEvent
* de
= (DrawEvent
*)ev
.user
.data1
;
751 startStroke(de
->cursor_id
, de
->x
, de
->y
);
755 case SDL_NP_APPEND_STROKE
: {
756 DrawEvent
* de
= (DrawEvent
*)ev
.user
.data1
;
757 extendStroke(de
->cursor_id
, de
->x
, de
->y
);
761 case SDL_NP_FINISH_STROKE
: {
762 DrawEvent
* de
= (DrawEvent
*)ev
.user
.data1
;
763 finishStroke(de
->cursor_id
);
767 case SDL_NP_START_ROPE
:
770 case SDL_NP_APPEND_ROPE
:
773 case SDL_NP_FINISH_ROPE
:
776 case SDL_NP_START_DRAG
: {
777 DragEvent
* de
= (DragEvent
*)ev
.user
.data1
;
778 startMoveStroke(de
->cursor_id
, de
->x
, de
->y
);
783 DragEvent
* de
= (DragEvent
*)ev
.user
.data1
;
784 moveStroke(de
->cursor_id
, de
->x
, de
->y
);
788 case SDL_NP_END_DRAG
: {
789 DragEvent
* de
= (DragEvent
*)ev
.user
.data1
;
790 endMoveStroke(de
->cursor_id
);
800 case SDL_NP_DELETE
: {
801 DeleteEvent
* de
= (DeleteEvent
*)ev
.user
.data1
;
802 deleteStroke(de
->x
, de
->y
);
812 ////////////////////////////////////////////////////////////////
814 ////////////////////////////////////////////////////////////////
816 virtual bool isDirty()
818 //TODO this can be a bit heavyweight
819 return !dirtyArea().isEmpty();
822 virtual Rect
dirtyArea()
826 return FULLSCREEN_RECT
;
828 return m_scene
.dirtyArea();
832 virtual void onTick( int tick
)
841 while ( m_player
.fetchEvent(ev
) ) {
842 // todo only dispatch play events?
844 || handleGameEvent(ev
)
845 || handleEditEvent(ev
)
846 || handlePlayEvent(ev
)
847 || handleMultitouchEvent(ev
);
850 if ( m_isCompleted
&& m_edit
) {
851 hideOverlay( m_completedOverlay
);
852 m_isCompleted
= false;
854 if ( m_scene
.isCompleted() != m_isCompleted
&& !m_edit
) {
855 m_isCompleted
= m_scene
.isCompleted();
856 if ( m_isCompleted
) {
857 m_stats
.endTime
= SDL_GetTicks();
860 m_completedOverlay
= createNextLevelOverlay(*this, m_scene
.getWinner());
861 showOverlay( m_completedOverlay
);
863 hideOverlay( m_completedOverlay
);
869 for ( char *f
= m_os
->getLaunchFile(); f
; f
=m_os
->getLaunchFile() ) {
870 if ( strstr(f
,".npz") ) {
873 m_levels
->addPath( f
);
874 int l
= m_levels
->findLevel( f
);
883 virtual void draw( Canvas
& screen
, const Rect
& area
)
885 m_scene
.draw( screen
, area
);
891 virtual bool handleEvent( SDL_Event
& ev
)
893 if ( m_player
.isRunning()
894 && ( ev
.type
==SDL_MOUSEMOTION
||
895 ev
.type
==SDL_MOUSEBUTTONDOWN
||
896 ev
.type
==SDL_MOUSEBUTTONUP
) ) {
898 } else if (handleModEvent(ev
)
899 || handleGameEvent(ev
)
900 || handleEditEvent(ev
)
901 || handlePlayEvent(ev
)
902 || handleMultitouchEvent(ev
)) {
903 //\TODO only record edit,play events etc
904 m_recorder
.record( ev
);
913 Widget
* createGameLayer( Levels
* levels
, int width
, int height
)
915 return new Game(levels
,width
,height
);