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 profile.c
21 /// @brief Implementation of functions for controlling and accessing object profiles
22 /// @details
23 
24 #include "profile.inl"
25 
26 #include "ChrList.h"
27 #include "PrtList.h"
28 
29 #include "texture.h"
30 #include "log.h"
31 #include "script_compile.h"
32 #include "game.h"
33 
34 #include "egoboo_setup.h"
35 #include "egoboo_strutil.h"
36 #include "egoboo_fileutil.h"
37 #include "egoboo_vfs.h"
38 #include "egoboo_mem.h"
39 
40 #include "mesh.inl"
41 #include "bsp.inl"
42 #include "particle.inl"
43 
44 //--------------------------------------------------------------------------------------------
45 //--------------------------------------------------------------------------------------------
46 static bool_t _profile_system_initialized = bfalse;
47 
48 chop_data_t  chop_mem = {0, 0};
49 pro_import_t import_data;
50 
51 size_t bookicon_count   = 0;
52 TX_REF bookicon_ref[MAX_SKIN];                      // The first book icon
53 
54 INSTANTIATE_LIST( ACCESS_TYPE_NONE, pro_t, ProList, MAX_PROFILE );
55 INSTANTIATE_STATIC_ARY( MessageOffsetAry, MessageOffset );
56 
57 Uint32  message_buffer_carat = 0;                           // Where to put letter
58 char    message_buffer[MESSAGEBUFFERSIZE] = EMPTY_CSTR;     // The text buffer
59 
60 //--------------------------------------------------------------------------------------------
61 //--------------------------------------------------------------------------------------------
62 static void get_message( vfs_FILE* fileread );
63 
64 //--------------------------------------------------------------------------------------------
65 //--------------------------------------------------------------------------------------------
init_all_profiles()66 void init_all_profiles()
67 {
68     /// @details ZZ@> This function initializes all of the model profiles
69 
70     int tnc;
71 
72     // initialize all the sub-profile lists
73     init_all_pip();
74     init_all_eve();
75     init_all_cap();
76     init_all_mad();
77     init_all_ai_scripts();
78 
79     // initialize the profile list
80     ProList_init();
81 
82     // fix the book icon list
83     for ( tnc = 0; tnc < MAX_SKIN; tnc++ )
84     {
85         bookicon_ref[tnc] = INVALID_TX_TEXTURE;
86     }
87     bookicon_count = 0;
88 }
89 
90 //--------------------------------------------------------------------------------------------
release_all_profiles()91 void release_all_profiles()
92 {
93     /// @details ZZ@> This function clears out all of the model data
94 
95     // release the allocated data in all profiles (sounds, textures, etc.)
96     release_all_pro_data();
97 
98     // relese every type of sub-profile and re-initalize the lists
99     release_all_pip();
100     release_all_eve();
101     release_all_cap();
102     release_all_mad();
103     release_all_ai_scripts();
104 
105     // re-initialize the profile list
106     ProList_init();
107 }
108 
109 //--------------------------------------------------------------------------------------------
profile_system_begin()110 void profile_system_begin()
111 {
112     /// @details BB@> initialize the profile list and load up some intialization files
113     ///     necessary for the the profile loading code to work
114 
115     if ( _profile_system_initialized )
116     {
117         // release all profile data and reinitialize the profile list
118         release_all_profiles();
119 
120         _profile_system_initialized = bfalse;
121     }
122 
123     // initialize all the profile lists
124     init_all_profiles();
125 
126     // initialize the script compiler
127     script_compiler_init();
128 
129     // necessary for loading up the copy.txt file
130     load_action_names_vfs( "mp_data/actions.txt" );
131 
132     // necessary for properly reading the "message.txt"
133     reset_messages();
134 
135     // necessary for reading "naming.txt" properly
136     chop_data_init( &chop_mem );
137 
138     // something that is used in the game that is somewhat related to the profile stuff
139     init_slot_idsz();
140 
141     // let the code know that everything is initialized
142     _profile_system_initialized = btrue;
143 }
144 
145 //--------------------------------------------------------------------------------------------
profile_system_end()146 void profile_system_end()
147 {
148     /// @details BB@> initialize the profile list and load up some intialization files
149     ///     necessary for the the profile loading code to work
150 
151     if ( _profile_system_initialized )
152     {
153         // release all profile data and reinitialize the profile list
154         release_all_profiles();
155 
156         _profile_system_initialized = bfalse;
157     }
158 }
159 
160 //--------------------------------------------------------------------------------------------
161 //--------------------------------------------------------------------------------------------
pro_init(pro_t * pobj)162 bool_t pro_init( pro_t * pobj )
163 {
164     int cnt;
165 
166     if ( NULL == pobj ) return bfalse;
167 
168     if ( pobj->loaded )
169     {
170         log_warning( "pro_init() - trying to init an object in use" );
171     }
172 
173     //---- reset everything to safe values
174     memset( pobj, 0, sizeof( *pobj ) );
175 
176     pobj->icap = ( CAP_REF ) MAX_CAP;
177     pobj->imad = ( MAD_REF ) MAX_MAD;
178     pobj->ieve = ( EVE_REF ) MAX_EVE;
179 
180     for ( cnt = 0; cnt < MAX_PIP_PER_PROFILE; cnt++ )
181     {
182         pobj->prtpip[cnt] = ( PIP_REF ) MAX_PIP;
183     }
184 
185     chop_definition_init( &( pobj->chop ) );
186 
187     // do the final invalidation
188     pobj->loaded   = bfalse;
189     strncpy( pobj->name, "*NONE*", SDL_arraysize( pobj->name ) );
190 
191     // clear out the textures
192     for ( cnt = 0; cnt < MAX_SKIN; cnt++ )
193     {
194         pobj->tex_ref[cnt] = INVALID_TX_TEXTURE;
195         pobj->ico_ref[cnt] = INVALID_TX_TEXTURE;
196     }
197 
198     return btrue;
199 }
200 
201 //--------------------------------------------------------------------------------------------
202 // The "private" ProList management functions
203 //--------------------------------------------------------------------------------------------
ProList_search_free(const PRO_REF iobj)204 int ProList_search_free( const PRO_REF iobj )
205 {
206     /// @details BB@> if an object of index iobj exists on the free list, return the free list index
207     ///     otherwise return -1
208 
209     int cnt, retval;
210 
211     // determine whether this character is already in the list of free textures
212     // that is an error
213     retval = -1;
214     for ( cnt = 0; cnt < ProList.free_count; cnt++ )
215     {
216         if ( iobj == ProList.free_ref[cnt] )
217         {
218             retval = cnt;
219             break;
220         }
221     }
222 
223     return retval;
224 }
225 
226 //--------------------------------------------------------------------------------------------
ProList_pop_free(int idx)227 size_t ProList_pop_free( int idx )
228 {
229     /// @details BB@> pop off whatever object exists at the free list index idx
230 
231     size_t retval;
232 
233     if ( idx >= 0 && idx < ProList.free_count )
234     {
235         // move the index idx to the top
236         int idx_top, idx_bottom;
237 
238         idx_bottom = idx;
239         idx_top    = ProList.free_count - 1;
240 
241         // make sure this is a valid case
242         if ( idx_top > idx_bottom && idx_top > 1 )
243         {
244             SWAP( size_t, ProList.free_ref[idx_top], ProList.free_ref[idx_bottom] );
245         }
246     }
247 
248     // just pop off the top index
249     retval = MAX_PROFILE;
250     if ( ProList.free_count > 0 )
251     {
252         ProList.free_count--;
253         ProList.update_guid++;
254 
255         retval = ProList.free_ref[ProList.free_count];
256     }
257 
258     return retval;
259 }
260 
261 //--------------------------------------------------------------------------------------------
ProList_push_free(const PRO_REF iobj)262 bool_t ProList_push_free( const PRO_REF iobj )
263 {
264     /// @details BB@> push an object onto the free stack
265 
266     bool_t retval;
267 
268 #if defined(_DEBUG)
269     // determine whether this character is already in the list of free objects
270     // that is an error
271     if ( -1 != ProList_search_free( iobj ) ) return bfalse;
272 #endif
273 
274     // push it on the free stack
275     retval = bfalse;
276     if ( ProList.free_count < MAX_PROFILE )
277     {
278         ProList.free_ref[ProList.free_count] = REF_TO_INT( iobj );
279 
280         ProList.free_count++;
281         ProList.update_guid++;
282 
283         retval = btrue;
284     }
285 
286     return retval;
287 }
288 
289 //--------------------------------------------------------------------------------------------
290 // The "public" ProList management functions
291 //--------------------------------------------------------------------------------------------
ProList_init()292 void ProList_init()
293 {
294     /// @details BB@> initialize all the objects and the object free list.
295     ///     call before ever using the object list.
296 
297     PRO_REF cnt;
298 
299     ProList.free_count = 0;
300     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
301     {
302         // make sure we don't get a stupid warning
303         ProList.lst[cnt].loaded = bfalse;
304         pro_init( ProList.lst + cnt );
305 
306         ProList_push_free( cnt );
307     }
308 }
309 
310 //--------------------------------------------------------------------------------------------
ProList_get_free(const PRO_REF override)311 size_t ProList_get_free( const PRO_REF override )
312 {
313     /// @details ZZ@> This function returns the next free character or MAX_PROFILE if there are none
314 
315     size_t retval = MAX_PROFILE;
316 
317     if ( VALID_PRO_RANGE( override ) )
318     {
319         // grab the object that is specified.
320         int free_idx;
321 
322         // if the object is in use, make sure to free everything associated with it
323         if ( LOADED_PRO( override ) )
324         {
325             release_one_pro( override );
326         }
327 
328         // grab the specified index
329         free_idx = ProList_search_free( override );
330         retval   = ProList_pop_free( free_idx );
331     }
332     else
333     {
334         // grab the next free index
335         retval = ProList_pop_free( -1 );
336     }
337 
338     return retval;
339 }
340 
341 //--------------------------------------------------------------------------------------------
ProList_free_one(const PRO_REF iobj)342 bool_t ProList_free_one( const PRO_REF iobj )
343 {
344     /// @details ZZ@> This function sticks an object back on the free object stack
345 
346     if ( !VALID_PRO_RANGE( iobj ) ) return bfalse;
347 
348     // object "destructor"
349     // inilializes an object to safe values
350     pro_init( ProList.lst + iobj );
351 
352     return ProList_push_free( iobj );
353 }
354 
355 //--------------------------------------------------------------------------------------------
356 // object functions
357 //--------------------------------------------------------------------------------------------
358 //--------------------------------------------------------------------------------------------
release_one_profile_textures(const PRO_REF iobj)359 bool_t release_one_profile_textures( const PRO_REF iobj )
360 {
361     int tnc;
362     pro_t  * pobj;
363 
364     if ( !LOADED_PRO( iobj ) ) return bfalse;
365     pobj = ProList.lst + iobj;
366 
367     for ( tnc = 0; tnc < MAX_SKIN; tnc++ )
368     {
369         TX_REF itex;
370 
371         itex = pobj->tex_ref[tnc];
372         if ( itex > TX_LAST )
373         {
374             TxTexture_free_one( itex );
375         }
376         pobj->tex_ref[tnc] = INVALID_TX_TEXTURE;
377 
378         itex = pobj->ico_ref[tnc] ;
379         if ( itex > TX_LAST )
380         {
381             TxTexture_free_one( itex );
382         }
383         pobj->ico_ref[tnc] = INVALID_TX_TEXTURE;
384     }
385 
386     // reset the bookicon stuff if this object is a book
387     if ( SPELLBOOK == iobj )
388     {
389         for ( tnc = 0; tnc < MAX_SKIN; tnc++ )
390         {
391             bookicon_ref[tnc] = INVALID_TX_TEXTURE;
392         }
393         bookicon_count = 0;
394     }
395 
396     return btrue;
397 }
398 
399 //--------------------------------------------------------------------------------------------
release_all_profile_textures()400 void release_all_profile_textures()
401 {
402     PRO_REF cnt;
403 
404     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
405     {
406         release_one_profile_textures( cnt );
407     }
408 }
409 
410 //--------------------------------------------------------------------------------------------
release_one_pro_data(const PRO_REF iobj)411 bool_t release_one_pro_data( const PRO_REF iobj )
412 {
413     int cnt;
414     pro_t * pobj;
415 
416     if ( !LOADED_PRO( iobj ) ) return bfalse;
417     pobj = ProList.lst + iobj;
418 
419     // free all sounds
420     for ( cnt = 0; cnt < MAX_WAVE; cnt++ )
421     {
422         sound_free_chunk( pobj->wavelist[cnt] );
423         pobj->wavelist[cnt] = NULL;
424     }
425 
426     // release whatever textures are being used
427     release_one_profile_textures( iobj );
428 
429     return btrue;
430 }
431 
432 //--------------------------------------------------------------------------------------------
release_one_pro(const PRO_REF iobj)433 bool_t release_one_pro( const PRO_REF iobj )
434 {
435     pro_t * pobj;
436 
437     if ( !VALID_PRO_RANGE( iobj ) ) return bfalse;
438 
439     if ( !LOADED_PRO( iobj ) ) return btrue;
440     pobj = ProList.lst + iobj;
441 
442     // release all of the sub-profiles
443     //release_one_ai ( pobj->iai  );
444     release_one_cap( pobj->icap );
445     release_one_mad( pobj->imad );
446     //release_one_eve( pobj->ieve );
447 
448     release_one_local_pips( iobj );
449 
450     // release the allocated data
451     release_one_pro_data( iobj );
452 
453     pro_init( pobj );
454 
455     return btrue;
456 }
457 
458 //--------------------------------------------------------------------------------------------
release_all_pro()459 void release_all_pro()
460 {
461     /// @details BB@> release the allocated data for all objects
462     PRO_REF cnt;
463 
464     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
465     {
466         release_one_pro( cnt );
467     }
468 }
469 
470 //--------------------------------------------------------------------------------------------
release_all_pro_data()471 void release_all_pro_data()
472 {
473     /// @details BB@> release the allocated data for all objects
474     PRO_REF cnt;
475 
476     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
477     {
478         release_one_pro_data( cnt );
479     }
480 }
481 
482 //--------------------------------------------------------------------------------------------
483 //--------------------------------------------------------------------------------------------
load_profile_skins_vfs(const char * tmploadname,const PRO_REF object)484 int load_profile_skins_vfs( const char * tmploadname, const PRO_REF object )
485 {
486     TX_REF min_skin_tx, min_icon_tx;
487     int    max_skin, max_icon, max_tex;
488     TX_REF iskin, iicon;
489     int    cnt;
490 
491     STRING newloadname;
492 
493     pro_t * pobj;
494 
495     if ( !VALID_PRO_RANGE( object ) ) return 0;
496     pobj = ProList.lst + object;
497 
498     // Load the skins and icons
499     max_skin    = max_icon    = -1;
500     min_skin_tx = min_icon_tx = INVALID_TX_TEXTURE;
501     for ( cnt = 0; cnt < MAX_SKIN; cnt++ )
502     {
503         snprintf( newloadname, SDL_arraysize( newloadname ), "%s/tris%d", tmploadname, cnt );
504 
505         pobj->tex_ref[cnt] = TxTexture_load_one_vfs( newloadname, ( TX_REF )INVALID_TX_TEXTURE, TRANSCOLOR );
506         if ( INVALID_TX_TEXTURE != pobj->tex_ref[cnt] )
507         {
508             max_skin = cnt;
509             if ( INVALID_TX_TEXTURE == min_skin_tx )
510             {
511                 min_skin_tx = pobj->tex_ref[cnt];
512             }
513         }
514 
515         snprintf( newloadname, SDL_arraysize( newloadname ), "%s/icon%d", tmploadname, cnt );
516         pobj->ico_ref[cnt] = TxTexture_load_one_vfs( newloadname, ( TX_REF )INVALID_TX_TEXTURE, INVALID_KEY );
517 
518         if ( INVALID_TX_TEXTURE != pobj->ico_ref[cnt] )
519         {
520             max_icon = cnt;
521 
522             if ( INVALID_TX_TEXTURE == min_icon_tx )
523             {
524                 min_icon_tx = pobj->ico_ref[cnt];
525             }
526 
527             if ( SPELLBOOK == object )
528             {
529                 if ( bookicon_count < MAX_SKIN )
530                 {
531                     bookicon_ref[bookicon_count] = pobj->ico_ref[cnt];
532                     bookicon_count++;
533                 }
534             }
535         }
536     }
537 
538     if ( max_skin < 0 )
539     {
540         // If we didn't get a skin, set it to the water texture
541         max_skin = 0;
542         pobj->tex_ref[cnt] = TX_WATER_TOP;
543 
544         log_debug( "Object is missing a skin (%s)!\n", tmploadname );
545     }
546 
547     max_tex = MAX( max_skin, max_icon );
548 
549     // fill in any missing textures
550     iskin = min_skin_tx;
551     iicon = min_icon_tx;
552     for ( cnt = 0; cnt <= max_tex; cnt++ )
553     {
554         if ( INVALID_TX_TEXTURE != pobj->tex_ref[cnt] && iskin != pobj->tex_ref[cnt] )
555         {
556             iskin = pobj->tex_ref[cnt];
557         }
558 
559         if ( INVALID_TX_TEXTURE != pobj->ico_ref[cnt] && iicon != pobj->ico_ref[cnt] )
560         {
561             iicon = pobj->ico_ref[cnt];
562         }
563 
564         pobj->tex_ref[cnt] = iskin;
565         pobj->ico_ref[cnt] = iicon;
566     }
567 
568     return max_tex + 1;
569 }
570 
571 //--------------------------------------------------------------------------------------------
get_message(vfs_FILE * fileread)572 void get_message( vfs_FILE* fileread )
573 {
574     /// @details ZZ@> This function loads a string into the message buffer, making sure it
575     ///    is null terminated.
576 
577     int cnt;
578     char cTmp;
579     STRING szTmp;
580 
581     if ( message_buffer_carat >= MESSAGEBUFFERSIZE )
582     {
583         message_buffer_carat = MESSAGEBUFFERSIZE - 1;
584         message_buffer[message_buffer_carat] = CSTR_END;
585         return;
586     }
587 
588     if ( MessageOffset.count >= MAXTOTALMESSAGE )
589     {
590         return;
591     }
592 
593     MessageOffset.ary[MessageOffset.count] = message_buffer_carat;
594     fget_string( fileread, szTmp, SDL_arraysize( szTmp ) );
595     szTmp[255] = CSTR_END;
596 
597     cTmp = szTmp[0];
598     cnt = 1;
599     while ( CSTR_END != cTmp && message_buffer_carat < MESSAGEBUFFERSIZE - 1 )
600     {
601         if ( '_' == cTmp )  cTmp = ' ';
602 
603         message_buffer[message_buffer_carat] = cTmp;
604         message_buffer_carat++;
605         cTmp = szTmp[cnt];
606         cnt++;
607     }
608 
609     message_buffer[message_buffer_carat] = CSTR_END;
610     message_buffer_carat++;
611     MessageOffset.count++;
612 }
613 
614 //--------------------------------------------------------------------------------------------
load_all_messages_vfs(const char * loadname,const PRO_REF object)615 void load_all_messages_vfs( const char *loadname, const PRO_REF object )
616 {
617     /// @details ZZ@> This function loads all of an objects messages
618     vfs_FILE *fileread;
619 
620     ProList.lst[object].message_start = 0;
621     fileread = vfs_openRead( loadname );
622     if ( fileread )
623     {
624         ProList.lst[object].message_start = MessageOffset.count;
625 
626         while ( goto_colon( NULL, fileread, btrue ) )
627         {
628             get_message( fileread );
629         }
630 
631         vfs_close( fileread );
632     }
633 }
634 
635 //--------------------------------------------------------------------------------------------
release_one_local_pips(const PRO_REF iobj)636 bool_t release_one_local_pips( const PRO_REF iobj )
637 {
638     int cnt;
639     pro_t * pobj;
640 
641     if ( !VALID_PRO_RANGE( iobj ) ) return bfalse;
642 
643     if ( !LOADED_PRO( iobj ) ) return btrue;
644     pobj = ProList.lst + iobj;
645 
646     for ( cnt = 0; cnt < MAX_PIP_PER_PROFILE; cnt++ )
647     {
648         release_one_pip( pobj->prtpip[cnt] );
649         pobj->prtpip[cnt] = ( PIP_REF ) MAX_PIP;
650     }
651 
652     return btrue;
653 }
654 
655 //--------------------------------------------------------------------------------------------
release_all_local_pips()656 void release_all_local_pips()
657 {
658     // clear out the local pips
659 
660     PRO_REF object;
661     int cnt;
662 
663     for ( object = 0; object < MAX_PROFILE; object++ )
664     {
665         pro_t * pobj;
666 
667         if ( !ProList.lst[object].loaded ) continue;
668         pobj = ProList.lst + object;
669 
670         for ( cnt = 0; cnt < MAX_PIP_PER_PROFILE; cnt++ )
671         {
672             release_one_pip( pobj->prtpip[cnt] );
673             pobj->prtpip[cnt] = ( PIP_REF ) MAX_PIP;
674         }
675     }
676 }
677 
678 //--------------------------------------------------------------------------------------------
obj_read_slot_vfs(const char * tmploadname)679 int obj_read_slot_vfs( const char * tmploadname )
680 {
681     vfs_FILE* fileread;
682     int slot;
683     STRING szLoadName;
684 
685     make_newloadname( tmploadname, "/data.txt", szLoadName );
686 
687     // Open the file
688     fileread = vfs_openRead( szLoadName );
689     if ( NULL == fileread ) return -1;
690 
691     // load the slot's slot no matter what
692     slot = fget_next_int( fileread );
693 
694     vfs_close( fileread );
695 
696     return slot;
697 }
698 
699 //--------------------------------------------------------------------------------------------
obj_verify_file_vfs(const char * tmploadname)700 bool_t obj_verify_file_vfs( const char * tmploadname )
701 {
702     STRING szLoadName;
703 
704     make_newloadname( tmploadname, "/data.txt", szLoadName );
705 
706     // Open the file
707     return ( 0 != vfs_exists( szLoadName ) );
708 }
709 
710 //--------------------------------------------------------------------------------------------
pro_get_slot_vfs(const char * tmploadname,int slot_override)711 int pro_get_slot_vfs( const char * tmploadname, int slot_override )
712 {
713     int slot;
714 
715     slot = -1;
716     if ( VALID_PRO_RANGE( slot_override ) )
717     {
718         // just use the slot that was provided
719         slot = slot_override;
720     }
721 
722     // grab the slot from the file
723     else
724     {
725         int tmp_slot = obj_read_slot_vfs( tmploadname );
726 
727         // set the slot slot
728         if ( tmp_slot >= 0 )
729         {
730             slot = tmp_slot;
731         }
732         else if ( import_data.slot >= 0 )
733         {
734             slot = import_data.slot;
735         }
736     }
737 
738     // return an error value if the file does not exist
739     if ( !obj_verify_file_vfs( tmploadname ) )
740     {
741         slot = -1;
742     }
743 
744     return slot;
745 }
746 
747 //--------------------------------------------------------------------------------------------
load_one_profile_vfs(const char * tmploadname,int slot_override)748 int load_one_profile_vfs( const char* tmploadname, int slot_override )
749 {
750     /// @details ZZ@> This function loads one object and returns the object slot
751 
752     int cnt;
753     STRING newloadname;
754     bool_t required;
755 
756     int islot;     // this has to be a signed value for this function to work properly
757 
758     PRO_REF iobj;
759     pro_t * pobj;
760 
761     required = !VALID_CAP_RANGE( slot_override );
762 
763     // get a slot value
764     islot = pro_get_slot_vfs( tmploadname, slot_override );
765 
766     // throw an error code if the slot is invalid of if the file doesn't exist
767     if ( islot < 0 || islot > MAX_PROFILE )
768     {
769         // The data file wasn't found
770         if ( required )
771         {
772             log_debug( "load_one_profile_vfs() - \"%s\" was not found. Overriding a global object?\n", tmploadname );
773         }
774         else if ( VALID_CAP_RANGE( slot_override ) && slot_override > PMod->importamount * MAXIMPORTPERPLAYER )
775         {
776             log_debug( "load_one_profile_vfs() - Not able to open file \"%s\"\n", tmploadname );
777         }
778 
779         return MAX_PROFILE;
780     }
781 
782     // convert the slot to a profile reference
783     iobj = islot;
784 
785     // throw an error code if we are trying to load over an existing profile
786     // without permission
787     if ( LOADED_PRO( iobj ) )
788     {
789         pro_t * pobj = ProList.lst + iobj;
790 
791         // Make sure global objects don't load over existing models
792         if ( required && SPELLBOOK == iobj )
793         {
794             log_error( "load_one_profile_vfs() - object slot %i is a special reserved slot number (cannot be used by %s).\n", SPELLBOOK, tmploadname );
795         }
796         else if ( required && overrideslots )
797         {
798             log_error( "load_one_profile_vfs() - object slot %i used twice (%s, %s)\n", REF_TO_INT( iobj ), pobj->name, tmploadname );
799         }
800         else
801         {
802             // Stop, we don't want to override it
803             return MAX_PROFILE;
804         }
805     }
806 
807     // allocate/reallocate this slot
808     iobj = ProList_get_free( iobj );
809     if ( !VALID_PRO_RANGE( iobj ) )
810     {
811         log_warning( "load_one_profile_vfs() - Cannot allocate object %d (\"%s\")\n", REF_TO_INT( iobj ), tmploadname );
812         return MAX_PROFILE;
813     }
814 
815     // grab a pointer to the object
816     pobj  = ProList.lst + iobj;
817 
818     // load the character profile
819     pobj->icap = load_one_character_profile_vfs( tmploadname, islot, bfalse );
820     islot = REF_TO_INT( pobj->icap );
821 
822     // Load the model for this iobj
823     pobj->imad = load_one_model_profile_vfs( tmploadname, ( MAD_REF )islot );
824 
825     // Load the enchantment for this iobj
826     make_newloadname( tmploadname, "/enchant.txt", newloadname );
827     pobj->ieve = load_one_enchant_profile_vfs( newloadname, ( EVE_REF )islot );
828 
829     // Load the AI script for this iobj
830     make_newloadname( tmploadname, "/script.txt", newloadname );
831     pobj->iai = load_ai_script_vfs( newloadname );
832 
833     // Load the messages for this iobj
834     make_newloadname( tmploadname, "/message.txt", newloadname );
835     load_all_messages_vfs( newloadname, iobj );
836 
837     // Load the particles for this iobj
838     for ( cnt = 0; cnt < MAX_PIP_PER_PROFILE; cnt++ )
839     {
840         snprintf( newloadname, SDL_arraysize( newloadname ), "%s/part%d.txt", tmploadname, cnt );
841 
842         // Make sure it's referenced properly
843         pobj->prtpip[cnt] = load_one_particle_profile_vfs( newloadname, ( PIP_REF )MAX_PIP );
844     }
845 
846     pobj->skins = load_profile_skins_vfs( tmploadname, iobj );
847 
848     // Load the waves for this iobj
849     for ( cnt = 0; cnt < MAX_WAVE; cnt++ )
850     {
851         STRING  szLoadName, wavename;
852 
853         snprintf( wavename, SDL_arraysize( wavename ), "/sound%d", cnt );
854         make_newloadname( tmploadname, wavename, szLoadName );
855         pobj->wavelist[cnt] = sound_load_chunk_vfs( szLoadName );
856     }
857 
858     // Load the random naming table for this icap
859     make_newloadname( tmploadname, "/naming.txt", newloadname );
860     pro_load_chop_vfs( iobj, newloadname );
861 
862     // Fix lighting if need be
863     if ( CapStack.lst[pobj->icap].uniformlit )
864     {
865         mad_make_equally_lit_ref( pobj->imad );
866     }
867 
868     // mark the profile as loaded
869     strncpy( pobj->name, tmploadname, SDL_arraysize( pobj->name ) );
870     pobj->loaded = btrue;
871 
872     return islot;
873 }
874 
875 //--------------------------------------------------------------------------------------------
reset_messages()876 void reset_messages()
877 {
878     /// @details ZZ@> This makes messages safe to use
879     int cnt;
880 
881     MessageOffset.count = 0;
882     message_buffer_carat = 0;
883     msgtimechange = 0;
884     DisplayMsg.count = 0;
885 
886     for ( cnt = 0; cnt < MAX_MESSAGE; cnt++ )
887     {
888         DisplayMsg.ary[cnt].time = 0;
889     }
890 
891     for ( cnt = 0; cnt < MAXTOTALMESSAGE; cnt++ )
892     {
893         MessageOffset.ary[cnt] = 0;
894     }
895 
896     message_buffer[0] = CSTR_END;
897 }
898 
899 //--------------------------------------------------------------------------------------------
900 //--------------------------------------------------------------------------------------------
pro_create_chop(const PRO_REF iprofile)901 const char * pro_create_chop( const PRO_REF iprofile )
902 {
903     /// BB@> use the profile's chop to generate a name. Return "*NONE*" on a falure.
904 
905     pro_t * ppro;
906     cap_t * pcap;
907     const char * szTmp;
908 
909     // The name returned by the function
910     static char buffer[MAXCAPNAMESIZE] = EMPTY_CSTR;
911 
912     // the default "bad" name
913     strncpy( buffer, "*NONE*", SDL_arraysize( buffer ) );
914 
915     if ( !LOADED_PRO( iprofile ) ) return buffer;
916     ppro = ProList.lst + iprofile;
917 
918     if ( !LOADED_CAP( ppro->icap ) ) return buffer;
919     pcap = CapStack.lst + ppro->icap;
920 
921     if ( 0 == ppro->chop.section[0].size )
922     {
923         strncpy( buffer, pcap->classname, SDL_arraysize( buffer ) );
924     }
925     else
926     {
927         szTmp = chop_create( &chop_mem, &( ppro->chop ) );
928 
929         if ( VALID_CSTR( szTmp ) )
930         {
931             strncpy( buffer, szTmp, SDL_arraysize( buffer ) );
932         }
933     }
934 
935     return buffer;
936 }
937 
938 //--------------------------------------------------------------------------------------------
pro_load_chop_vfs(const PRO_REF iprofile,const char * szLoadname)939 bool_t pro_load_chop_vfs( const PRO_REF iprofile, const char *szLoadname )
940 {
941     /// BB@> load the chop for the given profile
942     pro_t * ppro;
943 
944     if ( !VALID_PRO_RANGE( iprofile ) ) return bfalse;
945     ppro = ProList.lst + iprofile;
946 
947     // clear out any current definition
948     chop_definition_init( &( ppro->chop ) );
949 
950     return chop_load_vfs( &chop_mem, szLoadname, &( ppro->chop ) );
951 }
952 
953 //--------------------------------------------------------------------------------------------
954 //--------------------------------------------------------------------------------------------
chop_definition_init(chop_definition_t * pdefinition)955 chop_definition_t * chop_definition_init( chop_definition_t * pdefinition )
956 {
957     int cnt;
958 
959     if ( NULL == pdefinition ) return pdefinition;
960 
961     for ( cnt = 0; cnt < MAXSECTION; cnt++ )
962     {
963         pdefinition->section[cnt].start = MAXCHOP;
964         pdefinition->section[cnt].size  = 0;
965     }
966 
967     return pdefinition;
968 }
969 
970 //--------------------------------------------------------------------------------------------
chop_data_init(chop_data_t * pdata)971 chop_data_t * chop_data_init( chop_data_t * pdata )
972 {
973     /// @details ZZ@> This function prepares the name chopper for use
974     ///          BB@> It may actually be useful to blank the chop buffer
975 
976     if ( NULL == pdata ) return pdata;
977 
978     pdata->chop_count = 0;
979     pdata->carat      = 0;
980 
981     return pdata;
982 }
983 
984 //--------------------------------------------------------------------------------------------
chop_create(chop_data_t * pdata,chop_definition_t * pdefinition)985 const char * chop_create( chop_data_t * pdata, chop_definition_t * pdefinition )
986 {
987     /// @details ZZ@> This function generates a random name.  Return "Blah" on a falure.
988 
989     int read, write, section, mychop;
990     char cTmp;
991 
992     // The name returned by the function
993     static char buffer[MAXCAPNAMESIZE] = EMPTY_CSTR;
994 
995     strncpy( buffer, "Blah", SDL_arraysize( buffer ) );
996 
997     if ( NULL == pdata || NULL == pdefinition ) return buffer;
998 
999     write = 0;
1000     for ( section = 0; section < MAXSECTION; section++ )
1001     {
1002         if ( 0 != pdefinition->section[section].size )
1003         {
1004             int irand = RANDIE;
1005 
1006             mychop = pdefinition->section[section].start + ( irand % pdefinition->section[section].size );
1007 
1008             if ( mychop < MAXCHOP )
1009             {
1010                 read = pdata->start[mychop];
1011                 cTmp = pdata->buffer[read];
1012                 while ( CSTR_END != cTmp && write < MAXCAPNAMESIZE - 1 )
1013                 {
1014                     buffer[write] = cTmp;
1015                     write++;
1016                     read++;
1017                     cTmp = pdata->buffer[read];
1018                 }
1019                 buffer[write] = CSTR_END;
1020             }
1021         }
1022     }
1023     if ( write >= MAXCAPNAMESIZE ) write = MAXCAPNAMESIZE - 1;
1024 
1025     buffer[write] = CSTR_END;
1026 
1027     return buffer;
1028 }
1029 
1030 //--------------------------------------------------------------------------------------------
chop_load_vfs(chop_data_t * pdata,const char * szLoadname,chop_definition_t * pdefinition)1031 bool_t chop_load_vfs( chop_data_t * pdata, const char *szLoadname, chop_definition_t * pdefinition )
1032 {
1033     /// @details ZZ@> This function reads a naming.txt file into the chop data buffer and sets the
1034     ///               values of a chop definition
1035 
1036     int       which_section, section_count;
1037     STRING    tmp_buffer = EMPTY_CSTR;
1038     vfs_FILE *fileread;
1039 
1040     chop_definition_t local_definition;
1041 
1042     if ( NULL == pdata || pdata->carat >= CHOPDATACHUNK ) return bfalse;
1043 
1044     fileread = vfs_openRead( szLoadname );
1045     if ( NULL == fileread ) return bfalse;
1046 
1047     // in case we get a stupid value.
1048     // we could create a dynamically allocated struct in this case...
1049     if ( NULL == pdefinition ) pdefinition = &local_definition;
1050 
1051     // clear out any old definition
1052     chop_definition_init( pdefinition );
1053 
1054     which_section = 0;
1055     section_count = 0;
1056     while ( which_section < MAXSECTION && pdata->carat < CHOPDATACHUNK && goto_colon( NULL, fileread, btrue ) )
1057     {
1058         fget_string( fileread, tmp_buffer, SDL_arraysize( tmp_buffer ) );
1059 
1060         // convert all the '_' and junk in the string
1061         str_decode( tmp_buffer, SDL_arraysize( tmp_buffer ), tmp_buffer );
1062 
1063         if ( 0 == strcmp( tmp_buffer, "STOP" ) )
1064         {
1065             if ( which_section < MAXSECTION )
1066             {
1067                 int itmp;
1068                 pdefinition->section[which_section].size  = section_count;
1069                 itmp = ( int )pdata->chop_count - ( int )section_count;
1070                 pdefinition->section[which_section].start = MAX( 0, itmp );
1071             }
1072 
1073             which_section++;
1074             section_count = 0;
1075             tmp_buffer[0] = CSTR_END;
1076         }
1077         else
1078         {
1079             int chop_len;
1080 
1081             // fill in the chop data
1082             pdata->start[pdata->chop_count] = pdata->carat;
1083             chop_len = snprintf( pdata->buffer + pdata->carat, CHOPDATACHUNK - pdata->carat - 1, "%s", tmp_buffer );
1084 
1085             pdata->carat += chop_len + 1;
1086             pdata->chop_count++;
1087             section_count++;
1088             tmp_buffer[0] = CSTR_END;
1089         }
1090     }
1091 
1092     // handle the case where the chop buffer has overflowed
1093     // pretend the last command was "STOP"
1094     if ( CSTR_END != tmp_buffer[0] && which_section < MAXSECTION )
1095     {
1096         int itmp;
1097         pdefinition->section[which_section].size  = section_count;
1098         itmp = ( int )pdata->chop_count - ( int )section_count;
1099         pdefinition->section[which_section].start = MAX( 0, itmp );
1100     }
1101 
1102     vfs_close( fileread );
1103 
1104     return section_count > 0;
1105 }
1106 
1107 //--------------------------------------------------------------------------------------------
chop_export_vfs(const char * szSaveName,const char * szChop)1108 bool_t chop_export_vfs( const char *szSaveName, const char * szChop )
1109 {
1110     /// @details ZZ@> This function exports a simple string to the naming.txt file
1111 
1112     vfs_FILE* filewrite;
1113     char cTmp;
1114     int cnt, tnc;
1115 
1116     if ( !VALID_CSTR( szChop ) ) return bfalse;
1117 
1118     // Can it export?
1119     filewrite = vfs_openWrite( szSaveName );
1120     if ( NULL == filewrite ) return bfalse;
1121 
1122     cnt = 0;
1123     cTmp = szChop[0];
1124     cnt++;
1125     while ( cnt < MAXCAPNAMESIZE && CSTR_END != cTmp )
1126     {
1127         vfs_printf( filewrite, ":" );
1128 
1129         for ( tnc = 0; tnc < 8 && cTmp != 0; tnc++ )
1130         {
1131             if ( ' ' == cTmp )
1132             {
1133                 vfs_printf( filewrite, "_" );
1134             }
1135             else
1136             {
1137                 vfs_printf( filewrite, "%c", cTmp );
1138             }
1139 
1140             cTmp = szChop[cnt];
1141             cnt++;
1142         }
1143 
1144         vfs_printf( filewrite, "\n" );
1145         vfs_printf( filewrite, ":STOP\n\n" );
1146     }
1147 
1148     vfs_close( filewrite );
1149 
1150     return btrue;
1151 }
1152