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