1 // Description:c
2 //   A Selectable
3 //
4 // Copyright (C) 2010 Frank Becker
5 //
6 // This program is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free Software
8 // Foundation;  either version 2 of the License,  or (at your option) any  later
9 // version.
10 //
11 // This program is distributed in the hope that it will be useful,  but  WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
14 //
15 #include "Selectable.hpp"
16 
17 #include "Trace.hpp"
18 #include "Tokenizer.hpp"
19 #include "Config.hpp"
20 #include "Value.hpp"
21 #include "Game.hpp"
22 #include "GameState.hpp"
23 #include "Audio.hpp"
24 #include "Input.hpp"
25 #include "Trigger.hpp"
26 #include "MenuManager.hpp"
27 #include "FontManager.hpp"
28 #include "BitmapManager.hpp"
29 #include "ScoreKeeper.hpp"
30 
31 #include "GLee.h"
32 #include "GLVertexBufferObject.hpp"
33 
34 #include <algorithm>
35 using namespace std;
36 
37 Selectable *Selectable::_active = 0;
38 
Selectable(bool enabled,const BoundingBox & r,const string & info)39 Selectable::Selectable( bool enabled, const BoundingBox &r, const string &info):
40     _enabled(enabled),
41     _inputBox(r),
42     _boundingBox(r),
43     _info(info),
44     _fontWhite(0)
45 {
46     XTRACE();
47 #ifndef DEMO
48     _enabled = true;
49 #endif
50     _fontWhite = FontManagerS::instance()->getFont( "bitmaps/menuWhite");
51     if( !_fontWhite)
52     {
53 	LOG_ERROR << "Unable to load menuWhite font." << endl;
54     }
55 }
56 
~Selectable()57 Selectable::~Selectable()
58 {
59     XTRACE();
60 }
61 
draw(const Point2Di & offset)62 void Selectable::draw( const Point2Di &offset)
63 {
64 #if 0
65     glColor4f( 1.0, 0.2, 0.2, 0.5);
66     vec3f v[4] = {
67         vec3f( _inputBox.min.x+offset.x, _inputBox.min.y+offset.y, 0 ),
68         vec3f( _inputBox.min.x+offset.x, _inputBox.max.y+offset.y, 0 ),
69         vec3f( _inputBox.max.x+offset.x, _inputBox.max.y+offset.y, 0 ),
70         vec3f( _inputBox.max.x+offset.x, _inputBox.min.y+offset.y, 0 ),
71     };
72     GLVBO::DrawQuad( GL_QUADS, v);
73 #endif
74 
75     if( _active == this)
76     {
77 	glColor4f(1.0f, 1.0f, 1.0f, 0.6f);
78 	//_fontWhite->DrawString( _info.c_str(), offset.x+23, offset.y+8, 0.7f, 0.65f);
79 	_fontWhite->DrawString( _info.c_str(), offset.x+122, offset.y+57, 0.7f, 0.65f);
80 	//_fontWhite->DrawString( _info.c_str(), 122, 57, 0.7f, 0.65f);
81     }
82 }
83 
updateActive(UserFeedback feedback)84 void Selectable::updateActive( UserFeedback feedback)
85 {
86     if( (_active != this))
87     {
88         if( _active) _active->deactivate();
89         _active = this;
90         MenuManagerS::instance()->Goto( this);
91 
92         switch( feedback)
93         {
94             case eBeep:
95                 AudioS::instance()->playSample( "sounds/beep.wav");
96                 break;
97 
98             case eTick:
99                 AudioS::instance()->playSample( "sounds/click1.wav");
100                 break;
101 
102             case eNoFeedback:
103             default:
104                 break;
105         }
106     }
107 }
108 
109 //------------------------------------------------------------------------------
110 
EscapeSelectable(bool enabled,const BoundingBox & r,float size)111 EscapeSelectable::EscapeSelectable( bool enabled, const BoundingBox &r, float size):
112     Selectable(enabled, r, "Escape"),
113     _size(size),
114     _icons(0),
115     _exitOn(0),
116     _exitOff(0)
117 {
118     _icons = BitmapManagerS::instance()->getBitmap( "bitmaps/menuIcons");
119     if( !_icons)
120     {
121 	LOG_ERROR << "Unable to load menuIcons." << endl;
122     }
123 
124     _exitOn = _icons->getIndex( "ExitOn");
125     if( _exitOn == -1)
126     {
127 	LOG_ERROR << "ExitOn button not found" << endl;
128     }
129 
130     _exitOff = _icons->getIndex( "ExitOff");
131     if( _exitOff == -1)
132     {
133 	LOG_ERROR << "ExitOff button not found" << endl;
134     }
135 }
136 
select(void)137 void EscapeSelectable::select( void)
138 {
139     MenuManagerS::instance()->Exit( true);
140 }
141 
input(const Trigger & trigger,const bool & isDown,const Point2Di &)142 void EscapeSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &/*offset*/)
143 {
144     if( !_enabled) return;
145 
146     if( !isDown) return;
147 
148     switch( trigger.type)
149     {
150 	case eButtonTrigger:
151 	    MenuManagerS::instance()->Exit( true);
152 	    break;
153 
154 	case eMotionTrigger:
155 	    this->activate();
156 	    break;
157 
158 	default:
159 	    break;
160     }
161 }
162 
activate(bool beQuiet)163 void EscapeSelectable::activate( bool beQuiet)
164 {
165     updateActive( beQuiet ? eNoFeedback : eBeep);
166 }
167 
draw(const Point2Di & offset)168 void EscapeSelectable::draw( const Point2Di &offset)
169 {
170     Selectable::draw(offset);
171 
172     _icons->bind();
173     glColor4f(1.0, 1.0, 1.0, 1.0);
174     glEnable(GL_TEXTURE_2D);
175     if( (_active == this))
176     {
177 	_icons->Draw( _exitOn,
178 	    _boundingBox.min.x + offset.x,
179             _boundingBox.min.y + offset.y, _size, _size);
180     }
181     else
182     {
183 	_icons->Draw( _exitOff,
184 	    _boundingBox.min.x + offset.x,
185             _boundingBox.min.y + offset.y, _size, _size);
186     }
187     glDisable(GL_TEXTURE_2D);
188 }
189 
190 //------------------------------------------------------------------------------
191 
TextOnlySelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info,bool center,float size,float red,float green,float blue)192 TextOnlySelectable::TextOnlySelectable(
193     bool enabled,
194     const BoundingBox &rect,
195     const string &text,
196     const string &info,
197     bool center,
198     float size,
199     float red, float green, float blue):
200 
201     Selectable(enabled, rect, info),
202     _text(text),
203     _fontShadow(0),
204     _icons(0),
205     r(red),
206     g(green),
207     b(blue),
208     _size(size)
209 {
210     _fontShadow = FontManagerS::instance()->getFont( "bitmaps/menuShadow");
211     if( !_fontShadow)
212     {
213 	LOG_ERROR << "Unable to load shadow font." << endl;
214     }
215 
216     float width  = _fontWhite->GetWidth( _text.c_str(), _size);
217     float height = _fontWhite->GetHeight( _size);
218 
219     if( center)
220     {
221 	_boundingBox.min.x -= width*0.5f;
222     }
223     _boundingBox.max.x = _boundingBox.min.x + width;
224     _boundingBox.max.y = _boundingBox.min.y + height;
225 
226     _inputBox = _boundingBox;
227 
228     _icons = BitmapManagerS::instance()->getBitmap( "bitmaps/menuIcons");
229     if( !_icons)
230     {
231 	LOG_ERROR << "Unable to load menuIcons." << endl;
232     }
233 }
234 
input(const Trigger & trigger,const bool &,const Point2Di &)235 void TextOnlySelectable::input( const Trigger &trigger, const bool &/*isDown*/, const Point2Di &/*offset*/)
236 {
237     if( !_enabled) return;
238 
239     switch( trigger.type)
240     {
241 	case eMotionTrigger:
242 	    this->activate();
243 	    break;
244 
245 	default:
246 	    break;
247     }
248 }
249 
activate(bool)250 void TextOnlySelectable::activate( bool /*beQuiet*/)
251 {
252     updateActive( eNoFeedback);
253 }
254 
draw(const Point2Di & offset)255 void TextOnlySelectable::draw( const Point2Di &offset)
256 {
257     Selectable::draw(offset);
258 
259     glColor4f(1.0, 1.0, 1.0, 1.0);
260 
261     _fontShadow->DrawString(
262 	_text.c_str(),
263 	_boundingBox.min.x + offset.x +9*_size,
264         _boundingBox.min.y + offset.y -9*_size,
265 	_size, _size);
266 
267     if( !_enabled)
268         glColor4f(0.5, 0.5, 0.5, 1.0);
269     else
270         glColor4f(r, g, b, 1.0);
271     _fontWhite->DrawString(
272 	_text.c_str(),
273 	_boundingBox.min.x + offset.x,
274         _boundingBox.min.y + offset.y,
275 	_size, _size);
276 }
277 
278 //------------------------------------------------------------------------------
279 
FloatSelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info,const string & variable,const string & range,const string & sliderOffset)280 FloatSelectable::FloatSelectable(
281     bool enabled,
282     const BoundingBox &rect,
283     const string &text,
284     const string &info,
285     const string &variable,
286     const string &range,
287     const string &sliderOffset):
288 
289     TextOnlySelectable(enabled, rect, text, info, false, 0.7f, 1.0f, 1.0f, 1.0f),
290     _variable(variable),
291     _startX(-1.0f)
292 {
293     Tokenizer t(range);
294 
295     _min = (float)atof(t.next().c_str());
296     _max = (float)atof(t.next().c_str());
297 
298     _sliderOffset = atoi(sliderOffset.c_str());
299 
300     float curVal = _min;
301     ConfigS::instance()->getFloat( _variable, curVal);
302     _xPos = (curVal-_min) * 140.0f / (_max-_min);
303 
304     //bounding box for double arrow
305     _bRect.min.x = _boundingBox.min.x + 102 + _sliderOffset;
306     _bRect.max.x = _bRect.min.x + 20;
307     _bRect.min.y = _boundingBox.min.y ;
308     _bRect.max.y = _bRect.min.y + 30;
309 
310     _slider = _icons->getIndex( "Slider");
311     if( _slider == -1)
312     {
313 	LOG_ERROR << "Slider not found" << endl;
314     }
315     _doubleArrow = _icons->getIndex( "DoubleArrow");
316     if( _doubleArrow == -1)
317     {
318 	LOG_ERROR << "DoubleArrow not found" << endl;
319     }
320 
321     _boundingBox.max.x = _bRect.min.x + _icons->getWidth( _slider)*1.6f;
322 
323     _inputBox = _boundingBox;
324 }
325 
incBy(float delta)326 void FloatSelectable::incBy( float delta)
327 {
328     float curVal = _min;
329     ConfigS::instance()->getFloat( _variable, curVal);
330     curVal += delta;
331     Clamp( curVal, _min, _max);
332 
333     LOG_INFO << "New val = " << curVal << "for " << _variable <<  "\n";
334     _xPos = (curVal-_min) * 140.0f / (_max-_min);
335 
336     Value *v = new Value( curVal);
337     ConfigS::instance()->updateKeyword( _variable, v);
338     AudioS::instance()->playSample( "sounds/tick1.wav");
339 }
340 
input(const Trigger & trigger,const bool & isDown,const Point2Di & offset)341 void FloatSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &offset)
342 {
343     if( !_enabled) return;
344 
345     float mouseX = trigger.fData1;
346     float mouseY = trigger.fData2;
347 
348     switch( trigger.type)
349     {
350         case eKeyTrigger:
351             if( isDown && (_active == this))
352             {
353                 switch( trigger.data1)
354                 {
355                     case SDLK_LEFT:
356                         incBy( -0.1);
357                         break;
358 
359                     case SDLK_RIGHT:
360                         incBy( 0.1);
361                         break;
362 
363                     default:
364                         break;
365                 }
366             }
367             break;
368 
369 	case eButtonTrigger:
370 	    if( isDown)
371 	    {
372 		if( (mouseX >= (_bRect.min.x+ offset.x+_xPos)) &&
373 		    (mouseX <= (_bRect.max.x+ offset.x+_xPos)) &&
374 		    (mouseY >= (_bRect.min.y+ offset.y)) &&
375 		    (mouseY <= (_bRect.max.y+ offset.y)) )
376 		{
377 		    _startX = mouseX;
378 		}
379 	    }
380 	    else
381 	    {
382 		_startX = -1.0f;
383 		float curVal = _min;
384 		ConfigS::instance()->getFloat( _variable, curVal);
385 		curVal = _min + _xPos*(_max-_min)/140.0f;
386                 //LOG_INFO << "New val = " << curVal << "\n";
387 
388 		Value *v = new Value( curVal);
389 		ConfigS::instance()->updateKeyword( _variable, v);
390 	    }
391 	    break;
392 
393 	case eMotionTrigger:
394 	    if( _startX >= 0.0f)
395 	    {
396 		float dx =  mouseX - _startX;
397 		_startX = mouseX;
398 
399 		_xPos += dx;
400 		Clamp( _xPos, 0.0, 140.0);
401 	    }
402 
403 	    if( (mouseX >= (_boundingBox.min.x+ offset.x)) &&
404 		(mouseX <= (_boundingBox.max.x+ offset.x)) &&
405 		(mouseY >= (_boundingBox.min.y+ offset.y)) &&
406 		(mouseY <= (_boundingBox.max.y+ offset.y)) )
407 	    {
408 		this->activate();
409 	    }
410 	    break;
411 
412 	default:
413 	    break;
414     }
415 }
416 
activate(bool beQuiet)417 void FloatSelectable::activate( bool beQuiet)
418 {
419     updateActive( beQuiet ? eNoFeedback : eBeep);
420 }
421 
draw(const Point2Di & offset)422 void FloatSelectable::draw( const Point2Di &offset)
423 {
424 #if 0
425     glColor4f( 0.2, 0.2, 1.0, 0.5);
426     vec3f v[4] = {
427         vec3f( _bRect.min.x+ offset.x+_xPos, _bRect.min.y+ offset.y, 0),
428         vec3f( _bRect.min.x+ offset.x+_xPos, _bRect.max.y+ offset.y, 0),
429         vec3f( _bRect.max.x+ offset.x+_xPos, _bRect.max.y+ offset.y, 0),
430         vec3f( _bRect.max.x+ offset.x+_xPos, _bRect.min.y+ offset.y, 0),
431     };
432     GLVBO::DrawQuad( GL_QUADS, v);
433 #endif
434     TextOnlySelectable::draw(offset);
435 
436     _icons->bind();
437     if( _active == this)
438     {
439         if( !_enabled)
440             glColor4f(0.7, 0.7, 0.7, 1.0);
441         else
442             glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
443     }
444     else
445     {
446         if( !_enabled)
447             glColor4f(0.5, 0.5, 0.5, 1.0);
448         else
449             glColor4f(1.0, 1.0, 1.0, 1.0);
450     }
451 
452     glEnable(GL_TEXTURE_2D);
453     _icons->DrawC(
454         _slider,
455         _boundingBox.min.x + offset.x+180+_sliderOffset,
456         _boundingBox.min.y + offset.y+15, 1.6f, 0.4f);
457     _icons->DrawC(
458         _doubleArrow,
459 	_boundingBox.min.x + offset.x+112+_sliderOffset+_xPos,
460         _boundingBox.min.y + offset.y+15, 0.8f, 0.8f);
461     glDisable(GL_TEXTURE_2D);
462 }
463 
464 //------------------------------------------------------------------------------
465 
EnumSelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info,const string & variable,const string & values)466 EnumSelectable::EnumSelectable(
467     bool enabled,
468     const BoundingBox &rect,
469     const string &text,
470     const string &info,
471     const string &variable,
472     const string &values):
473 
474     TextOnlySelectable(enabled, rect, text, info, false, 0.7f, 1.0f, 1.0f, 1.0f),
475     _variable(variable)
476 {
477     string val;
478     ConfigS::instance()->getString( _variable, val);
479 
480     _activeEnum = _enumList.end();
481     Tokenizer t(values);
482     string s = t.next();
483     while( s != "")
484     {
485 	_enumList.insert( _enumList.end(), s);
486 	if( s == val)
487 	{
488 	    _activeEnum = --_enumList.end();
489 	}
490 	s = t.next();
491     }
492 
493     if( _activeEnum == _enumList.end())
494     {
495 	_activeEnum = _enumList.begin();
496     }
497 
498     _xOff = _boundingBox.max.x + 5.0f;
499     _boundingBox.max.x += 100.0f;
500 
501     _inputBox = _boundingBox;
502 }
503 
nextEnum(void)504 void EnumSelectable::nextEnum( void)
505 {
506     _activeEnum++;
507     if( _activeEnum == _enumList.end())
508     {
509         _activeEnum = _enumList.begin();
510     }
511 
512     Value *v = new Value( *_activeEnum);
513     ConfigS::instance()->updateKeyword( _variable, v);
514     AudioS::instance()->playSample( "sounds/click1.wav");
515 }
516 
prevEnum(void)517 void EnumSelectable::prevEnum( void)
518 {
519     if( _activeEnum == _enumList.begin())
520     {
521         _activeEnum = _enumList.end();
522     }
523     _activeEnum--;
524 
525     Value *v = new Value( *_activeEnum);
526     ConfigS::instance()->updateKeyword( _variable, v);
527     AudioS::instance()->playSample( "sounds/click1.wav");
528 }
529 
input(const Trigger & trigger,const bool & isDown,const Point2Di &)530 void EnumSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &/*offset*/)
531 {
532     if( !_enabled) return;
533 
534     if( !isDown) return;
535 
536     switch( trigger.type)
537     {
538         case eKeyTrigger:
539             switch( trigger.data1)
540             {
541                 case SDLK_LEFT:
542                     prevEnum();
543                     break;
544 
545                 case SDLK_RIGHT:
546                     nextEnum();
547                     break;
548 
549                 default:
550                     break;
551             }
552             break;
553 
554 	case eButtonTrigger:
555 	    {
556                 if( trigger.data1 == SDL_BUTTON_WHEELDOWN)
557                 {
558                     prevEnum();
559                 }
560                 else
561                 {
562                     nextEnum();
563                 }
564 	    }
565 	    break;
566 
567 	case eMotionTrigger:
568 	    this->activate();
569 	    break;
570 
571 	default:
572 	    break;
573     }
574 }
575 
activate(bool beQuiet)576 void EnumSelectable::activate( bool beQuiet)
577 {
578     updateActive( beQuiet ? eNoFeedback : eBeep);
579 }
580 
draw(const Point2Di & offset)581 void EnumSelectable::draw( const Point2Di &offset)
582 {
583     TextOnlySelectable::draw(offset);
584 
585     string val = *_activeEnum;
586 
587     glColor4f(1.0, 1.0, 1.0, 1.0);
588     _fontShadow->DrawString(
589 	val.c_str(),
590 	_xOff + offset.x+9*_size,
591         _boundingBox.min.y + offset.y-9*_size,
592 	_size, _size);
593 
594     if( _active == this)
595     {
596         if( !_enabled)
597             glColor4f(0.7, 0.7, 0.7, 1.0);
598         else
599             glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
600     }
601     else
602     {
603         if( !_enabled)
604             glColor4f(0.5, 0.5, 0.5, 1.0);
605         else
606             glColor4f(1.0f, 0.852f, 0.0f, 1.0f);
607     }
608 
609     _fontWhite->DrawString(
610 	val.c_str(),
611 	_xOff + offset.x,
612         _boundingBox.min.y + offset.y,
613 	_size, _size);
614 }
615 
616 //------------------------------------------------------------------------------
617 
BoolSelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info,const string & variable)618 BoolSelectable::BoolSelectable(
619     bool enabled,
620     const BoundingBox &rect,
621     const string &text,
622     const string &info,
623     const string &variable):
624 
625     TextOnlySelectable(enabled, rect, text, info, false, 0.7f, 1.0f, 1.0f, 1.0f),
626     _variable(variable)
627 {
628     _checkmark = _icons->getIndex( "Checkmark");
629     _checkmarkOff = _icons->getIndex( "CheckmarkOff");
630 
631     _xOff = _boundingBox.max.x + 10.0f;
632     _boundingBox.max.x = _xOff + _icons->getWidth( _checkmark)*0.5f;
633 
634     _inputBox = _boundingBox;
635 }
636 
select(void)637 void BoolSelectable::select( void)
638 {
639     if( !_enabled) return;
640 
641     toggle();
642 }
643 
toggle(void)644 void BoolSelectable::toggle( void)
645 {
646     bool val = false;
647     ConfigS::instance()->getBoolean( _variable, val);
648     Value *v = new Value( !val);
649 
650 //    LOG_INFO << "New value for " << _variable
651 //             << " is " << v->getString() << endl;
652 
653     ConfigS::instance()->updateKeyword( _variable, v);
654     AudioS::instance()->playSample( "sounds/click1.wav");
655 }
656 
input(const Trigger & trigger,const bool & isDown,const Point2Di &)657 void BoolSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &/*offset*/)
658 {
659     if( !_enabled) return;
660 
661     if( !isDown) return;
662 
663     switch( trigger.type)
664     {
665 	case eButtonTrigger:
666 	    {
667                 toggle();
668 	    }
669 	    break;
670 
671 	case eMotionTrigger:
672 	    this->activate();
673 	    break;
674 
675 	default:
676 	    break;
677     }
678 }
679 
activate(bool beQuiet)680 void BoolSelectable::activate( bool beQuiet)
681 {
682     updateActive( beQuiet ? eNoFeedback : eBeep);
683 }
684 
draw(const Point2Di & offset)685 void BoolSelectable::draw( const Point2Di &offset)
686 {
687     TextOnlySelectable::draw(offset);
688 
689     bool val = false;
690     ConfigS::instance()->getBoolean( _variable, val);
691 
692     _icons->bind();
693     if( _active == this)
694     {
695         if( !_enabled)
696             glColor4f(0.7, 0.7, 0.7, 1.0);
697         else
698             glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
699     }
700     else
701     {
702         if( !_enabled)
703             glColor4f(0.5, 0.5, 0.5, 1.0);
704         else
705             glColor4f(1.0, 1.0, 1.0, 1.0);
706     }
707 
708     glEnable(GL_TEXTURE_2D);
709     if( val)
710     {
711 	_icons->Draw( _checkmark,
712                       _xOff + offset.x,
713                       _boundingBox.min.y + offset.y+2, 0.5, 0.5);
714     }
715     else
716     {
717 	_icons->Draw( _checkmarkOff,
718                       _xOff + offset.x,
719                       _boundingBox.min.y + offset.y+2, 0.5, 0.5);
720     }
721     glDisable(GL_TEXTURE_2D);
722 }
723 
724 //------------------------------------------------------------------------------
725 
LeaderBoardSelectable(bool enabled,const BoundingBox & r,const string & text,const string & info)726 LeaderBoardSelectable::LeaderBoardSelectable(
727     bool enabled,
728     const BoundingBox &r,
729     const string &text,
730     const string &info):
731 
732     Selectable(enabled, r, info),
733     _text(text),
734     _size(1.0f)
735 {
736     _fontShadow = FontManagerS::instance()->getFont( "bitmaps/menuShadow");
737 
738     float width  = _fontWhite->GetWidth( _text.c_str(), _size);
739     float height = _fontWhite->GetHeight( _size);
740 
741     _boundingBox.min.x -= width*0.5f;
742     _boundingBox.max.x = _boundingBox.min.x + width;
743     _boundingBox.max.y = _boundingBox.min.y + height;
744 
745     _inputBox.min.x =   0;
746     _inputBox.min.y =  30;
747     _inputBox.max.x = 600;
748     _inputBox.max.y = 470;
749 
750     //ScoreKeeperS::instance()->setActive("");
751 }
752 
~LeaderBoardSelectable()753 LeaderBoardSelectable::~LeaderBoardSelectable()
754 {
755 }
756 
input(const Trigger & trigger,const bool & isDown,const Point2Di & offset)757 void LeaderBoardSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &offset)
758 {
759     if( !_enabled) return;
760 
761     if( !isDown) return;
762 
763     float mouseY = trigger.fData2;
764 
765     switch( trigger.type)
766     {
767 	case eButtonTrigger:
768 	    break;
769 
770 	case eMotionTrigger:
771 	    {
772                 float spacing = 36.0f;
773 
774 		int idx = (int)((mouseY+offset.y-100.0f)/spacing);
775 		if( idx < 0) idx = 0;
776 		if( idx > 9) idx = 9;
777 		_info = ScoreKeeperS::instance()->getInfoText( 9-idx);
778 //		LOG_INFO << "spot = " << idx << endl;
779 	    }
780 	    this->activate();
781 	    break;
782 
783 	default:
784 	    break;
785     }
786 }
787 
activate(bool)788 void LeaderBoardSelectable::activate( bool /*beQuiet*/)
789 {
790     updateActive( eNoFeedback);
791 }
792 
draw(const Point2Di & offset)793 void LeaderBoardSelectable::draw( const Point2Di &offset)
794 {
795 #if 0
796     glColor4f( 0.2, 0.2, 1.0, 0.5);
797     vec3f v[4] = {
798         vec3f( _inputBox.min.x+offset.x, _inputBox.min.y+offset.y, 0 ),
799         vec3f( _inputBox.min.x+offset.x, _inputBox.max.y+offset.y, 0 ),
800         vec3f( _inputBox.max.x+offset.x, _inputBox.max.y+offset.y, 0 ),
801         vec3f( _inputBox.max.x+offset.x, _inputBox.min.y+offset.y, 0 ),
802     };
803     GLVBO::DrawQuad( GL_QUADS, v);
804 #endif
805 
806     Selectable::draw(offset);
807 #if 0
808     //Draw "Leader Board:" sub-header
809     glColor4f(1.0, 1.0, 1.0, 1.0);
810     _fontShadow->DrawString(
811 	_text.c_str(),
812 	_boundingBox.min.x + offset.x+9*_size,
813         _boundingBox.min.y + offset.y-9*_size,
814 	_size, _size);
815 
816     glColor4f(1.0, 1.0, 1.0, 1.0);
817     _fontWhite->DrawString(
818 	_text.c_str(),
819 	_boundingBox.min.x + offset.x,
820         _boundingBox.min.y + offset.y,
821 	_size, _size);
822 #endif
823     ScoreKeeperS::instance()->draw(offset);
824 }
825 
826 
827 //------------------------------------------------------------------------------
828 
829 
DescendingAreaOrder(const Resolution & res1,const Resolution & res2)830 bool ResolutionSelectable::DescendingAreaOrder( const Resolution &res1,
831                                                 const Resolution &res2)
832 {
833     return res1.width*res1.height < res2.width*res2.height;
834 }
835 
ResolutionSelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info)836 ResolutionSelectable::ResolutionSelectable(
837     bool enabled,
838     const BoundingBox &rect,
839     const string &text,
840     const string &info):
841 
842     TextOnlySelectable(enabled, rect, text, info, false, 0.7f, 1.0f, 1.0f, 1.0f)
843 {
844     _fontShadow = FontManagerS::instance()->getFont( "bitmaps/menuShadow");
845 
846     _checkmark = _icons->getIndex( "Checkmark");
847     _checkmarkOff = _icons->getIndex( "CheckmarkOff");
848 
849     //Get current resolution
850     int currentWidth = 640;
851     ConfigS::instance()->getInteger( "width", currentWidth);
852     int currentHeight = 480;
853     ConfigS::instance()->getInteger( "height", currentHeight);
854 
855     //get custom resolutions from config file
856     string resolutions;
857     if( !ConfigS::instance()->getString( "resolutions", resolutions))
858     {
859 	resolutions = "512x384,640x480,800x600,1024x768,1152x864,1280x960,1600x1200";
860 	Value *res = new Value( resolutions);
861 	ConfigS::instance()->updateKeyword( "resolutions", res);
862     }
863 
864     //extract resolutions
865     Tokenizer rToken(resolutions, ",");
866     int count = 1;
867     while( count < 20)
868     {
869 	string nextRes = rToken.next();
870 	if( nextRes == "") break;
871 
872 	Tokenizer t(nextRes, "x");
873 
874 	int width = atoi(t.next().c_str());
875 	int height = atoi(t.next().c_str());
876 
877 	_resolutionList.push_back( Resolution(width, height));
878 
879 	count++;
880     }
881 
882     //Add other fullscreen resolutions
883     addFullscreenResolutions();
884 
885     if( _resolutionList.size() == 0 )
886     {
887         _resolutionList.push_back( Resolution(currentWidth, currentHeight));
888     }
889 
890     //sort resolutions by area
891     std::sort(_resolutionList.begin(), _resolutionList.end(), DescendingAreaOrder);
892 
893     float fwidth  = 0;
894     float fheight = _fontWhite->GetHeight( _size);
895 
896     _activeResolution = _resolutionList.begin();
897     vector<Resolution>::iterator i;
898     for( i=_resolutionList.begin(); i!=_resolutionList.end(); i++)
899     {
900         Resolution res = (*i);
901 	if( ((*i).width == currentWidth) && ((*i).height == currentHeight))
902 	{
903 	    _activeResolution = i;
904 	}
905 
906         float nextfwidth  = _fontWhite->GetWidth( (*i).text.c_str(), _size);
907         if( nextfwidth > fwidth) fwidth = nextfwidth;
908     }
909 
910 
911 //    _boundingBox.min.x -= fwidth*0.5;
912     _boundingBox.max.x =
913         _boundingBox.min.x + fwidth +
914             _fontWhite->GetWidth( _text.c_str(), _size) +
915         _icons->getWidth( _checkmark)*0.5f + 10;
916     _boundingBox.max.y = _boundingBox.min.y + fheight;
917 
918     //bounding box for checkmark
919     _bRect.min.x = _boundingBox.max.x - _icons->getWidth( _checkmark)*0.5f;
920     _bRect.max.x = _boundingBox.max.x;
921     _bRect.min.y = _boundingBox.min.y ;
922     _bRect.max.y = _bRect.min.y + 30;
923 
924     _inputBox = _boundingBox;
925 }
926 
select(void)927 void ResolutionSelectable::select( void)
928 {
929     applyResolution();
930 }
931 
applyResolution(void)932 void ResolutionSelectable::applyResolution( void)
933 {
934     int width = (*_activeResolution).width;
935     int height = (*_activeResolution).height;
936 
937     Value *w = new Value( width);
938     ConfigS::instance()->updateKeyword( "width", w);
939     Value *h = new Value( height);
940     ConfigS::instance()->updateKeyword( "height", h);
941     AudioS::instance()->playSample( "sounds/click1.wav");
942 }
943 
addFullscreenResolutions(void)944 void ResolutionSelectable::addFullscreenResolutions(void)
945 {
946     SDL_Rect **modes=SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_OPENGL);
947 
948     if( modes == (SDL_Rect **)0)
949     {
950 	return;
951     }
952 
953     if( modes == (SDL_Rect **)-1)
954     {
955 	return;
956     }
957 
958     for(int i=0; modes[i]; i++)
959     {
960 	Resolution newRes( modes[i]->w, modes[i]->h);
961 
962 	bool add = true;
963 	vector<Resolution>::iterator res;
964         for( res=_resolutionList.begin(); res!=_resolutionList.end(); res++)
965         {
966 	    if( (*res) == newRes)
967 	    {
968 		//LOG_INFO << "Found dup " << buf << endl;
969 		add = false;
970 		break;
971 	    }
972         }
973 
974 	if( add)
975 	{
976 	    //LOG_INFO << "Adding new " << newRes.text << endl;
977 	    _resolutionList.push_back( newRes);
978 	}
979     }
980 }
981 
prevResolution(void)982 void ResolutionSelectable::prevResolution( void)
983 {
984     if( _activeResolution == _resolutionList.begin())
985     {
986         _activeResolution = _resolutionList.end();
987     }
988     _activeResolution--;
989     AudioS::instance()->playSample( "sounds/tick1.wav");
990 }
991 
nextResolution(void)992 void ResolutionSelectable::nextResolution( void)
993 {
994     _activeResolution++;
995     if( _activeResolution == _resolutionList.end())
996     {
997         _activeResolution = _resolutionList.begin();
998     }
999     AudioS::instance()->playSample( "sounds/tick1.wav");
1000 }
1001 
input(const Trigger & trigger,const bool & isDown,const Point2Di & offset)1002 void ResolutionSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &offset)
1003 {
1004     if( !_enabled) return;
1005 
1006     if( !isDown) return;
1007 
1008     float mouseX = trigger.fData1;
1009     float mouseY = trigger.fData2;
1010 
1011     switch( trigger.type)
1012     {
1013         case eKeyTrigger:
1014             switch( trigger.data1)
1015             {
1016                 case SDLK_LEFT:
1017                     prevResolution();
1018                     break;
1019 
1020                 case SDLK_RIGHT:
1021                     nextResolution();
1022                     break;
1023 
1024                 default:
1025                     break;
1026             }
1027             break;
1028 
1029 	case eButtonTrigger:
1030 	    {
1031 		if( isDown)
1032 		{
1033 		    if( (mouseX >= (_bRect.min.x+offset.x)) &&
1034 			(mouseX <= (_bRect.max.x+offset.x)) &&
1035 			(mouseY >= (_bRect.min.y+offset.y)) &&
1036 			(mouseY <= (_bRect.max.y+offset.y)) )
1037 		    {
1038                         applyResolution();
1039 		    }
1040 		    else
1041 		    {
1042                         if( trigger.data1 == SDL_BUTTON_WHEELDOWN)
1043                         {
1044                             prevResolution();
1045                         }
1046                         else
1047                         {
1048                             nextResolution();
1049                         }
1050 		    }
1051 		}
1052 	    }
1053 	    break;
1054 
1055 	case eMotionTrigger:
1056 	    this->activate();
1057 	    break;
1058 
1059 	default:
1060 	    break;
1061     }
1062 }
1063 
activate(bool beQuiet)1064 void ResolutionSelectable::activate( bool beQuiet)
1065 {
1066     updateActive( beQuiet ? eNoFeedback : eBeep);
1067 }
1068 
draw(const Point2Di & offset)1069 void ResolutionSelectable::draw( const Point2Di &offset)
1070 {
1071 //    Selectable::draw(offset);
1072     TextOnlySelectable::draw(offset);
1073     string resolution = /*_text +*/ (*_activeResolution).text;
1074 
1075     float xOff = _fontWhite->GetWidth( _text.c_str(), _size);
1076     glColor4f(1.0, 1.0, 1.0, 1.0);
1077     _fontShadow->DrawString(
1078 	resolution.c_str(),
1079 	xOff+_boundingBox.min.x + offset.x+9*_size,
1080         _boundingBox.min.y + offset.y-9*_size,
1081 	_size, _size);
1082 
1083     glColor4f(1.0f, 0.852f, 0.0f, 1.0f);
1084 //    glColor4f(1.0, 1.0, 1.0, 1.0);
1085     _fontWhite->DrawString(
1086 	resolution.c_str(),
1087 	xOff+_boundingBox.min.x + offset.x,
1088         _boundingBox.min.y + offset.y,
1089 	_size, _size);
1090 
1091     _icons->bind();
1092     if( _active == this)
1093     {
1094         if( !_enabled)
1095             glColor4f(0.7, 0.7, 0.7, 1.0);
1096         else
1097             glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
1098     }
1099     else
1100     {
1101         if( !_enabled)
1102             glColor4f(0.5, 0.5, 0.5, 1.0);
1103         else
1104             glColor4f(1.0, 1.0, 1.0, 1.0);
1105     }
1106 
1107     glEnable(GL_TEXTURE_2D);
1108     float _xOff = _bRect.min.x;
1109 
1110     int currentWidth;
1111     ConfigS::instance()->getInteger( "width", currentWidth);
1112     int currentHeight;
1113     ConfigS::instance()->getInteger( "height", currentHeight);
1114 
1115     int width = (*_activeResolution).width;
1116     int height = (*_activeResolution).height;
1117 
1118     if( (currentWidth == width) && (currentHeight == height))
1119     {
1120 	_icons->Draw( _checkmark,
1121                       _xOff + offset.x,
1122                       _boundingBox.min.y + offset.y+2, 0.5, 0.5);
1123     }
1124     else
1125     {
1126 	_icons->Draw( _checkmarkOff,
1127                       _xOff + offset.x,
1128                       _boundingBox.min.y + offset.y+2, 0.5, 0.5);
1129     }
1130     glDisable(GL_TEXTURE_2D);
1131 }
1132 
1133 //------------------------------------------------------------------------------
1134 
TextSelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info)1135 TextSelectable::TextSelectable(
1136     bool enabled,
1137     const BoundingBox &rect,
1138     const string &text,
1139     const string &info):
1140 
1141     TextOnlySelectable(enabled, rect, text, info),
1142     _ds(0.0)
1143 {
1144     _prevSize = _size;
1145 }
1146 
input(const Trigger & trigger,const bool & isDown,const Point2Di &)1147 void TextSelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &/*offset*/)
1148 {
1149     if( !_enabled) return;
1150 
1151     if( !isDown) return;
1152 
1153     switch( trigger.type)
1154     {
1155 	case eButtonTrigger:
1156 	    MenuManagerS::instance()->Enter();
1157 	    break;
1158 
1159 	case eMotionTrigger:
1160 	    this->activate();
1161 	    break;
1162 
1163 	default:
1164 	    break;
1165     }
1166 }
1167 
activate(bool beQuiet)1168 void TextSelectable::activate( bool beQuiet)
1169 {
1170     updateActive( beQuiet ? eNoFeedback : eBeep);
1171     _ds = 0.1f;
1172 }
1173 
deactivate(void)1174 void TextSelectable::deactivate( void)
1175 {
1176 //	LOG_INFO << "Deactivate " << _text << endl;
1177     _ds = -0.1f;
1178 }
1179 
update(void)1180 void TextSelectable::update( void)
1181 {
1182     if( !_enabled) return;
1183 
1184     _prevSize = _size;
1185     _size += _ds;
1186     Clamp( _size, 1.0, 1.8); //any bigger and we'll have overlapping activation areas
1187 
1188     //adjust the input box according to the scaled text
1189     float dx = (float)(_boundingBox.max.x - _boundingBox.min.x) * (_size-1.0f) / 2.0f;
1190     float dy = (float)(_boundingBox.max.y - _boundingBox.min.y) * (_size-1.0f) / 2.0f;
1191 
1192     _inputBox.min.x = _boundingBox.min.x - dx;
1193     _inputBox.min.y = _boundingBox.min.y - dy;
1194     _inputBox.max.x = _boundingBox.max.x + dx;
1195     _inputBox.max.y = _boundingBox.max.y + dy;
1196 }
1197 
draw(const Point2Di & offset)1198 void TextSelectable::draw( const Point2Di &offset)
1199 {
1200     Selectable::draw(offset);
1201 
1202     float iSize = _prevSize + (_size - _prevSize) *
1203 	GameState::frameFractionOther;
1204     Clamp( iSize, 1.0, 2.0);
1205 
1206     float halfWidth = _fontWhite->GetWidth( _text.c_str(), iSize-1.0f)/2.0f;
1207     float halfHeight = _fontWhite->GetHeight( iSize-1.0f)/2.0f;
1208 
1209     glColor4f(1.0, 1.0, 1.0, 1.0);
1210     _fontShadow->DrawString(
1211 	_text.c_str(),
1212 	_boundingBox.min.x + offset.x-halfWidth+5*iSize,
1213 	_boundingBox.min.y + offset.y-halfHeight-5*iSize,
1214 	iSize, iSize);
1215 
1216     if( !_enabled)
1217         glColor4f(0.5, 0.5, 0.5, 1.0);
1218     else
1219         glColor4f(r, g, b, 0.8f);
1220     _fontWhite->DrawString(
1221 	_text.c_str(),
1222 	_boundingBox.min.x + offset.x-halfWidth,
1223         _boundingBox.min.y + offset.y-halfHeight,
1224 	iSize, iSize);
1225 }
1226 
1227 //------------------------------------------------------------------------------
1228 
ActionSelectable(bool enabled,const BoundingBox & rect,const string & action,const string & text,const string & info)1229 ActionSelectable::ActionSelectable(
1230     bool enabled,
1231     const BoundingBox &rect,
1232     const string &action,
1233     const string &text,
1234     const string &info):
1235 
1236     TextSelectable(enabled, rect, text, info),
1237     _action(action)
1238 {
1239 }
1240 
select(void)1241 void ActionSelectable::select( void)
1242 {
1243 //	LOG_INFO << "Selecting: " << _action << endl;
1244     if( _action == "NewGame")
1245     {
1246 	GameS::instance()->startNewGame();
1247 
1248 	//no confirm sound in this case
1249 	return;
1250     }
1251     else if( _action == "Quit")
1252     {
1253 	GameState::isAlive = false;
1254     }
1255     AudioS::instance()->playSample( "sounds/confirm.wav");
1256 }
1257 
1258 //------------------------------------------------------------------------------
1259 
MenuSelectable(TiXmlNode * node,bool enabled,const BoundingBox & rect,const string & text,const string & info)1260 MenuSelectable::MenuSelectable(
1261 	TiXmlNode *node,
1262         bool enabled,
1263 	const BoundingBox &rect,
1264 	const string &text,
1265 	const string &info):
1266 
1267     TextSelectable(enabled, rect, text, info),
1268     _node(node)
1269 {
1270 }
1271 
select(void)1272 void MenuSelectable::select( void)
1273 {
1274     MenuManagerS::instance()->makeMenu( _node);
1275     AudioS::instance()->playSample( "sounds/confirm.wav");
1276 }
1277 
1278 
1279 //------------------------------------------------------------------------------
1280 
BindKeySelectable(bool enabled,const BoundingBox & rect,const string & text,const string & info,const std::string & action,float size)1281 BindKeySelectable::BindKeySelectable( bool enabled,
1282                                       const BoundingBox &rect,
1283                                       const string &text,
1284                                       const string &info,
1285                                       const std::string &action,
1286                                       float size):
1287 TextOnlySelectable(enabled, rect, text, info, false, size, 1.0f, 1.0f, 1.0f),
1288 _bindKeyOn(0),
1289 _bindKeyOff(0),
1290 _action(action)
1291 {
1292     _icons = BitmapManagerS::instance()->getBitmap( "bitmaps/scoreBoard");
1293 
1294     _bindKeyOn = _icons->getIndex( _action+"On");
1295     if( _bindKeyOn == -1)
1296     {
1297 	LOG_ERROR << "Icon-On for action not found" << endl;
1298     }
1299 
1300     _bindKeyOff = _icons->getIndex( _action+"Off");
1301     if( _bindKeyOff == -1)
1302     {
1303 	LOG_ERROR << "Icon-Off for action not found" << endl;
1304     }
1305 
1306     _size = _size * 0.6;
1307     _boundingBox.max.x = _boundingBox.min.x + _icons->getWidth( _bindKeyOn)*_size;
1308     _boundingBox.max.y = _boundingBox.min.y + _icons->getHeight( _bindKeyOn)*_size;
1309     _inputBox = _boundingBox;
1310 }
1311 
select(void)1312 void BindKeySelectable::select( void)
1313 {
1314     InputS::instance()->bindNextTrigger( _action);
1315 }
1316 
input(const Trigger & trigger,const bool & isDown,const Point2Di &)1317 void BindKeySelectable::input( const Trigger &trigger, const bool &isDown, const Point2Di &/*offset*/)
1318 {
1319     if( !_enabled) return;
1320 
1321     if( !isDown) return;
1322 
1323     switch( trigger.type)
1324     {
1325 	case eButtonTrigger:
1326             InputS::instance()->bindNextTrigger( _action);
1327 	    break;
1328 
1329 	case eMotionTrigger:
1330 	    this->activate();
1331 	    break;
1332 
1333 	default:
1334 	    break;
1335     }
1336 }
1337 
activate(bool beQuiet)1338 void BindKeySelectable::activate( bool beQuiet)
1339 {
1340     updateActive( beQuiet ? eNoFeedback : eBeep);
1341 }
1342 
draw(const Point2Di & offset)1343 void BindKeySelectable::draw( const Point2Di &offset)
1344 {
1345     _text = InputS::instance()->getTriggerName(_action);
1346     //TextOnlySelectable::draw(offset);
1347 
1348     Selectable::draw(offset);
1349 
1350     float fontScale = _size * 1.8;
1351     float center = (_boundingBox.max.x - _boundingBox.min.x - _fontWhite->GetWidth(_text.c_str(), fontScale)) / 2.0 ;
1352     glColor4f(1.0, 1.0, 1.0, 1.0);
1353     _fontShadow->DrawString(
1354                              _text.c_str(),
1355                              center + _boundingBox.min.x + offset.x +9*fontScale,
1356                              _boundingBox.min.y + offset.y -9*fontScale - 20.0,
1357                              fontScale, fontScale);
1358 
1359     if( !_enabled)
1360         glColor4f(0.5, 0.5, 0.5, 1.0);
1361     else
1362         glColor4f(r, g, b, 1.0);
1363     _fontWhite->DrawString(
1364                             _text.c_str(),
1365                             center + _boundingBox.min.x + offset.x,
1366                             _boundingBox.min.y + offset.y - 20.0,
1367                             fontScale, fontScale);
1368 
1369 
1370     _icons->bind();
1371     glColor4f(1.0, 1.0, 1.0, 1.0);
1372     glEnable(GL_TEXTURE_2D);
1373     if( _active == this)
1374     {
1375         if( InputS::instance()->waitingForBind())
1376         {
1377             glColor4f(1.0f, 0.852f, 0.0f, 1.0f);
1378         }
1379         else
1380         {
1381             glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
1382         }
1383 	_icons->Draw( _bindKeyOn,
1384                       _boundingBox.min.x + offset.x,
1385                       _boundingBox.min.y + offset.y, _size, _size);
1386     }
1387     else
1388     {
1389 	_icons->Draw( _bindKeyOff,
1390                       _boundingBox.min.x + offset.x,
1391                       _boundingBox.min.y + offset.y, _size, _size);
1392     }
1393     glDisable(GL_TEXTURE_2D);
1394 }
1395 
1396 //------------------------------------------------------------------------------
1397