1 //********************************************************************************************
2 //*
3 //*    This file is part of Egoboo.
4 //*
5 //*    Egoboo is free software: you can redistribute it and/or modify it
6 //*    under the terms of the GNU General Public License as published by
7 //*    the Free Software Foundation, either version 3 of the License, or
8 //*    (at your option) any later version.
9 //*
10 //*    Egoboo is distributed in the hope that it will be useful, but
11 //*    WITHOUT ANY WARRANTY; without even the implied warranty of
12 //*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //*    General Public License for more details.
14 //*
15 //*    You should have received a copy of the GNU General Public License
16 //*    along with Egoboo.  If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19 
20 /// @file menu.c
21 /// @brief Implements the main menu tree, using the code in Ui.*
22 /// @details
23 
24 #include "menu.h"
25 
26 #include "particle.inl"
27 #include "mad.h"
28 #include "char.inl"
29 #include "profile.inl"
30 
31 #include "game.h"
32 #include "quest.h"
33 
34 #include "controls_file.h"
35 #include "scancode_file.h"
36 #include "ui.h"
37 #include "log.h"
38 #include "link.h"
39 #include "game.h"
40 #include "texture.h"
41 #include "module_file.h"
42 
43 // To allow changing settings
44 #include "sound.h"
45 #include "input.h"
46 #include "camera.h"
47 #include "graphic.h"
48 
49 #include "egoboo_math.h"
50 #include "egoboo_vfs.h"
51 #include "egoboo_typedef.h"
52 #include "egoboo_fileutil.h"
53 #include "egoboo_setup.h"
54 #include "egoboo_strutil.h"
55 
56 #include "egoboo.h"
57 
58 #include "SDL_extensions.h"
59 
60 //--------------------------------------------------------------------------------------------
61 //--------------------------------------------------------------------------------------------
62 
63 /// The possible states of the menu state machine
64 enum e_menu_states
65 {
66     MM_Begin,
67     MM_Entering,
68     MM_Running,
69     MM_Leaving,
70     MM_Finish
71 };
72 
73 #define MENU_STACK_COUNT   256
74 #define MAXWIDGET          100
75 #define MENU_MAX_GAMETIPS  100
76 
77 //--------------------------------------------------------------------------------------------
78 //--------------------------------------------------------------------------------------------
79 // "Slidy" buttons used in some of the menus.  They're shiny.
80 struct s_SlidyButtonState
81 {
82     char **buttons;
83     float lerp;
84     int top;
85     int left;
86 };
87 typedef struct s_SlidyButtonState mnu_SlidyButtonState_t;
88 
89 //--------------------------------------------------------------------------------------------
90 //--------------------------------------------------------------------------------------------
91 
92 /// the data to display a chosen player in the load player menu
93 struct s_ChoosePlayer_element
94 {
95     CAP_REF           cap_ref;  ///< the character profile reference from "data.txt"
96     TX_REF            tx_ref;   ///< the index of the icon texture
97     int               skin_ref; ///< the index of the object skin from "skin.txt"
98     chop_definition_t chop;     ///< the chop data from "naming.txt". put this here so we can generate a name without loading an entire profile
99 };
100 typedef struct s_ChoosePlayer_element ChoosePlayer_element_t;
101 
102 ChoosePlayer_element_t * ChoosePlayer_ctor( ChoosePlayer_element_t * ptr );
103 ChoosePlayer_element_t * ChoosePlayer_dtor( ChoosePlayer_element_t * ptr );
104 
105 bool_t ChoosePlayer_init( ChoosePlayer_element_t * ptr );
106 bool_t ChoosePlayer_dealloc( ChoosePlayer_element_t * ptr );
107 
108 //--------------------------------------------------------------------------------------------
109 
110 /// The data that menu.c uses to store the users' choice of players
111 struct s_ChoosePlayer_list
112 {
113     int                    count;                         ///< the profiles that have been loaded
114     ChoosePlayer_element_t lst[MAXIMPORTPERPLAYER + 1];   ///< the profile data
115 };
116 typedef struct s_ChoosePlayer_list ChoosePlayer_list_t;
117 
118 ChoosePlayer_list_t * ChoosePlayer_list_dealloc( ChoosePlayer_list_t * );
119 
120 //--------------------------------------------------------------------------------------------
121 //--------------------------------------------------------------------------------------------
122 
123 /// the module data that the menu system needs
124 struct s_mnu_module
125 {
126     EGO_PROFILE_STUFF                           ///< the "base class" of a profile obbject
127 
128     mod_file_t base;                               ///< the data for the "base class" of the module
129 
130     // extended data
131     TX_REF tex_index;                              ///< the index of the module's tile image
132 
133     STRING vfs_path;                               ///< the virtual pathname of the module
134     STRING dest_path;                              ///< the path that module data can be written into
135 };
136 typedef struct s_mnu_module mnu_module_t;
137 
138 #define VALID_MOD_RANGE( IMOD ) ( ((IMOD) >= 0) && ((IMOD) < MAX_MODULE) )
139 #define VALID_MOD( IMOD )       ( VALID_MOD_RANGE( IMOD ) && IMOD < mnu_ModList.count && mnu_ModList.lst[IMOD].loaded )
140 #define INVALID_MOD( IMOD )     ( !VALID_MOD_RANGE( IMOD ) || IMOD >= mnu_ModList.count || !mnu_ModList.lst[IMOD].loaded )
141 
142 INSTANTIATE_STACK_STATIC( mnu_module_t, mnu_ModList, MAX_MODULE );
143 
144 //--------------------------------------------------------------------------------------------
145 //--------------------------------------------------------------------------------------------
146 
147 /// The data that menu.c uses to store the users' choice of players
148 struct s_GameTips
149 {
150     //These are loaded only once
151     Uint8  count;                         //< Number of global tips loaded
152     STRING hint[MENU_MAX_GAMETIPS];       //< The global hints/tips
153 
154     //These are loaded for every module
155     Uint8  local_count;                   //< Number of module specific tips loaded
156     STRING local_hint[MENU_MAX_GAMETIPS]; //< Module specific hints and tips
157 };
158 typedef struct s_GameTips GameTips_t;
159 
160 //--------------------------------------------------------------------------------------------
161 //--------------------------------------------------------------------------------------------
162 struct s_SelectedPlayer_element
163 {
164     Uint32  input;
165     int     player;
166 };
167 typedef struct s_SelectedPlayer_element SelectedPlayer_element_t;
168 
169 egoboo_rv SelectedPlayer_element_init( SelectedPlayer_element_t * ptr );
170 
171 //--------------------------------------------------------------------------------------------
172 struct s_SelectedPlayer_list
173 {
174     int count;
175     SelectedPlayer_element_t lst[MAX_PLAYER];
176 };
177 typedef struct s_SelectedPlayer_list SelectedPlayer_list_t;
178 
179 #define SELECTED_PLAYER_LIST_INIT { 0 }
180 
181 // implementation of the SelectedPlayer_list_t
182 static egoboo_rv SelectedPlayer_list_init( SelectedPlayer_list_t * sp_lst );
183 static bool_t    SelectedPlayer_list_check_loadplayer( SelectedPlayer_list_t * sp_lst, int loadplayer_idx );
184 static bool_t    SelectedPlayer_list_add( SelectedPlayer_list_t * sp_lst, int loadplayer_idx );
185 static bool_t    SelectedPlayer_list_remove( SelectedPlayer_list_t * sp_lst, int loadplayer_idx );
186 static bool_t    SelectedPlayer_list_add_input( SelectedPlayer_list_t * sp_lst, int loadplayer_idx, Uint32 input_bits );
187 static bool_t    SelectedPlayer_list_remove_input( SelectedPlayer_list_t * sp_lst, int loadplayer_idx, Uint32 input_bits );
188 static int       SelectedPlayer_list_index_from_loadplayer( SelectedPlayer_list_t * sp_lst, int loadplayer_idx );
189 
190 //--------------------------------------------------------------------------------------------
191 // declaration of "private" variables
192 //--------------------------------------------------------------------------------------------
193 
194 static int          mnu_stack_index = 0;
195 static which_menu_t mnu_stack[MENU_STACK_COUNT];
196 
197 static which_menu_t mnu_whichMenu = emnu_Main;
198 
199 static module_filter_t mnu_moduleFilter = FILTER_OFF;
200 
201 static ui_Widget_t mnu_widgetList[MAXWIDGET];
202 
203 static int selectedModule = -1;
204 
205 /* Copyright text variables.  Change these to change how the copyright text appears */
206 static const char * copyrightText = "Welcome to Egoboo!\nhttp://egoboo.sourceforge.net\nVersion " VERSION "\n";
207 static int  copyrightLeft = 0;
208 static int  copyrightTop  = 0;
209 
210 /* Options info text variables.  Change these to change how the options text appears */
211 static const char * tipText = "Put a tip in this box";
212 static int tipTextLeft = 0;
213 static int tipTextTop  = 0;
214 
215 /* Button position for the "easy" menus, like the main one */
216 static int buttonLeft = 0;
217 static int buttonTop = 0;
218 
219 static menu_process_t    _mproc;
220 
221 static GameTips_t mnu_GameTip = { 0 };
222 
223 static mnu_SlidyButtonState_t mnu_SlidyButtonState = { NULL };
224 
225 static SelectedPlayer_list_t mnu_SelectedList = SELECTED_PLAYER_LIST_INIT;
226 
227 //--------------------------------------------------------------------------------------------
228 // declaration of public variables
229 //--------------------------------------------------------------------------------------------
230 
231 #define TITLE_TEXTURE_COUNT   MAX_MODULE
232 #define INVALID_TITLE_TEXTURE TITLE_TEXTURE_COUNT
233 
234 INSTANTIATE_STACK_STATIC( oglx_texture_t, TxTitleImage, TITLE_TEXTURE_COUNT ); // OpenGL title image surfaces
235 
236 menu_process_t * MProc             = &_mproc;
237 bool_t           start_new_player  = bfalse;
238 bool_t           module_list_valid = bfalse;
239 
240 /* The font used for drawing text.  It's smaller than the button font */
241 Font *menuFont = NULL;
242 
243 bool_t mnu_draw_background = btrue;
244 
245 LoadPlayer_list_t mnu_loadplayer     = LOADPLAYER_LIST_INIT;
246 
247 //--------------------------------------------------------------------------------------------
248 // "private" function prototypes
249 //--------------------------------------------------------------------------------------------
250 
251 // Implementation of the mnu_stack
252 static bool_t       mnu_stack_push( which_menu_t menu );
253 static which_menu_t mnu_stack_pop();
254 static which_menu_t mnu_stack_peek();
255 static void         mnu_stack_clear();
256 
257 // Implementation of the mnu_SlidyButton array
258 static void mnu_SlidyButton_init( float lerp, const char *button_text[] );
259 static void mnu_SlidyButton_update_all( float deltaTime );
260 static void mnu_SlidyButton_draw_all();
261 
262 // implementation of "private" TxTitleImage functions
263 static void             TxTitleImage_clear_data();
264 static void             TxTitleImage_release_one( const TX_REF index );
265 static void             TxTitleImage_ctor();
266 static void             TxTitleImage_release_all();
267 static void             TxTitleImage_dtor();
268 static oglx_texture_t * TxTitleImage_get_ptr( const TX_REF itex );
269 
270 // tipText functions
271 static void tipText_set_position( Font * font, const char * text, int spacing );
272 
273 // copyrightText functions
274 static void copyrightText_set_position( Font * font, const char * text, int spacing );
275 
276 // implementation of "private" ModList functions
277 static void mnu_ModList_release_images();
278 void        mnu_ModList_release_all();
279 
280 // "process" management
281 static int do_menu_proc_begin( menu_process_t * mproc );
282 static int do_menu_proc_running( menu_process_t * mproc );
283 static int do_menu_proc_leaving( menu_process_t * mproc );
284 
285 // the hint system
286 static void   mnu_GameTip_load_global_vfs();
287 static bool_t mnu_GameTip_load_local_vfs();
288 
289 // "private" module utility
290 static void   mnu_load_all_module_info();
291 
292 // "private" asset function
293 static TX_REF mnu_get_icon_ref( const CAP_REF icap, const TX_REF default_ref );
294 
295 // implementation of the autoformatting
296 static void autoformat_init_slidy_buttons();
297 static void autoformat_init_tip_text();
298 static void autoformat_init_copyright_text();
299 
300 // misc other stuff
301 static void      mnu_release_one_module( const MOD_REF imod );
302 static void      mnu_load_all_module_images_vfs( LoadPlayer_list_t * lp_lst );
303 static egoboo_rv mnu_set_local_import_list( Import_list_t * imp_lst, SelectedPlayer_list_t * sp_lst );
304 static egoboo_rv mnu_set_selected_list( LoadPlayer_list_t * dst, LoadPlayer_list_t * src, SelectedPlayer_list_t * sp_lst );
305 static egoboo_rv mnu_copy_local_imports( Import_list_t * imp_lst );
306 
307 //--------------------------------------------------------------------------------------------
308 // implementation of the menu stack
309 //--------------------------------------------------------------------------------------------
mnu_stack_push(which_menu_t menu)310 bool_t mnu_stack_push( which_menu_t menu )
311 {
312     mnu_stack_index = CLIP( mnu_stack_index, 0, MENU_STACK_COUNT ) ;
313 
314     if ( mnu_stack_index >= MENU_STACK_COUNT ) return bfalse;
315 
316     mnu_stack[mnu_stack_index] = menu;
317     mnu_stack_index++;
318 
319     return btrue;
320 }
321 
322 //--------------------------------------------------------------------------------------------
mnu_stack_pop()323 which_menu_t mnu_stack_pop()
324 {
325     if ( mnu_stack_index < 0 )
326     {
327         mnu_stack_index = 0;
328         return emnu_Main;
329     }
330     if ( mnu_stack_index > MENU_STACK_COUNT )
331     {
332         mnu_stack_index = MENU_STACK_COUNT;
333     }
334 
335     if ( 0 == mnu_stack_index ) return emnu_Main;
336 
337     mnu_stack_index--;
338     return mnu_stack[mnu_stack_index];
339 }
340 
341 //--------------------------------------------------------------------------------------------
mnu_stack_peek()342 which_menu_t mnu_stack_peek()
343 {
344     which_menu_t return_menu = emnu_Main;
345 
346     if ( mnu_stack_index > 0 )
347     {
348         return_menu = mnu_stack[mnu_stack_index-1];
349     }
350 
351     return return_menu;
352 }
353 
354 //--------------------------------------------------------------------------------------------
mnu_stack_clear()355 void mnu_stack_clear()
356 {
357     mnu_stack_index = 0;
358     mnu_stack[0] = emnu_Main;
359 }
360 
361 //--------------------------------------------------------------------------------------------
362 // The implementation of the menu process
363 //--------------------------------------------------------------------------------------------
do_menu_proc_begin(menu_process_t * mproc)364 int do_menu_proc_begin( menu_process_t * mproc )
365 {
366     // reset the fps counter
367     menu_fps_clock        = 0;
368     menu_fps_loops        = 0;
369 
370     stabilized_menu_fps        = TARGET_FPS;
371     stabilized_menu_fps_sum    = 0.1f * TARGET_FPS;
372     stabilized_menu_fps_weight = 0.1f;
373 
374     // play some music
375     sound_play_song( MENU_SONG, 0, -1 );
376 
377     // initialize all these structures
378     menu_system_begin();        // start the menu menu
379 
380     // load all module info at menu initialization
381     // this will not change unless a new module is downloaded for a network menu?
382     mnu_load_all_module_info();
383 
384     // initialize the process state
385     mproc->base.valid = btrue;
386 
387     return 1;
388 }
389 
390 //--------------------------------------------------------------------------------------------
do_menu_proc_running(menu_process_t * mproc)391 int do_menu_proc_running( menu_process_t * mproc )
392 {
393     int menuResult;
394 
395     if ( !process_validate( PROC_PBASE( mproc ) ) ) return -1;
396 
397     mproc->was_active = mproc->base.valid;
398 
399     if ( mproc->base.paused ) return 0;
400 
401     // play the menu music
402     mnu_draw_background = !process_running( PROC_PBASE( GProc ) );
403     menuResult          = game_do_menu( mproc );
404 
405     switch ( menuResult )
406     {
407         case MENU_SELECT:
408             // go ahead and start the game
409             process_pause( PROC_PBASE( mproc ) );
410             break;
411 
412         case MENU_QUIT:
413             // the user selected "quit"
414             process_kill( PROC_PBASE( mproc ) );
415             break;
416     }
417 
418     if ( mnu_get_menu_depth() <= GProc->menu_depth )
419     {
420         GProc->menu_depth   = -1;
421         GProc->escape_latch = bfalse;
422 
423         // We have exited the menu and restarted the game
424         GProc->mod_paused = bfalse;
425         process_pause( PROC_PBASE( MProc ) );
426     }
427 
428     return 0;
429 }
430 
431 //--------------------------------------------------------------------------------------------
do_menu_proc_leaving(menu_process_t * mproc)432 int do_menu_proc_leaving( menu_process_t * mproc )
433 {
434     if ( !process_validate( PROC_PBASE( mproc ) ) ) return -1;
435 
436     // terminate the menu system
437     menu_system_end();
438 
439     // finish the menu song
440     sound_finish_song( 500 );
441 
442     // reset the fps counter
443     menu_fps_clock        = 0;
444     menu_fps_loops        = 0;
445 
446     stabilized_menu_fps        = TARGET_FPS;
447     stabilized_menu_fps_sum    = 0.1f * TARGET_FPS;
448     stabilized_menu_fps_weight = 0.1f;
449 
450     return 1;
451 }
452 
453 //--------------------------------------------------------------------------------------------
do_menu_proc_run(menu_process_t * mproc,double frameDuration)454 int do_menu_proc_run( menu_process_t * mproc, double frameDuration )
455 {
456     int result = 0, proc_result = 0;
457 
458     if ( !process_validate( PROC_PBASE( mproc ) ) ) return -1;
459     mproc->base.dtime = frameDuration;
460 
461     if ( mproc->base.paused ) return 0;
462 
463     if ( mproc->base.killme )
464     {
465         mproc->base.state = proc_leaving;
466     }
467 
468     switch ( mproc->base.state )
469     {
470         case proc_begin:
471             proc_result = do_menu_proc_begin( mproc );
472 
473             if ( 1 == proc_result )
474             {
475                 mproc->base.state = proc_entering;
476             }
477             break;
478 
479         case proc_entering:
480             // proc_result = do_menu_proc_entering( mproc );
481 
482             mproc->base.state = proc_running;
483             break;
484 
485         case proc_running:
486             proc_result = do_menu_proc_running( mproc );
487 
488             if ( 1 == proc_result )
489             {
490                 mproc->base.state = proc_leaving;
491             }
492             break;
493 
494         case proc_leaving:
495             proc_result = do_menu_proc_leaving( mproc );
496 
497             if ( 1 == proc_result )
498             {
499                 mproc->base.state  = proc_finish;
500                 mproc->base.killme = bfalse;
501             }
502             break;
503 
504         case proc_finish:
505             process_terminate( PROC_PBASE( mproc ) );
506             break;
507 
508         default:
509         case proc_invalid:
510             break;
511     }
512 
513     return result;
514 }
515 
516 //--------------------------------------------------------------------------------------------
menu_process_init(menu_process_t * mproc)517 menu_process_t * menu_process_init( menu_process_t * mproc )
518 {
519     if ( NULL == mproc ) return NULL;
520 
521     memset( mproc, 0, sizeof( *mproc ) );
522 
523     process_init( PROC_PBASE( mproc ) );
524 
525     return mproc;
526 }
527 
528 //--------------------------------------------------------------------------------------------
529 // Code for global initialization/deinitialization of the menu system
530 //--------------------------------------------------------------------------------------------
menu_system_begin()531 int menu_system_begin()
532 {
533     // initializes the menu system
534     //
535     // Loads resources for the menus, and figures out where things should
536     // be positioned.  If we ever allow changing resolution on the fly, this
537     // function will have to be updated/called more than once.
538 
539     autoformat_init( &gfx );
540 
541     menuFont = ui_loadFont( vfs_resolveReadFilename( "mp_data/Bo_Chen.ttf" ), 18 );
542     if ( NULL == menuFont )
543     {
544         log_error( "Could not load the menu font! (\"mp_data/Bo_Chen.ttf\")\n" );
545         return 0;
546     }
547 
548     // Figure out where to draw the copyright text
549     copyrightText_set_position( menuFont, copyrightText, 20 );
550 
551     // Figure out where to draw the options text
552     tipText_set_position( menuFont, tipText, 20 );
553 
554     // construct the TxTitleImage array
555     TxTitleImage_ctor();
556 
557     // Load game hints
558     mnu_GameTip_load_global_vfs();
559 
560     return 1;
561 }
562 
563 //--------------------------------------------------------------------------------------------
menu_system_end()564 void menu_system_end()
565 {
566     // initializes the menu system
567     //
568     // Loads resources for the menus, and figures out where things should
569     // be positioned.  If we ever allow changing resolution on the fly, this
570     // function will have to be updated/called more than once.
571 
572     // if this has not been done before yet, do it now
573     LoadPlayer_list_dealloc( &mnu_loadplayer );
574 
575     if ( NULL != menuFont )
576     {
577         fnt_freeFont( menuFont );
578         menuFont = NULL;
579     }
580 
581     // destruct the TxTitleImage array
582     TxTitleImage_dtor();
583 }
584 
585 //--------------------------------------------------------------------------------------------
586 // Interface for starting and stopping menus
587 //--------------------------------------------------------------------------------------------
mnu_begin_menu(which_menu_t which)588 bool_t mnu_begin_menu( which_menu_t which )
589 {
590     if ( !mnu_stack_push( mnu_whichMenu ) ) return bfalse;
591     mnu_whichMenu = which;
592 
593     return btrue;
594 }
595 
596 //--------------------------------------------------------------------------------------------
mnu_end_menu()597 void mnu_end_menu()
598 {
599     mnu_whichMenu = mnu_stack_pop();
600 }
601 
602 //--------------------------------------------------------------------------------------------
mnu_get_menu_depth()603 int mnu_get_menu_depth()
604 {
605     return mnu_stack_index;
606 }
607 //--------------------------------------------------------------------------------------------
608 // Implementations of the various menus
609 //--------------------------------------------------------------------------------------------
doMainMenu(float deltaTime)610 int doMainMenu( float deltaTime )
611 {
612     static int menuState = MM_Begin;
613     static oglx_texture_t background;
614     static oglx_texture_t logo;
615 
616     // static float lerp;
617     static int menuChoice = 0;
618     static SDL_Rect bg_rect, logo_rect;
619 
620     /* Button labels.  Defined here for consistency's sake, rather than leaving them as constants */
621     static const char *sz_buttons[] =
622     {
623         "New Game",
624         "Load Game",
625         "Options",
626         "Quit",
627         ""
628     };
629 
630     float fminw = 1, fminh = 1, fmin = 1;
631     int result = 0;
632 
633     switch ( menuState )
634     {
635         case MM_Begin:
636 
637             menuChoice = 0;
638             menuState = MM_Entering;
639 
640             //Special xmas theme
641             if ( check_time( SEASON_CHRISTMAS ) )
642             {
643                 // load the menu image
644                 ego_texture_load_vfs( &background, "mp_data/menu/menu_xmas", INVALID_KEY );
645 
646                 // load the logo image
647                 ego_texture_load_vfs( &logo,       "mp_data/menu/snowy_logo", INVALID_KEY );
648             }
649 
650             //Special Halloween theme
651             else if ( check_time( SEASON_HALLOWEEN ) )
652             {
653                 // load the menu image
654                 ego_texture_load_vfs( &background, "mp_data/menu/menu_halloween", INVALID_KEY );
655 
656                 // load the logo image
657                 ego_texture_load_vfs( &logo,       "mp_data/menu/creepy_logo", INVALID_KEY );
658             }
659 
660             //Default egoboo theme
661             else
662             {
663                 // load the menu image
664                 ego_texture_load_vfs( &background, "mp_data/menu/menu_main", INVALID_KEY );
665 
666                 // load the logo image
667                 ego_texture_load_vfs( &logo,       "mp_data/menu/menu_logo", INVALID_KEY );
668             }
669 
670             // calculate the centered position of the background
671             fminw = ( float ) MIN( GFX_WIDTH , background.imgW ) / ( float ) background.imgW;
672             fminh = ( float ) MIN( GFX_HEIGHT, background.imgH ) / ( float ) background.imgW;
673             fmin  = MIN( fminw, fminh );
674 
675             bg_rect.w = background.imgW * fmin;
676             bg_rect.h = background.imgH * fmin;
677             bg_rect.x = ( GFX_WIDTH  - bg_rect.w ) * 0.5f;
678             bg_rect.y = ( GFX_HEIGHT - bg_rect.h ) * 0.5f;
679 
680             // calculate the position of the logo
681             fmin  = MIN( bg_rect.w * 0.5f / logo.imgW, bg_rect.h * 0.5f / logo.imgH );
682 
683             logo_rect.x = bg_rect.x;
684             logo_rect.y = bg_rect.y;
685             logo_rect.w = logo.imgW * fmin;
686             logo_rect.h = logo.imgH * fmin;
687 
688             mnu_SlidyButton_init( 1.0f, sz_buttons );
689             // let this fall through into MM_Entering
690 
691         case MM_Entering:
692             // do buttons sliding in animation, and background fading in
693             // background
694             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
695 
696             if ( mnu_draw_background )
697             {
698                 ui_drawImage( 0, &background, bg_rect.x,   bg_rect.y,   bg_rect.w,   bg_rect.h, NULL );
699                 ui_drawImage( 0, &logo,       logo_rect.x, logo_rect.y, logo_rect.w, logo_rect.h, NULL );
700             }
701 
702             // "Copyright" text
703             ui_drawTextBox( menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20 );
704 
705             mnu_SlidyButton_draw_all();
706             mnu_SlidyButton_update_all( -deltaTime );
707 
708             // Let lerp wind down relative to the time elapsed
709             if ( mnu_SlidyButtonState.lerp <= 0.0f )
710             {
711                 menuState = MM_Running;
712             }
713             break;
714 
715         case MM_Running:
716             // Do normal run
717             // Background
718 
719             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
720 
721             if ( mnu_draw_background )
722             {
723                 ui_drawImage( 0, &background, bg_rect.x,   bg_rect.y,   bg_rect.w,   bg_rect.h, NULL );
724                 ui_drawImage( 0, &logo,       logo_rect.x, logo_rect.y, logo_rect.w, logo_rect.h, NULL );
725             }
726 
727             // "Copyright" text
728             ui_drawTextBox( menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20 );
729 
730             // Buttons
731             if ( BUTTON_UP == ui_doButton( 1, sz_buttons[0], NULL, buttonLeft, buttonTop, 200, 30 ) )
732             {
733                 // begin single player stuff
734                 menuChoice = 1;
735             }
736             if ( BUTTON_UP == ui_doButton( 2, sz_buttons[1], NULL, buttonLeft, buttonTop + 35, 200, 30 ) )
737             {
738                 // begin multi player stuff
739                 menuChoice = 2;
740             }
741             if ( BUTTON_UP == ui_doButton( 3, sz_buttons[2], NULL, buttonLeft, buttonTop + 35 * 2, 200, 30 ) )
742             {
743                 // go to options menu
744                 menuChoice = 3;
745             }
746             if ( SDLKEYDOWN( SDLK_ESCAPE ) || BUTTON_UP == ui_doButton( 4, sz_buttons[3], NULL, buttonLeft, buttonTop + 35 * 3, 200, 30 ) )
747             {
748                 // quit game
749                 menuChoice = 4;
750             }
751             if ( menuChoice != 0 )
752             {
753                 menuState = MM_Leaving;
754                 mnu_SlidyButton_init( 0.0f, sz_buttons );
755             }
756             break;
757 
758         case MM_Leaving:
759             // Do buttons sliding out and background fading
760             // Do the same stuff as in MM_Entering, but backwards
761             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
762 
763             if ( mnu_draw_background )
764             {
765                 ui_drawImage( 0, &background, bg_rect.x,   bg_rect.y,   bg_rect.w,   bg_rect.h, NULL );
766                 ui_drawImage( 0, &logo,       logo_rect.x, logo_rect.y, logo_rect.w, logo_rect.h, NULL );
767             }
768 
769             // "Copyright" text
770             ui_drawTextBox( menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20 );
771 
772             // Buttons
773             mnu_SlidyButton_draw_all();
774             mnu_SlidyButton_update_all( deltaTime );
775             if ( mnu_SlidyButtonState.lerp >= 1.0f )
776             {
777                 menuState = MM_Finish;
778             }
779             break;
780 
781         case MM_Finish:
782             // Free the background texture; don't need to hold onto it
783             oglx_texture_Release( &background );
784             menuState = MM_Begin;  // Make sure this all resets next time
785 
786             // reset the ui
787             ui_Reset();
788 
789             // Set the next menu to load
790             result = menuChoice;
791             break;
792     };
793 
794     return result;
795 }
796 
797 //--------------------------------------------------------------------------------------------
doSinglePlayerMenu(float deltaTime)798 int doSinglePlayerMenu( float deltaTime )
799 {
800     static int menuState = MM_Begin;
801     static oglx_texture_t background;
802     static int menuChoice;
803 
804     static const char *sz_buttons[] =
805     {
806         "New Player",
807         "Load Saved Player",
808         "Back",
809         ""
810     };
811 
812     int result = 0;
813 
814     switch ( menuState )
815     {
816         case MM_Begin:
817             // Load resources for this menu
818             ego_texture_load_vfs( &background, "mp_data/menu/menu_advent", TRANSCOLOR );
819             menuChoice = 0;
820 
821             menuState = MM_Entering;
822 
823             mnu_SlidyButton_init( 1.0f, sz_buttons );
824 
825             // Let this fall through
826 
827         case MM_Entering:
828             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
829 
830             // Draw the background image
831             if ( mnu_draw_background )
832             {
833                 ui_drawImage( 0, &background, GFX_WIDTH  - background.imgW, 0, 0, 0, NULL );
834             }
835 
836             // "Copyright" text
837             ui_drawTextBox( menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20 );
838 
839             mnu_SlidyButton_draw_all();
840             mnu_SlidyButton_update_all( -deltaTime );
841             if ( mnu_SlidyButtonState.lerp <= 0.0f )
842                 menuState = MM_Running;
843 
844             break;
845 
846         case MM_Running:
847 
848             // Draw the background image
849             if ( mnu_draw_background )
850             {
851                 ui_drawImage( 0, &background, GFX_WIDTH  - background.imgW, 0, 0, 0, NULL );
852             }
853 
854             // "Copyright" text
855             ui_drawTextBox( menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20 );
856 
857             // Buttons
858             if ( BUTTON_UP == ui_doButton( 1, sz_buttons[0], NULL, buttonLeft, buttonTop, 200, 30 ) )
859             {
860                 menuChoice = 1;
861             }
862             if ( BUTTON_UP == ui_doButton( 2, sz_buttons[1], NULL, buttonLeft, buttonTop + 35, 200, 30 ) )
863             {
864                 menuChoice = 2;
865             }
866             if ( SDLKEYDOWN( SDLK_ESCAPE ) || BUTTON_UP == ui_doButton( 3, sz_buttons[2], NULL, buttonLeft, buttonTop + 35 * 2, 200, 30 ) )
867             {
868                 menuChoice = 3;     //back
869             }
870             if ( menuChoice != 0 )
871             {
872                 menuState = MM_Leaving;
873                 mnu_SlidyButton_init( 0.0f, sz_buttons );
874             }
875             break;
876 
877         case MM_Leaving:
878             // Do buttons sliding out and background fading
879             // Do the same stuff as in MM_Entering, but backwards
880             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
881 
882             if ( mnu_draw_background )
883             {
884                 ui_drawImage( 0, &background, GFX_WIDTH  - background.imgW, 0, 0, 0, NULL );
885             }
886 
887             // "Copyright" text
888             ui_drawTextBox( menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20 );
889 
890             mnu_SlidyButton_draw_all();
891             mnu_SlidyButton_update_all( deltaTime );
892             if ( mnu_SlidyButtonState.lerp >= 1.0f )
893             {
894                 menuState = MM_Finish;
895             }
896             break;
897 
898         case MM_Finish:
899             // Release the background texture
900             oglx_texture_Release( &background );
901 
902             // reset the ui
903             ui_Reset();
904 
905             // Set the next menu to load
906             result = menuChoice;
907 
908             // And make sure that if we come back to this menu, it resets
909             // properly
910             menuState = MM_Begin;
911     }
912 
913     return result;
914 }
915 
916 //--------------------------------------------------------------------------------------------
917 static int cmp_mod_ref_mult = 1;
918 
cmp_mod_ref(const void * vref1,const void * vref2)919 int cmp_mod_ref( const void * vref1, const void * vref2 )
920 {
921     /// @details BB@> Sort MOD REF values based on the rank of the module that they point to.
922     ///               Trap all stupid values.
923 
924     MOD_REF * pref1 = ( MOD_REF * )vref1;
925     MOD_REF * pref2 = ( MOD_REF * )vref2;
926 
927     int retval = 0;
928 
929     if ( NULL == pref1 && NULL == pref2 )
930     {
931         return 0;
932     }
933     else if ( NULL == pref1 )
934     {
935         return 1;
936     }
937     else if ( NULL == pref2 )
938     {
939         return -1;
940     }
941 
942     if ( *pref1 > mnu_ModList.count && *pref2 > mnu_ModList.count )
943     {
944         return 0;
945     }
946     else if ( *pref1 > mnu_ModList.count )
947     {
948         return 1;
949     }
950     else if ( *pref2 > mnu_ModList.count )
951     {
952         return -1;
953     }
954 
955     // if they are beaten, float them to the end of the list
956     retval = ( int )mnu_ModList.lst[*pref1].base.beaten - ( int )mnu_ModList.lst[*pref2].base.beaten;
957 
958     if ( 0 == retval )
959     {
960         // I want to uot the "newest" == "hardest" modules at the front, but this should be opposite for
961         // beginner modules
962         retval = cmp_mod_ref_mult * strncmp( mnu_ModList.lst[*pref1].base.rank, mnu_ModList.lst[*pref2].base.rank, RANKSIZE );
963     }
964 
965     if ( 0 == retval )
966     {
967         retval = strncmp( mnu_ModList.lst[*pref1].base.longname, mnu_ModList.lst[*pref2].base.longname, sizeof( STRING ) );
968     }
969 
970     return retval;
971 }
972 
973 //--------------------------------------------------------------------------------------------
doChooseModule(float deltaTime)974 int doChooseModule( float deltaTime )
975 {
976     /// @details Choose the module
977 
978     static oglx_texture_t background;
979     static int menuState = MM_Begin;
980     static Uint8 keycooldown;
981     static char* filterText = "All Modules";
982 
983     static int validModules_count;
984     static MOD_REF validModules[MAX_MODULE];
985 
986     static int moduleMenuOffsetX;
987     static int moduleMenuOffsetY;
988 
989     static int startIndex = 0;
990     static int int_module = -1;
991     static int ext_module = -1;
992 
993     // keep a local list of the currently selected players
994     static LoadPlayer_list_t loc_selectedplayer = LOADPLAYER_LIST_INIT;
995 
996     int result = 0;
997     int i, x, y;
998     MOD_REF imod;
999 
1000     switch ( menuState )
1001     {
1002         case MM_Begin:
1003 
1004             // copy over the selected players from mnu_SelectedList to loc_selectedplayer
1005             mnu_set_selected_list( &loc_selectedplayer, &mnu_loadplayer, &mnu_SelectedList );
1006 
1007             // reload the module data, if necessary
1008             if ( !module_list_valid )
1009             {
1010                 mnu_load_all_module_info();
1011             }
1012 
1013             // Reload all modules, something might be unlocked
1014             mnu_load_all_module_images_vfs( &loc_selectedplayer );
1015 
1016             // Reset which module we are selecting
1017             startIndex = 0;
1018             ext_module = int_module = -1;
1019 
1020             // reset the global module selection index
1021             selectedModule = -1;
1022 
1023             // blank out the valid modules
1024             validModules_count = 0;
1025             for ( i = 0; i < MAX_MODULE; i++ )
1026             {
1027                 memset( validModules + i, 0, sizeof( MOD_REF ) );
1028             }
1029 
1030             // Figure out at what offset we want to draw the module menu.
1031             moduleMenuOffsetX = ( GFX_WIDTH  - 640 ) / 2;
1032             moduleMenuOffsetX = MAX( 0, moduleMenuOffsetX );
1033 
1034             moduleMenuOffsetY = ( GFX_HEIGHT - 480 ) / 2;
1035             moduleMenuOffsetY = MAX( 0, moduleMenuOffsetY );
1036 
1037             menuState = MM_Entering;
1038 
1039             // fall through...
1040 
1041         case MM_Entering:
1042             menuState = MM_Running;
1043 
1044             if ( !module_list_valid )
1045             {
1046                 mnu_load_all_module_info();
1047                 mnu_load_all_module_images_vfs( &loc_selectedplayer );
1048             }
1049 
1050             // Find the modules that we want to allow loading for.  If start_new_player
1051             // is true, we want ones that don't allow imports (e.g. starter modules).
1052             // Otherwise, we want modules that allow imports
1053             validModules_count = 0;
1054             for ( imod = 0; imod < mnu_ModList.count; imod++ )
1055             {
1056                 // if this module is not valid given the game options and the
1057                 // selected players, skip it
1058                 if ( !mnu_test_module_by_index( &loc_selectedplayer, imod, 0, NULL ) ) continue;
1059 
1060                 if ( start_new_player && 0 == mnu_ModList.lst[imod].base.importamount )
1061                 {
1062                     // starter module
1063                     validModules[validModules_count] = REF_TO_INT( imod );
1064                     validModules_count++;
1065                 }
1066                 else
1067                 {
1068                     if ( FILTER_OFF != mnu_moduleFilter && mnu_ModList.lst[imod].base.moduletype != mnu_moduleFilter ) continue;
1069                     if ( mnu_SelectedList.count > mnu_ModList.lst[imod].base.importamount ) continue;
1070                     if ( mnu_SelectedList.count < mnu_ModList.lst[imod].base.minplayers ) continue;
1071                     if ( mnu_SelectedList.count > mnu_ModList.lst[imod].base.maxplayers ) continue;
1072 
1073                     // regular module
1074                     validModules[validModules_count] = REF_TO_INT( imod );
1075                     validModules_count++;
1076                 }
1077             }
1078 
1079             // sort the modules by difficulty. easiest to hardeest for starting a new character
1080             // hardest to easiest for loading a module
1081             cmp_mod_ref_mult = start_new_player ? 1 : -1;
1082             qsort( validModules, validModules_count, sizeof( MOD_REF ), cmp_mod_ref );
1083 
1084             // load background depending on current filter
1085             if ( start_new_player )
1086             {
1087                 ego_texture_load_vfs( &background, "mp_data/menu/menu_advent", TRANSCOLOR );
1088             }
1089             else switch ( mnu_moduleFilter )
1090                 {
1091                     case FILTER_MAIN: ego_texture_load_vfs( &background, "mp_data/menu/menu_draco", TRANSCOLOR ); break;
1092                     case FILTER_SIDE: ego_texture_load_vfs( &background, "mp_data/menu/menu_sidequest", TRANSCOLOR ); break;
1093                     case FILTER_TOWN: ego_texture_load_vfs( &background, "mp_data/menu/menu_town", TRANSCOLOR ); break;
1094                     case FILTER_FUN:  ego_texture_load_vfs( &background, "mp_data/menu/menu_funquest", TRANSCOLOR ); break;
1095 
1096                     default:
1097                     case FILTER_OFF: ego_texture_load_vfs( &background, "mp_data/menu/menu_allquest", TRANSCOLOR ); break;
1098                 }
1099 
1100             // set the tip text
1101             if ( 0 == validModules_count )
1102             {
1103                 tipText_set_position( menuFont, "Sorry, there are no valid games!\n Please press the \"Back\" button.", 20 );
1104             }
1105             else if ( validModules_count <= 3 )
1106             {
1107                 tipText_set_position( menuFont, "Press an icon to select a game.", 20 );
1108             }
1109             else
1110             {
1111                 tipText_set_position( menuFont, "Press an icon to select a game.\nUse the mouse wheel or the \"<-\" and \"->\" buttons to scroll.", 20 );
1112             }
1113 
1114             // fall through for now...
1115 
1116         case MM_Running:
1117             {
1118                 GLXvector4f beat_tint = { 0.5f, 0.25f, 0.25f, 1.0f };
1119                 GLXvector4f normal_tint = { 1.0f, 1.0f, 1.0f, 1.0f };
1120 
1121                 if ( !module_list_valid )
1122                 {
1123                     mnu_load_all_module_info();
1124                     mnu_load_all_module_images_vfs( &loc_selectedplayer );
1125                 }
1126 
1127                 // Draw the background
1128                 GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
1129                 x = ( GFX_WIDTH  / 2 ) - ( background.imgW / 2 );
1130                 y = GFX_HEIGHT - background.imgH;
1131 
1132                 if ( mnu_draw_background )
1133                 {
1134                     ui_drawImage( 0, &background, x, y, 0, 0, NULL );
1135                 }
1136 
1137                 // use the mouse wheel to scan the modules
1138                 if ( cursor_wheel_event_pending() )
1139                 {
1140                     if ( cursor.z > 0 )
1141                     {
1142                         startIndex++;
1143                     }
1144                     else if ( cursor.z < 0 )
1145                     {
1146                         startIndex--;
1147                     }
1148 
1149                     cursor_finish_wheel_event();
1150                 }
1151 
1152                 //Allow arrow keys to scroll as well
1153                 if ( SDLKEYDOWN( SDLK_RIGHT ) )
1154                 {
1155                     if ( 0 == keycooldown )
1156                     {
1157                         startIndex++;
1158                         keycooldown = 5;
1159                     }
1160                 }
1161                 else if ( SDLKEYDOWN( SDLK_LEFT ) )
1162                 {
1163                     if ( 0 == keycooldown )
1164                     {
1165                         startIndex--;
1166                         keycooldown = 5;
1167                     }
1168                 }
1169                 else keycooldown = 0;
1170                 if ( keycooldown > 0 ) keycooldown--;
1171 
1172                 // Draw the arrows to pick modules
1173                 if ( validModules_count > 3 )
1174                 {
1175                     if ( BUTTON_UP == ui_doButton( 1051, "<-", NULL, moduleMenuOffsetX + 20, moduleMenuOffsetY + 74, 30, 30 ) )
1176                     {
1177                         startIndex--;
1178                     }
1179                     if ( BUTTON_UP == ui_doButton( 1052, "->", NULL, moduleMenuOffsetX + 590, moduleMenuOffsetY + 74, 30, 30 ) )
1180                     {
1181                         startIndex++;
1182                     }
1183                 }
1184 
1185                 // restrict the range to valid values
1186                 startIndex = CLIP( startIndex, 0, validModules_count - 3 );
1187 
1188                 // Draw buttons for the modules that can be selected
1189                 x = 93;
1190                 y = 20;
1191                 for ( i = startIndex; i < MIN( startIndex + 3, validModules_count ); i++ )
1192                 {
1193                     // fix the menu images in case one or more of them are undefined
1194                     MOD_REF          imod       = validModules[i];
1195                     TX_REF           tex_offset = mnu_ModList.lst[imod].tex_index;
1196                     oglx_texture_t * ptex       = TxTitleImage_get_ptr( tex_offset );
1197 
1198                     GLfloat * img_tint = normal_tint;
1199 
1200                     if ( mnu_ModList.lst[imod].base.beaten )
1201                     {
1202                         img_tint = beat_tint;
1203                     }
1204 
1205                     if ( ui_doImageButton( i, ptex, moduleMenuOffsetX + x, moduleMenuOffsetY + y, 138, 138, img_tint ) )
1206                     {
1207                         int_module = i;
1208                         ext_module = (( int_module < 0 ) || ( int_module > validModules_count ) ) ? -1 : validModules[int_module];
1209                     }
1210 
1211                     //Draw a text over the image explaining what it means
1212                     if ( mnu_ModList.lst[imod].base.beaten )
1213                     {
1214                         ui_drawTextBox( NULL, "BEATEN", moduleMenuOffsetX + x + 32, moduleMenuOffsetY + y + 64, 64, 30, 20 );
1215                     }
1216 
1217                     x += 138 + 20;  // Width of the button, and the spacing between buttons
1218                 }
1219 
1220                 // Draw an empty button as the backdrop for the module text
1221                 ui_drawButton( UI_Nothing, moduleMenuOffsetX + 21, moduleMenuOffsetY + 173, 291, 250, NULL );
1222 
1223                 // Draw the text description of the selected module
1224                 if ( ext_module > -1 && ext_module < mnu_ModList.count )
1225                 {
1226                     char    buffer[1024]  = EMPTY_CSTR;
1227                     const char * rank_string, * name_string;
1228                     char  * carat = buffer, * carat_end = buffer + SDL_arraysize( buffer );
1229 
1230                     mod_file_t * pmod = &( mnu_ModList.lst[ext_module].base );
1231 
1232                     GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
1233 
1234                     name_string = "Unnamed";
1235                     if ( CSTR_END != pmod->longname[0] )
1236                     {
1237                         name_string = pmod->longname;
1238                     }
1239                     carat += snprintf( carat, carat_end - carat - 1, "%s\n", name_string );
1240 
1241                     rank_string = "Unranked";
1242                     if ( CSTR_END != pmod->rank[0] )
1243                     {
1244                         rank_string = pmod->rank;
1245                     }
1246                     carat += snprintf( carat, carat_end - carat - 1, "Difficulty: %s\n", rank_string );
1247 
1248                     if ( pmod->maxplayers > 1 )
1249                     {
1250                         if ( pmod->minplayers == pmod->maxplayers )
1251                         {
1252                             carat += snprintf( carat, carat_end - carat - 1, "%d Players\n", pmod->minplayers );
1253                         }
1254                         else
1255                         {
1256                             carat += snprintf( carat, carat_end - carat - 1, "%d - %d Players\n", pmod->minplayers, pmod->maxplayers );
1257                         }
1258                     }
1259                     else
1260                     {
1261                         if ( 0 != pmod->importamount )
1262                         {
1263                             carat += snprintf( carat, carat_end - carat - 1, "Single Player\n" );
1264                         }
1265                         else
1266                         {
1267                             carat += snprintf( carat, carat_end - carat - 1, "Starter Module\n" );
1268                         }
1269                     }
1270                     carat += snprintf( carat, carat_end - carat - 1, " \n" );
1271 
1272                     for ( i = 0; i < SUMMARYLINES; i++ )
1273                     {
1274                         carat += snprintf( carat, carat_end - carat - 1, "%s\n", pmod->summary[i] );
1275                     }
1276 
1277                     // Draw a text box
1278                     ui_drawTextBox( menuFont, buffer, moduleMenuOffsetX + 21, moduleMenuOffsetY + 173, 291, 230, 20 );
1279                 }
1280 
1281                 // And draw the next & back buttons
1282                 if ( ext_module > -1 )
1283                 {
1284                     if ( SDLKEYDOWN( SDLK_RETURN ) || BUTTON_UP == ui_doButton( 53, "Select Module", NULL, moduleMenuOffsetX + 327, moduleMenuOffsetY + 173, 200, 30 ) )
1285                     {
1286                         // go to the next menu with this module selected
1287                         menuState = MM_Leaving;
1288                     }
1289                 }
1290 
1291                 if ( SDLKEYDOWN( SDLK_ESCAPE ) || BUTTON_UP == ui_doButton( 54, "Back", NULL, moduleMenuOffsetX + 327, moduleMenuOffsetY + 208, 200, 30 ) )
1292                 {
1293                     // Signal doMenu to go back to the previous menu
1294                     int_module = -1;
1295                     ext_module = -1;
1296                     menuState = MM_Leaving;
1297                 }
1298 
1299                 //Do the module filter button
1300                 if ( !start_new_player )
1301                 {
1302                     bool_t click_button;
1303 
1304                     // unly display the filter name
1305                     ui_doButton( 55, filterText, NULL, moduleMenuOffsetX + 327, moduleMenuOffsetY + 390, 200, 30 );
1306 
1307                     // use the ">" button to change since we are already using arrows to indicate "spin control"-like widgets
1308                     click_button = ( BUTTON_UP == ui_doButton( 56, ">", NULL, moduleMenuOffsetX + 532, moduleMenuOffsetY + 390, 30, 30 ) );
1309 
1310                     if ( click_button )
1311                     {
1312                         //Reload the modules with the new filter
1313                         menuState = MM_Entering;
1314 
1315                         //Swap to the next filter
1316                         mnu_moduleFilter = CLIP( mnu_moduleFilter, FILTER_NORMAL_BEGIN, FILTER_NORMAL_END );
1317 
1318                         mnu_moduleFilter = ( module_filter_t )( mnu_moduleFilter + 1 );
1319 
1320                         if ( mnu_moduleFilter > FILTER_NORMAL_END ) mnu_moduleFilter = FILTER_NORMAL_BEGIN;
1321 
1322                         switch ( mnu_moduleFilter )
1323                         {
1324                             case FILTER_MAIN:    filterText = "Main Quest";       break;
1325                             case FILTER_SIDE:    filterText = "Sidequests";       break;
1326                             case FILTER_TOWN:    filterText = "Towns and Cities"; break;
1327                             case FILTER_FUN:     filterText = "Fun Modules";      break;
1328                             case FILTER_STARTER: filterText = "Starter Modules";  break;
1329                         default: case FILTER_OFF:     filterText = "All Modules";      break;
1330                         }
1331                     }
1332                 }
1333 
1334                 // the tool-tip text
1335                 GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
1336                 ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
1337             }
1338             break;
1339 
1340         case MM_Leaving:
1341             menuState = MM_Finish;
1342             // fall through for now
1343 
1344         case MM_Finish:
1345             oglx_texture_Release( &background );
1346 
1347             pickedmodule_index         = -1;
1348             pickedmodule_path[0]       = CSTR_END;
1349             pickedmodule_name[0]       = CSTR_END;
1350             pickedmodule_write_path[0] = CSTR_END;
1351 
1352             menuState = MM_Begin;
1353             if ( -1 == ext_module )
1354             {
1355                 result = -1;
1356             }
1357             else
1358             {
1359                 // Save the name of the module that we've picked
1360                 pickedmodule_index = ext_module;
1361 
1362                 strncpy( pickedmodule_path,       mnu_ModList_get_vfs_path( pickedmodule_index ), SDL_arraysize( pickedmodule_path ) );
1363                 strncpy( pickedmodule_name,       mnu_ModList_get_name( pickedmodule_index ), SDL_arraysize( pickedmodule_name ) );
1364                 strncpy( pickedmodule_write_path, mnu_ModList_get_dest_path( pickedmodule_index ), SDL_arraysize( pickedmodule_write_path ) );
1365 
1366                 if ( !game_choose_module( ext_module, -1 ) )
1367                 {
1368                     log_warning( "Tried to select an invalid module. index == %d\n", ext_module );
1369                     result = -1;
1370                 }
1371                 else
1372                 {
1373                     pickedmodule_ready = btrue;
1374                     result = ( PMod->importamount > 0 ) ? 1 : 2;
1375                 }
1376             }
1377 
1378             // just blank out the data since we do not own it
1379             LoadPlayer_list_init( &loc_selectedplayer );
1380 
1381             // post the selected module
1382             selectedModule = ext_module;
1383 
1384             // reset the ui
1385             ui_Reset();
1386 
1387             break;
1388     }
1389 
1390     return result;
1391 }
1392 
1393 //--------------------------------------------------------------------------------------------
doChoosePlayer_load_profiles(LoadPlayer_element_t * loadplayer_ptr,ChoosePlayer_list_t * chooseplayer)1394 bool_t doChoosePlayer_load_profiles( LoadPlayer_element_t * loadplayer_ptr, ChoosePlayer_list_t * chooseplayer )
1395 {
1396     int    i;
1397     STRING  szFilename;
1398 
1399     CAP_REF   cap_ref;
1400     cap_t   * cap_ptr = NULL;
1401 
1402     ChoosePlayer_element_t * chooseplayer_ptr;
1403 
1404     // free any allocated data
1405     ChoosePlayer_list_dealloc( chooseplayer );
1406 
1407     if ( NULL == loadplayer_ptr ) return bfalse;
1408 
1409     // grab the loadplayer_idx data
1410     if ( !LOADED_CAP( loadplayer_ptr->cap_ref ) )
1411     {
1412         return bfalse;
1413     }
1414     cap_ptr = CapStack.lst + loadplayer_ptr->cap_ref;
1415 
1416     // go to the next element in the list
1417     chooseplayer_ptr = chooseplayer->lst + chooseplayer->count;
1418     chooseplayer->count++;
1419 
1420     // blank out the data
1421     ChoosePlayer_ctor( chooseplayer_ptr );
1422 
1423     // set the index of this object
1424     chooseplayer_ptr->cap_ref = loadplayer_ptr->cap_ref;
1425 
1426     // copy the skin
1427     chooseplayer_ptr->skin_ref = loadplayer_ptr->skin_ref;
1428     chooseplayer_ptr->skin_ref = CLIP( chooseplayer_ptr->skin_ref, 0, MAX_SKIN - 1 );
1429 
1430     // copy the texture reference over
1431     chooseplayer_ptr->tx_ref = loadplayer_ptr->tx_ref;
1432 
1433     // copy the chop info
1434     memmove( &( chooseplayer_ptr->chop ), &( loadplayer_ptr->chop ), sizeof( chooseplayer_ptr->chop ) );
1435 
1436     // make sure the book data is loaded
1437     if ( 0 == bookicon_count )
1438     {
1439         load_one_profile_vfs( "mp_data/globalobjects/book.obj", SPELLBOOK );
1440     }
1441 
1442     // grab the inventory data
1443     for ( i = 0; i < MAXIMPORTOBJECTS; i++ )
1444     {
1445         int slot = i + 1;
1446 
1447         snprintf( szFilename, SDL_arraysize( szFilename ), "%s/%d.obj", loadplayer_ptr->dir, i );
1448 
1449         // load the profile
1450         cap_ref = load_one_character_profile_vfs( szFilename, slot, bfalse );
1451         if ( LOADED_CAP( cap_ref ) )
1452         {
1453             cap_ptr = CapStack.lst + cap_ref;
1454 
1455             // go to the next element in the list
1456             chooseplayer_ptr = chooseplayer->lst + chooseplayer->count;
1457             chooseplayer->count++;
1458 
1459             // sace the cap reference
1460             chooseplayer_ptr->cap_ref = cap_ref;
1461 
1462             // get the skin info
1463             chooseplayer_ptr->skin_ref = cap_ptr->skin_override % MAX_SKIN;
1464             chooseplayer_ptr->skin_ref = CLIP( chooseplayer_ptr->skin_ref, 0, MAX_SKIN - 1 );
1465 
1466             // load the icon of the skin
1467             snprintf( szFilename, SDL_arraysize( szFilename ), "%s/%d.obj/icon%d", loadplayer_ptr->dir, i, chooseplayer_ptr->skin_ref );
1468             chooseplayer_ptr->tx_ref = TxTexture_load_one_vfs( szFilename, ( TX_REF )INVALID_TX_TEXTURE, INVALID_KEY );
1469 
1470             // load the chop info from "naming.txt"
1471             snprintf( szFilename, SDL_arraysize( szFilename ), "%s/%d.obj/naming.txt", loadplayer_ptr->dir, i );
1472             chop_load_vfs( &chop_mem, szFilename, &( chooseplayer_ptr->chop ) );
1473         }
1474     }
1475 
1476     return btrue;
1477 }
1478 
1479 //--------------------------------------------------------------------------------------------
doChoosePlayer_show_stats(LoadPlayer_element_t * loadplayer_ptr,int mode,const int x,const int y,const int width,const int height)1480 bool_t doChoosePlayer_show_stats( LoadPlayer_element_t * loadplayer_ptr, int mode, const int x, const int y, const int width, const int height )
1481 {
1482     int i, x1, y1;
1483 
1484     static ChoosePlayer_list_t objects = { 0 };
1485 
1486     if ( NULL == loadplayer_ptr ) mode = 1;
1487 
1488     // handle the profile data
1489     switch ( mode )
1490     {
1491         case 0: // load new loadplayer data
1492 
1493             if ( !doChoosePlayer_load_profiles( loadplayer_ptr, &objects ) )
1494             {
1495                 loadplayer_ptr = NULL;
1496             }
1497             break;
1498 
1499         case 1: // unload loadplayer data
1500 
1501             // release all of the temporary profiles
1502             ChoosePlayer_list_dealloc( &objects );
1503 
1504             loadplayer_ptr = NULL;
1505 
1506             break;
1507     }
1508 
1509     // do the actual display
1510     x1 = x + 25;
1511     y1 = y + 10;
1512     if ( NULL != loadplayer_ptr && objects.count > 0 )
1513     {
1514         CAP_REF icap = objects.lst[0].cap_ref;
1515 
1516         if ( LOADED_CAP( icap ) )
1517         {
1518             STRING temp_string;
1519             cap_t * pcap = CapStack.lst + icap;
1520             Uint8 skin = MAX( pcap->skin_override, 0 );
1521 
1522             ui_drawButton( UI_Nothing, x, y, width, height, NULL );
1523 
1524             // fix class name capitalization
1525             pcap->classname[0] = toupper( pcap->classname[0] );
1526 
1527             //Character level and class
1528             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
1529             snprintf( temp_string, SDL_arraysize( temp_string ), "A level %d %s", pcap->level_override + 1, pcap->classname );
1530             ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1531 
1532             // Armor
1533             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
1534             snprintf( temp_string, SDL_arraysize( temp_string ), "Wearing %s %s", pcap->skinname[skin], HAS_SOME_BITS( pcap->skindressy, 1 << skin ) ? "(Light)" : "(Heavy)" );
1535             ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 30;
1536 
1537             // Life and mana (can be less than maximum if not in easy mode)
1538             if ( cfg.difficulty >= GAME_NORMAL )
1539             {
1540                 snprintf( temp_string, SDL_arraysize( temp_string ), "Life: %d/%d", MIN( FP8_TO_INT( pcap->life_spawn ), ( int )pcap->life_stat.val.from ), ( int )pcap->life_stat.val.from );
1541                 ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1542 
1543                 y1 = ui_drawBar( 0, x1, y1, FP8_TO_INT( pcap->life_spawn ), ( int )pcap->life_stat.val.from, pcap->lifecolor );
1544 
1545                 if ( pcap->mana_stat.val.from > 0 )
1546                 {
1547                     snprintf( temp_string, SDL_arraysize( temp_string ), "Mana: %d/%d", MIN( FP8_TO_INT( pcap->mana_spawn ), ( int )pcap->mana_stat.val.from ), ( int )pcap->mana_stat.val.from );
1548                     ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1549 
1550                     y1 = ui_drawBar( 0, x1, y1, FP8_TO_INT( pcap->mana_spawn ), ( int )pcap->mana_stat.val.from, pcap->manacolor );
1551                 }
1552             }
1553             else
1554             {
1555                 snprintf( temp_string, SDL_arraysize( temp_string ), "Life: %d", ( int )pcap->life_stat.val.from );
1556                 ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1557 
1558                 y1 = ui_drawBar( 0, x1, y1, ( int )pcap->life_stat.val.from, ( int )pcap->life_stat.val.from, pcap->lifecolor );
1559 
1560                 if ( pcap->mana_stat.val.from > 0 )
1561                 {
1562                     snprintf( temp_string, SDL_arraysize( temp_string ), "Mana: %d", ( int )pcap->mana_stat.val.from );
1563                     ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1564 
1565                     y1 = ui_drawBar( 0, x1, y1, ( int )pcap->mana_stat.val.from, ( int )pcap->mana_stat.val.from, pcap->manacolor );
1566                 }
1567             }
1568             y1 += 10;
1569 
1570             //SWID
1571             ui_drawTextBox( menuFont, "Stats", x1, y1, 0, 0, 20 ); y1 += 20;
1572 
1573             snprintf( temp_string, SDL_arraysize( temp_string ), "  Str: %s (%d)", describe_value( pcap->strength_stat.val.from,     60, NULL ), ( int )pcap->strength_stat.val.from );
1574             ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1575 
1576             snprintf( temp_string, SDL_arraysize( temp_string ), "  Wis: %s (%d)", describe_value( pcap->wisdom_stat.val.from,       60, NULL ), ( int )pcap->wisdom_stat.val.from );
1577             ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1578 
1579             snprintf( temp_string, SDL_arraysize( temp_string ), "  Int: %s (%d)", describe_value( pcap->intelligence_stat.val.from, 60, NULL ), ( int )pcap->intelligence_stat.val.from );
1580             ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 20;
1581 
1582             snprintf( temp_string, SDL_arraysize( temp_string ), "  Dex: %s (%d)", describe_value( pcap->dexterity_stat.val.from,    60, NULL ), ( int )pcap->dexterity_stat.val.from );
1583             ui_drawTextBox( menuFont, temp_string, x1, y1, 0, 0, 20 ); y1 += 30;
1584 
1585             //Inventory
1586             if ( objects.count > 1 )
1587             {
1588                 ChoosePlayer_element_t * chooseplayer_ptr;
1589 
1590                 ui_drawTextBox( menuFont, "Inventory", x1, y1, 0, 0, 20 ); y1 += 20;
1591 
1592                 for ( i = 1; i < objects.count; i++ )
1593                 {
1594                     chooseplayer_ptr = objects.lst + i;
1595 
1596                     icap = chooseplayer_ptr->cap_ref;
1597                     if ( LOADED_CAP( icap ) )
1598                     {
1599                         TX_REF  icon_ref;
1600                         cap_t * pcap = CapStack.lst + icap;
1601 
1602                         STRING itemname;
1603                         if ( pcap->nameknown ) strncpy( itemname, chop_create( &chop_mem, &( chooseplayer_ptr->chop ) ), SDL_arraysize( itemname ) );
1604                         else                   strncpy( itemname, pcap->classname,   SDL_arraysize( itemname ) );
1605 
1606                         //draw the icon for this item
1607                         icon_ref = mnu_get_icon_ref( icap, chooseplayer_ptr->tx_ref );
1608                         ui_drawImage( 0, TxTexture_get_ptr( icon_ref ), x1, y1, 32, 32, NULL );
1609 
1610                         if ( icap == SLOT_LEFT + 1 )
1611                         {
1612                             snprintf( temp_string, SDL_arraysize( temp_string ), "  Left: %s", itemname );
1613                             ui_drawTextBox( menuFont, temp_string, x1 + 32, y1 + 6, 0, 0, 20 ); y1 += 32;
1614                         }
1615                         else if ( icap == SLOT_RIGHT + 1 )
1616                         {
1617                             snprintf( temp_string, SDL_arraysize( temp_string ), "  Right: %s", itemname );
1618                             ui_drawTextBox( menuFont, temp_string, x1 + 32, y1 + 6, 0, 0, 20 ); y1 += 32;
1619                         }
1620                         else
1621                         {
1622                             snprintf( temp_string, SDL_arraysize( temp_string ), "  Item: %s", itemname );
1623                             ui_drawTextBox( menuFont, temp_string, x1 + 32, y1 + 6, 0, 0, 20 ); y1 += 32;
1624                         }
1625                     }
1626                 }
1627             }
1628         }
1629     }
1630 
1631     return btrue;
1632 }
1633 
1634 //--------------------------------------------------------------------------------------------
doChoosePlayer(float deltaTime)1635 int doChoosePlayer( float deltaTime )
1636 {
1637     const int x0 = 20, y0 = 20, icon_size = 42, text_width = 175, button_repeat = 47;
1638 
1639     static int menuState = MM_Begin;
1640     static oglx_texture_t background;
1641     static int    startIndex = 0;
1642     static int    last_player = -1;
1643     static bool_t new_player = bfalse;
1644     static int numVertical, numHorizontal;
1645     static Uint32 BitsInput[4];
1646     static bool_t device_on[4];
1647     static const char * button_text[] = { "N/A", "Back", ""};
1648 
1649     int result = 0;
1650     int i, j, x, y;
1651 
1652     switch ( menuState )
1653     {
1654         case MM_Begin:
1655             TxTexture_free_one(( TX_REF )TX_BARS );
1656 
1657             SelectedPlayer_list_init( &mnu_SelectedList );
1658 
1659             TxTexture_load_one_vfs( "mp_data/nullicon", ( TX_REF )ICON_NULL, INVALID_KEY );
1660 
1661             TxTexture_load_one_vfs( "mp_data/keybicon", ( TX_REF )ICON_KEYB, INVALID_KEY );
1662             BitsInput[0] = INPUT_BITS_KEYBOARD;
1663             device_on[0] = keyb.on;
1664 
1665             TxTexture_load_one_vfs( "mp_data/mousicon", ( TX_REF )ICON_MOUS, INVALID_KEY );
1666             BitsInput[1] = INPUT_BITS_MOUSE;
1667             device_on[1] = mous.on;
1668 
1669             TxTexture_load_one_vfs( "mp_data/joyaicon", ( TX_REF )ICON_JOYA, INVALID_KEY );
1670             BitsInput[2] = INPUT_BITS_JOYA;
1671             device_on[2] = joy[0].on;
1672 
1673             TxTexture_load_one_vfs( "mp_data/joybicon", ( TX_REF )ICON_JOYB, INVALID_KEY );
1674             BitsInput[3] = INPUT_BITS_JOYB;
1675             device_on[3] = joy[1].on;
1676 
1677             ego_texture_load_vfs( &background, "mp_data/menu/menu_sleepy", TRANSCOLOR );
1678 
1679             TxTexture_load_one_vfs( "mp_data/bars", ( TX_REF )TX_BARS, INVALID_KEY );
1680 
1681             // load information for all the players that could be imported
1682             LoadPlayer_list_import_all( &mnu_loadplayer, "mp_players", btrue );
1683 
1684             // reset button 0, or it will mess up the menu.
1685             // must do it before mnu_SlidyButton_init()
1686             button_text[0] = "N/A";
1687 
1688             mnu_SlidyButton_init( 1.0f, button_text );
1689 
1690             numVertical   = ( buttonTop - y0 ) / button_repeat - 1;
1691             numHorizontal = 1;
1692 
1693             x = x0;
1694             y = y0;
1695             for ( i = 0; i < numVertical; i++ )
1696             {
1697                 int m = i * 5;
1698 
1699                 ui_initWidget( mnu_widgetList + m, m, NULL, NULL, NULL, x, y, text_width, icon_size );
1700                 ui_widgetAddMask( mnu_widgetList + m, UI_BITS_CLICKED );
1701 
1702                 for ( j = 0, m++; j < 4; j++, m++ )
1703                 {
1704                     ui_initWidget( mnu_widgetList + m, m, menuFont, NULL, TxTexture_get_ptr(( TX_REF )( ICON_KEYB + j ) ), x + text_width + j*icon_size, y, icon_size, icon_size );
1705                     ui_widgetAddMask( mnu_widgetList + m, UI_BITS_CLICKED );
1706                 };
1707 
1708                 y += button_repeat;
1709             };
1710 
1711             if ( mnu_loadplayer.count < 10 )
1712             {
1713                 tipText_set_position( menuFont, "Choose an input device to select your player(s)", 20 );
1714             }
1715             else
1716             {
1717                 tipText_set_position( menuFont, "Choose an input device to select your player(s)\nUse the mouse wheel to scroll.", 20 );
1718             }
1719 
1720             menuState = MM_Entering;
1721             // fall through
1722 
1723         case MM_Entering:
1724 
1725             /*GL_DEBUG(glColor4f)(1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
1726             mnu_SlidyButton_draw_all();
1727             mnu_SlidyButton_update_all( -deltaTime );
1728             // Let lerp wind down relative to the time elapsed
1729             if ( mnu_SlidyButtonState.lerp <= 0.0f )
1730             {
1731                 menuState = MM_Running;
1732             }*/
1733 
1734             // Simply fall through
1735             // menuState = MM_Running;
1736             // break;
1737 
1738         case MM_Running:
1739             // Figure out how many players we can show without scrolling
1740 
1741             if ( 0 == mnu_SelectedList.count )
1742             {
1743                 button_text[0] = "";
1744             }
1745             else if ( 1 == mnu_SelectedList.count )
1746             {
1747                 button_text[0] = "Select Player";
1748             }
1749             else
1750             {
1751                 button_text[0] = "Select Players";
1752             }
1753 
1754             // Draw the background
1755             x = ( GFX_WIDTH  / 2 ) - ( background.imgW / 2 );
1756             y = GFX_HEIGHT - background.imgH;
1757 
1758             if ( mnu_draw_background )
1759             {
1760                 ui_drawImage( 0, &background, x, y, 0, 0, NULL );
1761             }
1762 
1763             // use the mouse wheel to scan the characters
1764             if ( cursor_wheel_event_pending() )
1765             {
1766                 if ( cursor.z > 0 )
1767                 {
1768                     if ( startIndex + numVertical < mnu_loadplayer.count )
1769                     {
1770                         startIndex++;
1771                     }
1772                 }
1773                 else if ( cursor.z < 0 )
1774                 {
1775                     if ( startIndex > 0 )
1776                     {
1777                         startIndex--;
1778                     }
1779                 }
1780 
1781                 cursor_finish_wheel_event();
1782             }
1783 
1784             // Draw the lplayer selection buttons
1785             x = x0;
1786             y = y0;
1787             for ( i = 0; i < numVertical; i++ )
1788             {
1789                 int lplayer;
1790                 int splayer;
1791                 LoadPlayer_element_t * loadplayer_ptr = NULL;
1792                 int m = i * 5;
1793 
1794                 lplayer = i + startIndex;
1795                 if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, lplayer ) ) continue;
1796 
1797                 loadplayer_ptr = mnu_loadplayer.lst + lplayer;
1798                 splayer        = SelectedPlayer_list_index_from_loadplayer( &mnu_SelectedList,  lplayer );
1799 
1800                 // do the character button
1801                 mnu_widgetList[m].img  = TxTexture_get_ptr( loadplayer_ptr->tx_ref );
1802                 mnu_widgetList[m].text = loadplayer_ptr->name;
1803                 if ( INVALID_PLAYER != splayer )
1804                 {
1805                     SET_BIT( mnu_widgetList[m].state, UI_BITS_CLICKED );
1806                 }
1807                 else
1808                 {
1809                     UNSET_BIT( mnu_widgetList[m].state, UI_BITS_CLICKED );
1810                 }
1811 
1812                 if ( BUTTON_DOWN == ui_doWidget( mnu_widgetList + m ) )
1813                 {
1814                     if ( HAS_SOME_BITS( mnu_widgetList[m].state, UI_BITS_CLICKED ) && !SelectedPlayer_list_check_loadplayer( &mnu_SelectedList,  lplayer ) )
1815                     {
1816                         // button has become cursor_clicked
1817                         // SelectedPlayer_list_add( &mnu_SelectedList, lplayer);
1818                         last_player = lplayer;
1819                         new_player  = btrue;
1820                     }
1821                     else if ( HAS_NO_BITS( mnu_widgetList[m].state, UI_BITS_CLICKED ) && SelectedPlayer_list_check_loadplayer( &mnu_SelectedList,  lplayer ) )
1822                     {
1823                         // button has become unclicked
1824                         if ( SelectedPlayer_list_remove( &mnu_SelectedList,  lplayer ) )
1825                         {
1826                             last_player = -1;
1827                         }
1828                     }
1829                 }
1830 
1831                 // do each of the input buttons
1832                 for ( j = 0, m++; j < 4; j++, m++ )
1833                 {
1834                     /// @note check for devices being turned on and off each time
1835                     /// technically we COULD recognize joysticks being inserted and removed while the
1836                     /// game is running but we don't. I will set this up to handle such future behavior
1837                     /// anyway :)
1838                     switch ( j )
1839                     {
1840                         case INPUT_DEVICE_KEYBOARD: device_on[j] = keyb.on; break;
1841                         case INPUT_DEVICE_MOUSE:    device_on[j] = mous.on; break;
1842                         case INPUT_DEVICE_JOY_A:  device_on[j] = joy[0].on; break;
1843                         case INPUT_DEVICE_JOY_B:  device_on[j] = joy[1].on; break;
1844                         default: device_on[j] = bfalse;
1845                     }
1846 
1847                     // replace any not on device with a null icon
1848                     if ( !device_on[j] )
1849                     {
1850                         mnu_widgetList[m].img = TxTexture_get_ptr(( TX_REF )ICON_NULL );
1851 
1852                         // draw the widget, even though it will not do anything
1853                         // the menu would looks funny if odd columns missing
1854                         ui_doWidget( mnu_widgetList + m );
1855                     }
1856 
1857                     // make the button states reflect the chosen input devices
1858                     if ( INVALID_PLAYER == splayer || HAS_NO_BITS( mnu_SelectedList.lst[ splayer ].input, BitsInput[j] ) )
1859                     {
1860                         UNSET_BIT( mnu_widgetList[m].state, UI_BITS_CLICKED );
1861                     }
1862                     else if ( HAS_SOME_BITS( mnu_SelectedList.lst[splayer].input, BitsInput[j] ) )
1863                     {
1864                         SET_BIT( mnu_widgetList[m].state, UI_BITS_CLICKED );
1865                     }
1866 
1867                     if ( BUTTON_DOWN == ui_doWidget( mnu_widgetList + m ) )
1868                     {
1869                         if ( HAS_SOME_BITS( mnu_widgetList[m].state, UI_BITS_CLICKED ) )
1870                         {
1871                             // button has become cursor_clicked
1872                             if ( INVALID_PLAYER == splayer )
1873                             {
1874                                 if ( SelectedPlayer_list_add( &mnu_SelectedList,  lplayer ) )
1875                                 {
1876                                     last_player = lplayer;
1877                                     new_player  = btrue;
1878                                 }
1879                             }
1880                             if ( SelectedPlayer_list_add_input( &mnu_SelectedList,  lplayer, BitsInput[j] ) )
1881                             {
1882                                 last_player = lplayer;
1883                                 new_player  = btrue;
1884                             }
1885                         }
1886                         else if ( INVALID_PLAYER != splayer && HAS_NO_BITS( mnu_widgetList[m].state, UI_BITS_CLICKED ) )
1887                         {
1888                             // button has become unclicked
1889                             if ( SelectedPlayer_list_remove_input( &mnu_SelectedList,  lplayer, BitsInput[j] ) )
1890                             {
1891                                 last_player = -1;
1892                             }
1893                         }
1894                     }
1895                 }
1896             }
1897 
1898             // Buttons for going ahead
1899 
1900             // Continue
1901             if ( 0 != mnu_SelectedList.count )
1902             {
1903                 if ( SDLKEYDOWN( SDLK_RETURN ) || BUTTON_UP == ui_doButton( 100, button_text[0], NULL, buttonLeft, buttonTop, 200, 30 ) )
1904                 {
1905                     menuState = MM_Leaving;
1906                 }
1907             }
1908 
1909             // Back
1910             if ( SDLKEYDOWN( SDLK_ESCAPE ) || BUTTON_UP == ui_doButton( 101, button_text[1], NULL, buttonLeft, buttonTop + 35, 200, 30 ) )
1911             {
1912                 SelectedPlayer_list_init( &mnu_SelectedList );
1913                 menuState = MM_Leaving;
1914             }
1915 
1916             // show the stats
1917             if ( VALID_LOADPLAYER_IDX( mnu_loadplayer, last_player ) )
1918             {
1919                 LoadPlayer_element_t * loadplayer_ptr = mnu_loadplayer.lst + last_player;
1920                 if ( new_player )
1921                 {
1922                     // load and display the new lplayer data
1923                     new_player = bfalse;
1924                     doChoosePlayer_show_stats( loadplayer_ptr, 0, GFX_WIDTH - 400, 10, 350, GFX_HEIGHT - 60 );
1925                 }
1926                 else
1927                 {
1928                     // just display the new lplayer data
1929                     doChoosePlayer_show_stats( loadplayer_ptr, 2, GFX_WIDTH - 400, 10, 350, GFX_HEIGHT - 60 );
1930                 }
1931             }
1932             else
1933             {
1934                 doChoosePlayer_show_stats( NULL, 1, GFX_WIDTH - 100, 10, 100, GFX_HEIGHT - 60 );
1935             }
1936 
1937             // tool-tip text
1938             ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
1939 
1940             break;
1941 
1942         case MM_Leaving:
1943             /*
1944             // Do buttons sliding out and background fading
1945             // Do the same stuff as in MM_Entering, but backwards
1946             GL_DEBUG(glColor4f)(1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
1947             // Buttons
1948             mnu_SlidyButton_draw_all();
1949             mnu_SlidyButton_update_all( deltaTime );
1950             if ( mnu_SlidyButtonState.lerp >= 1.0f )
1951             {
1952                 menuState = MM_Finish;
1953             }
1954             */
1955 
1956             // Simply fall through
1957             //  menuState = MM_Finish;
1958             //  break;
1959 
1960         case MM_Finish:
1961 
1962             // release all of the temporary profiles
1963             doChoosePlayer_show_stats( NULL, 1, 0, 0, 0, 0 );
1964 
1965             oglx_texture_Release( &background );
1966             TxTexture_free_one(( TX_REF )TX_BARS );
1967 
1968             menuState = MM_Begin;
1969             if ( 0 == mnu_SelectedList.count )
1970             {
1971                 result = -1;
1972             }
1973             else
1974             {
1975                 // set up the slots and the import stuff for the selected players
1976                 if ( rv_success == mnu_set_local_import_list( &ImportList, &mnu_SelectedList ) )
1977                 {
1978                     game_copy_imports( &ImportList );
1979                 }
1980                 else
1981                 {
1982                     // erase the data in the import folder
1983                     vfs_removeDirectoryAndContents( "import", VFS_TRUE );
1984                 }
1985 
1986                 // if there are no selected players, there is no reason to keep this data
1987                 if ( 0 == mnu_SelectedList.count )
1988                 {
1989                     LoadPlayer_list_dealloc( &mnu_loadplayer );
1990                 }
1991 
1992                 result = 1;
1993             }
1994 
1995             // reset the ui
1996             ui_Reset();
1997 
1998             break;
1999     }
2000 
2001     return result;
2002 }
2003 
2004 //--------------------------------------------------------------------------------------------
doOptions(float deltaTime)2005 int doOptions( float deltaTime )
2006 {
2007     static int menuState = MM_Begin;
2008     static oglx_texture_t background;
2009     static int menuChoice = 0;
2010 
2011     static const char *sz_buttons[] =
2012     {
2013         "Game Options",
2014         "Audio Options",
2015         "Input Controls",
2016         "Video Settings",
2017         "Back",
2018         ""
2019     };
2020 
2021     int result = 0;
2022 
2023     switch ( menuState )
2024     {
2025         case MM_Begin:
2026             // set up menu variables
2027             ego_texture_load_vfs( &background, "mp_data/menu/menu_gnome", TRANSCOLOR );
2028             menuChoice = 0;
2029             menuState = MM_Entering;
2030 
2031             tipText_set_position( menuFont, "Change your audio, input and video\nsettings here.", 20 );
2032 
2033             mnu_SlidyButton_init( 1.0f, sz_buttons );
2034             // let this fall through into MM_Entering
2035 
2036         case MM_Entering:
2037             // do buttons sliding in animation, and background fading in
2038             // background
2039             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2040 
2041             // Draw the background
2042             if ( mnu_draw_background )
2043             {
2044                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
2045             }
2046 
2047             mnu_SlidyButton_draw_all();
2048             mnu_SlidyButton_update_all( -deltaTime );
2049 
2050             // Let lerp wind down relative to the time elapsed
2051             if ( mnu_SlidyButtonState.lerp <= 0.0f )
2052             {
2053                 menuState = MM_Running;
2054             }
2055             break;
2056 
2057         case MM_Running:
2058             // Do normal run
2059             // Background
2060             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
2061 
2062             if ( mnu_draw_background )
2063             {
2064                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
2065             }
2066 
2067             // "Options" text
2068             ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
2069 
2070             // Buttons
2071             if ( BUTTON_UP == ui_doButton( 1, sz_buttons[0], NULL, buttonLeft, buttonTop, 200, 30 ) )
2072             {
2073                 // game options
2074                 menuChoice = 5;
2075             }
2076             if ( BUTTON_UP == ui_doButton( 2, sz_buttons[1], NULL, buttonLeft, buttonTop + 35, 200, 30 ) )
2077             {
2078                 // audio options
2079                 menuChoice = 1;
2080             }
2081             if ( BUTTON_UP == ui_doButton( 3, sz_buttons[2], NULL, buttonLeft, buttonTop + 35 * 2, 200, 30 ) )
2082             {
2083                 // input options
2084                 menuChoice = 2;
2085             }
2086             if ( BUTTON_UP == ui_doButton( 4, sz_buttons[3], NULL, buttonLeft, buttonTop + 35 * 3, 200, 30 ) )
2087             {
2088                 // video options
2089                 menuChoice = 3;
2090             }
2091             if ( SDLKEYDOWN( SDLK_ESCAPE ) || BUTTON_UP == ui_doButton( 5, sz_buttons[4], NULL, buttonLeft, buttonTop + 35 * 4, 200, 30 ) )
2092             {
2093                 // back to main menu
2094                 menuChoice = 4;
2095             }
2096             if ( menuChoice != 0 )
2097             {
2098                 menuState = MM_Leaving;
2099                 mnu_SlidyButton_init( 0.0f, sz_buttons );
2100             }
2101             break;
2102 
2103         case MM_Leaving:
2104             // Do buttons sliding out and background fading
2105             // Do the same stuff as in MM_Entering, but backwards
2106             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2107 
2108             if ( mnu_draw_background )
2109             {
2110                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
2111             }
2112 
2113             // Buttons
2114             mnu_SlidyButton_draw_all();
2115             mnu_SlidyButton_update_all( deltaTime );
2116             if ( mnu_SlidyButtonState.lerp >= 1.0f )
2117             {
2118                 menuState = MM_Finish;
2119             }
2120             break;
2121 
2122         case MM_Finish:
2123             // Free the background texture; don't need to hold onto it
2124             oglx_texture_Release( &background );
2125             menuState = MM_Begin;  // Make sure this all resets next time
2126 
2127             // reset the ui
2128             ui_Reset();
2129 
2130             // Set the next menu to load
2131             result = menuChoice;
2132             break;
2133     }
2134 
2135     return result;
2136 }
2137 
2138 //--------------------------------------------------------------------------------------------
doInputOptions(float deltaTime)2139 int doInputOptions( float deltaTime )
2140 {
2141     static int menuState = MM_Begin;
2142     static int menuChoice = 0;
2143     static int waitingforinput = -1;
2144 
2145     static STRING inputOptionsButtons[CONTROL_COMMAND_COUNT + 2];
2146 
2147     Sint8  result = 0;
2148     static Sint32 player = 0;
2149 
2150     Uint32              i;
2151     Sint32              idevice, iicon;
2152     device_controls_t * pdevice;
2153 
2154     pdevice = NULL;
2155     if ( player >= 0 && player < input_device_count )
2156     {
2157         pdevice = controls + player;
2158     };
2159 
2160     idevice = player;
2161     if ( NULL == pdevice )
2162     {
2163         idevice = -1;
2164     }
2165 
2166     iicon = MIN( 4, idevice );
2167     if ( idevice < 0 )
2168     {
2169         iicon = -1;
2170     }
2171 
2172     switch ( menuState )
2173     {
2174         case MM_Begin:
2175             // set up menu variables
2176             menuChoice = 0;
2177             menuState = MM_Entering;
2178             // let this fall through into MM_Entering
2179 
2180             waitingforinput = -1;
2181 
2182             for ( i = 0; i < CONTROL_COMMAND_COUNT; i++ )
2183             {
2184                 inputOptionsButtons[i][0] = CSTR_END;
2185             }
2186             strncpy( inputOptionsButtons[i++], "Player 1", sizeof( STRING ) );
2187             strncpy( inputOptionsButtons[i++], "Save Settings", sizeof( STRING ) );
2188 
2189             tipText_set_position( menuFont, "Change input settings here.", 20 );
2190 
2191             // Load the global icons (keyboard, mouse, etc.)
2192             if ( !load_all_global_icons() ) log_warning( "Could not load all global icons!\n" );
2193 
2194         case MM_Entering:
2195             // do buttons sliding in animation, and background fading in
2196             // background
2197             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2198 
2199             // Fall trough
2200             menuState = MM_Running;
2201             break;
2202 
2203         case MM_Running:
2204             // Do normal run
2205             // Background
2206             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
2207             ui_drawTextBox( menuFont, "ATK_LEFT HAND", buttonLeft, GFX_HEIGHT - 470, 0, 0, 20 );
2208 
2209             // Are we waiting for input?
2210             if ( SDLKEYDOWN( SDLK_ESCAPE ) ) waitingforinput = -1;  // Someone pressed abort
2211 
2212             // Grab the key/button input from the selected device
2213             if ( -1 != waitingforinput )
2214             {
2215                 if ( NULL == pdevice || idevice < 0 || idevice >= input_device_count )
2216                 {
2217                     waitingforinput = -1;
2218                 }
2219                 else
2220                 {
2221                     if ( waitingforinput >= pdevice->count )
2222                     {
2223                         // invalid input range for this device
2224                         waitingforinput = -1;
2225                     }
2226                     else
2227                     {
2228                         control_t * pcontrol;
2229                         int         tag;
2230 
2231                         // make sure to update the input
2232                         input_read();
2233 
2234                         pcontrol = pdevice->control + waitingforinput;
2235                         if ( idevice >= INPUT_DEVICE_JOY )
2236                         {
2237                             int ijoy = idevice - INPUT_DEVICE_JOY;
2238                             if ( ijoy < MAXJOYSTICK )
2239                             {
2240                                 for ( tag = 0; tag < scantag_count; tag++ )
2241                                 {
2242                                     if ( 0 != scantag[tag].value && ( Uint32 )scantag[tag].value == joy[ijoy].b )
2243                                     {
2244                                         pcontrol->tag    = scantag[tag].value;
2245                                         pcontrol->is_key = bfalse;
2246                                         waitingforinput = -1;
2247                                     }
2248                                 }
2249 
2250                                 for ( tag = 0; tag < scantag_count; tag++ )
2251                                 {
2252                                     if ( scantag[tag].value < SDLK_NUMLOCK && SDLKEYDOWN( scantag[tag].value ) )
2253                                     {
2254                                         pcontrol->tag    = scantag[tag].value;
2255                                         pcontrol->is_key = btrue;
2256                                         waitingforinput = -1;
2257                                     }
2258                                 }
2259                             }
2260                         }
2261                         else
2262                         {
2263                             switch ( idevice )
2264                             {
2265                                 case INPUT_DEVICE_KEYBOARD:
2266                                     {
2267                                         for ( tag = 0; tag < scantag_count; tag++ )
2268                                         {
2269                                             if ( scantag[tag].value < SDLK_NUMLOCK && SDLKEYDOWN( scantag[tag].value ) )
2270                                             {
2271                                                 pcontrol->tag    = scantag[tag].value;
2272                                                 pcontrol->is_key = btrue;
2273                                                 waitingforinput = -1;
2274                                             }
2275                                         }
2276                                     }
2277                                     break;
2278 
2279                                 case INPUT_DEVICE_MOUSE:
2280                                     {
2281                                         for ( tag = 0; tag < scantag_count; tag++ )
2282                                         {
2283                                             if ( 0 != scantag[tag].value && ( Uint32 )scantag[tag].value == mous.b )
2284                                             {
2285                                                 pcontrol->tag    = scantag[tag].value;
2286                                                 pcontrol->is_key = bfalse;
2287                                                 waitingforinput = -1;
2288                                             }
2289                                         }
2290 
2291                                         for ( tag = 0; tag < scantag_count; tag++ )
2292                                         {
2293                                             if ( scantag[tag].value < SDLK_NUMLOCK && SDLKEYDOWN( scantag[tag].value ) )
2294                                             {
2295                                                 pcontrol->tag    = scantag[tag].value;
2296                                                 pcontrol->is_key = btrue;
2297                                                 waitingforinput = -1;
2298                                             }
2299                                         }
2300                                     }
2301                                     break;
2302                             }
2303                         }
2304 
2305                     }
2306                 }
2307             }
2308             if ( NULL != pdevice && -1 == waitingforinput )
2309             {
2310                 // update the control names
2311                 for ( i = CONTROL_BEGIN; i <= CONTROL_END && i < pdevice->count; i++ )
2312                 {
2313                     const char * tag = scantag_get_string( pdevice->device, pdevice->control[i].tag, pdevice->control[i].is_key );
2314 
2315                     strncpy( inputOptionsButtons[i], tag, sizeof( STRING ) );
2316                 }
2317                 for ( /* nothing */; i <= CONTROL_END ; i++ )
2318                 {
2319                     inputOptionsButtons[i][0] = CSTR_END;
2320                 }
2321             }
2322 
2323             // Left hand
2324             if ( CSTR_END != inputOptionsButtons[CONTROL_LEFT_USE][0] )
2325             {
2326                 ui_drawTextBox( menuFont, "Use:", buttonLeft, GFX_HEIGHT - 440, 0, 0, 20 );
2327                 if ( BUTTON_UP == ui_doButton( 1, inputOptionsButtons[CONTROL_LEFT_USE], menuFont, buttonLeft + 100, GFX_HEIGHT - 440, 140, 30 ) )
2328                 {
2329                     waitingforinput = CONTROL_LEFT_USE;
2330                     strncpy( inputOptionsButtons[CONTROL_LEFT_USE], "...", sizeof( STRING ) );
2331                 }
2332             }
2333             if ( CSTR_END != inputOptionsButtons[CONTROL_LEFT_GET][0] )
2334             {
2335                 ui_drawTextBox( menuFont, "Get/Drop:", buttonLeft, GFX_HEIGHT - 410, 0, 0, 20 );
2336                 if ( BUTTON_UP == ui_doButton( 2, inputOptionsButtons[CONTROL_LEFT_GET], menuFont, buttonLeft + 100, GFX_HEIGHT - 410, 140, 30 ) )
2337                 {
2338                     waitingforinput = CONTROL_LEFT_GET;
2339                     strncpy( inputOptionsButtons[CONTROL_LEFT_GET], "...", sizeof( STRING ) );
2340                 }
2341             }
2342             if ( CSTR_END != inputOptionsButtons[CONTROL_LEFT_PACK][0] )
2343             {
2344                 ui_drawTextBox( menuFont, "Inventory:", buttonLeft, GFX_HEIGHT - 380, 0, 0, 20 );
2345                 if ( BUTTON_UP == ui_doButton( 3, inputOptionsButtons[CONTROL_LEFT_PACK], menuFont, buttonLeft + 100, GFX_HEIGHT - 380, 140, 30 ) )
2346                 {
2347                     waitingforinput = CONTROL_LEFT_PACK;
2348                     strncpy( inputOptionsButtons[CONTROL_LEFT_PACK], "...", sizeof( STRING ) );
2349                 }
2350             }
2351 
2352             // Right hand
2353             ui_drawTextBox( menuFont, "ATK_RIGHT HAND", buttonLeft + 300, GFX_HEIGHT - 470, 0, 0, 20 );
2354             if ( CSTR_END != inputOptionsButtons[CONTROL_RIGHT_USE][0] )
2355             {
2356                 ui_drawTextBox( menuFont, "Use:", buttonLeft + 300, GFX_HEIGHT - 440, 0, 0, 20 );
2357                 if ( BUTTON_UP == ui_doButton( 4, inputOptionsButtons[CONTROL_RIGHT_USE], menuFont, buttonLeft + 400, GFX_HEIGHT - 440, 140, 30 ) )
2358                 {
2359                     waitingforinput = CONTROL_RIGHT_USE;
2360                     strncpy( inputOptionsButtons[CONTROL_RIGHT_USE], "...", sizeof( STRING ) );
2361                 }
2362             }
2363             if ( CSTR_END != inputOptionsButtons[CONTROL_RIGHT_GET][0] )
2364             {
2365                 ui_drawTextBox( menuFont, "Get/Drop:", buttonLeft + 300, GFX_HEIGHT - 410, 0, 0, 20 );
2366                 if ( BUTTON_UP == ui_doButton( 5, inputOptionsButtons[CONTROL_RIGHT_GET], menuFont, buttonLeft + 400, GFX_HEIGHT - 410, 140, 30 ) )
2367                 {
2368                     waitingforinput = CONTROL_RIGHT_GET;
2369                     strncpy( inputOptionsButtons[CONTROL_RIGHT_GET], "...", sizeof( STRING ) );
2370                 }
2371             }
2372             if ( CSTR_END != inputOptionsButtons[CONTROL_RIGHT_PACK][0] )
2373             {
2374                 ui_drawTextBox( menuFont, "Inventory:", buttonLeft + 300, GFX_HEIGHT - 380, 0, 0, 20 );
2375                 if ( BUTTON_UP == ui_doButton( 6, inputOptionsButtons[CONTROL_RIGHT_PACK], menuFont, buttonLeft + 400, GFX_HEIGHT - 380, 140, 30 ) )
2376                 {
2377                     waitingforinput = CONTROL_RIGHT_PACK;
2378                     strncpy( inputOptionsButtons[CONTROL_RIGHT_PACK], "...", sizeof( STRING ) );
2379                 }
2380             }
2381 
2382             // Controls
2383             ui_drawTextBox( menuFont, "CONTROLS", buttonLeft, GFX_HEIGHT - 320, 0, 0, 20 );
2384             if ( CSTR_END != inputOptionsButtons[CONTROL_JUMP][0] )
2385             {
2386                 ui_drawTextBox( menuFont, "Jump:", buttonLeft, GFX_HEIGHT - 290, 0, 0, 20 );
2387                 if ( BUTTON_UP == ui_doButton( 7, inputOptionsButtons[CONTROL_JUMP], menuFont, buttonLeft + 100, GFX_HEIGHT - 290, 140, 30 ) )
2388                 {
2389                     waitingforinput = CONTROL_JUMP;
2390                     strncpy( inputOptionsButtons[CONTROL_JUMP], "...", sizeof( STRING ) );
2391                 }
2392             }
2393             if ( CSTR_END != inputOptionsButtons[CONTROL_UP][0] )
2394             {
2395                 ui_drawTextBox( menuFont, "Up:", buttonLeft, GFX_HEIGHT - 260, 0, 0, 20 );
2396                 if ( BUTTON_UP == ui_doButton( 8, inputOptionsButtons[CONTROL_UP], menuFont, buttonLeft + 100, GFX_HEIGHT - 260, 140, 30 ) )
2397                 {
2398                     waitingforinput = CONTROL_UP;
2399                     strncpy( inputOptionsButtons[CONTROL_UP], "...", sizeof( STRING ) );
2400                 }
2401             }
2402             if ( CSTR_END != inputOptionsButtons[CONTROL_DOWN][0] )
2403             {
2404                 ui_drawTextBox( menuFont, "Down:", buttonLeft, GFX_HEIGHT - 230, 0, 0, 20 );
2405                 if ( BUTTON_UP == ui_doButton( 9, inputOptionsButtons[CONTROL_DOWN], menuFont, buttonLeft + 100, GFX_HEIGHT - 230, 140, 30 ) )
2406                 {
2407                     waitingforinput = CONTROL_DOWN;
2408                     strncpy( inputOptionsButtons[CONTROL_DOWN], "...", sizeof( STRING ) );
2409                 }
2410             }
2411             if ( CSTR_END != inputOptionsButtons[CONTROL_LEFT][0] )
2412             {
2413                 ui_drawTextBox( menuFont, "Left:", buttonLeft, GFX_HEIGHT - 200, 0, 0, 20 );
2414                 if ( BUTTON_UP == ui_doButton( 10, inputOptionsButtons[CONTROL_LEFT], menuFont, buttonLeft + 100, GFX_HEIGHT - 200, 140, 30 ) )
2415                 {
2416                     waitingforinput = CONTROL_LEFT;
2417                     strncpy( inputOptionsButtons[CONTROL_LEFT], "...", sizeof( STRING ) );
2418                 }
2419             }
2420             if ( CSTR_END != inputOptionsButtons[CONTROL_RIGHT][0] )
2421             {
2422                 ui_drawTextBox( menuFont, "Right:", buttonLeft, GFX_HEIGHT - 170, 0, 0, 20 );
2423                 if ( BUTTON_UP == ui_doButton( 11, inputOptionsButtons[CONTROL_RIGHT], menuFont, buttonLeft + 100, GFX_HEIGHT - 170, 140, 30 ) )
2424                 {
2425                     waitingforinput = CONTROL_RIGHT;
2426                     strncpy( inputOptionsButtons[CONTROL_RIGHT], "...", sizeof( STRING ) );
2427                 }
2428             }
2429 
2430             // Controls
2431             ui_drawTextBox( menuFont, "CAMERA CONTROL", buttonLeft + 300, GFX_HEIGHT - 320, 0, 0, 20 );
2432             if ( CSTR_END != inputOptionsButtons[CONTROL_CAMERA_IN][0] )
2433             {
2434                 ui_drawTextBox( menuFont, "Zoom In:", buttonLeft + 300, GFX_HEIGHT - 290, 0, 0, 20 );
2435                 if ( BUTTON_UP == ui_doButton( 12, inputOptionsButtons[CONTROL_CAMERA_IN], menuFont, buttonLeft + 450, GFX_HEIGHT - 290, 140, 30 ) )
2436                 {
2437                     waitingforinput = CONTROL_CAMERA_IN;
2438                     strncpy( inputOptionsButtons[CONTROL_CAMERA_IN], "...", sizeof( STRING ) );
2439                 }
2440             }
2441             else
2442             {
2443                 // single button camera control
2444                 ui_drawTextBox( menuFont, "Camera:", buttonLeft + 300, GFX_HEIGHT - 290, 0, 0, 20 );
2445                 if ( BUTTON_UP == ui_doButton( 12, inputOptionsButtons[CONTROL_CAMERA], menuFont, buttonLeft + 450, GFX_HEIGHT - 290, 140, 30 ) )
2446                 {
2447                     waitingforinput = CONTROL_CAMERA;
2448                     strncpy( inputOptionsButtons[CONTROL_CAMERA], "...", sizeof( STRING ) );
2449                 }
2450             }
2451             if ( CSTR_END != inputOptionsButtons[CONTROL_CAMERA_OUT][0] )
2452             {
2453                 ui_drawTextBox( menuFont, "Zoom Out:", buttonLeft + 300, GFX_HEIGHT - 260, 0, 0, 20 );
2454                 if ( BUTTON_UP == ui_doButton( 13, inputOptionsButtons[CONTROL_CAMERA_OUT], menuFont, buttonLeft + 450, GFX_HEIGHT - 260, 140, 30 ) )
2455                 {
2456                     waitingforinput = CONTROL_CAMERA_OUT;
2457                     strncpy( inputOptionsButtons[CONTROL_CAMERA_OUT], "...", sizeof( STRING ) );
2458                 }
2459             }
2460             if ( CSTR_END != inputOptionsButtons[CONTROL_CAMERA_LEFT][0] )
2461             {
2462                 ui_drawTextBox( menuFont, "Rotate Left:", buttonLeft + 300, GFX_HEIGHT - 230, 0, 0, 20 );
2463                 if ( BUTTON_UP == ui_doButton( 14, inputOptionsButtons[CONTROL_CAMERA_LEFT], menuFont, buttonLeft + 450, GFX_HEIGHT - 230, 140, 30 ) )
2464                 {
2465                     waitingforinput = CONTROL_CAMERA_LEFT;
2466                     strncpy( inputOptionsButtons[CONTROL_CAMERA_LEFT], "...", sizeof( STRING ) );
2467                 }
2468             }
2469             if ( CSTR_END != inputOptionsButtons[CONTROL_CAMERA_RIGHT][0] )
2470             {
2471                 ui_drawTextBox( menuFont, "Rotate Right:", buttonLeft + 300, GFX_HEIGHT - 200, 0, 0, 20 );
2472                 if ( BUTTON_UP == ui_doButton( 15, inputOptionsButtons[CONTROL_CAMERA_RIGHT], menuFont, buttonLeft + 450, GFX_HEIGHT - 200, 140, 30 ) )
2473                 {
2474                     waitingforinput = CONTROL_CAMERA_RIGHT;
2475                     strncpy( inputOptionsButtons[CONTROL_CAMERA_RIGHT], "...", sizeof( STRING ) );
2476                 }
2477             }
2478 
2479             // The select player button
2480             if ( iicon < 0 )
2481             {
2482                 if ( BUTTON_UP == ui_doButton( 16, "Select Player...", NULL, buttonLeft + 300, GFX_HEIGHT - 90, 140, 50 ) )
2483                 {
2484                     player = 0;
2485                 }
2486             }
2487             else if ( BUTTON_UP ==  ui_doImageButtonWithText( 16, TxTexture_get_ptr(( TX_REF )( ICON_KEYB + iicon ) ), inputOptionsButtons[CONTROL_COMMAND_COUNT+0], menuFont, buttonLeft + 300, GFX_HEIGHT - 90, 140, 50 ) )
2488             {
2489                 if ( input_device_count > 0 )
2490                 {
2491                     player++;
2492                     player %= input_device_count;
2493                 }
2494 
2495                 snprintf( inputOptionsButtons[CONTROL_COMMAND_COUNT+0], sizeof( STRING ), "Player %i", player + 1 );
2496             }
2497 
2498             // Save settings button
2499             if ( BUTTON_UP == ui_doButton( 17, inputOptionsButtons[CONTROL_COMMAND_COUNT+1], menuFont, buttonLeft, GFX_HEIGHT - 60, 200, 30 ) )
2500             {
2501                 // save settings and go back
2502                 player = 0;
2503                 input_settings_save_vfs( "controls.txt" );
2504                 menuState = MM_Leaving;
2505             }
2506 
2507             // tool-tip text
2508             ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
2509 
2510             break;
2511 
2512         case MM_Leaving:
2513             // Do buttons sliding out and background fading
2514             // Do the same stuff as in MM_Entering, but backwards
2515             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2516 
2517             // Fall trough
2518             menuState = MM_Finish;
2519             break;
2520 
2521         case MM_Finish:
2522             menuState = MM_Begin;  // Make sure this all resets next time
2523 
2524             // reset the ui
2525             ui_Reset();
2526 
2527             // Set the next menu to load
2528             result = 1;
2529             break;
2530     }
2531 
2532     return result;
2533 }
2534 
2535 //--------------------------------------------------------------------------------------------
doGameOptions(float deltaTime)2536 int doGameOptions( float deltaTime )
2537 {
2538     /// @details Game options menu
2539 
2540     static int menuState = MM_Begin;
2541     static oglx_texture_t background;
2542     static int menuChoice = 0;
2543 
2544     static STRING Cdifficulty;
2545     static STRING Cmaxmessage;
2546     static const char *sz_buttons[] =
2547     {
2548         "N/A",        // Difficulty
2549         "N/A",        // Max messages
2550         "N/A",        // Message duration
2551         "N/A",        // Autoturn camera
2552         "N/A",        // Show FPS
2553         "N/A",        // Feedback
2554         "Save Settings",
2555         ""
2556     };
2557 
2558     char szDifficulty[4096] = EMPTY_CSTR;
2559     int  result = 0;
2560 
2561     switch ( menuState )
2562     {
2563         case MM_Begin:
2564             // set up menu variables
2565             ego_texture_load_vfs( &background, "mp_data/menu/menu_fairy", TRANSCOLOR );
2566 
2567             menuChoice = 0;
2568             menuState = MM_Entering;
2569 
2570             tipText_set_position( menuFont, "Change game settings here.", 20 );
2571 
2572             mnu_SlidyButton_init( 1.0f, sz_buttons );
2573             // let this fall through into MM_Entering
2574 
2575         case MM_Entering:
2576             // do buttons sliding in animation, and background fading in
2577             // background
2578             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2579 
2580             // Draw the background
2581             if ( mnu_draw_background )
2582             {
2583                 ui_drawImage( 0, &background, ( GFX_WIDTH  / 2 ) + ( background.imgW / 2 ), GFX_HEIGHT - background.imgH, 0, 0, NULL );
2584             }
2585 
2586             // Load the current settings
2587             switch ( cfg.difficulty )
2588             {
2589                 case GAME_HARD: snprintf( Cdifficulty, SDL_arraysize( Cdifficulty ), "Punishing" ); break;
2590                 case GAME_EASY: snprintf( Cdifficulty, SDL_arraysize( Cdifficulty ), "Forgiving" ); break;
2591                 default: cfg.difficulty = GAME_NORMAL; /* fall through */
2592                 case GAME_NORMAL:  snprintf( Cdifficulty, SDL_arraysize( Cdifficulty ), "Challenging" ); break;
2593             }
2594             sz_buttons[0] = Cdifficulty;
2595 
2596             maxmessage = CLIP( maxmessage, 4, MAX_MESSAGE );
2597             if ( 0 == maxmessage )
2598             {
2599                 snprintf( Cmaxmessage, SDL_arraysize( Cmaxmessage ), "None" );          // Set to default
2600             }
2601             else
2602             {
2603                 snprintf( Cmaxmessage, SDL_arraysize( Cmaxmessage ), "%i", maxmessage );
2604             }
2605             sz_buttons[1] = Cmaxmessage;
2606 
2607             // Message duration
2608             if ( cfg.message_duration <= 100 )
2609             {
2610                 sz_buttons[2] = "Short";
2611             }
2612             else if ( cfg.message_duration <= 150 )
2613             {
2614                 sz_buttons[2] = "Normal";
2615             }
2616             else
2617             {
2618                 sz_buttons[2] = "Long";
2619             }
2620 
2621             // Autoturn camera
2622             if ( CAM_TURN_GOOD == cfg.autoturncamera )        sz_buttons[3] = "Fast";
2623             else if ( CAM_TURN_AUTO == cfg.autoturncamera )   sz_buttons[3] = "On";
2624             else
2625             {
2626                 sz_buttons[3] = "Off";
2627                 cfg.autoturncamera = CAM_TURN_NONE;
2628             }
2629 
2630             // Show FPS
2631             sz_buttons[4] = cfg.fps_allowed ? "On" : "Off";
2632 
2633             //Billboard feedback
2634             if ( !cfg.feedback ) sz_buttons[5] = "Disabled";
2635             else
2636             {
2637                 if ( cfg.feedback == FEEDBACK_TEXT ) sz_buttons[5] = "Enabled";
2638                 else                              sz_buttons[5] = "Debug";
2639             }
2640 
2641             // Fall trough
2642             menuState = MM_Running;
2643             //break;
2644 
2645         case MM_Running:
2646             // Do normal run
2647             // Background
2648             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
2649 
2650             if ( mnu_draw_background )
2651             {
2652                 ui_drawImage( 0, &background, ( GFX_WIDTH  / 2 ) - ( background.imgW / 2 ), GFX_HEIGHT - background.imgH, 0, 0, NULL );
2653             }
2654 
2655             ui_drawTextBox( menuFont, "Game Difficulty:", buttonLeft, 50, 0, 0, 20 );
2656 
2657             // Buttons
2658             if ( !PMod->active && BUTTON_UP == ui_doButton( 1, sz_buttons[0], menuFont, buttonLeft + 150, 50, 150, 30 ) )
2659             {
2660                 // Increase difficulty
2661                 cfg.difficulty++;
2662                 switch ( cfg.difficulty )
2663                 {
2664                     case GAME_NORMAL: snprintf( Cdifficulty, SDL_arraysize( Cdifficulty ), "Challenging" ); break;
2665                     case GAME_HARD: snprintf( Cdifficulty, SDL_arraysize( Cdifficulty ), "Punishing" ); break;
2666                 default: case GAME_EASY:
2667                         {
2668                             snprintf( Cdifficulty, SDL_arraysize( Cdifficulty ), "Forgiving" );
2669                             cfg.difficulty = GAME_EASY;
2670                             break;
2671                         }
2672                 }
2673                 sz_buttons[0] = Cdifficulty;
2674             }
2675 
2676             // Now do difficulty description. Currently it's handled very bad, but it works.
2677             switch ( cfg.difficulty )
2678             {
2679                 case GAME_EASY:
2680                     strncpy( szDifficulty, "FORGIVING (Easy)\n - Players gain no bonus XP \n - 15%% XP loss upon death\n - Monsters take 25%% extra damage by players\n - Players take 25%% less damage by monsters\n - Halves the chance for Kursed items\n - Cannot unlock the final level in this mode\n - Life and Mana is refilled after quitting a module", SDL_arraysize( szDifficulty ) );
2681                     break;
2682                 case GAME_NORMAL:
2683                     strncpy( szDifficulty, "CHALLENGING (Normal)\n - Players gain 10%% bonus XP \n - 15%% XP loss upon death \n - 15%% money loss upon death", SDL_arraysize( szDifficulty ) );
2684                     break;
2685                 case GAME_HARD:
2686                     strncpy( szDifficulty, "PUNISHING (Hard)\n - Monsters award 20%% extra xp! \n - 15%% XP loss upon death\n - 15%% money loss upon death\n - No respawning\n - Channeling life can kill you\n - Players take 25%% more damage\n - Doubles the chance for Kursed items", SDL_arraysize( szDifficulty ) );
2687                     break;
2688             }
2689             str_add_linebreaks( szDifficulty, SDL_arraysize( szDifficulty ), 30 );
2690             ui_drawTextBox( menuFont, szDifficulty, buttonLeft, 100, 0, 0, 20 );
2691 
2692             // Text messages
2693             ui_drawTextBox( menuFont, "Max  Messages:", buttonLeft + 350, 50, 0, 0, 20 );
2694             if ( BUTTON_UP == ui_doButton( 2, sz_buttons[1], menuFont, buttonLeft + 515, 50, 75, 30 ) )
2695             {
2696                 cfg.message_count_req++;
2697                 if ( cfg.message_count_req > MAX_MESSAGE ) cfg.message_count_req = 0;
2698                 if ( cfg.message_count_req < 4 && cfg.message_count_req != 0 ) cfg.message_count_req = 4;
2699 
2700                 if ( 0 == cfg.message_count_req )
2701                 {
2702                     snprintf( Cmaxmessage, SDL_arraysize( Cmaxmessage ), "None" );
2703                 }
2704                 else
2705                 {
2706                     snprintf( Cmaxmessage, SDL_arraysize( Cmaxmessage ), "%i", cfg.message_count_req );   // Convert integer to a char we can use
2707                 }
2708 
2709                 sz_buttons[1] = Cmaxmessage;
2710             }
2711 
2712             // Message time
2713             ui_drawTextBox( menuFont, "Message Duration:", buttonLeft + 350, 100, 0, 0, 20 );
2714             if ( BUTTON_UP == ui_doButton( 3, sz_buttons[2], menuFont, buttonLeft + 515, 100, 100, 30 ) )
2715             {
2716                 if ( cfg.message_duration <= 0 )
2717                 {
2718                     cfg.message_duration = 100;
2719                 }
2720                 else
2721                 {
2722                     cfg.message_duration += 50;
2723                 }
2724 
2725                 if ( cfg.message_duration >= 250 )
2726                 {
2727                     cfg.message_duration = 100;
2728                 }
2729 
2730                 if ( cfg.message_duration <= 100 )
2731                 {
2732                     sz_buttons[2] = "Short";
2733                 }
2734                 else if ( cfg.message_duration <= 150 )
2735                 {
2736                     sz_buttons[2] = "Normal";
2737                 }
2738                 else
2739                 {
2740                     sz_buttons[2] = "Long";
2741                 }
2742             }
2743 
2744             // Autoturn camera
2745             ui_drawTextBox( menuFont, "Autoturn Camera:", buttonLeft + 350, 150, 0, 0, 20 );
2746             if ( BUTTON_UP == ui_doButton( 4, sz_buttons[3], menuFont, buttonLeft + 515, 150, 100, 30 ) )
2747             {
2748                 if ( CAM_TURN_GOOD == cfg.autoturncamera )
2749                 {
2750                     sz_buttons[3] = "Off";
2751                     cfg.autoturncamera = CAM_TURN_NONE;
2752                 }
2753                 else if ( cfg.autoturncamera )
2754                 {
2755                     sz_buttons[3] = "Fast";
2756                     cfg.autoturncamera = CAM_TURN_GOOD;
2757                 }
2758                 else
2759                 {
2760                     sz_buttons[3] = "On";
2761                     cfg.autoturncamera = CAM_TURN_AUTO;
2762                 }
2763             }
2764 
2765             // Show the fps?
2766             ui_drawTextBox( menuFont, "Display FPS:", buttonLeft + 350, 200, 0, 0, 20 );
2767             if ( BUTTON_UP == ui_doButton( 5, sz_buttons[4], menuFont, buttonLeft + 515, 200, 100, 30 ) )
2768             {
2769                 cfg.fps_allowed = !cfg.fps_allowed;
2770                 if ( cfg.fps_allowed )   sz_buttons[4] = "On";
2771                 else                     sz_buttons[4] = "Off";
2772             }
2773 
2774             // Feedback
2775             ui_drawTextBox( menuFont, "Floating Text:", buttonLeft + 350, 250, 0, 0, 20 );
2776             if ( BUTTON_UP == ui_doButton( 6, sz_buttons[5], menuFont, buttonLeft + 515, 250, 75, 30 ) )
2777             {
2778                 if ( cfg.dev_mode )
2779                 {
2780                     cfg.feedback = ( FEEDBACK_TYPE )( cfg.feedback + 1 );
2781                     if ( cfg.feedback > FEEDBACK_NUMBER ) cfg.feedback = FEEDBACK_OFF;
2782                 }
2783                 else
2784                 {
2785                     if ( FEEDBACK_OFF == cfg.feedback )
2786                     {
2787                         cfg.feedback = FEEDBACK_TEXT;
2788                     }
2789                     else
2790                     {
2791                         cfg.feedback = FEEDBACK_OFF;
2792                     }
2793                 }
2794 
2795                 switch ( cfg.feedback )
2796                 {
2797                     case FEEDBACK_OFF:    sz_buttons[5] = "Disabled"; break;
2798                     case FEEDBACK_TEXT:   sz_buttons[5] = "Enabled";  break;
2799                     case FEEDBACK_NUMBER: sz_buttons[5] = "Debug";    break;
2800                 }
2801             }
2802 
2803             // Save settings
2804             if ( BUTTON_UP == ui_doButton( 7, sz_buttons[6], menuFont, buttonLeft, GFX_HEIGHT - 60, 200, 30 ) )
2805             {
2806                 // synchronoze the config values with the various game subsystems
2807                 setup_synch( &cfg );
2808 
2809                 // save the setup file
2810                 setup_upload( &cfg );
2811                 setup_write();
2812 
2813                 menuState = MM_Leaving;
2814             }
2815 
2816             // tool-tip text
2817             ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
2818             break;
2819 
2820         case MM_Leaving:
2821             // Do buttons sliding out and background fading
2822             // Do the same stuff as in MM_Entering, but backwards
2823             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2824 
2825             if ( mnu_draw_background )
2826             {
2827                 ui_drawImage( 0, &background, ( GFX_WIDTH  / 2 ) + ( background.imgW / 2 ), GFX_HEIGHT - background.imgH, 0, 0, NULL );
2828             }
2829 
2830             // Fall trough
2831             menuState = MM_Finish;
2832             break;
2833 
2834         case MM_Finish:
2835             // Free the background texture; don't need to hold onto it
2836             oglx_texture_Release( &background );
2837             menuState = MM_Begin;  // Make sure this all resets next time
2838 
2839             // reset the ui
2840             ui_Reset();
2841 
2842             // Set the next menu to load
2843             result = 1;
2844             break;
2845     }
2846 
2847     return result;
2848 }
2849 
2850 //--------------------------------------------------------------------------------------------
doAudioOptions(float deltaTime)2851 int doAudioOptions( float deltaTime )
2852 {
2853     /// @details Audio options menu
2854 
2855     static int menuState = MM_Begin;
2856     static oglx_texture_t background;
2857     static int menuChoice = 0;
2858     static STRING Cmaxsoundchannel;
2859     static STRING Cbuffersize;
2860     static STRING Csoundvolume;
2861     static STRING Cmusicvolume;
2862     static const char *sz_buttons[] =
2863     {
2864         "N/A",        // Enable sound
2865         "N/A",        // Sound volume
2866         "N/A",        // Enable music
2867         "N/A",        // Music volume
2868         "N/A",        // Sound channels
2869         "N/A",        // Sound buffer
2870         "N/A",        // Sound quality
2871         "N/A",        // Play footsteps
2872         "Save Settings",
2873         ""
2874     };
2875 
2876     int result = 0;
2877 
2878     switch ( menuState )
2879     {
2880         case MM_Begin:
2881             // set up menu variables
2882             ego_texture_load_vfs( &background, "mp_data/menu/menu_sound", TRANSCOLOR );
2883 
2884             menuChoice = 0;
2885             menuState = MM_Entering;
2886 
2887             tipText_set_position( menuFont, "Change audio settings here.", 20 );
2888 
2889             mnu_SlidyButton_init( 1.0f, sz_buttons );
2890             // let this fall through into MM_Entering
2891 
2892         case MM_Entering:
2893             // do buttons sliding in animation, and background fading in
2894             // background
2895             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
2896 
2897             // Draw the background
2898             if ( mnu_draw_background )
2899             {
2900                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
2901             }
2902 
2903             // Load the current settings
2904             sz_buttons[0] = cfg.sound_allowed ? "On" : "Off";
2905 
2906             snprintf( Csoundvolume, SDL_arraysize( Csoundvolume ), "%i", cfg.sound_volume );
2907             sz_buttons[1] = Csoundvolume;
2908 
2909             sz_buttons[2] = cfg.music_allowed ? "On" : "Off";
2910 
2911             snprintf( Cmusicvolume, SDL_arraysize( Cmusicvolume ), "%i", cfg.music_volume );
2912             sz_buttons[3] = Cmusicvolume;
2913 
2914             snprintf( Cmaxsoundchannel, SDL_arraysize( Cmaxsoundchannel ), "%i", cfg.sound_channel_count );
2915             sz_buttons[4] = Cmaxsoundchannel;
2916 
2917             snprintf( Cbuffersize, SDL_arraysize( Cbuffersize ), "%i", cfg.sound_buffer_size );
2918             sz_buttons[5] = Cbuffersize;
2919 
2920             sz_buttons[6] = cfg.sound_highquality ?  "Normal" : "High";
2921 
2922             sz_buttons[7] = cfg.sound_footfall ? "Enabled" : "Disabled";
2923 
2924             // Fall trough
2925             menuState = MM_Running;
2926             break;
2927 
2928         case MM_Running:
2929             // Do normal run
2930             // Background
2931             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
2932 
2933             if ( mnu_draw_background )
2934             {
2935                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
2936             }
2937 
2938             ui_drawTextBox( menuFont, "Sound:", buttonLeft, GFX_HEIGHT - 270, 0, 0, 20 );
2939 
2940             // Buttons
2941             if ( BUTTON_UP == ui_doButton( 1, sz_buttons[0], menuFont, buttonLeft + 150, GFX_HEIGHT - 270, 100, 30 ) )
2942             {
2943                 cfg.sound_allowed = !cfg.sound_allowed;
2944                 sz_buttons[0] = cfg.sound_allowed ? "On" : "Off";
2945             }
2946 
2947             ui_drawTextBox( menuFont, "Sound Volume:", buttonLeft, GFX_HEIGHT - 235, 0, 0, 20 );
2948             if ( BUTTON_UP == ui_doButton( 2, sz_buttons[1], menuFont, buttonLeft + 150, GFX_HEIGHT - 235, 100, 30 ) )
2949             {
2950                 cfg.sound_volume += 5;
2951                 if ( cfg.sound_volume > 100 ) cfg.sound_volume = 0;
2952 
2953                 snprintf( Csoundvolume, SDL_arraysize( Csoundvolume ), "%i", cfg.sound_volume );
2954                 sz_buttons[1] = Csoundvolume;
2955             }
2956 
2957             ui_drawTextBox( menuFont, "Music:", buttonLeft, GFX_HEIGHT - 165, 0, 0, 20 );
2958             if ( BUTTON_UP == ui_doButton( 3, sz_buttons[2], menuFont, buttonLeft + 150, GFX_HEIGHT - 165, 100, 30 ) )
2959             {
2960                 cfg.music_allowed = !cfg.music_allowed;
2961                 sz_buttons[2] = cfg.music_allowed ? "On" : "Off";
2962             }
2963 
2964             ui_drawTextBox( menuFont, "Music Volume:", buttonLeft, GFX_HEIGHT - 130, 0, 0, 20 );
2965             if ( BUTTON_UP == ui_doButton( 4, sz_buttons[3], menuFont, buttonLeft + 150, GFX_HEIGHT - 130, 100, 30 ) )
2966             {
2967                 cfg.music_volume += 5;
2968                 if ( cfg.music_volume > 100 ) cfg.music_volume = 0;
2969 
2970                 snprintf( Cmusicvolume, SDL_arraysize( Cmusicvolume ), "%i", cfg.music_volume );
2971                 sz_buttons[3] = Cmusicvolume;
2972             }
2973 
2974             ui_drawTextBox( menuFont, "Sound Channels:", buttonLeft + 300, GFX_HEIGHT - 200, 0, 0, 20 );
2975             if ( BUTTON_UP == ui_doButton( 5, sz_buttons[4], menuFont, buttonLeft + 450, GFX_HEIGHT - 200, 100, 30 ) )
2976             {
2977                 if ( cfg.sound_channel_count < 8 )
2978                 {
2979                     cfg.sound_channel_count = 8;
2980                 }
2981                 else
2982                 {
2983                     cfg.sound_channel_count <<= 1;
2984                 }
2985 
2986                 if ( cfg.sound_channel_count > 128 )
2987                 {
2988                     cfg.sound_channel_count = 8;
2989                 }
2990 
2991                 snprintf( Cmaxsoundchannel, SDL_arraysize( Cmaxsoundchannel ), "%i", cfg.sound_channel_count );
2992                 sz_buttons[4] = Cmaxsoundchannel;
2993             }
2994 
2995             ui_drawTextBox( menuFont, "Buffer Size:", buttonLeft + 300, GFX_HEIGHT - 165, 0, 0, 20 );
2996             if ( BUTTON_UP == ui_doButton( 6, sz_buttons[5], menuFont, buttonLeft + 450, GFX_HEIGHT - 165, 100, 30 ) )
2997             {
2998                 if ( cfg.sound_buffer_size < 512 )
2999                 {
3000                     cfg.sound_buffer_size = 512;
3001                 }
3002                 else
3003                 {
3004                     cfg.sound_buffer_size <<= 1;
3005                 }
3006 
3007                 if ( cfg.sound_buffer_size > 8196 )
3008                 {
3009                     cfg.sound_buffer_size = 512;
3010                 }
3011 
3012                 snprintf( Cbuffersize, SDL_arraysize( Cbuffersize ), "%i", cfg.sound_buffer_size );
3013                 sz_buttons[5] = Cbuffersize;
3014             }
3015 
3016             ui_drawTextBox( menuFont, "Sound Quality:", buttonLeft + 300, GFX_HEIGHT - 130, 0, 0, 20 );
3017             if ( BUTTON_UP == ui_doButton( 7, sz_buttons[6], menuFont, buttonLeft + 450, GFX_HEIGHT - 130, 100, 30 ) )
3018             {
3019                 cfg.sound_highquality = !cfg.sound_highquality;
3020                 sz_buttons[6] = cfg.sound_highquality ? "Normal" : "High";
3021             }
3022 
3023             //Footfall sounds
3024             ui_drawTextBox( menuFont, "Footstep Sounds:", buttonLeft + 300, GFX_HEIGHT - 235, 0, 0, 20 );
3025             if ( BUTTON_UP == ui_doButton( 8, sz_buttons[7], menuFont, buttonLeft + 450, GFX_HEIGHT - 235, 100, 30 ) )
3026             {
3027                 cfg.sound_footfall = !cfg.sound_footfall;
3028                 sz_buttons[7] = cfg.sound_footfall ? "Enabled" : "Disabled";
3029             }
3030 
3031             //Save settings
3032             if ( BUTTON_UP == ui_doButton( 9, sz_buttons[8], menuFont, buttonLeft, GFX_HEIGHT - 60, 200, 30 ) )
3033             {
3034                 // synchronoze the config values with the various game subsystems
3035                 setup_synch( &cfg );
3036 
3037                 // save the setup file
3038                 setup_upload( &cfg );
3039                 setup_write();
3040 
3041                 // Reload the sound system
3042                 sound_restart();
3043 
3044                 // Do we restart the music?
3045                 if ( cfg.music_allowed )
3046                 {
3047                     load_all_music_sounds_vfs();
3048                     fade_in_music( musictracksloaded[songplaying] );
3049                 }
3050 
3051                 menuState = MM_Leaving;
3052             }
3053 
3054             // tool-tip text
3055             ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
3056 
3057             break;
3058 
3059         case MM_Leaving:
3060             // Do buttons sliding out and background fading
3061             // Do the same stuff as in MM_Entering, but backwards
3062             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
3063 
3064             if ( mnu_draw_background )
3065             {
3066                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
3067             }
3068 
3069             // Fall trough
3070             menuState = MM_Finish;
3071             break;
3072 
3073         case MM_Finish:
3074             // Free the background texture; don't need to hold onto it
3075             oglx_texture_Release( &background );
3076             menuState = MM_Begin;  // Make sure this all resets next time
3077 
3078             // reset the ui
3079             ui_Reset();
3080 
3081             // Set the next menu to load
3082             result = 1;
3083             break;
3084     }
3085 
3086     return result;
3087 }
3088 
3089 //--------------------------------------------------------------------------------------------
doVideoOptions_coerce_aspect_ratio(int width,int height,float * pratio,STRING * psz_ratio)3090 bool_t doVideoOptions_coerce_aspect_ratio( int width, int height, float * pratio, STRING * psz_ratio )
3091 {
3092     /// @details BB@> coerce the aspect ratio of the screen to some standard size
3093 
3094     float req_aspect_ratio;
3095 
3096     if ( 0 == height || NULL == pratio || NULL == psz_ratio ) return bfalse;
3097 
3098     req_aspect_ratio = ( float )width / ( float )height;
3099 
3100     if ( req_aspect_ratio > 0.0f && req_aspect_ratio < 0.5f*(( 5.0f / 4.0f ) + ( 4.0f / 3.0f ) ) )
3101     {
3102         *pratio = 5.0f / 4.0f;
3103         strncpy( *psz_ratio, "5:4", sizeof( *psz_ratio ) );
3104     }
3105     else if ( req_aspect_ratio >= 0.5f*(( 5.0f / 4.0f ) + ( 4.0f / 3.0f ) ) && req_aspect_ratio < 0.5f*(( 4.0f / 3.0f ) + ( 8.0f / 5.0f ) ) )
3106     {
3107         *pratio = 4.0f / 3.0f;
3108         strncpy( *psz_ratio, "4:3", sizeof( *psz_ratio ) );
3109     }
3110     else if ( req_aspect_ratio >= 0.5f*(( 4.0f / 3.0f ) + ( 8.0f / 5.0f ) ) && req_aspect_ratio < 0.5f*(( 8.0f / 5.0f ) + ( 5.0f / 3.0f ) ) )
3111     {
3112         *pratio = 8.0f / 5.0f;
3113         strncpy( *psz_ratio, "8:5", sizeof( *psz_ratio ) );
3114     }
3115     else if ( req_aspect_ratio >= 0.5f*(( 8.0f / 5.0f ) + ( 5.0f / 3.0f ) ) && req_aspect_ratio < 0.5f*(( 5.0f / 3.0f ) + ( 16.0f / 9.0f ) ) )
3116     {
3117         *pratio = 5.0f / 3.0f;
3118         strncpy( *psz_ratio, "5:3", sizeof( *psz_ratio ) );
3119     }
3120     else
3121     {
3122         *pratio = 16.0f / 9.0f;
3123         strncpy( *psz_ratio, "16:9", sizeof( *psz_ratio ) );
3124     }
3125 
3126     return btrue;
3127 
3128 }
3129 
3130 //--------------------------------------------------------------------------------------------
doVideoOptions_fix_fullscreen_resolution(egoboo_config_t * pcfg,SDLX_screen_info_t * psdl_scr,STRING * psz_screen_size)3131 int doVideoOptions_fix_fullscreen_resolution( egoboo_config_t * pcfg, SDLX_screen_info_t * psdl_scr, STRING * psz_screen_size )
3132 {
3133     STRING     sz_aspect_ratio = "unknown";
3134     float      req_screen_area  = ( float )pcfg->scrx_req * ( float )pcfg->scry_req;
3135     float      min_diff = 0.0f;
3136     SDL_Rect * found_rect = NULL, ** pprect = NULL;
3137 
3138     float       aspect_ratio;
3139 
3140     doVideoOptions_coerce_aspect_ratio( pcfg->scrx_req, pcfg->scry_req, &aspect_ratio, &sz_aspect_ratio );
3141 
3142     found_rect = NULL;
3143     pprect = psdl_scr->video_mode_list;
3144     while ( NULL != *pprect )
3145     {
3146         SDL_Rect * prect = *pprect;
3147 
3148         float sdl_aspect_ratio;
3149         float sdl_screen_area;
3150         float diff, diff1, diff2;
3151 
3152         sdl_aspect_ratio = ( float )prect->w / ( float )prect->h;
3153         sdl_screen_area  = prect->w * prect->h;
3154 
3155         diff1 = LOG( sdl_aspect_ratio / aspect_ratio );
3156         diff2 = LOG( sdl_screen_area / req_screen_area );
3157 
3158         diff = 2.0f * ABS( diff1 ) + ABS( diff2 );
3159 
3160         if ( NULL == found_rect || diff < min_diff )
3161         {
3162             found_rect = prect;
3163             min_diff   = diff;
3164 
3165             if ( 0.0f == min_diff ) break;
3166         }
3167 
3168         pprect++;
3169     }
3170 
3171     if ( NULL != found_rect )
3172     {
3173         pcfg->scrx_req = found_rect->w;
3174         pcfg->scry_req = found_rect->h;
3175     }
3176     else
3177     {
3178         // we cannot find an approximate screen size
3179 
3180         switch ( pcfg->scrx_req )
3181         {
3182                 // Normal resolutions
3183             case 1024:
3184                 pcfg->scry_req  = 768;
3185                 strncpy( sz_aspect_ratio, "4:3", sizeof( sz_aspect_ratio ) );
3186                 break;
3187 
3188             case 640:
3189                 pcfg->scry_req = 480;
3190                 strncpy( sz_aspect_ratio, "4:3", sizeof( sz_aspect_ratio ) );
3191                 break;
3192 
3193             case 800:
3194                 pcfg->scry_req = 600;
3195                 strncpy( sz_aspect_ratio, "4:3", sizeof( sz_aspect_ratio ) );
3196                 break;
3197 
3198                 // 1280 can be both widescreen and normal
3199             case 1280:
3200                 if ( pcfg->scry_req > 800 )
3201                 {
3202                     pcfg->scry_req = 1024;
3203                     strncpy( sz_aspect_ratio, "5:4", sizeof( sz_aspect_ratio ) );
3204                 }
3205                 else
3206                 {
3207                     pcfg->scry_req = 800;
3208                     strncpy( sz_aspect_ratio, "8:5", sizeof( sz_aspect_ratio ) );
3209                 }
3210                 break;
3211 
3212                 // Widescreen resolutions
3213             case 1440:
3214                 pcfg->scry_req = 900;
3215                 strncpy( sz_aspect_ratio, "8:5", sizeof( sz_aspect_ratio ) );
3216                 break;
3217 
3218             case 1680:
3219                 pcfg->scry_req = 1050;
3220                 strncpy( sz_aspect_ratio, "8:5", sizeof( sz_aspect_ratio ) );
3221                 break;
3222 
3223             case 1920:
3224                 pcfg->scry_req = 1200;
3225                 strncpy( sz_aspect_ratio, "8:5", sizeof( sz_aspect_ratio ) );
3226                 break;
3227 
3228                 // unknown
3229             default:
3230                 doVideoOptions_coerce_aspect_ratio( pcfg->scrx_req, pcfg->scry_req, &aspect_ratio, &sz_aspect_ratio );
3231                 break;
3232         }
3233     }
3234 
3235     snprintf( *psz_screen_size, sizeof( *psz_screen_size ), "%dx%d - %s", pcfg->scrx_req, pcfg->scry_req, sz_aspect_ratio );
3236 
3237     return btrue;
3238 }
3239 
3240 //--------------------------------------------------------------------------------------------
doVideoOptions(float deltaTime)3241 int doVideoOptions( float deltaTime )
3242 {
3243     /// @details Video options menu
3244 
3245     enum
3246     {
3247         but_antialiasing =  0,  // Antialaising
3248         but_unused           ,    // Unused button
3249         but_dither           ,    // Fast & ugly
3250         but_fullscreen       ,    // Fullscreen
3251         but_reflections      ,    // Reflections
3252         but_filtering        ,    // Texture filtering
3253         but_shadow           ,    // Shadows
3254         but_zbuffer          ,    // Z bit
3255         but_maxlights        ,    // Fog
3256         but_3dfx             ,    // 3D effects
3257         but_multiwater       ,    // Multi water layer
3258         but_widescreen       ,    // Widescreen
3259         but_screensize       ,    // Screen resolution
3260         but_save             ,
3261         but_maxparticles      ,  // Max particles
3262         but_end,
3263 
3264         but_last
3265     };
3266 
3267     static int menuState = MM_Begin;
3268     static oglx_texture_t background;
3269     static int    menuChoice = 0;
3270     static STRING Cantialiasing;
3271     static STRING Cmaxlights;
3272     static STRING Cscrz;
3273     static STRING Cmaxparticles;
3274     static STRING Cmaxdyna;
3275 
3276     static bool_t widescreen;
3277     static float  aspect_ratio;
3278     static STRING sz_screen_size;
3279 
3280     static const char *sz_buttons[but_last];
3281 
3282     int cnt, result = 0;
3283 
3284     switch ( menuState )
3285     {
3286         case MM_Begin:
3287 
3288             // set up the button text
3289             for ( cnt = 0; cnt < but_last; cnt++ ) sz_buttons[cnt] = "N/A";
3290             sz_buttons[but_end] = "";
3291 
3292             // set up menu variables
3293             ego_texture_load_vfs( &background, "mp_data/menu/menu_video", TRANSCOLOR );
3294 
3295             menuChoice = 0;
3296             menuState = MM_Entering;
3297 
3298             tipText_set_position( menuFont, "Change video settings here.", 20 );
3299 
3300             mnu_SlidyButton_init( 1.0f, sz_buttons );
3301             // let this fall through into MM_Entering
3302 
3303         case MM_Entering:
3304             // do buttons sliding in animation, and background fading in
3305             // background
3306             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
3307 
3308             // Draw the background
3309             if ( mnu_draw_background )
3310             {
3311                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
3312             }
3313 
3314             // Load all the current video settings
3315             if ( 0 == cfg.multisamples ) strncpy( Cantialiasing , "Off", SDL_arraysize( Cantialiasing ) );
3316             else snprintf( Cantialiasing, SDL_arraysize( Cantialiasing ), "X%i", cfg.multisamples );
3317             sz_buttons[but_antialiasing] = Cantialiasing;
3318 
3319             // Texture filtering
3320             switch ( cfg.texturefilter_req )
3321             {
3322                 case TX_UNFILTERED:
3323                     sz_buttons[but_filtering] = "Unfiltered";
3324                     break;
3325                 case TX_LINEAR:
3326                     sz_buttons[but_filtering] = "Linear";
3327                     break;
3328                 case TX_MIPMAP:
3329                     sz_buttons[but_filtering] = "Mipmap";
3330                     break;
3331                 case TX_BILINEAR:
3332                     sz_buttons[but_filtering] = "Bilinear";
3333                     break;
3334                 case TX_TRILINEAR_1:
3335                     sz_buttons[but_filtering] = "Trilinear 1";
3336                     break;
3337                 case TX_TRILINEAR_2:
3338                     sz_buttons[but_filtering] = "Trilinear 2";
3339                     break;
3340                 case TX_ANISOTROPIC:
3341                     sz_buttons[but_filtering] = "Ansiotropic";
3342                     break;
3343                 default:                  // Set to defaults
3344                     sz_buttons[but_filtering] = "Linear";
3345                     cfg.texturefilter_req = TX_LINEAR;
3346                     break;
3347             }
3348 
3349             sz_buttons[but_dither] = cfg.use_dither ? "Yes" : "No";
3350 
3351             sz_buttons[but_fullscreen] = cfg.fullscreen_req ? "True" : "False";
3352 
3353             if ( cfg.reflect_allowed )
3354             {
3355                 sz_buttons[but_reflections] = "Low";
3356                 if ( cfg.reflect_prt )
3357                 {
3358                     sz_buttons[but_reflections] = "Medium";
3359                     if ( 0 == cfg.reflect_fade )
3360                     {
3361                         sz_buttons[but_reflections] = "High";
3362                     }
3363                 }
3364             }
3365             else
3366             {
3367                 sz_buttons[but_reflections] = "Off";
3368             }
3369 
3370             if ( cfg.shadow_allowed )
3371             {
3372                 sz_buttons[but_shadow] = "Normal";
3373                 if ( !cfg.shadow_sprite )
3374                 {
3375                     sz_buttons[but_shadow] = "Best";
3376                 }
3377             }
3378             else sz_buttons[but_shadow] = "Off";
3379 
3380 #if defined(__unix__)
3381             //Clip linux defaults to valid values so that the game doesn't crash on startup
3382             if ( cfg.scrz_req == 32 ) cfg.scrz_req = 24;
3383             if ( cfg.scrd_req == 32 ) cfg.scrd_req = 24;
3384 #endif
3385 
3386             if ( cfg.scrz_req != 32 && cfg.scrz_req != 16 && cfg.scrz_req != 24 )
3387             {
3388                 cfg.scrz_req = 16;              // Set to default
3389             }
3390             snprintf( Cscrz, SDL_arraysize( Cscrz ), "%i", cfg.scrz_req );     // Convert the integer to a char we can use
3391             sz_buttons[but_zbuffer] = Cscrz;
3392 
3393             snprintf( Cmaxlights, SDL_arraysize( Cmaxlights ), "%i", cfg.dyna_count_req );
3394             sz_buttons[but_maxlights] = Cmaxlights;
3395 
3396             if ( cfg.use_phong )
3397             {
3398                 sz_buttons[but_3dfx] = "Okay";
3399                 if ( cfg.overlay_allowed && cfg.background_allowed )
3400                 {
3401                     sz_buttons[but_3dfx] = "Good";
3402                     if ( cfg.use_perspective )
3403                     {
3404                         sz_buttons[but_3dfx] = "Superb";
3405                     }
3406                 }
3407                 else                            // Set to defaults
3408                 {
3409                     cfg.use_perspective    = bfalse;
3410                     cfg.background_allowed = bfalse;
3411                     cfg.overlay_allowed    = bfalse;
3412                     sz_buttons[but_3dfx] = "Off";
3413                 }
3414             }
3415             else                              // Set to defaults
3416             {
3417                 cfg.use_perspective    = bfalse;
3418                 cfg.background_allowed = bfalse;
3419                 cfg.overlay_allowed    = bfalse;
3420                 sz_buttons[but_3dfx] = "Off";
3421             }
3422 
3423             if ( cfg.twolayerwater_allowed ) sz_buttons[but_multiwater] = "On";
3424             else sz_buttons[but_multiwater] = "Off";
3425 
3426             snprintf( Cmaxparticles, SDL_arraysize( Cmaxparticles ), "%i", cfg.particle_count_req );     // Convert the integer to a char we can use
3427             sz_buttons[but_maxparticles] = Cmaxparticles;
3428 
3429             if ( cfg.fullscreen_req && NULL != sdl_scr.video_mode_list )
3430             {
3431                 doVideoOptions_fix_fullscreen_resolution( &cfg, &sdl_scr, &sz_screen_size );
3432                 sz_buttons[but_screensize] = sz_screen_size;
3433 
3434                 aspect_ratio = ( float )cfg.scrx_req / ( float )cfg.scry_req;
3435                 widescreen = ( aspect_ratio > ( 4.0f / 3.0f ) );
3436             }
3437             else
3438             {
3439                 snprintf( sz_screen_size, sizeof( sz_screen_size ), "%dx%d", cfg.scrx_req, cfg.scry_req );
3440                 sz_buttons[but_screensize] = sz_screen_size;
3441 
3442                 aspect_ratio = ( float )cfg.scrx_req / ( float )cfg.scry_req;
3443                 widescreen = ( aspect_ratio > ( 4.0f / 3.0f ) );
3444             }
3445 
3446             if ( widescreen ) sz_buttons[but_widescreen] = "X";
3447             else             sz_buttons[but_widescreen] = " ";
3448 
3449             menuState = MM_Running;
3450             break;
3451 
3452         case MM_Running:
3453             // Do normal run
3454             // Background
3455             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
3456 
3457             if ( mnu_draw_background )
3458             {
3459                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
3460             }
3461 
3462             // Antialiasing Button
3463             ui_drawTextBox( menuFont, "Antialiasing:", buttonLeft, GFX_HEIGHT - 215, 0, 0, 20 );
3464             if ( BUTTON_UP == ui_doButton( 1, sz_buttons[but_antialiasing], menuFont, buttonLeft + 150, GFX_HEIGHT - 215, 100, 30 ) )
3465             {
3466                 // make the multi-sampling even
3467 
3468                 if ( cfg.multisamples < 0 )
3469                 {
3470                     cfg.multisamples = 0;
3471                 }
3472                 else
3473                 {
3474                     cfg.multisamples += 1;
3475                 }
3476 
3477                 // set some arbitrary limit
3478                 if ( cfg.multisamples > 4 ) cfg.multisamples = 0;
3479 
3480                 if ( 0 == cfg.multisamples ) strncpy( Cantialiasing , "Off", SDL_arraysize( Cantialiasing ) );
3481                 else snprintf( Cantialiasing, SDL_arraysize( Cantialiasing ), "X%i", cfg.multisamples );
3482 
3483                 sz_buttons[but_antialiasing] = Cantialiasing;
3484             }
3485 
3486             // Dithering
3487             ui_drawTextBox( menuFont, "Dithering:", buttonLeft, GFX_HEIGHT - 145, 0, 0, 20 );
3488             if ( BUTTON_UP == ui_doButton( 3, sz_buttons[but_dither], menuFont, buttonLeft + 150, GFX_HEIGHT - 145, 100, 30 ) )
3489             {
3490                 cfg.use_dither = !cfg.use_dither;
3491                 sz_buttons[but_dither] = cfg.use_dither ? "Yes" : "No";
3492             }
3493 
3494             // Fullscreen
3495             ui_drawTextBox( menuFont, "Fullscreen:", buttonLeft, GFX_HEIGHT - 110, 0, 0, 20 );
3496             if ( BUTTON_UP == ui_doButton( 4, sz_buttons[but_fullscreen], menuFont, buttonLeft + 150, GFX_HEIGHT - 110, 100, 30 ) )
3497             {
3498                 cfg.fullscreen_req = !cfg.fullscreen_req;
3499 
3500                 sz_buttons[but_fullscreen] = cfg.fullscreen_req ? "True" : "False";
3501             }
3502 
3503             // Reflection
3504             ui_drawTextBox( menuFont, "Reflections:", buttonLeft, GFX_HEIGHT - 250, 0, 0, 20 );
3505             if ( BUTTON_UP == ui_doButton( 5, sz_buttons[but_reflections], menuFont, buttonLeft + 150, GFX_HEIGHT - 250, 100, 30 ) )
3506             {
3507 
3508                 if ( cfg.reflect_allowed && 0 == cfg.reflect_fade && cfg.reflect_prt )
3509                 {
3510                     cfg.reflect_allowed = bfalse;
3511                     cfg.reflect_fade = 255;
3512                     cfg.reflect_prt = bfalse;
3513                     sz_buttons[but_reflections] = "Off";
3514                 }
3515                 else
3516                 {
3517                     if ( cfg.reflect_allowed && !cfg.reflect_prt )
3518                     {
3519                         sz_buttons[but_reflections] = "Medium";
3520                         cfg.reflect_fade = 255;
3521                         cfg.reflect_prt = btrue;
3522                     }
3523                     else
3524                     {
3525                         if ( cfg.reflect_allowed && cfg.reflect_fade == 255 && cfg.reflect_prt )
3526                         {
3527                             sz_buttons[but_reflections] = "High";
3528                             cfg.reflect_fade = 0;
3529                         }
3530                         else
3531                         {
3532                             cfg.reflect_allowed = btrue;
3533                             cfg.reflect_fade = 255;
3534                             sz_buttons[but_reflections] = "Low";
3535                             cfg.reflect_prt = bfalse;
3536                         }
3537                     }
3538                 }
3539             }
3540 
3541             // Texture Filtering
3542             ui_drawTextBox( menuFont, "Texture Filtering:", buttonLeft, GFX_HEIGHT - 285, 0, 0, 20 );
3543             if ( BUTTON_UP == ui_doButton( 6, sz_buttons[but_filtering], menuFont, buttonLeft + 150, GFX_HEIGHT - 285, 130, 30 ) )
3544             {
3545                 if ( cfg.texturefilter_req < TX_UNFILTERED )
3546                 {
3547                     cfg.texturefilter_req = TX_UNFILTERED;
3548                 }
3549                 else
3550                 {
3551                     cfg.texturefilter_req = ( TX_FILTERS )(( int )cfg.texturefilter_req + 1 );
3552                 }
3553 
3554                 if ( cfg.texturefilter_req > TX_ANISOTROPIC )
3555                 {
3556                     cfg.texturefilter_req = TX_UNFILTERED;
3557                 }
3558 
3559                 switch ( cfg.texturefilter_req )
3560                 {
3561 
3562                     case TX_UNFILTERED:
3563                         sz_buttons[but_filtering] = "Unfiltered";
3564                         break;
3565 
3566                     case TX_LINEAR:
3567                         sz_buttons[but_filtering] = "Linear";
3568                         break;
3569 
3570                     case TX_MIPMAP:
3571                         sz_buttons[but_filtering] = "Mipmap";
3572                         break;
3573 
3574                     case TX_BILINEAR:
3575                         sz_buttons[but_filtering] = "Bilinear";
3576                         break;
3577 
3578                     case TX_TRILINEAR_1:
3579                         sz_buttons[but_filtering] = "Trilinear 1";
3580                         break;
3581 
3582                     case TX_TRILINEAR_2:
3583                         sz_buttons[but_filtering] = "Trilinear 2";
3584                         break;
3585 
3586                     case TX_ANISOTROPIC:
3587                         sz_buttons[but_filtering] = "Anisotropic";
3588                         break;
3589 
3590                     default:
3591                         cfg.texturefilter_req = TX_UNFILTERED;
3592                         sz_buttons[but_filtering] = "Unfiltered";
3593                         break;
3594                 }
3595             }
3596 
3597             // Shadows
3598             ui_drawTextBox( menuFont, "Shadows:", buttonLeft, GFX_HEIGHT - 320, 0, 0, 20 );
3599             if ( BUTTON_UP == ui_doButton( 7, sz_buttons[but_shadow], menuFont, buttonLeft + 150, GFX_HEIGHT - 320, 100, 30 ) )
3600             {
3601                 if ( cfg.shadow_allowed && !cfg.shadow_sprite )
3602                 {
3603                     cfg.shadow_allowed = bfalse;
3604                     cfg.shadow_sprite = bfalse;                // Just in case
3605                     sz_buttons[but_shadow] = "Off";
3606                 }
3607                 else
3608                 {
3609                     if ( cfg.shadow_allowed && cfg.shadow_sprite )
3610                     {
3611                         sz_buttons[but_shadow] = "Best";
3612                         cfg.shadow_sprite = bfalse;
3613                     }
3614                     else
3615                     {
3616                         cfg.shadow_allowed = btrue;
3617                         cfg.shadow_sprite = btrue;
3618                         sz_buttons[but_shadow] = "Normal";
3619                     }
3620                 }
3621             }
3622 
3623             // Z bit
3624             ui_drawTextBox( menuFont, "Z Bit:", buttonLeft + 300, GFX_HEIGHT - 320, 0, 0, 20 );
3625             if ( BUTTON_UP == ui_doButton( 8, sz_buttons[but_zbuffer], menuFont, buttonLeft + 450, GFX_HEIGHT - 320, 100, 30 ) )
3626             {
3627                 if ( cfg.scrz_req < 0 )
3628                 {
3629                     cfg.scrz_req = 8;
3630                 }
3631                 else
3632                 {
3633                     cfg.scrz_req += 8;
3634                 }
3635 
3636 #if defined(__unix__)
3637                 if ( cfg.scrz_req > 24 ) cfg.scrz_req = 8;          //Linux max is 24
3638 #else
3639                 if ( cfg.scrz_req > 32 ) cfg.scrz_req = 8;          //Others can have up to 32 bit!
3640 #endif
3641 
3642                 snprintf( Cscrz, SDL_arraysize( Cscrz ), "%d", cfg.scrz_req );
3643                 sz_buttons[but_zbuffer] = Cscrz;
3644             }
3645 
3646             // Max dynamic lights
3647             ui_drawTextBox( menuFont, "Max Lights:", buttonLeft + 300, GFX_HEIGHT - 285, 0, 0, 20 );
3648             if ( BUTTON_UP == ui_doButton( 9, sz_buttons[but_maxlights], menuFont, buttonLeft + 450, GFX_HEIGHT - 285, 100, 30 ) )
3649             {
3650                 if ( cfg.dyna_count_req < 16 )
3651                 {
3652                     cfg.dyna_count_req = 16;
3653                 }
3654                 else
3655                 {
3656                     cfg.dyna_count_req += 8;
3657                 }
3658 
3659                 if ( cfg.dyna_count_req > TOTAL_MAX_DYNA )
3660                 {
3661                     cfg.dyna_count_req = 8;
3662                 }
3663 
3664                 snprintf( Cmaxdyna, SDL_arraysize( Cmaxdyna ), "%d", cfg.dyna_count_req );
3665                 sz_buttons[but_maxlights] = Cmaxdyna;
3666             }
3667 
3668             // Perspective correction, overlay, underlay and phong mapping
3669             ui_drawTextBox( menuFont, "Special Effects:", buttonLeft + 300, GFX_HEIGHT - 250, 0, 0, 20 );
3670             if ( BUTTON_UP == ui_doButton( 10, sz_buttons[but_3dfx], menuFont, buttonLeft + 450, GFX_HEIGHT - 250, 100, 30 ) )
3671             {
3672                 if ( cfg.use_phong && cfg.use_perspective && cfg.overlay_allowed && cfg.background_allowed )
3673                 {
3674                     cfg.use_phong          = bfalse;
3675                     cfg.use_perspective    = bfalse;
3676                     cfg.overlay_allowed    = bfalse;
3677                     cfg.background_allowed = bfalse;
3678                     sz_buttons[but_3dfx] = "Off";
3679                 }
3680                 else
3681                 {
3682                     if ( !cfg.use_phong )
3683                     {
3684                         sz_buttons[but_3dfx] = "Okay";
3685                         cfg.use_phong = btrue;
3686                     }
3687                     else
3688                     {
3689                         if ( !cfg.use_perspective && cfg.overlay_allowed && cfg.background_allowed )
3690                         {
3691                             sz_buttons[but_3dfx] = "Superb";
3692                             cfg.use_perspective = btrue;
3693                         }
3694                         else
3695                         {
3696                             cfg.overlay_allowed = btrue;
3697                             cfg.background_allowed = btrue;
3698                             sz_buttons[but_3dfx] = "Good";
3699                         }
3700                     }
3701                 }
3702             }
3703 
3704             // Water Quality
3705             ui_drawTextBox( menuFont, "Good Water:", buttonLeft + 300, GFX_HEIGHT - 215, 0, 0, 20 );
3706             if ( BUTTON_UP == ui_doButton( 11, sz_buttons[but_multiwater], menuFont, buttonLeft + 450, GFX_HEIGHT - 215, 100, 30 ) )
3707             {
3708                 if ( cfg.twolayerwater_allowed )
3709                 {
3710                     sz_buttons[but_multiwater] = "Off";
3711                     cfg.twolayerwater_allowed = bfalse;
3712                 }
3713                 else
3714                 {
3715                     sz_buttons[but_multiwater] = "On";
3716                     cfg.twolayerwater_allowed = btrue;
3717                 }
3718             }
3719 
3720             // Max particles
3721             ui_drawTextBox( menuFont, "Max Particles:", buttonLeft + 300, GFX_HEIGHT - 180, 0, 0, 20 );
3722 
3723             if ( PMod->active )
3724             {
3725                 snprintf( Cmaxparticles, SDL_arraysize( Cmaxparticles ), "%i (%i currently used)", maxparticles, maxparticles - prt_count_free() );
3726                 ui_drawTextBox( menuFont, Cmaxparticles, buttonLeft + 450, GFX_HEIGHT - 180, 0, 100, 30 );
3727             }
3728             else if ( BUTTON_UP == ui_doButton( 15, sz_buttons[but_maxparticles], menuFont, buttonLeft + 450, GFX_HEIGHT - 180, 100, 30 ) )
3729             {
3730                 if ( cfg.particle_count_req < 256 )
3731                 {
3732                     cfg.particle_count_req = 256;
3733                 }
3734                 else
3735                 {
3736                     cfg.particle_count_req += 128;
3737                 }
3738 
3739                 if ( cfg.particle_count_req > MAX_PRT ) cfg.particle_count_req = 256;
3740 
3741                 snprintf( Cmaxparticles, SDL_arraysize( Cmaxparticles ), "%i", cfg.particle_count_req );  // Convert integer to a char we can use
3742                 sz_buttons[but_maxparticles] =  Cmaxparticles;
3743             }
3744 
3745             // Widescreen
3746             ui_drawTextBox( menuFont, "Widescreen:", buttonLeft + 300, GFX_HEIGHT - 70, 0, 0, 20 );
3747             if ( BUTTON_UP == ui_doButton( 12, sz_buttons[but_widescreen], menuFont, buttonLeft + 450, GFX_HEIGHT - 70, 25, 25 ) )
3748             {
3749                 bool_t old_widescreen = widescreen;
3750 
3751                 // toggle widescreen
3752                 widescreen = !widescreen;
3753 
3754                 if ( old_widescreen )
3755                 {
3756                     // switch the display from widescreen to non-widescreen
3757                     sz_buttons[but_widescreen] = " ";
3758 
3759                     // Set to default non-widescreen resolution
3760                     cfg.scrx_req = 800;
3761                     cfg.scry_req = 600;
3762                     sz_buttons[but_screensize] = "800x600";
3763                 }
3764                 else
3765                 {
3766                     // switch the display from non-widescreen to widescreen
3767                     sz_buttons[but_widescreen] = "X";
3768 
3769                     // Set to "default" widescreen resolution
3770                     cfg.scrx_req = 960;
3771                     cfg.scry_req = 600;
3772                     sz_buttons[but_screensize] = "960x600";
3773                 }
3774             }
3775 
3776             // Screen Resolution
3777             ui_drawTextBox( menuFont, "Resolution:", buttonLeft + 300, GFX_HEIGHT - 110, 0, 0, 20 );
3778             if ( BUTTON_UP == ui_doButton( 13, sz_buttons[but_screensize], menuFont, buttonLeft + 450, GFX_HEIGHT - 110, 125, 30 ) )
3779             {
3780                 float req_area;
3781 
3782                 cfg.scrx_req *= 1.1f;
3783                 cfg.scry_req *= 1.1f;
3784 
3785                 req_area = cfg.scrx_req * cfg.scry_req;
3786 
3787                 // use 1920x1200 as a kind of max resolution
3788                 if ( req_area > 1920 * 1200 )
3789                 {
3790                     // reset the screen size to the minimum
3791                     if ( widescreen )
3792                     {
3793                         // "default" widescreen
3794                         cfg.scrx_req = 960;
3795                         cfg.scry_req = 600;
3796                     }
3797                     else
3798                     {
3799                         // "default"
3800                         cfg.scrx_req = 800;
3801                         cfg.scry_req = 600;
3802                     }
3803                 }
3804 
3805                 if ( cfg.fullscreen_req && NULL != sdl_scr.video_mode_list )
3806                 {
3807                     // coerce the screen size to a valid fullscreen mode
3808                     doVideoOptions_fix_fullscreen_resolution( &cfg, &sdl_scr, &sz_screen_size );
3809                 }
3810                 else
3811                 {
3812                     // just accept whatever we are given
3813                     snprintf( sz_screen_size, sizeof( sz_screen_size ), "%dx%d", cfg.scrx_req, cfg.scry_req );
3814                 }
3815 
3816                 sz_buttons[but_screensize] = sz_screen_size;
3817 
3818                 aspect_ratio = ( float )cfg.scrx_req / ( float )cfg.scry_req;
3819 
3820                 // 1.539 is "half way" between normal aspect ratio (4/3) and anamorphic (16/9)
3821                 widescreen = ( aspect_ratio > ( 1.539f ) );
3822 
3823                 if ( widescreen ) sz_buttons[but_widescreen] = "X";
3824                 else              sz_buttons[but_widescreen] = " ";
3825             }
3826 
3827             // Save settings button
3828             if ( BUTTON_UP == ui_doButton( 14, "Save Settings", NULL, buttonLeft, GFX_HEIGHT - 60, 200, 30 ) )
3829             {
3830                 menuChoice = 1;
3831 
3832                 // synchronoze the config values with the various game subsystems
3833                 setup_synch( &cfg );
3834 
3835                 // save the setup file
3836                 setup_upload( &cfg );
3837                 setup_write();
3838 
3839                 // Reload some of the graphics
3840                 load_graphics();
3841             }
3842             if ( menuChoice != 0 )
3843             {
3844                 menuState = MM_Leaving;
3845                 mnu_SlidyButton_init( 0.0f, sz_buttons );
3846             }
3847 
3848             // tool-tip text
3849             ui_drawTextBox( menuFont, tipText, tipTextLeft, tipTextTop, 0, 0, 20 );
3850 
3851             break;
3852 
3853         case MM_Leaving:
3854             // Do buttons sliding out and background fading
3855             // Do the same stuff as in MM_Entering, but backwards
3856             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
3857 
3858             if ( mnu_draw_background )
3859             {
3860                 ui_drawImage( 0, &background, ( GFX_WIDTH  - background.imgW ), 0, 0, 0, NULL );
3861             }
3862 
3863             // Fall trough
3864             menuState = MM_Finish;
3865             break;
3866 
3867         case MM_Finish:
3868             // Free the background texture; don't need to hold onto it
3869             oglx_texture_Release( &background );
3870             menuState = MM_Begin;  // Make sure this all resets next time
3871 
3872             // reset the ui
3873             ui_Reset();
3874 
3875             // Set the next menu to load
3876             result = menuChoice;
3877             break;
3878     }
3879 
3880     return result;
3881 }
3882 
3883 //--------------------------------------------------------------------------------------------
doShowResults(float deltaTime)3884 int doShowResults( float deltaTime )
3885 {
3886     static Font   *font;
3887     static int     menuState = MM_Begin;
3888     static int     count;
3889     static char*   game_hint;
3890     static char    buffer[1024] = EMPTY_CSTR;
3891 
3892     int menuResult = 0;
3893 
3894     switch ( menuState )
3895     {
3896         case MM_Begin:
3897             {
3898                 Uint8 i;
3899                 char * carat = buffer, * carat_end = buffer + SDL_arraysize( buffer );
3900 
3901                 font = ui_getFont();
3902                 count = 0;
3903                 menuState = MM_Entering;
3904 
3905                 // Prepeare the summary text
3906                 for ( i = 0; i < SUMMARYLINES; i++ )
3907                 {
3908                     carat += snprintf( carat, carat_end - carat - 1, "%s\n", mnu_ModList.lst[( MOD_REF )selectedModule].base.summary[i] );
3909                 }
3910 
3911                 // Randomize the next game hint, but only if not in hard mode
3912                 game_hint = CSTR_END;
3913                 if ( cfg.difficulty <= GAME_NORMAL )
3914                 {
3915                     // Should be okay to randomize the seed here, the random seed isnt standarized or
3916                     // used elsewhere before the module is loaded.
3917                     srand( time( NULL ) );
3918                     if ( mnu_GameTip_load_local_vfs() )   game_hint = mnu_GameTip.local_hint[rand() % mnu_GameTip.local_count];
3919                     else if ( mnu_GameTip.count > 0 )     game_hint = mnu_GameTip.hint[rand() % mnu_GameTip.count];
3920                 }
3921             }
3922             // pass through
3923 
3924         case MM_Entering:
3925             menuState = MM_Running;
3926             // pass through
3927 
3928         case MM_Running:
3929             {
3930                 int text_h, text_w;
3931                 ui_drawButton( UI_Nothing, 30, 30, GFX_WIDTH  - 60, GFX_HEIGHT - 65, NULL );
3932 
3933                 GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
3934 
3935                 // the module name
3936                 ui_drawTextBox( font, mnu_ModList.lst[( MOD_REF )selectedModule].base.longname, 50, 80, 291, 230, 20 );
3937 
3938                 // Draw a text box
3939                 ui_drawTextBox( menuFont, buffer, 50, 120, 291, 230, 20 );
3940 
3941                 // Loading game... please wait
3942                 fnt_getTextSize( font, "Loading module...", &text_w, &text_h );
3943                 ui_drawTextBox( font, "Loading module...", ( GFX_WIDTH / 2 ) - text_w / 2, GFX_HEIGHT - 200, 0, 0, 20 );
3944 
3945                 // Draw the game tip
3946                 if ( VALID_CSTR( game_hint ) )
3947                 {
3948                     fnt_getTextSize( menuFont, "GAME TIP", &text_w, &text_h );
3949                     ui_drawTextBox( font, "GAME TIP", ( GFX_WIDTH / 2 ) - ( text_w / 2 ), GFX_HEIGHT - 150, 0, 0, 20 );
3950 
3951                     fnt_getTextSize( menuFont, game_hint, &text_w, &text_h );       /// @todo ZF@> : this doesnt work as I intended, fnt_get_TextSize() does not take line breaks into account
3952                     ui_drawTextBox( menuFont, game_hint, ( GFX_WIDTH / 2 ) - ( text_w / 2 ), GFX_HEIGHT - 110, 0, 0, 10 );
3953                 }
3954 
3955                 // keep track of the iterations through this section for a timer
3956                 count++;
3957                 if ( count > UPDATE_SKIP )
3958                 {
3959                     menuState  = MM_Leaving;
3960                 }
3961             }
3962             break;
3963 
3964         case MM_Leaving:
3965             menuState = MM_Finish;
3966             // pass through
3967 
3968         case MM_Finish:
3969             menuResult = 1;
3970             menuState  = MM_Begin;
3971     }
3972 
3973     return menuResult;
3974 }
3975 
3976 //--------------------------------------------------------------------------------------------
doNotImplemented(float deltaTime)3977 int doNotImplemented( float deltaTime )
3978 {
3979     int x, y;
3980     int w, h;
3981     char notImplementedMessage[] = "Not implemented yet!  Check back soon!";
3982 
3983     fnt_getTextSize( ui_getFont(), notImplementedMessage, &w, &h );
3984     w += 50; // add some space on the sides
3985 
3986     x = GFX_WIDTH  / 2 - w / 2;
3987     y = GFX_HEIGHT / 2 - 17;
3988     if ( BUTTON_UP == ui_doButton( 1, notImplementedMessage, NULL, x, y, w, 30 ) )
3989     {
3990         return 1;
3991     }
3992 
3993     return 0;
3994 }
3995 
3996 //--------------------------------------------------------------------------------------------
doGamePaused(float deltaTime)3997 int doGamePaused( float deltaTime )
3998 {
3999     static int menuState = MM_Begin;
4000     static int menuChoice = 0;
4001 
4002     static const char * buttons[] =
4003     {
4004         "Quit Module",
4005         "Restart Module",
4006         "Return to Module",
4007         "Options",
4008         ""
4009     };
4010 
4011     int result = 0, cnt;
4012 
4013     switch ( menuState )
4014     {
4015         case MM_Begin:
4016             // set up menu variables
4017             menuChoice = 0;
4018             menuState = MM_Entering;
4019 
4020             if ( PMod->exportvalid && !local_stats.allpladead ) buttons[0] = "Save and Exit";
4021             else                                                buttons[0] = "Quit Module";
4022 
4023             mnu_SlidyButton_init( 1.0f, buttons );
4024 
4025         case MM_Entering:
4026             mnu_SlidyButton_draw_all();
4027             mnu_SlidyButton_update_all( -deltaTime );
4028 
4029             // Let lerp wind down relative to the time elapsed
4030             if ( mnu_SlidyButtonState.lerp <= 0.0f )
4031             {
4032                 menuState = MM_Running;
4033             }
4034             break;
4035 
4036         case MM_Running:
4037             // Do normal run
4038             // Background
4039             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
4040 
4041             // Buttons
4042             for ( cnt = 0; cnt < 4; cnt ++ )
4043             {
4044                 if ( BUTTON_UP == ui_doButton( cnt + 1, buttons[cnt], NULL, buttonLeft, buttonTop + ( cnt * 35 ), 200, 30 ) )
4045                 {
4046                     // audio options
4047                     menuChoice = cnt + 1;
4048                 }
4049             }
4050 
4051             // Quick return to game
4052             if ( SDLKEYDOWN( SDLK_ESCAPE ) ) menuChoice = 3;
4053 
4054             if ( menuChoice != 0 )
4055             {
4056                 menuState = MM_Leaving;
4057                 mnu_SlidyButton_init( 0.0f, buttons );
4058             }
4059             break;
4060 
4061         case MM_Leaving:
4062             // Do buttons sliding out and background fading
4063             // Do the same stuff as in MM_Entering, but backwards
4064             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
4065 
4066             // Buttons
4067             mnu_SlidyButton_draw_all();
4068             mnu_SlidyButton_update_all( deltaTime );
4069             if ( mnu_SlidyButtonState.lerp >= 1.0f )
4070             {
4071                 menuState = MM_Finish;
4072             }
4073             break;
4074 
4075         case MM_Finish:
4076             // Free the background texture; don't need to hold onto it
4077             menuState = MM_Begin;  // Make sure this all resets next time
4078 
4079             // reset the ui
4080             ui_Reset();
4081 
4082             // Set the next menu to load
4083             result = menuChoice;
4084             break;
4085     }
4086 
4087     return result;
4088 }
4089 
4090 //--------------------------------------------------------------------------------------------
doShowEndgame(float deltaTime)4091 int doShowEndgame( float deltaTime )
4092 {
4093     static int menuState = MM_Begin;
4094     static int menuChoice = 0;
4095     static int x, y, w, h;
4096     static Font *font;
4097 
4098     static const char * buttons[] =
4099     {
4100         "BLAH",
4101         ""
4102     };
4103 
4104     int cnt, retval;
4105 
4106     retval = 0;
4107     switch ( menuState )
4108     {
4109         case MM_Begin:
4110             menuState = MM_Entering;
4111             font = ui_getFont();
4112 
4113             mnu_SlidyButton_init( 1.0f, buttons );
4114 
4115             if ( PMod->exportvalid )
4116             {
4117                 buttons[0] = "Save and Exit";
4118             }
4119             else
4120             {
4121                 buttons[0] = "Exit Game";
4122             }
4123 
4124             x = 70;
4125             y = 70;
4126             w = GFX_WIDTH  - 2 * x;
4127             h = GFX_HEIGHT - 2 * y;
4128 
4129         case MM_Entering:
4130 
4131             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
4132 
4133             ui_drawTextBox( NULL, endtext, x, y, w, h, 20 );
4134             mnu_SlidyButton_draw_all();
4135 
4136             mnu_SlidyButton_update_all( -deltaTime );
4137 
4138             // Let lerp wind down relative to the time elapsed
4139             if ( mnu_SlidyButtonState.lerp <= 0.0f )
4140             {
4141                 menuState = MM_Running;
4142             }
4143             break;
4144 
4145         case MM_Running:
4146             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
4147 
4148             // Buttons
4149             for ( cnt = 0; cnt < 1; cnt ++ )
4150             {
4151                 if ( BUTTON_UP == ui_doButton( cnt + 1, buttons[cnt], NULL, buttonLeft, buttonTop + ( cnt * 35 ), 200, 30 ) )
4152                 {
4153                     // audio options
4154                     menuChoice = cnt + 1;
4155                     menuState = MM_Leaving;
4156                 }
4157             }
4158 
4159             // escape also kills this menu
4160             if ( SDLKEYDOWN( SDLK_ESCAPE ) )
4161             {
4162                 menuChoice = 1;
4163                 menuState = MM_Leaving;
4164             }
4165 
4166             ui_drawTextBox( NULL, endtext, x, y, w, h, 20 );
4167 
4168             break;
4169 
4170         case MM_Leaving:
4171             // Do buttons sliding out and background fading
4172             // Do the same stuff as in MM_Entering, but backwards
4173             GL_DEBUG( glColor4f )( 1, 1, 1, 1 - mnu_SlidyButtonState.lerp );
4174 
4175             ui_drawTextBox( NULL, endtext, x, y, w, h, 20 );
4176 
4177             // Buttons
4178             mnu_SlidyButton_draw_all();
4179             mnu_SlidyButton_update_all( deltaTime );
4180             if ( mnu_SlidyButtonState.lerp >= 1.0f )
4181             {
4182                 menuState = MM_Finish;
4183             }
4184             break;
4185 
4186         case MM_Finish:
4187             {
4188                 bool_t reloaded = bfalse;
4189 
4190                 // try to pop the last module off the module stack
4191                 reloaded = link_pop_module();
4192 
4193                 // try to go to the world map
4194                 // if( !reloaded )
4195                 // {
4196                 //    reloaded = link_load_parent( mnu_ModList.lst[pickedmodule_index].base.parent_modname, mnu_ModList.lst[pickedmodule_index].base.parent_pos );
4197                 // }
4198 
4199                 // fix the menu that is returned when you break out of the game
4200                 if ( PMod->beat && start_new_player )
4201                 {
4202                     // we started with a new player and beat the module... yay!
4203                     // now we want to graduate to the ChoosePlayer menu to
4204                     // build our party
4205 
4206                     start_new_player = bfalse;
4207 
4208                     // if we beat a beginner module, we want to
4209                     // go to ChoosePlayer instead of ChooseModule.
4210                     if ( mnu_stack_peek() == emnu_ChooseModule )
4211                     {
4212                         mnu_stack_pop();
4213                         mnu_stack_push( emnu_ChoosePlayer );
4214                     }
4215                 }
4216 
4217                 // actually quit the module
4218                 if ( !reloaded )
4219                 {
4220                     game_finish_module();
4221                     pickedmodule_index = -1;
4222                     process_kill( PROC_PBASE( GProc ) );
4223                 }
4224 
4225                 menuState = MM_Begin;
4226 
4227                 // Set the next menu to load
4228                 retval = menuChoice;
4229             }
4230     }
4231 
4232     return retval;
4233 }
4234 
4235 //--------------------------------------------------------------------------------------------
4236 // place this last so that we do not have to prototype every menu function
doMenu(float deltaTime)4237 int doMenu( float deltaTime )
4238 {
4239     /// @details the global function that controls the navigation between menus
4240 
4241     int retval, result = 0;
4242 
4243     if ( mnu_whichMenu == emnu_Main )
4244     {
4245         mnu_stack_clear();
4246     };
4247 
4248     retval = MENU_NOTHING;
4249 
4250     switch ( mnu_whichMenu )
4251     {
4252         case emnu_Main:
4253             result = doMainMenu( deltaTime );
4254             if ( result != 0 )
4255             {
4256                 if ( 1 == result )      { mnu_begin_menu( emnu_ChooseModule ); start_new_player = btrue; }
4257                 else if ( 2 == result ) { mnu_begin_menu( emnu_ChoosePlayer ); start_new_player = bfalse; }
4258                 else if ( 3 == result ) { mnu_begin_menu( emnu_Options ); }
4259                 else if ( 4 == result ) retval = MENU_QUIT;  // need to request a quit somehow
4260             }
4261             break;
4262 
4263         case emnu_SinglePlayer:
4264             result = doSinglePlayerMenu( deltaTime );
4265 
4266             if ( result != 0 )
4267             {
4268                 if ( 1 == result )
4269                 {
4270                     mnu_begin_menu( emnu_ChooseModule );
4271                     start_new_player = btrue;
4272                 }
4273                 else if ( 2 == result )
4274                 {
4275                     mnu_begin_menu( emnu_ChoosePlayer );
4276                     start_new_player = bfalse;
4277                 }
4278                 else if ( 3 == result )
4279                 {
4280                     mnu_end_menu();
4281                     retval = MENU_END;
4282                 }
4283                 else
4284                 {
4285                     mnu_begin_menu( emnu_NewPlayer );
4286                 }
4287             }
4288             break;
4289 
4290         case emnu_ChooseModule:
4291             result = doChooseModule( deltaTime );
4292 
4293             if ( -1 == result )     { mnu_end_menu(); retval = MENU_END; }
4294             else if ( 1 == result ) mnu_begin_menu( emnu_ShowMenuResults );  // imports are not valid (starter module)
4295             else if ( 2 == result ) mnu_begin_menu( emnu_ShowMenuResults );  // imports are valid
4296 
4297             break;
4298 
4299         case emnu_ChoosePlayer:
4300             result = doChoosePlayer( deltaTime );
4301 
4302             if ( -1 == result )     { mnu_end_menu(); retval = MENU_END; }
4303             else if ( 1 == result ) mnu_begin_menu( emnu_ChooseModule );
4304 
4305             break;
4306 
4307         case emnu_Options:
4308             result = doOptions( deltaTime );
4309             if ( result != 0 )
4310             {
4311                 if ( 1 == result )      mnu_begin_menu( emnu_AudioOptions );
4312                 else if ( 2 == result ) mnu_begin_menu( emnu_InputOptions );
4313                 else if ( 3 == result ) mnu_begin_menu( emnu_VideoOptions );
4314                 else if ( 4 == result ) { mnu_end_menu(); retval = MENU_END; }
4315                 else if ( 5 == result ) mnu_begin_menu( emnu_GameOptions );
4316             }
4317             break;
4318 
4319         case emnu_GameOptions:
4320             result = doGameOptions( deltaTime );
4321             if ( result != 0 )
4322             {
4323                 mnu_end_menu();
4324                 retval = MENU_END;
4325             }
4326             break;
4327 
4328         case emnu_AudioOptions:
4329             result = doAudioOptions( deltaTime );
4330             if ( result != 0 )
4331             {
4332                 mnu_end_menu();
4333                 retval = MENU_END;
4334             }
4335             break;
4336 
4337         case emnu_VideoOptions:
4338             result = doVideoOptions( deltaTime );
4339             if ( result != 0 )
4340             {
4341                 mnu_end_menu();
4342                 retval = MENU_END;
4343             }
4344             break;
4345 
4346         case emnu_InputOptions:
4347             result = doInputOptions( deltaTime );
4348             if ( result != 0 )
4349             {
4350                 mnu_end_menu();
4351                 retval = MENU_END;
4352             }
4353             break;
4354 
4355         case emnu_ShowMenuResults:
4356             result = doShowResults( deltaTime );
4357             if ( result != 0 )
4358             {
4359                 mnu_end_menu();
4360                 retval = MENU_SELECT;
4361             }
4362             break;
4363 
4364         case emnu_GamePaused:
4365             result = doGamePaused( deltaTime );
4366             if ( result != 0 )
4367             {
4368                 if ( 1 == result )
4369                 {
4370                     // "Quit Module"
4371 
4372                     bool_t reloaded = bfalse;
4373 
4374                     mnu_end_menu();
4375 
4376                     // try to pop the last module off the module stack
4377                     reloaded = link_pop_module();
4378 
4379                     // try to go to the world map
4380                     // if( !reloaded )
4381                     // {
4382                     //    reloaded = link_load_parent( mnu_ModList.lst[pickedmodule_index].base.parent_modname, mnu_ModList.lst[pickedmodule_index].base.parent_pos );
4383                     // }
4384 
4385                     if ( !reloaded )
4386                     {
4387                         game_finish_module();
4388                         process_kill( PROC_PBASE( GProc ) );
4389                     }
4390 
4391                     result = MENU_QUIT;
4392                 }
4393                 else if ( 2 == result )
4394                 {
4395                     // "Restart Module"
4396                     mnu_end_menu();
4397 
4398                     //Simply quit the current module and begin it again
4399                     game_quit_module();
4400                     game_begin_module( PMod->loadname, ( Uint32 )~0 );
4401 
4402                     retval = MENU_END;
4403                 }
4404                 else if ( 3 == result )
4405                 {
4406                     // "Return to Module"
4407                     mnu_end_menu();
4408                     retval = MENU_END;
4409                 }
4410                 else if ( 4 == result )
4411                 {
4412                     // "Options"
4413                     mnu_begin_menu( emnu_Options );
4414                 }
4415             }
4416             break;
4417 
4418         case emnu_ShowEndgame:
4419             result = doShowEndgame( deltaTime );
4420             if ( 1 == result )
4421             {
4422                 mnu_end_menu();
4423                 retval = MENU_END;
4424             }
4425             break;
4426 
4427         case emnu_NotImplemented:
4428         default:
4429             result = doNotImplemented( deltaTime );
4430             if ( result != 0 )
4431             {
4432                 mnu_end_menu();
4433                 retval = MENU_END;
4434             }
4435     }
4436 
4437     return retval;
4438 }
4439 
4440 //--------------------------------------------------------------------------------------------
4441 // Auto formatting functions
4442 //--------------------------------------------------------------------------------------------
autoformat_init(gfx_config_t * pgfx)4443 void autoformat_init( gfx_config_t * pgfx )
4444 {
4445     autoformat_init_slidy_buttons();
4446     autoformat_init_tip_text();
4447     autoformat_init_copyright_text();
4448 
4449     if ( NULL != pgfx )
4450     {
4451         ui_set_virtual_screen( pgfx->vw, pgfx->vh, GFX_WIDTH, GFX_HEIGHT );
4452     }
4453 }
4454 
4455 //--------------------------------------------------------------------------------------------
autoformat_init_slidy_buttons()4456 void autoformat_init_slidy_buttons()
4457 {
4458     // Figure out where to draw the buttons
4459     buttonLeft = 40;
4460     buttonTop = GFX_HEIGHT - 20;
4461 }
4462 
4463 //--------------------------------------------------------------------------------------------
autoformat_init_tip_text()4464 void autoformat_init_tip_text()
4465 {
4466     // set the text
4467     tipText = NULL;
4468 
4469     // Draw the options text to the right of the buttons
4470     tipTextLeft = 280;
4471 
4472     // And relative to the bottom of the screen
4473     tipTextTop = GFX_HEIGHT;
4474 }
4475 
4476 //--------------------------------------------------------------------------------------------
autoformat_init_copyright_text()4477 void autoformat_init_copyright_text()
4478 {
4479     // set the text
4480     copyrightText = "Welcome to Egoboo!\nhttp://egoboo.sourceforge.net\nVersion " VERSION "\n";
4481 
4482     // Draw the copyright text to the right of the buttons
4483     copyrightLeft = 280;
4484 
4485     // And relative to the bottom of the screen
4486     copyrightTop = GFX_HEIGHT;
4487 }
4488 
4489 //--------------------------------------------------------------------------------------------
4490 // Implementation of tipText
4491 //--------------------------------------------------------------------------------------------
tipText_set_position(Font * font,const char * text,int spacing)4492 void tipText_set_position( Font * font, const char * text, int spacing )
4493 {
4494     int w, h;
4495 
4496     autoformat_init_tip_text();
4497 
4498     if ( NULL == text ) return;
4499 
4500     fnt_getTextBoxSize( font, text, spacing, &w, &h );
4501 
4502     // set the text
4503     tipText = text;
4504 
4505     // Draw the options text to the right of the buttons
4506     tipTextLeft = 280;
4507 
4508     // And relative to the bottom of the screen
4509     tipTextTop = GFX_HEIGHT - h - spacing;
4510 }
4511 
4512 //--------------------------------------------------------------------------------------------
4513 // Implementation of copyrightText
4514 //--------------------------------------------------------------------------------------------
copyrightText_set_position(Font * font,const char * text,int spacing)4515 void copyrightText_set_position( Font * font, const char * text, int spacing )
4516 {
4517     int w, h;
4518 
4519     autoformat_init_copyright_text();
4520 
4521     if ( NULL == text ) return;
4522 
4523     copyrightLeft = 0;
4524     copyrightLeft = 0;
4525 
4526     // Figure out where to draw the copyright text
4527     fnt_getTextBoxSize( font, text, 20, &w, &h );
4528 
4529     // set the text
4530     copyrightText = text;
4531 
4532     // Draw the copyright text to the right of the buttons
4533     copyrightLeft = 280;
4534 
4535     // And relative to the bottom of the screen
4536     copyrightTop = GFX_HEIGHT - h - spacing;
4537 }
4538 
4539 //--------------------------------------------------------------------------------------------
4540 // Asset management
4541 //--------------------------------------------------------------------------------------------
mnu_load_all_module_images_vfs(LoadPlayer_list_t * lp_lst)4542 void mnu_load_all_module_images_vfs( LoadPlayer_list_t * lp_lst )
4543 {
4544     /// @details ZZ@> This function loads the title image for each module.  Modules without a
4545     ///     title are marked as invalid
4546 
4547     STRING loadname;
4548     MOD_REF imod;
4549     vfs_FILE* filesave;
4550 
4551     // release all allocated data from the mnu_ModList and empty the list
4552     mnu_ModList_release_images();
4553 
4554     // Log a directory list
4555     filesave = vfs_openWrite( "/debug/modules.txt" );
4556     if ( NULL != filesave )
4557     {
4558         vfs_printf( filesave, "This file logs all of the modules found\n" );
4559         vfs_printf( filesave, "** Denotes an invalid module\n" );
4560         vfs_printf( filesave, "## Denotes an unlockable module\n\n" );
4561     }
4562 
4563     // load all the title images for modules that we are going to display
4564     for ( imod = 0; imod < mnu_ModList.count; imod++ )
4565     {
4566         if ( !mnu_ModList.lst[imod].loaded )
4567         {
4568             vfs_printf( filesave, "**.  %s\n", mnu_ModList.lst[imod].vfs_path );
4569         }
4570         else if ( mnu_test_module_by_index( lp_lst, imod, 0, NULL ) )
4571         {
4572             // @note just because we can't load the title image DOES NOT mean that we ignore the module
4573             snprintf( loadname, SDL_arraysize( loadname ), "%s/gamedat/title", mnu_ModList.lst[imod].vfs_path );
4574 
4575             mnu_ModList.lst[imod].tex_index = TxTitleImage_load_one_vfs( loadname );
4576 
4577             vfs_printf( filesave, "%02d.  %s\n", REF_TO_INT( imod ), mnu_ModList.lst[imod].vfs_path );
4578         }
4579         else
4580         {
4581             vfs_printf( filesave, "##.  %s\n", mnu_ModList.lst[imod].vfs_path );
4582         }
4583     }
4584 
4585     if ( NULL != filesave )
4586     {
4587         vfs_close( filesave );
4588     }
4589 }
4590 
4591 //--------------------------------------------------------------------------------------------
mnu_get_icon_ref(const CAP_REF icap,const TX_REF default_ref)4592 TX_REF mnu_get_icon_ref( const CAP_REF icap, const TX_REF default_ref )
4593 {
4594     /// @details BB@> This function gets the proper icon for a an object profile.
4595     //
4596     //     In the character preview section of the menu system, we do not load
4597     //     entire profiles, just the character definition file ("data.txt")
4598     //     and one icon. Sometimes, though the item is actually a spell effect which means
4599     //     that we need to display the book icon.
4600 
4601     TX_REF icon_ref = ( TX_REF )ICON_NULL;
4602     bool_t is_spell_fx, is_book, draw_book;
4603 
4604     cap_t * pitem_cap;
4605 
4606     if ( !LOADED_CAP( icap ) ) return icon_ref;
4607     pitem_cap = CapStack.lst + icap;
4608 
4609     // what do we need to draw?
4610     is_spell_fx = ( NO_SKIN_OVERRIDE != pitem_cap->spelleffect_type );
4611     is_book     = ( SPELLBOOK == icap );
4612     draw_book   = ( is_book || is_spell_fx ) && ( bookicon_count > 0 );
4613 
4614     if ( !draw_book )
4615     {
4616         icon_ref = default_ref;
4617     }
4618     else if ( draw_book )
4619     {
4620         int iskin = 0;
4621 
4622         if ( NO_SKIN_OVERRIDE != pitem_cap->spelleffect_type )
4623         {
4624             iskin = pitem_cap->spelleffect_type;
4625         }
4626         else if ( NO_SKIN_OVERRIDE != pitem_cap->skin_override )
4627         {
4628             iskin = pitem_cap->skin_override;
4629         }
4630 
4631         iskin = CLIP( iskin, 0, bookicon_count );
4632 
4633         icon_ref = bookicon_ref[ iskin ];
4634     }
4635 
4636     return icon_ref;
4637 }
4638 
4639 //--------------------------------------------------------------------------------------------
4640 // module utilities
4641 //--------------------------------------------------------------------------------------------
mnu_get_mod_number(const char * szModName)4642 int mnu_get_mod_number( const char *szModName )
4643 {
4644     /// @details ZZ@> This function returns -1 if the module does not exist locally, the module
4645     ///    index otherwise
4646 
4647     MOD_REF modnum;
4648     int     retval = -1;
4649 
4650     for ( modnum = 0; modnum < mnu_ModList.count; modnum++ )
4651     {
4652         if ( 0 == strcmp( mnu_ModList.lst[modnum].vfs_path, szModName ) )
4653         {
4654             retval = REF_TO_INT( modnum );
4655             break;
4656         }
4657     }
4658 
4659     return retval;
4660 }
4661 
4662 //--------------------------------------------------------------------------------------------
mnu_test_module_by_index(LoadPlayer_list_t * lp_lst,const MOD_REF modnumber,size_t buffer_len,char * buffer)4663 bool_t mnu_test_module_by_index( LoadPlayer_list_t * lp_lst, const MOD_REF modnumber, size_t buffer_len, char * buffer )
4664 {
4665     int            cnt;
4666     mnu_module_t * pmod;
4667     bool_t         allowed;
4668 
4669     if ( INVALID_MOD( modnumber ) ) return bfalse;
4670     pmod = mnu_ModList.lst + modnumber;
4671 
4672     // First check if we are in developers mode or that the right module has been beaten before
4673     allowed = bfalse;
4674 
4675     if ( cfg.dev_mode )
4676     {
4677         allowed = btrue;
4678     }
4679 
4680     if ( !allowed )
4681     {
4682         if ( module_has_idsz_vfs( pmod->base.reference, pmod->base.unlockquest.id, buffer_len, buffer ) )
4683         {
4684             allowed = btrue;
4685         }
4686     }
4687 
4688     if ( !allowed && pmod->base.importamount > 0 )
4689     {
4690         int player_count = 0;
4691         int player_allowed = 0;
4692 
4693         // If that did not work, then check all selected players directories, but only if it isn't a starter module
4694         for ( cnt = 0; cnt < lp_lst->count; cnt++ )
4695         {
4696             int                    quest_level = QUEST_NONE;
4697             LoadPlayer_element_t * ptr         = lp_lst->lst + cnt;
4698 
4699             player_count++;
4700 
4701             quest_level = quest_get_level( ptr->quest_log, SDL_arraysize( ptr->quest_log ), pmod->base.unlockquest.id );
4702 
4703             // find beaten quests or quests with proper level
4704             if ( quest_level <= QUEST_BEATEN || pmod->base.unlockquest.level <= quest_level )
4705             {
4706                 player_allowed++;
4707             }
4708         }
4709 
4710         allowed = ( player_allowed == player_count );
4711     }
4712 
4713     return allowed;
4714 }
4715 
4716 //--------------------------------------------------------------------------------------------
mnu_test_module_by_name(LoadPlayer_list_t * lp_lst,const char * szModName)4717 bool_t mnu_test_module_by_name( LoadPlayer_list_t * lp_lst, const char *szModName )
4718 {
4719     /// @details ZZ@> This function tests to see if a module can be entered by
4720     ///    the players
4721 
4722     bool_t retval;
4723 
4724     // find the module by name
4725     int modnumber = mnu_get_mod_number( szModName );
4726 
4727     retval = bfalse;
4728     if ( modnumber >= 0 )
4729     {
4730         retval = mnu_test_module_by_index( lp_lst, ( MOD_REF )modnumber, 0, NULL );
4731     }
4732 
4733     return retval;
4734 }
4735 
4736 //--------------------------------------------------------------------------------------------
mnu_module_init(mnu_module_t * pmod)4737 void mnu_module_init( mnu_module_t * pmod )
4738 {
4739     if ( NULL == pmod ) return;
4740 
4741     // clear the module
4742     memset( pmod, 0, sizeof( *pmod ) );
4743 
4744     pmod->tex_index = INVALID_TITLE_TEXTURE;
4745 }
4746 
4747 //--------------------------------------------------------------------------------------------
mnu_load_all_module_info()4748 void mnu_load_all_module_info()
4749 {
4750     vfs_search_context_t * ctxt;
4751 
4752     const char *vfs_ModPath;
4753     STRING      loadname;
4754 
4755     // reset the module list
4756     mnu_ModList_release_all();
4757 
4758     // Search for all .mod directories and load the module info
4759     ctxt = vfs_findFirst( "mp_modules", "mod", VFS_SEARCH_DIR );
4760     vfs_ModPath = vfs_search_context_get_current( ctxt );
4761 
4762     while ( NULL != ctxt && VALID_CSTR( vfs_ModPath ) && mnu_ModList.count < MAX_MODULE )
4763     {
4764         mnu_module_t * pmod = mnu_ModList.lst + ( MOD_REF )mnu_ModList.count;
4765 
4766         // clear the module
4767         mnu_module_init( pmod );
4768 
4769         // save the filename
4770         snprintf( loadname, SDL_arraysize( loadname ), "%s/gamedat/menu.txt", vfs_ModPath );
4771         if ( NULL != module_load_info_vfs( loadname, &( pmod->base ) ) )
4772         {
4773             mnu_ModList.count++;
4774 
4775             // mark the module data as loaded
4776             pmod->loaded = btrue;
4777 
4778             // save the module path
4779             strncpy( pmod->vfs_path, vfs_ModPath, SDL_arraysize( pmod->vfs_path ) );
4780 
4781             // Save the user data directory version of the module path.
4782             // @note This is kinda a cheat since we know that the virtual paths all begin with "mp_" at the moment.
4783             // If that changes, this line must be changed as well.
4784             snprintf( pmod->dest_path, SDL_arraysize( pmod->dest_path ), "/%s", vfs_ModPath + 3 );
4785 
4786             // same problem as above
4787             strncpy( pmod->name, vfs_ModPath + 11, SDL_arraysize( pmod->name ) );
4788         };
4789 
4790         ctxt = vfs_findNext( &ctxt );
4791         vfs_ModPath = vfs_search_context_get_current( ctxt );
4792     }
4793     vfs_findClose( &ctxt );
4794 
4795     module_list_valid = btrue;
4796 }
4797 
4798 //--------------------------------------------------------------------------------------------
mnu_release_one_module(const MOD_REF imod)4799 void mnu_release_one_module( const MOD_REF imod )
4800 {
4801     mnu_module_t * pmod;
4802 
4803     if ( !VALID_MOD( imod ) ) return;
4804     pmod = mnu_ModList.lst + imod;
4805 
4806     TxTitleImage_release_one( pmod->tex_index );
4807     pmod->tex_index = INVALID_TITLE_TEXTURE;
4808 }
4809 
4810 //--------------------------------------------------------------------------------------------
4811 // Implementation of the ModList struct
4812 //--------------------------------------------------------------------------------------------
mnu_ModList_get_base(int imod)4813 mod_file_t * mnu_ModList_get_base( int imod )
4814 {
4815     if ( imod < 0 || imod >= MAX_MODULE ) return NULL;
4816 
4817     return &( mnu_ModList.lst[( MOD_REF )imod].base );
4818 }
4819 
4820 //--------------------------------------------------------------------------------------------
mnu_ModList_get_vfs_path(int imod)4821 const char * mnu_ModList_get_vfs_path( int imod )
4822 {
4823     if ( imod < 0 || imod >= MAX_MODULE ) return NULL;
4824 
4825     return mnu_ModList.lst[( MOD_REF )imod].vfs_path;
4826 }
4827 
4828 //--------------------------------------------------------------------------------------------
mnu_ModList_get_dest_path(int imod)4829 const char * mnu_ModList_get_dest_path( int imod )
4830 {
4831     if ( imod < 0 || imod >= MAX_MODULE ) return NULL;
4832 
4833     return mnu_ModList.lst[( MOD_REF )imod].dest_path;
4834 }
4835 
4836 //--------------------------------------------------------------------------------------------
mnu_ModList_get_name(int imod)4837 const char * mnu_ModList_get_name( int imod )
4838 {
4839     if ( imod < 0 || imod >= MAX_MODULE ) return NULL;
4840 
4841     return mnu_ModList.lst[( MOD_REF )imod].name;
4842 }
4843 
4844 //--------------------------------------------------------------------------------------------
mnu_ModList_release_all()4845 void mnu_ModList_release_all()
4846 {
4847     MOD_REF cnt;
4848 
4849     for ( cnt = 0; cnt < MAX_MODULE; cnt++ )
4850     {
4851         // release any allocated data
4852         if ( cnt < mnu_ModList.count )
4853         {
4854             mnu_release_one_module( cnt );
4855         }
4856 
4857         memset( mnu_ModList.lst + cnt, 0, sizeof( mnu_module_t ) );
4858     }
4859 
4860     mnu_ModList.count = 0;
4861 }
4862 
4863 //--------------------------------------------------------------------------------------------
mnu_ModList_release_images()4864 void mnu_ModList_release_images()
4865 {
4866     MOD_REF cnt;
4867     int tnc;
4868 
4869     tnc = -1;
4870     for ( cnt = 0; cnt < mnu_ModList.count; cnt++ )
4871     {
4872         if ( !mnu_ModList.lst[cnt].loaded ) continue;
4873         tnc = REF_TO_INT( cnt );
4874 
4875         TxTitleImage_release_one( mnu_ModList.lst[cnt].tex_index );
4876         mnu_ModList.lst[cnt].tex_index = INVALID_TITLE_TEXTURE;
4877     }
4878     TxTitleImage.count = 0;
4879 
4880     // make sure that mnu_ModList.count is the right size, in case some modules were unloaded?
4881     mnu_ModList.count = tnc + 1;
4882 }
4883 
4884 //--------------------------------------------------------------------------------------------
4885 // Functions for implementing the TxTitleImage array of textures
4886 //--------------------------------------------------------------------------------------------
TxTitleImage_clear_data()4887 void TxTitleImage_clear_data()
4888 {
4889     TxTitleImage.count = 0;
4890 }
4891 
4892 //--------------------------------------------------------------------------------------------
TxTitleImage_ctor()4893 void TxTitleImage_ctor()
4894 {
4895     /// @details ZZ@> This function clears out all of the textures
4896 
4897     TX_REF cnt;
4898 
4899     for ( cnt = 0; cnt < MAX_MODULE; cnt++ )
4900     {
4901         oglx_texture_ctor( TxTitleImage.lst + cnt );
4902     }
4903 
4904     TxTitleImage_clear_data();
4905 }
4906 
4907 //--------------------------------------------------------------------------------------------
TxTitleImage_release_one(const TX_REF index)4908 void TxTitleImage_release_one( const TX_REF index )
4909 {
4910     if ( index < 0 || index >= MAX_MODULE ) return;
4911 
4912     oglx_texture_Release( TxTitleImage.lst + index );
4913 }
4914 
4915 //--------------------------------------------------------------------------------------------
TxTitleImage_release_all()4916 void TxTitleImage_release_all()
4917 {
4918     /// @details ZZ@> This function releases all of the textures
4919 
4920     TX_REF cnt;
4921 
4922     for ( cnt = 0; cnt < MAX_MODULE; cnt++ )
4923     {
4924         TxTitleImage_release_one( cnt );
4925     }
4926 
4927     TxTitleImage_clear_data();
4928 }
4929 
4930 //--------------------------------------------------------------------------------------------
TxTitleImage_dtor()4931 void TxTitleImage_dtor()
4932 {
4933     /// @details ZZ@> This function clears out all of the textures
4934 
4935     TX_REF cnt;
4936 
4937     for ( cnt = 0; cnt < MAX_MODULE; cnt++ )
4938     {
4939         oglx_texture_dtor( TxTitleImage.lst + cnt );
4940     }
4941 
4942     TxTitleImage_clear_data();
4943 }
4944 
4945 //--------------------------------------------------------------------------------------------
TxTitleImage_load_one_vfs(const char * szLoadName)4946 TX_REF TxTitleImage_load_one_vfs( const char *szLoadName )
4947 {
4948     /// @details ZZ@> This function loads a title in the specified image slot, forcing it into
4949     ///    system memory.  Returns btrue if it worked
4950 
4951     TX_REF itex;
4952 
4953     if ( INVALID_CSTR( szLoadName ) ) return ( TX_REF )INVALID_TITLE_TEXTURE;
4954 
4955     if ( TxTitleImage.count >= TITLE_TEXTURE_COUNT ) return ( TX_REF )INVALID_TITLE_TEXTURE;
4956 
4957     itex  = ( TX_REF )TxTitleImage.count;
4958     if ( INVALID_GL_ID != ego_texture_load_vfs( TxTitleImage.lst + itex, szLoadName, INVALID_KEY ) )
4959     {
4960         TxTitleImage.count++;
4961     }
4962     else
4963     {
4964         itex = ( TX_REF )INVALID_TITLE_TEXTURE;
4965     }
4966 
4967     return itex;
4968 }
4969 
4970 //--------------------------------------------------------------------------------------------
TxTitleImage_get_ptr(const TX_REF itex)4971 oglx_texture_t * TxTitleImage_get_ptr( const TX_REF itex )
4972 {
4973     if ( itex >= TxTitleImage.count || itex >= MAX_MODULE ) return NULL;
4974 
4975     return TxTitleImage.lst + itex;
4976 }
4977 
4978 //--------------------------------------------------------------------------------------------
TxTitleImage_reload_all()4979 void TxTitleImage_reload_all()
4980 {
4981     /// @details ZZ@> This function re-loads all the current textures back into
4982     ///               OpenGL texture memory using the cached SDL surfaces
4983 
4984     TX_REF cnt;
4985 
4986     for ( cnt = 0; cnt < TX_TEXTURE_COUNT; cnt++ )
4987     {
4988         oglx_texture_t * ptex = TxTitleImage.lst + cnt;
4989 
4990         if ( ptex->valid )
4991         {
4992             oglx_texture_Convert( ptex, ptex->surface, INVALID_KEY );
4993         }
4994     }
4995 }
4996 
4997 //--------------------------------------------------------------------------------------------
4998 // Implementation of the mnu_GameTip system
4999 //--------------------------------------------------------------------------------------------
mnu_GameTip_load_global_vfs()5000 void mnu_GameTip_load_global_vfs()
5001 {
5002     /// ZF@> This function loads all of the game hints and tips
5003     STRING buffer;
5004     vfs_FILE *fileread;
5005     Uint8 cnt;
5006 
5007     // reset the count
5008     mnu_GameTip.count = 0;
5009 
5010     // Open the file with all the tips
5011     fileread = vfs_openRead( "mp_data/gametips.txt" );
5012     if ( NULL == fileread )
5013     {
5014         log_warning( "Could not load the game tips and hints. (\"mp_data/gametips.txt\")\n" );
5015         return;
5016     }
5017 
5018     // Load the data
5019     for ( cnt = 0; cnt < MENU_MAX_GAMETIPS && !vfs_eof( fileread ); cnt++ )
5020     {
5021         if ( goto_colon( NULL, fileread, btrue ) )
5022         {
5023             //Read the line
5024             fget_string( fileread, buffer, SDL_arraysize( buffer ) );
5025             strcpy( mnu_GameTip.hint[cnt], buffer );
5026 
5027             //Make it look nice
5028             str_decode( mnu_GameTip.hint[cnt], SDL_arraysize( mnu_GameTip.hint[cnt] ), mnu_GameTip.hint[cnt] );
5029             //str_add_linebreaks( mnu_GameTip.hint[cnt], SDL_arraysize( mnu_GameTip.hint[cnt] ), 50 );
5030 
5031             //Keep track of how many we have total
5032             mnu_GameTip.count++;
5033         }
5034     }
5035 
5036     vfs_close( fileread );
5037 }
5038 
5039 //--------------------------------------------------------------------------------------------
mnu_GameTip_load_local_vfs()5040 bool_t mnu_GameTip_load_local_vfs()
5041 {
5042     /// ZF@> This function loads all module specific hints and tips. If this fails, the game will
5043     //       default to the global hints and tips instead
5044 
5045     STRING buffer;
5046     vfs_FILE *fileread;
5047     Uint8 cnt;
5048 
5049     // reset the count
5050     mnu_GameTip.local_count = 0;
5051 
5052     // Open all the tips
5053     snprintf( buffer, SDL_arraysize( buffer ), "mp_modules/%s/gamedat/gametips.txt", pickedmodule_name );
5054     fileread = vfs_openRead( buffer );
5055     if ( NULL == fileread ) return bfalse;
5056 
5057     // Load the data
5058     for ( cnt = 0; cnt < MENU_MAX_GAMETIPS && !vfs_eof( fileread ); cnt++ )
5059     {
5060         if ( goto_colon( NULL, fileread, btrue ) )
5061         {
5062             //Read the line
5063             fget_string( fileread, buffer, SDL_arraysize( buffer ) );
5064             strcpy( mnu_GameTip.local_hint[cnt], buffer );
5065 
5066             //Make it look nice
5067             str_decode( mnu_GameTip.local_hint[cnt], SDL_arraysize( mnu_GameTip.local_hint[cnt] ), mnu_GameTip.local_hint[cnt] );
5068             //str_add_linebreaks( mnu_GameTip.local_hint[cnt], SDL_arraysize( mnu_GameTip.local_hint[cnt] ), 50 );
5069 
5070             //Keep track of how many we have total
5071             mnu_GameTip.local_count++;
5072         }
5073     }
5074 
5075     vfs_close( fileread );
5076 
5077     return mnu_GameTip.local_count > 0;
5078 }
5079 
5080 //--------------------------------------------------------------------------------------------
5081 // Implementation of the mnu_SlidyButton array
5082 //--------------------------------------------------------------------------------------------
mnu_SlidyButton_init(float lerp,const char * button_text[])5083 void mnu_SlidyButton_init( float lerp, const char *button_text[] )
5084 {
5085     int i;
5086 
5087     autoformat_init_slidy_buttons();
5088 
5089     // Figure out where to draw the buttons
5090     for ( i = 0; button_text[i][0] != 0; i++ )
5091     {
5092         buttonTop -= 35;
5093     }
5094 
5095     mnu_SlidyButtonState.lerp = lerp;
5096     mnu_SlidyButtonState.buttons = ( char** )button_text;
5097 }
5098 
5099 //--------------------------------------------------------------------------------------------
mnu_SlidyButton_update_all(float deltaTime)5100 void mnu_SlidyButton_update_all( float deltaTime )
5101 {
5102     mnu_SlidyButtonState.lerp += ( deltaTime * 1.5f );
5103 }
5104 
5105 //--------------------------------------------------------------------------------------------
mnu_SlidyButton_draw_all()5106 void mnu_SlidyButton_draw_all()
5107 {
5108     int i;
5109 
5110     for ( i = 0; mnu_SlidyButtonState.buttons[i][0] != 0; i++ )
5111     {
5112         int x = buttonLeft - ( 360 - i * 35 )  * mnu_SlidyButtonState.lerp;
5113         int y = buttonTop + ( i * 35 );
5114 
5115         ui_doButton( UI_Nothing, mnu_SlidyButtonState.buttons[i], NULL, x, y, 200, 30 );
5116     }
5117 }
5118 
5119 //--------------------------------------------------------------------------------------------
5120 //--------------------------------------------------------------------------------------------
ChoosePlayer_ctor(ChoosePlayer_element_t * ptr)5121 ChoosePlayer_element_t * ChoosePlayer_ctor( ChoosePlayer_element_t * ptr )
5122 {
5123     if ( NULL == ptr ) return ptr;
5124 
5125     ChoosePlayer_init( ptr );
5126 
5127     return ptr;
5128 }
5129 
5130 //--------------------------------------------------------------------------------------------
ChoosePlayer_dtor(ChoosePlayer_element_t * ptr)5131 ChoosePlayer_element_t * ChoosePlayer_dtor( ChoosePlayer_element_t * ptr )
5132 {
5133     if ( NULL == ptr ) return ptr;
5134 
5135     ChoosePlayer_dealloc( ptr );
5136 
5137     ChoosePlayer_init( ptr );
5138 
5139     return ptr;
5140 }
5141 
5142 //--------------------------------------------------------------------------------------------
ChoosePlayer_init(ChoosePlayer_element_t * ptr)5143 bool_t ChoosePlayer_init( ChoosePlayer_element_t * ptr )
5144 {
5145     if ( NULL == ptr ) return bfalse;
5146 
5147     memset( ptr, 0, sizeof( *ptr ) );
5148 
5149     ptr->cap_ref  = MAX_CAP;
5150     ptr->tx_ref   = INVALID_TX_TEXTURE;
5151     ptr->skin_ref = NO_SKIN_OVERRIDE;
5152 
5153     chop_definition_init( &( ptr->chop ) );
5154 
5155     return btrue;
5156 }
5157 
5158 //--------------------------------------------------------------------------------------------
ChoosePlayer_dealloc(ChoosePlayer_element_t * ptr)5159 bool_t ChoosePlayer_dealloc( ChoosePlayer_element_t * ptr )
5160 {
5161     // release all allocated resources
5162 
5163     if ( MAX_CAP != ptr->cap_ref )
5164     {
5165         release_one_cap( ptr->cap_ref );
5166     }
5167     ptr->cap_ref = MAX_CAP;
5168 
5169     if ( INVALID_TX_TEXTURE != ptr->tx_ref )
5170     {
5171         TxTexture_free_one( ptr->tx_ref );
5172     }
5173     ptr->tx_ref = INVALID_TX_TEXTURE;
5174 
5175     return btrue;
5176 }
5177 
5178 //--------------------------------------------------------------------------------------------
5179 //--------------------------------------------------------------------------------------------
ChoosePlayer_list_dealloc(ChoosePlayer_list_t * chooseplayer)5180 ChoosePlayer_list_t * ChoosePlayer_list_dealloc( ChoosePlayer_list_t * chooseplayer )
5181 {
5182     int i;
5183     ChoosePlayer_element_t * chooseplayer_ptr;
5184 
5185     if ( NULL == chooseplayer ) return chooseplayer;
5186 
5187     // release any data that we have accumulated
5188     for ( i = 0; i < chooseplayer->count; i++ )
5189     {
5190         chooseplayer_ptr = chooseplayer->lst + i;
5191 
5192         // don't release the first index, since we don't own that one
5193         if ( 0 == i )
5194         {
5195             ChoosePlayer_init( chooseplayer_ptr );
5196         }
5197         else
5198         {
5199             ChoosePlayer_dtor( chooseplayer_ptr );
5200         }
5201     }
5202     chooseplayer->count = 0;
5203 
5204     return chooseplayer;
5205 }
5206 
5207 //--------------------------------------------------------------------------------------------
5208 // Implementation of the mnu_loadplayer array
5209 //--------------------------------------------------------------------------------------------
LoadPlayer_list_init(LoadPlayer_list_t * lst)5210 egoboo_rv LoadPlayer_list_init( LoadPlayer_list_t * lst )
5211 {
5212     if ( NULL == lst ) return rv_error;
5213 
5214     // restart from nothing
5215     LoadPlayer_list_dealloc( lst );
5216 
5217     chop_data_init( &chop_mem );
5218 
5219     return rv_success;
5220 }
5221 
5222 //--------------------------------------------------------------------------------------------
LoadPlayer_list_import_one(LoadPlayer_list_t * lst,const char * foundfile)5223 egoboo_rv LoadPlayer_list_import_one( LoadPlayer_list_t * lst, const char * foundfile )
5224 {
5225     STRING  filename;
5226     int     slot;
5227 
5228     CAP_REF  icap = MAX_CAP;
5229     cap_t  * pcap = NULL;
5230 
5231     LoadPlayer_element_t * ptr = NULL;
5232     int                    idx = MAX_LOADPLAYER;
5233 
5234     // valid mnu_loadplayer list?
5235     if ( NULL == lst ) return rv_error;
5236 
5237     // is it a valid filename?
5238     if ( !VALID_CSTR( foundfile ) ) return rv_error;
5239 
5240     // does the directory exist?
5241     if ( lst->count >= MAX_LOADPLAYER ) return rv_fail;
5242 
5243     // does the directory exist?
5244     if ( !vfs_exists( foundfile ) ) return rv_fail;
5245 
5246     // offset the slots so that ChoosePlayer will have space to load the inventory objects
5247     slot = ( MAXIMPORTOBJECTS + 2 ) + lst->count;
5248 
5249     // try to load the character profile
5250     icap = load_one_character_profile_vfs( foundfile, slot, bfalse );
5251     if ( !LOADED_CAP( icap ) ) return rv_fail;
5252     pcap = CapStack.lst + icap;
5253 
5254     // get the next index
5255     idx = LoadPlayer_list_get_free( lst );
5256     if ( idx < 0 ) return rv_fail;
5257 
5258     // grab a valid mnu_loadplayer pointer
5259     ptr = lst->lst + idx;
5260 
5261     // set the player directory
5262     snprintf( ptr->dir, SDL_arraysize( ptr->dir ), "%s", str_convert_slash_net(( char* )foundfile, strlen( foundfile ) ) );
5263 
5264     // set the loaded character profile for this object
5265     ptr->cap_ref = icap;
5266 
5267     // read in the skin from "skin.txt"
5268     // We are no longer supporting skin.txt. Use the [SKIN] expansion, instead
5269     //snprintf( filename, SDL_arraysize( filename ), "%s/skin.txt", foundfile );
5270     //ptr->skin_ref = read_skin_vfs( filename );
5271 
5272     // get the skin from the [SKIN] expansion in the character profile
5273     ptr->skin_ref = pcap->skin_override % MAX_SKIN;
5274     ptr->skin_ref = CLIP( ptr->skin_ref, 0, MAX_SKIN - 1 );
5275 
5276     // don't load in the md2 at this time
5277     //snprintf( filename, SDL_arraysize(filename), "%s" SLASH_STR "tris.md2", foundfile );
5278     //md2_load_one( vfs_resolveReadFilename(filename), &(MadStack.lst[idx].md2_data) );
5279 
5280     // load in just the one icon
5281     snprintf( filename, SDL_arraysize( filename ), "%s/icon%d", foundfile, ptr->skin_ref );
5282     ptr->tx_ref = TxTexture_load_one_vfs( filename, ( TX_REF )INVALID_TX_TEXTURE, INVALID_KEY );
5283 
5284     // load the quest info from "quest.txt" so we can determine the valid modules
5285     snprintf( ptr->dir, SDL_arraysize( ptr->dir ), "%s", str_convert_slash_net(( char* )foundfile, strlen( foundfile ) ) );
5286     quest_log_download_vfs( ptr->quest_log, SDL_arraysize( ptr->quest_log ), ptr->dir );
5287 
5288     // load the chop data from "naming.txt" to generate the character name
5289     snprintf( filename, SDL_arraysize( filename ), "%s/naming.txt", foundfile );
5290     chop_load_vfs( &chop_mem, filename, &( ptr->chop ) );
5291 
5292     // generate the name from the chop
5293     snprintf( ptr->name, SDL_arraysize( ptr->name ), "%s", chop_create( &chop_mem, &( ptr->chop ) ) );
5294 
5295     return rv_success;
5296 }
5297 
5298 //--------------------------------------------------------------------------------------------
LoadPlayer_list_get_free(LoadPlayer_list_t * lst)5299 int LoadPlayer_list_get_free( LoadPlayer_list_t * lst )
5300 {
5301     int idx = -1;
5302 
5303     if ( NULL == lst ) return -1;
5304 
5305     // are there any loadplayers left?
5306     if ( lst->count >= MAX_LOADPLAYER ) return -1;
5307 
5308     // grab the next one
5309     idx = lst->count;
5310     lst->count++;
5311 
5312     return idx;
5313 }
5314 
5315 //--------------------------------------------------------------------------------------------
LoadPlayer_list_get_ptr(LoadPlayer_list_t * lst,int idx)5316 LoadPlayer_element_t * LoadPlayer_list_get_ptr( LoadPlayer_list_t * lst, int idx )
5317 {
5318 
5319     if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, idx ) ) return NULL;
5320 
5321     return lst->lst + idx;
5322 }
5323 
5324 //--------------------------------------------------------------------------------------------
LoadPlayer_list_dealloc(LoadPlayer_list_t * lst)5325 egoboo_rv LoadPlayer_list_dealloc( LoadPlayer_list_t * lst )
5326 {
5327     int i;
5328 
5329     if ( NULL == lst ) return rv_error;
5330 
5331     if ( 0 == lst->count ) return rv_success;
5332 
5333     lst->count = MIN( lst->count, MAX_LOADPLAYER );
5334     for ( i = 0; i < lst->count; i++ )
5335     {
5336         LoadPlayer_element_dtor( lst->lst + i );
5337     }
5338     lst->count = 0;
5339 
5340     return rv_success;
5341 }
5342 
5343 //--------------------------------------------------------------------------------------------
LoadPlayer_list_import_all(LoadPlayer_list_t * lst,const char * dirname,bool_t initialize)5344 egoboo_rv LoadPlayer_list_import_all( LoadPlayer_list_t * lst, const char *dirname, bool_t initialize )
5345 {
5346     /// @details ZZ@> This function figures out which players may be imported, and loads basic
5347     ///     data for each
5348 
5349     vfs_search_context_t * ctxt;
5350     const char *foundfile;
5351 
5352     if ( NULL == lst ) return rv_error;
5353 
5354     if ( initialize )
5355     {
5356         LoadPlayer_list_init( lst );
5357     };
5358 
5359     // Search for all objects
5360     ctxt = vfs_findFirst( dirname, "obj", VFS_SEARCH_DIR );
5361     foundfile = vfs_search_context_get_current( ctxt );
5362 
5363     while ( NULL != ctxt && VALID_CSTR( foundfile ) && lst->count < MAX_LOADPLAYER )
5364     {
5365         LoadPlayer_list_import_one( lst, foundfile );
5366 
5367         ctxt = vfs_findNext( &ctxt );
5368         foundfile = vfs_search_context_get_current( ctxt );
5369     }
5370     vfs_findClose( &ctxt );
5371 
5372     return rv_success;
5373 }
5374 
5375 //--------------------------------------------------------------------------------------------
LoadPlayer_list_from_players(LoadPlayer_list_t * lst)5376 egoboo_rv LoadPlayer_list_from_players( LoadPlayer_list_t * lst )
5377 {
5378     int ipla;
5379     chr_t * pchr;
5380     pro_t * ppro;
5381     player_t * ppla;
5382 
5383     int                    lp_idx;
5384     LoadPlayer_element_t * lp_ptr;
5385 
5386     if ( NULL == lst || 0 != lst->count ) return rv_error;
5387 
5388     for ( ipla = 0; ipla < MAX_PLAYER; ipla++ )
5389     {
5390         ppla = PlaStack.lst + ipla;
5391         if ( !ppla->valid ) continue;
5392 
5393         if ( !INGAME_CHR( ppla->index ) ) continue;
5394         pchr = ChrList.lst + ppla->index;
5395 
5396         if ( !LOADED_PRO( pchr->profile_ref ) )continue;
5397         ppro = ProList.lst + pchr->profile_ref;
5398 
5399         // grab a free LoadPlayer_element_t
5400         lp_idx = LoadPlayer_list_get_free( lst );
5401         if ( lp_idx < 0 ) break;
5402 
5403         lp_ptr = lst->lst + lp_idx;
5404 
5405         // fill up the data from the player's info
5406         strncpy( lp_ptr->name, pchr->Name, SDL_arraysize( lp_ptr->name ) );
5407         strncpy( lp_ptr->dir, pchr->obj_base._name, SDL_arraysize( lp_ptr->name ) );
5408 
5409         lp_ptr->cap_ref  = pro_get_icap( pchr->profile_ref );
5410         lp_ptr->skin_ref = pchr->skin;
5411         lp_ptr->tx_ref   = pchr->inst.texture;
5412 
5413         memmove( lp_ptr->quest_log, ppla->quest_log, sizeof( lp_ptr->quest_log ) );
5414         memmove( &( lp_ptr->chop ), &( ppro->chop ), sizeof( lp_ptr->chop ) );
5415     }
5416 
5417     return ( lst->count > 0 ) ? rv_success : rv_fail;
5418 }
5419 
5420 //--------------------------------------------------------------------------------------------
5421 //--------------------------------------------------------------------------------------------
LoadPlayer_element_ctor(LoadPlayer_element_t * ptr)5422 LoadPlayer_element_t * LoadPlayer_element_ctor( LoadPlayer_element_t * ptr )
5423 {
5424     if ( NULL == ptr ) return ptr;
5425 
5426     LoadPlayer_element_init( ptr );
5427 
5428     return ptr;
5429 }
5430 
5431 //--------------------------------------------------------------------------------------------
LoadPlayer_element_dtor(LoadPlayer_element_t * ptr)5432 LoadPlayer_element_t * LoadPlayer_element_dtor( LoadPlayer_element_t * ptr )
5433 {
5434     if ( NULL == ptr ) return ptr;
5435 
5436     LoadPlayer_element_dealloc( ptr );
5437     LoadPlayer_element_init( ptr );
5438 
5439     return ptr;
5440 }
5441 
5442 //--------------------------------------------------------------------------------------------
LoadPlayer_element_dealloc(LoadPlayer_element_t * ptr)5443 bool_t LoadPlayer_element_dealloc( LoadPlayer_element_t * ptr )
5444 {
5445     if ( NULL == ptr ) return bfalse;
5446 
5447     // release the cap
5448     if ( MAX_CAP != ptr->cap_ref )
5449     {
5450         release_one_cap( ptr->cap_ref );
5451     }
5452     ptr->cap_ref = MAX_CAP;
5453 
5454     // release the texture
5455     if ( INVALID_TX_TEXTURE != ptr->tx_ref )
5456     {
5457         TxTexture_free_one( ptr->tx_ref );
5458     }
5459     ptr->tx_ref = INVALID_TX_TEXTURE;
5460 
5461     return btrue;
5462 }
5463 
5464 //--------------------------------------------------------------------------------------------
LoadPlayer_element_init(LoadPlayer_element_t * ptr)5465 bool_t LoadPlayer_element_init( LoadPlayer_element_t * ptr )
5466 {
5467     if ( NULL == ptr ) return bfalse;
5468 
5469     memset( ptr, 0, sizeof( *ptr ) );
5470 
5471     // set the non-zero, non-null values
5472     ptr->cap_ref = MAX_CAP;
5473     ptr->tx_ref = INVALID_TX_TEXTURE;
5474 
5475     idsz_map_init( ptr->quest_log, MAX_IDSZ_MAP_SIZE );
5476     chop_definition_init( &( ptr->chop ) );
5477 
5478     return btrue;
5479 }
5480 
5481 //--------------------------------------------------------------------------------------------
5482 //--------------------------------------------------------------------------------------------
mnu_set_selected_list(LoadPlayer_list_t * dst,LoadPlayer_list_t * src,SelectedPlayer_list_t * sp_lst)5483 egoboo_rv mnu_set_selected_list( LoadPlayer_list_t * dst, LoadPlayer_list_t * src, SelectedPlayer_list_t * sp_lst )
5484 {
5485     int                        src_idx = -1;
5486     LoadPlayer_element_t     * src_ptr = NULL;
5487     LoadPlayer_element_t     * dst_ptr = NULL;
5488 
5489     int                        selectedplayer_idx;
5490     SelectedPlayer_element_t * selectedplayer_ptr = NULL;
5491 
5492     if ( NULL == src || NULL == dst || NULL == sp_lst ) return rv_error;
5493 
5494     // blank out any existing data
5495     LoadPlayer_list_init( dst );
5496 
5497     if ( 0 == src->count || 0 == sp_lst->count ) return rv_success;
5498 
5499     // loop through the selected players and store all the valid data in the list of imported players
5500     for ( selectedplayer_idx = 0; selectedplayer_idx < sp_lst->count; selectedplayer_idx++ )
5501     {
5502         // grab a pointer to the selectedplayer data
5503         selectedplayer_ptr = sp_lst->lst + selectedplayer_idx;
5504 
5505         // does the loadplayer exist?
5506         src_idx = selectedplayer_ptr->player;
5507         if ( !VALID_LOADPLAYER_IDX( *src, src_idx ) ) continue;
5508 
5509         // grab the loadplayer info
5510         src_ptr = src->lst + src_idx;
5511 
5512         // get a new import data pointer
5513         dst_ptr = dst->lst + dst->count;
5514         dst->count++;
5515 
5516         // copy the data over
5517         memcpy( dst_ptr, src_ptr, sizeof( *dst_ptr ) );
5518     }
5519 
5520     return ( dst->count > 0 ) ? rv_success : rv_fail;
5521 }
5522 
5523 //--------------------------------------------------------------------------------------------
mnu_set_local_import_list(Import_list_t * imp_lst,SelectedPlayer_list_t * sp_lst)5524 egoboo_rv mnu_set_local_import_list( Import_list_t * imp_lst, SelectedPlayer_list_t * sp_lst )
5525 {
5526     int                import_idx;
5527     Import_element_t * import_ptr = NULL;
5528 
5529     int                        loadplayer_idx = -1;
5530     LoadPlayer_element_t     * loadplayer_ptr = NULL;
5531 
5532     int                        selectedplayer_idx;
5533     SelectedPlayer_element_t * selectedplayer_ptr = NULL;
5534 
5535     if ( NULL == imp_lst || NULL == sp_lst ) return rv_error;
5536 
5537     // blank out any existing data
5538     Import_list_init( imp_lst );
5539 
5540     // loop through the selected players and store all the valid data in the list of imported players
5541     for ( selectedplayer_idx = 0; selectedplayer_idx < sp_lst->count; selectedplayer_idx++ )
5542     {
5543         // grab a pointer to the selectedplayer data
5544         selectedplayer_ptr = sp_lst->lst + selectedplayer_idx;
5545 
5546         // does the loadplayer exist?
5547         loadplayer_idx = selectedplayer_ptr->player;
5548         if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, loadplayer_idx ) ) continue;
5549 
5550         // grab the loadplayer info
5551         loadplayer_ptr = mnu_loadplayer.lst + loadplayer_idx;
5552 
5553         // get a new import data pointer
5554         import_idx = imp_lst->count;
5555         import_ptr = imp_lst->lst + imp_lst->count;
5556         imp_lst->count++;
5557 
5558         // set the import info
5559         import_ptr->bits   = selectedplayer_ptr->input;
5560         import_ptr->slot   = selectedplayer_idx * MAXIMPORTPERPLAYER;
5561         import_ptr->player = selectedplayer_idx;
5562 
5563         strncpy( import_ptr->srcDir, loadplayer_ptr->dir, SDL_arraysize( import_ptr->srcDir ) );
5564         import_ptr->dstDir[0] = CSTR_END;
5565     }
5566 
5567     return ( imp_lst->count > 0 ) ? rv_success : rv_fail;
5568 }
5569 
5570 //--------------------------------------------------------------------------------------------
SelectedPlayer_element_init(SelectedPlayer_element_t * ptr)5571 egoboo_rv SelectedPlayer_element_init( SelectedPlayer_element_t * ptr )
5572 {
5573     if ( NULL == ptr ) return rv_error;
5574 
5575     memset( ptr, 0, sizeof( *ptr ) );
5576 
5577     // the non-zero, non-null values
5578     ptr->player = MAX_PLAYER;
5579 
5580     return rv_success;
5581 }
5582 
5583 //--------------------------------------------------------------------------------------------
5584 // implementation of SelectedPlayer_list_t
5585 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_init(SelectedPlayer_list_t * sp_lst)5586 egoboo_rv SelectedPlayer_list_init( SelectedPlayer_list_t * sp_lst )
5587 {
5588     int cnt;
5589 
5590     if ( NULL == sp_lst ) return rv_error;
5591 
5592     for ( cnt = 0; cnt < MAX_PLAYER; cnt++ )
5593     {
5594         SelectedPlayer_element_init( sp_lst->lst + cnt );
5595     }
5596     sp_lst->count = 0;
5597 
5598     return rv_success;
5599 }
5600 
5601 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_check_loadplayer(SelectedPlayer_list_t * sp_lst,int loadplayer_idx)5602 bool_t SelectedPlayer_list_check_loadplayer( SelectedPlayer_list_t * sp_lst, int loadplayer_idx )
5603 {
5604     int i;
5605 
5606     if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, loadplayer_idx ) ) return bfalse;
5607 
5608     for ( i = 0; i < MAX_PLAYER && i < sp_lst->count; i++ )
5609     {
5610         if ( sp_lst->lst[i].player == loadplayer_idx ) return btrue;
5611     }
5612 
5613     return bfalse;
5614 }
5615 
5616 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_index_from_loadplayer(SelectedPlayer_list_t * sp_lst,int loadplayer_idx)5617 int SelectedPlayer_list_index_from_loadplayer( SelectedPlayer_list_t * sp_lst,  int loadplayer_idx )
5618 {
5619     int cnt, selected_index;
5620 
5621     if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, loadplayer_idx ) ) return INVALID_PLAYER;
5622 
5623     selected_index = INVALID_PLAYER;
5624     for ( cnt = 0; cnt < MAX_PLAYER && cnt < sp_lst->count; cnt++ )
5625     {
5626         if ( sp_lst->lst[ cnt ].player == loadplayer_idx )
5627         {
5628             selected_index = cnt;
5629             break;
5630         }
5631     }
5632 
5633     return selected_index;
5634 }
5635 
5636 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_add(SelectedPlayer_list_t * sp_lst,int loadplayer_idx)5637 bool_t SelectedPlayer_list_add( SelectedPlayer_list_t * sp_lst, int loadplayer_idx )
5638 {
5639     if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, loadplayer_idx ) || sp_lst->count >= MAX_PLAYER ) return bfalse;
5640     if ( SelectedPlayer_list_check_loadplayer( sp_lst,  loadplayer_idx ) ) return bfalse;
5641 
5642     sp_lst->lst[sp_lst->count].player = loadplayer_idx;
5643     sp_lst->lst[sp_lst->count].input  = INPUT_BITS_NONE;
5644     sp_lst->count++;
5645 
5646     return btrue;
5647 }
5648 
5649 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_remove(SelectedPlayer_list_t * sp_lst,int loadplayer_idx)5650 bool_t SelectedPlayer_list_remove( SelectedPlayer_list_t * sp_lst, int loadplayer_idx )
5651 {
5652     int i;
5653     bool_t found = bfalse;
5654 
5655     if ( !VALID_LOADPLAYER_IDX( mnu_loadplayer, loadplayer_idx ) || sp_lst->count <= 0 ) return bfalse;
5656 
5657     if ( 1 == sp_lst->count )
5658     {
5659         if ( sp_lst->lst[0].player == loadplayer_idx )
5660         {
5661             sp_lst->count = 0;
5662         }
5663     }
5664     else
5665     {
5666         for ( i = 0; i < MAX_PLAYER && i < sp_lst->count; i++ )
5667         {
5668             if ( sp_lst->lst[i].player == loadplayer_idx )
5669             {
5670                 found = btrue;
5671                 break;
5672             }
5673         }
5674 
5675         if ( found )
5676         {
5677             i++;
5678             for ( /* nothing */; i < MAX_PLAYER && i < sp_lst->count; i++ )
5679             {
5680                 sp_lst->lst[i-1].player = sp_lst->lst[i].player;
5681                 sp_lst->lst[i-1].input  = sp_lst->lst[i].input;
5682             }
5683 
5684             sp_lst->count--;
5685         }
5686     };
5687 
5688     return found;
5689 }
5690 
5691 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_add_input(SelectedPlayer_list_t * sp_lst,int loadplayer_idx,BIT_FIELD input_bits)5692 bool_t SelectedPlayer_list_add_input( SelectedPlayer_list_t * sp_lst, int loadplayer_idx, BIT_FIELD input_bits )
5693 {
5694     int i;
5695     bool_t done, retval = bfalse;
5696 
5697     int selected_index = -1;
5698 
5699     for ( i = 0; i < sp_lst->count; i++ )
5700     {
5701         if ( sp_lst->lst[i].player == loadplayer_idx )
5702         {
5703             selected_index = i;
5704             break;
5705         }
5706     }
5707 
5708     if ( -1 == selected_index )
5709     {
5710         SelectedPlayer_list_add( sp_lst,  loadplayer_idx );
5711     }
5712 
5713     if ( selected_index >= 0 && selected_index < sp_lst->count )
5714     {
5715         for ( i = 0; i < sp_lst->count; i++ )
5716         {
5717             if ( i == selected_index )
5718             {
5719                 // add in the selected bits for the selected loadplayer_idx
5720                 SET_BIT( sp_lst->lst[i].input, input_bits );
5721                 retval = btrue;
5722             }
5723             else
5724             {
5725                 // remove the selectd bits from all other players
5726                 UNSET_BIT( sp_lst->lst[i].input, input_bits );
5727             }
5728         }
5729     }
5730 
5731     // Do the tricky part of removing all players with invalid inputs from the list
5732     // It is tricky because removing a loadplayer_idx changes the value of the loop control
5733     // value sp_lst->count within the loop.
5734     done = bfalse;
5735     while ( !done )
5736     {
5737         // assume the best
5738         done = btrue;
5739 
5740         for ( i = 0; i < sp_lst->count; i++ )
5741         {
5742             if ( INPUT_BITS_NONE == sp_lst->lst[i].input )
5743             {
5744                 // we found one
5745                 done = bfalse;
5746                 SelectedPlayer_list_remove( sp_lst,  sp_lst->lst[i].player );
5747             }
5748         }
5749     }
5750 
5751     return retval;
5752 }
5753 
5754 //--------------------------------------------------------------------------------------------
SelectedPlayer_list_remove_input(SelectedPlayer_list_t * sp_lst,int loadplayer_idx,Uint32 input_bits)5755 bool_t SelectedPlayer_list_remove_input( SelectedPlayer_list_t * sp_lst, int loadplayer_idx, Uint32 input_bits )
5756 {
5757     int i;
5758     bool_t retval = bfalse;
5759 
5760     for ( i = 0; i < MAX_PLAYER && i < sp_lst->count; i++ )
5761     {
5762         if ( sp_lst->lst[i].player == loadplayer_idx )
5763         {
5764             UNSET_BIT( sp_lst->lst[i].input, input_bits );
5765 
5766             // This part is not so tricky as in SelectedPlayer_list_add_input.
5767             // Even though we are modding the loop control variable, it is never
5768             // tested in the loop because we are using the break command to
5769             // break out of the loop immediately
5770 
5771             if ( INPUT_BITS_NONE == sp_lst->lst[i].input )
5772             {
5773                 SelectedPlayer_list_remove( sp_lst,  loadplayer_idx );
5774             }
5775 
5776             retval = btrue;
5777 
5778             break;
5779         }
5780     }
5781 
5782     return retval;
5783 }
5784