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 egoboo.c
21 /// @brief Code for the main program process
22 /// @details
23 
24 #define DECLARE_GLOBALS
25 
26 #include "log.h"
27 #include "clock.h"
28 #include "system.h"
29 #include "graphic.h"
30 #include "network.h"
31 #include "sound.h"
32 #include "profile.inl"
33 #include "ui.h"
34 #include "font_bmp.h"
35 #include "input.h"
36 #include "game.h"
37 #include "menu.h"
38 
39 #include "char.inl"
40 #include "particle.inl"
41 #include "enchant.inl"
42 #include "collision.h"
43 
44 #include "file_formats/scancode_file.h"
45 #include "file_formats/treasure_table_file.h"
46 #include "SDL_extensions.h"
47 
48 #include "egoboo_fileutil.h"
49 #include "egoboo_setup.h"
50 #include "egoboo_vfs.h"
51 #include "egoboo_console.h"
52 #include "egoboo_strutil.h"
53 #include "egoboo.h"
54 
55 #include <SDL.h>
56 #include <SDL_image.h>
57 
58 //--------------------------------------------------------------------------------------------
59 //--------------------------------------------------------------------------------------------
60 
61 static int do_ego_proc_begin( ego_process_t * eproc );
62 static int do_ego_proc_running( ego_process_t * eproc );
63 static int do_ego_proc_leaving( ego_process_t * eproc );
64 static int do_ego_proc_run( ego_process_t * eproc, double frameDuration );
65 
66 static void memory_cleanUp( void );
67 static int  ego_init_SDL();
68 static void console_begin();
69 static void console_end();
70 
71 static void object_systems_begin( void );
72 static void object_systems_end( void );
73 
74 static void _quit_game( ego_process_t * pgame );
75 
76 static ego_process_t * ego_process_init( ego_process_t * eproc, int argc, char **argv );
77 
78 //--------------------------------------------------------------------------------------------
79 //--------------------------------------------------------------------------------------------
80 static ClockState_t    * _gclock = NULL;
81 static ego_process_t     _eproc;
82 
83 static bool_t  screenshot_keyready  = btrue;
84 
85 static bool_t _sdl_atexit_registered    = bfalse;
86 static bool_t _sdl_initialized_base     = bfalse;
87 
88 static void * _top_con = NULL;
89 
90 //--------------------------------------------------------------------------------------------
91 //--------------------------------------------------------------------------------------------
92 ego_process_t     * EProc   = &_eproc;
93 
94 //--------------------------------------------------------------------------------------------
95 //--------------------------------------------------------------------------------------------
do_ego_proc_begin(ego_process_t * eproc)96 int do_ego_proc_begin( ego_process_t * eproc )
97 {
98     // initialize the virtual filesystem first
99     vfs_init();
100     egoboo_setup_vfs_paths();
101 
102     // Initialize logging next, so that we can use it everywhere.
103     log_init( vfs_resolveWriteFilename( "/debug/log.txt" ) );
104     log_setLoggingLevel( 3 );
105 
106     // start initializing the various subsystems
107     log_message( "Starting Egoboo " VERSION " ...\n" );
108     log_info( "PhysFS file system version %s has been initialized...\n", vfs_getVersion() );
109 
110     sys_initialize();
111     clk_init();
112     _gclock = clk_create( "global clock", -1 );
113 
114     // read the "setup.txt" file
115     setup_read_vfs();
116 
117     // download the "setup.txt" values into the cfg struct
118     setup_download( &cfg );
119 
120     // do basic system initialization
121     ego_init_SDL();
122     gfx_system_begin();
123     console_begin();
124     net_initialize();
125 
126     log_info( "Initializing SDL_Image version %d.%d.%d... ", SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL );
127     GLSetup_SupportedFormats();
128 
129     // read all the scantags
130     scantag_read_all_vfs( "mp_data/scancode.txt" );
131 
132     if ( fs_ensureUserFile( "controls.txt", btrue ) )
133     {
134         input_settings_load_vfs( "/controls.txt" );
135     }
136 
137     // synchronoze the config values with the various game subsystems
138     // do this acter the ego_init_SDL() and ogl_init() in case the config values are clamped
139     // to valid values
140     setup_synch( &cfg );
141 
142     // initialize the sound system
143     sound_initialize();
144     load_all_music_sounds_vfs();
145 
146     // initialize the random treasure system
147     init_random_treasure_tables_vfs( "mp_data/randomtreasure.txt" );
148 
149     // make sure that a bunch of stuff gets initialized properly
150     object_systems_begin();
151     game_module_init( PMod );
152     mesh_ctor( PMesh );
153     init_all_graphics();
154     profile_system_begin();
155 
156     // setup the menu system's gui
157     ui_begin( vfs_resolveReadFilename( "mp_data/Bo_Chen.ttf" ), 24 );
158     font_bmp_load_vfs( "mp_data/font_new_shadow", "mp_data/font.txt" );  // must be done after init_all_graphics()
159 
160     // clear out the import and remote directories
161     vfs_empty_temp_directories();
162 
163     // register the memory_cleanUp function to automatically run whenever the program exits
164     atexit( memory_cleanUp );
165 
166     // initialize the game process (not active)
167     game_process_init( GProc );
168 
169     // initialize the menu process (active)
170     menu_process_init( MProc );
171     process_start( PROC_PBASE( MProc ) );
172 
173     // Initialize the process
174     process_start( PROC_PBASE( eproc ) );
175 
176     return 1;
177 }
178 
179 //--------------------------------------------------------------------------------------------
do_ego_proc_running(ego_process_t * eproc)180 int do_ego_proc_running( ego_process_t * eproc )
181 {
182     bool_t menu_valid, game_valid;
183 
184     if ( !process_validate( PROC_PBASE( eproc ) ) ) return -1;
185 
186     eproc->was_active  = eproc->base.valid;
187 
188     menu_valid = process_validate( PROC_PBASE( MProc ) );
189     game_valid = process_validate( PROC_PBASE( GProc ) );
190     if ( !menu_valid && !game_valid )
191     {
192         process_kill( PROC_PBASE( eproc ) );
193         return 1;
194     }
195 
196     if ( eproc->base.paused ) return 0;
197 
198     if ( process_running( PROC_PBASE( MProc ) ) )
199     {
200         // menu settings
201         SDL_WM_GrabInput( SDL_GRAB_OFF );
202         SDL_ShowCursor( SDL_ENABLE );
203     }
204     else
205     {
206         // in-game settings
207         SDL_ShowCursor( cfg.hide_mouse ? SDL_DISABLE : SDL_ENABLE );
208         SDL_WM_GrabInput( cfg.grab_mouse ? SDL_GRAB_ON : SDL_GRAB_OFF );
209     }
210 
211     // Clock updates each frame
212     clk_frameStep( _gclock );
213     eproc->frameDuration = clk_getFrameDuration( _gclock );
214 
215     // read the input values
216     input_read();
217 
218     if ( pickedmodule_ready && !process_running( PROC_PBASE( MProc ) ) )
219     {
220         // a new module has been picked
221 
222         // reset the flag
223         pickedmodule_ready = bfalse;
224 
225         // start the game process
226         process_start( PROC_PBASE( GProc ) );
227     }
228 
229     // Test the panic button
230     if ( SDLKEYDOWN( SDLK_q ) && SDLKEYDOWN( SDLK_LCTRL ) )
231     {
232         // terminate the program
233         process_kill( PROC_PBASE( eproc ) );
234     }
235 
236     if ( cfg.dev_mode )
237     {
238         if ( !SDLKEYDOWN( SDLK_F10 ) )
239         {
240             single_frame_keyready = btrue;
241         }
242         else if ( single_frame_keyready && SDLKEYDOWN( SDLK_F10 ) )
243         {
244             if ( !single_frame_mode )
245             {
246                 single_frame_mode = btrue;
247             }
248 
249             // request one update and one frame
250             single_frame_requested  = btrue;
251             single_update_requested = btrue;
252             single_frame_keyready   = bfalse;
253         }
254 
255     }
256 
257     // Check for screenshots
258     if ( !SDLKEYDOWN( SDLK_F11 ) )
259     {
260         screenshot_keyready = btrue;
261     }
262     else if ( screenshot_keyready && SDLKEYDOWN( SDLK_F11 ) && keyb.on )
263     {
264         screenshot_keyready = bfalse;
265         screenshot_requested = btrue;
266     }
267 
268     if ( cfg.dev_mode && SDLKEYDOWN( SDLK_F9 ) && NULL != PMod && PMod->active )
269     {
270         // super secret "I win" button
271         //PMod->beat        = btrue;
272         //PMod->exportvalid = btrue;
273 
274         CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
275         {
276             if ( !VALID_PLA( pchr->is_which_player ) )
277             {
278                 kill_character( cnt, ( CHR_REF )511, bfalse );
279             }
280         }
281         CHR_END_LOOP();
282     }
283 
284     // handle an escape by passing it on to all active sub-processes
285     if ( eproc->escape_requested )
286     {
287         eproc->escape_requested = bfalse;
288 
289         // use the escape key to get out of single frame mode
290         single_frame_mode = bfalse;
291 
292         if ( process_running( PROC_PBASE( GProc ) ) )
293         {
294             GProc->escape_requested = btrue;
295         }
296 
297         if ( process_running( PROC_PBASE( MProc ) ) )
298         {
299             MProc->escape_requested = btrue;
300         }
301     }
302 
303     // run the sub-processes
304     do_game_proc_run( GProc, EProc->frameDuration );
305     do_menu_proc_run( MProc, EProc->frameDuration );
306 
307     // a heads up display that can be used to debug values that are used by both the menu and the game
308     // do_game_hud();
309 
310     return 0;
311 }
312 
313 //--------------------------------------------------------------------------------------------
do_ego_proc_leaving(ego_process_t * eproc)314 int do_ego_proc_leaving( ego_process_t * eproc )
315 {
316     if ( !process_validate( PROC_PBASE( eproc ) ) ) return -1;
317 
318     // make sure that the
319     if ( !GProc->base.terminated )
320     {
321         do_game_proc_run( GProc, eproc->frameDuration );
322     }
323 
324     if ( !MProc->base.terminated )
325     {
326         do_menu_proc_run( MProc, eproc->frameDuration );
327     }
328 
329     if ( GProc->base.terminated && MProc->base.terminated )
330     {
331         process_terminate( PROC_PBASE( eproc ) );
332     }
333 
334     if ( eproc->base.terminated )
335     {
336         // hopefully this will only happen once
337         object_systems_end();
338         clk_destroy( &_gclock );
339         console_end();
340         ui_end();
341         gfx_system_end();
342         egoboo_clear_vfs_paths();
343     }
344 
345     return eproc->base.terminated ? 0 : 1;
346 }
347 
348 //--------------------------------------------------------------------------------------------
do_ego_proc_run(ego_process_t * eproc,double frameDuration)349 int do_ego_proc_run( ego_process_t * eproc, double frameDuration )
350 {
351     int result = 0, proc_result = 0;
352 
353     if ( !process_validate( PROC_PBASE( eproc ) ) ) return -1;
354     eproc->base.dtime = frameDuration;
355 
356     if ( !eproc->base.paused ) return 0;
357 
358     if ( eproc->base.killme )
359     {
360         eproc->base.state = proc_leaving;
361     }
362 
363     switch ( eproc->base.state )
364     {
365         case proc_begin:
366             proc_result = do_ego_proc_begin( eproc );
367 
368             if ( 1 == proc_result )
369             {
370                 eproc->base.state = proc_entering;
371             }
372             break;
373 
374         case proc_entering:
375             // proc_result = do_ego_proc_entering( eproc );
376 
377             eproc->base.state = proc_running;
378             break;
379 
380         case proc_running:
381             proc_result = do_ego_proc_running( eproc );
382 
383             if ( 1 == proc_result )
384             {
385                 eproc->base.state = proc_leaving;
386             }
387             break;
388 
389         case proc_leaving:
390             proc_result = do_ego_proc_leaving( eproc );
391 
392             if ( 1 == proc_result )
393             {
394                 eproc->base.state  = proc_finish;
395                 eproc->base.killme = bfalse;
396             }
397             break;
398 
399         case proc_finish:
400             process_terminate( PROC_PBASE( eproc ) );
401             break;
402     }
403 
404     return result;
405 }
406 
407 //--------------------------------------------------------------------------------------------
408 //--------------------------------------------------------------------------------------------
SDL_main(int argc,char ** argv)409 int SDL_main( int argc, char **argv )
410 {
411     /// @details ZZ@> This is where the program starts and all the high level stuff happens
412 
413     int result = 0;
414 
415     // initialize the process
416     ego_process_init( EProc, argc, argv );
417 
418     // turn on all basic services
419     do_ego_proc_begin( EProc );
420 
421     // run the processes
422     request_clear_screen();
423     while ( !EProc->base.killme && !EProc->base.terminated )
424     {
425         // put a throttle on the ego process
426         EProc->ticks_now = SDL_GetTicks();
427         if ( EProc->ticks_now < EProc->ticks_next ) continue;
428 
429         // update the timer: 10ms delay between loops
430         EProc->ticks_next = EProc->ticks_now + 10;
431 
432         // clear the screen if needed
433         do_clear_screen();
434 
435         do_ego_proc_running( EProc );
436 
437         // flip the graphics page if need be
438         do_flip_pages();
439 
440         // let the OS breathe. It may delay as long as 10ms
441         if ( update_lag < 3 )
442         {
443             SDL_Delay( 1 );
444         }
445     }
446 
447     // terminate the game and menu processes
448     process_kill( PROC_PBASE( GProc ) );
449     process_kill( PROC_PBASE( MProc ) );
450     while ( !EProc->base.terminated )
451     {
452         result = do_ego_proc_leaving( EProc );
453     }
454 
455     return result;
456 }
457 
458 //--------------------------------------------------------------------------------------------
memory_cleanUp(void)459 void memory_cleanUp( void )
460 {
461     /// @details ZF@> This function releases all loaded things in memory and cleans up everything properly
462 
463     log_info( "memory_cleanUp() - Attempting to clean up loaded things in memory... " );
464 
465     // quit any existing game
466     _quit_game( EProc );
467 
468     // synchronoze the config values with the various game subsystems
469     setup_synch( &cfg );
470 
471     // quit the setup system, making sure that the setup file is written
472     setup_upload( &cfg );
473     setup_write();
474     setup_quit();
475 
476     // delete all the graphics allocated by SDL and OpenGL
477     delete_all_graphics();
478 
479     // make sure that the current control configuration is written
480     input_settings_save_vfs( "controls.txt" );
481 
482     // shut down the ui
483     ui_end();
484 
485     // shut down the network
486     if ( PNet->on )
487     {
488         net_shutDown();
489     }
490 
491     // shut down the clock services
492     clk_destroy( &_gclock );
493     clk_shutdown();
494 
495     // deallocate any dynamically allocated collision memory
496     collision_system_end();
497 
498     // deallocate any dynamically allocated scripting memory
499     scripting_system_end();
500 
501     // deallocate all dynamically allocated memory for characters, particles, and enchants
502     object_systems_end();
503 
504     // clean up any remaining models that might have dynamic data
505     MadList_dtor();
506 
507     log_message( "Success!\n" );
508     log_info( "Exiting Egoboo " VERSION " the good way...\n" );
509 
510     // shut down the log services
511     log_shutdown();
512 }
513 
514 //--------------------------------------------------------------------------------------------
ego_init_SDL()515 int ego_init_SDL()
516 {
517     ego_init_SDL_base();
518     input_init();
519 
520     return _sdl_initialized_base;
521 }
522 
523 //--------------------------------------------------------------------------------------------
ego_init_SDL_base()524 void ego_init_SDL_base()
525 {
526     if ( _sdl_initialized_base ) return;
527 
528     log_info( "Initializing SDL version %d.%d.%d... ", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL );
529     if ( SDL_Init( 0 ) < 0 )
530     {
531         log_message( "Failure!\n" );
532         log_error( "Unable to initialize SDL: %s\n", SDL_GetError() );
533     }
534     else
535     {
536         log_message( "Success!\n" );
537     }
538 
539     if ( !_sdl_atexit_registered )
540     {
541         atexit( SDL_Quit );
542         _sdl_atexit_registered = bfalse;
543     }
544 
545     log_info( "Intializing SDL Timing Services... " );
546     if ( SDL_InitSubSystem( SDL_INIT_TIMER ) < 0 )
547     {
548         log_message( "Failed!\n" );
549         log_warning( "SDL error == \"%s\"\n", SDL_GetError() );
550     }
551     else
552     {
553         log_message( "Success!\n" );
554     }
555 
556     log_info( "Intializing SDL Event Threading... " );
557     if ( SDL_InitSubSystem( SDL_INIT_EVENTTHREAD ) < 0 )
558     {
559         log_message( "Failed!\n" );
560         log_warning( "SDL error == \"%s\"\n", SDL_GetError() );
561     }
562     else
563     {
564         log_message( "Success!\n" );
565     }
566 
567     _sdl_initialized_base = btrue;
568 }
569 
570 //--------------------------------------------------------------------------------------------
console_begin()571 void console_begin()
572 {
573     /// @details BB@> initialize the console. This must happen after the screen has been defines,
574     ///     otherwise sdl_scr.x == sdl_scr.y == 0 and the screen will be defined to
575     ///     have no area...
576 
577     SDL_Rect blah = {0, 0, sdl_scr.x, sdl_scr.y / 4};
578 
579 #if defined(USE_LUA_CONSOLE)
580     _top_con = lua_console_create( NULL, blah );
581 #else
582     // without a callback, this console just dumps the input and generates no output
583     _top_con = egoboo_console_create( NULL, blah, NULL, NULL );
584 #endif
585 }
586 
587 //--------------------------------------------------------------------------------------------
console_end()588 void console_end()
589 {
590     /// @details BB@> de-initialize the top console
591 
592 #if defined(USE_LUA_CONSOLE)
593     {
594         lua_console_t * ptmp = ( lua_console_t* )_top_con;
595         lua_console_destroy( &ptmp );
596     }
597 #else
598     // without a callback, this console just dumps the input and generates no output
599     {
600         egoboo_console_t * ptmp = ( egoboo_console_t* )_top_con;
601         egoboo_console_destroy( &ptmp, SDL_TRUE );
602     }
603 #endif
604 
605     _top_con = NULL;
606 }
607 
608 //--------------------------------------------------------------------------------------------
object_systems_begin(void)609 void object_systems_begin( void )
610 {
611     /// @details BB@> initialize all the object systems
612 
613     particle_system_begin();
614     enchant_system_begin();
615     character_system_begin();
616 }
617 
618 //--------------------------------------------------------------------------------------------
object_systems_end(void)619 void object_systems_end( void )
620 {
621     /// @details BB@> quit all the object systems
622 
623     particle_system_end();
624     enchant_system_end();
625     character_system_end();
626 }
627 
628 //--------------------------------------------------------------------------------------------
_quit_game(ego_process_t * pgame)629 void _quit_game( ego_process_t * pgame )
630 {
631     /// @details ZZ@> This function exits the game entirely
632 
633     if ( process_running( PROC_PBASE( pgame ) ) )
634     {
635         game_quit_module();
636     }
637 
638     // tell the game to kill itself
639     process_kill( PROC_PBASE( pgame ) );
640 
641     // clear out the import and remote directories
642     vfs_empty_temp_directories();
643 }
644 
645 //--------------------------------------------------------------------------------------------
ego_process_init(ego_process_t * eproc,int argc,char ** argv)646 ego_process_t * ego_process_init( ego_process_t * eproc, int argc, char **argv )
647 {
648     if ( NULL == eproc ) return NULL;
649 
650     memset( eproc, 0, sizeof( *eproc ) );
651 
652     process_init( PROC_PBASE( eproc ) );
653 
654     eproc->argv0 = ( argc > 0 ) ? argv[0] : NULL;
655 
656     return eproc;
657 }
658 
659 //--------------------------------------------------------------------------------------------
egoboo_clear_vfs_paths()660 void egoboo_clear_vfs_paths()
661 {
662     /// @details BB@> clear out the basic mount points
663 
664     vfs_remove_mount_point( "mp_data" );
665     vfs_remove_mount_point( "mp_modules" );
666     vfs_remove_mount_point( "mp_players" );
667     vfs_remove_mount_point( "mp_remote" );
668 }
669 
670 //--------------------------------------------------------------------------------------------
egoboo_setup_vfs_paths()671 void egoboo_setup_vfs_paths()
672 {
673     /// @details BB@> set the basic mount points used by the main program
674 
675     //---- tell the vfs to add the basic search paths
676     vfs_set_base_search_paths();
677 
678     //---- mount all of the default global directories
679 
680     // mount the global basicdat directory t the beginning of the list
681     vfs_add_mount_point( fs_getDataDirectory(), "basicdat", "mp_data", 1 );
682 
683     // Create a mount point for the /user/modules directory
684     vfs_add_mount_point( fs_getUserDirectory(), "modules", "mp_modules", 1 );
685 
686     // Create a mount point for the /data/modules directory
687     vfs_add_mount_point( fs_getDataDirectory(), "modules", "mp_modules", 1 );
688 
689     // Create a mount point for the /user/players directory
690     vfs_add_mount_point( fs_getUserDirectory(), "players", "mp_players", 1 );
691 
692     // Create a mount point for the /data/players directory
693     //vfs_add_mount_point( fs_getDataDirectory(), "players", "mp_players", 1 );     //ZF> Let's remove the local players folder since it caused so many problems for people
694 
695     // Create a mount point for the /user/remote directory
696     vfs_add_mount_point( fs_getUserDirectory(), "import", "mp_import", 1 );
697 
698     // Create a mount point for the /user/remote directory
699     vfs_add_mount_point( fs_getUserDirectory(), "remote", "mp_remote", 1 );
700 }
701 
702 //--------------------------------------------------------------------------------------------
egoboo_get_ticks(void)703 Uint32 egoboo_get_ticks( void )
704 {
705     Uint32 ticks = 0;
706 
707     if ( single_frame_mode )
708     {
709         ticks = UPDATE_SKIP * update_wld;
710     }
711     else
712     {
713         ticks = SDL_GetTicks();
714     }
715 
716     return ticks;
717 }
718