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