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.
28 extern Rect FULLSCREEN_RECT
;
30 class OverlayBase
: public Overlay
33 OverlayBase( GameControl
& game
, int x
=10, int y
=10, bool dragging_allowed
=true )
39 m_dragging_allowed(dragging_allowed
),
42 virtual ~OverlayBase()
55 return Rect(m_x
,m_y
,m_x
+m_canvas
->width()-1,m_y
+m_canvas
->height()-1);
61 virtual void onShow() { dirty(); }
62 virtual void onHide() {}
63 virtual void onTick( int tick
) {}
65 virtual void draw( Canvas
& screen
, const Rect
& area
)
68 screen
.drawImage( m_canvas
, m_x
, m_y
);
73 virtual bool handleEvent( SDL_Event
& ev
)
76 case SDL_MOUSEBUTTONDOWN
:
77 //printf("overlay click\n");
78 if ( ev
.button
.button
== SDL_BUTTON_LEFT
79 && ev
.button
.x
>= m_x
&& ev
.button
.x
<= m_x
+ m_canvas
->width()
80 && ev
.button
.y
>= m_y
&& ev
.button
.y
<= m_y
+ m_canvas
->height() ) {
83 m_prevx
= ev
.button
.x
;
84 m_prevy
= ev
.button
.y
;
89 case SDL_MOUSEBUTTONUP
:
90 if ( ev
.button
.button
== SDL_BUTTON_LEFT
) {
93 onDragStop( ev
.button
.x
-m_x
, ev
.button
.y
-m_y
);
94 } else if ( Abs(ev
.button
.x
-m_orgx
) < CLICK_TOLERANCE
95 && Abs(ev
.button
.y
-m_orgy
) < CLICK_TOLERANCE
){
96 onClick( m_orgx
-m_x
, m_orgy
-m_y
);
101 case SDL_MOUSEMOTION
:
104 && m_dragging_allowed
105 && ( Abs(ev
.button
.x
-m_orgx
) >= CLICK_TOLERANCE
106 || Abs(ev
.button
.y
-m_orgy
) >= CLICK_TOLERANCE
) ) {
108 onDragStart( ev
.button
.x
-m_x
, ev
.button
.y
-m_y
);
109 } else if ( m_dragging
) {
110 onDrag( ev
.button
.x
- m_prevx
, ev
.button
.y
- m_prevy
);
111 m_prevx
= ev
.button
.x
;
112 m_prevy
= ev
.button
.y
;
116 return onKey( ev
.key
.keysym
.sym
);
130 void dirty( const Rect
& r
)
135 virtual bool onKey( int k
)
139 virtual bool onDragStart( int x
, int y
)
143 virtual bool onDragStop( int x
, int y
)
147 virtual bool onDrag( int dx
, int dy
)
151 m_game
.m_refresh
= true;
155 virtual bool onClick( int x
, int y
) {
156 for ( int i
=m_hotSpots
.size()-1; i
>=0; i
-- ) {
157 if ( m_hotSpots
[i
].rect
.contains( Vec2(x
,y
) ) ) {
158 if ( m_hotSpots
[i
].c
) {
159 m_hotSpots
[i
].c(m_hotSpots
[i
].o
,m_hotSpots
[i
].f
);
161 } else if ( onHotSpot( m_hotSpots
[i
].id
) ) {
169 virtual bool onHotSpot( int id
) { return false; }
171 void addHotSpot( const Rect
& r
, int id
)
173 HotSpot hs
= { r
, id
, NULL
, NULL
, NULL
};
174 m_hotSpots
.append( hs
);
178 void addHotSpot( const Rect
& r
, void (C::*func
)() )
181 static void call( void* o
, void (Overlay::*f
)() ) {
182 return (((C
*)o
)->*(void (C::*)())f
)();
185 HotSpot hs
= { r
, -1, this, (void (Overlay::*)())func
, Caller::call
};
186 m_hotSpots
.append( hs
);
194 void (Overlay::*f
)();
195 void (*c
)(void*,void (Overlay::*)());
202 int m_prevx
, m_prevy
;
205 bool m_dragging_allowed
;
207 Array
<HotSpot
> m_hotSpots
;
210 class TextOverlay
: public OverlayBase
213 const std::string
& m_text
;
215 TextOverlay(GameControl
& game
, const std::string
& text
,
216 int x
=10, int y
=10, bool dragging_allowed
=false)
217 : OverlayBase(game
, x
, y
, dragging_allowed
)
220 m_canvas
= Font::headingFont()->renderFont(m_text
, 1);
223 virtual bool handleEvent( SDL_Event
& ev
) {
224 /* Do not react to or consume mouse/keyboard events */
229 class IconOverlay
: public OverlayBase
231 std::string m_filename
;
233 IconOverlay(GameControl
& game
, const char* file
, int x
=100,int y
=20, bool dragging_allowed
=true)
234 : OverlayBase(game
,x
,y
,dragging_allowed
),
237 m_canvas
= new Image( m_filename
.c_str() );
242 class UiOverlay
: public OverlayBase
245 UiOverlay(GameControl
& game
, const Rect
& pos
)
246 : OverlayBase( game
, pos
.tl
.x
, pos
.tl
.y
, true )
247 , m_bgColour(0xf0f0e0)
250 , m_width(pos
.width())
251 , m_height(pos
.height())
253 m_canvas
= new Canvas( pos
.width(), pos
.height() );
254 m_canvas
->setBackground( m_bgColour
);
255 m_canvas
->drawRect( 0, 0, pos
.width(), pos
.height(),
256 m_canvas
->makeColour(0xffffff) );
257 m_canvas
->drawRect( 3, 3, pos
.width()-6, pos
.height()-6,
258 m_canvas
->makeColour(m_bgColour
) );
261 void moveTo( const Vec2
& dest
)
263 if ( dest
!= Vec2(m_x
,m_y
) ) {
269 void onTick( int tick
)
271 if ( m_motion
> 0 ) {
279 m_game
.m_refresh
= 1;
284 void addButton( const char* icon
, const Rect
& r
, void (C::*func
)() )
286 if ( Config::findFile(icon
) != icon
) {
287 Image
*i
= new Image(icon
);
288 m_canvas
->drawImage( i
, r
.tl
.x
, r
.tl
.y
);
292 if ( p
.size() > 1 ) {
293 p
.translate( r
.tl
- p
.bbox().tl
);
294 m_canvas
->drawPath( p
, m_fgColour
, false );
296 Font::headingFont()->drawCenter( m_canvas
, r
.centroid(),
300 addHotSpot( r
, func
);
313 class ListOverlay
: public UiOverlay
316 ListOverlay( GameControl
& game
,
317 ListProvider
* provider
,
320 : UiOverlay(game
,Rect(x
,y
,x
+w
,y
+h
)),
321 m_provider( provider
),
328 m_listarea( m_hgap
, m_vgap
, w
-m_hgap
, h
-m_vgap
)
330 Canvas
*first
= m_provider
->provideItem( m_first
, NULL
);
331 m_itemh
= first
->height();
332 m_numvis
= m_listarea
.height() / (m_itemh
+ m_vgap
) + 2;
334 if ( m_numvis
<= m_provider
->countItems() ) {
335 m_allowscroll
= false;
337 m_allowscroll
= true;
338 m_listarea
.tl
.y
+= 20;
339 m_listarea
.br
.y
-= 20;
340 m_numvis
= m_listarea
.height() / (m_itemh
+ m_vgap
);
341 addButton( "Back", Rect(0,0,w
,20), &ListOverlay::doBack
);
342 addButton( "Forward", Rect(0,y
-20,w
,20), &ListOverlay::doForward
);
345 m_items
= (Canvas
**)calloc( sizeof(Canvas
*), m_numvis
);
348 for ( int i
=1; i
<m_numvis
; i
++ ) {
349 m_items
[i
] = m_provider
->provideItem( i
, NULL
);
356 for ( int i
=0; i
<m_numvis
; i
++ ) {
358 m_provider
->releaseItem( m_items
[0] );
365 m_canvas
->clear( m_listarea
);
366 for ( int i
=0; i
< m_numvis
; i
++ ) {
368 if ( q
< m_provider
->countItems() ) {
369 m_canvas
->drawImage( m_items
[q
%m_numvis
],
370 m_listarea
.tl
.x
+ m_offset
.x
,
371 m_listarea
.tl
.y
+ m_offset
.y
+ (m_itemh
+m_vgap
)*i
);
373 if ( q
== m_current
) {
374 m_canvas
->drawRect( m_listarea
.tl
.x
-1 + m_offset
.x
,
375 m_listarea
.tl
.y
+ (m_itemh
+m_vgap
)*i
- 1 + m_offset
.y
,
376 m_items
[q
%m_numvis
]->width()+2,
377 m_items
[q
%m_numvis
]->height()+2,
385 void onTick( int tick
)
387 UiOverlay::onTick( tick
);
388 if ( m_velocity
.y
) {
389 m_offset
+= m_velocity
;
390 while ( m_offset
.y
< -m_itemh
-m_vgap
) {
391 m_offset
.y
+= m_itemh
+m_vgap
;
394 while ( m_offset
.y
>= m_itemh
+m_vgap
) {
395 m_offset
.y
-= m_itemh
+m_vgap
;
398 if ( m_velocity
.y
> 0 ) m_velocity
.y
--;
399 if ( m_velocity
.y
< 0 ) m_velocity
.y
++;
405 virtual bool onClick( int x
, int y
)
407 int i
= m_first
+ (y
- m_vgap
) / (m_itemh
+ m_vgap
);
408 if ( i
>= 0 && i
< m_provider
->countItems() ) {
410 m_provider
->onSelection( m_current
,
412 y
- m_listarea
.tl
.y
- (m_itemh
+m_vgap
)*i
);
417 virtual bool onKey( int k
)
421 if ( m_current
> 0 ) {
423 if ( m_current
< m_first
) {
430 if ( m_current
< m_provider
->countItems()-1 ) {
432 if ( m_current
>= m_first
+m_numvis
-1 ) {
442 bool onDrag( int dx
, int dy
)
452 m_items
[m_first
%m_numvis
] = m_provider
->provideItem( m_first
,
453 m_items
[m_first
%m_numvis
] );
459 int last
= m_first
+ m_numvis
- 1;
460 if ( last
< m_provider
->countItems() ) {
462 last
= m_first
+ m_numvis
- 1;
463 m_items
[last
%m_numvis
] = m_provider
->provideItem( last
, m_items
[last
%m_numvis
] );
468 ListProvider
*m_provider
;
482 class MenuOverlay
: public UiOverlay
485 MenuOverlay(GameControl
& game
)
486 : UiOverlay(game
,Rect(100,100,400,200))
488 addButton( "48,18 47,25 48,25 47,27 48,27 47,29 48,29 47,31 48,33 47,34 48,35 47,40 48,45 49,43 48,43 49,41 48,39 49,38 48,37 47,36 50,34 49,31 44,31 42,30 40,30 36,32 37,33 36,38 35,45 34,47 35,53 34,54 36,56 37,58 38,59 41,59 42,61 43,62 45,62 47,63 49,63 51,64 59,64 61,62 63,62 64,60 66,60 68,58 69,54 70,52 69,52 68,46 67,46 66,42 63,36 62,36 61,34 60,33 59,33 58,32 54,32 53,31 50,31 54,31 53,33", Rect(10,10,90,90), &MenuOverlay::doPause
);
491 m_game
.gotoLevel( m_game
.m_level
+ 1 );
494 void onShow() { moveTo(Vec2(200,200)); }
498 class NextLevelOverlay
: public UiOverlay
501 NextLevelOverlay( GameControl
& game
, int winner
,
502 int width
=400, int height
=240 )
503 : UiOverlay(game
,Rect(FULLSCREEN_RECT
.centroid().x
-width
/2,
504 FULLSCREEN_RECT
.centroid().y
-height
/2,
505 FULLSCREEN_RECT
.centroid().x
+width
/2,
506 FULLSCREEN_RECT
.centroid().y
+height
/2 ) ),
514 snprintf(text
, 15, "Bravo!");
518 snprintf(text
, 15, "Player %d wins!", winner
+1);
521 Font::titleFont()->drawCenter( m_canvas
, Vec2(200,32), text
, 0 );
523 addHotSpot( Rect(0, 0,100,180), &NextLevelOverlay::doPrevLevel
);
524 addHotSpot( Rect(300, 0,400,180), &NextLevelOverlay::doNextLevel
);
526 addButton( "<<", Rect(13,100,50,140),
527 &NextLevelOverlay::doPrevLevel
);
528 addButton( ">>", Rect(m_width
-60,100,m_width
,140),
529 &NextLevelOverlay::doNextLevel
);
530 addButton( "Action Replay", Rect(0,180,200,240),
531 &NextLevelOverlay::doActionReplay
);
532 addButton( "Continue", Rect(200,180,400,240),
533 &NextLevelOverlay::doContinue
);
539 virtual void onShow()
542 m_game
.m_refresh
= true; //for fullscreen fade
543 m_game
.m_fade
= true;
544 m_selectedLevel
= m_game
.m_level
+1;
546 virtual void onHide()
549 m_game
.m_refresh
= true; //for fullscreen fade
550 m_game
.m_fade
= false;
552 virtual void draw( Canvas
& screen
, const Rect
& area
)
554 UiOverlay::draw( screen
, area
);
556 Font::blurbFont()->drawCenter( &screen
, Vec2(m_x
+200,m_y
+70),
557 m_game
.levels().levelName(m_selectedLevel
), 0 );
558 int icon_x
= m_x
+ (m_width
-m_icon
->width())/2;
559 int icon_y
= m_y
+ (m_height
-m_icon
->height())/2;
560 screen
.drawRect(icon_x
-1, icon_y
-1,
561 m_icon
->width()+2, m_icon
->height()+2,
562 screen
.makeColour(0x909090));
563 screen
.drawImage(m_icon
, icon_x
, icon_y
);
570 if (m_selectedLevel
>0) {
580 void doActionReplay()
582 m_game
.gotoLevel( m_game
.m_level
,true );
586 m_game
.gotoLevel( m_selectedLevel
);
591 if ( m_icon
==NULL
|| m_levelIcon
!= m_selectedLevel
) {
593 //Worker w( NULL, 1, 100, 200 );
597 if ( m_selectedLevel
< m_game
.levels().numLevels() ) {
598 Canvas
temp( SCREEN_WIDTH
, SCREEN_HEIGHT
);
599 if ( m_game
.renderScene( temp
, m_selectedLevel
) ) {
600 m_icon
= temp
.scale( ICON_SCALE_FACTOR
*SCREEN_HEIGHT
/480 );
603 m_icon
= new Image("theend.png");
604 m_caption
= "no more levels!";
605 m_selectedLevel
= m_game
.levels().numLevels();
607 m_levelIcon
= m_selectedLevel
;
615 std::string m_caption
;
620 class EditOverlay
: public IconOverlay
622 int m_saving
, m_sending
;
624 EditOverlay( GameControl
& game
)
625 : IconOverlay(game
,"edit.png"),
626 m_saving(0), m_sending(0)
629 for ( int i
=0; i
<18; i
++ ) {
630 addHotSpot( pos(i
), i
);
632 for ( int i
=0; i
<NUM_BRUSHES
; i
++ ) {
633 m_canvas
->drawRect( pos(i
), m_canvas
->makeColour(brushColours
[i
]), true );
638 int c
= i
%3, r
= i
/3;
639 return Rect( c
*28+13, r
*28+33, c
*28+33, r
*28+53 );
641 int index( int x
, int y
)
645 if ( r
<0 || c
<0 || c
>2 ) return -1;
648 void outline( Canvas
& screen
, int i
, int c
)
650 Rect r
= pos(i
) + Vec2(m_x
,m_y
);
651 r
.tl
.x
-=2; r
.tl
.y
-=2;
652 r
.br
.x
+=2; r
.br
.y
+=2;
653 screen
.drawRect( r
, c
, false );
655 virtual void draw( Canvas
& screen
, const Rect
& area
)
657 IconOverlay::draw( screen
, area
);
658 outline( screen
, m_game
.m_colour
, 0 );
659 if ( m_game
.m_strokeFixed
) outline( screen
, 12, 0 );
660 if ( m_game
.m_strokeSleep
) outline( screen
, 13, 0 );
661 if ( m_game
.m_strokeDecor
) outline( screen
, 14, 0 );
662 if ( m_sending
) outline( screen
, 16, screen
.makeColour((m_sending
--)<<9) );
663 if ( m_saving
) outline( screen
, 17, screen
.makeColour((m_saving
--)<<9) );
664 if ( m_saving
|| m_sending
) dirty();
666 virtual bool onHotSpot( int i
)
669 case 12: m_game
.m_strokeFixed
= ! m_game
.m_strokeFixed
; break;
670 case 13: m_game
.m_strokeSleep
= ! m_game
.m_strokeSleep
; break;
671 case 14: m_game
.m_strokeDecor
= ! m_game
.m_strokeDecor
; break;
672 case 16: if ( m_game
.send() ) m_sending
=10; break;
673 case 17: if ( m_game
.save() ) m_saving
=10; break;
674 default: if (i
<NUM_BRUSHES
) m_game
.m_colour
= i
; else return false;
683 Overlay
* createTextOverlay( GameControl
& game
, const std::string
& text
)
685 return new TextOverlay(game
, text
);
688 Overlay
* createIconOverlay( GameControl
& game
, const char* file
,
690 bool dragging_allowed
)
692 return new IconOverlay( game
, file
, x
, y
, dragging_allowed
);
695 Overlay
* createEditOverlay( GameControl
& game
)
697 return new EditOverlay( game
);
700 Overlay
* createNextLevelOverlay( GameControl
& game
, int winner
)
702 return new NextLevelOverlay( game
, winner
);
705 Overlay
* createListOverlay( GameControl
& game
,
706 ListProvider
* provider
,
710 return new ListOverlay( game
, provider
, x
, y
, w
, h
);
713 Overlay
* createMenuOverlay( GameControl
& game
)
715 return new MenuOverlay( game
);