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