1 // Description:
2 // Menu Manager/Controller.
3 //
4 // Copyright (C) 2001 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 "SDL/SDL.h" //key syms
16
17 #include <Trace.hpp>
18 #include <MenuManager.hpp>
19 #include <XMLHelper.hpp>
20 #include <GameState.hpp>
21 #include <Game.hpp>
22 #include <Audio.hpp>
23 #include <Input.hpp>
24 #include <FontManager.hpp>
25 #include <SelectableFactory.hpp>
26 #include <ModelManager.hpp>
27 #include <Video.hpp>
28 #include <BitmapManager.hpp>
29 #include <Config.hpp>
30
31 #include "GLee.h"
32
33 #include <GLExtensionTextureCubeMap.hpp>
34 #include <GLTextureCubeMap.hpp>
35 #include <ResourceManager.hpp>
36 #include <zrwops.hpp>
37
38 using namespace std;
39
MenuManager()40 MenuManager::MenuManager():
41 _menu(0),
42 _topMenu(0),
43 _currentMenu(0),
44 _mouseX(200.0),
45 _mouseY(650.0),
46 _prevContext(Context::eUnknown),
47 _delayedExit(false),
48 _newLevelLoaded(false),
49 _angle(0.0),
50 _prevAngle(0.0),
51 _showSparks(true),
52 _burst( "SparkBurst", 1000),
53 _nextGenShippyCubeMap(0)
54 {
55 XTRACE();
56
57 updateSettings();
58 }
59
~MenuManager()60 MenuManager::~MenuManager()
61 {
62 XTRACE();
63
64 clearActiveSelectables();
65
66 SelectableFactory::cleanup();
67
68 delete _menu;
69 _menu = 0;
70
71 delete _nextGenShippyCubeMap;
72 }
73
init(void)74 bool MenuManager::init( void)
75 {
76 XTRACE();
77 _menu = XMLHelper::load( "system/Menu.xml");
78 if( !_menu)
79 {
80 _menu = 0;
81 return false;
82 }
83
84 _currentMenu = _menu->FirstChild("Menu");
85 _topMenu = _currentMenu;
86
87 loadMenuLevel();
88
89 GLBitmapCollection *icons =
90 BitmapManagerS::instance()->getBitmap( "bitmaps/menuIcons");
91 if( !icons)
92 {
93 LOG_ERROR << "Unable to load menuIcons." << endl;
94 return false;
95 }
96 _pointer = icons->getIndex( "Pointer");
97
98 GLBitmapCollection *menuBoard =
99 BitmapManagerS::instance()->getBitmap( "bitmaps/menuBoard");
100 if( !menuBoard)
101 {
102 LOG_ERROR << "Unable to load menuBoard." << endl;
103 return false;
104 }
105 _board = menuBoard->getIndex( "MenuBoard");
106
107 _mapleLeaf = ModelManagerS::instance()->getModel("models/MapleLeaf");
108 if( !_mapleLeaf)
109 {
110 LOG_ERROR << "Unable to load Maple Leaf, eh?" << endl;
111 return false;
112 }
113
114 _nextGenShippy = ModelManagerS::instance()->getModel("models/NextGenShippy");
115 if( !_nextGenShippy)
116 {
117 LOG_ERROR << "Unable to sphere."<< endl;
118 return false;
119 }
120
121 GLExtensionTextureCubeMap _nextGenShippyExt;
122 if( _nextGenShippyExt.isSupported())
123 {
124 SDL_Surface *images[6];
125 for( int i=0; i<6; i++)
126 {
127 char buf[128];
128 sprintf( buf, "bitmaps/cubemap_%d.png", i);
129 string fileName = buf;
130 if( ResourceManagerS::instance()->selectResource( fileName))
131 {
132 ziStream &bminfile1 = ResourceManagerS::instance()->getInputStream();
133 SDL_RWops *src = RWops_from_ziStream( bminfile1);
134 images[i] = IMG_LoadPNG_RW( src);
135 SDL_RWclose( src);
136 }
137 else
138 {
139 LOG_ERROR << "Could not load cubemap image\n";
140 images[i] = 0;
141 }
142 }
143
144 _nextGenShippyCubeMap = new GLTextureCubeMap( images);
145 }
146 else
147 {
148 LOG_WARNING << "ARB_texture_cube_map not supported\n";
149 }
150
151 _burst.init();
152
153 return true;
154 }
155
updateSettings(void)156 void MenuManager::updateSettings( void)
157 {
158 ConfigS::instance()->getBoolean( "showSparks", _showSparks);
159 }
160
clearActiveSelectables(void)161 void MenuManager::clearActiveSelectables( void)
162 {
163 list<Selectable*>::iterator i;
164 for( i=_activeSelectables.begin(); i!=_activeSelectables.end(); i++)
165 {
166 delete (*i);
167 }
168 _activeSelectables.clear();
169
170 Selectable::reset();
171 }
172
loadMenuLevel(void)173 void MenuManager::loadMenuLevel( void)
174 {
175 _newLevelLoaded = true;
176 clearActiveSelectables();
177
178 TiXmlNode *node = _currentMenu->FirstChild();
179 while( node)
180 {
181 // LOG_INFO << "MenuItem: [" << node->Value() << "]" << endl;
182
183 SelectableFactory *selF = SelectableFactory::getFactory( node->Value());
184 if( selF)
185 {
186 Selectable *sel = selF->createSelectable( node);
187 if( sel)
188 {
189 _activeSelectables.insert( _activeSelectables.end(), sel);
190 }
191 }
192 else
193 {
194 LOG_WARNING << "No Factory found for:" << node->Value() << endl;
195 }
196 node = node->NextSibling();
197 }
198
199 //add escape button
200 if( _currentMenu != _topMenu)
201 {
202 BoundingBox r;
203 r.min.x = 860;
204 r.min.y = 530;
205 r.max.x = r.min.x+15;
206 r.max.y = r.min.y+15;
207
208 Selectable *sel = new EscapeSelectable( true, r, 2.0);
209 _activeSelectables.insert( _activeSelectables.end(), sel);
210 }
211 _currentSelectable = _activeSelectables.end();
212 activateSelectableUnderMouse();
213 }
214
makeMenu(TiXmlNode * _node)215 void MenuManager::makeMenu( TiXmlNode *_node)
216 {
217 _currentMenu = _node;
218 loadMenuLevel();
219 }
220
update(void)221 bool MenuManager::update( void)
222 {
223 static float nextTime = Timer::getTime()+0.5f;
224 float thisTime = Timer::getTime();
225 if( thisTime > nextTime)
226 {
227 updateSettings();
228 nextTime = thisTime+0.5f;
229 }
230 #ifdef USE_ONLINE_UPDATE
231 _onlineUpdateDisplay.update();
232 #endif
233 _prevAngle = _angle;
234 _angle += 10.0f;
235
236 if( _delayedExit)
237 {
238 if( !Exit())
239 {
240 turnMenuOff();
241 }
242 _delayedExit = false;
243 }
244 list<Selectable*>::iterator i;
245 for( i=_activeSelectables.begin(); i!=_activeSelectables.end(); i++)
246 {
247 (*i)->update();
248 }
249
250 _prevMouseX = _mouseX;
251 _prevMouseY = _mouseY;
252
253 if( _showSparks)
254 {
255 GLBitmapCollection *icons =
256 BitmapManagerS::instance()->getBitmap( "bitmaps/menuIcons");
257 float w = _mouseX + icons->getWidth( _pointer)*0.4f;
258 float h = _mouseY - icons->getHeight( _pointer)*0.4f;
259 for( int i=0; i<10; i++)
260 {
261 _burst.newParticle( "Spark", w-i, h+i, 0);
262 }
263
264 _burst.update();
265 }
266
267 return true;
268 }
269
draw(void)270 bool MenuManager::draw( void)
271 {
272 if( GameState::context != Context::eMenu) return true;
273
274 glEnable( GL_LIGHTING);
275 glEnable( GL_DEPTH_TEST);
276
277 GLfloat light_position[] = { 820.0, 620.0, 500.0, 0.0 };
278 glLightfv(GL_LIGHT0, GL_POSITION, light_position);
279
280 glPushMatrix();
281 glTranslatef( 820.0, 620.0, 0.0);
282 float iAngle = _prevAngle+(_angle-_prevAngle)*GameState::frameFractionOther;
283 glRotatef(iAngle, 0,1.0,0);
284 _mapleLeaf->draw();
285 glPopMatrix();
286
287 if( _nextGenShippyCubeMap)
288 {
289 glPushMatrix();
290 glTranslatef( 200.0, 620.0, 0.0);
291 iAngle = _prevAngle+(_angle-_prevAngle)*GameState::frameFractionOther;
292 glRotatef(iAngle/10.0, 0.0,1.0,0);
293 _nextGenShippyCubeMap->bind();
294 _nextGenShippyCubeMap->enable();
295 _nextGenShippy->draw();
296 _nextGenShippyCubeMap->disable();
297 glPopMatrix();
298 }
299
300 glDisable( GL_DEPTH_TEST);
301 glDisable( GL_LIGHTING);
302 #ifdef USE_ONLINE_UPDATE
303 _onlineUpdateDisplay.draw();
304 #endif
305 GLBitmapCollection *menuBoard =
306 BitmapManagerS::instance()->getBitmap( "bitmaps/menuBoard");
307 menuBoard->bind();
308
309 float orthoHeight = 750.0;
310 float orthoWidth = 1000.0;
311
312 float boardScaleX = 800.0/256.0;
313 float boardScaleY = 2.0;
314 _boardOffset.x = (int)((orthoWidth - (float)menuBoard->getWidth(_board)*boardScaleX) / 2.0);
315 _boardOffset.y = (int)((orthoHeight - (float)menuBoard->getHeight(_board)*boardScaleY) / 5.0);
316
317 glColor4f(1.0, 1.0, 1.0, 0.7f);
318 glEnable(GL_TEXTURE_2D);
319 menuBoard->Draw( _board, _boardOffset.x, _boardOffset.y, boardScaleX, boardScaleY);
320 glDisable(GL_TEXTURE_2D);
321
322 TiXmlElement* elem = _currentMenu->ToElement();
323 const char *val = elem->Attribute("Text");
324 if( val)
325 {
326 GLBitmapFont &fontWhite =
327 *(FontManagerS::instance()->getFont( "bitmaps/menuWhite"));
328 glColor4f(1.0, 1.0, 1.0, 1.0);
329 fontWhite.DrawString( val, _boardOffset.x + 21, _boardOffset.y + 477, 0.5, 0.5);
330 }
331
332 list<Selectable*>::iterator i;
333 _boardOffset.x=0;
334 _boardOffset.y=0;
335 for( i=_activeSelectables.begin(); i!=_activeSelectables.end(); i++)
336 {
337 (*i)->draw( _boardOffset);
338 }
339
340 glEnable(GL_TEXTURE_2D);
341 if( _showSparks)
342 {
343 _burst.draw();
344 }
345
346 GLBitmapCollection *icons =
347 BitmapManagerS::instance()->getBitmap( "bitmaps/menuIcons");
348 icons->bind();
349 glColor4f(1.0, 1.0, 1.0, 1.0);
350 float gf = GameState::frameFractionOther;
351 float interpMouseX = _prevMouseX + (_mouseX-_prevMouseX)*gf;
352 float interpMouseY = _prevMouseY + (_mouseY-_prevMouseY)*gf;
353 icons->Draw( _pointer, interpMouseX, interpMouseY, 0.5, 0.5);
354 glDisable(GL_TEXTURE_2D);
355
356 return true;
357 }
358
reload(void)359 void MenuManager::reload( void)
360 {
361 _nextGenShippyCubeMap->reload();
362 }
363
turnMenuOn(void)364 void MenuManager::turnMenuOn( void)
365 {
366 AudioS::instance()->playSample( "sounds/humm.wav");
367 _prevContext = GameState::context;
368 GameState::context = Context::eMenu;
369
370 //ask input system to forward all input to us
371 InputS::instance()->enableInterceptor( this);
372 GameState::stopwatch.pause();
373
374 _currentSelectable = _activeSelectables.end();
375 Selectable::reset();
376 }
377
turnMenuOff(void)378 void MenuManager::turnMenuOff( void)
379 {
380 if( _prevContext == Context::eUnknown) return;
381
382 AudioS::instance()->playSample( "sounds/humm.wav");
383 GameState::context = _prevContext;
384
385 //don't want anymore input
386 InputS::instance()->disableInterceptor();
387
388 if( GameState::context == Context::eInGame)
389 {
390 GameState::stopwatch.start();
391 }
392 }
393
input(const Trigger & trigger,const bool & isDown)394 void MenuManager::input( const Trigger &trigger, const bool &isDown)
395 {
396 Trigger t = trigger;
397 if( isDown)
398 {
399 switch( trigger.type)
400 {
401 case eKeyTrigger:
402 switch( trigger.data1)
403 {
404 case SDLK_RETURN:
405 Enter();
406 break;
407
408 case SDLK_ESCAPE:
409 if( !Exit())
410 {
411 turnMenuOff();
412 }
413 break;
414
415 case SDLK_UP:
416 Up();
417 break;
418 case SDLK_DOWN:
419 Down();
420 break;
421
422 case SDLK_F12:
423 VideoS::instance()->takeSnapshot();
424 break;
425
426 default:
427 break;
428 }
429 break;
430
431 case eButtonTrigger:
432 _prevMouseX = _mouseX;
433 case eMotionTrigger:
434 {
435 _prevMouseX = _mouseX;
436 _prevMouseY = _mouseY;
437
438 _mouseX += (trigger.fData1*10.0f);
439 _mouseY += (trigger.fData2*10.0f);
440
441 Clamp( _mouseX, 0.0f, 1000.0f);
442 Clamp( _mouseY, 0.0f, 750.0f);
443
444 activateSelectableUnderMouse();
445 }
446 break;
447
448 default:
449 break;
450 }
451 }
452
453 //put the absolute mouse position in to trigger
454 t.fData1 = _mouseX;
455 t.fData2 = _mouseY;
456
457 if( _currentSelectable != _activeSelectables.end())
458 {
459 (*_currentSelectable)->input( t, isDown, _boardOffset);
460 }
461 }
462
activateSelectableUnderMouse(void)463 void MenuManager::activateSelectableUnderMouse( void)
464 {
465 list<Selectable*>::iterator i;
466 for( i=_activeSelectables.begin(); i!=_activeSelectables.end(); i++)
467 {
468 Selectable *sel = *i;
469 if( !sel)
470 {
471 LOG_ERROR << "Selectable is 0 !!!" << endl;
472 continue;
473 }
474 const BoundingBox &r = sel->getInputBox();
475 if( (_mouseX >= (r.min.x+_boardOffset.x)) &&
476 (_mouseX <= (r.max.x+_boardOffset.x)) &&
477 (_mouseY >= (r.min.y+_boardOffset.y)) &&
478 (_mouseY <= (r.max.y+_boardOffset.y)) )
479 {
480 sel->activate();
481 break;
482 }
483 }
484 }
485
Down(void)486 void MenuManager::Down( void)
487 {
488 XTRACE();
489 if( _currentSelectable == _activeSelectables.end()) return;
490
491 _currentSelectable++;
492 if( _currentSelectable == _activeSelectables.end())
493 {
494 _currentSelectable = _activeSelectables.begin();
495 }
496
497 (*_currentSelectable)->activate();
498 }
499
Up(void)500 void MenuManager::Up( void)
501 {
502 XTRACE();
503 if( _currentSelectable == _activeSelectables.end()) return;
504
505 if( _currentSelectable == _activeSelectables.begin())
506 {
507 _currentSelectable = _activeSelectables.end();
508 }
509 _currentSelectable--;
510
511 (*_currentSelectable)->activate();
512 }
513
Goto(Selectable * s)514 void MenuManager::Goto( Selectable *s)
515 {
516 list<Selectable*>::iterator i;
517 for( i=_activeSelectables.begin(); i!=_activeSelectables.end(); i++)
518 {
519 if( (*i) == s)
520 {
521 // LOG_INFO << "Goto found match" << endl;
522 break;
523 }
524 }
525 _currentSelectable = i;
526 }
527
Enter(void)528 void MenuManager::Enter( void)
529 {
530 XTRACE();
531 (*_currentSelectable)->select();
532 }
533
Exit(bool delayed)534 bool MenuManager::Exit( bool delayed)
535 {
536 XTRACE();
537 if( delayed)
538 {
539 //while iterating over the selectables we dont want to loadMenuLevel
540 _delayedExit = true;
541 return true;
542 }
543 if( _currentMenu != _topMenu)
544 {
545 _currentMenu = _currentMenu->Parent();
546 loadMenuLevel();
547 AudioS::instance()->playSample( "sounds/humm.wav");
548 return true;
549 }
550
551 //at the top level menu
552 return false;
553 }
554