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 ChrList.c
21 /// @brief Implementation of the ChrList_* functions
22 /// @details
23 
24 #include "ChrList.h"
25 #include "log.h"
26 #include "egoboo_object.h"
27 
28 #include "char.inl"
29 
30 //--------------------------------------------------------------------------------------------
31 //--------------------------------------------------------------------------------------------
32 
33 INSTANTIATE_LIST( ACCESS_TYPE_NONE, chr_t, ChrList, MAX_CHR );
34 
35 static size_t  chr_termination_count = 0;
36 static CHR_REF chr_termination_list[MAX_CHR];
37 
38 static size_t  chr_activation_count = 0;
39 static CHR_REF chr_activation_list[MAX_CHR];
40 
41 //--------------------------------------------------------------------------------------------
42 //--------------------------------------------------------------------------------------------
43 int chr_loop_depth = 0;
44 
45 //--------------------------------------------------------------------------------------------
46 //--------------------------------------------------------------------------------------------
47 
48 static size_t  ChrList_get_free();
49 
50 static bool_t ChrList_remove_used( const CHR_REF ichr );
51 static bool_t ChrList_remove_used_index( int index );
52 static bool_t ChrList_add_free( const CHR_REF ichr );
53 static bool_t ChrList_remove_free( const CHR_REF ichr );
54 static bool_t ChrList_remove_free_index( int index );
55 
56 //--------------------------------------------------------------------------------------------
57 //--------------------------------------------------------------------------------------------
ChrList_init()58 void ChrList_init()
59 {
60     int cnt;
61 
62     ChrList.free_count = 0;
63     ChrList.used_count = 0;
64     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
65     {
66         ChrList.free_ref[cnt] = MAX_CHR;
67         ChrList.used_ref[cnt] = MAX_CHR;
68     }
69 
70     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
71     {
72         CHR_REF ichr = ( MAX_CHR - 1 ) - cnt;
73         chr_t * pchr = ChrList.lst + ichr;
74 
75         // blank out all the data, including the obj_base data
76         memset( pchr, 0, sizeof( *pchr ) );
77 
78         // character "initializer"
79         ego_object_ctor( POBJ_GET_PBASE( pchr ) );
80 
81         ChrList_add_free( ichr );
82     }
83 }
84 
85 //--------------------------------------------------------------------------------------------
ChrList_dtor()86 void ChrList_dtor()
87 {
88     CHR_REF cnt;
89 
90     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
91     {
92         chr_config_deconstruct( ChrList.lst + cnt, 100 );
93     }
94 
95     ChrList.free_count = 0;
96     ChrList.used_count = 0;
97     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
98     {
99         ChrList.free_ref[cnt] = MAX_CHR;
100         ChrList.used_ref[cnt] = MAX_CHR;
101     }
102 }
103 
104 //--------------------------------------------------------------------------------------------
ChrList_prune_used()105 void ChrList_prune_used()
106 {
107     // prune the used list
108 
109     size_t cnt;
110     CHR_REF ichr;
111 
112     for ( cnt = 0; cnt < ChrList.used_count; cnt++ )
113     {
114         bool_t removed = bfalse;
115 
116         ichr = ChrList.used_ref[cnt];
117 
118         if ( !VALID_CHR_RANGE( ichr ) || !DEFINED_CHR( ichr ) )
119         {
120             removed = ChrList_remove_used_index( cnt );
121         }
122 
123         if ( removed && !ChrList.lst[ichr].obj_base.in_free_list )
124         {
125             ChrList_add_free( ichr );
126         }
127     }
128 }
129 
130 //--------------------------------------------------------------------------------------------
ChrList_prune_free()131 void ChrList_prune_free()
132 {
133     // prune the free list
134 
135     size_t cnt;
136     CHR_REF ichr;
137 
138     for ( cnt = 0; cnt < ChrList.free_count; cnt++ )
139     {
140         bool_t removed = bfalse;
141 
142         ichr = ChrList.free_ref[cnt];
143 
144         if ( VALID_CHR_RANGE( ichr ) && INGAME_CHR_BASE( ichr ) )
145         {
146             removed = ChrList_remove_free_index( cnt );
147         }
148 
149         if ( removed && !ChrList.lst[ichr].obj_base.in_free_list )
150         {
151             ChrList_add_used( ichr );
152         }
153     }
154 }
155 
156 //--------------------------------------------------------------------------------------------
ChrList_update_used()157 void ChrList_update_used()
158 {
159     size_t cnt;
160     CHR_REF ichr;
161 
162     ChrList_prune_used();
163     ChrList_prune_free();
164 
165     // go through the character list to see if there are any dangling characters
166     for ( ichr = 0; ichr < MAX_CHR; ichr++ )
167     {
168         if ( !ALLOCATED_CHR( ichr ) ) continue;
169 
170         if ( INGAME_CHR( ichr ) )
171         {
172             if ( !ChrList.lst[ichr].obj_base.in_used_list )
173             {
174                 ChrList_add_used( ichr );
175             }
176         }
177         else if ( !DEFINED_CHR( ichr ) )
178         {
179             if ( !ChrList.lst[ichr].obj_base.in_free_list )
180             {
181                 ChrList_add_free( ichr );
182             }
183         }
184     }
185 
186     // blank out the unused elements of the used list
187     for ( cnt = ChrList.used_count; cnt < MAX_CHR; cnt++ )
188     {
189         ChrList.used_ref[cnt] = MAX_CHR;
190     }
191 
192     // blank out the unused elements of the free list
193     for ( cnt = ChrList.free_count; cnt < MAX_CHR; cnt++ )
194     {
195         ChrList.free_ref[cnt] = MAX_CHR;
196     }
197 }
198 
199 //--------------------------------------------------------------------------------------------
ChrList_free_one(const CHR_REF ichr)200 bool_t ChrList_free_one( const CHR_REF ichr )
201 {
202     /// @details ZZ@> This function sticks a character back on the free enchant stack
203     ///
204     /// @note Tying ALLOCATED_CHR() and POBJ_TERMINATE() to ChrList_free_one()
205     /// should be enough to ensure that no character is freed more than once
206 
207     bool_t retval;
208     chr_t * pchr;
209     obj_data_t * pbase;
210 
211     if ( !ALLOCATED_CHR( ichr ) ) return bfalse;
212     pchr = ChrList.lst + ichr;
213     pbase = POBJ_GET_PBASE( pchr );
214 
215 #if (DEBUG_SCRIPT_LEVEL > 0) && defined(DEBUG_PROFILE) && defined(_DEBUG)
216     chr_log_script_time( ichr );
217 #endif
218 
219     // if we are inside a ChrList loop, do not actually change the length of the
220     // list. This will cause some problems later.
221     if ( chr_loop_depth > 0 )
222     {
223         retval = ChrList_add_termination( ichr );
224     }
225     else
226     {
227         // deallocate any dynamically allocated memory
228         pchr = chr_config_deinitialize( pchr, 100 );
229         if ( NULL == pchr ) return bfalse;
230 
231         if ( pbase->in_used_list )
232         {
233             ChrList_remove_used( ichr );
234         }
235 
236         if ( pbase->in_free_list )
237         {
238             retval = btrue;
239         }
240         else
241         {
242             retval = ChrList_add_free( ichr );
243         }
244 
245         // character "destructor"
246         pchr = chr_dtor( pchr );
247         if ( NULL == pchr ) return bfalse;
248     }
249 
250     return retval;
251 }
252 
253 //--------------------------------------------------------------------------------------------
ChrList_get_free()254 size_t ChrList_get_free()
255 {
256     /// @details ZZ@> This function returns the next free character or MAX_CHR if there are none
257 
258     size_t retval = MAX_CHR;
259 
260     if ( ChrList.free_count > 0 )
261     {
262         ChrList.free_count--;
263         ChrList.update_guid++;
264 
265         retval = ChrList.free_ref[ChrList.free_count];
266 
267         // completely remove it from the free list
268         ChrList.free_ref[ChrList.free_count] = MAX_CHR;
269 
270         if ( VALID_CHR_RANGE( retval ) )
271         {
272             // let the object know it is not in the free list any more
273             ChrList.lst[retval].obj_base.in_free_list = bfalse;
274         }
275     }
276 
277     return retval;
278 }
279 
280 //--------------------------------------------------------------------------------------------
ChrList_free_all()281 void ChrList_free_all()
282 {
283     CHR_REF cnt;
284 
285     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
286     {
287         ChrList_free_one( cnt );
288     }
289 }
290 
291 //--------------------------------------------------------------------------------------------
ChrList_get_free_list_index(const CHR_REF ichr)292 int ChrList_get_free_list_index( const CHR_REF ichr )
293 {
294     int retval = -1, cnt;
295 
296     if ( !VALID_CHR_RANGE( ichr ) ) return retval;
297 
298     for ( cnt = 0; cnt < ChrList.free_count; cnt++ )
299     {
300         if ( ichr == ChrList.free_ref[cnt] )
301         {
302             EGOBOO_ASSERT( ChrList.lst[ichr].obj_base.in_free_list );
303             retval = cnt;
304             break;
305         }
306     }
307 
308     return retval;
309 }
310 
311 //--------------------------------------------------------------------------------------------
ChrList_add_free(const CHR_REF ichr)312 bool_t ChrList_add_free( const CHR_REF ichr )
313 {
314     bool_t retval;
315 
316     if ( !VALID_CHR_RANGE( ichr ) ) return bfalse;
317 
318 #if defined(_DEBUG) && defined(DEBUG_CHR_LIST)
319     if ( ChrList_get_free_list_index( ichr ) > 0 )
320     {
321         return bfalse;
322     }
323 #endif
324 
325     EGOBOO_ASSERT( !ChrList.lst[ichr].obj_base.in_free_list );
326 
327     retval = bfalse;
328     if ( ChrList.free_count < MAX_CHR )
329     {
330         ChrList.free_ref[ChrList.free_count] = ichr;
331 
332         ChrList.free_count++;
333         ChrList.update_guid++;
334 
335         ChrList.lst[ichr].obj_base.in_free_list = btrue;
336 
337         retval = btrue;
338     }
339 
340     return retval;
341 }
342 
343 //--------------------------------------------------------------------------------------------
ChrList_remove_free_index(int index)344 bool_t ChrList_remove_free_index( int index )
345 {
346     CHR_REF ichr;
347 
348     // was it found?
349     if ( index < 0 || index >= ChrList.free_count ) return bfalse;
350 
351     ichr = ChrList.free_ref[index];
352 
353     // blank out the index in the list
354     ChrList.free_ref[index] = MAX_CHR;
355 
356     if ( VALID_CHR_RANGE( ichr ) )
357     {
358         // let the object know it is not in the list anymore
359         ChrList.lst[ichr].obj_base.in_free_list = bfalse;
360     }
361 
362     // shorten the list
363     ChrList.free_count--;
364     ChrList.update_guid++;
365 
366     if ( ChrList.free_count > 0 )
367     {
368         // swap the last element for the deleted element
369         SWAP( size_t, ChrList.free_ref[index], ChrList.free_ref[ChrList.free_count] );
370     }
371 
372     return btrue;
373 }
374 
375 //--------------------------------------------------------------------------------------------
ChrList_remove_free(const CHR_REF ichr)376 bool_t ChrList_remove_free( const CHR_REF ichr )
377 {
378     // find the object in the free list
379     int index = ChrList_get_free_list_index( ichr );
380 
381     return ChrList_remove_free_index( index );
382 }
383 
384 //--------------------------------------------------------------------------------------------
ChrList_get_used_list_index(const CHR_REF ichr)385 int ChrList_get_used_list_index( const CHR_REF ichr )
386 {
387     int retval = -1, cnt;
388 
389     if ( !VALID_CHR_RANGE( ichr ) ) return retval;
390 
391     for ( cnt = 0; cnt < ChrList.used_count; cnt++ )
392     {
393         if ( ichr == ChrList.used_ref[cnt] )
394         {
395             EGOBOO_ASSERT( ChrList.lst[ichr].obj_base.in_used_list );
396             retval = cnt;
397             break;
398         }
399     }
400 
401     return retval;
402 }
403 
404 //--------------------------------------------------------------------------------------------
ChrList_add_used(const CHR_REF ichr)405 bool_t ChrList_add_used( const CHR_REF ichr )
406 {
407     bool_t retval;
408 
409     if ( !VALID_CHR_RANGE( ichr ) ) return bfalse;
410 
411 #if defined(_DEBUG) && defined(DEBUG_CHR_LIST)
412     if ( ChrList_get_used_list_index( ichr ) > 0 )
413     {
414         return bfalse;
415     }
416 #endif
417 
418     EGOBOO_ASSERT( !ChrList.lst[ichr].obj_base.in_used_list );
419 
420     retval = bfalse;
421     if ( ChrList.used_count < MAX_CHR )
422     {
423         ChrList.used_ref[ChrList.used_count] = ichr;
424 
425         ChrList.used_count++;
426         ChrList.update_guid++;
427 
428         ChrList.lst[ichr].obj_base.in_used_list = btrue;
429 
430         retval = btrue;
431     }
432 
433     return retval;
434 }
435 
436 //--------------------------------------------------------------------------------------------
ChrList_remove_used_index(int index)437 bool_t ChrList_remove_used_index( int index )
438 {
439     CHR_REF ichr;
440 
441     // was it found?
442     if ( index < 0 || index >= ChrList.used_count ) return bfalse;
443 
444     ichr = ChrList.used_ref[index];
445 
446     // blank out the index in the list
447     ChrList.used_ref[index] = MAX_CHR;
448 
449     if ( VALID_CHR_RANGE( ichr ) )
450     {
451         // let the object know it is not in the list anymore
452         ChrList.lst[ichr].obj_base.in_used_list = bfalse;
453     }
454 
455     // shorten the list
456     ChrList.used_count--;
457     ChrList.update_guid++;
458 
459     if ( ChrList.used_count > 0 )
460     {
461         // swap the last element for the deleted element
462         SWAP( size_t, ChrList.used_ref[index], ChrList.used_ref[ChrList.used_count] );
463     }
464 
465     return btrue;
466 }
467 
468 //--------------------------------------------------------------------------------------------
ChrList_remove_used(const CHR_REF ichr)469 bool_t ChrList_remove_used( const CHR_REF ichr )
470 {
471     // find the object in the used list
472     int index = ChrList_get_used_list_index( ichr );
473 
474     return ChrList_remove_used_index( index );
475 }
476 
477 //--------------------------------------------------------------------------------------------
ChrList_allocate(const CHR_REF override)478 CHR_REF ChrList_allocate( const CHR_REF override )
479 {
480     CHR_REF ichr = ( CHR_REF )MAX_CHR;
481 
482     if ( VALID_CHR_RANGE( override ) )
483     {
484         ichr = ChrList_get_free();
485         if ( override != ichr )
486         {
487             int override_index = ChrList_get_free_list_index( override );
488 
489             if ( override_index < 0 || override_index >= ChrList.free_count )
490             {
491                 ichr = ( CHR_REF )MAX_CHR;
492             }
493             else
494             {
495                 // store the "wrong" value in the override character's index
496                 ChrList.free_ref[override_index] = ichr;
497 
498                 // fix the in_free_list values
499                 ChrList.lst[ichr].obj_base.in_free_list = btrue;
500                 ChrList.lst[override].obj_base.in_free_list = bfalse;
501 
502                 ichr = override;
503             }
504         }
505 
506         if ( MAX_CHR == ichr )
507         {
508             log_warning( "ChrList_allocate() - failed to override a character? character %d already spawned? \n", REF_TO_INT( override ) );
509         }
510     }
511     else
512     {
513         ichr = ChrList_get_free();
514         if ( MAX_CHR == ichr )
515         {
516             log_warning( "ChrList_allocate() - failed to allocate a new character\n" );
517         }
518     }
519 
520     if ( VALID_CHR_RANGE( ichr ) )
521     {
522         // if the character is already being used, make sure to destroy the old one
523         if ( DEFINED_CHR( ichr ) )
524         {
525             ChrList_free_one( ichr );
526         }
527 
528         // allocate the new one
529         POBJ_ALLOCATE( ChrList.lst +  ichr , REF_TO_INT( ichr ) );
530     }
531 
532     if ( ALLOCATED_CHR( ichr ) )
533     {
534         // construct the new structure
535         chr_config_construct( ChrList.lst + ichr, 100 );
536     }
537 
538     return ichr;
539 }
540 
541 //--------------------------------------------------------------------------------------------
ChrList_cleanup()542 void ChrList_cleanup()
543 {
544     int     cnt;
545     chr_t * pchr;
546 
547     // go through the list and activate all the characters that
548     // were created while the list was iterating
549     for ( cnt = 0; cnt < chr_activation_count; cnt++ )
550     {
551         CHR_REF ichr = chr_activation_list[cnt];
552 
553         if ( !ALLOCATED_CHR( ichr ) ) continue;
554         pchr = ChrList.lst + ichr;
555 
556         if ( !pchr->obj_base.turn_me_on ) continue;
557 
558         pchr->obj_base.on         = btrue;
559         pchr->obj_base.turn_me_on = bfalse;
560     }
561     chr_activation_count = 0;
562 
563     // go through and delete any characters that were
564     // supposed to be deleted while the list was iterating
565     for ( cnt = 0; cnt < chr_termination_count; cnt++ )
566     {
567         ChrList_free_one( chr_termination_list[cnt] );
568     }
569     chr_termination_count = 0;
570 }
571 
572 //--------------------------------------------------------------------------------------------
ChrList_add_activation(CHR_REF ichr)573 bool_t ChrList_add_activation( CHR_REF ichr )
574 {
575     // put this character into the activation list so that it can be activated right after
576     // the ChrList loop is completed
577 
578     bool_t retval = bfalse;
579 
580     if ( !VALID_CHR_RANGE( ichr ) ) return bfalse;
581 
582     if ( chr_activation_count < MAX_CHR )
583     {
584         chr_activation_list[chr_activation_count] = ichr;
585         chr_activation_count++;
586 
587         retval = btrue;
588     }
589 
590     ChrList.lst[ichr].obj_base.turn_me_on = btrue;
591 
592     return retval;
593 }
594 
595 //--------------------------------------------------------------------------------------------
ChrList_add_termination(CHR_REF ichr)596 bool_t ChrList_add_termination( CHR_REF ichr )
597 {
598     bool_t retval = bfalse;
599 
600     if ( !VALID_CHR_RANGE( ichr ) ) return bfalse;
601 
602     if ( chr_termination_count < MAX_CHR )
603     {
604         chr_termination_list[chr_termination_count] = ichr;
605         chr_termination_count++;
606 
607         retval = btrue;
608     }
609 
610     // at least mark the object as "waiting to be terminated"
611     POBJ_REQUEST_TERMINATE( ChrList.lst + ichr );
612 
613     return retval;
614 }
615