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 game.c
21 /// @brief The code for controlling the game
22 /// @details
23 
24 #include "game.h"
25 
26 #include "mad.h"
27 
28 #include "controls_file.h"
29 #include "scancode_file.h"
30 #include "treasure_table_file.h"
31 
32 #include "clock.h"
33 #include "link.h"
34 #include "ui.h"
35 #include "font_bmp.h"
36 #include "font_ttf.h"
37 #include "log.h"
38 #include "system.h"
39 #include "script.h"
40 #include "sound.h"
41 #include "graphic.h"
42 #include "passage.h"
43 #include "input.h"
44 #include "menu.h"
45 #include "network.h"
46 #include "texture.h"
47 #include "wawalite_file.h"
48 #include "clock.h"
49 #include "spawn_file.h"
50 #include "camera.h"
51 #include "id_md2.h"
52 #include "collision.h"
53 #include "graphic_fan.h"
54 #include "quest.h"
55 #include "obj_BSP.h"
56 #include "mpd_BSP.h"
57 
58 #include "script_compile.h"
59 #include "script.h"
60 
61 #include "egoboo_vfs.h"
62 #include "egoboo_endian.h"
63 #include "egoboo_setup.h"
64 #include "egoboo_strutil.h"
65 #include "egoboo_fileutil.h"
66 #include "egoboo_vfs.h"
67 #include "egoboo.h"
68 
69 #include "SDL_extensions.h"
70 
71 #include "egoboo_console.h"
72 #if defined(USE_LUA_CONSOLE)
73 #include "lua_console.h"
74 #endif
75 
76 #include "char.inl"
77 #include "particle.inl"
78 #include "enchant.inl"
79 #include "profile.inl"
80 #include "mesh.inl"
81 #include "physics.inl"
82 
83 #include <SDL_image.h>
84 
85 #include <time.h>
86 #include <assert.h>
87 #include <float.h>
88 #include <string.h>
89 
90 //--------------------------------------------------------------------------------------------
91 //--------------------------------------------------------------------------------------------
92 
93 /// Data needed to specify a line-of-sight test
94 struct s_line_of_sight_info
95 {
96     float x0, y0, z0;
97     float x1, y1, z1;
98     Uint32 stopped_by;
99 
100     CHR_REF collide_chr;
101     Uint32  collide_fx;
102     int     collide_x;
103     int     collide_y;
104 };
105 
106 typedef struct s_line_of_sight_info line_of_sight_info_t;
107 
108 static bool_t collide_ray_with_mesh( line_of_sight_info_t * plos );
109 static bool_t collide_ray_with_characters( line_of_sight_info_t * plos );
110 static bool_t do_line_of_sight( line_of_sight_info_t * plos );
111 
112 //--------------------------------------------------------------------------------------------
113 void do_weather_spawn_particles();
114 
115 //--------------------------------------------------------------------------------------------
116 //--------------------------------------------------------------------------------------------
117 
118 static ego_mpd_t         _mesh[2];
119 static camera_t          _camera[2];
120 
121 static game_process_t    _gproc;
122 static game_module_t      gmod;
123 
124 PROFILE_DECLARE( game_update_loop );
125 PROFILE_DECLARE( gfx_loop );
126 PROFILE_DECLARE( game_single_update );
127 
128 PROFILE_DECLARE( talk_to_remotes );
129 PROFILE_DECLARE( listen_for_packets );
130 PROFILE_DECLARE( check_stats );
131 PROFILE_DECLARE( set_local_latches );
132 PROFILE_DECLARE( check_passage_music );
133 PROFILE_DECLARE( cl_talkToHost );
134 
135 //--------------------------------------------------------------------------------------------
136 //--------------------------------------------------------------------------------------------
137 bool_t  overrideslots      = bfalse;
138 
139 // End text
140 char   endtext[MAXENDTEXT] = EMPTY_CSTR;
141 size_t endtext_carat = 0;
142 
143 // Status displays
144 bool_t  StatusList_on     = btrue;
145 int     StatusList_count    = 0;
146 CHR_REF StatusList[MAXSTAT];
147 
148 ego_mpd_t         * PMesh   = _mesh + 0;
149 camera_t          * PCamera = _camera + 0;
150 game_module_t     * PMod    = &gmod;
151 game_process_t    * GProc   = &_gproc;
152 
153 pit_info_t pits = { bfalse, bfalse, ZERO_VECT3 };
154 
155 FACING_T glouseangle = 0;                                        // actually still used
156 
157 Uint32                animtile_update_and = 0;
158 animtile_instance_t   animtile[2];
159 damagetile_instance_t damagetile;
160 weather_instance_t    weather;
161 water_instance_t      water;
162 fog_instance_t        fog;
163 
164 bool_t activate_spawn_file_active = bfalse;
165 
166 Import_list_t ImportList  = IMPORT_LIST_INIT;
167 
168 //--------------------------------------------------------------------------------------------
169 //--------------------------------------------------------------------------------------------
170 
171 // game initialization / deinitialization - not accessible by scripts
172 static void reset_timers();
173 
174 // looping - stuff called every loop - not accessible by scripts
175 static void check_stats();
176 static void tilt_characters_to_terrain();
177 static void update_pits();
178 static int  update_game();
179 static void game_update_timers();
180 static void do_damage_tiles( void );
181 static void set_local_latches( void );
182 static void let_all_characters_think();
183 
184 // module initialization / deinitialization - not accessible by scripts
185 static bool_t game_load_module_data( const char *smallname );
186 static void   game_release_module_data();
187 static void   game_load_all_profiles( const char *modname );
188 
189 static void   activate_spawn_file_vfs();
190 static void   activate_alliance_file_vfs();
191 static void   load_all_global_objects();
192 
193 static bool_t chr_setup_apply( const CHR_REF ichr, spawn_file_info_t *pinfo );
194 
195 static void   game_reset_players();
196 
197 // Model stuff
198 static void log_madused_vfs( const char *savename );
199 
200 // "process" management
201 static int do_game_proc_begin( game_process_t * gproc );
202 static int do_game_proc_running( game_process_t * gproc );
203 static int do_game_proc_leaving( game_process_t * gproc );
204 
205 // misc
206 static bool_t game_begin_menu( menu_process_t * mproc, which_menu_t which );
207 static void   game_end_menu( menu_process_t * mproc );
208 
209 static void   do_game_hud();
210 
211 // manage the game's vfs mount points
212 static void   game_clear_vfs_paths();
213 
214 // place the object lists in the initial state
215 void reset_all_object_lists( void );
216 
217 //--------------------------------------------------------------------------------------------
218 // Random Things
219 //--------------------------------------------------------------------------------------------
export_one_character(const CHR_REF character,const CHR_REF owner,int chr_obj_index,bool_t is_local)220 egoboo_rv export_one_character( const CHR_REF character, const CHR_REF owner, int chr_obj_index, bool_t is_local )
221 {
222     /// @details ZZ@> This function exports a character
223 
224     cap_t * pcap;
225     pro_t * pobj;
226 
227     STRING fromdir;
228     STRING todir;
229     STRING fromfile;
230     STRING tofile;
231     STRING todirname;
232     STRING todirfullname;
233 
234     // Don't export enchants
235     disenchant_character( character );
236 
237     pobj = chr_get_ppro( character );
238     if ( NULL == pobj ) return rv_error;
239 
240     pcap = chr_get_pcap( character );
241     if ( NULL == pcap ) return rv_error;
242 
243     if ( !PMod->exportvalid || ( pcap->isitem && !pcap->cancarrytonextmodule ) )
244     {
245         return rv_fail;
246     }
247 
248     // TWINK_BO.OBJ
249     snprintf( todirname, SDL_arraysize( todirname ), "%s", str_encode_path( ChrList.lst[owner].Name ) );
250 
251     // Is it a character or an item?
252     if ( chr_obj_index < 0 )
253     {
254         // Character directory
255         snprintf( todirfullname, SDL_arraysize( todirfullname ), "%s", todirname );
256     }
257     else
258     {
259         // Item is a subdirectory of the owner directory...
260         snprintf( todirfullname, SDL_arraysize( todirfullname ), "%s/%d.obj", todirname, chr_obj_index );
261     }
262 
263     // players/twink.obj or players/twink.obj/0.obj
264     if ( is_local )
265     {
266         snprintf( todir, SDL_arraysize( todir ), "/players/%s", todirfullname );
267     }
268     else
269     {
270         snprintf( todir, SDL_arraysize( todir ), "/remote/%s", todirfullname );
271     }
272 
273     // Remove all the original info
274     if ( chr_obj_index < 0 )
275     {
276         vfs_removeDirectoryAndContents( todir, VFS_TRUE );
277         if ( !vfs_mkdir( todir ) )
278         {
279             log_warning( "export_one_character() - cannot create object directory \"%s\"\n", todir );
280             return rv_error;
281         }
282     }
283 
284     // modules/advent.mod/objects/advent.obj
285     snprintf( fromdir, SDL_arraysize( fromdir ), "%s", pobj->name );
286 
287     // Build the DATA.TXT file
288     snprintf( tofile, SDL_arraysize( tofile ), "%s/data.txt", todir ); /*DATA.TXT*/
289     export_one_character_profile_vfs( tofile, character );
290 
291     // Build the SKIN.TXT file
292     // this is now handled by the [SKIN] expansion in data.txt
293     //snprintf( tofile, SDL_arraysize( tofile ), "%s/skin.txt", todir ); /*SKIN.TXT*/
294     //export_one_character_skin_vfs( tofile, character );
295 
296     // Build the NAMING.TXT file
297     snprintf( tofile, SDL_arraysize( tofile ), "%s/naming.txt", todir ); /*NAMING.TXT*/
298     export_one_character_name_vfs( tofile, character );
299 
300     // Build the QUEST.TXT file
301     snprintf( tofile, SDL_arraysize( tofile ), "%s/quest.txt", todir );
302     export_one_character_quest_vfs( tofile, character );
303 
304     // copy every file that does not already exist in the todir
305     {
306         vfs_search_context_t * ctxt;
307         const char * searchResult;
308 
309         ctxt = vfs_findFirst( fromdir, NULL, VFS_SEARCH_FILE | VFS_SEARCH_BARE );
310         searchResult = vfs_search_context_get_current( ctxt );
311 
312         while ( NULL != ctxt && VALID_CSTR( searchResult ) )
313         {
314             snprintf( fromfile, SDL_arraysize( fromfile ), "%s/%s", fromdir, searchResult );
315             snprintf( tofile, SDL_arraysize( tofile ), "%s/%s", todir, searchResult );
316 
317             if ( !vfs_exists( tofile ) )
318             {
319                 vfs_copyFile( fromfile, tofile );
320             }
321 
322             vfs_findNext( &ctxt );
323             searchResult = vfs_search_context_get_current( ctxt );
324         }
325 
326         vfs_findClose( &ctxt );
327     }
328 
329     return rv_success;
330 }
331 
332 //--------------------------------------------------------------------------------------------
export_all_players(bool_t require_local)333 egoboo_rv export_all_players( bool_t require_local )
334 {
335     /// @details ZZ@> This function saves all the local players in the
336     ///    PLAYERS directory
337 
338     egoboo_rv export_chr_rv;
339     egoboo_rv retval;
340     bool_t is_local;
341     PLA_REF ipla;
342     int number;
343     CHR_REF character;
344 
345     // Don't export if the module isn't running
346     if ( !process_running( PROC_PBASE( GProc ) ) ) return rv_fail;
347 
348     // Stop if export isnt valid
349     if ( !PMod->exportvalid ) return rv_fail;
350 
351     // assume the best
352     retval = rv_success;
353 
354     // Check each player
355     for ( ipla = 0; ipla < MAX_PLAYER; ipla++ )
356     {
357         CHR_REF item;
358         player_t * ppla;
359         chr_t    * pchr;
360 
361         if ( !VALID_PLA( ipla ) ) continue;
362         ppla = PlaStack.lst + ipla;
363 
364         is_local = ( 0 != ppla->device.bits );
365         if ( require_local && !is_local ) continue;
366 
367         // Is it alive?
368         if ( !INGAME_CHR( ppla->index ) ) continue;
369         character = ppla->index;
370         pchr      = ChrList.lst + character;
371 
372         // don't export dead characters
373         if ( !pchr->alive ) continue;
374 
375         // Export the character
376         export_chr_rv = export_one_character( character, character, -1, is_local );
377         if ( rv_error == export_chr_rv )
378         {
379             retval = rv_error;
380         }
381 
382         // Export the left hand item
383         item = pchr->holdingwhich[SLOT_LEFT];
384         if ( INGAME_CHR( item ) )
385         {
386             export_chr_rv = export_one_character( item, character, SLOT_LEFT, is_local );
387             if ( rv_error == export_chr_rv )
388             {
389                 retval = rv_error;
390             }
391         }
392 
393         // Export the right hand item
394         item = pchr->holdingwhich[SLOT_RIGHT];
395         if ( INGAME_CHR( item ) )
396         {
397             export_chr_rv = export_one_character( item, character, SLOT_RIGHT, is_local );
398             if ( rv_error == export_chr_rv )
399             {
400                 retval = rv_error;
401             }
402         }
403 
404         // Export the inventory
405         number = 0;
406         PACK_BEGIN_LOOP( ipacked, pchr->pack.next )
407         {
408             if ( number >= MAXINVENTORY ) break;
409 
410             export_chr_rv = export_one_character( ipacked, character, number + SLOT_COUNT, is_local );
411             if ( rv_error == export_chr_rv )
412             {
413                 retval = rv_error;
414             }
415             else if ( rv_success == export_chr_rv )
416             {
417                 number++;
418             }
419         }
420         PACK_END_LOOP( ipacked );
421     }
422 
423     return retval;
424 }
425 
426 //--------------------------------------------------------------------------------------------
427 //--------------------------------------------------------------------------------------------
log_madused_vfs(const char * savename)428 void log_madused_vfs( const char *savename )
429 {
430     /// @details ZZ@> This is a debug function for checking model loads
431 
432     vfs_FILE* hFileWrite;
433     PRO_REF cnt;
434 
435     hFileWrite = vfs_openWrite( savename );
436     if ( hFileWrite )
437     {
438         vfs_printf( hFileWrite, "Slot usage for objects in last module loaded...\n" );
439         //vfs_printf( hFileWrite, "%d of %d frames used...\n", Md2FrameList_index, MAXFRAME );
440 
441         for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
442         {
443             if ( LOADED_PRO( cnt ) )
444             {
445                 CAP_REF icap = pro_get_icap( cnt );
446                 MAD_REF imad = pro_get_imad( cnt );
447 
448                 vfs_printf( hFileWrite, "%3d %32s %s\n", REF_TO_INT( cnt ), CapStack.lst[icap].classname, MadStack.lst[imad].name );
449             }
450             else if ( cnt <= 36 )    vfs_printf( hFileWrite, "%3d  %32s.\n", REF_TO_INT( cnt ), "Slot reserved for import players" );
451             else                    vfs_printf( hFileWrite, "%3d  %32s.\n", REF_TO_INT( cnt ), "Slot Unused" );
452         }
453 
454         vfs_close( hFileWrite );
455     }
456 }
457 
458 //--------------------------------------------------------------------------------------------
statlist_add(const CHR_REF character)459 void statlist_add( const CHR_REF character )
460 {
461     /// @details ZZ@> This function adds a status display to the do list
462 
463     chr_t * pchr;
464 
465     if ( StatusList_count >= MAXSTAT ) return;
466 
467     if ( !INGAME_CHR( character ) ) return;
468     pchr = ChrList.lst + character;
469 
470     if ( pchr->StatusList_on ) return;
471 
472     StatusList[StatusList_count] = character;
473     pchr->StatusList_on = btrue;
474     StatusList_count++;
475 }
476 
477 //--------------------------------------------------------------------------------------------
statlist_move_to_top(const CHR_REF character)478 void statlist_move_to_top( const CHR_REF character )
479 {
480     /// @details ZZ@> This function puts the character on top of the StatusList
481 
482     int cnt, oldloc;
483 
484     // Find where it is
485     oldloc = StatusList_count;
486 
487     for ( cnt = 0; cnt < StatusList_count; cnt++ )
488     {
489         if ( StatusList[cnt] == character )
490         {
491             oldloc = cnt;
492             cnt = StatusList_count;
493         }
494     }
495 
496     // Change position
497     if ( oldloc < StatusList_count )
498     {
499         // Move all the lower ones up
500         while ( oldloc > 0 )
501         {
502             oldloc--;
503             StatusList[oldloc+1] = StatusList[oldloc];
504         }
505 
506         // Put the character in the top slot
507         StatusList[0] = character;
508     }
509 }
510 
511 //--------------------------------------------------------------------------------------------
statlist_sort()512 void statlist_sort()
513 {
514     /// @details ZZ@> This function puts all of the local players on top of the StatusList
515 
516     PLA_REF ipla;
517 
518     for ( ipla = 0; ipla < PlaStack.count; ipla++ )
519     {
520         if ( PlaStack.lst[ipla].valid && PlaStack.lst[ipla].device.bits != INPUT_BITS_NONE )
521         {
522             statlist_move_to_top( PlaStack.lst[ipla].index );
523         }
524     }
525 }
526 
527 //--------------------------------------------------------------------------------------------
chr_set_frame(const CHR_REF character,int req_action,int frame_along,int ilip)528 egoboo_rv chr_set_frame( const CHR_REF character, int req_action, int frame_along, int ilip )
529 {
530     /// @details ZZ@> This function sets the frame for a character explicitly...  This is used to
531     ///    rotate Tank turrets
532 
533     chr_t * pchr;
534     MAD_REF imad;
535     egoboo_rv retval;
536     int action;
537 
538     if ( !INGAME_CHR( character ) ) return rv_error;
539     pchr = ChrList.lst + character;
540 
541     imad = chr_get_imad( character );
542     if ( !LOADED_MAD( imad ) ) return rv_fail;
543 
544     // resolve the requested action to a action that is valid for this model (if possible)
545     action = mad_get_action_ref( imad, req_action );
546 
547     // set the action
548     retval = chr_set_action( pchr, action, btrue, btrue );
549     if ( rv_success == retval )
550     {
551         // the action is set. now set the frame info.
552         // pass along the imad in case the pchr->inst is not using this same mad
553         // (corrupted data?)
554         retval = ( egoboo_rv )chr_instance_set_frame_full( &( pchr->inst ), frame_along, ilip, imad );
555     }
556 
557     return retval;
558 }
559 
560 //--------------------------------------------------------------------------------------------
activate_alliance_file_vfs()561 void activate_alliance_file_vfs()
562 {
563     /// @details ZZ@> This function reads the alliance file
564     STRING szTemp;
565     TEAM_REF teama, teamb;
566     vfs_FILE *fileread;
567 
568     // Load the file
569     fileread = vfs_openRead( "mp_data/alliance.txt" );
570     if ( fileread )
571     {
572         while ( goto_colon( NULL, fileread, btrue ) )
573         {
574             fget_string( fileread, szTemp, SDL_arraysize( szTemp ) );
575             teama = ( szTemp[0] - 'A' ) % TEAM_MAX;
576 
577             fget_string( fileread, szTemp, SDL_arraysize( szTemp ) );
578             teamb = ( szTemp[0] - 'A' ) % TEAM_MAX;
579             TeamStack.lst[teama].hatesteam[REF_TO_INT( teamb )] = bfalse;
580         }
581 
582         vfs_close( fileread );
583     }
584 }
585 
586 //--------------------------------------------------------------------------------------------
update_used_lists()587 void update_used_lists()
588 {
589     ChrList_update_used();
590     PrtList_update_used();
591     EncList_update_used();
592 }
593 
594 //--------------------------------------------------------------------------------------------
update_all_objects()595 void update_all_objects()
596 {
597     chr_stoppedby_tests = prt_stoppedby_tests = 0;
598     chr_pressure_tests  = prt_pressure_tests  = 0;
599 
600     update_all_characters();
601     update_all_particles();
602     update_all_enchants();
603 }
604 
605 //--------------------------------------------------------------------------------------------
move_all_objects()606 void move_all_objects()
607 {
608     mesh_mpdfx_tests = 0;
609 
610     move_all_particles();
611     move_all_characters();
612 }
613 
614 //--------------------------------------------------------------------------------------------
cleanup_all_objects()615 void cleanup_all_objects()
616 {
617     cleanup_all_characters();
618     cleanup_all_particles();
619     cleanup_all_enchants();
620 }
621 
622 //--------------------------------------------------------------------------------------------
bump_all_update_counters()623 void bump_all_update_counters()
624 {
625     bump_all_characters_update_counters();
626     //bump_all_particles_update_counters();
627     bump_all_enchants_update_counters();
628 }
629 
630 //--------------------------------------------------------------------------------------------
initialize_all_objects()631 void initialize_all_objects()
632 {
633     /// @details BB@> begin the code for updating in-game objects
634 
635     // update all object timers etc.
636     update_all_objects();
637 
638     // fix the list optimization, in case update_all_objects() turned some objects off.
639     update_used_lists();
640 }
641 
642 //--------------------------------------------------------------------------------------------
finalize_all_objects()643 void finalize_all_objects()
644 {
645     /// @details BB@> end the code for updating in-game objects
646 
647     // update the object's update counter for every active object
648     bump_all_update_counters();
649 
650     // do end-of-life care for all objects
651     cleanup_all_objects();
652 }
653 
654 //--------------------------------------------------------------------------------------------
update_game()655 int update_game()
656 {
657     /// @details ZZ@> This function does several iterations of character movements and such
658     ///    to keep the game in sync.
659 
660     int tnc, numdead, numalive;
661     int update_loop_cnt;
662     PLA_REF ipla;
663 
664     // Check for all local players being dead
665     local_stats.allpladead      = bfalse;
666     local_stats.seeinvis_level  = 0.0f;
667     local_stats.seekurse_level  = 0.0f;
668     local_stats.seedark_level   = 0.0f;
669     local_stats.grog_level      = 0.0f;
670     local_stats.daze_level      = 0.0f;
671 
672     numplayer = 0;
673     numdead = numalive = 0;
674     for ( ipla = 0; ipla < MAX_PLAYER; ipla++ )
675     {
676         CHR_REF ichr;
677         chr_t * pchr;
678 
679         if ( !PlaStack.lst[ipla].valid ) continue;
680 
681         // fix bad players
682         ichr = PlaStack.lst[ipla].index;
683         if ( !INGAME_CHR( ichr ) )
684         {
685             PlaStack.lst[ipla].index = ( CHR_REF )MAX_CHR;
686             PlaStack.lst[ipla].valid = bfalse;
687             continue;
688         }
689         pchr = ChrList.lst + ichr;
690 
691         // count the total number of players
692         numplayer++;
693 
694         // only interested in local players
695         if ( INPUT_BITS_NONE == PlaStack.lst[ipla].device.bits ) continue;
696 
697         if ( pchr->alive )
698         {
699             numalive++;
700 
701             local_stats.seeinvis_level += pchr->see_invisible_level;
702             local_stats.seekurse_level += pchr->see_kurse_level;
703             local_stats.seedark_level  += pchr->darkvision_level;
704             local_stats.grog_level     += pchr->grog_timer;
705             local_stats.daze_level     += pchr->daze_timer;
706         }
707         else
708         {
709             numdead++;
710         }
711     }
712 
713     if ( numalive > 0 )
714     {
715         local_stats.seeinvis_level /= ( float )numalive;
716         local_stats.seekurse_level /= ( float )numalive;
717         local_stats.seedark_level  /= ( float )numalive;
718         local_stats.grog_level     /= ( float )numalive;
719         local_stats.daze_level     /= ( float )numalive;
720     }
721 
722     // this allows for kurses, which might make negative values to do something reasonable
723     local_stats.seeinvis_mag = exp( 0.32f * local_stats.seeinvis_level );
724     local_stats.seedark_mag  = exp( 0.32f * local_stats.seedark_level );
725 
726     // Did everyone die?
727     if ( numdead >= local_numlpla )
728     {
729         local_stats.allpladead = btrue;
730     }
731 
732     // check for autorespawn
733     for ( ipla = 0; ipla < MAX_PLAYER; ipla++ )
734     {
735         CHR_REF ichr;
736         chr_t * pchr;
737 
738         if ( !PlaStack.lst[ipla].valid ) continue;
739 
740         ichr = PlaStack.lst[ipla].index;
741         if ( !INGAME_CHR( ichr ) ) continue;
742         pchr = ChrList.lst + ichr;
743 
744         if ( !pchr->alive )
745         {
746             if ( cfg.difficulty < GAME_HARD && local_stats.allpladead && SDLKEYDOWN( SDLK_SPACE ) && PMod->respawnvalid && 0 == local_stats.revivetimer )
747             {
748                 respawn_character( ichr );
749                 pchr->experience *= EXPKEEP;        // Apply xp Penality
750 
751                 if ( cfg.difficulty > GAME_EASY )
752                 {
753                     pchr->money *= EXPKEEP;        //Apply money loss
754                 }
755             }
756         }
757     }
758 
759     update_lag = 0;
760     update_loop_cnt = 0;
761     if ( update_wld < true_update )
762     {
763         int max_iterations = single_frame_mode ? 1 : 2 * TARGET_UPS;
764 
765         /// @todo claforte@> Put that back in place once networking is functional (Jan 6th 2001)
766         for ( tnc = 0; update_wld < true_update && tnc < max_iterations; tnc++ )
767         {
768             PROFILE_BEGIN( game_single_update );
769             {
770                 // do important stuff to keep in sync inside this loop
771 
772                 srand( PMod->randsave );
773                 PMod->randsave = rand();
774 
775                 // read the input values
776                 input_read();
777 
778                 // NETWORK PORT
779                 PROFILE_BEGIN( listen_for_packets );
780                 {
781                     listen_for_packets();
782                 }
783                 PROFILE_END2( listen_for_packets );
784 
785                 PROFILE_BEGIN( set_local_latches );
786                 {
787                     set_local_latches();
788                 }
789                 PROFILE_END2( set_local_latches );
790 
791                 PROFILE_BEGIN( cl_talkToHost );
792                 {
793                     cl_talkToHost();
794                 }
795                 PROFILE_END2( cl_talkToHost );
796 
797                 PROFILE_BEGIN( talk_to_remotes );
798                 {
799                     // get all player latches from the "remotes"
800                     sv_talkToRemotes();
801                 }
802                 PROFILE_END2( talk_to_remotes );
803 
804                 //---- begin the code for updating misc. game stuff
805                 {
806                     BillboardList_update_all();
807                     animate_tiles();
808                     move_water( &water );
809                     looped_update_all_sound( &renderlist );
810                     do_damage_tiles();
811                     update_pits();
812                     do_weather_spawn_particles();
813                 }
814                 //---- end the code for updating misc. game stuff
815 
816                 //---- begin the code object I/O
817                 {
818                     let_all_characters_think();           // sets the non-player latches
819                     unbuffer_player_latches();            // sets the player latches
820                 }
821                 //---- end the code object I/O
822 
823                 //---- begin the code for updating in-game objects
824                 initialize_all_objects();
825                 {
826                     move_all_objects();                   // clears some latches
827                     bump_all_objects();    // do the actual object interaction
828                 }
829                 finalize_all_objects();
830                 //---- end the code for updating in-game objects
831 
832                 // put the camera movement inside here
833                 camera_move( PCamera, PMesh );
834 
835                 // Timers
836                 clock_wld += UPDATE_SKIP;
837                 clock_enc_stat++;
838                 clock_chr_stat++;
839 
840                 // Reset the respawn timer
841                 if ( local_stats.revivetimer > 0 ) local_stats.revivetimer--;
842 
843                 update_wld++;
844                 ups_loops++;
845                 update_loop_cnt++;
846             }
847             PROFILE_END2( game_single_update );
848 
849             // estimate how much time the main loop is taking per second
850             est_single_update_time = 0.9F * est_single_update_time + 0.1F * PROFILE_QUERY( game_single_update );
851             est_single_ups         = 0.9F * est_single_ups         + 0.1F * ( 1.0F / PROFILE_QUERY( game_single_update ) );
852         }
853         update_lag = tnc;
854     }
855 
856     est_update_game_time = 0.9F * est_update_game_time + 0.1F * est_single_update_time * update_loop_cnt;
857     est_max_game_ups     = 0.9F * est_max_game_ups     + 0.1F * ( 1.0F / est_update_game_time );
858 
859     if ( PNet->on )
860     {
861         if ( 0 == numplatimes )
862         {
863             // The remote ran out of messages, and is now twiddling its thumbs...
864             // Make it go slower so it doesn't happen again
865             clock_wld += 25;
866         }
867         if ( numplatimes > 3 && !PNet->hostactive )
868         {
869             // The host has too many messages, and is probably experiencing control
870             // lag...  Speed it up so it gets closer to sync
871             clock_wld -= 5;
872         }
873     }
874 
875     return update_loop_cnt;
876 }
877 
878 //--------------------------------------------------------------------------------------------
game_update_timers()879 void game_update_timers()
880 {
881     /// @details ZZ@> This function updates the game timers
882 
883     static bool_t update_was_paused = bfalse;
884 
885     int ticks_diff;
886     int clock_diff;
887 
888     const float fold = 0.77f;
889     const float fnew = 1.0f - fold;
890 
891     ticks_last = ticks_now;
892     ticks_now  = egoboo_get_ticks();
893 
894     // check to make sure that the game is running
895     if ( !process_running( PROC_PBASE( GProc ) ) || GProc->mod_paused )
896     {
897         // for a local game, force the function to ignore the accumulation of time
898         // until you re-join the game
899         if ( !PNet->on )
900         {
901             ticks_last = ticks_now;
902             update_was_paused = btrue;
903             return;
904         }
905     }
906 
907     // make sure some amount of time has passed
908     ticks_diff = ticks_now - ticks_last;
909     if ( 0 == ticks_diff ) return;
910 
911     // calculate the time since the from the last update
912     // if the game was paused, assume that only one update time elapsed since the last time through this function
913     clock_diff = UPDATE_SKIP;
914     if ( !update_was_paused && !single_frame_mode )
915     {
916         clock_diff = ticks_diff;
917         clock_diff = MIN( clock_diff, 10 * UPDATE_SKIP );
918     }
919 
920     if ( PNet->on )
921     {
922         // if the network game is on, there really is no real "pause"
923         // so we can always measure the game time from the first clock reading
924         clock_all = ticks_now - clock_stt;
925     }
926     else
927     {
928         // if the net is not on, the game clock will pause when the local game is paused.
929         // if we use the other calculation, the game will freeze while it handles the updates
930         // for all the time that the game was paused... not so good
931         clock_all  += clock_diff;
932     }
933 
934     // Use the number of updates that should have been performed up to this point (true_update)
935     // to try to regulate the update speed of the game
936     // By limiting this loop to 10, you are essentially saying that the update loop
937     // can go 10 times as fast as normal to help update_wld catch up to true_update,
938     // but it can't completely bog doen the game
939     true_update = clock_all / UPDATE_SKIP;
940 
941     // get the number of frames that should have happened so far in a similar way
942     true_frame  = clock_all / FRAME_SKIP;
943 
944     // figure out the update rate
945     ups_clock += clock_diff;
946 
947     if ( ups_loops > 0 && ups_clock > 0 )
948     {
949         stabilized_ups_sum    = stabilized_ups_sum * fold + fnew * ( float ) ups_loops / (( float ) ups_clock / TICKS_PER_SEC );
950         stabilized_ups_weight = stabilized_ups_weight * fold + fnew;
951 
952         // blank these every so often so that the numbers don't overflow
953         if ( ups_loops > 10 * TARGET_UPS )
954         {
955             ups_loops = 0;
956             ups_clock = 0;
957         }
958     }
959 
960     if ( stabilized_ups_weight > 0.5f )
961     {
962         stabilized_ups = stabilized_ups_sum / stabilized_ups_weight;
963     }
964 
965     // if it got this far and the funciton had been paused, it is time to unpause it
966     update_was_paused = bfalse;
967 }
968 
969 //--------------------------------------------------------------------------------------------
reset_timers()970 void reset_timers()
971 {
972     /// @details ZZ@> This function resets the timers...
973 
974     clock_stt = ticks_now = ticks_last = egoboo_get_ticks();
975 
976     clock_all = 0;
977     clock_wld = 0;
978     clock_enc_stat = 0;
979     clock_chr_stat = 0;
980     clock_pit = 0;
981 
982     update_wld = 0;
983     game_frame_all = 0;
984     outofsync = bfalse;
985 
986     pits.kill = pits.teleport = bfalse;
987 }
988 
989 //--------------------------------------------------------------------------------------------
game_do_menu(menu_process_t * mproc)990 int game_do_menu( menu_process_t * mproc )
991 {
992     /// @details BB@> do menus
993 
994     int menuResult;
995     bool_t need_menu = bfalse;
996 
997     need_menu = bfalse;
998     if ( flip_pages_requested() )
999     {
1000         // someone else (and that means the game) has drawn a frame
1001         // so we just need to draw the menu over that frame
1002         need_menu = btrue;
1003 
1004         // force the menu to be displayed immediately when the game stops
1005         mproc->base.dtime = 1.0f / ( float )cfg.framelimit;
1006     }
1007     else if ( !process_running( PROC_PBASE( GProc ) ) )
1008     {
1009         // the menu's frame rate is controlled by a timer
1010         mproc->ticks_now = SDL_GetTicks();
1011         if ( mproc->ticks_now > mproc->ticks_next )
1012         {
1013             // FPS limit
1014             float  frameskip = ( float )TICKS_PER_SEC / ( float )cfg.framelimit;
1015             mproc->ticks_next = mproc->ticks_now + frameskip;
1016 
1017             need_menu = btrue;
1018             mproc->base.dtime = 1.0f / ( float )cfg.framelimit;
1019         }
1020     }
1021 
1022     menuResult = 0;
1023     if ( need_menu )
1024     {
1025         ui_beginFrame( mproc->base.dtime );
1026         {
1027             menuResult = doMenu( mproc->base.dtime );
1028             request_flip_pages();
1029         }
1030         ui_endFrame();
1031     }
1032 
1033     return menuResult;
1034 }
1035 
1036 //--------------------------------------------------------------------------------------------
1037 //--------------------------------------------------------------------------------------------
do_game_proc_begin(game_process_t * gproc)1038 int do_game_proc_begin( game_process_t * gproc )
1039 {
1040     BillboardList_init_all();
1041 
1042     gproc->escape_latch = bfalse;
1043 
1044     // initialize math objects
1045     make_randie();
1046     make_turntosin();
1047 
1048     // Linking system
1049     log_info( "Initializing module linking... " );
1050     if ( link_build_vfs( "mp_data/link.txt", LinkList ) ) log_message( "Success!\n" );
1051     else log_message( "Failure!\n" );
1052 
1053     // initialize the collision system
1054     collision_system_begin();
1055 
1056     // intialize the "profile system"
1057     profile_system_begin();
1058 
1059     // do some graphics initialization
1060     //make_lightdirectionlookup();
1061     make_enviro();
1062     camera_ctor( PCamera );
1063 
1064     // try to start a new module
1065     if ( !game_begin_module( pickedmodule_path, ( Uint32 )~0 ) )
1066     {
1067         // failure - kill the game process
1068         process_kill( PROC_PBASE( gproc ) );
1069         process_resume( PROC_PBASE( MProc ) );
1070     }
1071 
1072     // Initialize the process
1073     gproc->base.valid = btrue;
1074 
1075     // initialize all the profile variables
1076     PROFILE_RESET( game_update_loop );
1077     PROFILE_RESET( game_single_update );
1078     PROFILE_RESET( gfx_loop );
1079 
1080     PROFILE_RESET( talk_to_remotes );
1081     PROFILE_RESET( listen_for_packets );
1082     PROFILE_RESET( check_stats );
1083     PROFILE_RESET( set_local_latches );
1084     PROFILE_RESET( check_passage_music );
1085     PROFILE_RESET( cl_talkToHost );
1086 
1087     // reset the ups counter
1088     ups_clock        = 0;
1089     ups_loops        = 0;
1090 
1091     stabilized_ups        = TARGET_UPS;
1092     stabilized_ups_sum    = 0.1f * TARGET_UPS;
1093     stabilized_ups_weight = 0.1f;
1094 
1095     // reset the fps counter
1096     game_fps_clock        = 0;
1097     game_fps_loops        = 0;
1098 
1099     stabilized_game_fps        = TARGET_FPS;
1100     stabilized_game_fps_sum    = 0.1f * TARGET_FPS;
1101     stabilized_game_fps_weight = 0.1f;
1102 
1103     // re-initialize these variables
1104     est_max_fps          =  TARGET_FPS;
1105     est_render_time      =  1.0f / TARGET_FPS;
1106 
1107     est_update_time      =  1.0f / TARGET_UPS;
1108     est_max_ups          =  TARGET_UPS;
1109 
1110     est_gfx_time         =  1.0f / TARGET_FPS;
1111     est_max_gfx          =  TARGET_FPS;
1112 
1113     est_single_update_time  = 1.0f / TARGET_UPS;
1114     est_single_ups          = TARGET_UPS;
1115 
1116     est_update_game_time  = 1.0f / TARGET_UPS;
1117     est_max_game_ups      = TARGET_UPS;
1118 
1119     obj_BSP_system_begin( &mpd_BSP_root );
1120 
1121     return 1;
1122 }
1123 
1124 //--------------------------------------------------------------------------------------------
do_game_proc_running(game_process_t * gproc)1125 int do_game_proc_running( game_process_t * gproc )
1126 {
1127     int update_loops = 0;
1128 
1129     if ( !process_validate( PROC_PBASE( gproc ) ) ) return -1;
1130 
1131     gproc->was_active  = gproc->base.valid;
1132 
1133     if ( gproc->base.paused ) return 0;
1134 
1135     gproc->ups_ticks_now = SDL_GetTicks();
1136     if (( !single_frame_mode && gproc->ups_ticks_now > gproc->ups_ticks_next ) || ( single_frame_mode && single_update_requested ) )
1137     {
1138         // UPS limit
1139         gproc->ups_ticks_next = gproc->ups_ticks_now + UPDATE_SKIP / 4;
1140 
1141         PROFILE_BEGIN( game_update_loop );
1142         {
1143             // update all the timers
1144             game_update_timers();
1145 
1146             // do the updates
1147             if ( gproc->mod_paused && !PNet->on )
1148             {
1149                 clock_wld = clock_all;
1150             }
1151             else
1152             {
1153                 // start the console mode?
1154                 if ( control_is_pressed( INPUT_DEVICE_KEYBOARD, CONTROL_MESSAGE ) )
1155                 {
1156                     // reset the keyboard buffer
1157                     SDL_EnableKeyRepeat( 20, SDL_DEFAULT_REPEAT_DELAY );
1158                     console_mode = btrue;
1159                     console_done = bfalse;
1160                     keyb.buffer_count = 0;
1161                     keyb.buffer[0] = CSTR_END;
1162                 }
1163 
1164                 // This is the control loop
1165                 if ( PNet->on && console_done )
1166                 {
1167                     net_send_message();
1168                 }
1169 
1170                 game_update_timers();
1171 
1172                 PROFILE_BEGIN( check_stats );
1173                 {
1174                     check_stats();
1175                 }
1176                 PROFILE_END2( check_stats );
1177 
1178                 PROFILE_BEGIN( check_passage_music );
1179                 {
1180                     check_passage_music();
1181                 }
1182                 PROFILE_END2( check_passage_music );
1183 
1184                 if ( PNet->waitingforplayers )
1185                 {
1186                     clock_wld = clock_all;
1187                 }
1188                 else
1189                 {
1190                     update_loops = update_game();
1191                 }
1192             }
1193         }
1194         PROFILE_END2( game_update_loop );
1195 
1196         // estimate the main-loop update time is taking per inner-loop iteration
1197         // do a kludge to average out the effects of functions like check_passage_music()
1198         // even when the inner loop does not execute
1199         if ( update_loops > 0 )
1200         {
1201             est_update_time = 0.9F * est_update_time + 0.1F * PROFILE_QUERY( game_update_loop ) / update_loops;
1202             est_max_ups     = 0.9F * est_max_ups     + 0.1F * ( update_loops / PROFILE_QUERY( game_update_loop ) );
1203         }
1204         else
1205         {
1206             est_update_time = 0.9F * est_update_time + 0.1F * PROFILE_QUERY( game_update_loop );
1207             est_max_ups     = 0.9F * est_max_ups     + 0.1F * ( 1.0F / PROFILE_QUERY( game_update_loop ) );
1208         }
1209 
1210         single_update_requested = bfalse;
1211     }
1212 
1213     // Do the display stuff
1214     gproc->fps_ticks_now = SDL_GetTicks();
1215     if (( !single_frame_mode && gproc->fps_ticks_now > gproc->fps_ticks_next ) || ( single_frame_mode && single_frame_requested ) )
1216     {
1217         // FPS limit
1218         float  frameskip = ( float )TICKS_PER_SEC / ( float )cfg.framelimit;
1219         gproc->fps_ticks_next = gproc->fps_ticks_now + frameskip;
1220 
1221         PROFILE_BEGIN( gfx_loop );
1222         {
1223             gfx_main();
1224 
1225             msgtimechange++;
1226         }
1227         PROFILE_END2( gfx_loop );
1228 
1229         // estimate how much time the main loop is taking per second
1230         est_gfx_time = 0.9F * est_gfx_time + 0.1F * PROFILE_QUERY( gfx_loop );
1231         est_max_gfx  = 0.9F * est_max_gfx  + 0.1F * ( 1.0F / PROFILE_QUERY( gfx_loop ) );
1232 
1233         // estimate how much time the main loop is taking per second
1234         est_render_time = est_gfx_time * TARGET_FPS;
1235         est_max_fps  = 0.9F * est_max_fps + 0.1F * ( 1.0F - est_update_time * TARGET_UPS ) / PROFILE_QUERY( gfx_loop );
1236 
1237         single_frame_requested = bfalse;
1238     }
1239 
1240     if ( gproc->escape_requested )
1241     {
1242         gproc->escape_requested = bfalse;
1243 
1244         if ( !gproc->escape_latch )
1245         {
1246             if ( PMod->beat )
1247             {
1248                 game_begin_menu( MProc, emnu_ShowEndgame );
1249             }
1250             else
1251             {
1252                 game_begin_menu( MProc, emnu_GamePaused );
1253             }
1254 
1255             gproc->escape_latch = btrue;
1256             gproc->mod_paused   = btrue;
1257         }
1258     }
1259 
1260     return 0;
1261 }
1262 
1263 //--------------------------------------------------------------------------------------------
do_game_proc_leaving(game_process_t * gproc)1264 int do_game_proc_leaving( game_process_t * gproc )
1265 {
1266     if ( !process_validate( PROC_PBASE( gproc ) ) ) return -1;
1267 
1268     // get rid of all module data
1269     game_quit_module();
1270 
1271     // resume the menu
1272     process_resume( PROC_PBASE( MProc ) );
1273 
1274     // deallocate any dynamically allocated collision memory
1275     collision_system_end();
1276 
1277     // deallocate any data used by the profile system
1278     profile_system_end();
1279 
1280     // deallocate the obj_BSP
1281     obj_BSP_system_end();
1282 
1283     // deallocate any dynamically allocated scripting memory
1284     scripting_system_end();
1285 
1286     // clean up any remaining models that might have dynamic data
1287     release_all_mad();
1288 
1289     // reset the fps counter
1290     game_fps_clock             = 0;
1291     game_fps_loops             = 0;
1292 
1293     stabilized_game_fps        = TARGET_FPS;
1294     stabilized_game_fps_sum    = 0.1f * TARGET_FPS;
1295     stabilized_game_fps_weight = 0.1f;
1296 
1297     PROFILE_FREE( game_update_loop );
1298     PROFILE_FREE( game_single_update );
1299     PROFILE_FREE( gfx_loop );
1300 
1301     PROFILE_FREE( talk_to_remotes );
1302     PROFILE_FREE( listen_for_packets );
1303     PROFILE_FREE( check_stats );
1304     PROFILE_FREE( set_local_latches );
1305     PROFILE_FREE( check_passage_music );
1306     PROFILE_FREE( cl_talkToHost );
1307 
1308     return 1;
1309 }
1310 
1311 //--------------------------------------------------------------------------------------------
do_game_proc_run(game_process_t * gproc,double frameDuration)1312 int do_game_proc_run( game_process_t * gproc, double frameDuration )
1313 {
1314     int result = 0, proc_result = 0;
1315 
1316     if ( !process_validate( PROC_PBASE( gproc ) ) ) return -1;
1317     gproc->base.dtime = frameDuration;
1318 
1319     if ( gproc->base.paused ) return 0;
1320 
1321     if ( gproc->base.killme )
1322     {
1323         gproc->base.state = proc_leaving;
1324     }
1325 
1326     switch ( gproc->base.state )
1327     {
1328         case proc_begin:
1329             proc_result = do_game_proc_begin( gproc );
1330 
1331             if ( 1 == proc_result )
1332             {
1333                 gproc->base.state = proc_entering;
1334             }
1335             break;
1336 
1337         case proc_entering:
1338             // proc_result = do_game_proc_entering( gproc );
1339 
1340             gproc->base.state = proc_running;
1341             break;
1342 
1343         case proc_running:
1344             proc_result = do_game_proc_running( gproc );
1345 
1346             if ( 1 == proc_result )
1347             {
1348                 gproc->base.state = proc_leaving;
1349             }
1350             break;
1351 
1352         case proc_leaving:
1353             proc_result = do_game_proc_leaving( gproc );
1354 
1355             if ( 1 == proc_result )
1356             {
1357                 gproc->base.state  = proc_finish;
1358                 gproc->base.killme = bfalse;
1359             }
1360             break;
1361 
1362         case proc_finish:
1363             process_terminate( PROC_PBASE( gproc ) );
1364             process_resume( PROC_PBASE( MProc ) );
1365             break;
1366     }
1367 
1368     return result;
1369 }
1370 
1371 //--------------------------------------------------------------------------------------------
1372 //--------------------------------------------------------------------------------------------
prt_find_target(float pos_x,float pos_y,float pos_z,FACING_T facing,const PIP_REF particletype,const TEAM_REF team,const CHR_REF donttarget,const CHR_REF oldtarget)1373 CHR_REF prt_find_target( float pos_x, float pos_y, float pos_z, FACING_T facing,
1374                          const PIP_REF particletype, const TEAM_REF team, const CHR_REF donttarget, const CHR_REF oldtarget )
1375 {
1376     /// @details ZF@> This is the new improved targeting system for particles. Also includes distance in the Z direction.
1377 
1378     const float max_dist2 = WIDE * WIDE;
1379 
1380     pip_t * ppip;
1381 
1382     CHR_REF besttarget = ( CHR_REF )MAX_CHR;
1383     float  longdist2 = max_dist2;
1384 
1385     if ( !LOADED_PIP( particletype ) ) return ( CHR_REF )MAX_CHR;
1386     ppip = PipStack.lst + particletype;
1387 
1388     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
1389     {
1390         bool_t target_friend, target_enemy;
1391 
1392         if ( !pchr->alive || pchr->isitem || pchr->pack.is_packed ) continue;
1393 
1394         // prefer targeting riders over the mount itself
1395         if ( pchr->ismount && ( INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) || INGAME_CHR( pchr->holdingwhich[SLOT_RIGHT] ) ) ) continue;
1396 
1397         // ignore invictus
1398         if ( pchr->invictus ) continue;
1399 
1400         // we are going to give the player a break and not target things that
1401         // can't be damaged, unless the particle is homing. If it homes in,
1402         // the he damage_timer could drop off en route.
1403         if ( !ppip->homing && ( 0 != pchr->damage_timer ) ) continue;
1404 
1405         // Don't retarget someone we already had or not supposed to target
1406         if ( cnt == oldtarget || cnt == donttarget ) continue;
1407 
1408         target_friend = ppip->onlydamagefriendly && team == chr_get_iteam( cnt );
1409         target_enemy  = !ppip->onlydamagefriendly && team_hates_team( team, chr_get_iteam( cnt ) );
1410 
1411         if ( target_friend || target_enemy )
1412         {
1413             FACING_T angle = - facing + vec_to_facing( pchr->pos.x - pos_x , pchr->pos.y - pos_y );
1414 
1415             // Only proceed if we are facing the target
1416             if ( angle < ppip->targetangle || angle > ( 0xFFFF - ppip->targetangle ) )
1417             {
1418                 float dist2 =
1419                     POW( ABS( pchr->pos.x - pos_x ), 2 ) +
1420                     POW( ABS( pchr->pos.y - pos_y ), 2 ) +
1421                     POW( ABS( pchr->pos.z - pos_z ), 2 );
1422 
1423                 if ( dist2 < longdist2 && dist2 <= max_dist2 )
1424                 {
1425                     glouseangle = angle;
1426                     besttarget = cnt;
1427                     longdist2 = dist2;
1428                 }
1429             }
1430         }
1431     }
1432     CHR_END_LOOP();
1433 
1434     // All done
1435     return besttarget;
1436 }
1437 
1438 //--------------------------------------------------------------------------------------------
check_target(chr_t * psrc,const CHR_REF ichr_test,IDSZ idsz,BIT_FIELD targeting_bits)1439 bool_t check_target( chr_t * psrc, const CHR_REF ichr_test, IDSZ idsz, BIT_FIELD targeting_bits )
1440 {
1441     bool_t retval = bfalse;
1442 
1443     bool_t is_hated, hates_me;
1444     bool_t is_friend, is_prey, is_predator, is_mutual;
1445     chr_t * ptst;
1446 
1447     // Skip non-existing objects
1448     if ( !ACTIVE_PCHR( psrc ) ) return bfalse;
1449 
1450     if ( !INGAME_CHR( ichr_test ) ) return bfalse;
1451     ptst = ChrList.lst + ichr_test;
1452 
1453     // Skip hidden characters
1454     if ( ptst->is_hidden ) return bfalse;
1455 
1456     // Players only?
1457     if (( HAS_SOME_BITS( targeting_bits, TARGET_PLAYERS ) || HAS_SOME_BITS( targeting_bits, TARGET_QUEST ) ) && !VALID_PLA( ptst->is_which_player ) ) return bfalse;
1458 
1459     // Skip held objects
1460     if ( INGAME_CHR( ptst->attachedto ) || ptst->pack.is_packed ) return bfalse;
1461 
1462     // Allow to target ourselves?
1463     if ( psrc == ptst && HAS_NO_BITS( targeting_bits, TARGET_SELF ) ) return bfalse;
1464 
1465     // Don't target our holder if we are an item and being held
1466     if ( psrc->isitem && psrc->attachedto == GET_REF_PCHR( ptst ) ) return bfalse;
1467 
1468     // Allow to target dead stuff?
1469     if ( ptst->alive == HAS_SOME_BITS( targeting_bits, TARGET_DEAD ) ) return bfalse;
1470 
1471     // Don't target invisible stuff, unless we can actually see them
1472     if ( !chr_can_see_object( GET_REF_PCHR( psrc ), ichr_test ) ) return bfalse;
1473 
1474     //Need specific skill? ([NONE] always passes)
1475     if ( HAS_SOME_BITS( targeting_bits, TARGET_SKILL ) && 0 == chr_get_skill( ptst, idsz ) ) return bfalse;
1476 
1477     // Require player to have specific quest?
1478     if ( HAS_SOME_BITS( targeting_bits, TARGET_QUEST ) )
1479     {
1480         int quest_level = QUEST_NONE;
1481         player_t * ppla = PlaStack.lst + ptst->is_which_player;
1482 
1483         quest_level = quest_get_level( ppla->quest_log, SDL_arraysize( ppla->quest_log ), idsz );
1484 
1485         // find only active quests?
1486         // this makes it backward-compatible with zefz's version
1487         if ( quest_level < 0 ) return bfalse;
1488     }
1489 
1490     is_hated = team_hates_team( psrc->team, ptst->team );
1491     hates_me = team_hates_team( ptst->team, psrc->team );
1492 
1493     // Target neutral items? (still target evil items, could be pets)
1494     if (( ptst->isitem || ptst->invictus ) && !HAS_SOME_BITS( targeting_bits, TARGET_ITEMS ) ) return bfalse;
1495 
1496     // Only target those of proper team. Skip this part if it's a item
1497     if ( !ptst->isitem )
1498     {
1499         if (( HAS_NO_BITS( targeting_bits, TARGET_ENEMIES ) && is_hated ) ) return bfalse;
1500         if (( HAS_NO_BITS( targeting_bits, TARGET_FRIENDS ) && !is_hated ) ) return bfalse;
1501     }
1502 
1503     // these options are here for ideas of ways to mod this function
1504     is_friend    = !is_hated && !hates_me;
1505     is_prey      =  is_hated && !hates_me;
1506     is_predator  = !is_hated &&  hates_me;
1507     is_mutual    =  is_hated &&  hates_me;
1508 
1509     //This is the last and final step! Check for specific IDSZ too? (not needed if we are looking for a quest)
1510     if ( IDSZ_NONE == idsz || HAS_SOME_BITS( targeting_bits, TARGET_QUEST ) )
1511     {
1512         retval = btrue;
1513     }
1514     else
1515     {
1516         bool_t match_idsz = ( idsz == pro_get_idsz( ptst->profile_ref, IDSZ_PARENT ) ) ||
1517                             ( idsz == pro_get_idsz( ptst->profile_ref, IDSZ_TYPE ) );
1518 
1519         if ( match_idsz )
1520         {
1521             if ( !HAS_SOME_BITS( targeting_bits, TARGET_INVERTID ) ) retval = btrue;
1522         }
1523         else
1524         {
1525             if ( HAS_SOME_BITS( targeting_bits, TARGET_INVERTID ) ) retval = btrue;
1526         }
1527     }
1528 
1529     return retval;
1530 }
1531 
1532 //--------------------------------------------------------------------------------------------
chr_find_target(chr_t * psrc,float max_dist,IDSZ idsz,BIT_FIELD targeting_bits)1533 CHR_REF chr_find_target( chr_t * psrc, float max_dist, IDSZ idsz, BIT_FIELD targeting_bits )
1534 {
1535     /// @details ZF@> This is the new improved AI targeting system. Also includes distance in the Z direction.
1536     ///     If max_dist is 0 then it searches without a max limit.
1537 
1538     line_of_sight_info_t los_info;
1539 
1540     Uint16 cnt;
1541     CHR_REF best_target = ( CHR_REF )MAX_CHR;
1542     float  best_dist2, max_dist2;
1543 
1544     size_t search_list_size = 0;
1545     CHR_REF search_list[MAX_CHR];
1546 
1547     if ( !ACTIVE_PCHR( psrc ) ) return ( CHR_REF )MAX_CHR;
1548 
1549     max_dist2 = max_dist * max_dist;
1550 
1551     if ( HAS_SOME_BITS( targeting_bits, TARGET_PLAYERS ) )
1552     {
1553         PLA_REF ipla;
1554 
1555         for ( ipla = 0; ipla < MAX_PLAYER; ipla ++ )
1556         {
1557             if ( !PlaStack.lst[ipla].valid || !INGAME_CHR( PlaStack.lst[ipla].index ) ) continue;
1558 
1559             search_list[search_list_size] = PlaStack.lst[ipla].index;
1560             search_list_size++;
1561         }
1562     }
1563     else
1564     {
1565         CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
1566         {
1567             search_list[search_list_size] = cnt;
1568             search_list_size++;
1569         }
1570         CHR_END_LOOP();
1571     }
1572 
1573     // set the line-of-sight source
1574     los_info.x0         = psrc->pos.x;
1575     los_info.y0         = psrc->pos.y;
1576     los_info.z0         = psrc->pos.z + psrc->bump.height;
1577     los_info.stopped_by = psrc->stoppedby;
1578 
1579     best_target = ( CHR_REF )MAX_CHR;
1580     best_dist2  = max_dist2;
1581     for ( cnt = 0; cnt < search_list_size; cnt++ )
1582     {
1583         float  dist2;
1584         fvec3_t   diff;
1585         chr_t * ptst;
1586         CHR_REF ichr_test = search_list[cnt];
1587 
1588         if ( !INGAME_CHR( ichr_test ) ) continue;
1589         ptst = ChrList.lst + ichr_test;
1590 
1591         if ( !check_target( psrc, ichr_test, idsz, targeting_bits ) ) continue;
1592 
1593         diff  = fvec3_sub( psrc->pos.v, ptst->pos.v );
1594         dist2 = fvec3_dot_product( diff.v, diff.v );
1595 
1596         if (( 0 == max_dist2 || dist2 <= max_dist2 ) && ( MAX_CHR == best_target || dist2 < best_dist2 ) )
1597         {
1598             //Invictus chars do not need a line of sight
1599             if ( !psrc->invictus )
1600             {
1601                 // set the line-of-sight source
1602                 los_info.x1 = ptst->pos.x;
1603                 los_info.y1 = ptst->pos.y;
1604                 los_info.z1 = ptst->pos.z + MAX( 1, ptst->bump.height );
1605 
1606                 if ( do_line_of_sight( &los_info ) ) continue;
1607             }
1608 
1609             //Set the new best target found
1610             best_target = ichr_test;
1611             best_dist2  = dist2;
1612         }
1613     }
1614 
1615     // make sure the target is valid
1616     if ( !INGAME_CHR( best_target ) ) best_target = ( CHR_REF )MAX_CHR;
1617 
1618     return best_target;
1619 }
1620 
1621 //--------------------------------------------------------------------------------------------
do_damage_tiles()1622 void do_damage_tiles()
1623 {
1624     // do the damage tile stuff
1625 
1626     CHR_BEGIN_LOOP_ACTIVE( character, pchr )
1627     {
1628         cap_t * pcap;
1629         chr_t * pchr;
1630 
1631         if ( !INGAME_CHR( character ) ) continue;
1632         pchr = ChrList.lst + character;
1633 
1634         pcap = pro_get_pcap( pchr->profile_ref );
1635         if ( NULL == pcap ) continue;
1636 
1637         // if the object is not really in the game, do nothing
1638         if ( pchr->is_hidden || !pchr->alive ) continue;
1639 
1640         // if you are being held by something, you are protected
1641         if ( pchr->pack.is_packed ) continue;
1642 
1643         // are we on a damage tile?
1644         if ( !mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) ) continue;
1645         if ( 0 == mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_DAMAGE ) ) continue;
1646 
1647         // are we low enough?
1648         if ( pchr->pos.z > pchr->enviro.floor_level + DAMAGERAISE ) continue;
1649 
1650         // allow reaffirming damage to things like torches, even if they are being held,
1651         // but make the tolerance closer so that books won't burn so easily
1652         if ( !INGAME_CHR( pchr->attachedto ) || pchr->pos.z < pchr->enviro.floor_level + DAMAGERAISE )
1653         {
1654             if ( pchr->reaffirm_damagetype == damagetile.damagetype )
1655             {
1656                 if ( 0 == ( update_wld & TILE_REAFFIRM_AND ) )
1657                 {
1658                     reaffirm_attached_particles( character );
1659                 }
1660             }
1661         }
1662 
1663         // do not do direct damage to items that are being held
1664         if ( INGAME_CHR( pchr->attachedto ) ) continue;
1665 
1666         // don't do direct damage to invulnerable objects
1667         if ( pchr->invictus ) continue;
1668 
1669         //@todo: sound of lava sizzling and such
1670         //distance = ABS( PCamera->track_pos.x - pchr->pos.x ) + ABS( PCamera->track_pos.y - pchr->pos.y );
1671 
1672         //if ( distance < damagetile.min_distance )
1673         //{
1674         //    damagetile.min_distance = distance;
1675         //}
1676 
1677         //if ( distance < damagetile.min_distance + 256 )
1678         //{
1679         //    damagetile.sound_time = 0;
1680         //}
1681 
1682         if ( 0 == pchr->damage_timer )
1683         {
1684             int actual_damage;
1685             actual_damage = damage_character( character, ATK_BEHIND, damagetile.amount, damagetile.damagetype, ( TEAM_REF )TEAM_DAMAGE, ( CHR_REF )MAX_CHR, DAMFX_NBLOC | DAMFX_ARMO, bfalse );
1686             pchr->damage_timer = DAMAGETILETIME;
1687 
1688             if (( actual_damage > 0 ) && ( -1 != damagetile.part_gpip ) && 0 == ( update_wld & damagetile.partand ) )
1689             {
1690                 spawn_one_particle_global( pchr->pos, ATK_FRONT, damagetile.part_gpip, 0 );
1691             }
1692         }
1693     }
1694     CHR_END_LOOP();
1695 }
1696 
1697 //--------------------------------------------------------------------------------------------
update_pits()1698 void update_pits()
1699 {
1700     /// @details ZZ@> This function kills any character in a deep pit...
1701 
1702     if ( pits.kill || pits.teleport )
1703     {
1704         //Decrease the timer
1705         if ( clock_pit > 0 ) clock_pit--;
1706 
1707         if ( 0 == clock_pit )
1708         {
1709             //Reset timer
1710             clock_pit = 20;
1711 
1712             // Kill any particles that fell in a pit, if they die in water...
1713             PRT_BEGIN_LOOP_ACTIVE( iprt, prt_bdl )
1714             {
1715                 if ( prt_bdl.prt_ptr->pos.z < PITDEPTH && prt_bdl.pip_ptr->end_water )
1716                 {
1717                     end_one_particle_now( prt_bdl.prt_ref );
1718                 }
1719             }
1720             PRT_END_LOOP();
1721 
1722             // Kill or teleport any characters that fell in a pit...
1723             CHR_BEGIN_LOOP_ACTIVE( ichr, pchr )
1724             {
1725                 // Is it a valid character?
1726                 if ( pchr->invictus || !pchr->alive ) continue;
1727                 if ( INGAME_CHR( pchr->attachedto ) || pchr->pack.is_packed ) continue;
1728 
1729                 // Do we kill it?
1730                 if ( pits.kill && pchr->pos.z < PITDEPTH )
1731                 {
1732                     // Got one!
1733                     kill_character( ichr, ( CHR_REF )MAX_CHR, bfalse );
1734                     pchr->vel.x = 0;
1735                     pchr->vel.y = 0;
1736 
1737                     /// @note ZF@> Disabled, the pitfall sound was intended for pits.teleport only
1738                     /// Play sound effect
1739                     /// sound_play_chunk( pchr->pos, g_wavelist[GSND_PITFALL] );
1740                 }
1741 
1742                 // Do we teleport it?
1743                 if ( pits.teleport && pchr->pos.z < PITDEPTH * 4 )
1744                 {
1745                     bool_t teleported;
1746 
1747                     // Teleport them back to a "safe" spot
1748                     teleported = chr_teleport( ichr, pits.teleport_pos.x, pits.teleport_pos.y, pits.teleport_pos.z, pchr->ori.facing_z );
1749 
1750                     if ( !teleported )
1751                     {
1752                         // Kill it instead
1753                         kill_character( ichr, ( CHR_REF )MAX_CHR, bfalse );
1754                     }
1755                     else
1756                     {
1757                         // Stop movement
1758                         pchr->vel.z = 0;
1759                         pchr->vel.x = 0;
1760                         pchr->vel.y = 0;
1761 
1762                         // Play sound effect
1763                         if ( VALID_PLA( pchr->is_which_player ) )
1764                         {
1765                             sound_play_chunk_full( g_wavelist[GSND_PITFALL] );
1766                         }
1767                         else
1768                         {
1769                             sound_play_chunk( pchr->pos, g_wavelist[GSND_PITFALL] );
1770                         }
1771 
1772                         // Do some damage (same as damage tile)
1773                         damage_character( ichr, ATK_BEHIND, damagetile.amount, damagetile.damagetype, ( TEAM_REF )TEAM_DAMAGE, chr_get_pai( ichr )->bumplast, DAMFX_NBLOC | DAMFX_ARMO, bfalse );
1774                     }
1775                 }
1776             }
1777             CHR_END_LOOP();
1778         }
1779     }
1780 }
1781 
1782 //--------------------------------------------------------------------------------------------
do_weather_spawn_particles()1783 void do_weather_spawn_particles()
1784 {
1785     /// @details ZZ@> This function drops snowflakes or rain or whatever, also swings the camera
1786 
1787     int    cnt;
1788     bool_t foundone;
1789 
1790     if ( weather.time > 0 )
1791     {
1792         weather.time--;
1793         if ( 0 == weather.time )
1794         {
1795             weather.time = weather.timer_reset;
1796 
1797             // Find a valid player
1798             foundone = bfalse;
1799             for ( cnt = 0; cnt < MAX_PLAYER; cnt++ )
1800             {
1801                 weather.iplayer = ( PLA_REF )(( REF_TO_INT( weather.iplayer ) + 1 ) % MAX_PLAYER );
1802                 if ( PlaStack.lst[weather.iplayer].valid )
1803                 {
1804                     foundone = btrue;
1805                     break;
1806                 }
1807             }
1808 
1809             // Did we find one?
1810             if ( foundone )
1811             {
1812                 // Yes, but is the character valid?
1813                 CHR_REF ichr = PlaStack.lst[weather.iplayer].index;
1814                 if ( INGAME_CHR( ichr ) && !ChrList.lst[ichr].pack.is_packed )
1815                 {
1816                     chr_t * pchr = ChrList.lst + ichr;
1817 
1818                     // Yes, so spawn over that character
1819                     PRT_REF particle = spawn_one_particle_global( pchr->pos, ATK_FRONT, weather.part_gpip, 0 );
1820                     if ( DEFINED_PRT( particle ) )
1821                     {
1822                         prt_t * pprt = PrtList.lst + particle;
1823 
1824                         bool_t destroy_particle = bfalse;
1825 
1826                         if ( weather.over_water && !prt_is_over_water( particle ) )
1827                         {
1828                             destroy_particle = btrue;
1829                         }
1830                         else if ( EMPTY_BIT_FIELD != prt_test_wall( pprt, NULL, NULL ) )
1831                         {
1832                             destroy_particle = btrue;
1833                         }
1834                         else
1835                         {
1836                             // Weather particles spawned at the edge of the map look ugly, so don't spawn them there
1837                             if ( pprt->pos.x < EDGE || pprt->pos.x > PMesh->gmem.edge_x - EDGE )
1838                             {
1839                                 destroy_particle = btrue;
1840                             }
1841                             else if ( pprt->pos.y < EDGE || pprt->pos.y > PMesh->gmem.edge_y - EDGE )
1842                             {
1843                                 destroy_particle = btrue;
1844                             }
1845                         }
1846 
1847                         if ( destroy_particle )
1848                         {
1849                             PrtList_free_one( particle );
1850                         }
1851                     }
1852                 }
1853             }
1854         }
1855     }
1856 
1857     PCamera->swing = ( PCamera->swing + PCamera->swingrate ) & 0x3FFF;
1858 }
1859 
1860 //--------------------------------------------------------------------------------------------
set_one_player_latch(const PLA_REF player)1861 void set_one_player_latch( const PLA_REF player )
1862 {
1863     /// @details ZZ@> This function converts input readings to latch settings, so players can
1864     ///    move around
1865 
1866     Uint16 turnsin;
1867     float dist, scale;
1868     float fsin, fcos;
1869     latch_t sum;
1870 
1871     chr_t          * pchr;
1872     player_t       * ppla;
1873     input_device_t * pdevice;
1874 
1875     if ( INVALID_PLA( player ) ) return;
1876     ppla = PlaStack.lst + player;
1877 
1878     pdevice = &( ppla->device );
1879 
1880     if ( !INGAME_CHR( ppla->index ) ) return;
1881     pchr = ChrList.lst + ppla->index;
1882 
1883     // is the device a local device or an internet device?
1884     if ( pdevice->bits == EMPTY_BIT_FIELD ) return;
1885 
1886     // Clear the player's latch buffers
1887     latch_init( &( sum ) );
1888 
1889     // generate the transforms relative to the camera
1890     turnsin = TO_TURN( PCamera->ori.facing_z );
1891     fsin    = turntosin[ turnsin ];
1892     fcos    = turntocos[ turnsin ];
1893 
1894     // Mouse routines
1895     if ( HAS_SOME_BITS( pdevice->bits, INPUT_BITS_MOUSE ) && mous.on )
1896     {
1897         fvec2_t joy_pos, joy_new;
1898 
1899         fvec2_self_clear( joy_new.v );
1900 
1901         if (( CAM_TURN_GOOD == PCamera->turn_mode && 1 == local_numlpla ) ||
1902             !control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_CAMERA ) )  // Don't allow movement in camera control mode
1903         {
1904             dist = SQRT( mous.x * mous.x + mous.y * mous.y );
1905             if ( dist > 0 )
1906             {
1907                 scale = mous.sense / dist;
1908                 if ( dist < mous.sense )
1909                 {
1910                     scale = dist / mous.sense;
1911                 }
1912 
1913                 scale = scale / mous.sense;
1914                 joy_pos.x = mous.x * scale;
1915                 joy_pos.y = mous.y * scale;
1916 
1917                 if ( CAM_TURN_GOOD == PCamera->turn_mode &&
1918                      1 == local_numlpla &&
1919                      0 == control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_CAMERA ) )  joy_pos.x = 0;
1920 
1921                 joy_new.x = ( joy_pos.x * fcos + joy_pos.y * fsin );
1922                 joy_new.y = ( -joy_pos.x * fsin + joy_pos.y * fcos );
1923             }
1924         }
1925 
1926         sum.x += joy_new.x;
1927         sum.y += joy_new.y;
1928 
1929         // Read buttons
1930         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_JUMP ) )
1931             SET_BIT( sum.b, LATCHBUTTON_JUMP );
1932         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_LEFT_USE ) )
1933             SET_BIT( sum.b, LATCHBUTTON_LEFT );
1934         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_LEFT_GET ) )
1935             SET_BIT( sum.b, LATCHBUTTON_ALTLEFT );
1936         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_LEFT_PACK ) )
1937             SET_BIT( sum.b, LATCHBUTTON_PACKLEFT );
1938         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_RIGHT_USE ) )
1939             SET_BIT( sum.b, LATCHBUTTON_RIGHT );
1940         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_RIGHT_GET ) )
1941             SET_BIT( sum.b, LATCHBUTTON_ALTRIGHT );
1942         if ( control_is_pressed( INPUT_DEVICE_MOUSE,  CONTROL_RIGHT_PACK ) )
1943             SET_BIT( sum.b, LATCHBUTTON_PACKRIGHT );
1944     }
1945 
1946     // Joystick A routines
1947     if ( HAS_SOME_BITS( pdevice->bits, INPUT_BITS_JOYA ) && joy[0].on )
1948     {
1949         fvec2_t joy_pos, joy_new;
1950 
1951         fvec2_self_clear( joy_new.v );
1952 
1953         if (( CAM_TURN_GOOD == PCamera->turn_mode && 1 == local_numlpla ) ||
1954             !control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_CAMERA ) )
1955         {
1956             joy_pos.x = joy[0].x;
1957             joy_pos.y = joy[0].y;
1958 
1959             dist = joy_pos.x * joy_pos.x + joy_pos.y * joy_pos.y;
1960             if ( dist > 1.0f )
1961             {
1962                 scale = 1.0f / SQRT( dist );
1963                 joy_pos.x *= scale;
1964                 joy_pos.y *= scale;
1965             }
1966 
1967             if ( CAM_TURN_GOOD == PCamera->turn_mode &&
1968                  1 == local_numlpla &&
1969                  !control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_CAMERA ) )  joy_pos.x = 0;
1970 
1971             joy_new.x = ( joy_pos.x * fcos + joy_pos.y * fsin );
1972             joy_new.y = ( -joy_pos.x * fsin + joy_pos.y * fcos );
1973         }
1974 
1975         sum.x += joy_new.x;
1976         sum.y += joy_new.y;
1977 
1978         // Read buttons
1979         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_JUMP ) )
1980             SET_BIT( sum.b, LATCHBUTTON_JUMP );
1981         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_LEFT_USE ) )
1982             SET_BIT( sum.b, LATCHBUTTON_LEFT );
1983         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_LEFT_GET ) )
1984             SET_BIT( sum.b, LATCHBUTTON_ALTLEFT );
1985         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_LEFT_PACK ) )
1986             SET_BIT( sum.b, LATCHBUTTON_PACKLEFT );
1987         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_RIGHT_USE ) )
1988             SET_BIT( sum.b, LATCHBUTTON_RIGHT );
1989         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_RIGHT_GET ) )
1990             SET_BIT( sum.b, LATCHBUTTON_ALTRIGHT );
1991         if ( control_is_pressed( INPUT_DEVICE_JOY_A, CONTROL_RIGHT_PACK ) )
1992             SET_BIT( sum.b, LATCHBUTTON_PACKRIGHT );
1993     }
1994 
1995     // Joystick B routines
1996     if ( HAS_SOME_BITS( pdevice->bits, INPUT_BITS_JOYB ) && joy[1].on )
1997     {
1998         fvec2_t joy_pos, joy_new;
1999 
2000         fvec2_self_clear( joy_new.v );
2001 
2002         if (( CAM_TURN_GOOD == PCamera->turn_mode && 1 == local_numlpla ) ||
2003             !control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_CAMERA ) )
2004         {
2005             joy_pos.x = joy[1].x;
2006             joy_pos.y = joy[1].y;
2007 
2008             dist = joy_pos.x * joy_pos.x + joy_pos.y * joy_pos.y;
2009             if ( dist > 1.0f )
2010             {
2011                 scale = 1.0f / SQRT( dist );
2012                 joy_pos.x *= scale;
2013                 joy_pos.y *= scale;
2014             }
2015 
2016             if ( CAM_TURN_GOOD == PCamera->turn_mode &&
2017                  1 == local_numlpla &&
2018                  !control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_CAMERA ) )  joy_pos.x = 0;
2019 
2020             joy_new.x = ( joy_pos.x * fcos + joy_pos.y * fsin );
2021             joy_new.y = ( -joy_pos.x * fsin + joy_pos.y * fcos );
2022         }
2023 
2024         sum.x += joy_new.x;
2025         sum.y += joy_new.y;
2026 
2027         // Read buttons
2028         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_JUMP ) )
2029             SET_BIT( sum.b, LATCHBUTTON_JUMP );
2030         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_LEFT_USE ) )
2031             SET_BIT( sum.b, LATCHBUTTON_LEFT );
2032         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_LEFT_GET ) )
2033             SET_BIT( sum.b, LATCHBUTTON_ALTLEFT );
2034         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_LEFT_PACK ) )
2035             SET_BIT( sum.b, LATCHBUTTON_PACKLEFT );
2036         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_RIGHT_USE ) )
2037             SET_BIT( sum.b, LATCHBUTTON_RIGHT );
2038         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_RIGHT_GET ) )
2039             SET_BIT( sum.b, LATCHBUTTON_ALTRIGHT );
2040         if ( control_is_pressed( INPUT_DEVICE_JOY_B, CONTROL_RIGHT_PACK ) )
2041             SET_BIT( sum.b, LATCHBUTTON_PACKRIGHT );
2042     }
2043 
2044     // Keyboard routines
2045     if ( HAS_SOME_BITS( pdevice->bits, INPUT_BITS_KEYBOARD ) && keyb.on )
2046     {
2047         fvec2_t joy_pos, joy_new;
2048 
2049         fvec2_self_clear( joy_new.v );
2050         fvec2_self_clear( joy_pos.v );
2051 
2052         if (( CAM_TURN_GOOD == PCamera->turn_mode && 1 == local_numlpla ) ||
2053             !control_is_pressed( INPUT_DEVICE_KEYBOARD, CONTROL_CAMERA ) )
2054         {
2055             joy_pos.x = ( (int)control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_RIGHT ) - (int)control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_LEFT ) );
2056             joy_pos.y = ( (int)control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_DOWN ) - (int)control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_UP ) );
2057 
2058             if ( CAM_TURN_GOOD == PCamera->turn_mode &&
2059                  1 == local_numlpla )  joy_pos.x = 0;
2060 
2061             joy_new.x = ( joy_pos.x * fcos + joy_pos.y * fsin );
2062             joy_new.y = ( -joy_pos.x * fsin + joy_pos.y * fcos );
2063         }
2064 
2065         sum.x += joy_new.x;
2066         sum.y += joy_new.y;
2067 
2068         // Read buttons
2069         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_JUMP ) )
2070             sum.b |= LATCHBUTTON_JUMP;
2071         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_LEFT_USE ) )
2072             sum.b |= LATCHBUTTON_LEFT;
2073         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_LEFT_GET ) )
2074             sum.b |= LATCHBUTTON_ALTLEFT;
2075         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_LEFT_PACK ) )
2076             sum.b |= LATCHBUTTON_PACKLEFT;
2077         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_RIGHT_USE ) )
2078             sum.b |= LATCHBUTTON_RIGHT;
2079         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_RIGHT_GET ) )
2080             sum.b |= LATCHBUTTON_ALTRIGHT;
2081         if ( control_is_pressed( INPUT_DEVICE_KEYBOARD,  CONTROL_RIGHT_PACK ) )
2082             sum.b |= LATCHBUTTON_PACKRIGHT;
2083     }
2084 
2085     input_device_add_latch( pdevice, sum.x, sum.y );
2086 
2087     ppla->local_latch.x = pdevice->latch.x;
2088     ppla->local_latch.y = pdevice->latch.y;
2089     ppla->local_latch.b = sum.b;
2090 }
2091 
2092 //--------------------------------------------------------------------------------------------
set_local_latches(void)2093 void set_local_latches( void )
2094 {
2095     /// @details ZZ@> This function emulates AI thinkin' by setting latches from input devices
2096 
2097     PLA_REF cnt;
2098 
2099     for ( cnt = 0; cnt < MAX_PLAYER; cnt++ )
2100     {
2101         set_one_player_latch( cnt );
2102     }
2103 }
2104 
2105 //--------------------------------------------------------------------------------------------
check_stats()2106 void check_stats()
2107 {
2108     /// @details ZZ@> This function lets the players check character stats
2109 
2110     static int stat_check_timer = 0;
2111     static int stat_check_delay = 0;
2112 
2113     int ticks;
2114     if ( console_mode ) return;
2115 
2116     ticks = egoboo_get_ticks();
2117     if ( ticks > stat_check_timer + 20 )
2118     {
2119         stat_check_timer = ticks;
2120     }
2121 
2122     stat_check_delay -= 20;
2123     if ( stat_check_delay > 0 )
2124         return;
2125 
2126     // Show map cheat
2127     if ( cfg.dev_mode && SDLKEYDOWN( SDLK_m ) && SDLKEYDOWN( SDLK_LSHIFT ) && mapvalid )
2128     {
2129         mapon = !mapon;
2130         youarehereon = btrue;
2131         stat_check_delay = 150;
2132     }
2133 
2134     // XP CHEAT
2135     if ( cfg.dev_mode && SDLKEYDOWN( SDLK_x ) )
2136     {
2137         PLA_REF docheat = ( PLA_REF )MAX_PLAYER;
2138         if ( SDLKEYDOWN( SDLK_1 ) )  docheat = 0;
2139         else if ( SDLKEYDOWN( SDLK_2 ) )  docheat = 1;
2140         else if ( SDLKEYDOWN( SDLK_3 ) )  docheat = 2;
2141         else if ( SDLKEYDOWN( SDLK_4 ) )  docheat = 3;
2142 
2143         //Apply the cheat if valid
2144         if ( INGAME_CHR( PlaStack.lst[docheat].index ) )
2145         {
2146             Uint32 xpgain;
2147             chr_t * pchr = ChrList.lst + PlaStack.lst[docheat].index;
2148             cap_t * pcap = pro_get_pcap( pchr->profile_ref );
2149 
2150             //Give 10% of XP needed for next level
2151             xpgain = 0.1f * ( pcap->experience_forlevel[MIN( pchr->experiencelevel+1, MAXLEVEL )] - pcap->experience_forlevel[pchr->experiencelevel] );
2152             give_experience( pchr->ai.index, xpgain, XP_DIRECT, btrue );
2153             stat_check_delay = 1;
2154         }
2155     }
2156 
2157     // LIFE CHEAT
2158     if ( cfg.dev_mode && SDLKEYDOWN( SDLK_z ) )
2159     {
2160         PLA_REF docheat = ( PLA_REF )MAX_PLAYER;
2161 
2162         if ( SDLKEYDOWN( SDLK_1 ) )  docheat = 0;
2163         else if ( SDLKEYDOWN( SDLK_2 ) )  docheat = 1;
2164         else if ( SDLKEYDOWN( SDLK_3 ) )  docheat = 2;
2165         else if ( SDLKEYDOWN( SDLK_4 ) )  docheat = 3;
2166 
2167         //Apply the cheat if valid
2168         if ( INGAME_CHR( PlaStack.lst[docheat].index ) )
2169         {
2170             cap_t * pcap;
2171             chr_t * pchr = ChrList.lst + PlaStack.lst[docheat].index;
2172             pcap = pro_get_pcap( pchr->profile_ref );
2173 
2174             //Heal 1 life
2175             heal_character( pchr->ai.index, pchr->ai.index, 256, btrue );
2176             stat_check_delay = 1;
2177         }
2178     }
2179 
2180     // Display armor stats?
2181     if ( SDLKEYDOWN( SDLK_LSHIFT ) )
2182     {
2183         if ( SDLKEYDOWN( SDLK_1 ) )  { show_armor( 0 ); stat_check_delay = 1000; }
2184         if ( SDLKEYDOWN( SDLK_2 ) )  { show_armor( 1 ); stat_check_delay = 1000; }
2185         if ( SDLKEYDOWN( SDLK_3 ) )  { show_armor( 2 ); stat_check_delay = 1000; }
2186         if ( SDLKEYDOWN( SDLK_4 ) )  { show_armor( 3 ); stat_check_delay = 1000; }
2187         if ( SDLKEYDOWN( SDLK_5 ) )  { show_armor( 4 ); stat_check_delay = 1000; }
2188         if ( SDLKEYDOWN( SDLK_6 ) )  { show_armor( 5 ); stat_check_delay = 1000; }
2189         if ( SDLKEYDOWN( SDLK_7 ) )  { show_armor( 6 ); stat_check_delay = 1000; }
2190         if ( SDLKEYDOWN( SDLK_8 ) )  { show_armor( 7 ); stat_check_delay = 1000; }
2191     }
2192 
2193     // Display enchantment stats?
2194     else if ( SDLKEYDOWN( SDLK_LCTRL ) )
2195     {
2196         if ( SDLKEYDOWN( SDLK_1 ) )  { show_full_status( 0 ); stat_check_delay = 1000; }
2197         if ( SDLKEYDOWN( SDLK_2 ) )  { show_full_status( 1 ); stat_check_delay = 1000; }
2198         if ( SDLKEYDOWN( SDLK_3 ) )  { show_full_status( 2 ); stat_check_delay = 1000; }
2199         if ( SDLKEYDOWN( SDLK_4 ) )  { show_full_status( 3 ); stat_check_delay = 1000; }
2200         if ( SDLKEYDOWN( SDLK_5 ) )  { show_full_status( 4 ); stat_check_delay = 1000; }
2201         if ( SDLKEYDOWN( SDLK_6 ) )  { show_full_status( 5 ); stat_check_delay = 1000; }
2202         if ( SDLKEYDOWN( SDLK_7 ) )  { show_full_status( 6 ); stat_check_delay = 1000; }
2203         if ( SDLKEYDOWN( SDLK_8 ) )  { show_full_status( 7 ); stat_check_delay = 1000; }
2204     }
2205 
2206     // Display character special powers?
2207     else if ( SDLKEYDOWN( SDLK_LALT ) )
2208     {
2209         if ( SDLKEYDOWN( SDLK_1 ) )  { show_magic_status( 0 ); stat_check_delay = 1000; }
2210         if ( SDLKEYDOWN( SDLK_2 ) )  { show_magic_status( 1 ); stat_check_delay = 1000; }
2211         if ( SDLKEYDOWN( SDLK_3 ) )  { show_magic_status( 2 ); stat_check_delay = 1000; }
2212         if ( SDLKEYDOWN( SDLK_4 ) )  { show_magic_status( 3 ); stat_check_delay = 1000; }
2213         if ( SDLKEYDOWN( SDLK_5 ) )  { show_magic_status( 4 ); stat_check_delay = 1000; }
2214         if ( SDLKEYDOWN( SDLK_6 ) )  { show_magic_status( 5 ); stat_check_delay = 1000; }
2215         if ( SDLKEYDOWN( SDLK_7 ) )  { show_magic_status( 6 ); stat_check_delay = 1000; }
2216         if ( SDLKEYDOWN( SDLK_8 ) )  { show_magic_status( 7 ); stat_check_delay = 1000; }
2217     }
2218 
2219     // Display character stats?
2220     else
2221     {
2222         if ( SDLKEYDOWN( SDLK_1 ) )  { show_stat( 0 ); stat_check_delay = 1000; }
2223         if ( SDLKEYDOWN( SDLK_2 ) )  { show_stat( 1 ); stat_check_delay = 1000; }
2224         if ( SDLKEYDOWN( SDLK_3 ) )  { show_stat( 2 ); stat_check_delay = 1000; }
2225         if ( SDLKEYDOWN( SDLK_4 ) )  { show_stat( 3 ); stat_check_delay = 1000; }
2226         if ( SDLKEYDOWN( SDLK_5 ) )  { show_stat( 4 ); stat_check_delay = 1000; }
2227         if ( SDLKEYDOWN( SDLK_6 ) )  { show_stat( 5 ); stat_check_delay = 1000; }
2228         if ( SDLKEYDOWN( SDLK_7 ) )  { show_stat( 6 ); stat_check_delay = 1000; }
2229         if ( SDLKEYDOWN( SDLK_8 ) )  { show_stat( 7 ); stat_check_delay = 1000; }
2230     }
2231 }
2232 
2233 //--------------------------------------------------------------------------------------------
show_stat(int statindex)2234 void show_stat( int statindex )
2235 {
2236     /// @details ZZ@> This function shows the more specific stats for a character
2237 
2238     CHR_REF character;
2239     int     level;
2240     char    gender[8] = EMPTY_CSTR;
2241 
2242     if ( statindex < StatusList_count )
2243     {
2244         character = StatusList[statindex];
2245 
2246         if ( INGAME_CHR( character ) )
2247         {
2248             cap_t * pcap;
2249             chr_t * pchr = ChrList.lst + character;
2250 
2251             pcap = pro_get_pcap( pchr->profile_ref );
2252 
2253             // Name
2254             debug_printf( "=%s=", chr_get_name( GET_REF_PCHR( pchr ), CHRNAME_ARTICLE | CHRNAME_CAPITAL ) );
2255 
2256             // Level and gender and class
2257             gender[0] = 0;
2258             if ( pchr->alive )
2259             {
2260                 int itmp;
2261                 const char * gender_str;
2262 
2263                 gender_str = "";
2264                 switch ( pchr->gender )
2265                 {
2266                     case GENDER_MALE: gender_str = "male "; break;
2267                     case GENDER_FEMALE: gender_str = "female "; break;
2268                 }
2269 
2270                 level = 1 + pchr->experiencelevel;
2271                 itmp = level % 10;
2272                 if ( 1 == itmp )
2273                 {
2274                     debug_printf( "~%dst level %s%s", level, gender_str, pcap->classname );
2275                 }
2276                 else if ( 2 == itmp )
2277                 {
2278                     debug_printf( "~%dnd level %s%s", level, gender_str, pcap->classname );
2279                 }
2280                 else if ( 3 == itmp )
2281                 {
2282                     debug_printf( "~%drd level %s%s", level, gender_str, pcap->classname );
2283                 }
2284                 else
2285                 {
2286                     debug_printf( "~%dth level %s%s", level, gender_str, pcap->classname );
2287                 }
2288             }
2289             else
2290             {
2291                 debug_printf( "~Dead %s", pcap->classname );
2292             }
2293 
2294             // Stats
2295             debug_printf( "~STR:~%2d~WIS:~%2d~DEF:~%d", FP8_TO_INT( pchr->strength ), FP8_TO_INT( pchr->wisdom ), 255 - pchr->defense );
2296             debug_printf( "~INT:~%2d~DEX:~%2d~EXP:~%d", FP8_TO_INT( pchr->intelligence ), FP8_TO_INT( pchr->dexterity ), pchr->experience );
2297         }
2298     }
2299 }
2300 
2301 //--------------------------------------------------------------------------------------------
show_armor(int statindex)2302 void show_armor( int statindex )
2303 {
2304     /// @details ZF@> This function shows detailed armor information for the character
2305 
2306     STRING tmps;
2307     CHR_REF ichr;
2308 
2309     Uint8  skinlevel;
2310 
2311     cap_t * pcap;
2312     chr_t * pchr;
2313 
2314     if ( statindex >= StatusList_count ) return;
2315 
2316     ichr = StatusList[statindex];
2317     if ( !INGAME_CHR( ichr ) ) return;
2318 
2319     pchr = ChrList.lst + ichr;
2320     skinlevel = pchr->skin;
2321 
2322     pcap = chr_get_pcap( ichr );
2323     if ( NULL == pcap ) return;
2324 
2325     // Armor Name
2326     debug_printf( "=%s=", pcap->skinname[skinlevel] );
2327 
2328     // Armor Stats
2329     debug_printf( "~DEF: %d  SLASH:%3d~CRUSH:%3d POKE:%3d", 255 - pcap->defense[skinlevel],
2330                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_SLASH][skinlevel] ),
2331                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_CRUSH][skinlevel] ),
2332                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_POKE ][skinlevel] ) );
2333 
2334     debug_printf( "~HOLY:~%i~EVIL:~%i~FIRE:~%i~ICE:~%i~ZAP:~%i",
2335                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_HOLY][skinlevel] ),
2336                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_EVIL][skinlevel] ),
2337                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_FIRE][skinlevel] ),
2338                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_ICE ][skinlevel] ),
2339                   GET_DAMAGE_RESIST( pcap->damage_modifier[DAMAGE_ZAP ][skinlevel] ) );
2340 
2341     debug_printf( "~Type: %s", ( pcap->skindressy & ( 1 << skinlevel ) ) ? "Light Armor" : "Heavy Armor" );
2342 
2343     // jumps
2344     tmps[0] = CSTR_END;
2345     switch ( pcap->jumpnumber )
2346     {
2347         case 0:  snprintf( tmps, SDL_arraysize( tmps ), "None    (%i)", pchr->jumpnumberreset ); break;
2348         case 1:  snprintf( tmps, SDL_arraysize( tmps ), "Novice  (%i)", pchr->jumpnumberreset ); break;
2349         case 2:  snprintf( tmps, SDL_arraysize( tmps ), "Skilled (%i)", pchr->jumpnumberreset ); break;
2350         case 3:  snprintf( tmps, SDL_arraysize( tmps ), "Adept   (%i)", pchr->jumpnumberreset ); break;
2351         default: snprintf( tmps, SDL_arraysize( tmps ), "Master  (%i)", pchr->jumpnumberreset ); break;
2352     };
2353 
2354     debug_printf( "~Speed:~%3.0f~Jump Skill:~%s", pchr->maxaccel_reset*80, tmps );
2355 }
2356 
2357 //--------------------------------------------------------------------------------------------
get_chr_regeneration(chr_t * pchr,int * pliferegen,int * pmanaregen)2358 bool_t get_chr_regeneration( chr_t * pchr, int * pliferegen, int * pmanaregen )
2359 {
2360     /// @details ZF@> Get a character's life and mana regeneration, considering all sources
2361 
2362     int local_liferegen, local_manaregen;
2363     CHR_REF ichr;
2364 
2365     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
2366     ichr = GET_REF_PCHR( pchr );
2367 
2368     if ( NULL == pliferegen ) pliferegen = &local_liferegen;
2369     if ( NULL == pmanaregen ) pmanaregen = &local_manaregen;
2370 
2371     // set the base values
2372     ( *pmanaregen ) = pchr->manareturn / MANARETURNSHIFT;
2373     ( *pliferegen ) = pchr->life_return;
2374 
2375     // Don't forget to add gains and costs from enchants
2376     ENC_BEGIN_LOOP_ACTIVE( enchant, penc )
2377     {
2378         if ( penc->target_ref == ichr )
2379         {
2380             ( *pliferegen ) += penc->target_life;
2381             ( *pmanaregen ) += penc->target_mana;
2382         }
2383 
2384         if ( penc->owner_ref == ichr )
2385         {
2386             ( *pliferegen ) += penc->owner_life;
2387             ( *pmanaregen ) += penc->owner_mana;
2388         }
2389     }
2390     ENC_END_LOOP();
2391 
2392     return btrue;
2393 }
2394 
2395 //--------------------------------------------------------------------------------------------
show_full_status(int statindex)2396 void show_full_status( int statindex )
2397 {
2398     /// @details ZF@> This function shows detailed armor information for the character including magic
2399 
2400     CHR_REF character;
2401     int manaregen, liferegen;
2402     chr_t * pchr;
2403 
2404     if ( statindex >= StatusList_count ) return;
2405 
2406     character = StatusList[statindex];
2407     if ( !INGAME_CHR( character ) ) return;
2408     pchr = ChrList.lst + character;
2409 
2410     // clean up the enchant list
2411     cleanup_character_enchants( pchr );
2412 
2413     // Enchanted?
2414     debug_printf( "=%s is %s=", chr_get_name( GET_REF_PCHR( pchr ), CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ), INGAME_ENC( pchr->firstenchant ) ? "enchanted" : "unenchanted" );
2415 
2416     // Armor Stats
2417     debug_printf( "~DEF: %d  SLASH:%3d~CRUSH:%3d POKE:%3d",
2418                   255 - pchr->defense,
2419                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_SLASH] ),
2420                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_CRUSH] ),
2421                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_POKE ] ) );
2422 
2423     debug_printf( "~HOLY: %i~~EVIL:~%i~FIRE:~%i~ICE:~%i~ZAP: ~%i",
2424                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_HOLY] ),
2425                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_EVIL] ),
2426                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_FIRE] ),
2427                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_ICE ] ),
2428                   GET_DAMAGE_RESIST( pchr->damage_modifier[DAMAGE_ZAP ] ) );
2429 
2430     get_chr_regeneration( pchr, &liferegen, &manaregen );
2431 
2432     debug_printf( "Mana Regen:~%4.2f Life Regen:~%4.2f", FP8_TO_FLOAT( manaregen ), FP8_TO_FLOAT( liferegen ) );
2433 }
2434 
2435 //--------------------------------------------------------------------------------------------
show_magic_status(int statindex)2436 void show_magic_status( int statindex )
2437 {
2438     /// @details ZF@> Displays special enchantment effects for the character
2439 
2440     CHR_REF character;
2441     const char * missile_str;
2442     chr_t * pchr;
2443 
2444     if ( statindex >= StatusList_count ) return;
2445 
2446     character = StatusList[statindex];
2447 
2448     if ( !INGAME_CHR( character ) ) return;
2449     pchr = ChrList.lst + character;
2450 
2451     // clean up the enchant list
2452     cleanup_character_enchants( pchr );
2453 
2454     // Enchanted?
2455     debug_printf( "=%s is %s=", chr_get_name( GET_REF_PCHR( pchr ), CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ), INGAME_ENC( pchr->firstenchant ) ? "enchanted" : "unenchanted" );
2456 
2457     // Enchantment status
2458     debug_printf( "~See Invisible: %s~~See Kurses: %s",
2459                   pchr->see_invisible_level ? "Yes" : "No",
2460                   pchr->see_kurse_level ? "Yes" : "No" );
2461 
2462     debug_printf( "~Channel Life: %s~~Waterwalking: %s",
2463                   pchr->canchannel ? "Yes" : "No",
2464                   pchr->waterwalk ? "Yes" : "No" );
2465 
2466     switch ( pchr->missiletreatment )
2467     {
2468         case MISSILE_REFLECT: missile_str = "Reflect"; break;
2469         case MISSILE_DEFLECT: missile_str = "Deflect"; break;
2470 
2471         default:
2472         case MISSILE_NORMAL : missile_str = "None";    break;
2473     }
2474 
2475     debug_printf( "~Flying: %s~~Missile Protection: %s", ( pchr->flyheight > 0 ) ? "Yes" : "No", missile_str );
2476 }
2477 
2478 //--------------------------------------------------------------------------------------------
2479 //--------------------------------------------------------------------------------------------
tilt_characters_to_terrain()2480 void tilt_characters_to_terrain()
2481 {
2482     /// @details ZZ@> This function sets all of the character's starting tilt values
2483 
2484     Uint8 twist;
2485 
2486     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
2487     {
2488         if ( !INGAME_CHR( cnt ) ) continue;
2489 
2490         if ( pchr->stickybutt && mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
2491         {
2492             twist = PMesh->gmem.grid_list[pchr->onwhichgrid].twist;
2493             pchr->ori.map_facing_y = map_twist_y[twist];
2494             pchr->ori.map_facing_x = map_twist_x[twist];
2495         }
2496         else
2497         {
2498             pchr->ori.map_facing_y = MAP_TURN_OFFSET;
2499             pchr->ori.map_facing_x = MAP_TURN_OFFSET;
2500         }
2501     }
2502     CHR_END_LOOP();
2503 }
2504 
2505 //--------------------------------------------------------------------------------------------
import_dir_profiles_vfs(const char * dirname)2506 void import_dir_profiles_vfs( const char * dirname )
2507 {
2508     STRING newloadname;
2509     STRING filename;
2510     int cnt;
2511 
2512     if ( NULL == PMod || INVALID_CSTR( dirname ) ) return;
2513 
2514     if ( !PMod->importvalid || 0 == PMod->importamount ) return;
2515 
2516     for ( cnt = 0; cnt < PMod->importamount*MAXIMPORTPERPLAYER; cnt++ )
2517     {
2518         // Make sure the object exists...
2519         snprintf( filename, SDL_arraysize( filename ), "%s/temp%04d.obj", dirname, cnt );
2520         snprintf( newloadname, SDL_arraysize( newloadname ), "%s/data.txt", filename );
2521 
2522         if ( vfs_exists( newloadname ) )
2523         {
2524             // new player found
2525             if ( 0 == ( cnt % MAXIMPORTPERPLAYER ) ) import_data.player++;
2526 
2527             // store the slot info
2528             import_data.slot = cnt;
2529 
2530             // load it
2531             import_data.slot_lst[cnt] = load_one_profile_vfs( filename, MAX_PROFILE );
2532             import_data.max_slot      = MAX( import_data.max_slot, cnt );
2533         }
2534     }
2535 }
2536 
2537 //--------------------------------------------------------------------------------------------
load_all_profiles_import()2538 void load_all_profiles_import()
2539 {
2540     int cnt;
2541 
2542     // Clear the import slots...
2543     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
2544     {
2545         import_data.slot_lst[cnt] = 10000;
2546     }
2547     import_data.max_slot = -1;
2548 
2549     // This overwrites existing loaded slots that are loaded globally
2550     overrideslots = btrue;
2551     import_data.player = -1;
2552     import_data.slot   = -100;
2553 
2554     import_dir_profiles_vfs( "mp_import" );
2555     import_dir_profiles_vfs( "mp_remote" );
2556 }
2557 
2558 //--------------------------------------------------------------------------------------------
game_load_module_profiles(const char * modname)2559 void game_load_module_profiles( const char *modname )
2560 {
2561     /// @details BB@> Search for .obj directories int the module directory and load them
2562 
2563     vfs_search_context_t * ctxt;
2564     const char *filehandle;
2565     STRING newloadname;
2566 
2567     import_data.slot = -100;
2568     make_newloadname( modname, "objects", newloadname );
2569 
2570     ctxt = vfs_findFirst( newloadname, "obj", VFS_SEARCH_DIR );
2571     filehandle = vfs_search_context_get_current( ctxt );
2572 
2573     while ( NULL != ctxt && VALID_CSTR( filehandle ) )
2574     {
2575         load_one_profile_vfs( filehandle, MAX_PROFILE );
2576 
2577         ctxt = vfs_findNext( &ctxt );
2578         filehandle = vfs_search_context_get_current( ctxt );
2579     }
2580     vfs_findClose( &ctxt );
2581 }
2582 
2583 //--------------------------------------------------------------------------------------------
game_load_global_profiles()2584 void game_load_global_profiles()
2585 {
2586     // load all special objects
2587     load_one_profile_vfs( "mp_data/globalobjects/book.obj", SPELLBOOK );
2588 
2589     // load the objects from various import directories
2590     load_all_profiles_import();
2591 }
2592 
2593 //--------------------------------------------------------------------------------------------
game_load_all_profiles(const char * modname)2594 void game_load_all_profiles( const char *modname )
2595 {
2596     /// @details ZZ@> This function loads a module's local objects and overrides the global ones already loaded
2597 
2598     // Log all of the script errors
2599     parseerror = bfalse;
2600 
2601     // clear out any old object definitions
2602     release_all_pro();
2603 
2604     // load the global objects
2605     game_load_global_profiles();
2606 
2607     // load the objects from the module's directory
2608     game_load_module_profiles( modname );
2609 }
2610 
2611 //--------------------------------------------------------------------------------------------
chr_setup_apply(const CHR_REF ichr,spawn_file_info_t * pinfo)2612 bool_t chr_setup_apply( const CHR_REF ichr, spawn_file_info_t *pinfo )
2613 {
2614     chr_t * pchr, *pparent;
2615 
2616     // trap bad pointers
2617     if ( NULL == pinfo ) return bfalse;
2618 
2619     if ( !INGAME_CHR( ichr ) ) return bfalse;
2620     pchr = ChrList.lst + ichr;
2621 
2622     pparent = NULL;
2623     if ( INGAME_CHR( pinfo->parent ) ) pparent = ChrList.lst + pinfo->parent;
2624 
2625     pchr->money += pinfo->money;
2626     if ( pchr->money > MAXMONEY )  pchr->money = MAXMONEY;
2627     if ( pchr->money < 0 )  pchr->money = 0;
2628 
2629     pchr->ai.content = pinfo->content;
2630     pchr->ai.passage = pinfo->passage;
2631 
2632     if ( pinfo->attach == ATTACH_INVENTORY )
2633     {
2634         // Inventory character
2635         inventory_add_item( ichr, pinfo->parent );
2636 
2637         SET_BIT( pchr->ai.alert, ALERTIF_GRABBED );  // Make spellbooks change
2638         pchr->attachedto = pinfo->parent;  // Make grab work
2639         scr_run_chr_script( ichr );  // Empty the grabbed messages
2640 
2641         pchr->attachedto = ( CHR_REF )MAX_CHR;  // Fix grab
2642 
2643     }
2644     else if ( pinfo->attach == ATTACH_LEFT || pinfo->attach == ATTACH_RIGHT )
2645     {
2646         // Wielded character
2647         grip_offset_t grip_off = ( ATTACH_LEFT == pinfo->attach ) ? GRIP_LEFT : GRIP_RIGHT;
2648 
2649         if ( rv_success == attach_character_to_mount( ichr, pinfo->parent, grip_off ) )
2650         {
2651             // Handle the "grabbed" messages
2652             scr_run_chr_script( ichr );
2653         }
2654     }
2655 
2656     // Set the starting pinfo->level
2657     if ( pinfo->level > 0 )
2658     {
2659         while ( pchr->experiencelevel < pinfo->level && pchr->experience < MAXXP )
2660         {
2661             give_experience( ichr, 25, XP_DIRECT, btrue );
2662             do_level_up( ichr );
2663         }
2664     }
2665 
2666     // automatically identify and unkurse all player starting equipment? I think yes.
2667     if ( start_new_player && NULL != pparent && VALID_PLA( pparent->is_which_player ) )
2668     {
2669         chr_t *pitem;
2670         pchr->nameknown = btrue;
2671 
2672         //Unkurse both inhand items
2673         if ( INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) )
2674         {
2675             pitem = ChrList.lst + ichr;
2676             pitem->iskursed = bfalse;
2677         }
2678         if ( INGAME_CHR( pchr->holdingwhich[SLOT_RIGHT] ) )
2679         {
2680             pitem = ChrList.lst + ichr;
2681             pitem->iskursed = bfalse;
2682         }
2683 
2684     }
2685 
2686     // adjust the price of items that are spawned in a shop
2687 
2688     return btrue;
2689 }
2690 
2691 //--------------------------------------------------------------------------------------------
2692 // gcc does not define this function on linux (at least not Ubuntu),
2693 // but it is defined under MinGW, which is yucky.
2694 // I actually had to spend like 45 minutes looking up the compiler flags
2695 // to catch this... good documentation, guys!
2696 #if defined(__GNUC__) && !(defined (__MINGW) || defined(__MINGW32__))
strlwr(char * str)2697 int strlwr( char * str )
2698 {
2699     if ( NULL == str ) return -1;
2700 
2701     while ( CSTR_END != *str )
2702     {
2703         *str = tolower( *str );
2704         str++;
2705     }
2706 
2707     return 0;
2708 }
2709 #endif
2710 
2711 //--------------------------------------------------------------------------------------------
activate_spawn_file_load_object(spawn_file_info_t * psp_info)2712 bool_t activate_spawn_file_load_object( spawn_file_info_t * psp_info )
2713 {
2714     /// @details BB@> Try to load a global object named int psp_info->spawn_coment into
2715     ///               slot psp_info->slot
2716 
2717     STRING filename;
2718     PRO_REF ipro;
2719 
2720     if ( NULL == psp_info || psp_info->slot < 0 ) return bfalse;
2721 
2722     ipro = psp_info->slot;
2723     if ( LOADED_PRO( ipro ) ) return bfalse;
2724 
2725     // trim any excess spaces off the psp_info->spawn_coment
2726     str_trim( psp_info->spawn_coment );
2727 
2728     //If it is a reference to a random treasure table then get a random object from that table
2729     if ( '%' == psp_info->spawn_coment[0] )
2730     {
2731         get_random_treasure( psp_info->spawn_coment, SDL_arraysize( psp_info->spawn_coment ) );
2732     }
2733 
2734     if ( NULL == strstr( psp_info->spawn_coment, ".obj" ) )
2735     {
2736         strcat( psp_info->spawn_coment, ".obj" );
2737     }
2738 
2739     strlwr( psp_info->spawn_coment );
2740 
2741     // do the loading
2742     if ( CSTR_END != psp_info->spawn_coment[0] )
2743     {
2744         // we are relying on the virtual mount point "mp_objects", so use
2745         // the vfs/PHYSFS file naming conventions
2746         snprintf( filename, SDL_arraysize( filename ), "mp_objects/%s", psp_info->spawn_coment );
2747 
2748         psp_info->slot = load_one_profile_vfs( filename, psp_info->slot );
2749     }
2750 
2751     return LOADED_PRO(( PRO_REF ) psp_info->slot );
2752 }
2753 
2754 //--------------------------------------------------------------------------------------------
activate_spawn_file_spawn(spawn_file_info_t * psp_info)2755 bool_t activate_spawn_file_spawn( spawn_file_info_t * psp_info )
2756 {
2757     int     tnc, local_index = 0;
2758     CHR_REF new_object;
2759     chr_t * pobject;
2760     PRO_REF iprofile;
2761 
2762     if ( NULL == psp_info || !psp_info->do_spawn || psp_info->slot < 0 ) return bfalse;
2763 
2764     iprofile = ( PRO_REF )psp_info->slot;
2765 
2766     // Spawn the character
2767     new_object = spawn_one_character( psp_info->pos, iprofile, psp_info->team, psp_info->skin, psp_info->facing, psp_info->pname, ( CHR_REF )MAX_CHR );
2768     if ( !DEFINED_CHR( new_object ) ) return bfalse;
2769 
2770     pobject = ChrList.lst + new_object;
2771 
2772     // determine the attachment
2773     if ( psp_info->attach == ATTACH_NONE )
2774     {
2775         // Free character
2776         psp_info->parent = new_object;
2777         make_one_character_matrix( new_object );
2778     }
2779 
2780     chr_setup_apply( new_object, psp_info );
2781 
2782     // Turn on PlaStack.count input devices
2783     if ( psp_info->stat )
2784     {
2785         // what we do depends on what kind of module we're loading
2786         if ( 0 == PMod->importamount && PlaStack.count < PMod->playeramount )
2787         {
2788             // a single player module
2789 
2790             bool_t player_added;
2791 
2792             player_added = bfalse;
2793             if ( 0 == local_numlpla )
2794             {
2795                 // the first player gets everything
2796                 player_added = add_player( new_object, ( PLA_REF )PlaStack.count, ( Uint32 )( ~0 ) );
2797             }
2798             else
2799             {
2800                 PLA_REF ipla;
2801                 BIT_FIELD bits;
2802 
2803                 // each new player steals an input device from the 1st player
2804                 bits = 1 << local_numlpla;
2805                 for ( ipla = 0; ipla < MAX_PLAYER; ipla++ )
2806                 {
2807                     UNSET_BIT( PlaStack.lst[ipla].device.bits, bits );
2808                 }
2809 
2810                 player_added = add_player( new_object, ( PLA_REF )PlaStack.count, bits );
2811             }
2812 
2813             if ( start_new_player && player_added )
2814             {
2815                 // !!!! make sure the player is identified !!!!
2816                 pobject->nameknown = btrue;
2817             }
2818         }
2819         else if ( PlaStack.count < PMod->importamount && PlaStack.count < PMod->playeramount && PlaStack.count < ImportList.count )
2820         {
2821             // A multiplayer module
2822 
2823             bool_t player_added;
2824 
2825             local_index = -1;
2826             for ( tnc = 0; tnc < ImportList.count; tnc++ )
2827             {
2828                 if ( pobject->profile_ref <= import_data.max_slot && pobject->profile_ref < MAX_PROFILE )
2829                 {
2830                     int islot = REF_TO_INT( pobject->profile_ref );
2831 
2832                     if ( import_data.slot_lst[islot] == ImportList.lst[tnc].slot )
2833                     {
2834                         local_index = tnc;
2835                         break;
2836                     }
2837                 }
2838             }
2839 
2840             player_added = bfalse;
2841             if ( -1 != local_index )
2842             {
2843                 // It's a local PlaStack.count
2844                 player_added = add_player( new_object, ( PLA_REF )PlaStack.count, ImportList.lst[local_index].bits );
2845             }
2846             else
2847             {
2848                 // It's a remote PlaStack.count
2849                 player_added = add_player( new_object, ( PLA_REF )PlaStack.count, INPUT_BITS_NONE );
2850             }
2851 
2852             // if for SOME REASON your player is not identified, give him
2853             // about a 50% chance to get identified every time you enter a module
2854             if ( player_added && !pobject->nameknown )
2855             {
2856                 float frand = rand() / ( float )RAND_MAX;
2857 
2858                 if ( frand > 0.5f )
2859                 {
2860                     pobject->nameknown = btrue;
2861                 }
2862             }
2863         }
2864 
2865         // Turn on the stat display
2866         statlist_add( new_object );
2867     }
2868 
2869     return btrue;
2870 }
2871 
2872 //--------------------------------------------------------------------------------------------
activate_spawn_file_vfs()2873 void activate_spawn_file_vfs()
2874 {
2875     /// @details ZZ@> This function sets up character data, loaded from "SPAWN.TXT"
2876 
2877     const char       *newloadname;
2878     vfs_FILE         *fileread;
2879     spawn_file_info_t sp_info;
2880 
2881     // tell everyone we are spawning a module
2882     activate_spawn_file_active = btrue;
2883 
2884     // Turn some back on
2885     newloadname = "mp_data/spawn.txt";
2886     fileread = vfs_openRead( newloadname );
2887 
2888     PlaStack.count = 0;
2889     sp_info.parent = ( CHR_REF )MAX_CHR;
2890     if ( NULL == fileread )
2891     {
2892         log_error( "Cannot read file: %s\n", newloadname );
2893     }
2894     else
2895     {
2896         spawn_file_info_t dynamic_list[MAX_PROFILE];        //These need to be dynamically loaded later
2897         STRING loaded_objects[MAX_PROFILE];                 //This is a list of all objects already loaded
2898         size_t i, dynamic_count = 0;                        //The length of dynamic_list
2899 
2900         //Empty the list of loaded objects
2901         memset( loaded_objects, CSTR_END, SDL_arraysize( loaded_objects ) );
2902 
2903         sp_info.parent = ( CHR_REF )MAX_CHR;
2904         while ( spawn_file_scan( fileread, &sp_info ) )
2905         {
2906             int save_slot = sp_info.slot;
2907 
2908             // check to see if the slot is valid
2909             if ( sp_info.slot >= MAX_PROFILE )
2910             {
2911                 log_warning( "Invalid slot %d for \"%s\" in file \"%s\"\n", sp_info.slot, sp_info.spawn_coment, newloadname );
2912                 continue;
2913             }
2914 
2915             // If it is a dynamic slot, then wait with loading it until we have load all the static slot numbers
2916             if ( sp_info.slot == -1 )
2917             {
2918                 dynamic_list[dynamic_count] = sp_info;
2919                 dynamic_count++;
2920                 continue;
2921             }
2922 
2923             // If nothing is already in that slot, try to load it.
2924             if ( !LOADED_PRO(( PRO_REF ) sp_info.slot ) )
2925             {
2926                 if ( activate_spawn_file_load_object( &sp_info ) )
2927                 {
2928                     // successfully loaded the object
2929                     strncpy( loaded_objects[sp_info.slot], sp_info.spawn_coment, SDL_arraysize( loaded_objects[sp_info.slot] ) );
2930                 }
2931                 else
2932                 {
2933                     // no, give a warning if it is useful
2934                     if ( save_slot > PMod->importamount * MAXIMPORTPERPLAYER )
2935                     {
2936                         log_warning( "The object \"%s\"(slot %d) in file \"%s\" does not exist on this machine\n", sp_info.spawn_coment, save_slot, newloadname );
2937                     }
2938                     continue;
2939                 }
2940             }
2941 
2942             if ( !LOADED_PRO(( PRO_REF ) sp_info.slot ) ) log_error( "This should not happen %s uses slot %i\n", sp_info.spawn_coment, sp_info.slot );
2943 
2944             // we only reach this if everything was loaded properly
2945             activate_spawn_file_spawn( &sp_info );
2946         }
2947 
2948         //Now finally do dynamic slot numbers
2949         for ( i = 0; i < dynamic_count; i++ )
2950         {
2951             bool_t already_loaded = bfalse;
2952             sp_info = dynamic_list[i];
2953 
2954             //First check if this object is already loaded before, no need to reload it then
2955             for ( sp_info.slot = MAXIMPORTPERPLAYER * 4; sp_info.slot < MAX_PROFILE; sp_info.slot++ )
2956             {
2957                 if ( 0 == strcmp( loaded_objects[sp_info.slot], sp_info.spawn_coment ) )
2958                 {
2959                     already_loaded = btrue;
2960                     break;
2961                 }
2962             }
2963 
2964             // It wasn't loaded yet so we need to do it here for the first time
2965             if ( !already_loaded )
2966             {
2967                 //Find first free slot and load the object in there
2968                 sp_info.slot = MAXIMPORTPERPLAYER * 4;
2969                 while ( LOADED_PRO(( PRO_REF ) sp_info.slot ) ) sp_info.slot++;
2970 
2971                 if ( activate_spawn_file_load_object( &sp_info ) )
2972                 {
2973                     // successfully loaded the object into a dynamic slot number
2974                     strncpy( loaded_objects[sp_info.slot], sp_info.spawn_coment, SDL_arraysize( loaded_objects[sp_info.slot] ) );
2975                 }
2976                 else
2977                 {
2978                     //something went wrong
2979                     log_warning( "Could not load object (%s) into dynamic slot number %i\n", sp_info.spawn_coment, sp_info.slot );
2980                     continue;
2981                 }
2982             }
2983 
2984             // we only reach this if everything was loaded properly
2985             activate_spawn_file_spawn( &sp_info );
2986         }
2987 
2988         vfs_close( fileread );
2989     }
2990 
2991     clear_messages();
2992 
2993     // Make sure local players are displayed first
2994     statlist_sort();
2995 
2996     // Fix tilting trees problem
2997     tilt_characters_to_terrain();
2998 
2999     // done spawning
3000     activate_spawn_file_active = btrue;
3001 }
3002 
3003 //--------------------------------------------------------------------------------------------
load_all_global_objects()3004 void load_all_global_objects()
3005 {
3006     /// @details ZF@> This function loads all global objects found on the mp_data mount point
3007 
3008     vfs_search_context_t * ctxt;
3009     const char *filehandle;
3010 
3011     // Warn the user for any duplicate slots
3012     overrideslots = bfalse;
3013 
3014     // Search for .obj directories and load them
3015     ctxt = vfs_findFirst( "mp_data/globalobjects", "obj", VFS_SEARCH_DIR );
3016     filehandle = vfs_search_context_get_current( ctxt );
3017 
3018     while ( NULL != ctxt && VALID_CSTR( filehandle ) )
3019     {
3020         load_one_profile_vfs( filehandle, MAX_PROFILE );
3021 
3022         ctxt = vfs_findNext( &ctxt );
3023         filehandle = vfs_search_context_get_current( ctxt );
3024     }
3025 
3026     vfs_findClose( &ctxt );
3027 }
3028 
3029 //--------------------------------------------------------------------------------------------
game_reset_module_data()3030 void game_reset_module_data()
3031 {
3032     // reset all
3033     log_info( "Resetting module data\n" );
3034 
3035     // unload a lot of data
3036     reset_teams();
3037     release_all_profiles();
3038     free_all_objects();
3039     reset_messages();
3040     chop_data_init( &chop_mem );
3041     game_reset_players();
3042 
3043     reset_end_text();
3044     renderlist_reset( &renderlist );
3045 }
3046 
3047 //--------------------------------------------------------------------------------------------
game_load_global_assets()3048 void game_load_global_assets()
3049 {
3050     // load a bunch of assets that are used in the module
3051 
3052     // Load all the global icons
3053     if ( !load_all_global_icons() )
3054     {
3055         log_warning( "Could not load all global icons!\n" );
3056     }
3057     load_blips();
3058     load_bars();
3059     font_bmp_load_vfs( "mp_data/font", "mp_data/font.txt" );
3060 }
3061 
3062 //--------------------------------------------------------------------------------------------
game_load_module_assets(const char * modname)3063 void game_load_module_assets( const char *modname )
3064 {
3065     // load a bunch of assets that are used in the module
3066     load_global_waves();
3067     reset_particles();
3068 
3069     if ( NULL == read_wawalite() )
3070     {
3071         log_warning( "wawalite.txt not loaded for %s.\n", modname );
3072     }
3073 
3074     load_basic_textures();
3075     load_map();
3076 
3077     upload_wawalite();
3078 }
3079 
3080 //--------------------------------------------------------------------------------------------
game_load_all_assets(const char * modname)3081 void game_load_all_assets( const char *modname )
3082 {
3083     game_load_global_assets();
3084 
3085     game_load_module_assets( modname );
3086 }
3087 
3088 //--------------------------------------------------------------------------------------------
game_setup_module(const char * smallname)3089 void game_setup_module( const char *smallname )
3090 {
3091     /// @details ZZ@> This runst the setup functions for a module
3092 
3093     STRING modname;
3094 
3095     // make sure the object lists are empty
3096     free_all_objects();
3097 
3098     // generate the module directory
3099     strncpy( modname, smallname, SDL_arraysize( modname ) );
3100     str_append_slash_net( modname, SDL_arraysize( modname ) );
3101 
3102     // ust the information in these files to load the module
3103     activate_passages_file_vfs();        // read and implement the "passage script" passages.txt
3104     activate_spawn_file_vfs();           // read and implement the "spawn script" spawn.txt
3105     activate_alliance_file_vfs();        // set up the non-default team interactions
3106 }
3107 
3108 //--------------------------------------------------------------------------------------------
game_load_module_data(const char * smallname)3109 bool_t game_load_module_data( const char *smallname )
3110 {
3111     /// @details ZZ@> This function loads a module
3112 
3113     STRING modname;
3114     ego_mpd_t * pmesh_rv;
3115 
3116     log_info( "Loading module \"%s\"\n", smallname );
3117 
3118     if ( load_ai_script_vfs( "mp_data/script.txt" ) < 0 )
3119     {
3120         log_warning( "game_load_module_data() - cannot load the default script\n" );
3121         goto game_load_module_data_fail;
3122     }
3123 
3124     // generate the module directory
3125     strncpy( modname, smallname, SDL_arraysize( modname ) );
3126     str_append_slash( modname, SDL_arraysize( modname ) );
3127 
3128     // load all module assets
3129     game_load_all_assets( modname );
3130 
3131     // load all module objects
3132     game_load_all_profiles( modname );
3133 
3134     pmesh_rv = mesh_load( modname, PMesh );
3135     if ( NULL == pmesh_rv )
3136     {
3137         // do not cause the program to fail, in case we are using a script function to load a module
3138         // just return a failure value and log a warning message for debugging purposes
3139         log_warning( "game_load_module_data() - Uh oh! Problems loading the mesh! (%s)\n", modname );
3140 
3141         goto game_load_module_data_fail;
3142     }
3143 
3144     mpd_BSP_system_begin( pmesh_rv );
3145 
3146     return btrue;
3147 
3148 game_load_module_data_fail:
3149 
3150     // release any data that might have been allocated
3151     game_release_module_data();
3152 
3153     return bfalse;
3154 }
3155 
3156 //--------------------------------------------------------------------------------------------
disaffirm_attached_particles(const CHR_REF character)3157 void disaffirm_attached_particles( const CHR_REF character )
3158 {
3159     /// @details ZZ@> This function makes sure a character has no attached particles
3160 
3161     PRT_BEGIN_LOOP_ACTIVE( iprt, prt_bdl )
3162     {
3163         if ( prt_bdl.prt_ptr->attachedto_ref == character )
3164         {
3165             end_one_particle_in_game( prt_bdl.prt_ref );
3166         }
3167     }
3168     PRT_END_LOOP();
3169 
3170     if ( INGAME_CHR( character ) )
3171     {
3172         // Set the alert for disaffirmation ( wet torch )
3173         SET_BIT( ChrList.lst[character].ai.alert, ALERTIF_DISAFFIRMED );
3174     }
3175 }
3176 
3177 //--------------------------------------------------------------------------------------------
number_of_attached_particles(const CHR_REF character)3178 int number_of_attached_particles( const CHR_REF character )
3179 {
3180     /// @details ZZ@> This function returns the number of particles attached to the given character
3181 
3182     int     cnt = 0;
3183 
3184     PRT_BEGIN_LOOP_ACTIVE( iprt, prt_bdl )
3185     {
3186         if ( prt_bdl.prt_ptr->attachedto_ref == character )
3187         {
3188             cnt++;
3189         }
3190     }
3191     PRT_END_LOOP();
3192 
3193     return cnt;
3194 }
3195 
3196 //--------------------------------------------------------------------------------------------
reaffirm_attached_particles(const CHR_REF character)3197 int reaffirm_attached_particles( const CHR_REF character )
3198 {
3199     /// @details ZZ@> This function makes sure a character has all of it's particles
3200 
3201     int     number_added, number_attached;
3202     int     amount, attempts;
3203     PRT_REF particle;
3204     chr_t * pchr;
3205     cap_t * pcap;
3206 
3207     if ( !INGAME_CHR( character ) ) return 0;
3208     pchr = ChrList.lst + character;
3209 
3210     pcap = pro_get_pcap( pchr->profile_ref );
3211     if ( NULL == pcap ) return 0;
3212 
3213     amount = pcap->attachedprt_amount;
3214     if ( 0 == amount ) return 0;
3215 
3216     number_attached = number_of_attached_particles( character );
3217     if ( number_attached >= amount ) return 0;
3218 
3219     number_added = 0;
3220     for ( attempts = 0; attempts < amount && number_attached < amount; attempts++ )
3221     {
3222         particle = spawn_one_particle( pchr->pos, pchr->ori.facing_z, pchr->profile_ref, pcap->attachedprt_lpip, character, GRIP_LAST + number_attached, chr_get_iteam( character ), character, ( PRT_REF )MAX_PRT, number_attached, ( CHR_REF )MAX_CHR );
3223         if ( DEFINED_PRT( particle ) )
3224         {
3225             prt_t * pprt = PrtList.lst + particle;
3226 
3227             pprt = place_particle_at_vertex( pprt, character, pprt->attachedto_vrt_off );
3228             if ( NULL == pprt ) continue;
3229 
3230             number_added++;
3231             number_attached++;
3232         }
3233     }
3234 
3235     // Set the alert for reaffirmation ( for exploding barrels with fire )
3236     SET_BIT( pchr->ai.alert, ALERTIF_REAFFIRMED );
3237 
3238     return number_added;
3239 }
3240 
3241 //--------------------------------------------------------------------------------------------
game_quit_module()3242 void game_quit_module()
3243 {
3244     /// @details BB@> all of the de-initialization code after the module actually ends
3245 
3246     // stop the module
3247     game_module_stop( PMod );
3248 
3249     // get rid of the game/module data
3250     game_release_module_data();
3251 
3252     // turn off networking
3253     close_session();
3254 
3255     // reset the "ui" mouse state
3256     cursor_reset();
3257 
3258     // re-initialize all game/module data
3259     game_reset_module_data();
3260 
3261     // finish whatever in-game song is playing
3262     sound_finish_sound();
3263 
3264     // remove the module-dependent mount points from the vfs
3265     game_clear_vfs_paths();
3266 }
3267 
3268 //--------------------------------------------------------------------------------------------
game_clear_vfs_paths()3269 void game_clear_vfs_paths()
3270 {
3271     /// @details BB@> clear out the all mount points
3272 
3273     // clear out the basic mount points
3274     egoboo_clear_vfs_paths();
3275 
3276     // clear out the module's mount points
3277     vfs_remove_mount_point( "mp_objects" );
3278 
3279     // set up the basic mount points again
3280     egoboo_setup_vfs_paths();
3281 }
3282 
3283 //--------------------------------------------------------------------------------------------
game_setup_vfs_paths(const char * mod_path)3284 bool_t game_setup_vfs_paths( const char * mod_path )
3285 {
3286     /// @details BB@> set up the virtual mount points for the module's data
3287     ///               and objects
3288 
3289     const char * path_seperator_1, * path_seperator_2;
3290     const char * mod_dir_ptr;
3291     STRING mod_dir_string;
3292 
3293     STRING tmpDir;
3294 
3295     if ( INVALID_CSTR( mod_path ) ) return bfalse;
3296 
3297     // revert to the program's basic mount points
3298     game_clear_vfs_paths();
3299 
3300     path_seperator_1 = strrchr( mod_path, SLASH_CHR );
3301     path_seperator_2 = strrchr( mod_path, NET_SLASH_CHR );
3302     path_seperator_1 = MAX( path_seperator_1, path_seperator_2 );
3303 
3304     if ( NULL == path_seperator_1 )
3305     {
3306         mod_dir_ptr = mod_path;
3307     }
3308     else
3309     {
3310         mod_dir_ptr = path_seperator_1 + 1;
3311     }
3312 
3313     strncpy( mod_dir_string, mod_dir_ptr, SDL_arraysize( mod_dir_string ) );
3314 
3315     //==== set the module-dependent mount points
3316 
3317     //---- add the "/modules/*.mod/objects" directories to mp_objects
3318     snprintf( tmpDir, sizeof( tmpDir ), "modules" SLASH_STR "%s" SLASH_STR "objects", mod_dir_string );
3319 
3320     // mount the user's module objects directory at the beginning of the mount point list
3321     vfs_add_mount_point( fs_getDataDirectory(), tmpDir, "mp_objects", 1 );
3322 
3323     // mount the global module objects directory next in the mount point list
3324     vfs_add_mount_point( fs_getUserDirectory(), tmpDir, "mp_objects", 1 );
3325 
3326     //---- add the "/basicdat/globalobjects/*" directories to mp_objects
3327     //ZF> TODO: Maybe we should dynamically search for all folders in this directory and add them as valid mount points?
3328     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "items",            "mp_objects", 1 );
3329     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "magic",            "mp_objects", 1 );
3330     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "magic_item",       "mp_objects", 1 );
3331     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "misc",             "mp_objects", 1 );
3332     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "monsters",         "mp_objects", 1 );
3333     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "players",          "mp_objects", 1 );
3334     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "potions",          "mp_objects", 1 );
3335     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "unique",           "mp_objects", 1 );
3336     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "weapons",          "mp_objects", 1 );
3337     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "work_in_progress", "mp_objects", 1 );
3338     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "traps",            "mp_objects", 1 );
3339     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "pets",             "mp_objects", 1 );
3340     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "scrolls",          "mp_objects", 1 );
3341     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalobjects" SLASH_STR "armor",            "mp_objects", 1 );
3342 
3343     //---- add the "/modules/*.mod/gamedat" directory to mp_data
3344     snprintf( tmpDir, sizeof( tmpDir ), "modules" SLASH_STR "%s" SLASH_STR "gamedat",  mod_dir_string );
3345 
3346     // mount the user's module gamedat directory at the beginning of the mount point list
3347     vfs_add_mount_point( fs_getUserDirectory(), tmpDir, "mp_data", 1 );
3348 
3349     // append the global module gamedat directory
3350     vfs_add_mount_point( fs_getDataDirectory(), tmpDir, "mp_data", 1 );
3351 
3352     // put the global globalparticles data after the module gamedat data
3353     vfs_add_mount_point( fs_getDataDirectory(), "basicdat" SLASH_STR "globalparticles", "mp_data", 1 );
3354 
3355     return btrue;
3356 }
3357 
3358 //--------------------------------------------------------------------------------------------
game_begin_module(const char * modname,Uint32 seed)3359 bool_t game_begin_module( const char * modname, Uint32 seed )
3360 {
3361     /// @details BB@> all of the initialization code before the module actually starts
3362 
3363     if ((( Uint32 )( ~0 ) ) == seed ) seed = time( NULL );
3364 
3365     // make sure the old game has been quit
3366     game_quit_module();
3367 
3368     // make sure that the object lists are in a good state
3369     reset_all_object_lists();
3370 
3371     // set up the virtual file system for the module
3372     if ( !game_setup_vfs_paths( modname ) ) return bfalse;
3373 
3374     // load all the in-game module data
3375     srand( seed );
3376     if ( !game_load_module_data( modname ) )
3377     {
3378         game_module_stop( PMod );
3379         return bfalse;
3380     };
3381 
3382     game_setup_module( modname );
3383 
3384     // make sure the per-module configuration settings are correct
3385     setup_synch( &cfg );
3386 
3387     // initialize the game objects
3388     initialize_all_objects();
3389     cursor_reset();
3390     game_module_reset( PMod, seed );
3391     camera_reset( PCamera, PMesh );
3392     update_all_character_matrices();
3393     attach_all_particles();
3394 
3395     // log debug info for every object loaded into the module
3396     if ( cfg.dev_mode ) log_madused_vfs( "/debug/slotused.txt" );
3397 
3398     // initialize the network
3399     net_initialize();
3400     net_sayHello();
3401 
3402     // start the module
3403     game_module_start( PMod );
3404 
3405     // initialize the timers as the very last thing
3406     timeron = bfalse;
3407     reset_timers();
3408 
3409     return btrue;
3410 }
3411 
3412 //--------------------------------------------------------------------------------------------
game_update_imports()3413 bool_t game_update_imports()
3414 {
3415     /// @details BB@> This function saves all the players to the players dir
3416     ///    and also copies them into the imports dir to prepare for the next module
3417 
3418     // save the players and their inventories to their original directory
3419     if ( PMod->exportvalid )
3420     {
3421         // export the players
3422         export_all_players( bfalse );
3423 
3424         // update the import list
3425         Import_list_from_players( &ImportList );
3426     }
3427 
3428     // erase the data in the import folder
3429     vfs_removeDirectoryAndContents( "import", VFS_TRUE );
3430 
3431     // copy the import data back into the import folder
3432     game_copy_imports( &ImportList );
3433 
3434     return btrue;
3435 }
3436 
3437 //--------------------------------------------------------------------------------------------
game_release_module_data()3438 void game_release_module_data()
3439 {
3440     /// @details ZZ@> This function frees up memory used by the module
3441 
3442     ego_mpd_t * ptmp;
3443 
3444     // Disable ESP
3445     local_stats.sense_enemies_idsz = IDSZ_NONE;
3446     local_stats.sense_enemies_team = ( TEAM_REF ) TEAM_MAX;
3447 
3448     // make sure that the object lists are cleared out
3449     free_all_objects();
3450 
3451     // deal with dynamically allocated game assets
3452     release_all_graphics();
3453     release_all_profiles();
3454     release_all_ai_scripts();
3455 
3456     // deal with the mesh
3457     clear_all_passages();
3458 
3459     // delete the mesh data
3460     ptmp = PMesh;
3461     mesh_destroy( &ptmp );
3462 
3463     // delete the mesh BSP data
3464     mpd_BSP_system_end();
3465 
3466     // restore the original statically allocated ego_mpd_t header
3467     PMesh = _mesh + 0;
3468 }
3469 
3470 //--------------------------------------------------------------------------------------------
attach_one_particle(prt_bundle_t * pbdl_prt)3471 bool_t attach_one_particle( prt_bundle_t * pbdl_prt )
3472 {
3473     prt_t * pprt;
3474     chr_t * pchr;
3475 
3476     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
3477     pprt = pbdl_prt->prt_ptr;
3478 
3479     if ( !INGAME_CHR( pbdl_prt->prt_ptr->attachedto_ref ) ) return bfalse;
3480     pchr = ChrList.lst + pbdl_prt->prt_ptr->attachedto_ref;
3481 
3482     pprt = place_particle_at_vertex( pprt, pprt->attachedto_ref, pprt->attachedto_vrt_off );
3483     if ( NULL == pprt ) return bfalse;
3484 
3485     // the previous function can inactivate a particle
3486     if ( ACTIVE_PPRT( pprt ) )
3487     {
3488         // Correct facing so swords knock characters in the right direction...
3489         if ( NULL != pbdl_prt->pip_ptr && HAS_SOME_BITS( pbdl_prt->pip_ptr->damfx, DAMFX_TURN ) )
3490         {
3491             pprt->facing = pchr->ori.facing_z;
3492         }
3493     }
3494 
3495     return btrue;
3496 }
3497 
3498 //--------------------------------------------------------------------------------------------
attach_all_particles()3499 void attach_all_particles()
3500 {
3501     /// @details ZZ@> This function attaches particles to their characters so everything gets
3502     ///    drawn right
3503 
3504     PRT_BEGIN_LOOP_DISPLAY( cnt, prt_bdl )
3505     {
3506         attach_one_particle( &prt_bdl );
3507     }
3508     PRT_END_LOOP()
3509 }
3510 
3511 //--------------------------------------------------------------------------------------------
add_player(const CHR_REF character,const PLA_REF player,Uint32 device_bits)3512 bool_t add_player( const CHR_REF character, const PLA_REF player, Uint32 device_bits )
3513 {
3514     /// @details ZZ@> This function adds a player, returning bfalse if it fails, btrue otherwise
3515 
3516     player_t * ppla = NULL;
3517     chr_t    * pchr = NULL;
3518 
3519     if ( !VALID_PLA_RANGE( player ) ) return bfalse;
3520     ppla = PlaStack.lst + player;
3521 
3522     // does the player already exist?
3523     if ( ppla->valid ) return bfalse;
3524 
3525     // re-construct the players
3526     pla_reinit( ppla );
3527 
3528     if ( !DEFINED_CHR( character ) ) return bfalse;
3529     pchr = ChrList.lst + character;
3530 
3531     // set the reference to the player
3532     pchr->is_which_player = player;
3533 
3534     // download the quest info
3535     quest_log_download_vfs( ppla->quest_log, SDL_arraysize( ppla->quest_log ), chr_get_dir_name( character ) );
3536 
3537     //---- skeleton for using a ConfigFile to save quests
3538     // ppla->quest_file = quest_file_open( chr_get_dir_name(character) );
3539 
3540     ppla->index       = character;
3541     ppla->valid       = btrue;
3542     ppla->device.bits = device_bits;
3543 
3544     if ( device_bits != EMPTY_BIT_FIELD )
3545     {
3546         local_stats.noplayers = bfalse;
3547         pchr->islocalplayer = btrue;
3548         local_numlpla++;
3549 
3550         // reset the camera
3551         camera_reset_target( PCamera, PMesh );
3552     }
3553 
3554     PlaStack.count++;
3555 
3556     return btrue;
3557 }
3558 
3559 //--------------------------------------------------------------------------------------------
let_all_characters_think()3560 void let_all_characters_think()
3561 {
3562     /// @details ZZ@> This function funst the ai scripts for all eligible objects
3563 
3564     static Uint32 last_update = ( Uint32 )( ~0 );
3565 
3566     // make sure there is only one script update per game update
3567     if ( update_wld == last_update ) return;
3568     last_update = update_wld;
3569 
3570     blip_count = 0;
3571 
3572     CHR_BEGIN_LOOP_ACTIVE( character, pchr )
3573     {
3574         cap_t * pcap;
3575 
3576         bool_t is_crushed, is_cleanedup, can_think;
3577 
3578         pcap = chr_get_pcap( character );
3579         if ( NULL == pcap ) continue;
3580 
3581         // check for actions that must always be handled
3582         is_cleanedup = HAS_SOME_BITS( pchr->ai.alert, ALERTIF_CLEANEDUP );
3583         is_crushed   = HAS_SOME_BITS( pchr->ai.alert, ALERTIF_CRUSHED );
3584 
3585         // let the script run sometimes even if the item is in your backpack
3586         can_think = !pchr->pack.is_packed || pcap->isequipment;
3587 
3588         // only let dead/destroyed things think if they have beem crushed/cleanedup
3589         if (( pchr->alive && can_think ) || is_crushed || is_cleanedup )
3590         {
3591             // Figure out alerts that weren't already set
3592             set_alerts( character );
3593 
3594             // Cleaned up characters shouldn't be alert to anything else
3595             if ( is_cleanedup )  { pchr->ai.alert = ALERTIF_CLEANEDUP; /*pchr->ai.timer = update_wld + 1;*/ }
3596 
3597             // Crushed characters shouldn't be alert to anything else
3598             if ( is_crushed )  { pchr->ai.alert = ALERTIF_CRUSHED; pchr->ai.timer = update_wld + 1; }
3599 
3600             scr_run_chr_script( character );
3601         }
3602     }
3603     CHR_END_LOOP();
3604 }
3605 
3606 //--------------------------------------------------------------------------------------------
game_begin_menu(menu_process_t * mproc,which_menu_t which)3607 bool_t game_begin_menu( menu_process_t * mproc, which_menu_t which )
3608 {
3609     if ( NULL == mproc ) return bfalse;
3610 
3611     if ( !process_running( PROC_PBASE( mproc ) ) )
3612     {
3613         GProc->menu_depth = mnu_get_menu_depth();
3614     }
3615 
3616     if ( mnu_begin_menu( which ) )
3617     {
3618         process_start( PROC_PBASE( mproc ) );
3619     }
3620 
3621     return btrue;
3622 }
3623 
3624 //--------------------------------------------------------------------------------------------
game_end_menu(menu_process_t * mproc)3625 void game_end_menu( menu_process_t * mproc )
3626 {
3627     mnu_end_menu();
3628 
3629     if ( mnu_get_menu_depth() <= GProc->menu_depth )
3630     {
3631         process_resume( PROC_PBASE( MProc ) );
3632         GProc->menu_depth = -1;
3633     }
3634 }
3635 
3636 //--------------------------------------------------------------------------------------------
game_finish_module()3637 void game_finish_module()
3638 {
3639     // export all the local and remote characters
3640     game_update_imports();
3641 
3642     // restart the menu song
3643     sound_play_song( MENU_SONG, 0, -1 );
3644 
3645     // quit the old module
3646 //    game_quit_module();       //@note: ZF> uncommented, but might cause a texture allocation bug?
3647 }
3648 
3649 //--------------------------------------------------------------------------------------------
free_all_objects(void)3650 void free_all_objects( void )
3651 {
3652     /// @details BB@> free every instance of the three object types used in the game.
3653 
3654     PrtList_free_all();
3655     EncList_free_all();
3656     free_all_chraracters();
3657 }
3658 
3659 //--------------------------------------------------------------------------------------------
reset_all_object_lists(void)3660 void reset_all_object_lists( void )
3661 {
3662     PrtList_init();
3663     ChrList_init();
3664     EncList_init();
3665 }
3666 
3667 //--------------------------------------------------------------------------------------------
3668 //--------------------------------------------------------------------------------------------
set_PMesh(ego_mpd_t * pmpd)3669 ego_mpd_t * set_PMesh( ego_mpd_t * pmpd )
3670 {
3671     ego_mpd_t * pmpd_old = PMesh;
3672 
3673     PMesh = pmpd;
3674 
3675     return pmpd_old;
3676 }
3677 
3678 //--------------------------------------------------------------------------------------------
set_PCamera(camera_t * pcam)3679 camera_t * set_PCamera( camera_t * pcam )
3680 {
3681     camera_t * pcam_old = PCamera;
3682 
3683     PCamera = pcam;
3684 
3685     // Matrix init stuff (from remove.c)
3686     rotmesh_topside    = (( float )sdl_scr.x / sdl_scr.y ) * CAM_ROTMESH_TOPSIDE / ( 1.33333f );
3687     rotmesh_bottomside = (( float )sdl_scr.x / sdl_scr.y ) * CAM_ROTMESH_BOTTOMSIDE / ( 1.33333f );
3688     rotmesh_up         = (( float )sdl_scr.x / sdl_scr.y ) * CAM_ROTMESH_UP / ( 1.33333f );
3689     rotmesh_down       = (( float )sdl_scr.x / sdl_scr.y ) * CAM_ROTMESH_DOWN / ( 1.33333f );
3690 
3691     return pcam_old;
3692 }
3693 
3694 //--------------------------------------------------------------------------------------------
get_mesh_level(ego_mpd_t * pmesh,float x,float y,bool_t waterwalk)3695 float get_mesh_level( ego_mpd_t * pmesh, float x, float y, bool_t waterwalk )
3696 {
3697     /// @details ZZ@> This function returns the height of a point within a mesh fan, precise
3698     ///    If waterwalk is nonzero and the fan is watery, then the level returned is the
3699     ///    level of the water.
3700 
3701     float zdone;
3702 
3703     zdone = mesh_get_level( pmesh, x, y );
3704 
3705     if ( waterwalk && water.surface_level > zdone && water.is_water )
3706     {
3707         int tile = mesh_get_grid( pmesh, x, y );
3708 
3709         if ( 0 != mesh_test_fx( pmesh, tile, MPDFX_WATER ) )
3710         {
3711             zdone = water.surface_level;
3712         }
3713     }
3714 
3715     return zdone;
3716 }
3717 
3718 //--------------------------------------------------------------------------------------------
make_water(water_instance_t * pinst,wawalite_water_t * pdata)3719 bool_t make_water( water_instance_t * pinst, wawalite_water_t * pdata )
3720 {
3721     /// @details ZZ@> This function sets up water movements
3722 
3723     int layer, frame, point, cnt;
3724     float temp;
3725     Uint8 spek;
3726 
3727     if ( NULL == pinst || NULL == pdata ) return bfalse;
3728 
3729     for ( layer = 0; layer < pdata->layer_count; layer++ )
3730     {
3731         pinst->layer[layer].tx.x = 0;
3732         pinst->layer[layer].tx.y = 0;
3733 
3734         for ( frame = 0; frame < MAXWATERFRAME; frame++ )
3735         {
3736             // Do first mode
3737             for ( point = 0; point < WATERPOINTS; point++ )
3738             {
3739                 temp = SIN(( frame * TWO_PI / MAXWATERFRAME ) + ( TWO_PI * point / WATERPOINTS ) + ( PI / 2 * layer / MAXWATERLAYER ) );
3740                 pinst->layer_z_add[layer][frame][point] = temp * pdata->layer[layer].amp;
3741             }
3742         }
3743     }
3744 
3745     // Calculate specular highlights
3746     spek = 0;
3747     for ( cnt = 0; cnt < 256; cnt++ )
3748     {
3749         spek = 0;
3750         if ( cnt > pdata->spek_start )
3751         {
3752             temp = cnt - pdata->spek_start;
3753             temp = temp / ( 256 - pdata->spek_start );
3754             temp = temp * temp;
3755             spek = temp * pdata->spek_level;
3756         }
3757 
3758         // [claforte] Probably need to replace this with a
3759         //           GL_DEBUG(glColor4f)(spek/256.0f, spek/256.0f, spek/256.0f, 1.0f) call:
3760         if ( GL_FLAT == gfx.shading )
3761             pinst->spek[cnt] = 0;
3762         else
3763             pinst->spek[cnt] = spek;
3764     }
3765 
3766     return btrue;
3767 }
3768 
3769 //--------------------------------------------------------------------------------------------
reset_end_text()3770 void reset_end_text()
3771 {
3772     /// @details ZZ@> This function resets the end-module text
3773 
3774     endtext_carat = snprintf( endtext, SDL_arraysize( endtext ), "The game has ended..." );
3775 
3776     /*
3777     if ( PlaStack.count > 1 )
3778     {
3779         endtext_carat = snprintf( endtext, SDL_arraysize( endtext), "Sadly, they were never heard from again..." );
3780     }
3781     else
3782     {
3783         if ( 0 == PlaStack.count )
3784         {
3785             // No players???
3786             endtext_carat = snprintf( endtext, SDL_arraysize( endtext), "The game has ended..." );
3787         }
3788         else
3789         {
3790             // One player
3791             endtext_carat = snprintf( endtext, SDL_arraysize( endtext), "Sadly, no trace was ever found..." );
3792         }
3793     }
3794     */
3795 
3796     str_add_linebreaks( endtext, endtext_carat, 20 );
3797 }
3798 
3799 //--------------------------------------------------------------------------------------------
expand_escape_codes(const CHR_REF ichr,script_state_t * pstate,char * src,char * src_end,char * dst,char * dst_end)3800 void expand_escape_codes( const CHR_REF ichr, script_state_t * pstate, char * src, char * src_end, char * dst, char * dst_end )
3801 {
3802     int    cnt;
3803     STRING szTmp;
3804 
3805     chr_t      * pchr, *ptarget, *powner;
3806     ai_state_t * pai;
3807 
3808     pchr    = !INGAME_CHR( ichr ) ? NULL : ChrList.lst + ichr;
3809     pai     = ( NULL == pchr )    ? NULL : &( pchr->ai );
3810 
3811     ptarget = (( NULL == pai ) || !INGAME_CHR( pai->target ) ) ? pchr : ChrList.lst + pai->target;
3812     powner  = (( NULL == pai ) || !INGAME_CHR( pai->owner ) ) ? pchr : ChrList.lst + pai->owner;
3813 
3814     cnt = 0;
3815     while ( CSTR_END != *src && src < src_end && dst < dst_end )
3816     {
3817         if ( '%' == *src )
3818         {
3819             char * ebuffer, * ebuffer_end;
3820 
3821             // go to the escape character
3822             src++;
3823 
3824             // set up the buffer to hold the escape data
3825             ebuffer     = szTmp;
3826             ebuffer_end = szTmp + SDL_arraysize( szTmp ) - 1;
3827 
3828             // make the excape buffer an empty string
3829             *ebuffer = CSTR_END;
3830 
3831             switch ( *src )
3832             {
3833                 case '%' : // the % symbol
3834                     {
3835                         snprintf( szTmp, SDL_arraysize( szTmp ), "%%" );
3836                     }
3837                     break;
3838 
3839                 case 'n' : // Name
3840                     {
3841                         snprintf( szTmp, SDL_arraysize( szTmp ), "%s", chr_get_name( ichr, CHRNAME_ARTICLE ) );
3842                     }
3843                     break;
3844 
3845                 case 'c':  // Class name
3846                     {
3847                         if ( NULL != pchr )
3848                         {
3849                             ebuffer     = chr_get_pcap( ichr )->classname;
3850                             ebuffer_end = ebuffer + SDL_arraysize( chr_get_pcap( ichr )->classname );
3851                         }
3852                     }
3853                     break;
3854 
3855                 case 't':  // Target name
3856                     {
3857                         if ( NULL != pai )
3858                         {
3859                             snprintf( szTmp, SDL_arraysize( szTmp ), "%s", chr_get_name( pai->target, CHRNAME_ARTICLE ) );
3860                         }
3861                     }
3862                     break;
3863 
3864                 case 'o':  // Owner name
3865                     {
3866                         if ( NULL != pai )
3867                         {
3868                             snprintf( szTmp, SDL_arraysize( szTmp ), "%s", chr_get_name( pai->owner, CHRNAME_ARTICLE ) );
3869                         }
3870                     }
3871                     break;
3872 
3873                 case 's':  // Target class name
3874                     {
3875                         if ( NULL != ptarget )
3876                         {
3877                             ebuffer     = chr_get_pcap( pai->target )->classname;
3878                             ebuffer_end = ebuffer + SDL_arraysize( chr_get_pcap( pai->target )->classname );
3879                         }
3880                     }
3881                     break;
3882 
3883                 case '0':
3884                 case '1':
3885                 case '2':
3886                 case '3': // Target's skin name
3887                     {
3888                         if ( NULL != ptarget )
3889                         {
3890                             ebuffer = chr_get_pcap( pai->target )->skinname[( *src )-'0'];
3891                             ebuffer_end = ebuffer + SDL_arraysize( chr_get_pcap( pai->target )->skinname[( *src )-'0'] );
3892                         }
3893                     }
3894                     break;
3895 
3896                 case 'a':  // Character's ammo
3897                     {
3898                         if ( NULL != pchr )
3899                         {
3900                             if ( pchr->ammoknown )
3901                             {
3902                                 snprintf( szTmp, SDL_arraysize( szTmp ), "%d", pchr->ammo );
3903                             }
3904                             else
3905                             {
3906                                 snprintf( szTmp, SDL_arraysize( szTmp ), "?" );
3907                             }
3908                         }
3909                     }
3910                     break;
3911 
3912                 case 'k':  // Kurse state
3913                     {
3914                         if ( NULL != pchr )
3915                         {
3916                             if ( pchr->iskursed )
3917                             {
3918                                 snprintf( szTmp, SDL_arraysize( szTmp ), "kursed" );
3919                             }
3920                             else
3921                             {
3922                                 snprintf( szTmp, SDL_arraysize( szTmp ), "unkursed" );
3923                             }
3924                         }
3925                     }
3926                     break;
3927 
3928                 case 'p':  // Character's possessive
3929                     {
3930                         if ( NULL != pchr )
3931                         {
3932                             if ( pchr->gender == GENDER_FEMALE )
3933                             {
3934                                 snprintf( szTmp, SDL_arraysize( szTmp ), "her" );
3935                             }
3936                             else if ( pchr->gender == GENDER_MALE )
3937                             {
3938                                 snprintf( szTmp, SDL_arraysize( szTmp ), "his" );
3939                             }
3940                             else
3941                             {
3942                                 snprintf( szTmp, SDL_arraysize( szTmp ), "its" );
3943                             }
3944                         }
3945                     }
3946                     break;
3947 
3948                 case 'm':  // Character's gender
3949                     {
3950                         if ( NULL != pchr )
3951                         {
3952                             if ( pchr->gender == GENDER_FEMALE )
3953                             {
3954                                 snprintf( szTmp, SDL_arraysize( szTmp ), "female " );
3955                             }
3956                             else if ( pchr->gender == GENDER_MALE )
3957                             {
3958                                 snprintf( szTmp, SDL_arraysize( szTmp ), "male " );
3959                             }
3960                             else
3961                             {
3962                                 snprintf( szTmp, SDL_arraysize( szTmp ), " " );
3963                             }
3964                         }
3965                     }
3966                     break;
3967 
3968                 case 'g':  // Target's possessive
3969                     {
3970                         if ( NULL != ptarget )
3971                         {
3972                             if ( ptarget->gender == GENDER_FEMALE )
3973                             {
3974                                 snprintf( szTmp, SDL_arraysize( szTmp ), "her" );
3975                             }
3976                             else if ( ptarget->gender == GENDER_MALE )
3977                             {
3978                                 snprintf( szTmp, SDL_arraysize( szTmp ), "his" );
3979                             }
3980                             else
3981                             {
3982                                 snprintf( szTmp, SDL_arraysize( szTmp ), "its" );
3983                             }
3984                         }
3985                     }
3986                     break;
3987 
3988                 case '#':  // New line (enter)
3989                     {
3990                         snprintf( szTmp, SDL_arraysize( szTmp ), "\n" );
3991                     }
3992                     break;
3993 
3994                 case 'd':  // tmpdistance value
3995                     {
3996                         if ( NULL != pstate )
3997                         {
3998                             snprintf( szTmp, SDL_arraysize( szTmp ), "%d", pstate->distance );
3999                         }
4000                     }
4001                     break;
4002 
4003                 case 'x':  // tmpx value
4004                     {
4005                         if ( NULL != pstate )
4006                         {
4007                             snprintf( szTmp, SDL_arraysize( szTmp ), "%d", pstate->x );
4008                         }
4009                     }
4010                     break;
4011 
4012                 case 'y':  // tmpy value
4013                     {
4014                         if ( NULL != pstate )
4015                         {
4016                             snprintf( szTmp, SDL_arraysize( szTmp ), "%d", pstate->y );
4017                         }
4018                     }
4019                     break;
4020 
4021                 case 'D':  // tmpdistance value
4022                     {
4023                         if ( NULL != pstate )
4024                         {
4025                             snprintf( szTmp, SDL_arraysize( szTmp ), "%2d", pstate->distance );
4026                         }
4027                     }
4028                     break;
4029 
4030                 case 'X':  // tmpx value
4031                     {
4032                         if ( NULL != pstate )
4033                         {
4034                             snprintf( szTmp, SDL_arraysize( szTmp ), "%2d", pstate->x );
4035                         }
4036                     }
4037                     break;
4038 
4039                 case 'Y':  // tmpy value
4040                     {
4041                         if ( NULL != pstate )
4042                         {
4043                             snprintf( szTmp, SDL_arraysize( szTmp ), "%2d", pstate->y );
4044                         }
4045                     }
4046                     break;
4047 
4048                 default:
4049                     snprintf( szTmp, SDL_arraysize( szTmp ), "%%%c???", ( *src ) );
4050                     break;
4051             }
4052 
4053             if ( CSTR_END == *ebuffer )
4054             {
4055                 ebuffer     = szTmp;
4056                 ebuffer_end = szTmp + SDL_arraysize( szTmp );
4057                 snprintf( szTmp, SDL_arraysize( szTmp ), "%%%c???", ( *src ) );
4058             }
4059 
4060             // make the line capitalized if necessary
4061             if ( 0 == cnt && NULL != ebuffer )  *ebuffer = toupper( *ebuffer );
4062 
4063             // Copy the generated text
4064             while ( CSTR_END != *ebuffer && ebuffer < ebuffer_end && dst < dst_end )
4065             {
4066                 *dst++ = *ebuffer++;
4067             }
4068             *dst = CSTR_END;
4069         }
4070         else
4071         {
4072             // Copy the letter
4073             *dst = *src;
4074             dst++;
4075         }
4076 
4077         src++;
4078         cnt++;
4079     }
4080 
4081     // make sure the destination string is terminated
4082     if ( dst < dst_end )
4083     {
4084         *dst = CSTR_END;
4085     }
4086     *dst_end = CSTR_END;
4087 }
4088 
4089 //--------------------------------------------------------------------------------------------
game_choose_module(int imod,int seed)4090 bool_t game_choose_module( int imod, int seed )
4091 {
4092     bool_t retval;
4093 
4094     if ( seed < 0 ) seed = time( NULL );
4095 
4096     if ( NULL == PMod ) PMod = &gmod;
4097 
4098     retval = game_module_setup( PMod, mnu_ModList_get_base( imod ), mnu_ModList_get_vfs_path( imod ), seed );
4099 
4100     if ( retval )
4101     {
4102         // give everyone virtual access to the game directories
4103         game_setup_vfs_paths( pickedmodule_path );
4104     }
4105 
4106     return retval;
4107 }
4108 
4109 //--------------------------------------------------------------------------------------------
game_process_init(game_process_t * gproc)4110 game_process_t * game_process_init( game_process_t * gproc )
4111 {
4112     if ( NULL == gproc ) return NULL;
4113 
4114     memset( gproc, 0, sizeof( *gproc ) );
4115 
4116     process_init( PROC_PBASE( gproc ) );
4117 
4118     gproc->menu_depth = -1;
4119     gproc->pause_key_ready = btrue;
4120 
4121     // initialize all the profile variables
4122     PROFILE_INIT( game_update_loop );
4123     PROFILE_INIT( game_single_update );
4124     PROFILE_INIT( gfx_loop );
4125 
4126     PROFILE_INIT( talk_to_remotes );
4127     PROFILE_INIT( listen_for_packets );
4128     PROFILE_INIT( check_stats );
4129     PROFILE_INIT( set_local_latches );
4130     PROFILE_INIT( check_passage_music );
4131     PROFILE_INIT( cl_talkToHost );
4132 
4133     return gproc;
4134 }
4135 
4136 //--------------------------------------------------------------------------------------------
4137 //--------------------------------------------------------------------------------------------
do_game_hud()4138 void do_game_hud()
4139 {
4140     int y = 0;
4141 
4142     if ( flip_pages_requested() && cfg.dev_mode )
4143     {
4144         GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
4145         if ( fpson )
4146         {
4147             y = draw_string( 0, y, "%2.3f FPS, %2.3f UPS", stabilized_fps, stabilized_ups );
4148             y = draw_string( 0, y, "estimated max FPS %2.3f", est_max_fps ); \
4149         }
4150 
4151         y = draw_string( 0, y, "Menu time %f", MProc->base.dtime );
4152     }
4153 }
4154 
4155 //--------------------------------------------------------------------------------------------
4156 //--------------------------------------------------------------------------------------------
collide_ray_with_mesh(line_of_sight_info_t * plos)4157 bool_t collide_ray_with_mesh( line_of_sight_info_t * plos )
4158 {
4159     Uint32 fan_last;
4160 
4161     int Dx, Dy;
4162     int ix, ix_stt, ix_end;
4163     int iy, iy_stt, iy_end;
4164 
4165     int Dbig, Dsmall;
4166     int ibig, ibig_stt, ibig_end;
4167     int ismall, ismall_stt, ismall_end;
4168     int dbig, dsmall;
4169     int TwoDsmall, TwoDsmallMinusTwoDbig, TwoDsmallMinusDbig;
4170 
4171     bool_t steep;
4172 
4173     if ( NULL == plos ) return bfalse;
4174 
4175     if ( 0 == plos->stopped_by ) return bfalse;
4176 
4177     ix_stt = FLOOR( plos->x0 / GRID_FSIZE );
4178     ix_end = FLOOR( plos->x1 / GRID_FSIZE );
4179 
4180     iy_stt = FLOOR( plos->y0 / GRID_FSIZE );
4181     iy_end = FLOOR( plos->y1 / GRID_FSIZE );
4182 
4183     Dx = plos->x1 - plos->x0;
4184     Dy = plos->y1 - plos->y0;
4185 
4186     steep = ( ABS( Dy ) >= ABS( Dx ) );
4187 
4188     // determine which are the big and small values
4189     if ( steep )
4190     {
4191         ibig_stt = iy_stt;
4192         ibig_end = iy_end;
4193 
4194         ismall_stt = ix_stt;
4195         ismall_end = ix_end;
4196     }
4197     else
4198     {
4199         ibig_stt = ix_stt;
4200         ibig_end = ix_end;
4201 
4202         ismall_stt = iy_stt;
4203         ismall_end = iy_end;
4204     }
4205 
4206     // set up the big loop variables
4207     dbig = 1;
4208     Dbig = ibig_end - ibig_stt;
4209     if ( Dbig < 0 )
4210     {
4211         dbig = -1;
4212         Dbig = -Dbig;
4213         ibig_end--;
4214     }
4215     else
4216     {
4217         ibig_end++;
4218     }
4219 
4220     // set up the small loop variables
4221     dsmall = 1;
4222     Dsmall = ismall_end - ismall_stt;
4223     if ( Dsmall < 0 )
4224     {
4225         dsmall = -1;
4226         Dsmall = -Dsmall;
4227     }
4228 
4229     // pre-compute some common values
4230     TwoDsmall             = 2 * Dsmall;
4231     TwoDsmallMinusTwoDbig = TwoDsmall - 2 * Dbig;
4232     TwoDsmallMinusDbig    = TwoDsmall - Dbig;
4233 
4234     fan_last = INVALID_TILE;
4235     for ( ibig = ibig_stt, ismall = ismall_stt;  ibig != ibig_end;  ibig += dbig )
4236     {
4237         Uint32 fan;
4238 
4239         if ( steep )
4240         {
4241             ix = ismall;
4242             iy = ibig;
4243         }
4244         else
4245         {
4246             ix = ibig;
4247             iy = ismall;
4248         }
4249 
4250         // check to see if the "ray" collides with the mesh
4251         fan = mesh_get_tile_int( PMesh, ix, iy );
4252         if ( INVALID_TILE != fan && fan != fan_last )
4253         {
4254             Uint32 collide_fx = mesh_test_fx( PMesh, fan, plos->stopped_by );
4255             // collide the ray with the mesh
4256 
4257             if ( 0 != collide_fx )
4258             {
4259                 plos->collide_x  = ix;
4260                 plos->collide_y  = iy;
4261                 plos->collide_fx = collide_fx;
4262 
4263                 return btrue;
4264             }
4265 
4266             fan_last = fan;
4267         }
4268 
4269         // go to the next step
4270         if ( TwoDsmallMinusDbig > 0 )
4271         {
4272             TwoDsmallMinusDbig += TwoDsmallMinusTwoDbig;
4273             ismall             += dsmall;
4274         }
4275         else
4276         {
4277             TwoDsmallMinusDbig += TwoDsmall;
4278         }
4279     }
4280 
4281     return bfalse;
4282 }
4283 
4284 //--------------------------------------------------------------------------------------------
collide_ray_with_characters(line_of_sight_info_t * plos)4285 bool_t collide_ray_with_characters( line_of_sight_info_t * plos )
4286 {
4287 
4288     if ( NULL == plos ) return bfalse;
4289 
4290     CHR_BEGIN_LOOP_ACTIVE( ichr, pchr )
4291     {
4292         // do line/character intersection
4293     }
4294     CHR_END_LOOP();
4295 
4296     return bfalse;
4297 }
4298 
4299 //--------------------------------------------------------------------------------------------
do_line_of_sight(line_of_sight_info_t * plos)4300 bool_t do_line_of_sight( line_of_sight_info_t * plos )
4301 {
4302     bool_t mesh_hit = bfalse, chr_hit = bfalse;
4303     mesh_hit = collide_ray_with_mesh( plos );
4304 
4305     /*if ( mesh_hit )
4306     {
4307         plos->x1 = (plos->collide_x + 0.5f) * GRID_FSIZE;
4308         plos->y1 = (plos->collide_y + 0.5f) * GRID_FSIZE;
4309     }
4310 
4311     chr_hit = collide_ray_with_characters( plos );
4312     */
4313 
4314     return mesh_hit || chr_hit;
4315 }
4316 
4317 //--------------------------------------------------------------------------------------------
game_reset_players()4318 void game_reset_players()
4319 {
4320     /// @details ZZ@> This function clears the player list data
4321 
4322     // Reset the local data stuff
4323     local_stats.allpladead = bfalse;
4324 
4325     local_stats.seeinvis_level = 0.0f;
4326     local_stats.seeinvis_level = 0.0f;
4327     local_stats.seekurse_level = 0.0f;
4328     local_stats.seedark_level  = 0.0f;
4329     local_stats.grog_level     = 0.0f;
4330     local_stats.daze_level     = 0.0f;
4331 
4332     local_stats.sense_enemies_team = ( TEAM_REF ) TEAM_MAX;
4333     local_stats.sense_enemies_idsz = IDSZ_NONE;
4334 
4335     net_reset_players();
4336 }
4337 
4338 //--------------------------------------------------------------------------------------------
4339 //--------------------------------------------------------------------------------------------
upload_water_layer_data(water_instance_layer_t inst[],wawalite_water_layer_t data[],int layer_count)4340 bool_t upload_water_layer_data( water_instance_layer_t inst[], wawalite_water_layer_t data[], int layer_count )
4341 {
4342     int layer;
4343 
4344     if ( NULL == inst || 0 == layer_count ) return bfalse;
4345 
4346     // clear all data
4347     memset( inst, 0, layer_count * sizeof( *inst ) );
4348 
4349     // set the frame
4350     for ( layer = 0; layer < layer_count; layer++ )
4351     {
4352         inst[layer].frame = generate_randmask( 0 , WATERFRAMEAND );
4353     }
4354 
4355     if ( NULL != data )
4356     {
4357         for ( layer = 0; layer < layer_count; layer++ )
4358         {
4359             inst[layer].z         = data[layer].z;
4360             inst[layer].amp       = data[layer].amp;
4361 
4362             inst[layer].dist      = data[layer].dist;
4363 
4364             inst[layer].light_dir = data[layer].light_dir / 63.0f;
4365             inst[layer].light_add = data[layer].light_add / 63.0f;
4366 
4367             inst[layer].tx_add    = data[layer].tx_add;
4368 
4369             inst[layer].alpha     = data[layer].alpha;
4370 
4371             inst[layer].frame_add = data[layer].frame_add;
4372         }
4373     }
4374 
4375     return btrue;
4376 }
4377 
4378 //--------------------------------------------------------------------------------------------
upload_water_data(water_instance_t * pinst,wawalite_water_t * pdata)4379 bool_t upload_water_data( water_instance_t * pinst, wawalite_water_t * pdata )
4380 {
4381     //int layer;
4382 
4383     if ( NULL == pinst ) return bfalse;
4384 
4385     memset( pinst, 0, sizeof( *pinst ) );
4386 
4387     if ( NULL != pdata )
4388     {
4389         // upload the data
4390 
4391         pinst->surface_level    = pdata->surface_level;
4392         pinst->douse_level      = pdata->douse_level;
4393 
4394         pinst->is_water         = pdata->is_water;
4395         pinst->overlay_req      = pdata->overlay_req;
4396         pinst->background_req   = pdata->background_req;
4397 
4398         pinst->light            = pdata->light;
4399 
4400         pinst->foregroundrepeat = pdata->foregroundrepeat;
4401         pinst->backgroundrepeat = pdata->backgroundrepeat;
4402 
4403         // upload the layer data
4404         pinst->layer_count   = pdata->layer_count;
4405         upload_water_layer_data( pinst->layer, pdata->layer, pdata->layer_count );
4406     }
4407 
4408     // fix the light in case of self-lit textures
4409     //if ( pdata->light )
4410     //{
4411     //    for ( layer = 0; layer < pinst->layer_count; layer++ )
4412     //    {
4413     //        pinst->layer[layer].light_add = 1.0f;  // Some cards don't support light lights...
4414     //    }
4415     //}
4416 
4417     make_water( pinst, pdata );
4418 
4419     // Allow slow machines to ignore the fancy stuff
4420     if ( !cfg.twolayerwater_allowed && pinst->layer_count > 1 )
4421     {
4422         int iTmp = pdata->layer[0].light_add;
4423         iTmp = ( pdata->layer[1].light_add * iTmp * INV_FF ) + iTmp;
4424         if ( iTmp > 255 ) iTmp = 255;
4425 
4426         pinst->layer_count        = 1;
4427         pinst->layer[0].light_add = iTmp * INV_FF;
4428     }
4429 
4430     return btrue;
4431 }
4432 
4433 //--------------------------------------------------------------------------------------------
upload_weather_data(weather_instance_t * pinst,wawalite_weather_t * pdata)4434 bool_t upload_weather_data( weather_instance_t * pinst, wawalite_weather_t * pdata )
4435 {
4436     if ( NULL == pinst ) return bfalse;
4437 
4438     memset( pinst, 0, sizeof( *pinst ) );
4439 
4440     // set a default value
4441     pinst->timer_reset = 10;
4442 
4443     if ( NULL != pdata )
4444     {
4445         // copy the data
4446         pinst->timer_reset = pdata->timer_reset;
4447         pinst->over_water  = pdata->over_water;
4448         pinst->part_gpip   = pdata->part_gpip;
4449     }
4450 
4451     // set the new data
4452     pinst->time = pinst->timer_reset;
4453 
4454     return btrue;
4455 }
4456 
4457 //--------------------------------------------------------------------------------------------
upload_fog_data(fog_instance_t * pinst,wawalite_fog_t * pdata)4458 bool_t upload_fog_data( fog_instance_t * pinst, wawalite_fog_t * pdata )
4459 {
4460     if ( NULL == pinst ) return bfalse;
4461 
4462     memset( pinst, 0, sizeof( *pinst ) );
4463 
4464     pdata->top      = 0;
4465     pdata->bottom   = -100;
4466     pinst->on       = cfg.fog_allowed;
4467 
4468     if ( NULL != pdata )
4469     {
4470         pinst->on     = pdata->found && pinst->on;
4471         pinst->top    = pdata->top;
4472         pinst->bottom = pdata->bottom;
4473 
4474         pinst->red    = pdata->red * 0xFF;
4475         pinst->grn    = pdata->grn * 0xFF;
4476         pinst->blu    = pdata->blu * 0xFF;
4477     }
4478 
4479     pinst->distance = ( pdata->top - pdata->bottom );
4480     pinst->on       = ( pinst->distance < 1.0f ) && pinst->on;
4481 
4482     return btrue;
4483 }
4484 
4485 //--------------------------------------------------------------------------------------------
upload_damagetile_data(damagetile_instance_t * pinst,wawalite_damagetile_t * pdata)4486 bool_t upload_damagetile_data( damagetile_instance_t * pinst, wawalite_damagetile_t * pdata )
4487 {
4488     if ( NULL == pinst ) return bfalse;
4489 
4490     memset( pinst, 0, sizeof( *pinst ) );
4491 
4492     //pinst->sound_time   = TILESOUNDTIME;
4493     //pinst->min_distance = 9999;
4494 
4495     if ( NULL != pdata )
4496     {
4497         pinst->amount.base  = pdata->amount;
4498         pinst->amount.rand  = 1;
4499         pinst->damagetype   = pdata->damagetype;
4500 
4501         pinst->part_gpip    = pdata->part_gpip;
4502         pinst->partand      = pdata->partand;
4503         pinst->sound_index  = CLIP( pdata->sound_index, INVALID_SOUND, MAX_WAVE );
4504     }
4505 
4506     return btrue;
4507 }
4508 
4509 //--------------------------------------------------------------------------------------------
upload_animtile_data(animtile_instance_t inst[],wawalite_animtile_t * pdata,size_t animtile_count)4510 bool_t upload_animtile_data( animtile_instance_t inst[], wawalite_animtile_t * pdata, size_t animtile_count )
4511 {
4512     Uint32 cnt;
4513 
4514     if ( NULL == inst || 0 == animtile_count ) return bfalse;
4515 
4516     memset( inst, 0, sizeof( *inst ) );
4517 
4518     for ( cnt = 0; cnt < animtile_count; cnt++ )
4519     {
4520         inst[cnt].frame_and  = ( 1 << ( cnt + 2 ) ) - 1;
4521         inst[cnt].base_and   = ~inst[cnt].frame_and;
4522         inst[cnt].frame_add  = 0;
4523     }
4524 
4525     if ( NULL != pdata )
4526     {
4527         inst[0].update_and = pdata->update_and;
4528         inst[0].frame_and  = pdata->frame_and;
4529         inst[0].base_and   = ~inst[0].frame_and;
4530 
4531         for ( cnt = 1; cnt < animtile_count; cnt++ )
4532         {
4533             inst[cnt].update_and = pdata->update_and;
4534             inst[cnt].frame_and  = ( inst[cnt-1].frame_and << 1 ) | 1;
4535             inst[cnt].base_and   = ~inst[cnt].frame_and;
4536         }
4537     }
4538 
4539     return btrue;
4540 }
4541 
4542 //--------------------------------------------------------------------------------------------
upload_light_data(wawalite_data_t * pdata)4543 bool_t upload_light_data( wawalite_data_t * pdata )
4544 {
4545     if ( NULL == pdata ) return bfalse;
4546 
4547     // upload the lighting data
4548     light_nrm[kX] = pdata->light_x;
4549     light_nrm[kY] = pdata->light_y;
4550     light_nrm[kZ] = pdata->light_z;
4551     light_a = pdata->light_a;
4552 
4553     if ( ABS( light_nrm[kX] ) + ABS( light_nrm[kY] ) + ABS( light_nrm[kZ] ) > 0.0f )
4554     {
4555         float fTmp = SQRT( light_nrm[kX] * light_nrm[kX] + light_nrm[kY] * light_nrm[kY] + light_nrm[kZ] * light_nrm[kZ] );
4556 
4557         // get the extra magnitude of the direct light
4558         if ( gfx.usefaredge )
4559         {
4560             // we are outside, do the direct light as sunlight
4561             light_d = 1.0f;
4562             light_a = light_a / fTmp;
4563             light_a = CLIP( light_a, 0.0f, 1.0f );
4564         }
4565         else
4566         {
4567             // we are inside. take the lighting values at face value.
4568             //light_d = (1.0f - light_a) * fTmp;
4569             //light_d = CLIP(light_d, 0.0f, 1.0f);
4570             light_d = CLIP( fTmp, 0.0f, 1.0f );
4571         }
4572 
4573         light_nrm[kX] /= fTmp;
4574         light_nrm[kY] /= fTmp;
4575         light_nrm[kZ] /= fTmp;
4576     }
4577 
4578     //make_lighttable( pdata->light_x, pdata->light_y, pdata->light_z, pdata->light_a );
4579     //make_lighttospek();
4580 
4581     return btrue;
4582 }
4583 
4584 //--------------------------------------------------------------------------------------------
upload_phys_data(wawalite_physics_t * pdata)4585 bool_t upload_phys_data( wawalite_physics_t * pdata )
4586 {
4587     if ( NULL == pdata ) return bfalse;
4588 
4589     // upload the physics data
4590     hillslide      = pdata->hillslide;
4591     slippyfriction = pdata->slippyfriction;
4592     noslipfriction = pdata->noslipfriction;
4593     airfriction    = pdata->airfriction;
4594     waterfriction  = pdata->waterfriction;
4595     gravity        = pdata->gravity;
4596 
4597     return btrue;
4598 }
4599 
4600 //--------------------------------------------------------------------------------------------
upload_graphics_data(wawalite_graphics_t * pdata)4601 bool_t upload_graphics_data( wawalite_graphics_t * pdata )
4602 {
4603     if ( NULL == pdata ) return bfalse;
4604 
4605     // Read extra data
4606     gfx.exploremode = pdata->exploremode;
4607     gfx.usefaredge  = pdata->usefaredge;
4608 
4609     return btrue;
4610 }
4611 
4612 //--------------------------------------------------------------------------------------------
upload_camera_data(wawalite_camera_t * pdata)4613 bool_t upload_camera_data( wawalite_camera_t * pdata )
4614 {
4615     if ( NULL == pdata ) return bfalse;
4616 
4617     PCamera->swing     = pdata->swing;
4618     PCamera->swingrate = pdata->swingrate;
4619     PCamera->swingamp  = pdata->swingamp;
4620 
4621     return btrue;
4622 }
4623 
4624 //--------------------------------------------------------------------------------------------
upload_wawalite()4625 void upload_wawalite()
4626 {
4627     /// @details ZZ@> This function sets up water and lighting for the module
4628 
4629     wawalite_data_t * pdata = &wawalite_data;
4630 
4631     upload_phys_data( &( pdata->phys ) );
4632     upload_graphics_data( &( pdata->graphics ) );
4633     upload_light_data( pdata );                         // this statement depends on data from upload_graphics_data()
4634     upload_camera_data( &( pdata->camera ) );
4635     upload_fog_data( &fog, &( pdata->fog ) );
4636     upload_water_data( &water, &( pdata->water ) );
4637     upload_weather_data( &weather, &( pdata->weather ) );
4638     upload_damagetile_data( &damagetile, &( pdata->damagetile ) );
4639     upload_animtile_data( animtile, &( pdata->animtile ), SDL_arraysize( animtile ) );
4640 }
4641 
4642 //--------------------------------------------------------------------------------------------
game_module_setup(game_module_t * pinst,mod_file_t * pdata,const char * loadname,Uint32 seed)4643 bool_t game_module_setup( game_module_t * pinst, mod_file_t * pdata, const char * loadname, Uint32 seed )
4644 {
4645     //Prepeares a module to be played
4646     if ( NULL == pdata ) return bfalse;
4647 
4648     if ( !game_module_init( pinst ) ) return bfalse;
4649 
4650     pinst->importamount   = pdata->importamount;
4651     pinst->exportvalid    = pdata->allowexport;
4652     pinst->exportreset    = pdata->allowexport;
4653     pinst->playeramount   = pdata->maxplayers;
4654     pinst->importvalid    = ( pinst->importamount > 0 );
4655     pinst->respawnvalid   = ( bfalse != pdata->respawnvalid );
4656     pinst->respawnanytime = ( RESPAWN_ANYTIME == pdata->respawnvalid );
4657 
4658     strncpy( pinst->loadname, loadname, SDL_arraysize( pinst->loadname ) );
4659 
4660     pinst->active = bfalse;
4661     pinst->beat   = bfalse;
4662     pinst->seed   = seed;
4663 
4664     return btrue;
4665 }
4666 
4667 //--------------------------------------------------------------------------------------------
game_module_init(game_module_t * pinst)4668 bool_t game_module_init( game_module_t * pinst )
4669 {
4670     if ( NULL == pinst ) return bfalse;
4671 
4672     memset( pinst, 0, sizeof( *pinst ) );
4673 
4674     pinst->seed = ( Uint32 )~0;
4675 
4676     return btrue;
4677 }
4678 
4679 //--------------------------------------------------------------------------------------------
game_module_reset(game_module_t * pinst,Uint32 seed)4680 bool_t game_module_reset( game_module_t * pinst, Uint32 seed )
4681 {
4682     if ( NULL == pinst ) return bfalse;
4683 
4684     pinst->beat        = bfalse;
4685     pinst->exportvalid = pinst->exportreset;
4686     pinst->seed        = seed;
4687 
4688     return btrue;
4689 }
4690 
4691 //--------------------------------------------------------------------------------------------
game_module_start(game_module_t * pinst)4692 bool_t game_module_start( game_module_t * pinst )
4693 {
4694     /// @details BB@> Let the module go
4695 
4696     if ( NULL == pinst ) return bfalse;
4697 
4698     pinst->active = btrue;
4699 
4700     srand( pinst->seed );
4701     pinst->randsave = rand();
4702     randindex = rand() % RANDIE_COUNT;
4703 
4704     PNet->hostactive = btrue; // very important or the input will not work
4705 
4706     return btrue;
4707 }
4708 
4709 //--------------------------------------------------------------------------------------------
game_module_stop(game_module_t * pinst)4710 bool_t game_module_stop( game_module_t * pinst )
4711 {
4712     /// @details BB@> stop the module
4713 
4714     if ( NULL == pinst ) return bfalse;
4715 
4716     pinst->active      = bfalse;
4717 
4718     // network stuff
4719     PNet->hostactive  = bfalse;
4720 
4721     return btrue;
4722 }
4723 
4724 //--------------------------------------------------------------------------------------------
read_wawalite()4725 wawalite_data_t * read_wawalite( /* const char *modname */ )
4726 {
4727     int cnt, waterspeed_count, windspeed_count;
4728 
4729     wawalite_data_t * pdata;
4730     wawalite_water_layer_t * ilayer;
4731 
4732     // if( INVALID_CSTR(modname) ) return NULL;
4733 
4734     pdata = read_wawalite_file_vfs( "mp_data/wawalite.txt", NULL );
4735     if ( NULL == pdata ) return NULL;
4736 
4737     memcpy( &wawalite_data, pdata, sizeof( wawalite_data_t ) );
4738 
4739     // limit some values
4740     wawalite_data.damagetile.sound_index = CLIP( wawalite_data.damagetile.sound_index, INVALID_SOUND, MAX_WAVE );
4741 
4742     for ( cnt = 0; cnt < MAXWATERLAYER; cnt++ )
4743     {
4744         wawalite_data.water.layer[cnt].light_dir = CLIP( wawalite_data.water.layer[cnt].light_dir, 0, 63 );
4745         wawalite_data.water.layer[cnt].light_add = CLIP( wawalite_data.water.layer[cnt].light_add, 0, 63 );
4746     }
4747 
4748     windspeed_count = 0;
4749     fvec3_self_clear( windspeed.v );
4750 
4751     waterspeed_count = 0;
4752     fvec3_self_clear( waterspeed.v );
4753 
4754     ilayer = wawalite_data.water.layer + 0;
4755     if ( wawalite_data.water.background_req )
4756     {
4757         // this is a bit complicated.
4758         // it is the best I can do at reverse engineering what I did in render_world_background()
4759 
4760         const float cam_height = 1500.0f;
4761         const float default_bg_repeat = 4.0f;
4762 
4763         windspeed_count++;
4764 
4765         windspeed.x += -ilayer->tx_add.x * GRID_FSIZE / ( wawalite_data.water.backgroundrepeat / default_bg_repeat ) * ( cam_height + 1.0f / ilayer->dist.x ) / cam_height;
4766         windspeed.y += -ilayer->tx_add.y * GRID_FSIZE / ( wawalite_data.water.backgroundrepeat / default_bg_repeat ) * ( cam_height + 1.0f / ilayer->dist.y ) / cam_height;
4767         windspeed.z += -0;
4768     }
4769     else
4770     {
4771         waterspeed_count++;
4772 
4773         waterspeed.x += -ilayer->tx_add.x * GRID_FSIZE;
4774         waterspeed.y += -ilayer->tx_add.y * GRID_FSIZE;
4775         waterspeed.z += -0;
4776     }
4777 
4778     ilayer = wawalite_data.water.layer + 1;
4779     if ( wawalite_data.water.overlay_req )
4780     {
4781         windspeed_count++;
4782 
4783         windspeed.x += -600 * ilayer->tx_add.x * GRID_FSIZE / wawalite_data.water.foregroundrepeat * 0.04f;
4784         windspeed.y += -600 * ilayer->tx_add.y * GRID_FSIZE / wawalite_data.water.foregroundrepeat * 0.04f;
4785         windspeed.z += -0;
4786     }
4787     else
4788     {
4789         waterspeed_count++;
4790 
4791         waterspeed.x += -ilayer->tx_add.x * GRID_FSIZE;
4792         waterspeed.y += -ilayer->tx_add.y * GRID_FSIZE;
4793         waterspeed.z += -0;
4794     }
4795 
4796     if ( waterspeed_count > 1 )
4797     {
4798         waterspeed.x /= ( float )waterspeed_count;
4799         waterspeed.y /= ( float )waterspeed_count;
4800         waterspeed.z /= ( float )waterspeed_count;
4801     }
4802 
4803     if ( windspeed_count > 1 )
4804     {
4805         windspeed.x /= ( float )windspeed_count;
4806         windspeed.y /= ( float )windspeed_count;
4807         windspeed.z /= ( float )windspeed_count;
4808     }
4809 
4810     return &wawalite_data;
4811 }
4812 
4813 //--------------------------------------------------------------------------------------------
write_wawalite(const char * modname,wawalite_data_t * pdata)4814 bool_t write_wawalite( const char *modname, wawalite_data_t * pdata )
4815 {
4816     /// @details BB@> Prepare and write the wawalite file
4817 
4818     int cnt;
4819 //    STRING filename;
4820 
4821     if ( !VALID_CSTR( modname ) || NULL == pdata ) return bfalse;
4822 
4823     // limit some values
4824     pdata->damagetile.sound_index = CLIP( pdata->damagetile.sound_index, INVALID_SOUND, MAX_WAVE );
4825 
4826     for ( cnt = 0; cnt < MAXWATERLAYER; cnt++ )
4827     {
4828         pdata->water.layer[cnt].light_dir = CLIP( pdata->water.layer[cnt].light_dir, 0, 63 );
4829         pdata->water.layer[cnt].light_add = CLIP( pdata->water.layer[cnt].light_add, 0, 63 );
4830     }
4831 
4832     return write_wawalite_file_vfs( pdata );
4833 }
4834 
4835 //--------------------------------------------------------------------------------------------
get_alpha(int alpha,float seeinvis_mag)4836 Uint8 get_alpha( int alpha, float seeinvis_mag )
4837 {
4838     // This is a bit of a kludge, but it should allow the characters to see
4839     // completely invisible objects as SEEINVISIBLE if their level is high enough.
4840     // AND it should make mostly invisible objects approach SEEINVISIBLE
4841     // BUT objects that are already more visible than SEEINVISIBLE can be made fully visible
4842     // with a large enough level
4843 
4844     if ( 1.0f != seeinvis_mag )
4845     {
4846         if ( 0 == alpha )
4847         {
4848             if ( seeinvis_mag > 1.0f )
4849             {
4850                 alpha = SEEINVISIBLE * ( 1.0f - 1.0f / seeinvis_mag );
4851             }
4852         }
4853         else if ( alpha < SEEINVISIBLE )
4854         {
4855             alpha *= seeinvis_mag;
4856             alpha = MAX( alpha, SEEINVISIBLE );
4857         }
4858         else
4859         {
4860             alpha *= seeinvis_mag;
4861         }
4862     }
4863 
4864     return CLIP( alpha, 0, 255 );
4865 }
4866 
4867 //--------------------------------------------------------------------------------------------
get_light(int light,float seedark_mag)4868 Uint8 get_light( int light, float seedark_mag )
4869 {
4870     // ZF> Why should Darkvision reveal invisible?
4871     // BB> This modification makes the character's light (i.e how much it glows)
4872     //     more visible to characters with darkvision. This does not affect an object's
4873     //     alpha, which is what makes somethig invisible. If the object is glowing AND invisible,
4874     //     darkvision should make it more visible
4875 
4876     // is this object NOT glowing?
4877     if ( light >= 0xFF ) return 0xFF;
4878 
4879     if ( seedark_mag != 1.0f )
4880     {
4881         light *= seedark_mag;
4882     }
4883 
4884     return CLIP( light, 0, 0xFE );
4885 }
4886 
4887 //--------------------------------------------------------------------------------------------
do_shop_drop(const CHR_REF idropper,const CHR_REF iitem)4888 bool_t do_shop_drop( const CHR_REF idropper, const CHR_REF iitem )
4889 {
4890     chr_t * pdropper, * pitem;
4891     bool_t inshop;
4892 
4893     if ( !INGAME_CHR( iitem ) ) return bfalse;
4894     pitem = ChrList.lst + iitem;
4895 
4896     if ( !INGAME_CHR( idropper ) ) return bfalse;
4897     pdropper = ChrList.lst + idropper;
4898 
4899     inshop = bfalse;
4900     if ( pitem->isitem && ShopStack.count > 0 )
4901     {
4902         CHR_REF iowner;
4903 
4904         int ix = FLOOR( pitem->pos.x / GRID_FSIZE );
4905         int iy = FLOOR( pitem->pos.y / GRID_FSIZE );
4906 
4907         iowner = shop_get_owner( ix, iy );
4908         if ( INGAME_CHR( iowner ) )
4909         {
4910             int price;
4911             chr_t * powner = ChrList.lst + iowner;
4912 
4913             inshop = btrue;
4914 
4915             price = chr_get_price( iitem );
4916 
4917             // Are they are trying to sell junk or quest items?
4918             if ( 0 == price )
4919             {
4920                 ai_add_order( &( powner->ai ), ( Uint32 ) price, SHOP_BUY );
4921             }
4922             else
4923             {
4924                 pdropper->money += price;
4925                 pdropper->money  = CLIP( pdropper->money, 0, MAXMONEY );
4926 
4927                 powner->money -= price;
4928                 powner->money  = CLIP( powner->money, 0, MAXMONEY );
4929 
4930                 ai_add_order( &( powner->ai ), ( Uint32 ) price, SHOP_BUY );
4931             }
4932         }
4933     }
4934 
4935     return inshop;
4936 }
4937 
4938 //--------------------------------------------------------------------------------------------
do_shop_buy(const CHR_REF ipicker,const CHR_REF iitem)4939 bool_t do_shop_buy( const CHR_REF ipicker, const CHR_REF iitem )
4940 {
4941     bool_t can_grab, can_pay, in_shop;
4942     int price;
4943 
4944     chr_t * ppicker, * pitem;
4945 
4946     if ( !INGAME_CHR( iitem ) ) return bfalse;
4947     pitem = ChrList.lst + iitem;
4948 
4949     if ( !INGAME_CHR( ipicker ) ) return bfalse;
4950     ppicker = ChrList.lst + ipicker;
4951 
4952     can_grab = btrue;
4953     can_pay  = btrue;
4954     in_shop  = bfalse;
4955 
4956     if ( pitem->isitem && ShopStack.count > 0 )
4957     {
4958         CHR_REF iowner;
4959 
4960         int ix = FLOOR( pitem->pos.x / GRID_FSIZE );
4961         int iy = FLOOR( pitem->pos.y / GRID_FSIZE );
4962 
4963         iowner = shop_get_owner( ix, iy );
4964         if ( INGAME_CHR( iowner ) )
4965         {
4966             chr_t * powner = ChrList.lst + iowner;
4967 
4968             in_shop = btrue;
4969             price   = chr_get_price( iitem );
4970 
4971             if ( ppicker->money >= price )
4972             {
4973                 // Okay to sell
4974                 ai_add_order( &( powner->ai ), ( Uint32 ) price, SHOP_SELL );
4975 
4976                 ppicker->money -= price;
4977                 ppicker->money  = CLIP( ppicker->money, 0, MAXMONEY );
4978 
4979                 powner->money  += price;
4980                 powner->money   = CLIP( powner->money, 0, MAXMONEY );
4981 
4982                 can_grab = btrue;
4983                 can_pay  = btrue;
4984             }
4985             else
4986             {
4987                 // Don't allow purchase
4988                 ai_add_order( &( powner->ai ), price, SHOP_NOAFFORD );
4989                 can_grab = bfalse;
4990                 can_pay  = bfalse;
4991             }
4992         }
4993     }
4994 
4995     /// print some feedback messages
4996     /// @note: some of these are handled in scripts, so they could be disabled
4997     /*if( can_grab )
4998     {
4999         if( in_shop )
5000         {
5001             if( can_pay )
5002             {
5003                 debug_printf( "%s bought %s", chr_get_name( ipicker, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL), chr_get_name( iitem, CHRNAME_ARTICLE ) );
5004             }
5005             else
5006             {
5007                 debug_printf( "%s can't afford %s", chr_get_name( ipicker, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL), chr_get_name( iitem, CHRNAME_ARTICLE ) );
5008             }
5009         }
5010         else
5011         {
5012             debug_printf( "%s picked up %s", chr_get_name( ipicker, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL), chr_get_name( iitem, CHRNAME_ARTICLE ) );
5013         }
5014     }*/
5015 
5016     return can_grab;
5017 }
5018 
5019 //--------------------------------------------------------------------------------------------
do_shop_steal(const CHR_REF ithief,const CHR_REF iitem)5020 bool_t do_shop_steal( const CHR_REF ithief, const CHR_REF iitem )
5021 {
5022     // Pets can try to steal in addition to invisible characters
5023 
5024     bool_t can_steal;
5025 
5026     chr_t * pthief, * pitem;
5027 
5028     if ( !INGAME_CHR( iitem ) ) return bfalse;
5029     pitem = ChrList.lst + iitem;
5030 
5031     if ( !INGAME_CHR( ithief ) ) return bfalse;
5032     pthief = ChrList.lst + ithief;
5033 
5034     can_steal = btrue;
5035     if ( pitem->isitem && ShopStack.count > 0 )
5036     {
5037         CHR_REF iowner;
5038 
5039         int ix = FLOOR( pitem->pos.x / GRID_FSIZE );
5040         int iy = FLOOR( pitem->pos.y / GRID_FSIZE );
5041 
5042         iowner = shop_get_owner( ix, iy );
5043         if ( INGAME_CHR( iowner ) )
5044         {
5045             IPair  tmp_rand = {1, 100};
5046             Uint8  detection;
5047             chr_t * powner = ChrList.lst + iowner;
5048 
5049             detection = generate_irand_pair( tmp_rand );
5050 
5051             can_steal = btrue;
5052             if ( chr_can_see_object( iowner, ithief ) || detection <= 5 || ( detection - ( pthief->dexterity >> 7 ) + ( powner->wisdom >> 7 ) ) > 50 )
5053             {
5054                 ai_add_order( &( powner->ai ), SHOP_STOLEN, SHOP_THEFT );
5055                 powner->ai.target = ithief;
5056                 can_steal = bfalse;
5057             }
5058         }
5059     }
5060 
5061     return can_steal;
5062 }
5063 
5064 //--------------------------------------------------------------------------------------------
can_grab_item_in_shop(const CHR_REF ichr,const CHR_REF iitem)5065 bool_t can_grab_item_in_shop( const CHR_REF ichr, const CHR_REF iitem )
5066 {
5067     bool_t can_grab;
5068     bool_t is_invis, can_steal;
5069     chr_t * pchr, * pitem;
5070     int ix, iy;
5071     CHR_REF shop_keeper;
5072 
5073     if ( !INGAME_CHR( ichr ) ) return bfalse;
5074     pchr = ChrList.lst + ichr;
5075 
5076     if ( !INGAME_CHR( iitem ) ) return bfalse;
5077     pitem = ChrList.lst + iitem;
5078     ix = pitem->pos.x / GRID_FSIZE;
5079     iy = pitem->pos.y / GRID_FSIZE;
5080 
5081     // assume that there is no shop so that the character can grab anything
5082     can_grab = btrue;
5083 
5084     // check if we are doing this inside a shop
5085     shop_keeper = shop_get_owner( ix, iy );
5086     if ( INGAME_CHR( shop_keeper ) )
5087     {
5088         // check for a stealthy pickup
5089         is_invis  = !chr_can_see_object( shop_keeper, ichr );
5090 
5091         // pets are automatically stealthy
5092         can_steal = is_invis || pchr->isitem;
5093 
5094         if ( can_steal )
5095         {
5096             can_grab = do_shop_steal( ichr, iitem );
5097 
5098             if ( !can_grab )
5099             {
5100                 debug_printf( "%s was detected!!", chr_get_name( ichr, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ) );
5101             }
5102             else
5103             {
5104                 debug_printf( "%s stole %s", chr_get_name( ichr, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ), chr_get_name( iitem, CHRNAME_ARTICLE ) );
5105             }
5106         }
5107         else
5108         {
5109             can_grab = do_shop_buy( ichr, iitem );
5110         }
5111     }
5112 
5113     return can_grab;
5114 }
5115 
5116 //--------------------------------------------------------------------------------------------
get_mesh_max_vertex_0(ego_mpd_t * pmesh,int grid_x,int grid_y,bool_t waterwalk)5117 float get_mesh_max_vertex_0( ego_mpd_t * pmesh, int grid_x, int grid_y, bool_t waterwalk )
5118 {
5119     float zdone = mesh_get_max_vertex_0( pmesh, grid_x, grid_y );
5120 
5121     if ( waterwalk && water.surface_level > zdone && water.is_water )
5122     {
5123         int tile = mesh_get_grid( pmesh, grid_x, grid_y );
5124 
5125         if ( 0 != mesh_test_fx( pmesh, tile, MPDFX_WATER ) )
5126         {
5127             zdone = water.surface_level;
5128         }
5129     }
5130 
5131     return zdone;
5132 }
5133 
5134 //--------------------------------------------------------------------------------------------
get_mesh_max_vertex_1(ego_mpd_t * pmesh,int grid_x,int grid_y,oct_bb_t * pbump,bool_t waterwalk)5135 float get_mesh_max_vertex_1( ego_mpd_t * pmesh, int grid_x, int grid_y, oct_bb_t * pbump, bool_t waterwalk )
5136 {
5137     float zdone = mesh_get_max_vertex_1( pmesh, grid_x, grid_y, pbump->mins[OCT_X], pbump->mins[OCT_Y], pbump->maxs[OCT_X], pbump->maxs[OCT_Y] );
5138 
5139     if ( waterwalk && water.surface_level > zdone && water.is_water )
5140     {
5141         int tile = mesh_get_grid( pmesh, grid_x, grid_y );
5142 
5143         if ( 0 != mesh_test_fx( pmesh, tile, MPDFX_WATER ) )
5144         {
5145             zdone = water.surface_level;
5146         }
5147     }
5148 
5149     return zdone;
5150 }
5151 
5152 //--------------------------------------------------------------------------------------------
get_mesh_max_vertex_2(ego_mpd_t * pmesh,chr_t * pchr)5153 float get_mesh_max_vertex_2( ego_mpd_t * pmesh, chr_t * pchr )
5154 {
5155     /// @details BB@> the object does not overlap a single grid corner. Check the 4 corners of the collision volume
5156 
5157     int corner;
5158     int ix_off[4] = {1, 1, 0, 0};
5159     int iy_off[4] = {0, 1, 1, 0};
5160 
5161     float pos_x[4];
5162     float pos_y[4];
5163     float zmax;
5164 
5165     for ( corner = 0; corner < 4; corner++ )
5166     {
5167         pos_x[corner] = pchr->pos.x + (( 0 == ix_off[corner] ) ? pchr->chr_min_cv.mins[OCT_X] : pchr->chr_min_cv.maxs[OCT_X] );
5168         pos_y[corner] = pchr->pos.y + (( 0 == iy_off[corner] ) ? pchr->chr_min_cv.mins[OCT_Y] : pchr->chr_min_cv.maxs[OCT_Y] );
5169     }
5170 
5171     zmax = get_mesh_level( pmesh, pos_x[0], pos_y[0], pchr->waterwalk );
5172     for ( corner = 1; corner < 4; corner++ )
5173     {
5174         float fval = get_mesh_level( pmesh, pos_x[corner], pos_y[corner], pchr->waterwalk );
5175         zmax = MAX( zmax, fval );
5176     }
5177 
5178     return zmax;
5179 }
5180 
5181 //--------------------------------------------------------------------------------------------
get_chr_level(ego_mpd_t * pmesh,chr_t * pchr)5182 float get_chr_level( ego_mpd_t * pmesh, chr_t * pchr )
5183 {
5184     float zmax;
5185     int ix, ixmax, ixmin;
5186     int iy, iymax, iymin;
5187 
5188     int grid_vert_count = 0;
5189     int grid_vert_x[1024];
5190     int grid_vert_y[1024];
5191 
5192     oct_bb_t bump;
5193 
5194     if ( NULL == pmesh || !ACTIVE_PCHR( pchr ) ) return 0;
5195 
5196     // certain scenery items like doors and such just need to be able to
5197     // collide with the mesh. They all have 0 == pchr->bump.size
5198     if ( 0.0f == pchr->bump_stt.size )
5199     {
5200         return get_mesh_level( pmesh, pchr->pos.x, pchr->pos.y, pchr->waterwalk );
5201     }
5202 
5203     // otherwise, use the small collision volume to determine which tiles the object overlaps
5204     // move the collision volume so that it surrounds the object
5205     oct_bb_add_fvec3( &( pchr->chr_min_cv ), pchr->pos.v, &bump );
5206 
5207     // determine the size of this object in tiles
5208     ixmin = bump.mins[OCT_X] / GRID_FSIZE; ixmin = CLIP( ixmin, 0, pmesh->info.tiles_x - 1 );
5209     ixmax = bump.maxs[OCT_X] / GRID_FSIZE; ixmax = CLIP( ixmax, 0, pmesh->info.tiles_x - 1 );
5210 
5211     iymin = bump.mins[OCT_Y] / GRID_FSIZE; iymin = CLIP( iymin, 0, pmesh->info.tiles_y - 1 );
5212     iymax = bump.maxs[OCT_Y] / GRID_FSIZE; iymax = CLIP( iymax, 0, pmesh->info.tiles_y - 1 );
5213 
5214     // do the simplest thing if the object is just on one tile
5215     if ( ixmax == ixmin && iymax == iymin )
5216     {
5217         return get_mesh_max_vertex_2( pmesh, pchr );
5218     }
5219 
5220     // otherwise, make up a list of tiles that the object might overlap
5221     for ( iy = iymin; iy <= iymax; iy++ )
5222     {
5223         float grid_y = iy * GRID_ISIZE;
5224 
5225         for ( ix = ixmin; ix <= ixmax; ix++ )
5226         {
5227             float ftmp;
5228             int   itile;
5229             float grid_x = ix * GRID_ISIZE;
5230 
5231             ftmp = grid_x + grid_y;
5232             if ( ftmp < bump.mins[OCT_XY] || ftmp > bump.maxs[OCT_XY] ) continue;
5233 
5234             ftmp = -grid_x + grid_y;
5235             if ( ftmp < bump.mins[OCT_YX] || ftmp > bump.maxs[OCT_YX] ) continue;
5236 
5237             itile = mesh_get_tile_int( pmesh, ix, iy );
5238             if ( INVALID_TILE == itile ) continue;
5239 
5240             grid_vert_x[grid_vert_count] = ix;
5241             grid_vert_y[grid_vert_count] = iy;
5242             grid_vert_count++;
5243         }
5244     }
5245 
5246     // we did not intersect a single tile corner
5247     // this could happen for, say, a very long, but thin shape that fits between the tiles.
5248     // the current system would not work for that shape
5249     if ( 0 == grid_vert_count )
5250     {
5251         return get_mesh_max_vertex_2( pmesh, pchr );
5252     }
5253     else
5254     {
5255         int cnt;
5256         float fval;
5257 
5258         // scan through the vertices that we know will interact with the object
5259         zmax = get_mesh_max_vertex_1( pmesh, grid_vert_x[0], grid_vert_y[0], &bump, pchr->waterwalk );
5260         for ( cnt = 1; cnt < grid_vert_count; cnt ++ )
5261         {
5262             fval = get_mesh_max_vertex_1( pmesh, grid_vert_x[cnt], grid_vert_y[cnt], &bump, pchr->waterwalk );
5263             zmax = MAX( zmax, fval );
5264         }
5265     }
5266 
5267     if ( zmax == -1e6 ) zmax = 0.0f;
5268 
5269     return zmax;
5270 }
5271 
5272 //--------------------------------------------------------------------------------------------
move_water(water_instance_t * pwater)5273 egoboo_rv move_water( water_instance_t * pwater )
5274 {
5275     /// @details ZZ@> This function animates the water overlays
5276 
5277     int layer;
5278 
5279     if ( NULL == pwater ) return rv_error;
5280 
5281     for ( layer = 0; layer < MAXWATERLAYER; layer++ )
5282     {
5283         water_instance_layer_t * player = pwater->layer + layer;
5284 
5285         player->tx.x += player->tx_add.x;
5286         player->tx.y += player->tx_add.y;
5287 
5288         if ( player->tx.x >  1.0f )  player->tx.x -= 1.0f;
5289         if ( player->tx.y >  1.0f )  player->tx.y -= 1.0f;
5290         if ( player->tx.x < -1.0f )  player->tx.x += 1.0f;
5291         if ( player->tx.y < -1.0f )  player->tx.y += 1.0f;
5292 
5293         player->frame = ( player->frame + player->frame_add ) & WATERFRAMEAND;
5294     }
5295 
5296     return rv_success;
5297 }
5298 
5299 //--------------------------------------------------------------------------------------------
disenchant_character(const CHR_REF cnt)5300 void disenchant_character( const CHR_REF cnt )
5301 {
5302     /// @details ZZ@> This function removes all enchantments from a character
5303 
5304     chr_t * pchr;
5305     size_t ienc_count;
5306 
5307     if ( !ALLOCATED_CHR( cnt ) ) return;
5308     pchr = ChrList.lst + cnt;
5309 
5310     ienc_count = 0;
5311     while (( MAX_ENC != pchr->firstenchant ) && ( ienc_count < MAX_ENC ) )
5312     {
5313         // do not let disenchant_character() get stuck in an infinite loop if there is an error
5314         if ( !remove_enchant( pchr->firstenchant, &( pchr->firstenchant ) ) )
5315         {
5316             break;
5317         }
5318         ienc_count++;
5319     }
5320     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
5321 
5322 }
5323 
5324 //--------------------------------------------------------------------------------------------
cleanup_character_enchants(chr_t * pchr)5325 void cleanup_character_enchants( chr_t * pchr )
5326 {
5327     if ( NULL == pchr ) return;
5328 
5329     // clean up the enchant list
5330     pchr->firstenchant = cleanup_enchant_list( pchr->firstenchant, &( pchr->firstenchant ) );
5331 }
5332 
5333 //--------------------------------------------------------------------------------------------
5334 //--------------------------------------------------------------------------------------------
attach_chr_to_platform(chr_t * pchr,chr_t * pplat)5335 bool_t attach_chr_to_platform( chr_t * pchr, chr_t * pplat )
5336 {
5337     /// @details BB@> attach a character to a platform
5338     ///
5339     /// @note the function move_one_character_get_environment() has already been called from within the
5340     ///  move_one_character() function, so the environment has already been determined this round
5341 
5342     cap_t * pchr_cap;
5343     fvec3_t   platform_up;
5344 
5345     // verify that we do not have two dud pointers
5346     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
5347     if ( !ACTIVE_PCHR( pplat ) ) return bfalse;
5348 
5349     pchr_cap = pro_get_pcap( pchr->profile_ref );
5350     if ( NULL == pchr_cap ) return bfalse;
5351 
5352     // check if they can be connected
5353     if ( !pchr_cap->canuseplatforms || ( 0 != pchr->flyheight ) ) return bfalse;
5354     if ( !pplat->platform ) return bfalse;
5355 
5356     // do the attachment
5357     pchr->onwhichplatform_ref    = GET_REF_PCHR( pplat );
5358     pchr->onwhichplatform_update = update_wld;
5359     pchr->targetplatform_ref     = ( CHR_REF )MAX_CHR;
5360 
5361     // update the character's relationship to the ground
5362     pchr->enviro.level     = MAX( pchr->enviro.floor_level, pplat->pos.z + pplat->chr_min_cv.maxs[OCT_Z] );
5363     pchr->enviro.zlerp     = ( pchr->pos.z - pchr->enviro.level ) / PLATTOLERANCE;
5364     pchr->enviro.zlerp     = CLIP( pchr->enviro.zlerp, 0, 1 );
5365     pchr->enviro.grounded  = ( 0 == pchr->flyheight ) && ( pchr->enviro.zlerp < 0.25f );
5366 
5367     pchr->enviro.fly_level = MAX( pchr->enviro.fly_level, pchr->enviro.level );
5368     if ( 0 != pchr->flyheight )
5369     {
5370         if ( pchr->enviro.fly_level < 0 ) pchr->enviro.fly_level = 0;  // fly above pits...
5371     }
5372 
5373     // add the weight to the platform based on the new zlerp
5374     pplat->holdingweight += pchr->phys.weight * ( 1.0f - pchr->enviro.zlerp );
5375 
5376     // update the character jumping
5377     pchr->jumpready = pchr->enviro.grounded;
5378     if ( pchr->jumpready )
5379     {
5380         pchr->jumpnumber = pchr->jumpnumberreset;
5381     }
5382 
5383     // what to do about traction if the platform is tilted... hmmm?
5384     chr_getMatUp( pplat, platform_up.v );
5385     platform_up = fvec3_normalize( platform_up.v );
5386 
5387     pchr->enviro.traction = ABS( platform_up.z ) * ( 1.0f - pchr->enviro.zlerp ) + 0.25f * pchr->enviro.zlerp;
5388 
5389     // tell the platform that we bumped into it
5390     // this is necessary for key buttons to work properly, for instance
5391     ai_state_set_bumplast( &( pplat->ai ), GET_REF_PCHR( pchr ) );
5392 
5393     return btrue;
5394 }
5395 
5396 //--------------------------------------------------------------------------------------------
detach_character_from_platform(chr_t * pchr)5397 bool_t detach_character_from_platform( chr_t * pchr )
5398 {
5399     /// @details BB@> attach a character to a platform
5400     ///
5401     /// @note the function move_one_character_get_environment() has already been called from within the
5402     ///  move_one_character() function, so the environment has already been determined this round
5403 
5404     cap_t * pchr_cap;
5405     CHR_REF old_platform_ref;
5406     chr_t * old_platform_ptr;
5407     float   old_level, old_zlerp;
5408 
5409     // verify that we do not have two dud pointers
5410     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
5411 
5412     pchr_cap = pro_get_pcap( pchr->profile_ref );
5413     if ( NULL == pchr_cap ) return bfalse;
5414 
5415     // save some values
5416     old_platform_ref = pchr->onwhichplatform_ref;
5417     old_level        = pchr->enviro.level;
5418     old_platform_ptr = NULL;
5419     old_zlerp        = pchr->enviro.zlerp;
5420     if ( INGAME_CHR( old_platform_ref ) )
5421     {
5422         old_platform_ptr = ChrList.lst + old_platform_ref;
5423     }
5424 
5425     // undo the attachment
5426     pchr->onwhichplatform_ref    = ( CHR_REF ) MAX_CHR;
5427     pchr->onwhichplatform_update = 0;
5428     pchr->targetplatform_ref     = ( CHR_REF ) MAX_CHR;
5429     pchr->targetplatform_level   = -1e32;
5430 
5431     // adjust the platform weight, if necessary
5432     if ( NULL != old_platform_ptr )
5433     {
5434         old_platform_ptr->holdingweight -= pchr->phys.weight * ( 1.0f - old_zlerp );
5435     }
5436 
5437     // update the character-platform properties
5438     move_one_character_get_environment( pchr );
5439 
5440     // update the character jumping
5441     pchr->jumpready = pchr->enviro.grounded;
5442     if ( pchr->jumpready )
5443     {
5444         pchr->jumpnumber = pchr->jumpnumberreset;
5445     }
5446 
5447     return btrue;
5448 }
5449 
5450 //--------------------------------------------------------------------------------------------
attach_prt_to_platform(prt_t * pprt,chr_t * pplat)5451 bool_t attach_prt_to_platform( prt_t * pprt, chr_t * pplat )
5452 {
5453     /// @details BB@> attach a particle to a platform
5454 
5455     pip_t   * pprt_pip;
5456 
5457     // verify that we do not have two dud pointers
5458     if ( !ACTIVE_PPRT( pprt ) ) return bfalse;
5459     if ( !ACTIVE_PCHR( pplat ) ) return bfalse;
5460 
5461     pprt_pip = prt_get_ppip( GET_REF_PPRT( pprt ) );
5462     if ( NULL == pprt_pip ) return bfalse;
5463 
5464     // check if they can be connected
5465     if ( !pplat->platform ) return bfalse;
5466 
5467     // do the attachment
5468     pprt->onwhichplatform_ref    = GET_REF_PCHR( pplat );
5469     pprt->onwhichplatform_update = update_wld;
5470     pprt->targetplatform_ref     = ( CHR_REF )MAX_CHR;
5471 
5472     // update the character's relationship to the ground
5473     prt_set_level( pprt, MAX( pprt->enviro.level, pplat->pos.z + pplat->chr_min_cv.maxs[OCT_Z] ) );
5474 
5475     return btrue;
5476 }
5477 
5478 //--------------------------------------------------------------------------------------------
detach_particle_from_platform(prt_t * pprt)5479 bool_t detach_particle_from_platform( prt_t * pprt )
5480 {
5481     /// @details BB@> attach a particle to a platform
5482 
5483     prt_bundle_t bdl_prt;
5484 
5485     // verify that we do not have two dud pointers
5486     if ( !DEFINED_PPRT( pprt ) ) return bfalse;
5487 
5488     // grab all of the particle info
5489     prt_bundle_set( &bdl_prt, pprt );
5490 
5491     // check if they can be connected
5492     if ( INGAME_CHR( pprt->onwhichplatform_ref ) ) return bfalse;
5493 
5494     // undo the attachment
5495     pprt->onwhichplatform_ref    = ( CHR_REF ) MAX_CHR;
5496     pprt->onwhichplatform_update = 0;
5497     pprt->targetplatform_ref     = ( CHR_REF ) MAX_CHR;
5498     pprt->targetplatform_level   = -1e32;
5499 
5500     // get the correct particle environment
5501     move_one_particle_get_environment( &bdl_prt );
5502 
5503     return btrue;
5504 }
5505 
5506 //--------------------------------------------------------------------------------------------
5507 //--------------------------------------------------------------------------------------------
Import_element_init(Import_element_t * ptr)5508 bool_t Import_element_init( Import_element_t * ptr )
5509 {
5510     if ( NULL == ptr ) return bfalse;
5511 
5512     memset( ptr, 0, sizeof( *ptr ) );
5513 
5514     // all non-zero, non-null values
5515     ptr->player = MAX_PLAYER;
5516     ptr->slot   = -1;
5517 
5518     return btrue;
5519 }
5520 
5521 //--------------------------------------------------------------------------------------------
5522 //--------------------------------------------------------------------------------------------
game_copy_imports(Import_list_t * imp_lst)5523 egoboo_rv game_copy_imports( Import_list_t * imp_lst )
5524 {
5525     int       tnc;
5526     egoboo_rv retval;
5527     STRING    tmp_src_dir, tmp_dst_dir;
5528 
5529     int                import_idx = 0;
5530     Import_element_t * import_ptr = NULL;
5531 
5532     if ( NULL == imp_lst ) return rv_error;
5533 
5534     if ( 0 == imp_lst->count ) return rv_success;
5535 
5536     // assume the best
5537     retval = rv_success;
5538 
5539     // delete the data in the directory
5540     vfs_removeDirectoryAndContents( "import", VFS_TRUE );
5541 
5542     // make sure the directory exists
5543     if ( !vfs_mkdir( "/import" ) )
5544     {
5545         log_warning( "mnu_copy_local_imports() - Could not create the import folder. (%s)\n", vfs_getError() );
5546         return rv_error;
5547     }
5548 
5549     // copy all of the imports over
5550     for ( import_idx = 0; import_idx < imp_lst->count; import_idx++ )
5551     {
5552         // grab the loadplayer info
5553         import_ptr = imp_lst->lst + import_idx;
5554 
5555         snprintf( import_ptr->dstDir, SDL_arraysize( import_ptr->dstDir ), "/import/temp%04d.obj", import_ptr->slot );
5556 
5557         if ( !vfs_copyDirectory( import_ptr->srcDir, import_ptr->dstDir ) )
5558         {
5559             retval = rv_error;
5560             log_warning( "mnu_copy_local_imports() - Failed to copy an import character \"%s\" to \"%s\" (%s)\n", import_ptr->srcDir, import_ptr->dstDir, vfs_getError() );
5561         }
5562 
5563         // Copy all of the character's items to the import directory
5564         for ( tnc = 0; tnc < MAXIMPORTOBJECTS; tnc++ )
5565         {
5566             snprintf( tmp_src_dir, SDL_arraysize( tmp_src_dir ), "%s/%d.obj", import_ptr->srcDir, tnc );
5567 
5568             // make sure the source directory exists
5569             if ( vfs_isDirectory( tmp_src_dir ) )
5570             {
5571                 snprintf( tmp_dst_dir, SDL_arraysize( tmp_dst_dir ), "/import/temp%04d.obj", import_ptr->slot + tnc + 1 );
5572                 if ( !vfs_copyDirectory( tmp_src_dir, tmp_dst_dir ) )
5573                 {
5574                     retval = rv_error;
5575                     log_warning( "mnu_copy_local_imports() - Failed to copy an import inventory item \"%s\" to \"%s\" (%s)\n", tmp_src_dir, tmp_dst_dir, vfs_getError() );
5576                 }
5577             }
5578         }
5579     }
5580 
5581     return retval;
5582 }
5583 
5584 //--------------------------------------------------------------------------------------------
5585 //--------------------------------------------------------------------------------------------
Import_list_init(Import_list_t * imp_lst)5586 bool_t Import_list_init( Import_list_t * imp_lst )
5587 {
5588     int cnt;
5589 
5590     if ( NULL == imp_lst ) return bfalse;
5591 
5592     for ( cnt = 0; cnt < MAX_IMPORTS; cnt++ )
5593     {
5594         Import_element_init( imp_lst->lst + cnt );
5595     }
5596     imp_lst->count = 0;
5597 
5598     return btrue;
5599 }
5600 
5601 //--------------------------------------------------------------------------------------------
Import_list_from_players(Import_list_t * imp_lst)5602 egoboo_rv Import_list_from_players( Import_list_t * imp_lst )
5603 {
5604     bool_t is_local;
5605     PLA_REF player;
5606 
5607     PLA_REF                 player_idx;
5608     player_t              * player_ptr = NULL;
5609 
5610     Import_element_t      * import_ptr = NULL;
5611 
5612     CHR_REF                 ichr;
5613     chr_t                 * pchr;
5614 
5615     if ( NULL == imp_lst ) return rv_error;
5616 
5617     // blank out the ImportList list
5618     Import_list_init( &ImportList );
5619 
5620     // generate the ImportList list from the player info
5621     for ( player_idx = 0, player = 0; player_idx < MAX_PLAYER; player_idx++ )
5622     {
5623         if ( !VALID_PLA( player_idx ) ) continue;
5624         player_ptr = PlaStack.lst + player_idx;
5625 
5626         ichr = player_ptr->index;
5627         if ( !DEFINED_CHR( ichr ) ) continue;
5628         pchr = ChrList.lst + ichr;
5629 
5630         is_local = ( INPUT_BITS_NONE != player_ptr->device.bits );
5631 
5632         // grab a pointer
5633         import_ptr = imp_lst->lst + imp_lst->count;
5634         imp_lst->count++;
5635 
5636         import_ptr->player    = player_idx;
5637         import_ptr->bits      = player_ptr->device.bits;
5638         import_ptr->slot      = REF_TO_INT( player ) * MAXIMPORTPERPLAYER;
5639         import_ptr->srcDir[0] = CSTR_END;
5640         import_ptr->dstDir[0] = CSTR_END;
5641         strncpy( import_ptr->name, pchr->Name, SDL_arraysize( import_ptr->name ) );
5642 
5643         // only copy the "source" directory if the player is local
5644         if ( is_local )
5645         {
5646             snprintf( import_ptr->srcDir, SDL_arraysize( import_ptr->srcDir ), "mp_players/%s", str_encode_path( pchr->Name ) );
5647         }
5648         else
5649         {
5650             snprintf( import_ptr->srcDir, SDL_arraysize( import_ptr->srcDir ), "mp_remote/%s", str_encode_path( pchr->Name ) );
5651         }
5652 
5653         player++;
5654     }
5655 
5656     return ( imp_lst->count > 0 ) ? rv_success : rv_fail;
5657 }
5658 
5659 //--------------------------------------------------------------------------------------------
check_time(Uint32 check)5660 bool_t check_time( Uint32 check )
5661 {
5662     //ZF> Returns btrue if and only if all time and date specifications determined by the e_time parameter is true. This
5663     //    could indicate time of the day, a specific holiday season etc.
5664     switch( check )
5665     {
5666         //Halloween between 31th october and the 1st of november
5667         case SEASON_HALLOWEEN: return ( 10 == getCurrentTime()->tm_mon + 1 && getCurrentTime()->tm_mday >= 31 ||
5668                                         11 == getCurrentTime()->tm_mon + 1 && getCurrentTime()->tm_mday <= 1 );
5669 
5670         //Xmas from december 16th until newyear
5671         case SEASON_CHRISTMAS: return (12 == getCurrentTime()->tm_mon + 1 && getCurrentTime()->tm_mday >= 16 );
5672 
5673         //From 0:00 to 6:00 (spooky time!)
5674         case TIME_NIGHT: return getCurrentTime()->tm_hour <= 6;
5675 
5676         //Its day whenever it's not night
5677         case TIME_DAY: return !check_time( TIME_NIGHT );
5678 
5679         //Unhandled check
5680         default: log_warning("Unhandled time enum in check_time()\n"); return bfalse;
5681     }
5682 }
5683