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