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 char.c
21 /// @brief Implementation of character functions
22 /// @details
23 
24 #include "char.inl"
25 #include "ChrList.h"
26 
27 #include "mad.h"
28 
29 #include "log.h"
30 #include "script.h"
31 #include "menu.h"
32 #include "sound.h"
33 #include "camera.h"
34 #include "input.h"
35 #include "passage.h"
36 #include "graphic.h"
37 #include "game.h"
38 #include "texture.h"
39 #include "ui.h"
40 #include "collision.h"                  //Only or detach_character_from_platform()
41 #include "quest.h"
42 #include "obj_BSP.h"
43 
44 #include "egoboo_vfs.h"
45 #include "egoboo_setup.h"
46 #include "egoboo_fileutil.h"
47 #include "egoboo_strutil.h"
48 #include "egoboo.h"
49 
50 #include "egoboo_math.inl"
51 #include "mesh.inl"
52 
53 #include "egoboo_mem.h"
54 
55 //--------------------------------------------------------------------------------------------
56 //--------------------------------------------------------------------------------------------
57 static IDSZ    inventory_idsz[INVEN_COUNT];
58 
59 //--------------------------------------------------------------------------------------------
60 //--------------------------------------------------------------------------------------------
61 INSTANTIATE_STACK( ACCESS_TYPE_NONE, cap_t, CapStack, MAX_PROFILE );
62 INSTANTIATE_STACK( ACCESS_TYPE_NONE, team_t, TeamStack, TEAM_MAX );
63 
64 int chr_stoppedby_tests = 0;
65 int chr_pressure_tests = 0;
66 
67 //--------------------------------------------------------------------------------------------
68 //--------------------------------------------------------------------------------------------
69 
70 static CHR_REF chr_pack_has_a_stack( const CHR_REF item, const CHR_REF character );
71 static bool_t  chr_add_pack_item( const CHR_REF item, const CHR_REF character );
72 static CHR_REF chr_get_pack_item( const CHR_REF character, grip_offset_t grip_off, bool_t ignorekurse );
73 
74 static bool_t set_weapongrip( const CHR_REF iitem, const CHR_REF iholder, Uint16 vrt_off );
75 
76 static BBOARD_REF chr_add_billboard( const CHR_REF ichr, Uint32 lifetime_secs );
77 
78 static chr_t * resize_one_character( chr_t * pchr );
79 //static void    resize_all_characters();
80 
81 static bool_t  chr_free( chr_t * pchr );
82 
83 static chr_t * chr_config_ctor( chr_t * pchr );
84 static chr_t * chr_config_init( chr_t * pchr );
85 static chr_t * chr_config_deinit( chr_t * pchr );
86 static chr_t * chr_config_active( chr_t * pchr );
87 static chr_t * chr_config_dtor( chr_t * pchr );
88 
89 static int get_grip_verts( Uint16 grip_verts[], const CHR_REF imount, int vrt_offset );
90 
91 bool_t apply_one_character_matrix( chr_t * pchr, matrix_cache_t * mcache );
92 bool_t apply_one_weapon_matrix( chr_t * pweap, matrix_cache_t * mcache );
93 
94 int convert_grip_to_local_points( chr_t * pholder, Uint16 grip_verts[], fvec4_t   dst_point[] );
95 int convert_grip_to_global_points( const CHR_REF iholder, Uint16 grip_verts[], fvec4_t   dst_point[] );
96 
97 // definition that is consistent with using it as a callback in qsort() or some similar function
98 static int  cmp_matrix_cache( const void * vlhs, const void * vrhs );
99 
100 static bool_t chr_upload_cap( chr_t * pchr, cap_t * pcap );
101 
102 void cleanup_one_character( chr_t * pchr );
103 
104 static void chr_log_script_time( const CHR_REF ichr );
105 
106 static bool_t update_chr_darkvision( const CHR_REF character );
107 
108 static fvec2_t chr_get_mesh_diff( chr_t * pchr, float test_pos[], float center_pressure );
109 static float   chr_get_mesh_pressure( chr_t * pchr, float test_pos[] );
110 
111 static egoboo_rv chr_invalidate_child_instances( chr_t * pchr );
112 
113 //--------------------------------------------------------------------------------------------
114 //--------------------------------------------------------------------------------------------
character_system_begin()115 void character_system_begin()
116 {
117     ChrList_init();
118     init_all_cap();
119 }
120 
121 //--------------------------------------------------------------------------------------------
character_system_end()122 void character_system_end()
123 {
124     release_all_cap();
125     ChrList_dtor();
126 }
127 
128 //--------------------------------------------------------------------------------------------
129 //--------------------------------------------------------------------------------------------
chr_free(chr_t * pchr)130 bool_t chr_free( chr_t * pchr )
131 {
132     /// Free all allocated memory
133 
134     if ( !ALLOCATED_PCHR( pchr ) )
135     {
136         EGOBOO_ASSERT( NULL == pchr->inst.vrt_lst );
137         return bfalse;
138     }
139 
140     // do some list clean-up
141     disenchant_character( GET_REF_PCHR( pchr ) );
142 
143     // deallocate
144     BillboardList_free_one( REF_TO_INT( pchr->ibillboard ) );
145 
146     LoopedList_remove( pchr->loopedsound_channel );
147 
148     chr_instance_dtor( &( pchr->inst ) );
149     BSP_leaf_dtor( &( pchr->bsp_leaf ) );
150     ai_state_dtor( &( pchr->ai ) );
151 
152     EGOBOO_ASSERT( NULL == pchr->inst.vrt_lst );
153 
154     return btrue;
155 }
156 
157 //--------------------------------------------------------------------------------------------
chr_ctor(chr_t * pchr)158 chr_t * chr_ctor( chr_t * pchr )
159 {
160     /// @details BB@> initialize the character data to safe values
161     ///     since we use memset(..., 0, ...), everything == 0 == false == 0.0f
162     ///     statements are redundant
163 
164     int cnt;
165     obj_data_t save_base;
166     obj_data_t * pbase;
167 
168     if ( NULL == pchr ) return pchr;
169 
170     // grab the base object
171     pbase = POBJ_GET_PBASE( pchr );
172     if ( NULL == pbase ) return NULL;
173 
174     //---- construct the character object
175 
176     // save the base object data
177     memcpy( &save_base, pbase, sizeof( obj_data_t ) );
178 
179     if ( ALLOCATED_PCHR( pchr ) )
180     {
181         // deallocate any existing data
182         chr_free( pchr );
183 
184         EGOBOO_ASSERT( NULL == pchr->inst.vrt_lst );
185     }
186 
187     // clear out all data
188     memset( pchr, 0, sizeof( *pchr ) );
189 
190     // restore the base object data
191     memcpy( pbase, &save_base, sizeof( obj_data_t ) );
192 
193     // reset the base counters
194     pbase->update_count = 0;
195     pbase->frame_count = 0;
196 
197     // IMPORTANT!!!
198     pchr->ibillboard = INVALID_BILLBOARD;
199     pchr->sparkle = NOSPARKLE;
200     pchr->loopedsound_channel = INVALID_SOUND_CHANNEL;
201 
202     // Set up model stuff
203     pchr->inwhich_slot = SLOT_LEFT;
204     pchr->hitready = btrue;
205     pchr->bore_timer = BORETIME;
206     pchr->careful_timer = CAREFULTIME;
207 
208     // Enchant stuff
209     pchr->firstenchant = ( ENC_REF ) MAX_ENC;
210     pchr->undoenchant = ( ENC_REF ) MAX_ENC;
211     pchr->missiletreatment = MISSILE_NORMAL;
212 
213     // Character stuff
214     pchr->turnmode = TURNMODE_VELOCITY;
215     pchr->alive = btrue;
216 
217     // Jumping
218     pchr->jump_timer = JUMPDELAY;
219 
220     // Grip info
221     pchr->attachedto = ( CHR_REF )MAX_CHR;
222     for ( cnt = 0; cnt < SLOT_COUNT; cnt++ )
223     {
224         pchr->holdingwhich[cnt] = ( CHR_REF )MAX_CHR;
225     }
226 
227     // pack/inventory info
228     pchr->pack.next = ( CHR_REF )MAX_CHR;
229     for ( cnt = 0; cnt < INVEN_COUNT; cnt++ )
230     {
231         pchr->inventory[cnt] = ( CHR_REF )MAX_CHR;
232     }
233 
234     // Set up position
235     pchr->ori.map_facing_y = MAP_TURN_OFFSET;  // These two mean on level surface
236     pchr->ori.map_facing_x = MAP_TURN_OFFSET;
237 
238     // start the character out in the "dance" animation
239     chr_start_anim( pchr, ACTION_DA, btrue, btrue );
240 
241     // I think we have to set the dismount timer, otherwise objects that
242     // are spawned by chests will behave strangely...
243     // nope this did not fix it
244     // ZF@> If this is != 0 then scorpion claws and riders are dropped at spawn (non-item objects)
245     pchr->dismount_timer  = 0;
246     pchr->dismount_object = ( CHR_REF )MAX_CHR;
247 
248     // set all of the integer references to invalid values
249     pchr->firstenchant = ( ENC_REF ) MAX_ENC;
250     pchr->undoenchant  = ( ENC_REF ) MAX_ENC;
251     for ( cnt = 0; cnt < SLOT_COUNT; cnt++ )
252     {
253         pchr->holdingwhich[cnt] = ( CHR_REF )MAX_CHR;
254     }
255 
256     pchr->pack.next = ( CHR_REF )MAX_CHR;
257     for ( cnt = 0; cnt < INVEN_COUNT; cnt++ )
258     {
259         pchr->inventory[cnt] = ( CHR_REF )MAX_CHR;
260     }
261 
262     pchr->onwhichplatform_ref    = ( CHR_REF )MAX_CHR;
263     pchr->onwhichplatform_update = 0;
264     pchr->targetplatform_ref     = ( CHR_REF )MAX_CHR;
265 
266     // all movements valid
267     pchr->movement_bits   = ( unsigned )( ~0 );
268 
269     // not a player
270     pchr->is_which_player = MAX_PLAYER;
271 
272     // initialize the bsp node for this character
273     pchr->bsp_leaf.data      = pchr;
274     pchr->bsp_leaf.data_type = BSP_LEAF_CHR;
275     pchr->bsp_leaf.index     = GET_INDEX_PCHR( pchr );
276 
277     //---- call the constructors of the "has a" classes
278 
279     // set the insance values to safe values
280     chr_instance_ctor( &( pchr->inst ) );
281 
282     // intialize the ai_state
283     ai_state_ctor( &( pchr->ai ) );
284 
285     // initialize the bsp node for this character
286     BSP_leaf_ctor( &( pchr->bsp_leaf ), 3, pchr, BSP_LEAF_CHR );
287     pchr->bsp_leaf.index = GET_INDEX_PCHR( pchr );
288 
289     // initialize the physics
290     phys_data_ctor( &( pchr->phys ) );
291 
292     return pchr;
293 }
294 
295 //--------------------------------------------------------------------------------------------
chr_dtor(chr_t * pchr)296 chr_t * chr_dtor( chr_t * pchr )
297 {
298     if ( NULL == pchr ) return pchr;
299 
300     // destruct/free any allocated data
301     chr_free( pchr );
302 
303     // Destroy the base object.
304     // Sets the state to ego_object_terminated automatically.
305     POBJ_TERMINATE( pchr );
306 
307     return pchr;
308 }
309 
310 //--------------------------------------------------------------------------------------------
311 //--------------------------------------------------------------------------------------------
chr_count_free()312 int chr_count_free()
313 {
314     return ChrList.free_count;
315 }
316 
317 //--------------------------------------------------------------------------------------------
chr_count_used()318 int chr_count_used()
319 {
320     return ChrList.used_count; // MAX_CHR - ChrList.free_count;
321 }
322 
323 //--------------------------------------------------------------------------------------------
flash_character_height(const CHR_REF character,Uint8 valuelow,Sint16 low,Uint8 valuehigh,Sint16 high)324 egoboo_rv flash_character_height( const CHR_REF character, Uint8 valuelow, Sint16 low,
325                                   Uint8 valuehigh, Sint16 high )
326 {
327     /// @details ZZ@> This function sets a character's lighting depending on vertex height...
328     ///    Can make feet dark and head light...
329 
330     Uint32 cnt;
331     Sint16 z;
332 
333     mad_t * pmad;
334     chr_instance_t * pinst;
335 
336     pinst = chr_get_pinstance( character );
337     if ( NULL == pinst ) return rv_error;
338 
339     pmad = chr_get_pmad( character );
340     if ( NULL == pmad ) return rv_error;
341 
342     for ( cnt = 0; cnt < pinst->vrt_count; cnt++ )
343     {
344         z = pinst->vrt_lst[cnt].pos[ZZ];
345 
346         if ( z < low )
347         {
348             pinst->vrt_lst[cnt].col[RR] =
349                 pinst->vrt_lst[cnt].col[GG] =
350                     pinst->vrt_lst[cnt].col[BB] = valuelow;
351         }
352         else
353         {
354             if ( z > high )
355             {
356                 pinst->vrt_lst[cnt].col[RR] =
357                     pinst->vrt_lst[cnt].col[GG] =
358                         pinst->vrt_lst[cnt].col[BB] = valuehigh;
359             }
360             else
361             {
362                 Uint8 valuemid = ( valuehigh * ( z - low ) / ( high - low ) ) +
363                                  ( valuelow * ( high - z ) / ( high - low ) );
364 
365                 pinst->vrt_lst[cnt].col[RR] =
366                     pinst->vrt_lst[cnt].col[GG] =
367                         pinst->vrt_lst[cnt].col[BB] =  valuemid;
368             }
369         }
370     }
371 
372     return rv_success;
373 }
374 
375 //--------------------------------------------------------------------------------------------
chr_set_enviro_grid_level(chr_t * pchr,float level)376 void chr_set_enviro_grid_level( chr_t * pchr, float level )
377 {
378     if ( !DEFINED_PCHR( pchr ) ) return;
379 
380     if ( level != pchr->enviro.grid_level )
381     {
382         pchr->enviro.grid_level = level;
383 
384         apply_reflection_matrix( &( pchr->inst ), level );
385     }
386 }
387 
388 //--------------------------------------------------------------------------------------------
chr_copy_enviro(chr_t * chr_psrc,chr_t * chr_pdst)389 bool_t chr_copy_enviro( chr_t * chr_psrc, chr_t * chr_pdst )
390 {
391     /// BB@> do a deep copy on the character's enviro data
392 
393     chr_environment_t * psrc, * pdst;
394 
395     if ( NULL == chr_psrc || NULL == chr_pdst ) return bfalse;
396 
397     if ( chr_psrc == chr_pdst ) return btrue;
398 
399     psrc = &( chr_psrc->enviro );
400     pdst = &( chr_pdst->enviro );
401 
402     // use the special function to set the grid level
403     // this must done first so that the character's reflection data is set properly
404     chr_set_enviro_grid_level( chr_pdst, psrc->grid_level );
405 
406     // now just copy the other data.
407     // use memmove() in the odd case the regions overlap
408     memmove( psrc, pdst, sizeof( *psrc ) );
409 
410     return btrue;
411 }
412 
413 //--------------------------------------------------------------------------------------------
keep_weapons_with_holders()414 void keep_weapons_with_holders()
415 {
416     /// @details ZZ@> This function keeps weapons near their holders
417 
418     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
419     {
420         CHR_REF iattached = pchr->attachedto;
421 
422         if ( INGAME_CHR( iattached ) )
423         {
424             chr_t * pattached = ChrList.lst + iattached;
425 
426             // Keep in hand weapons with iattached
427             if ( chr_matrix_valid( pchr ) )
428             {
429                 chr_set_pos( pchr, mat_getTranslate_v( pchr->inst.matrix.v ) );
430             }
431             else
432             {
433                 chr_set_pos( pchr, chr_get_pos_v( pattached ) );
434             }
435 
436             pchr->ori.facing_z = pattached->ori.facing_z;
437 
438             // Copy this stuff ONLY if it's a weapon, not for mounts
439             if ( pattached->transferblend && pchr->isitem )
440             {
441 
442                 // Items become partially invisible in hands of players
443                 if ( VALID_PLA( pattached->is_which_player ) && 255 != pattached->inst.alpha )
444                 {
445                     chr_set_alpha( pchr, SEEINVISIBLE );
446                 }
447                 else
448                 {
449                     // Only if not naturally transparent
450                     if ( 255 == pchr->alpha_base )
451                     {
452                         chr_set_alpha( pchr, pattached->inst.alpha );
453                     }
454                     else
455                     {
456                         chr_set_alpha( pchr, pchr->alpha_base );
457                     }
458                 }
459 
460                 // Do light too
461                 if ( VALID_PLA( pattached->is_which_player ) && 255 != pattached->inst.light )
462                 {
463                     chr_set_light( pchr, SEEINVISIBLE );
464                 }
465                 else
466                 {
467                     // Only if not naturally transparent
468                     if ( 255 == chr_get_pcap( cnt )->light )
469                     {
470                         chr_set_light( pchr, pattached->inst.light );
471                     }
472                     else
473                     {
474                         chr_set_light( pchr, pchr->light_base );
475                     }
476                 }
477             }
478         }
479         else
480         {
481             pchr->attachedto = ( CHR_REF )MAX_CHR;
482 
483             // Keep inventory with iattached
484             if ( !pchr->pack.is_packed )
485             {
486                 PACK_BEGIN_LOOP( ipacked, pchr->pack.next )
487                 {
488                     chr_set_pos( ChrList.lst + ipacked, chr_get_pos_v( pchr ) );
489 
490                     // Copy olds to make SendMessageNear work
491                     ChrList.lst[ipacked].pos_old = pchr->pos_old;
492                 }
493                 PACK_END_LOOP( ipacked );
494             }
495         }
496     }
497     CHR_END_LOOP();
498 }
499 
500 //--------------------------------------------------------------------------------------------
make_one_character_matrix(const CHR_REF ichr)501 void make_one_character_matrix( const CHR_REF ichr )
502 {
503     /// @details ZZ@> This function sets one character's matrix
504 
505     chr_t * pchr;
506     chr_instance_t * pinst;
507 
508     if ( !INGAME_CHR( ichr ) ) return;
509     pchr = ChrList.lst + ichr;
510     pinst = &( pchr->inst );
511 
512     // invalidate this matrix
513     pinst->matrix_cache.matrix_valid = bfalse;
514 
515     if ( pchr->is_overlay )
516     {
517         // This character is an overlay and its ai.target points to the object it is overlaying
518         // Overlays are kept with their target...
519         if ( INGAME_CHR( pchr->ai.target ) )
520         {
521             chr_t * ptarget = ChrList.lst + pchr->ai.target;
522 
523             chr_set_pos( pchr, chr_get_pos_v( ptarget ) );
524 
525             // copy the matrix
526             CopyMatrix( &( pinst->matrix ), &( ptarget->inst.matrix ) );
527 
528             // copy the matrix data
529             pinst->matrix_cache = ptarget->inst.matrix_cache;
530         }
531     }
532     else
533     {
534         if ( pchr->stickybutt )
535         {
536             pinst->matrix = ScaleXYZRotateXYZTranslate_SpaceFixed( pchr->fat, pchr->fat, pchr->fat,
537                             TO_TURN( pchr->ori.facing_z ),
538                             TO_TURN( pchr->ori.map_facing_x - MAP_TURN_OFFSET ),
539                             TO_TURN( pchr->ori.map_facing_y - MAP_TURN_OFFSET ),
540                             pchr->pos.x, pchr->pos.y, pchr->pos.z );
541         }
542         else
543         {
544             pinst->matrix = ScaleXYZRotateXYZTranslate_BodyFixed( pchr->fat, pchr->fat, pchr->fat,
545                             TO_TURN( pchr->ori.facing_z ),
546                             TO_TURN( pchr->ori.map_facing_x - MAP_TURN_OFFSET ),
547                             TO_TURN( pchr->ori.map_facing_y - MAP_TURN_OFFSET ),
548                             pchr->pos.x, pchr->pos.y, pchr->pos.z );
549         }
550 
551         pinst->matrix_cache.valid        = btrue;
552         pinst->matrix_cache.matrix_valid = btrue;
553         pinst->matrix_cache.type_bits    = MAT_CHARACTER;
554 
555         pinst->matrix_cache.self_scale.x = pchr->fat;
556         pinst->matrix_cache.self_scale.y = pchr->fat;
557         pinst->matrix_cache.self_scale.z = pchr->fat;
558 
559         pinst->matrix_cache.rotate.x = CLIP_TO_16BITS( pchr->ori.map_facing_x - MAP_TURN_OFFSET );
560         pinst->matrix_cache.rotate.y = CLIP_TO_16BITS( pchr->ori.map_facing_y - MAP_TURN_OFFSET );
561         pinst->matrix_cache.rotate.z = pchr->ori.facing_z;
562 
563         pinst->matrix_cache.pos = chr_get_pos( pchr );
564     }
565 }
566 
567 //--------------------------------------------------------------------------------------------
568 //--------------------------------------------------------------------------------------------
chr_log_script_time(const CHR_REF ichr)569 void chr_log_script_time( const CHR_REF ichr )
570 {
571     // log the amount of script time that this object used up
572 
573     chr_t * pchr;
574     cap_t * pcap;
575     FILE * ftmp;
576 
577     if ( !DEFINED_CHR( ichr ) ) return;
578     pchr = ChrList.lst + ichr;
579 
580     if ( pchr->ai._clkcount <= 0 ) return;
581 
582     pcap = chr_get_pcap( ichr );
583     if ( NULL == pcap ) return;
584 
585     ftmp = fopen( vfs_resolveWriteFilename( "/debug/script_timing.txt" ), "a+" );
586     if ( NULL != ftmp )
587     {
588         fprintf( ftmp, "update == %d\tindex == %d\tname == \"%s\"\tclassname == \"%s\"\ttotal_time == %e\ttotal_calls == %f\n",
589                  update_wld, REF_TO_INT( ichr ), pchr->Name, pcap->classname,
590                  pchr->ai._clktime, pchr->ai._clkcount );
591         fflush( ftmp );
592         fclose( ftmp );
593     }
594 }
595 
596 //--------------------------------------------------------------------------------------------
free_one_character_in_game(const CHR_REF character)597 void free_one_character_in_game( const CHR_REF character )
598 {
599     /// @details ZZ@> This function sticks a character back on the free character stack
600     ///
601     /// @note This should only be called by cleanup_all_characters() or free_inventory_in_game()
602 
603     int     cnt;
604     cap_t * pcap;
605     chr_t * pchr;
606 
607     if ( !DEFINED_CHR( character ) ) return;
608     pchr = ChrList.lst + character;
609 
610     pcap = pro_get_pcap( pchr->profile_ref );
611     if ( NULL == pcap ) return;
612 
613     // Remove from stat list
614     if ( pchr->StatusList_on )
615     {
616         bool_t stat_found;
617 
618         pchr->StatusList_on = bfalse;
619 
620         stat_found = bfalse;
621         for ( cnt = 0; cnt < StatusList_count; cnt++ )
622         {
623             if ( StatusList[cnt] == character )
624             {
625                 stat_found = btrue;
626                 break;
627             }
628         }
629 
630         if ( stat_found )
631         {
632             for ( cnt++; cnt < StatusList_count; cnt++ )
633             {
634                 SWAP( CHR_REF, StatusList[cnt-1], StatusList[cnt] );
635             }
636             StatusList_count--;
637         }
638     }
639 
640     // Make sure everyone knows it died
641     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
642     {
643         ai_state_t * pai;
644 
645         if ( !INGAME_CHR( cnt ) || cnt == character ) continue;
646         pai = chr_get_pai( cnt );
647 
648         if ( pai->target == character )
649         {
650             SET_BIT( pai->alert, ALERTIF_TARGETKILLED );
651             pai->target = cnt;
652         }
653 
654         if ( chr_get_pteam( cnt )->leader == character )
655         {
656             SET_BIT( pai->alert, ALERTIF_LEADERKILLED );
657         }
658     }
659     CHR_END_LOOP();
660 
661     // Handle the team
662     if ( pchr->alive && !pcap->invictus && TeamStack.lst[pchr->baseteam].morale > 0 )
663     {
664         TeamStack.lst[pchr->baseteam].morale--;
665     }
666 
667     if ( TeamStack.lst[pchr->team].leader == character )
668     {
669         TeamStack.lst[pchr->team].leader = NOLEADER;
670     }
671 
672     // remove any attached particles
673     disaffirm_attached_particles( character );
674 
675     // actually get rid of the character
676     ChrList_free_one( character );
677 }
678 
679 //--------------------------------------------------------------------------------------------
free_inventory_in_game(const CHR_REF character)680 void free_inventory_in_game( const CHR_REF character )
681 {
682     /// @details ZZ@> This function frees every item in the character's inventory
683     ///
684     /// @note this should only be called by cleanup_all_characters()
685 
686     if ( !DEFINED_CHR( character ) ) return;
687 
688     PACK_BEGIN_LOOP( ipacked, ChrList.lst[character].pack.next )
689     {
690         free_one_character_in_game( ipacked );
691     }
692     PACK_END_LOOP( ipacked );
693 
694     // set the inventory to the "empty" state
695     ChrList.lst[character].pack.count = 0;
696     ChrList.lst[character].pack.next  = ( CHR_REF )MAX_CHR;
697 }
698 
699 //--------------------------------------------------------------------------------------------
place_particle_at_vertex(prt_t * pprt,const CHR_REF character,int vertex_offset)700 prt_t * place_particle_at_vertex( prt_t * pprt, const CHR_REF character, int vertex_offset )
701 {
702     /// @details ZZ@> This function sets one particle's position to be attached to a character.
703     ///    It will kill the particle if the character is no longer around
704 
705     int     vertex;
706     fvec4_t point[1], nupoint[1];
707 
708     chr_t * pchr;
709 
710     if ( !DEFINED_PPRT( pprt ) ) return pprt;
711 
712     if ( !INGAME_CHR( character ) )
713     {
714         goto place_particle_at_vertex_fail;
715     }
716     pchr = ChrList.lst + character;
717 
718     // Check validity of attachment
719     if ( pchr->pack.is_packed )
720     {
721         goto place_particle_at_vertex_fail;
722     }
723 
724     // Do we have a matrix???
725     if ( !chr_matrix_valid( pchr ) )
726     {
727         chr_update_matrix( pchr, btrue );
728     }
729 
730     // Do we have a matrix???
731     if ( chr_matrix_valid( pchr ) )
732     {
733         // Transform the weapon vertex_offset from model to world space
734         mad_t * pmad = chr_get_pmad( character );
735 
736         if ( vertex_offset == GRIP_ORIGIN )
737         {
738             fvec3_t tmp_pos = VECT3( pchr->inst.matrix.CNV( 3, 0 ), pchr->inst.matrix.CNV( 3, 1 ), pchr->inst.matrix.CNV( 3, 2 ) );
739             prt_set_pos( pprt, tmp_pos.v );
740 
741             return pprt;
742         }
743 
744         vertex = 0;
745         if ( NULL != pmad )
746         {
747             vertex = (( int )pchr->inst.vrt_count ) - vertex_offset;
748 
749             // do the automatic update
750             chr_instance_update_vertices( &( pchr->inst ), vertex, vertex, bfalse );
751 
752             // Calculate vertex_offset point locations with linear interpolation and other silly things
753             point[0].x = pchr->inst.vrt_lst[vertex].pos[XX];
754             point[0].y = pchr->inst.vrt_lst[vertex].pos[YY];
755             point[0].z = pchr->inst.vrt_lst[vertex].pos[ZZ];
756             point[0].w = 1.0f;
757         }
758         else
759         {
760             point[0].x =
761                 point[0].y =
762                     point[0].z = 0.0f;
763             point[0].w = 1.0f;
764         }
765 
766         // Do the transform
767         TransformVertices( &( pchr->inst.matrix ), point, nupoint, 1 );
768 
769         prt_set_pos( pprt, nupoint[0].v );
770     }
771     else
772     {
773         // No matrix, so just wing it...
774         prt_set_pos( pprt, chr_get_pos_v( pchr ) );
775     }
776 
777     return pprt;
778 
779 place_particle_at_vertex_fail:
780 
781     prt_request_terminate( GET_REF_PPRT( pprt ) );
782 
783     return NULL;
784 }
785 
786 //--------------------------------------------------------------------------------------------
update_all_character_matrices()787 void update_all_character_matrices()
788 {
789     /// @details ZZ@> This function makes all of the character's matrices
790 
791     // just call chr_update_matrix on every character
792     CHR_BEGIN_LOOP_ACTIVE( ichr, pchr )
793     {
794         chr_update_matrix( pchr, btrue );
795     }
796     CHR_END_LOOP();
797 }
798 
799 //--------------------------------------------------------------------------------------------
free_all_chraracters()800 void free_all_chraracters()
801 {
802     /// @details ZZ@> This function resets the character allocation list
803 
804     // free all the characters
805     ChrList_free_all();
806 
807     // free_all_players
808     PlaStack.count = 0;
809     local_numlpla = 0;
810     local_stats.noplayers = btrue;
811 
812     // free_all_stats
813     StatusList_count = 0;
814 }
815 
816 //--------------------------------------------------------------------------------------------
chr_get_mesh_pressure(chr_t * pchr,float test_pos[])817 float chr_get_mesh_pressure( chr_t * pchr, float test_pos[] )
818 {
819     float retval = 0.0f;
820     float radius = 0.0f;
821 
822     if ( !DEFINED_PCHR( pchr ) ) return retval;
823 
824     if ( CHR_INFINITE_WEIGHT == pchr->phys.weight ) return retval;
825 
826     // deal with the optional parameters
827     if ( NULL == test_pos ) test_pos = pchr->pos.v;
828 
829     // calculate the radius based on whether the character is on camera
830     // ZF> this may be the cause of the bug allowing AI to move through walls when the camera is not looking at them?
831     radius = 0.0f;
832     if ( cfg.dev_mode && !SDLKEYDOWN( SDLK_F8 ) )
833     {
834         if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
835         {
836             if ( PMesh->tmem.tile_list[ pchr->onwhichgrid ].inrenderlist )
837             {
838                 radius = pchr->bump_1.size;
839             }
840         }
841     }
842 
843     mesh_mpdfx_tests = 0;
844     mesh_bound_tests = 0;
845     mesh_pressure_tests = 0;
846     {
847         retval = mesh_get_pressure( PMesh, test_pos, radius, pchr->stoppedby );
848     }
849     chr_stoppedby_tests += mesh_mpdfx_tests;
850     chr_pressure_tests += mesh_pressure_tests;
851 
852     return retval;
853 }
854 
855 //--------------------------------------------------------------------------------------------
chr_get_mesh_diff(chr_t * pchr,float test_pos[],float center_pressure)856 fvec2_t chr_get_mesh_diff( chr_t * pchr, float test_pos[], float center_pressure )
857 {
858     fvec2_t retval = ZERO_VECT2;
859     float   radius;
860 
861     if ( !DEFINED_PCHR( pchr ) ) return retval;
862 
863     if ( CHR_INFINITE_WEIGHT == pchr->phys.weight ) return retval;
864 
865     // deal with the optional parameters
866     if ( NULL == test_pos ) test_pos = pchr->pos.v;
867 
868     // calculate the radius based on whether the character is on camera
869     // ZF> this may be the cause of the bug allowing AI to move through walls when the camera is not looking at them?
870     radius = 0.0f;
871     if ( cfg.dev_mode && !SDLKEYDOWN( SDLK_F8 ) )
872     {
873         if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
874         {
875             if ( PMesh->tmem.tile_list[ pchr->onwhichgrid ].inrenderlist )
876             {
877                 radius = pchr->bump_1.size;
878             }
879         }
880     }
881 
882     mesh_mpdfx_tests = 0;
883     mesh_bound_tests = 0;
884     mesh_pressure_tests = 0;
885     {
886         retval = mesh_get_diff( PMesh, test_pos, radius, center_pressure, pchr->stoppedby );
887     }
888     chr_stoppedby_tests += mesh_mpdfx_tests;
889     chr_pressure_tests += mesh_pressure_tests;
890 
891     return retval;
892 }
893 
894 //--------------------------------------------------------------------------------------------
chr_hit_wall(chr_t * pchr,const float test_pos[],float nrm[],float * pressure,mesh_wall_data_t * pdata)895 BIT_FIELD chr_hit_wall( chr_t * pchr, const float test_pos[], float nrm[], float * pressure, mesh_wall_data_t * pdata )
896 {
897     /// @details ZZ@> This function returns nonzero if the character hit a wall that the
898     ///    character is not allowed to cross
899 
900     BIT_FIELD    retval;
901     float        radius;
902 
903     if ( !DEFINED_PCHR( pchr ) ) return 0;
904 
905     if ( CHR_INFINITE_WEIGHT == pchr->phys.weight ) return 0;
906 
907     // deal with the optional parameters
908     if ( NULL == test_pos ) test_pos = pchr->pos.v;
909 
910     // calculate the radius based on whether the character is on camera
911     // ZF> this may be the cause of the bug allowing AI to move through walls when the camera is not looking at them?
912     radius = 0.0f;
913     if ( cfg.dev_mode && !SDLKEYDOWN( SDLK_F8 ) )
914     {
915         if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
916         {
917             if ( PMesh->tmem.tile_list[ pchr->onwhichgrid ].inrenderlist )
918             {
919                 radius = pchr->bump_1.size;
920             }
921         }
922     }
923 
924     mesh_mpdfx_tests = 0;
925     mesh_bound_tests = 0;
926     mesh_pressure_tests = 0;
927     {
928         retval = mesh_hit_wall( PMesh, test_pos, radius, pchr->stoppedby, nrm, pressure, pdata );
929     }
930     chr_stoppedby_tests += mesh_mpdfx_tests;
931     chr_pressure_tests  += mesh_pressure_tests;
932 
933     return retval;
934 }
935 
936 //--------------------------------------------------------------------------------------------
chr_test_wall(chr_t * pchr,const float test_pos[],mesh_wall_data_t * pdata)937 BIT_FIELD chr_test_wall( chr_t * pchr, const float test_pos[], mesh_wall_data_t * pdata )
938 {
939     /// @details ZZ@> This function returns nonzero if the character hit a wall that the
940     ///    character is not allowed to cross
941 
942     BIT_FIELD retval;
943     float  radius;
944 
945     if ( !ACTIVE_PCHR( pchr ) ) return EMPTY_BIT_FIELD;
946 
947     if ( CHR_INFINITE_WEIGHT == pchr->phys.weight ) return EMPTY_BIT_FIELD;
948 
949     // calculate the radius based on whether the character is on camera
950     // ZF> this may be the cause of the bug allowing AI to move through walls when the camera is not looking at them?
951     radius = 0.0f;
952     if ( cfg.dev_mode && !SDLKEYDOWN( SDLK_F8 ) )
953     {
954         if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
955         {
956             if ( PMesh->tmem.tile_list[ pchr->onwhichgrid ].inrenderlist )
957             {
958                 radius = pchr->bump_1.size;
959             }
960         }
961     }
962 
963     if ( NULL == test_pos ) test_pos = pchr->pos.v;
964 
965     // do the wall test
966     mesh_mpdfx_tests = 0;
967     mesh_bound_tests = 0;
968     mesh_pressure_tests = 0;
969     {
970         retval = mesh_test_wall( PMesh, test_pos, radius, pchr->stoppedby, pdata );
971     }
972     chr_stoppedby_tests += mesh_mpdfx_tests;
973     chr_pressure_tests += mesh_pressure_tests;
974 
975     return retval;
976 }
977 
978 //--------------------------------------------------------------------------------------------
chr_is_over_water(chr_t * pchr)979 bool_t chr_is_over_water( chr_t *pchr )
980 {
981     /// @details ZF@> This function returns true if the character is over a water tile
982 
983     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
984 
985     if ( !water.is_water || !mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) ) return bfalse;
986 
987     return 0 != mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_WATER );
988 }
989 
990 //--------------------------------------------------------------------------------------------
reset_character_accel(const CHR_REF character)991 void reset_character_accel( const CHR_REF character )
992 {
993     /// @details ZZ@> This function fixes a character's max acceleration
994 
995     ENC_REF ienc_now, ienc_nxt;
996     size_t  ienc_count;
997     chr_t * pchr;
998     cap_t * pcap;
999 
1000     if ( !INGAME_CHR( character ) ) return;
1001     pchr = ChrList.lst + character;
1002 
1003     // cleanup the enchant list
1004     cleanup_character_enchants( pchr );
1005 
1006     // Okay, remove all acceleration enchants
1007     ienc_now = pchr->firstenchant;
1008     ienc_count = 0;
1009     while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1010     {
1011         ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1012 
1013         enchant_remove_add( ienc_now, ADDACCEL );
1014 
1015         ienc_now = ienc_nxt;
1016         ienc_count++;
1017     }
1018     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1019 
1020     // Set the starting value
1021     pchr->maxaccel_reset = 0;
1022     pcap = chr_get_pcap( character );
1023     if ( NULL != pcap )
1024     {
1025         pchr->maxaccel = pchr->maxaccel_reset = pcap->maxaccel[pchr->skin];
1026     }
1027 
1028     // cleanup the enchant list
1029     cleanup_character_enchants( pchr );
1030 
1031     // Put the acceleration enchants back on
1032     ienc_now = pchr->firstenchant;
1033     ienc_count = 0;
1034     while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1035     {
1036         ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1037 
1038         enchant_apply_add( ienc_now, ADDACCEL, enc_get_ieve( ienc_now ) );
1039 
1040         ienc_now = ienc_nxt;
1041         ienc_count++;
1042     }
1043     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1044 }
1045 
1046 //--------------------------------------------------------------------------------------------
detach_character_from_mount(const CHR_REF character,Uint8 ignorekurse,Uint8 doshop)1047 bool_t detach_character_from_mount( const CHR_REF character, Uint8 ignorekurse, Uint8 doshop )
1048 {
1049     /// @details ZZ@> This function drops an item
1050 
1051     CHR_REF mount;
1052     Uint16  hand;
1053     bool_t  inshop;
1054     chr_t * pchr, * pmount;
1055 
1056     // Make sure the character is valid
1057     if ( !INGAME_CHR( character ) ) return bfalse;
1058     pchr = ChrList.lst + character;
1059 
1060     // Make sure the character is mounted
1061     mount = ChrList.lst[character].attachedto;
1062     if ( !INGAME_CHR( mount ) ) return bfalse;
1063     pmount = ChrList.lst + mount;
1064 
1065     // Don't allow living characters to drop kursed weapons
1066     if ( !ignorekurse && pchr->iskursed && pmount->alive && pchr->isitem )
1067     {
1068         SET_BIT( pchr->ai.alert, ALERTIF_NOTDROPPED );
1069         return bfalse;
1070     }
1071 
1072     // set the dismount timer
1073     if ( !pchr->isitem ) pchr->dismount_timer  = PHYS_DISMOUNT_TIME;
1074     pchr->dismount_object = mount;
1075 
1076     // Figure out which hand it's in
1077     hand = pchr->inwhich_slot;
1078 
1079     // Rip 'em apart
1080     pchr->attachedto = ( CHR_REF )MAX_CHR;
1081     if ( pmount->holdingwhich[SLOT_LEFT] == character )
1082         pmount->holdingwhich[SLOT_LEFT] = ( CHR_REF )MAX_CHR;
1083 
1084     if ( pmount->holdingwhich[SLOT_RIGHT] == character )
1085         pmount->holdingwhich[SLOT_RIGHT] = ( CHR_REF )MAX_CHR;
1086 
1087     if ( pchr->alive )
1088     {
1089         // play the falling animation...
1090         chr_play_action( pchr, ACTION_JB + hand, bfalse );
1091     }
1092     else if ( pchr->inst.action_which < ACTION_KA || pchr->inst.action_which > ACTION_KD )
1093     {
1094         // play the "killed" animation...
1095         chr_play_action( pchr, ACTION_KA + generate_randmask( 0, 3 ), bfalse );
1096         chr_instance_set_action_keep( &( pchr->inst ), btrue );
1097     }
1098 
1099     // Set the positions
1100     if ( chr_matrix_valid( pchr ) )
1101     {
1102         chr_set_pos( pchr, mat_getTranslate_v( pchr->inst.matrix.v ) );
1103     }
1104     else
1105     {
1106         chr_set_pos( pchr, chr_get_pos_v( pmount ) );
1107     }
1108 
1109     // Make sure it's not dropped in a wall...
1110     if ( EMPTY_BIT_FIELD != chr_test_wall( pchr, NULL, NULL ) )
1111     {
1112         fvec3_t pos_tmp;
1113 
1114         pos_tmp.x = pmount->pos.x;
1115         pos_tmp.y = pmount->pos.y;
1116         pos_tmp.z = pchr->pos.z;
1117 
1118         chr_set_pos( pchr, pos_tmp.v );
1119 
1120         chr_update_breadcrumb( pchr, btrue );
1121     }
1122 
1123     // Check for shop passages
1124     inshop = bfalse;
1125     if ( doshop )
1126     {
1127         inshop = do_shop_drop( mount, character );
1128     }
1129 
1130     // Make sure it works right
1131     pchr->hitready = btrue;
1132     if ( inshop )
1133     {
1134         // Drop straight down to avoid theft
1135         pchr->vel.x = 0;
1136         pchr->vel.y = 0;
1137     }
1138     else
1139     {
1140         pchr->vel.x = pmount->vel.x;
1141         pchr->vel.y = pmount->vel.y;
1142     }
1143 
1144     pchr->vel.z = DROPZVEL;
1145 
1146     // Turn looping off
1147     chr_instance_set_action_loop( &( pchr->inst ), bfalse );
1148 
1149     // Reset the team if it is a mount
1150     if ( pmount->ismount )
1151     {
1152         pmount->team = pmount->baseteam;
1153         SET_BIT( pmount->ai.alert, ALERTIF_DROPPED );
1154     }
1155 
1156     pchr->team = pchr->baseteam;
1157     SET_BIT( pchr->ai.alert, ALERTIF_DROPPED );
1158 
1159     // Reset transparency
1160     if ( pchr->isitem && pmount->transferblend )
1161     {
1162         ENC_REF ienc_now, ienc_nxt;
1163         size_t  ienc_count;
1164 
1165         // cleanup the enchant list
1166         cleanup_character_enchants( pchr );
1167 
1168         // Okay, reset transparency
1169         ienc_now = pchr->firstenchant;
1170         ienc_count = 0;
1171         while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1172         {
1173             ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1174 
1175             enchant_remove_set( ienc_now, SETALPHABLEND );
1176             enchant_remove_set( ienc_now, SETLIGHTBLEND );
1177 
1178             ienc_now = ienc_nxt;
1179             ienc_count++;
1180         }
1181         if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1182 
1183         chr_set_alpha( pchr, pchr->alpha_base );
1184         chr_set_light( pchr, pchr->light_base );
1185 
1186         // cleanup the enchant list
1187         cleanup_character_enchants( pchr );
1188 
1189         // apply the blend enchants
1190         ienc_now = pchr->firstenchant;
1191         ienc_count = 0;
1192         while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1193         {
1194             PRO_REF ipro = enc_get_ipro( ienc_now );
1195             ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1196 
1197             if ( LOADED_PRO( ipro ) )
1198             {
1199                 enchant_apply_set( ienc_now, SETALPHABLEND, ipro );
1200                 enchant_apply_set( ienc_now, SETLIGHTBLEND, ipro );
1201             }
1202 
1203             ienc_now = ienc_nxt;
1204             ienc_count++;
1205         }
1206         if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1207     }
1208 
1209     // Set twist
1210     pchr->ori.map_facing_y = MAP_TURN_OFFSET;
1211     pchr->ori.map_facing_x = MAP_TURN_OFFSET;
1212 
1213     // turn off keeping, unless the object is dead
1214     if ( pchr->life <= 0 )
1215     {
1216         // the object is dead. play the killed animation and make it freeze there
1217         chr_play_action( pchr, ACTION_KA + generate_randmask( 0, 3 ), bfalse );
1218         chr_instance_set_action_keep( &( pchr->inst ), btrue );
1219     }
1220     else
1221     {
1222         // play the jump animation, and un-keep it
1223         chr_play_action( pchr, ACTION_JA, btrue );
1224         chr_instance_set_action_keep( &( pchr->inst ), bfalse );
1225     }
1226 
1227     chr_update_matrix( pchr, btrue );
1228 
1229     return btrue;
1230 }
1231 
1232 //--------------------------------------------------------------------------------------------
reset_character_alpha(const CHR_REF character)1233 void reset_character_alpha( const CHR_REF character )
1234 {
1235     /// @details ZZ@> This function fixes an item's transparency
1236 
1237     CHR_REF mount;
1238     chr_t * pchr, * pmount;
1239 
1240     // Make sure the character is valid
1241     if ( !INGAME_CHR( character ) ) return;
1242     pchr = ChrList.lst + character;
1243 
1244     // Make sure the character is mounted
1245     mount = ChrList.lst[character].attachedto;
1246     if ( !INGAME_CHR( mount ) ) return;
1247     pmount = ChrList.lst + mount;
1248 
1249     if ( pchr->isitem && pmount->transferblend )
1250     {
1251         ENC_REF ienc_now, ienc_nxt;
1252         size_t  ienc_count;
1253 
1254         // cleanup the enchant list
1255         cleanup_character_enchants( pchr );
1256 
1257         // Okay, reset transparency
1258         ienc_now = pchr->firstenchant;
1259         ienc_count = 0;
1260         while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1261         {
1262             ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1263 
1264             enchant_remove_set( ienc_now, SETALPHABLEND );
1265             enchant_remove_set( ienc_now, SETLIGHTBLEND );
1266 
1267             ienc_now = ienc_nxt;
1268             ienc_count++;
1269         }
1270         if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1271 
1272         chr_set_alpha( pchr, pchr->alpha_base );
1273         chr_set_light( pchr, pchr->light_base );
1274 
1275         // cleanup the enchant list
1276         cleanup_character_enchants( pchr );
1277 
1278         ienc_now = pchr->firstenchant;
1279         ienc_count = 0;
1280         while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1281         {
1282             PRO_REF ipro = enc_get_ipro( ienc_now );
1283 
1284             ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1285 
1286             if ( LOADED_PRO( ipro ) )
1287             {
1288                 enchant_apply_set( ienc_now, SETALPHABLEND, ipro );
1289                 enchant_apply_set( ienc_now, SETLIGHTBLEND, ipro );
1290             }
1291 
1292             ienc_now = ienc_nxt;
1293             ienc_count++;
1294         }
1295         if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1296     }
1297 }
1298 
1299 //--------------------------------------------------------------------------------------------
attach_character_to_mount(const CHR_REF irider,const CHR_REF imount,grip_offset_t grip_off)1300 egoboo_rv attach_character_to_mount( const CHR_REF irider, const CHR_REF imount, grip_offset_t grip_off )
1301 {
1302     /// @details ZZ@> This function attaches one character/item to another ( the holder/mount )
1303     ///    at a certain vertex offset ( grip_off )
1304     ///   - This function is called as a part of spawning a module, so the item or the holder may not
1305     ///     be fully instantiated
1306     ///   - This function should do very little testing to see if attachment is allowed.
1307     ///     Most of that checking should be done by the calling function
1308 
1309     slot_t slot;
1310 
1311     chr_t * prider, * pmount;
1312     cap_t *pmount_cap;
1313 
1314     // Make sure the character/item is valid
1315     if ( !DEFINED_CHR( irider ) ) return rv_error;
1316     prider = ChrList.lst + irider;
1317 
1318     // Make sure the holder/mount is valid
1319     if ( !DEFINED_CHR( imount ) ) return rv_error;
1320     pmount = ChrList.lst + imount;
1321 
1322     pmount_cap = chr_get_pcap( imount );
1323     if ( NULL == pmount_cap ) return rv_error;
1324 
1325     // do not deal with packed items at this time
1326     // this would have to be changed to allow for pickpocketing
1327     if ( prider->pack.is_packed || pmount->pack.is_packed ) return rv_fail;
1328 
1329     // make a reasonable time for the character to remount something
1330     // for characters jumping out of pots, etc
1331     if ( imount == prider->dismount_object && prider->dismount_timer > 0 ) return rv_fail;
1332 
1333     // Figure out which slot this grip_off relates to
1334     slot = grip_offset_to_slot( grip_off );
1335 
1336     // Make sure the the slot is valid
1337     if ( !pmount_cap->slotvalid[slot] ) return rv_fail;
1338 
1339     // This is a small fix that allows special grabbable mounts not to be mountable while
1340     // held by another character (such as the magic carpet for example)
1341     // ( this is an example of a test that should not be done here )
1342     if ( pmount->ismount && INGAME_CHR( pmount->attachedto ) ) return rv_fail;
1343 
1344     // Put 'em together
1345     prider->inwhich_slot       = slot;
1346     prider->attachedto         = imount;
1347     pmount->holdingwhich[slot] = irider;
1348 
1349     // set the grip vertices for the irider
1350     set_weapongrip( irider, imount, grip_off );
1351 
1352     chr_update_matrix( prider, btrue );
1353 
1354     chr_set_pos( prider, mat_getTranslate_v( prider->inst.matrix.v ) );
1355 
1356     prider->enviro.inwater  = bfalse;
1357     prider->jump_timer = JUMPDELAY * 4;
1358 
1359     // Run the held animation
1360     if ( pmount->ismount && ( GRIP_ONLY == grip_off ) )
1361     {
1362         // Riding imount
1363 
1364         if ( INGAME_CHR( prider->holdingwhich[SLOT_LEFT] ) || INGAME_CHR( prider->holdingwhich[SLOT_RIGHT] ) )
1365         {
1366             // if the character is holding anything, make the animation
1367             // ACTION_MH == "sitting" so that it dies not look so silly
1368             chr_play_action( prider, ACTION_MH, btrue );
1369         }
1370         else
1371         {
1372             // if it is not holding anything, go for the riding animation
1373             chr_play_action( prider, ACTION_MI, btrue );
1374         }
1375 
1376         // set tehis action to loop
1377         chr_instance_set_action_loop( &( prider->inst ), btrue );
1378     }
1379     else if ( prider->alive )
1380     {
1381         /// @note ZF@> hmm, here is the torch holding bug. Removing
1382         /// the interpolation seems to fix it...
1383         chr_play_action( prider, ACTION_MM + slot, bfalse );
1384 
1385         chr_instance_remove_interpolation( &( prider->inst ) );
1386 
1387         // set the action to keep for items
1388         if ( prider->isitem )
1389         {
1390             // Item grab
1391             chr_instance_set_action_keep( &( prider->inst ), btrue );
1392         }
1393     }
1394 
1395     // Set the team
1396     if ( prider->isitem )
1397     {
1398         prider->team = pmount->team;
1399 
1400         // Set the alert
1401         if ( prider->alive )
1402         {
1403             SET_BIT( prider->ai.alert, ALERTIF_GRABBED );
1404         }
1405     }
1406 
1407     if ( pmount->ismount )
1408     {
1409         pmount->team = prider->team;
1410 
1411         // Set the alert
1412         if ( !pmount->isitem && pmount->alive )
1413         {
1414             SET_BIT( pmount->ai.alert, ALERTIF_GRABBED );
1415         }
1416     }
1417 
1418     // It's not gonna hit the floor
1419     prider->hitready = bfalse;
1420 
1421     return rv_success;
1422 }
1423 
1424 //--------------------------------------------------------------------------------------------
inventory_add_item(const CHR_REF item,const CHR_REF character)1425 bool_t inventory_add_item( const CHR_REF item, const CHR_REF character )
1426 {
1427     chr_t * pchr, * pitem;
1428     cap_t * pitem_cap;
1429     bool_t  slot_found, pack_added;
1430     int     slot_count;
1431     int     cnt;
1432 
1433     if ( !INGAME_CHR( item ) ) return bfalse;
1434     pitem = ChrList.lst + item;
1435 
1436     // don't allow sub-inventories
1437     if ( pitem->pack.is_packed || pitem->isequipped ) return bfalse;
1438 
1439     pitem_cap = pro_get_pcap( pitem->profile_ref );
1440     if ( NULL == pitem_cap ) return bfalse;
1441 
1442     if ( !INGAME_CHR( character ) ) return bfalse;
1443     pchr = ChrList.lst + character;
1444 
1445     // don't allow sub-inventories
1446     if ( pchr->pack.is_packed || pchr->isequipped ) return bfalse;
1447 
1448     slot_found = bfalse;
1449     slot_count = 0;
1450     for ( cnt = 0; cnt < INVEN_COUNT; cnt++ )
1451     {
1452         if ( IDSZ_NONE == inventory_idsz[cnt] ) continue;
1453 
1454         if ( inventory_idsz[cnt] == pitem_cap->idsz[IDSZ_PARENT] )
1455         {
1456             slot_count = cnt;
1457             slot_found = btrue;
1458         }
1459     }
1460 
1461     if ( slot_found )
1462     {
1463         if ( INGAME_CHR( pchr->holdingwhich[slot_count] ) )
1464         {
1465             pchr->inventory[slot_count] = ( CHR_REF )MAX_CHR;
1466         }
1467     }
1468 
1469     pack_added = chr_add_pack_item( item, character );
1470 
1471     if ( slot_found && pack_added )
1472     {
1473         pchr->inventory[slot_count] = item;
1474     }
1475 
1476     return pack_added;
1477 }
1478 
1479 //--------------------------------------------------------------------------------------------
inventory_get_item(const CHR_REF ichr,grip_offset_t grip_off,bool_t ignorekurse)1480 CHR_REF inventory_get_item( const CHR_REF ichr, grip_offset_t grip_off, bool_t ignorekurse )
1481 {
1482     chr_t * pchr;
1483     CHR_REF iitem;
1484     int     cnt;
1485 
1486     if ( !INGAME_CHR( ichr ) ) return ( CHR_REF )MAX_CHR;
1487     pchr = ChrList.lst + ichr;
1488 
1489     if ( pchr->pack.is_packed || pchr->isitem || MAX_CHR == pchr->pack.next )
1490         return ( CHR_REF )MAX_CHR;
1491 
1492     if ( 0 == pchr->pack.count ) return ( CHR_REF )MAX_CHR;
1493 
1494     iitem = chr_get_pack_item( ichr, grip_off, ignorekurse );
1495 
1496     // remove it from the "equipped" slots
1497     if ( INGAME_CHR( iitem ) )
1498     {
1499         for ( cnt = 0; cnt < INVEN_COUNT; cnt++ )
1500         {
1501             if ( pchr->inventory[cnt] == iitem )
1502             {
1503                 pchr->inventory[cnt] = iitem;
1504                 ChrList.lst[iitem].isequipped = bfalse;
1505                 break;
1506             }
1507         }
1508     }
1509 
1510     return iitem;
1511 }
1512 
1513 //--------------------------------------------------------------------------------------------
pack_add_item(pack_t * ppack,CHR_REF item)1514 bool_t pack_add_item( pack_t * ppack, CHR_REF item )
1515 {
1516     CHR_REF oldfirstitem;
1517     chr_t  * pitem;
1518     cap_t  * pitem_cap;
1519     pack_t * pitem_pack;
1520 
1521     if ( NULL == ppack || !INGAME_CHR( item ) ) return bfalse;
1522 
1523     if ( !INGAME_CHR( item ) ) return bfalse;
1524     pitem      = ChrList.lst + item;
1525     pitem_pack = &( pitem->pack );
1526     pitem_cap  = chr_get_pcap( item );
1527 
1528     oldfirstitem     = ppack->next;
1529     ppack->next      = item;
1530     pitem_pack->next = oldfirstitem;
1531     ppack->count++;
1532 
1533     return btrue;
1534 }
1535 
1536 //--------------------------------------------------------------------------------------------
pack_remove_item(pack_t * ppack,CHR_REF iparent,CHR_REF iitem)1537 bool_t pack_remove_item( pack_t * ppack, CHR_REF iparent, CHR_REF iitem )
1538 {
1539     CHR_REF old_next;
1540     chr_t * pitem, * pparent;
1541 
1542     // convert the iitem it to a pointer
1543     old_next = ( CHR_REF )MAX_CHR;
1544     pitem    = NULL;
1545     if ( DEFINED_CHR( iitem ) )
1546     {
1547         pitem    = ChrList.lst + iitem;
1548         old_next = pitem->pack.next;
1549     }
1550 
1551     // convert the pparent it to a pointer
1552     pparent = NULL;
1553     if ( DEFINED_CHR( iparent ) )
1554     {
1555         pparent = ChrList.lst + iparent;
1556     }
1557 
1558     // Remove the iitem from the pack
1559     if ( NULL != pitem )
1560     {
1561         pitem->pack.was_packed = pitem->pack.is_packed;
1562         pitem->pack.is_packed  = bfalse;
1563     }
1564 
1565     // adjust the iparent's next
1566     if ( NULL != pparent )
1567     {
1568         pparent->pack.next = old_next;
1569     }
1570 
1571     if ( NULL != ppack )
1572     {
1573         ppack->count--;
1574     }
1575 
1576     return btrue;
1577 }
1578 
1579 //--------------------------------------------------------------------------------------------
chr_pack_has_a_stack(const CHR_REF item,const CHR_REF character)1580 CHR_REF chr_pack_has_a_stack( const CHR_REF item, const CHR_REF character )
1581 {
1582     /// @details ZZ@> This function looks in the character's pack for an item similar
1583     ///    to the one given.  If it finds one, it returns the similar item's
1584     ///    index number, otherwise it returns MAX_CHR.
1585 
1586     CHR_REF istack;
1587     Uint16  id;
1588     bool_t  found;
1589 
1590     chr_t * pitem;
1591     cap_t * pitem_cap;
1592 
1593     found  = bfalse;
1594     istack = ( CHR_REF )MAX_CHR;
1595 
1596     if ( !INGAME_CHR( item ) ) return istack;
1597     pitem = ChrList.lst + item;
1598     pitem_cap = chr_get_pcap( item );
1599 
1600     if ( pitem_cap->isstackable )
1601     {
1602         PACK_BEGIN_LOOP( ipacked, ChrList.lst[character].pack.next )
1603         {
1604             if ( INGAME_CHR( ipacked ) )
1605             {
1606                 chr_t * pstack     = ChrList.lst + ipacked;
1607                 cap_t * pstack_cap = chr_get_pcap( ipacked );
1608 
1609                 found = pstack_cap->isstackable;
1610 
1611                 if ( pstack->ammo >= pstack->ammomax )
1612                 {
1613                     found = bfalse;
1614                 }
1615 
1616                 // you can still stack something even if the profiles don't match exactly,
1617                 // but they have to have all the same IDSZ properties
1618                 if ( found && ( pstack->profile_ref != pitem->profile_ref ) )
1619                 {
1620                     for ( id = 0; id < IDSZ_COUNT && found; id++ )
1621                     {
1622                         if ( chr_get_idsz( ipacked, id ) != chr_get_idsz( item, id ) )
1623                         {
1624                             found = bfalse;
1625                         }
1626                     }
1627                 }
1628             }
1629 
1630             if ( found )
1631             {
1632                 istack = ipacked;
1633                 break;
1634             }
1635         }
1636         PACK_END_LOOP( ipacked );
1637     }
1638 
1639     return istack;
1640 }
1641 
1642 //--------------------------------------------------------------------------------------------
chr_add_pack_item(const CHR_REF item,const CHR_REF character)1643 bool_t chr_add_pack_item( const CHR_REF item, const CHR_REF character )
1644 {
1645     /// @details ZZ@> This function puts one character inside the other's pack
1646 
1647     CHR_REF stack;
1648     int     newammo;
1649 
1650     chr_t  * pchr, * pitem;
1651     cap_t  * pchr_cap,  * pitem_cap;
1652     pack_t * pchr_pack, * pitem_pack;
1653 
1654     if ( !INGAME_CHR( character ) ) return bfalse;
1655     pchr      = ChrList.lst + character;
1656     pchr_pack = &( pchr->pack );
1657     pchr_cap  = chr_get_pcap( character );
1658 
1659     if ( !INGAME_CHR( item ) ) return bfalse;
1660     pitem      = ChrList.lst + item;
1661     pitem_pack = &( pitem->pack );
1662     pitem_cap  = chr_get_pcap( item );
1663 
1664     // Make sure everything is hunkydori
1665     if ( pitem_pack->is_packed || pchr_pack->is_packed || pchr->isitem )
1666         return bfalse;
1667 
1668     stack = chr_pack_has_a_stack( item, character );
1669     if ( INGAME_CHR( stack ) )
1670     {
1671         // We found a similar, stackable item in the pack
1672 
1673         chr_t  * pstack      = ChrList.lst + stack;
1674         cap_t  * pstack_cap  = chr_get_pcap( stack );
1675 
1676         // reveal the name of the item or the stack
1677         if ( pitem->nameknown || pstack->nameknown )
1678         {
1679             pitem->nameknown  = btrue;
1680             pstack->nameknown = btrue;
1681         }
1682 
1683         // reveal the usage of the item or the stack
1684         if ( pitem_cap->usageknown || pstack_cap->usageknown )
1685         {
1686             pitem_cap->usageknown  = btrue;
1687             pstack_cap->usageknown = btrue;
1688         }
1689 
1690         // add the item ammo to the stack
1691         newammo = pitem->ammo + pstack->ammo;
1692         if ( newammo <= pstack->ammomax )
1693         {
1694             // All transfered, so kill the in hand item
1695             pstack->ammo = newammo;
1696             if ( INGAME_CHR( pitem->attachedto ) )
1697             {
1698                 detach_character_from_mount( item, btrue, bfalse );
1699             }
1700 
1701             chr_request_terminate( item );
1702         }
1703         else
1704         {
1705             // Only some were transfered,
1706             pitem->ammo     = pitem->ammo + pstack->ammo - pstack->ammomax;
1707             pstack->ammo    = pstack->ammomax;
1708             SET_BIT( pchr->ai.alert, ALERTIF_TOOMUCHBAGGAGE );
1709         }
1710     }
1711     else
1712     {
1713         // Make sure we have room for another item
1714         if ( pchr_pack->count >= MAXNUMINPACK )
1715         {
1716             SET_BIT( pchr->ai.alert, ALERTIF_TOOMUCHBAGGAGE );
1717             return bfalse;
1718         }
1719 
1720         // Take the item out of hand
1721         if ( INGAME_CHR( pitem->attachedto ) )
1722         {
1723             detach_character_from_mount( item, btrue, bfalse );
1724 
1725             // clear the dropped flag
1726             UNSET_BIT( pitem->ai.alert, ALERTIF_DROPPED );
1727         }
1728 
1729         // Remove the item from play
1730         pitem->hitready        = bfalse;
1731         pitem_pack->was_packed = pitem_pack->is_packed;
1732         pitem_pack->is_packed  = btrue;
1733 
1734         // Insert the item into the pack as the first one
1735         pack_add_item( pchr_pack, item );
1736 
1737         // fix the flags
1738         if ( pitem_cap->isequipment )
1739         {
1740             SET_BIT( pitem->ai.alert, ALERTIF_PUTAWAY );  // same as ALERTIF_ATLASTWAYPOINT;
1741         }
1742     }
1743 
1744     return btrue;
1745 }
1746 
1747 //--------------------------------------------------------------------------------------------
chr_remove_pack_item(CHR_REF ichr,CHR_REF iparent,CHR_REF iitem)1748 bool_t chr_remove_pack_item( CHR_REF ichr, CHR_REF iparent, CHR_REF iitem )
1749 {
1750     chr_t  * pchr;
1751     pack_t * pchr_pack;
1752 
1753     bool_t removed;
1754 
1755     if ( !DEFINED_CHR( ichr ) ) return bfalse;
1756     pchr = ChrList.lst + ichr;
1757     pchr_pack = &( pchr->pack );
1758 
1759     // remove it from the pack
1760     removed = pack_remove_item( pchr_pack, iparent, iitem );
1761 
1762     // unequip the item
1763     if ( removed && DEFINED_CHR( iitem ) )
1764     {
1765         ChrList.lst[iitem].isequipped = bfalse;
1766         ChrList.lst[iitem].team       = chr_get_iteam( ichr );
1767     }
1768 
1769     return removed;
1770 }
1771 
1772 //--------------------------------------------------------------------------------------------
chr_get_pack_item(const CHR_REF character,grip_offset_t grip_off,bool_t ignorekurse)1773 CHR_REF chr_get_pack_item( const CHR_REF character, grip_offset_t grip_off, bool_t ignorekurse )
1774 {
1775     /// @details ZZ@> This function takes the last item in the character's pack and puts
1776     ///    it into the designated hand.  It returns the item number or MAX_CHR.
1777 
1778     CHR_REF found_item, found_item_parent;
1779 
1780     chr_t  * pchr, * pfound_item, *pfound_item_parent;
1781     pack_t * pchr_pack, * pfound_item_pack, *pfound_item_parent_pack;
1782 
1783     // does the character exist?
1784     if ( !DEFINED_CHR( character ) ) return bfalse;
1785     pchr      = ChrList.lst + character;
1786     pchr_pack = &( pchr->pack );
1787 
1788     // Can the character have a pack?
1789     if ( pchr_pack->is_packed || pchr->isitem ) return ( CHR_REF )MAX_CHR;
1790 
1791     // is the pack empty?
1792     if ( MAX_CHR == pchr_pack->next || 0 == pchr_pack->count ) return ( CHR_REF )MAX_CHR;
1793 
1794     // Find the last item in the pack
1795     found_item_parent = character;
1796     found_item        = character;
1797     PACK_BEGIN_LOOP( ipacked, pchr_pack->next )
1798     {
1799         found_item_parent = found_item;
1800         found_item        = ipacked;
1801     }
1802     PACK_END_LOOP( ipacked );
1803 
1804     // did we find anything?
1805     if ( character == found_item || MAX_CHR == found_item ) return bfalse;
1806 
1807     // convert the found_item it to a pointer
1808     pfound_item      = NULL;
1809     pfound_item_pack = NULL;
1810     if ( DEFINED_CHR( found_item ) )
1811     {
1812         pfound_item = ChrList.lst + found_item;
1813         pfound_item_pack = &( pfound_item->pack );
1814     }
1815 
1816     // convert the pfound_item_parent it to a pointer
1817     pfound_item_parent      = NULL;
1818     pfound_item_parent_pack = NULL;
1819     if ( DEFINED_CHR( found_item_parent ) )
1820     {
1821         pfound_item_parent      = ChrList.lst + found_item_parent;
1822         pfound_item_parent_pack = &( pfound_item_parent->pack );
1823     }
1824 
1825     // did we find a valid object?
1826     if ( !INGAME_CHR( found_item ) )
1827     {
1828         chr_remove_pack_item( character, found_item_parent, found_item );
1829 
1830         return bfalse;
1831     }
1832 
1833     // Figure out what to do with it
1834     if ( pfound_item->iskursed && pfound_item->isequipped && !ignorekurse )
1835     {
1836         // Flag the last found_item as not removed
1837         SET_BIT( pfound_item->ai.alert, ALERTIF_NOTTAKENOUT );  // Same as ALERTIF_NOTPUTAWAY
1838 
1839         // Cycle it to the front
1840         pfound_item_pack->next        = pchr_pack->next;
1841         pfound_item_parent_pack->next = ( CHR_REF )MAX_CHR;
1842         pchr_pack->next               = found_item;
1843 
1844         if ( character == found_item_parent )
1845         {
1846             pfound_item_pack->next = ( CHR_REF )MAX_CHR;
1847         }
1848 
1849         found_item = ( CHR_REF )MAX_CHR;
1850     }
1851     else
1852     {
1853         // Remove the last found_item from the pack
1854         chr_remove_pack_item( character, found_item_parent, found_item );
1855 
1856         // Attach the found_item to the character's hand
1857         // (1) if the mounting doesn't succees, I guess it will drop it at the character's feet,
1858         // or will it drop the item at 0,0... I think the keep_weapons_with_holders() function
1859         // works
1860         // (2) if it fails, do we need to treat it as if it was dropped? Or just not take it out?
1861         attach_character_to_mount( found_item, character, grip_off );
1862 
1863         // fix the flags
1864         UNSET_BIT( pfound_item->ai.alert, ALERTIF_GRABBED );
1865         SET_BIT( pfound_item->ai.alert, ALERTIF_TAKENOUT );
1866     }
1867 
1868     if ( MAX_CHR == pchr_pack->next )
1869     {
1870         pchr_pack->count = 0;
1871     }
1872 
1873     return found_item;
1874 }
1875 
1876 //--------------------------------------------------------------------------------------------
drop_keys(const CHR_REF character)1877 void drop_keys( const CHR_REF character )
1878 {
1879     /// @details ZZ@> This function drops all keys ( [KEYA] to [KEYZ] ) that are in a character's
1880     ///    inventory ( Not hands ).
1881 
1882     chr_t  * pchr;
1883 
1884     FACING_T direction;
1885     IDSZ     testa, testz;
1886 
1887     CHR_REF   key_lst[MAXNUMINPACK];
1888     CHR_REF * key_parent[MAXNUMINPACK];
1889     size_t    key_count;
1890 
1891     size_t    cnt;
1892     CHR_REF * pparent;
1893 
1894     if ( !INGAME_CHR( character ) ) return;
1895     pchr = ChrList.lst + character;
1896 
1897     // Don't lose keys in pits...
1898     if ( pchr->pos.z <= ( PITDEPTH >> 1 ) ) return;
1899 
1900     // The IDSZs to find
1901     testa = MAKE_IDSZ( 'K', 'E', 'Y', 'A' );  // [KEYA]
1902     testz = MAKE_IDSZ( 'K', 'E', 'Y', 'Z' );  // [KEYZ]
1903 
1904     key_count = 0;
1905     pparent = &( pchr->pack.next );
1906     PACK_BEGIN_LOOP( ipacked, pchr->pack.next )
1907     {
1908         if ( INGAME_CHR( ipacked ) && ipacked != character )  // Should never happen...
1909         {
1910             IDSZ idsz_parent;
1911             IDSZ idsz_type;
1912 
1913             chr_t * pitem = ChrList.lst + ipacked;
1914 
1915             idsz_parent = chr_get_idsz( ipacked, IDSZ_PARENT );
1916             idsz_type   = chr_get_idsz( ipacked, IDSZ_TYPE );
1917 
1918             if (( idsz_parent >= testa && idsz_parent <= testz ) ||
1919                 ( idsz_type >= testa && idsz_type <= testz ) )
1920             {
1921                 key_lst[key_count]    = ipacked;
1922                 key_parent[key_count] = pparent;
1923                 key_count++;
1924             }
1925             else
1926             {
1927                 // only save non-keys as parents
1928                 pparent = &( pitem->pack.next );
1929             }
1930         }
1931     }
1932     PACK_END_LOOP( ipacked );
1933 
1934     // We found some keys?
1935     // since we are MODIFYING the pack list, do not change the list
1936     // while inside the PACK_BEGIN_LOOP() ... PACK_END_LOOP()
1937     for ( cnt = 0; cnt < key_count; cnt++ )
1938     {
1939         CHR_REF ikey     = key_lst[cnt];
1940         CHR_REF *pparent = key_parent[cnt];
1941 
1942         chr_t * pkey = ChrList.lst + ikey;
1943 
1944         TURN_T turn;
1945 
1946         direction = RANDIE;
1947         turn      = TO_TURN( direction );
1948 
1949         // unpack the ikey
1950         *pparent = pkey->pack.next;
1951         pkey->pack.next = ( CHR_REF )MAX_CHR;
1952         pchr->pack.count--;
1953 
1954         // fix the attachments
1955         pkey->attachedto             = ( CHR_REF )MAX_CHR;
1956         pkey->dismount_timer         = PHYS_DISMOUNT_TIME;
1957         pkey->dismount_object        = GET_REF_PCHR( pchr );
1958         pkey->onwhichplatform_ref    = pchr->onwhichplatform_ref;
1959         pkey->onwhichplatform_update = pchr->onwhichplatform_update;
1960 
1961         // fix some flags
1962         pkey->hitready               = btrue;
1963         pkey->pack.was_packed        = pkey->pack.is_packed;
1964         pkey->pack.is_packed         = bfalse;
1965         pkey->isequipped             = bfalse;
1966         pkey->ori.facing_z           = direction + ATK_BEHIND;
1967         pkey->team                   = pkey->baseteam;
1968 
1969         // fix the current velocity
1970         pkey->vel.x                  += turntocos[ turn ] * DROPXYVEL;
1971         pkey->vel.y                  += turntosin[ turn ] * DROPXYVEL;
1972         pkey->vel.z                  += DROPZVEL;
1973 
1974         // do some more complicated things
1975         SET_BIT( pkey->ai.alert, ALERTIF_DROPPED );
1976         chr_set_pos( pkey, chr_get_pos_v( pchr ) );
1977         move_one_character_get_environment( pkey );
1978         chr_set_floor_level( pkey, pchr->enviro.floor_level );
1979     }
1980 }
1981 
1982 //--------------------------------------------------------------------------------------------
drop_all_items(const CHR_REF character)1983 bool_t drop_all_items( const CHR_REF character )
1984 {
1985     /// @details ZZ@> This function drops all of a character's items
1986 
1987     CHR_REF  item;
1988     FACING_T direction;
1989     Sint16   diradd;
1990     chr_t  * pchr;
1991 
1992     if ( !INGAME_CHR( character ) ) return bfalse;
1993     pchr = ChrList.lst + character;
1994 
1995     detach_character_from_mount( pchr->holdingwhich[SLOT_LEFT], btrue, bfalse );
1996     detach_character_from_mount( pchr->holdingwhich[SLOT_RIGHT], btrue, bfalse );
1997     if ( pchr->pack.count > 0 )
1998     {
1999         direction = pchr->ori.facing_z + ATK_BEHIND;
2000         diradd    = 0x00010000 / pchr->pack.count;
2001 
2002         while ( pchr->pack.count > 0 )
2003         {
2004             item = inventory_get_item( character, GRIP_LEFT, bfalse );
2005 
2006             if ( INGAME_CHR( item ) )
2007             {
2008                 chr_t * pitem = ChrList.lst + item;
2009 
2010                 // detach the item
2011                 detach_character_from_mount( item, btrue, btrue );
2012 
2013                 // fix the attachments
2014                 pitem->dismount_timer         = PHYS_DISMOUNT_TIME;
2015                 pitem->dismount_object        = GET_REF_PCHR( pchr );
2016                 pitem->onwhichplatform_ref    = pchr->onwhichplatform_ref;
2017                 pitem->onwhichplatform_update = pchr->onwhichplatform_update;
2018 
2019                 // fix some flags
2020                 pitem->hitready               = btrue;
2021                 pitem->ori.facing_z           = direction + ATK_BEHIND;
2022                 pitem->team                   = pitem->baseteam;
2023 
2024                 // fix the current velocity
2025                 pitem->vel.x                  += turntocos[( direction>>2 ) & TRIG_TABLE_MASK ] * DROPXYVEL;
2026                 pitem->vel.y                  += turntosin[( direction>>2 ) & TRIG_TABLE_MASK ] * DROPXYVEL;
2027                 pitem->vel.z                  += DROPZVEL;
2028 
2029                 // do some more complicated things
2030                 SET_BIT( pitem->ai.alert, ALERTIF_DROPPED );
2031                 chr_set_pos( pitem, chr_get_pos_v( pchr ) );
2032                 move_one_character_get_environment( pitem );
2033                 chr_set_floor_level( pitem, pchr->enviro.floor_level );
2034             }
2035 
2036             direction += diradd;
2037         }
2038     }
2039 
2040     return btrue;
2041 }
2042 
2043 //--------------------------------------------------------------------------------------------
2044 //--------------------------------------------------------------------------------------------
2045 struct s_grab_data
2046 {
2047     CHR_REF ichr;
2048     fvec3_t diff;
2049     float   diff2_hrz;
2050     float   diff2_vrt;
2051     bool_t  too_dark, too_invis;
2052 };
2053 typedef struct s_grab_data grab_data_t;
2054 
2055 //--------------------------------------------------------------------------------------------
grab_data_cmp(const void * pleft,const void * pright)2056 int grab_data_cmp( const void * pleft, const void * pright )
2057 {
2058     int rv;
2059     float diff;
2060 
2061     grab_data_t * dleft  = ( grab_data_t * )pleft;
2062     grab_data_t * dright = ( grab_data_t * )pright;
2063 
2064     // use only the horizontal distance
2065     diff = dleft->diff2_hrz - dright->diff2_hrz;
2066 
2067     // unless they are equal, then use the vertical distance
2068     if ( 0.0f == diff )
2069     {
2070         diff = dleft->diff2_vrt - dright->diff2_vrt;
2071     }
2072 
2073     if ( diff < 0.0f )
2074     {
2075         rv = -1;
2076     }
2077     else if ( diff > 0.0f )
2078     {
2079         rv = 1;
2080     }
2081     else
2082     {
2083         rv = 0;
2084     }
2085 
2086     return rv;
2087 }
2088 
2089 //--------------------------------------------------------------------------------------------
character_grab_stuff(const CHR_REF ichr_a,grip_offset_t grip_off,bool_t grab_people)2090 bool_t character_grab_stuff( const CHR_REF ichr_a, grip_offset_t grip_off, bool_t grab_people )
2091 {
2092     /// @details ZZ@> This function makes the character pick up an item if there's one around
2093 
2094     const SDL_Color color_red = {0xFF, 0x7F, 0x7F, 0xFF};
2095     const SDL_Color color_grn = {0x7F, 0xFF, 0x7F, 0xFF};
2096     const SDL_Color color_blu = {0x7F, 0x7F, 0xFF, 0xFF};
2097     const GLXvector4f default_tint = { 1.00f, 1.00f, 1.00f, 1.00f };
2098 
2099     // 1 a grid is fine. anything more than that and it gets crazy
2100     const float const_info2_hrz = SQR( 3.0f * GRID_FSIZE );
2101     const float const_grab2_hrz = SQR( 1.0f * GRID_FSIZE );
2102     const float const_grab2_vrt = SQR( GRABSIZE );
2103 
2104     int       cnt;
2105     CHR_REF   ichr_b;
2106     slot_t    slot;
2107     oct_vec_t mids;
2108     fvec3_t   slot_pos;
2109     float     bump_size2_a;
2110 
2111     chr_t * pchr_a;
2112     cap_t * pcap_a;
2113 
2114     bool_t retval;
2115 
2116     // valid objects that can be grabbed
2117     size_t      grab_count = 0;
2118     size_t      grab_visible_count = 0;
2119     grab_data_t grab_list[MAX_CHR];
2120 
2121     // valid objects that cannot be grabbed
2122     size_t      ungrab_count = 0;
2123     size_t      ungrab_visible_count = 0;
2124     grab_data_t ungrab_list[MAX_CHR];
2125 
2126     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
2127     pchr_a = ChrList.lst + ichr_a;
2128 
2129     pcap_a = pro_get_pcap( pchr_a->profile_ref );
2130     if ( NULL == pcap_a ) return bfalse;
2131 
2132     // find the slot from the grip
2133     slot = grip_offset_to_slot( grip_off );
2134     if ( slot < 0 || slot >= SLOT_COUNT ) return bfalse;
2135 
2136     // Make sure the character doesn't have something already, and that it has hands
2137     if ( INGAME_CHR( pchr_a->holdingwhich[slot] ) || !pcap_a->slotvalid[slot] )
2138         return bfalse;
2139 
2140     //Determine the position of the grip
2141     oct_bb_get_mids( pchr_a->slot_cv + slot, mids );
2142     slot_pos.x = mids[OCT_X];
2143     slot_pos.y = mids[OCT_Y];
2144     slot_pos.z = mids[OCT_Z];
2145     fvec3_self_sum( slot_pos.v, chr_get_pos_v( pchr_a ) );
2146 
2147     // get the size of object a
2148     bump_size2_a = SQR( 1.5f * pchr_a->bump.size );
2149 
2150     // Go through all characters to find the best match
2151     CHR_BEGIN_LOOP_ACTIVE( ichr_b, pchr_b )
2152     {
2153         fvec3_t   diff;
2154         float     bump_size2_b;
2155         float     diff2_hrz, diff2_vrt;
2156         float     grab2_vrt, grab2_hrz, info2_hrz;
2157         bool_t    can_grab = btrue;
2158         bool_t    too_dark = btrue;
2159         bool_t    too_invis = btrue;
2160 
2161         // do nothing to yourself
2162         if ( ichr_a == ichr_b ) continue;
2163 
2164         // Dont do hidden objects
2165         if ( pchr_b->is_hidden ) continue;
2166 
2167         // pickpocket not allowed yet
2168         if ( pchr_b->pack.is_packed ) continue;
2169 
2170         // disarm not allowed yet
2171         if ( MAX_CHR != pchr_b->attachedto ) continue;
2172 
2173         // do not pick up your mount
2174         if ( pchr_b->holdingwhich[SLOT_LEFT] == ichr_a ||
2175              pchr_b->holdingwhich[SLOT_RIGHT] == ichr_a ) continue;
2176 
2177         // do not notice completely broken items?
2178         if ( pchr_b->isitem && !pchr_b->alive ) continue;
2179 
2180         // reasonable carrying capacity
2181         if ( pchr_b->phys.weight > pchr_a->phys.weight + pchr_a->strength * INV_FF )
2182         {
2183             can_grab = bfalse;
2184         }
2185 
2186         // grab_people == btrue allows you to pick up living non-items
2187         // grab_people == false allows you to pick up living (functioning) items
2188         if ( !grab_people && !pchr_b->isitem )
2189         {
2190             can_grab = bfalse;
2191         }
2192 
2193         // is the object visible
2194         too_dark  = !chr_can_see_dark( pchr_a, pchr_b );
2195         too_invis = !chr_can_see_invis( pchr_a, pchr_b );
2196 
2197         // calculate the distance
2198         diff = fvec3_sub( chr_get_pos_v( pchr_b ), slot_pos.v );
2199         diff.z += pchr_b->bump.height * 0.5f;
2200 
2201         // find the squared difference horizontal and vertical
2202         diff2_hrz = fvec2_length_2( diff.v );
2203         diff2_vrt = diff.z * diff.z;
2204 
2205         // determine the actual max vertical distance
2206         grab2_vrt = SQR( pchr_b->bump.height );
2207         grab2_vrt = MAX( grab2_vrt, const_grab2_vrt );
2208 
2209         // the normal horizontal grab distance is dependent on the size of the two objects
2210         bump_size2_b = SQR( pchr_b->bump.size );
2211 
2212         // visibility affects the max grab distance.
2213         // if it is not visible then we have to be touching it.
2214         grab2_hrz = MAX( bump_size2_a, bump_size2_b );
2215         if ( !too_dark && !too_invis )
2216         {
2217             grab2_hrz = MAX( grab2_hrz, const_grab2_hrz );
2218         }
2219 
2220         // the player can get info from objects that are farther away
2221         info2_hrz = MAX( grab2_hrz, const_info2_hrz );
2222 
2223         // Is it too far away to interact with?
2224         if ( diff2_hrz > info2_hrz || diff2_vrt > grab2_vrt ) continue;
2225 
2226         // is it too far away to grab?
2227         if ( diff2_hrz > grab2_hrz )
2228         {
2229             can_grab = bfalse;
2230         }
2231 
2232         // count the number of objects that are within the max range
2233         // a difference between the *_total_count and the *_count
2234         // indicates that some objects were not detectable
2235         if ( !too_invis )
2236         {
2237             if ( can_grab )
2238             {
2239                 grab_visible_count++;
2240             }
2241             else
2242             {
2243                 ungrab_visible_count++;
2244             }
2245         }
2246 
2247         if ( can_grab )
2248         {
2249             grab_list[grab_count].ichr      = ichr_b;
2250             grab_list[grab_count].diff      = diff;
2251             grab_list[grab_count].diff2_hrz = diff2_hrz;
2252             grab_list[grab_count].diff2_vrt = diff2_vrt;
2253             grab_list[grab_count].too_dark  = too_dark;
2254             grab_list[grab_count].too_invis = too_invis;
2255             grab_count++;
2256         }
2257         else
2258         {
2259             ungrab_list[ungrab_count].ichr      = ichr_b;
2260             ungrab_list[ungrab_count].diff      = diff;
2261             ungrab_list[ungrab_count].diff2_hrz = diff2_hrz;
2262             ungrab_list[ungrab_count].diff2_vrt = diff2_vrt;
2263             ungrab_list[ungrab_count].too_dark  = too_dark;
2264             ungrab_list[ungrab_count].too_invis = too_invis;
2265             ungrab_count++;
2266         }
2267     }
2268     CHR_END_LOOP();
2269 
2270     // sort the grab list
2271     if ( grab_count > 1 )
2272     {
2273         qsort( grab_list, grab_count, sizeof( grab_data_t ), grab_data_cmp );
2274     }
2275 
2276     // try to grab something
2277     retval = bfalse;
2278     if (( 0 == grab_count ) && ( 0 != grab_visible_count ) )
2279     {
2280         // There are items within the "normal" range that could be grabbed
2281         // but somehow they can't be seen.
2282         // Generate a billboard that tells the player what the problem is.
2283         // NOTE: this is not corerect since it could alert a player to an invisible object
2284 
2285         // 5 seconds and blue
2286         chr_make_text_billboard( ichr_a, "I can't feel anything...", color_blu, default_tint, 5, bb_opt_none );
2287 
2288         retval = btrue;
2289     }
2290 
2291     if ( !retval )
2292     {
2293         for ( cnt = 0; cnt < grab_count; cnt++ )
2294         {
2295             bool_t can_grab;
2296 
2297             chr_t * pchr_b;
2298 
2299             if ( grab_list[cnt].too_dark || grab_list[cnt].too_invis ) continue;
2300 
2301             ichr_b = grab_list[cnt].ichr;
2302             pchr_b = ChrList.lst + ichr_b;
2303 
2304             can_grab = can_grab_item_in_shop( ichr_a, ichr_b );
2305 
2306             if ( can_grab )
2307             {
2308                 // Stick 'em together and quit
2309                 if ( rv_success == attach_character_to_mount( ichr_b, ichr_a, grip_off ) )
2310                 {
2311                     if ( grab_people )
2312                     {
2313                         // Start the slam animation...  ( Be sure to drop!!! )
2314                         chr_play_action( pchr_a, ACTION_MC + slot, bfalse );
2315                     }
2316                     retval = btrue;
2317                 }
2318                 break;
2319             }
2320             else
2321             {
2322                 // Lift the item a little and quit...
2323                 pchr_b->vel.z = DROPZVEL;
2324                 pchr_b->hitready = btrue;
2325                 SET_BIT( pchr_b->ai.alert, ALERTIF_DROPPED );
2326                 break;
2327             }
2328         }
2329     }
2330 
2331     if ( !retval )
2332     {
2333         fvec3_t   vforward;
2334 
2335         //---- generate billboards for things that players can interact with
2336         if ( FEEDBACK_OFF != cfg.feedback && VALID_PLA( pchr_a->is_which_player ) )
2337         {
2338             // things that can be grabbed
2339             for ( cnt = 0; cnt < grab_count; cnt++ )
2340             {
2341                 ichr_b = grab_list[cnt].ichr;
2342                 if ( grab_list[cnt].too_dark || grab_list[cnt].too_invis )
2343                 {
2344                     // (5 secs and blue)
2345                     chr_make_text_billboard( ichr_b, "Something...", color_blu, default_tint, 5, bb_opt_none );
2346                 }
2347                 else
2348                 {
2349                     // (5 secs and green)
2350                     chr_make_text_billboard( ichr_b, chr_get_name( ichr_b, CHRNAME_ARTICLE | CHRNAME_CAPITAL ), color_grn, default_tint, 5, bb_opt_none );
2351                 }
2352             }
2353 
2354             // things that can't be grabbed
2355             for ( cnt = 0; cnt < ungrab_count; cnt++ )
2356             {
2357                 ichr_b = ungrab_list[cnt].ichr;
2358 
2359                 if ( ungrab_list[cnt].too_dark || ungrab_list[cnt].too_invis )
2360                 {
2361                     // (5 secs and blue)
2362                     chr_make_text_billboard( ichr_b, "Something...", color_blu, default_tint, 5, bb_opt_none );
2363                 }
2364                 else
2365                 {
2366                     // (5 secs and red)
2367                     chr_make_text_billboard( ichr_b, chr_get_name( ichr_b, CHRNAME_ARTICLE | CHRNAME_CAPITAL ), color_red, default_tint, 5, bb_opt_none );
2368                 }
2369             }
2370         }
2371 
2372         //---- if you can't grab anything, activate something using ALERTIF_BUMPED
2373         if ( VALID_PLA( pchr_a->is_which_player ) && ungrab_count > 0 )
2374         {
2375             chr_getMatForward( pchr_a, vforward.v );
2376 
2377             // sort the ungrab list
2378             if ( ungrab_count > 1 )
2379             {
2380                 qsort( ungrab_list, ungrab_count, sizeof( grab_data_t ), grab_data_cmp );
2381             }
2382 
2383             for ( cnt = 0; cnt < ungrab_count; cnt++ )
2384             {
2385                 float       ftmp;
2386                 chr_t     * pchr_b;
2387 
2388                 // only do visible objects
2389                 if ( ungrab_list[cnt].too_dark || ungrab_list[cnt].too_invis ) continue;
2390 
2391                 pchr_b = ChrList.lst + ungrab_list[cnt].ichr;
2392 
2393                 // only bump the closest character that is in front of the character
2394                 // (ignore vertical displacement)
2395                 ftmp = fvec2_dot_product( vforward.v, ungrab_list[cnt].diff.v );
2396                 if ( ftmp > 0.0f )
2397                 {
2398                     ai_state_set_bumplast( &( pchr_b->ai ), ichr_a );
2399                     break;
2400                 }
2401             }
2402         }
2403     }
2404 
2405     return retval;
2406 }
2407 
2408 //--------------------------------------------------------------------------------------------
character_swipe(const CHR_REF ichr,slot_t slot)2409 void character_swipe( const CHR_REF ichr, slot_t slot )
2410 {
2411     /// @details ZZ@> This function spawns an attack particle
2412 
2413     CHR_REF iweapon, ithrown, iholder;
2414     chr_t * pchr, * pweapon;
2415     cap_t * pweapon_cap;
2416 
2417     PRT_REF iparticle;
2418 
2419     int   spawn_vrt_offset;
2420     Uint8 action;
2421     Uint16 turn;
2422     float velocity;
2423 
2424     bool_t unarmed_attack;
2425 
2426     if ( !INGAME_CHR( ichr ) ) return;
2427     pchr = ChrList.lst + ichr;
2428 
2429     iweapon = pchr->holdingwhich[slot];
2430 
2431     // See if it's an unarmed attack...
2432     if ( MAX_CHR == iweapon )
2433     {
2434         unarmed_attack   = btrue;
2435         iweapon          = ichr;
2436         spawn_vrt_offset = slot_to_grip_offset( slot );  // SLOT_LEFT -> GRIP_LEFT, SLOT_RIGHT -> GRIP_RIGHT
2437     }
2438     else
2439     {
2440         unarmed_attack   = bfalse;
2441         spawn_vrt_offset = GRIP_LAST;
2442         action = pchr->inst.action_which;
2443     }
2444 
2445     if ( !INGAME_CHR( iweapon ) ) return;
2446     pweapon = ChrList.lst + iweapon;
2447 
2448     pweapon_cap = chr_get_pcap( iweapon );
2449     if ( NULL == pweapon_cap ) return;
2450 
2451     // find the 1st non-item that is holding the weapon
2452     iholder = chr_get_lowest_attachment( iweapon, btrue );
2453 
2454     if ( iweapon != iholder && iweapon != ichr )
2455     {
2456         // This seems to be the "proper" place to activate the held object.
2457         // If the attack action  of the character holding the weapon does not have
2458         // MADFX_ACTLEFT or MADFX_ACTRIGHT bits (and so character_swipe function is never called)
2459         // then the action is played and the ALERTIF_USED bit is set in the chr_do_latch_attack()
2460         // function.
2461         //
2462         // It would be better to move all of this to the character_swipe() function, but we cannot be assured
2463         // that all models have the proper bits set.
2464 
2465         // Make the iweapon attack too
2466         chr_play_action( pweapon, ACTION_MJ, bfalse );
2467 
2468         SET_BIT( pweapon->ai.alert, ALERTIF_USED );
2469     }
2470 
2471     // What kind of attack are we going to do?
2472     if ( !unarmed_attack && (( pweapon_cap->isstackable && pweapon->ammo > 1 ) || ACTION_IS_TYPE( pweapon->inst.action_which, F ) ) )
2473     {
2474         // Throw the weapon if it's stacked or a hurl animation
2475         ithrown = spawn_one_character( pchr->pos, pweapon->profile_ref, chr_get_iteam( iholder ), 0, pchr->ori.facing_z, pweapon->Name, ( CHR_REF )MAX_CHR );
2476         if ( DEFINED_CHR( ithrown ) )
2477         {
2478             chr_t * pthrown = ChrList.lst + ithrown;
2479 
2480             pthrown->iskursed = bfalse;
2481             pthrown->ammo = 1;
2482             SET_BIT( pthrown->ai.alert, ALERTIF_THROWN );
2483             velocity = pchr->strength / ( pthrown->phys.weight * THROWFIX );
2484             velocity += MINTHROWVELOCITY;
2485             velocity = MIN( velocity, MAXTHROWVELOCITY );
2486 
2487             turn = TO_TURN( pchr->ori.facing_z + ATK_BEHIND );
2488             pthrown->vel.x += turntocos[ turn ] * velocity;
2489             pthrown->vel.y += turntosin[ turn ] * velocity;
2490             pthrown->vel.z = DROPZVEL;
2491             if ( pweapon->ammo <= 1 )
2492             {
2493                 // Poof the item
2494                 detach_character_from_mount( iweapon, btrue, bfalse );
2495                 chr_request_terminate( GET_REF_PCHR( pweapon ) );
2496             }
2497             else
2498             {
2499                 pweapon->ammo--;
2500             }
2501         }
2502     }
2503     else
2504     {
2505         // A generic attack. Spawn the damage particle.
2506         if ( 0 == pweapon->ammomax || 0 != pweapon->ammo )
2507         {
2508             if ( pweapon->ammo > 0 && !pweapon_cap->isstackable )
2509             {
2510                 pweapon->ammo--;  // Ammo usage
2511             }
2512 
2513             // Spawn an attack particle
2514             if ( -1 != pweapon_cap->attack_lpip )
2515             {
2516                 // make the weapon's holder the owner of the attack particle?
2517                 // will this mess up wands?
2518                 iparticle = spawn_one_particle( pweapon->pos, pchr->ori.facing_z, pweapon->profile_ref, pweapon_cap->attack_lpip, iweapon, spawn_vrt_offset, chr_get_iteam( iholder ), iholder, ( PRT_REF )MAX_PRT, 0, ( CHR_REF )MAX_CHR );
2519 
2520                 if ( DEFINED_PRT( iparticle ) )
2521                 {
2522                     fvec3_t tmp_pos;
2523                     prt_t * pprt = PrtList.lst + iparticle;
2524 
2525                     tmp_pos = prt_get_pos( pprt );
2526 
2527                     if ( pweapon_cap->attack_attached )
2528                     {
2529                         // attached particles get a strength bonus for reeling...
2530                         // dampen = REELBASE + ( pchr->strength / REEL );
2531 
2532                         // this gives a factor of 10 increase in bumping
2533                         // at a stat of 60, and a penalty for stats below about 10
2534                         float bumpdampen = exp( -1.8e-4 * ( pchr->strength - 2611 ) );
2535 
2536                         pprt->phys.weight     = pweapon->phys.weight;
2537                         pprt->phys.bumpdampen = pweapon->phys.bumpdampen * bumpdampen;
2538 
2539                         pprt = place_particle_at_vertex( pprt, iweapon, spawn_vrt_offset );
2540                         if ( NULL == pprt ) return;
2541                     }
2542                     else if ( prt_get_ppip( iparticle )->startontarget && INGAME_CHR( pprt->target_ref ) )
2543                     {
2544                         pprt = place_particle_at_vertex( pprt, pprt->target_ref, spawn_vrt_offset );
2545                         if ( NULL == pprt ) return;
2546 
2547                         // Correct Z spacing base, but nothing else...
2548                         tmp_pos.z += prt_get_ppip( iparticle )->spacing_vrt_pair.base;
2549                     }
2550                     else
2551                     {
2552                         // NOT ATTACHED
2553                         pprt->attachedto_ref = ( CHR_REF )MAX_CHR;
2554 
2555                         // Don't spawn in walls
2556                         if ( EMPTY_BIT_FIELD != prt_test_wall( pprt, tmp_pos.v, NULL ) )
2557                         {
2558                             tmp_pos.x = pweapon->pos.x;
2559                             tmp_pos.y = pweapon->pos.y;
2560                             if ( EMPTY_BIT_FIELD != prt_test_wall( pprt, tmp_pos.v, NULL ) )
2561                             {
2562                                 tmp_pos.x = pchr->pos.x;
2563                                 tmp_pos.y = pchr->pos.y;
2564                             }
2565                         }
2566                     }
2567 
2568                     // Initial particles get a bonus, which may be 0.00f
2569                     pprt->damage.base += ( pchr->strength     * pweapon_cap->str_bonus );
2570                     pprt->damage.base += ( pchr->wisdom       * pweapon_cap->wis_bonus );
2571                     pprt->damage.base += ( pchr->intelligence * pweapon_cap->int_bonus );
2572                     pprt->damage.base += ( pchr->dexterity    * pweapon_cap->dex_bonus );
2573 
2574                     // Initial particles get an enchantment bonus
2575                     pprt->damage.base += pweapon->damage_boost;
2576 
2577                     prt_set_pos( pprt, tmp_pos.v );
2578                 }
2579             }
2580         }
2581         else
2582         {
2583             pweapon->ammoknown = btrue;
2584         }
2585     }
2586 }
2587 
2588 //--------------------------------------------------------------------------------------------
drop_money(const CHR_REF character,int money)2589 void drop_money( const CHR_REF character, int money )
2590 {
2591     /// @details ZZ@> This function drops some of a character's money
2592 
2593     int vals[PIP_MONEY_COUNT] = {1, 5, 25, 100, 200, 500, 1000, 2000};
2594     int pips[PIP_MONEY_COUNT] =
2595     {
2596         PIP_COIN1, PIP_COIN5, PIP_COIN25, PIP_COIN100,
2597         PIP_GEM200, PIP_GEM500, PIP_GEM1000, PIP_GEM2000
2598     };
2599 
2600     chr_t * pchr;
2601     fvec3_t loc_pos;
2602 
2603     if ( !INGAME_CHR( character ) ) return;
2604     pchr = ChrList.lst + character;
2605 
2606     fvec3_base_copy( loc_pos.v, chr_get_pos_v( pchr ) );
2607 
2608     // limit the about of money to the character's actual money
2609     if ( money > ChrList.lst[character].money )
2610     {
2611         money = ChrList.lst[character].money;
2612     }
2613 
2614     if ( money > 0 && loc_pos.z > -2 )
2615     {
2616         int cnt, tnc;
2617         int count;
2618 
2619         // remove the money from inventory
2620         pchr->money -= money;
2621 
2622         // make the particles emit from "waist high"
2623         loc_pos.z += ( pchr->chr_min_cv.maxs[OCT_Z] + pchr->chr_min_cv.mins[OCT_Z] ) * 0.5f;
2624 
2625         // Give the character a time-out from interacting with particles so it
2626         // doesn't just grab the money again
2627         pchr->damage_timer = DAMAGETIME;
2628 
2629         // count and spawn the various denominations
2630         for ( cnt = PIP_MONEY_COUNT - 1; cnt >= 0 && money >= 0; cnt-- )
2631         {
2632             count = money / vals[cnt];
2633             money -= count * vals[cnt];
2634 
2635             for ( tnc = 0; tnc < count; tnc++ )
2636             {
2637                 spawn_one_particle_global( loc_pos, ATK_FRONT, pips[cnt], tnc );
2638             }
2639         }
2640     }
2641 }
2642 
2643 //--------------------------------------------------------------------------------------------
call_for_help(const CHR_REF character)2644 void call_for_help( const CHR_REF character )
2645 {
2646     /// @details ZZ@> This function issues a call for help to all allies
2647 
2648     TEAM_REF team;
2649 
2650     if ( !INGAME_CHR( character ) ) return;
2651 
2652     team = chr_get_iteam( character );
2653     TeamStack.lst[team].sissy = character;
2654 
2655     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
2656     {
2657         if ( cnt != character && !team_hates_team( pchr->team, team ) )
2658         {
2659             SET_BIT( pchr->ai.alert, ALERTIF_CALLEDFORHELP );
2660         }
2661     }
2662     CHR_END_LOOP();
2663 }
2664 
2665 //--------------------------------------------------------------------------------------------
setup_xp_table(const CAP_REF icap)2666 bool_t setup_xp_table( const CAP_REF icap )
2667 {
2668     /// @details ZF@> This calculates the xp needed to reach next level and stores it in an array for later use
2669 
2670     Uint8 level;
2671     cap_t * pcap;
2672 
2673     if ( !LOADED_CAP( icap ) ) return bfalse;
2674     pcap = CapStack.lst + icap;
2675 
2676     // Calculate xp needed
2677     for ( level = MAXBASELEVEL; level < MAXLEVEL; level++ )
2678     {
2679         Uint32 xpneeded = pcap->experience_forlevel[MAXBASELEVEL - 1];
2680         xpneeded += ( level * level * level * 15 );
2681         xpneeded -= (( MAXBASELEVEL - 1 ) * ( MAXBASELEVEL - 1 ) * ( MAXBASELEVEL - 1 ) * 15 );
2682         pcap->experience_forlevel[level] = xpneeded;
2683     }
2684     return btrue;
2685 }
2686 
2687 //--------------------------------------------------------------------------------------------
do_level_up(const CHR_REF character)2688 void do_level_up( const CHR_REF character )
2689 {
2690     /// @details BB@> level gains are done here, but only once a second
2691 
2692     Uint8 curlevel;
2693     int number;
2694     chr_t * pchr;
2695     cap_t * pcap;
2696 
2697     if ( !INGAME_CHR( character ) ) return;
2698     pchr = ChrList.lst + character;
2699 
2700     pcap = chr_get_pcap( character );
2701     if ( NULL == pcap ) return;
2702 
2703     // Do level ups and stat changes
2704     curlevel = pchr->experiencelevel + 1;
2705     if ( curlevel < MAXLEVEL )
2706     {
2707         Uint32 xpcurrent, xpneeded;
2708 
2709         xpcurrent = pchr->experience;
2710         xpneeded  = pcap->experience_forlevel[curlevel];
2711         if ( xpcurrent >= xpneeded )
2712         {
2713             // do the level up
2714             pchr->experiencelevel++;
2715             xpneeded = pcap->experience_forlevel[curlevel];
2716             SET_BIT( pchr->ai.alert, ALERTIF_LEVELUP );
2717 
2718             // The character is ready to advance...
2719             if ( VALID_PLA( pchr->is_which_player ) )
2720             {
2721                 debug_printf( "%s gained a level!!!", chr_get_name( GET_REF_PCHR( pchr ), CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ) );
2722                 sound_play_chunk_full( g_wavelist[GSND_LEVELUP] );
2723             }
2724 
2725             // Size
2726             pchr->fat_goto += pcap->size_perlevel * 0.25f;  // Limit this?
2727             pchr->fat_goto_time += SIZETIME;
2728 
2729             // Strength
2730             number = generate_irand_range( pcap->strength_stat.perlevel );
2731             number += pchr->strength;
2732             if ( number > PERFECTSTAT ) number = PERFECTSTAT;
2733             pchr->strength = number;
2734 
2735             // Wisdom
2736             number = generate_irand_range( pcap->wisdom_stat.perlevel );
2737             number += pchr->wisdom;
2738             if ( number > PERFECTSTAT ) number = PERFECTSTAT;
2739             pchr->wisdom = number;
2740 
2741             // Intelligence
2742             number = generate_irand_range( pcap->intelligence_stat.perlevel );
2743             number += pchr->intelligence;
2744             if ( number > PERFECTSTAT ) number = PERFECTSTAT;
2745             pchr->intelligence = number;
2746 
2747             // Dexterity
2748             number = generate_irand_range( pcap->dexterity_stat.perlevel );
2749             number += pchr->dexterity;
2750             if ( number > PERFECTSTAT ) number = PERFECTSTAT;
2751             pchr->dexterity = number;
2752 
2753             // Life
2754             number = generate_irand_range( pcap->life_stat.perlevel );
2755             number += pchr->lifemax;
2756             if ( number > PERFECTBIG ) number = PERFECTBIG;
2757             pchr->life += ( number - pchr->lifemax );
2758             pchr->lifemax = number;
2759 
2760             // Mana
2761             number = generate_irand_range( pcap->mana_stat.perlevel );
2762             number += pchr->manamax;
2763             if ( number > PERFECTBIG ) number = PERFECTBIG;
2764             pchr->mana += ( number - pchr->manamax );
2765             pchr->manamax = number;
2766 
2767             // Mana Return
2768             number = generate_irand_range( pcap->manareturn_stat.perlevel );
2769             number += pchr->manareturn;
2770             if ( number > PERFECTSTAT ) number = PERFECTSTAT;
2771             pchr->manareturn = number;
2772 
2773             // Mana Flow
2774             number = generate_irand_range( pcap->manaflow_stat.perlevel );
2775             number += pchr->manaflow;
2776             if ( number > PERFECTSTAT ) number = PERFECTSTAT;
2777             pchr->manaflow = number;
2778         }
2779     }
2780 }
2781 
2782 //--------------------------------------------------------------------------------------------
give_experience(const CHR_REF character,int amount,xp_type xptype,bool_t override_invictus)2783 void give_experience( const CHR_REF character, int amount, xp_type xptype, bool_t override_invictus )
2784 {
2785     /// @details ZZ@> This function gives a character experience
2786 
2787     float newamount;
2788 
2789     chr_t * pchr;
2790     cap_t * pcap;
2791 
2792     if ( !INGAME_CHR( character ) ) return;
2793     pchr = ChrList.lst + character;
2794 
2795     pcap = chr_get_pcap( character );
2796     if ( NULL == pcap ) return;
2797 
2798     //No xp to give
2799     if ( 0 == amount ) return;
2800 
2801     if ( !pchr->invictus || override_invictus )
2802     {
2803         float intadd = ( FP8_TO_INT( pchr->intelligence ) - 10.0f ) / 200.0f;
2804         float wisadd = ( FP8_TO_INT( pchr->wisdom )       - 10.0f ) / 400.0f;
2805 
2806         // Figure out how much experience to give
2807         newamount = amount;
2808         if ( xptype < XP_COUNT )
2809         {
2810             newamount = amount * pcap->experience_rate[xptype];
2811         }
2812 
2813         // Intelligence and slightly wisdom increases xp gained (0,5% per int and 0,25% per wisdom above 10)
2814         newamount *= 1.00f + intadd + wisadd;
2815 
2816         // Apply XP bonus/penality depending on game difficulty
2817         if ( cfg.difficulty >= GAME_HARD ) newamount *= 1.20f;                // 20% extra on hard
2818         else if ( cfg.difficulty >= GAME_NORMAL ) newamount *= 1.10f;       // 10% extra on normal
2819 
2820         pchr->experience += newamount;
2821     }
2822 }
2823 
2824 //--------------------------------------------------------------------------------------------
give_team_experience(const TEAM_REF team,int amount,Uint8 xptype)2825 void give_team_experience( const TEAM_REF team, int amount, Uint8 xptype )
2826 {
2827     /// @details ZZ@> This function gives every character on a team experience
2828 
2829     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
2830     {
2831         if ( pchr->team == team )
2832         {
2833             give_experience( cnt, amount, ( xp_type )xptype, bfalse );
2834         }
2835     }
2836     CHR_END_LOOP();
2837 }
2838 
2839 //--------------------------------------------------------------------------------------------
resize_one_character(chr_t * pchr)2840 chr_t * resize_one_character( chr_t * pchr )
2841 {
2842     /// @details ZZ@> This function makes the characters get bigger or smaller, depending
2843     ///    on their fat_goto and fat_goto_time. Spellbooks do not resize
2844     ///    BB@> assume that this will only be called from inside chr_config_do_active(),
2845     ///         so pchr is just right to be used here
2846 
2847     CHR_REF ichr;
2848     cap_t * pcap;
2849     bool_t  willgetcaught;
2850     float   newsize;
2851 
2852     if ( NULL == pchr ) return pchr;
2853 
2854     ichr = GET_REF_PCHR( pchr );
2855     pcap = chr_get_pcap( ichr );
2856 
2857     if ( pchr->fat_goto_time < 0 ) return pchr;
2858 
2859     if ( pchr->fat_goto != pchr->fat )
2860     {
2861         int bump_increase;
2862 
2863         bump_increase = ( pchr->fat_goto - pchr->fat ) * 0.10f * pchr->bump.size;
2864 
2865         // Make sure it won't get caught in a wall
2866         willgetcaught = bfalse;
2867         if ( pchr->fat_goto > pchr->fat )
2868         {
2869             pchr->bump.size += bump_increase;
2870 
2871             if ( EMPTY_BIT_FIELD != chr_test_wall( pchr, NULL, NULL ) )
2872             {
2873                 willgetcaught = btrue;
2874             }
2875 
2876             pchr->bump.size -= bump_increase;
2877         }
2878 
2879         // If it is getting caught, simply halt growth until later
2880         if ( !willgetcaught )
2881         {
2882             // Figure out how big it is
2883             pchr->fat_goto_time--;
2884 
2885             newsize = pchr->fat_goto;
2886             if ( pchr->fat_goto_time > 0 )
2887             {
2888                 newsize = ( pchr->fat * 0.90f ) + ( newsize * 0.10f );
2889             }
2890 
2891             // Make it that big...
2892             chr_set_fat( pchr, newsize );
2893 
2894             if ( CAP_INFINITE_WEIGHT == pcap->weight )
2895             {
2896                 pchr->phys.weight = CHR_INFINITE_WEIGHT;
2897             }
2898             else
2899             {
2900                 Uint32 itmp = pcap->weight * pchr->fat * pchr->fat * pchr->fat;
2901                 pchr->phys.weight = MIN( itmp, CHR_MAX_WEIGHT );
2902             }
2903         }
2904     }
2905 
2906     return pchr;
2907 }
2908 
2909 //--------------------------------------------------------------------------------------------
export_one_character_quest_vfs(const char * szSaveName,const CHR_REF character)2910 bool_t export_one_character_quest_vfs( const char *szSaveName, const CHR_REF character )
2911 {
2912     /// @details ZZ@> This function makes the naming.txt file for the character
2913 
2914     player_t *ppla;
2915     egoboo_rv rv;
2916 
2917     if ( !INGAME_CHR( character ) ) return bfalse;
2918 
2919     ppla = chr_get_ppla( character );
2920     if ( ppla == NULL ) return bfalse;
2921 
2922     rv = quest_log_upload_vfs( ppla->quest_log, SDL_arraysize( ppla->quest_log ), szSaveName );
2923     return rv_success == rv ? btrue : bfalse;
2924 }
2925 
2926 //--------------------------------------------------------------------------------------------
2927 //void resize_all_characters()
2928 //{
2929 //    /// @details ZZ@> This function makes the characters get bigger or smaller, depending
2930 //    ///    on their fat_goto and fat_goto_time. Spellbooks do not resize
2931 //
2932 //    CHR_BEGIN_LOOP_ACTIVE( ichr, pchr )
2933 //    {
2934 //        resize_one_character( pchr );
2935 //    }
2936 //    CHR_END_LOOP();
2937 //}
2938 
2939 //--------------------------------------------------------------------------------------------
export_one_character_name_vfs(const char * szSaveName,const CHR_REF character)2940 bool_t export_one_character_name_vfs( const char *szSaveName, const CHR_REF character )
2941 {
2942     /// @details ZZ@> This function makes the naming.txt file for the character
2943 
2944     if ( !INGAME_CHR( character ) ) return bfalse;
2945 
2946     return chop_export_vfs( szSaveName, ChrList.lst[character].Name );
2947 }
2948 
2949 //--------------------------------------------------------------------------------------------
chr_upload_cap(chr_t * pchr,cap_t * pcap)2950 bool_t chr_upload_cap( chr_t * pchr, cap_t * pcap )
2951 {
2952     /// @details BB@> prepare a character profile for exporting, by uploading some special values into the
2953     ///     cap. Just so that there is no confusion when you export multiple items of the same type,
2954     ///     DO NOT pass the pointer returned by chr_get_pcap(). Instead, use a custom cap_t declared on the stack,
2955     ///     or something similar
2956     ///
2957     /// @note This has been modified to basically reverse the actions of chr_download_cap().
2958     ///       If all enchants have been removed, this should export all permanent changes to the
2959     ///       base character profile.
2960 
2961     int tnc;
2962 
2963     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
2964 
2965     if ( NULL == pcap || !pcap->loaded ) return bfalse;
2966 
2967     // export values that override spawn.txt values
2968     pcap->content_override   = pchr->ai.content;
2969     pcap->state_override     = pchr->ai.state;
2970     pcap->money              = pchr->money;
2971     pcap->skin_override      = pchr->skin;
2972     pcap->level_override     = pchr->experiencelevel;
2973 
2974     // export the current experience
2975     ints_to_range( pchr->experience, 0, &( pcap->experience ) );
2976 
2977     // export the current mana and life
2978     pcap->life_spawn         = CLIP( pchr->life, 0, pchr->lifemax );
2979     pcap->mana_spawn         = CLIP( pchr->mana, 0, pchr->manamax );
2980 
2981     // Movement
2982     pcap->anim_speed_sneak = pchr->anim_speed_sneak;
2983     pcap->anim_speed_walk = pchr->anim_speed_walk;
2984     pcap->anim_speed_run = pchr->anim_speed_run;
2985 
2986     // weight and size
2987     pcap->size       = pchr->fat_goto;
2988     pcap->bumpdampen = pchr->phys.bumpdampen;
2989     if ( CHR_INFINITE_WEIGHT == pchr->phys.weight )
2990     {
2991         pcap->weight = CAP_INFINITE_WEIGHT;
2992     }
2993     else
2994     {
2995         Uint32 itmp = pchr->phys.weight / pchr->fat / pchr->fat / pchr->fat;
2996         pcap->weight = MIN( itmp, CAP_MAX_WEIGHT );
2997     }
2998 
2999     // Other junk
3000     pcap->flyheight   = pchr->flyheight;
3001     pcap->alpha       = pchr->alpha_base;
3002     pcap->light       = pchr->light_base;
3003     pcap->flashand    = pchr->flashand;
3004     pcap->dampen      = pchr->phys.dampen;
3005 
3006     // Jumping
3007     pcap->jump       = pchr->jump_power;
3008     pcap->jumpnumber = pchr->jumpnumberreset;
3009 
3010     // Flags
3011     pcap->stickybutt      = pchr->stickybutt;
3012     pcap->canopenstuff    = pchr->openstuff;
3013     pcap->transferblend   = pchr->transferblend;
3014     pcap->waterwalk       = pchr->waterwalk;
3015     pcap->platform        = pchr->platform;
3016     pcap->canuseplatforms = pchr->canuseplatforms;
3017     pcap->isitem          = pchr->isitem;
3018     pcap->invictus        = pchr->invictus;
3019     pcap->ismount         = pchr->ismount;
3020     pcap->cangrabmoney    = pchr->cangrabmoney;
3021 
3022     // Damage
3023     pcap->attachedprt_reaffirm_damagetype = pchr->reaffirm_damagetype;
3024     pcap->damagetarget_damagetype         = pchr->damagetarget_damagetype;
3025 
3026     // SWID
3027     ints_to_range( pchr->strength    , 0, &( pcap->strength_stat.val ) );
3028     ints_to_range( pchr->wisdom      , 0, &( pcap->wisdom_stat.val ) );
3029     ints_to_range( pchr->intelligence, 0, &( pcap->intelligence_stat.val ) );
3030     ints_to_range( pchr->dexterity   , 0, &( pcap->dexterity_stat.val ) );
3031 
3032     // Life and Mana
3033     pcap->lifecolor = pchr->lifecolor;
3034     pcap->manacolor = pchr->manacolor;
3035     ints_to_range( pchr->lifemax     , 0, &( pcap->life_stat.val ) );
3036     ints_to_range( pchr->manamax     , 0, &( pcap->mana_stat.val ) );
3037     ints_to_range( pchr->manareturn  , 0, &( pcap->manareturn_stat.val ) );
3038     ints_to_range( pchr->manaflow    , 0, &( pcap->manaflow_stat.val ) );
3039 
3040     // Gender
3041     pcap->gender  = pchr->gender;
3042 
3043     // Ammo
3044     pcap->ammomax = pchr->ammomax;
3045     pcap->ammo    = pchr->ammo;
3046 
3047     // update any skills that have been learned
3048     idsz_map_copy( pchr->skills, SDL_arraysize( pchr->skills ), pcap->skills );
3049 
3050     // Enchant stuff
3051     pcap->see_invisible_level = pchr->see_invisible_level;
3052 
3053     // base kurse state
3054     pcap->kursechance = pchr->iskursed ? 100 : 0;
3055 
3056     // Model stuff
3057     pcap->stoppedby = pchr->stoppedby;
3058     pcap->life_heal = pchr->life_heal;
3059     pcap->manacost  = pchr->manacost;
3060     pcap->nameknown = pchr->nameknown || pchr->ammoknown;          // make sure that identified items are saved as identified
3061     pcap->draw_icon = pchr->draw_icon;
3062 
3063     // sound stuff...
3064     for ( tnc = 0; tnc < SOUND_COUNT; tnc++ )
3065     {
3066         pcap->sound_index[tnc] = pchr->sound_index[tnc];
3067     }
3068 
3069     return btrue;
3070 }
3071 
3072 //--------------------------------------------------------------------------------------------
chr_download_cap(chr_t * pchr,cap_t * pcap)3073 bool_t chr_download_cap( chr_t * pchr, cap_t * pcap )
3074 {
3075     /// @details BB@> grab all of the data from the data.txt file
3076 
3077     int iTmp, tnc;
3078 
3079     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
3080 
3081     if ( NULL == pcap || !pcap->loaded ) return bfalse;
3082 
3083     // sound stuff...  copy from the cap
3084     for ( tnc = 0; tnc < SOUND_COUNT; tnc++ )
3085     {
3086         pchr->sound_index[tnc] = pcap->sound_index[tnc];
3087     }
3088 
3089     // Set up model stuff
3090     pchr->stoppedby = pcap->stoppedby;
3091     pchr->life_heal = pcap->life_heal;
3092     pchr->manacost  = pcap->manacost;
3093     pchr->nameknown = pcap->nameknown;
3094     pchr->ammoknown = pcap->nameknown;
3095     pchr->draw_icon = pcap->draw_icon;
3096 
3097     // calculate a base kurse state. this may be overridden later
3098     if ( pcap->isitem )
3099     {
3100         IPair loc_rand = {1, 100};
3101         pchr->iskursed = ( generate_irand_pair( loc_rand ) <= pcap->kursechance );
3102     }
3103 
3104     // Enchant stuff
3105     pchr->see_invisible_level = pcap->see_invisible_level;
3106 
3107     // Skillz
3108     idsz_map_copy( pcap->skills, SDL_arraysize( pcap->skills ), pchr->skills );
3109     pchr->darkvision_level = chr_get_skill( pchr, MAKE_IDSZ( 'D', 'A', 'R', 'K' ) );
3110     pchr->see_invisible_level = pcap->see_invisible_level;
3111 
3112     // Ammo
3113     pchr->ammomax = pcap->ammomax;
3114     pchr->ammo = pcap->ammo;
3115 
3116     // Gender
3117     pchr->gender = pcap->gender;
3118     if ( pchr->gender == GENDER_RANDOM )  pchr->gender = generate_randmask( GENDER_FEMALE, GENDER_MALE );
3119 
3120     // Life and Mana
3121     pchr->lifecolor = pcap->lifecolor;
3122     pchr->manacolor = pcap->manacolor;
3123     pchr->lifemax = generate_irand_range( pcap->life_stat.val );
3124     pchr->life_return = pcap->life_return;
3125     pchr->manamax = generate_irand_range( pcap->mana_stat.val );
3126     pchr->manaflow = generate_irand_range( pcap->manaflow_stat.val );
3127     pchr->manareturn = generate_irand_range( pcap->manareturn_stat.val );
3128 
3129     // SWID
3130     pchr->strength = generate_irand_range( pcap->strength_stat.val );
3131     pchr->wisdom = generate_irand_range( pcap->wisdom_stat.val );
3132     pchr->intelligence = generate_irand_range( pcap->intelligence_stat.val );
3133     pchr->dexterity = generate_irand_range( pcap->dexterity_stat.val );
3134 
3135     // Skin
3136     pchr->skin = 0;
3137     if ( NO_SKIN_OVERRIDE != pcap->spelleffect_type )
3138     {
3139         pchr->skin = pcap->spelleffect_type % MAX_SKIN;
3140     }
3141     else if ( NO_SKIN_OVERRIDE != pcap->skin_override )
3142     {
3143         pchr->skin = pcap->skin_override % MAX_SKIN;
3144     }
3145 
3146     // Damage
3147     pchr->defense = pcap->defense[pchr->skin];
3148     pchr->reaffirm_damagetype = pcap->attachedprt_reaffirm_damagetype;
3149     pchr->damagetarget_damagetype = pcap->damagetarget_damagetype;
3150     for ( tnc = 0; tnc < DAMAGE_COUNT; tnc++ )
3151     {
3152         pchr->damage_modifier[tnc] = pcap->damage_modifier[tnc][pchr->skin];
3153     }
3154 
3155     // Flags
3156     pchr->stickybutt      = pcap->stickybutt;
3157     pchr->openstuff       = pcap->canopenstuff;
3158     pchr->transferblend   = pcap->transferblend;
3159     pchr->waterwalk       = pcap->waterwalk;
3160     pchr->platform        = pcap->platform;
3161     pchr->canuseplatforms = pcap->canuseplatforms;
3162     pchr->isitem          = pcap->isitem;
3163     pchr->invictus        = pcap->invictus;
3164     pchr->ismount         = pcap->ismount;
3165     pchr->cangrabmoney    = pcap->cangrabmoney;
3166 
3167     // Jumping
3168     pchr->jump_power = pcap->jump;
3169     pchr->jumpnumberreset = pcap->jumpnumber;
3170 
3171     // Other junk
3172     pchr->flyheight   = pcap->flyheight;
3173     pchr->maxaccel    = pchr->maxaccel_reset = pcap->maxaccel[pchr->skin];
3174     pchr->alpha_base  = pcap->alpha;
3175     pchr->light_base  = pcap->light;
3176     pchr->flashand    = pcap->flashand;
3177     pchr->phys.dampen = pcap->dampen;
3178 
3179     // Load current life and mana. this may be overridden later
3180     pchr->life = CLIP( pcap->life_spawn, LOWSTAT, pchr->lifemax );
3181     pchr->mana = CLIP( pcap->mana_spawn,       0, pchr->manamax );
3182 
3183     pchr->phys.bumpdampen = pcap->bumpdampen;
3184     if ( CAP_INFINITE_WEIGHT == pcap->weight )
3185     {
3186         pchr->phys.weight = CHR_INFINITE_WEIGHT;
3187     }
3188     else
3189     {
3190         Uint32 itmp = pcap->weight * pcap->size * pcap->size * pcap->size;
3191         pchr->phys.weight = MIN( itmp, CHR_MAX_WEIGHT );
3192     }
3193 
3194     // Image rendering
3195     pchr->uoffvel = pcap->uoffvel;
3196     pchr->voffvel = pcap->voffvel;
3197 
3198     // Movement
3199     pchr->anim_speed_sneak = pcap->anim_speed_sneak;
3200     pchr->anim_speed_walk = pcap->anim_speed_walk;
3201     pchr->anim_speed_run = pcap->anim_speed_run;
3202 
3203     // Money is added later
3204     pchr->money = pcap->money;
3205 
3206     // Experience
3207     iTmp = generate_irand_range( pcap->experience );
3208     pchr->experience      = MIN( iTmp, MAXXP );
3209     pchr->experiencelevel = pcap->level_override;
3210 
3211     // Particle attachments
3212     pchr->reaffirm_damagetype = pcap->attachedprt_reaffirm_damagetype;
3213 
3214     // Character size and bumping
3215     chr_init_size( pchr, pcap );
3216 
3217     return btrue;
3218 }
3219 
3220 //--------------------------------------------------------------------------------------------
export_one_character_profile_vfs(const char * szSaveName,const CHR_REF character)3221 bool_t export_one_character_profile_vfs( const char *szSaveName, const CHR_REF character )
3222 {
3223     /// @details ZZ@> This function creates a data.txt file for the given character.
3224     ///    it is assumed that all enchantments have been done away with
3225 
3226     chr_t * pchr;
3227     cap_t * pcap;
3228 
3229     // a local version of the cap, so that the CapStack data won't be corrupted
3230     cap_t cap_tmp;
3231 
3232     if ( INVALID_CSTR( szSaveName ) && !DEFINED_CHR( character ) ) return bfalse;
3233     pchr = ChrList.lst + character;
3234 
3235     pcap = pro_get_pcap( pchr->profile_ref );
3236     if ( NULL == pcap ) return bfalse;
3237 
3238     // load up the temporary cap
3239     memcpy( &cap_tmp, pcap, sizeof( cap_t ) );
3240 
3241     // fill in the cap values with the ones we want to export from the character profile
3242     chr_upload_cap( pchr, &cap_tmp );
3243 
3244     return save_one_cap_file_vfs( szSaveName, NULL, &cap_tmp );
3245 }
3246 
3247 //--------------------------------------------------------------------------------------------
export_one_character_skin_vfs(const char * szSaveName,const CHR_REF character)3248 bool_t export_one_character_skin_vfs( const char *szSaveName, const CHR_REF character )
3249 {
3250     /// @details ZZ@> This function creates a skin.txt file for the given character.
3251 
3252     vfs_FILE* filewrite;
3253 
3254     if ( !INGAME_CHR( character ) ) return bfalse;
3255 
3256     // Open the file
3257     filewrite = vfs_openWrite( szSaveName );
3258     if ( NULL == filewrite ) return bfalse;
3259 
3260     vfs_printf( filewrite, "// This file is used only by the import menu\n" );
3261     vfs_printf( filewrite, ": %d\n", ChrList.lst[character].skin );
3262     vfs_close( filewrite );
3263     return btrue;
3264 }
3265 
3266 //--------------------------------------------------------------------------------------------
load_one_character_profile_vfs(const char * tmploadname,int slot_override,bool_t required)3267 CAP_REF load_one_character_profile_vfs( const char * tmploadname, int slot_override, bool_t required )
3268 {
3269     /// @details ZZ@> This function fills a character profile with data from data.txt, returning
3270     /// the icap slot that the profile was stuck into.  It may cause the program
3271     /// to abort if bad things happen.
3272 
3273     CAP_REF  icap = ( CAP_REF )MAX_CAP;
3274     cap_t * pcap;
3275     STRING  szLoadName;
3276 
3277     if ( VALID_PRO_RANGE( slot_override ) )
3278     {
3279         icap = ( CAP_REF )slot_override;
3280     }
3281     else
3282     {
3283         icap = pro_get_slot_vfs( tmploadname, MAX_PROFILE );
3284     }
3285 
3286     if ( !VALID_CAP_RANGE( icap ) )
3287     {
3288         // The data file wasn't found
3289         if ( required )
3290         {
3291             log_debug( "load_one_character_profile_vfs() - \"%s\" was not found. Overriding a global object?\n", szLoadName );
3292         }
3293         else if ( VALID_CAP_RANGE( slot_override ) && slot_override > PMod->importamount * MAXIMPORTPERPLAYER )
3294         {
3295             log_debug( "load_one_character_profile_vfs() - Not able to open file \"%s\"\n", szLoadName );
3296         }
3297 
3298         return ( CAP_REF )MAX_CAP;
3299     }
3300 
3301     pcap = CapStack.lst + icap;
3302 
3303     // if there is data in this profile, release it
3304     if ( pcap->loaded )
3305     {
3306         // Make sure global objects don't load over existing models
3307         if ( required && SPELLBOOK == icap )
3308         {
3309             log_error( "Object slot %i is a special reserved slot number (cannot be used by %s).\n", SPELLBOOK, szLoadName );
3310         }
3311         else if ( required && overrideslots )
3312         {
3313             log_error( "Object slot %i used twice (%s, %s)\n", REF_TO_INT( icap ), pcap->name, szLoadName );
3314         }
3315         else
3316         {
3317             // Stop, we don't want to override it
3318             return ( CAP_REF )MAX_CAP;
3319         }
3320 
3321         // If loading over an existing model is allowed (?how?) then make sure to release the old one
3322         release_one_cap( icap );
3323     }
3324 
3325     if ( NULL == load_one_cap_file_vfs( tmploadname, pcap ) )
3326     {
3327         return ( CAP_REF )MAX_CAP;
3328     }
3329 
3330     // do the rest of the levels not listed in data.txt
3331     setup_xp_table( icap );
3332 
3333     if ( cfg.gouraud_req )
3334     {
3335         pcap->uniformlit = bfalse;
3336     }
3337 
3338     // limit the wave indices to rational values
3339     pcap->sound_index[SOUND_FOOTFALL] = CLIP( pcap->sound_index[SOUND_FOOTFALL], INVALID_SOUND, MAX_WAVE );
3340     pcap->sound_index[SOUND_JUMP]     = CLIP( pcap->sound_index[SOUND_JUMP], INVALID_SOUND, MAX_WAVE );
3341 
3342     //0 == bumpdampenmeans infinite mass, and causes some problems
3343     pcap->bumpdampen = MAX( INV_FF, pcap->bumpdampen );
3344 
3345     return icap;
3346 }
3347 
3348 //--------------------------------------------------------------------------------------------
heal_character(const CHR_REF character,const CHR_REF healer,int amount,bool_t ignore_invictus)3349 bool_t heal_character( const CHR_REF character, const CHR_REF healer, int amount, bool_t ignore_invictus )
3350 {
3351     /// @details ZF@> This function gives some pure life points to the target, ignoring any resistances and so forth
3352     chr_t * pchr, *pchr_h;
3353 
3354     //Setup the healed character
3355     if ( !INGAME_CHR( character ) ) return bfalse;
3356     pchr = ChrList.lst + character;
3357 
3358     //Setup the healer
3359     if ( !INGAME_CHR( healer ) ) return bfalse;
3360     pchr_h = ChrList.lst + healer;
3361 
3362     //Don't heal dead and invincible stuff
3363     if ( !pchr->alive || ( pchr->invictus && !ignore_invictus ) ) return bfalse;
3364 
3365     //This actually heals the character
3366     pchr->life = CLIP( pchr->life, pchr->life + ABS( amount ), pchr->lifemax );
3367 
3368     // Set alerts, but don't alert that we healed ourselves
3369     if ( healer != character && pchr_h->attachedto != character && ABS( amount ) > HURTDAMAGE )
3370     {
3371         SET_BIT( pchr->ai.alert, ALERTIF_HEALED );
3372         pchr->ai.attacklast = healer;
3373     }
3374 
3375     return btrue;
3376 }
3377 
3378 //--------------------------------------------------------------------------------------------
cleanup_one_character(chr_t * pchr)3379 void cleanup_one_character( chr_t * pchr )
3380 {
3381     /// @details BB@> Everything necessary to disconnect one character from the game
3382 
3383     CHR_REF  ichr, itmp;
3384     SHOP_REF ishop;
3385 
3386     if ( !ALLOCATED_PCHR( pchr ) ) return;
3387     ichr = GET_REF_PCHR( pchr );
3388 
3389     pchr->sparkle = NOSPARKLE;
3390 
3391     // Remove it from the team
3392     pchr->team = pchr->baseteam;
3393     if ( TeamStack.lst[pchr->team].morale > 0 ) TeamStack.lst[pchr->team].morale--;
3394 
3395     if ( TeamStack.lst[pchr->team].leader == ichr )
3396     {
3397         // The team now has no leader if the character is the leader
3398         TeamStack.lst[pchr->team].leader = NOLEADER;
3399     }
3400 
3401     // Clear all shop passages that it owned...
3402     for ( ishop = 0; ishop < ShopStack.count; ishop++ )
3403     {
3404         if ( ShopStack.lst[ishop].owner != ichr ) continue;
3405         ShopStack.lst[ishop].owner = SHOP_NOOWNER;
3406     }
3407 
3408     // detach from any mount
3409     if ( INGAME_CHR( pchr->attachedto ) )
3410     {
3411         detach_character_from_mount( ichr, btrue, bfalse );
3412     }
3413 
3414     // drop your left item
3415     itmp = pchr->holdingwhich[SLOT_LEFT];
3416     if ( INGAME_CHR( itmp ) && ChrList.lst[itmp].isitem )
3417     {
3418         detach_character_from_mount( itmp, btrue, bfalse );
3419     }
3420 
3421     // drop your right item
3422     itmp = pchr->holdingwhich[SLOT_RIGHT];
3423     if ( INGAME_CHR( itmp ) && ChrList.lst[itmp].isitem )
3424     {
3425         detach_character_from_mount( itmp, btrue, bfalse );
3426     }
3427 
3428     // start with a clean list
3429     cleanup_character_enchants( pchr );
3430 
3431     // remove enchants from the character
3432     if ( pchr->life >= 0 )
3433     {
3434         disenchant_character( ichr );
3435     }
3436     else
3437     {
3438         eve_t * peve;
3439         ENC_REF ienc_now, ienc_nxt;
3440         size_t  ienc_count;
3441 
3442         // cleanup the enchant list
3443         cleanup_character_enchants( pchr );
3444 
3445         // remove all invalid enchants
3446         ienc_now = pchr->firstenchant;
3447         ienc_count = 0;
3448         while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
3449         {
3450             ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
3451 
3452             peve = enc_get_peve( ienc_now );
3453             if ( NULL != peve && !peve->stayiftargetdead )
3454             {
3455                 remove_enchant( ienc_now, NULL );
3456             }
3457 
3458             ienc_now = ienc_nxt;
3459             ienc_count++;
3460         }
3461         if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
3462     }
3463 
3464     // Stop all sound loops for this object
3465     looped_stop_object_sounds( ichr );
3466 }
3467 
3468 //--------------------------------------------------------------------------------------------
kill_character(const CHR_REF ichr,const CHR_REF killer,bool_t ignore_invictus)3469 void kill_character( const CHR_REF ichr, const CHR_REF killer, bool_t ignore_invictus )
3470 {
3471     /// @details BB@> Handle a character death. Set various states, disconnect it from the world, etc.
3472 
3473     chr_t * pchr;
3474     cap_t * pcap;
3475     int action;
3476     Uint16 experience;
3477     TEAM_REF killer_team;
3478 
3479     if ( !DEFINED_CHR( ichr ) ) return;
3480     pchr = ChrList.lst + ichr;
3481 
3482     //No need to continue is there?
3483     if ( !pchr->alive || ( pchr->invictus && !ignore_invictus ) ) return;
3484 
3485     pcap = pro_get_pcap( pchr->profile_ref );
3486     if ( NULL == pcap ) return;
3487 
3488     killer_team = chr_get_iteam( killer );
3489 
3490     pchr->alive = bfalse;
3491     pchr->waskilled = btrue;
3492 
3493     pchr->life            = -1;
3494     pchr->platform        = btrue;
3495     pchr->canuseplatforms = btrue;
3496     pchr->phys.bumpdampen = pchr->phys.bumpdampen / 2.0f;
3497 
3498     // Play the death animation
3499     action = ACTION_KA + generate_randmask( 0, 3 );
3500     chr_play_action( pchr, action, bfalse );
3501     chr_instance_set_action_keep( &( pchr->inst ), btrue );
3502 
3503     // Give kill experience
3504     experience = pcap->experience_worth + ( pchr->experience * pcap->experience_exchange );
3505 
3506     // distribute experience to the attacker
3507     if ( INGAME_CHR( killer ) )
3508     {
3509         // Set target
3510         pchr->ai.target = killer;
3511         if ( killer_team == TEAM_DAMAGE || killer_team == TEAM_NULL )  pchr->ai.target = ichr;
3512 
3513         // Award experience for kill?
3514         if ( team_hates_team( killer_team, pchr->team ) )
3515         {
3516             //Check for special hatred
3517             if ( chr_get_idsz( killer, IDSZ_HATE ) == chr_get_idsz( ichr, IDSZ_PARENT ) ||
3518                  chr_get_idsz( killer, IDSZ_HATE ) == chr_get_idsz( ichr, IDSZ_TYPE ) )
3519             {
3520                 give_experience( killer, experience, XP_KILLHATED, bfalse );
3521             }
3522 
3523             // Nope, award direct kill experience instead
3524             else give_experience( killer, experience, XP_KILLENEMY, bfalse );
3525         }
3526     }
3527 
3528     //Set various alerts to let others know it has died
3529     //and distribute experience to whoever needs it
3530     SET_BIT( pchr->ai.alert, ALERTIF_KILLED );
3531 
3532     CHR_BEGIN_LOOP_ACTIVE( tnc, plistener )
3533     {
3534         if ( !plistener->alive ) continue;
3535 
3536         // All allies get team experience, but only if they also hate the dead guy's team
3537         if ( tnc != killer && !team_hates_team( plistener->team, killer_team ) && team_hates_team( plistener->team, pchr->team ) )
3538         {
3539             give_experience( tnc, experience, XP_TEAMKILL, bfalse );
3540         }
3541 
3542         // Check if it was a leader
3543         if ( TeamStack.lst[pchr->team].leader == ichr && chr_get_iteam( tnc ) == pchr->team )
3544         {
3545             // All folks on the leaders team get the alert
3546             SET_BIT( plistener->ai.alert, ALERTIF_LEADERKILLED );
3547         }
3548 
3549         // Let the other characters know it died
3550         if ( plistener->ai.target == ichr )
3551         {
3552             SET_BIT( plistener->ai.alert, ALERTIF_TARGETKILLED );
3553         }
3554     }
3555     CHR_END_LOOP();
3556 
3557     // Detach the character from the game
3558     cleanup_one_character( pchr );
3559 
3560     // If it's a player, let it die properly before enabling respawn
3561     if ( VALID_PLA( pchr->is_which_player ) ) local_stats.revivetimer = ONESECOND; // 1 second
3562 
3563     // Let it's AI script run one last time
3564     pchr->ai.timer = update_wld + 1;            // Prevent IfTimeOut in scr_run_chr_script()
3565     scr_run_chr_script( ichr );
3566 
3567     // Stop any looped sounds
3568     if ( pchr->loopedsound_channel != INVALID_SOUND ) sound_stop_channel( pchr->loopedsound_channel );
3569     looped_stop_object_sounds( ichr );
3570     pchr->loopedsound_channel = INVALID_SOUND;
3571 }
3572 
3573 //--------------------------------------------------------------------------------------------
damage_character(const CHR_REF character,FACING_T direction,IPair damage,Uint8 damagetype,TEAM_REF team,CHR_REF attacker,BIT_FIELD effects,bool_t ignore_invictus)3574 int damage_character( const CHR_REF character, FACING_T direction,
3575                       IPair  damage, Uint8 damagetype, TEAM_REF team,
3576                       CHR_REF attacker, BIT_FIELD effects, bool_t ignore_invictus )
3577 {
3578     /// @details ZZ@> This function calculates and applies damage to a character.  It also
3579     ///    sets alerts and begins actions.  Blocking and frame invincibility
3580     ///    are done here too.  Direction is ATK_FRONT if the attack is coming head on,
3581     ///    ATK_RIGHT if from the right, ATK_BEHIND if from the back, ATK_LEFT if from the
3582     ///    left.
3583 
3584     int     action;
3585     int     actual_damage, base_damage, max_damage;
3586     chr_t * pchr;
3587     cap_t * pcap;
3588     bool_t do_feedback = ( FEEDBACK_OFF != cfg.feedback );
3589     bool_t friendly_fire = bfalse, immune_to_damage = bfalse;
3590     Uint8  damage_modifier = 0;
3591 
3592     // what to do is damagetype == NONE?
3593 
3594     if ( !INGAME_CHR( character ) ) return 0;
3595     pchr = ChrList.lst + character;
3596 
3597     pcap = pro_get_pcap( pchr->profile_ref );
3598     if ( NULL == pcap ) return 0;
3599 
3600     // make a special exception for DAMAGE_NONE
3601     damage_modifier = ( damagetype >= DAMAGE_COUNT ) ? 0 : pchr->damage_modifier[damagetype];
3602 
3603     //Don't continue if there is no damage or the character isn't alive
3604     max_damage = ABS( damage.base ) + ABS( damage.rand );
3605     if ( !pchr->alive || 0 == max_damage ) return 0;
3606 
3607     // determine some optional behavior
3608     if ( !INGAME_CHR( attacker ) )
3609     {
3610         do_feedback = bfalse;
3611     }
3612     else
3613     {
3614         // do not show feedback for damaging yourself
3615         if ( attacker == character )
3616         {
3617             do_feedback = bfalse;
3618         }
3619 
3620         // identify friendly fire for color selection :)
3621         if ( chr_get_iteam( character ) == chr_get_iteam( attacker ) )
3622         {
3623             friendly_fire = btrue;
3624         }
3625 
3626         // don't show feedback from random objects hitting each other
3627         if ( !ChrList.lst[attacker].StatusList_on )
3628         {
3629             do_feedback = bfalse;
3630         }
3631 
3632         // don't show damage to players since they get feedback from the status bars
3633         if ( pchr->StatusList_on || VALID_PLA( pchr->is_which_player ) )
3634         {
3635             do_feedback = bfalse;
3636         }
3637     }
3638 
3639     // Lessen actual_damage for resistance, 0 = Weakness, 1 = Normal, 2 = Resist, 3 = Big Resist
3640     // This can also be used to lessen effectiveness of healing
3641     actual_damage = generate_irand_pair( damage );
3642     base_damage   = actual_damage;
3643     actual_damage = actual_damage >> GET_DAMAGE_RESIST( damage_modifier );
3644 
3645     // Increase electric damage when in water
3646     if ( damagetype == DAMAGE_ZAP && chr_is_over_water( pchr ) )
3647     {
3648         // Only if actually in the water
3649         if ( pchr->pos.z <= water.surface_level )
3650             actual_damage = actual_damage << 1;     //@note: ZF> Is double damage too much?
3651     }
3652 
3653     // Allow actual_damage to be dealt to mana (mana shield spell)
3654     if ( HAS_SOME_BITS( damage_modifier, DAMAGEMANA ) )
3655     {
3656         int manadamage;
3657         manadamage = MAX( pchr->mana - actual_damage, 0 );
3658         pchr->mana = manadamage;
3659         actual_damage -= manadamage;
3660         if ( pchr->ai.index != attacker )
3661         {
3662             SET_BIT( pchr->ai.alert, ALERTIF_ATTACKED );
3663             pchr->ai.attacklast = attacker;
3664         }
3665     }
3666 
3667     // Allow charging (Invert actual_damage to mana)
3668     if ( HAS_SOME_BITS( damage_modifier, DAMAGECHARGE ) )
3669     {
3670         pchr->mana += actual_damage;
3671         if ( pchr->mana > pchr->manamax )
3672         {
3673             pchr->mana = pchr->manamax;
3674         }
3675         return 0;
3676     }
3677 
3678     // Invert actual_damage to heal
3679     if ( HAS_SOME_BITS( damage_modifier, DAMAGEINVERT ) )
3680         actual_damage = -actual_damage;
3681 
3682     // Remember the actual_damage type
3683     pchr->ai.damagetypelast = damagetype;
3684     pchr->ai.directionlast  = direction;
3685 
3686     // Check for characters who are immune to this damage, no need to continue if they have
3687     immune_to_damage = ( actual_damage > 0 && actual_damage <= pchr->damage_threshold ) || HAS_SOME_BITS( damage_modifier, DAMAGEINVICTUS );
3688     if ( immune_to_damage )
3689     {
3690         //Dark green text
3691         const float lifetime = 3;
3692         SDL_Color text_color = {0xFF, 0xFF, 0xFF, 0xFF};
3693         GLXvector4f tint  = { 0.0f, 0.5f, 0.00f, 1.00f };
3694 
3695         actual_damage = 0;
3696         spawn_defense_ping( pchr, attacker );
3697 
3698         //Character is simply immune to the damage
3699         chr_make_text_billboard( character, "Immune!", text_color, tint, lifetime, bb_opt_all );
3700     }
3701 
3702     // Do it already
3703     if ( actual_damage > 0 )
3704     {
3705         // Only actual_damage if not invincible
3706         if ( 0 == pchr->damage_timer || ignore_invictus )
3707         {
3708             // Hard mode deals 25% extra actual damage to players!
3709             if ( cfg.difficulty >= GAME_HARD && VALID_PLA( pchr->is_which_player ) && !VALID_PLA( ChrList.lst[attacker].is_which_player ) ) actual_damage *= 1.25f;
3710 
3711             // Easy mode deals 25% extra actual damage by players and 25% less to players
3712             if ( cfg.difficulty <= GAME_EASY )
3713             {
3714                 if ( VALID_PLA( ChrList.lst[attacker].is_which_player ) && !VALID_PLA( pchr->is_which_player ) ) actual_damage *= 1.25f;
3715                 if ( !VALID_PLA( ChrList.lst[attacker].is_which_player ) &&  VALID_PLA( pchr->is_which_player ) ) actual_damage *= 0.75f;
3716             }
3717 
3718             if ( 0 != actual_damage )
3719             {
3720                 if ( HAS_NO_BITS( DAMFX_ARMO, effects ) )
3721                 {
3722                     actual_damage *= pchr->defense * INV_FF;
3723                 }
3724 
3725                 pchr->life -= actual_damage;
3726 
3727                 // Spawn blud particles
3728                 if ( pcap->blud_valid )
3729                 {
3730                     if ( pcap->blud_valid == ULTRABLUDY || ( base_damage > HURTDAMAGE && damagetype < DAMAGE_HOLY ) )
3731                     {
3732                         spawn_one_particle( pchr->pos, pchr->ori.facing_z + direction, pchr->profile_ref, pcap->blud_lpip,
3733                                             ( CHR_REF )MAX_CHR, GRIP_LAST, pchr->team, character, ( PRT_REF )MAX_PRT, 0, ( CHR_REF )MAX_CHR );
3734                     }
3735                 }
3736 
3737                 // Set attack alert if it wasn't an accident
3738                 if ( base_damage > HURTDAMAGE )
3739                 {
3740                     if ( team == TEAM_DAMAGE )
3741                     {
3742                         pchr->ai.attacklast = ( CHR_REF )MAX_CHR;
3743                     }
3744                     else
3745                     {
3746                         // Don't alert the character too much if under constant fire
3747                         if ( 0 == pchr->careful_timer )
3748                         {
3749                             // Don't let characters chase themselves...  That would be silly
3750                             if ( attacker != character )
3751                             {
3752                                 SET_BIT( pchr->ai.alert, ALERTIF_ATTACKED );
3753                                 pchr->ai.attacklast = attacker;
3754                                 pchr->careful_timer = CAREFULTIME;
3755                             }
3756                         }
3757                     }
3758                 }
3759 
3760                 // Taking actual_damage action
3761                 if ( pchr->life <= 0 )
3762                 {
3763                     kill_character( character, attacker, ignore_invictus );
3764                 }
3765                 else
3766                 {
3767                     action = ACTION_HA;
3768                     if ( base_damage > HURTDAMAGE )
3769                     {
3770                         action += generate_randmask( 0, 3 );
3771                         chr_play_action( pchr, action, bfalse );
3772 
3773                         // Make the character invincible for a limited time only
3774                         if ( HAS_NO_BITS( effects, DAMFX_TIME ) )
3775                         {
3776                             pchr->damage_timer = DAMAGETIME;
3777                         }
3778                     }
3779                 }
3780             }
3781 
3782             /// @test spawn a fly-away damage indicator?
3783             if ( do_feedback )
3784             {
3785                 const char * tmpstr;
3786                 int rank;
3787 
3788                 //tmpstr = describe_wounds( pchr->lifemax, pchr->life );
3789 
3790                 tmpstr = describe_value( actual_damage, INT_TO_FP8( 10 ), &rank );
3791                 if ( rank < 4 )
3792                 {
3793                     tmpstr = describe_value( actual_damage, max_damage, &rank );
3794                     if ( rank < 0 )
3795                     {
3796                         tmpstr = "Fumble!";
3797                     }
3798                     else
3799                     {
3800                         tmpstr = describe_damage( actual_damage, pchr->lifemax, &rank );
3801                         if ( rank >= -1 && rank <= 1 )
3802                         {
3803                             tmpstr = describe_wounds( pchr->lifemax, pchr->life );
3804                         }
3805                     }
3806                 }
3807 
3808                 if ( NULL != tmpstr )
3809                 {
3810                     const int lifetime = 3;
3811                     STRING text_buffer = EMPTY_CSTR;
3812 
3813                     // "white" text
3814                     SDL_Color text_color = {0xFF, 0xFF, 0xFF, 0xFF};
3815 
3816                     // friendly fire damage = "purple"
3817                     GLXvector4f tint_friend = { 0.88f, 0.75f, 1.00f, 1.00f };
3818 
3819                     // enemy damage = "red"
3820                     GLXvector4f tint_enemy  = { 1.00f, 0.75f, 0.75f, 1.00f };
3821 
3822                     // write the string into the buffer
3823                     snprintf( text_buffer, SDL_arraysize( text_buffer ), "%s", tmpstr );
3824 
3825                     chr_make_text_billboard( character, text_buffer, text_color, friendly_fire ? tint_friend : tint_enemy, lifetime, bb_opt_all );
3826                 }
3827             }
3828         }
3829     }
3830 
3831     // Heal 'em instead
3832     else if ( actual_damage < 0 )
3833     {
3834         heal_character( character, attacker, actual_damage, ignore_invictus );
3835 
3836         // Isssue an alert
3837         if ( team == TEAM_DAMAGE )
3838         {
3839             pchr->ai.attacklast = ( CHR_REF )MAX_CHR;
3840         }
3841 
3842         /// @test spawn a fly-away heal indicator?
3843         if ( do_feedback )
3844         {
3845             const float lifetime = 3;
3846             STRING text_buffer = EMPTY_CSTR;
3847 
3848             // "white" text
3849             SDL_Color text_color = {0xFF, 0xFF, 0xFF, 0xFF};
3850 
3851             // heal == yellow, right ;)
3852             GLXvector4f tint = { 1.00f, 1.00f, 0.75f, 1.00f };
3853 
3854             // write the string into the buffer
3855             snprintf( text_buffer, SDL_arraysize( text_buffer ), "%s", describe_value( -actual_damage, damage.base + damage.rand, NULL ) );
3856 
3857             chr_make_text_billboard( character, text_buffer, text_color, tint, lifetime, bb_opt_all );
3858         }
3859     }
3860 
3861     return actual_damage;
3862 }
3863 
3864 //--------------------------------------------------------------------------------------------
spawn_defense_ping(chr_t * pchr,const CHR_REF attacker)3865 void spawn_defense_ping( chr_t *pchr, const CHR_REF attacker )
3866 {
3867     /// @details ZF@> Spawn a defend particle
3868     if ( 0 != pchr->damage_timer ) return;
3869 
3870     spawn_one_particle_global( pchr->pos, pchr->ori.facing_z, PIP_DEFEND, 0 );
3871 
3872     pchr->damage_timer    = DEFENDTIME;
3873     SET_BIT( pchr->ai.alert, ALERTIF_BLOCKED );
3874     pchr->ai.attacklast = attacker;                 // For the ones attacking a shield
3875 }
3876 
3877 //--------------------------------------------------------------------------------------------
spawn_poof(const CHR_REF character,const PRO_REF profile)3878 void spawn_poof( const CHR_REF character, const PRO_REF profile )
3879 {
3880     /// @details ZZ@> This function spawns a character poof
3881 
3882     FACING_T facing_z;
3883     CHR_REF  origin;
3884     int      cnt;
3885 
3886     chr_t * pchr;
3887     cap_t * pcap;
3888 
3889     if ( !INGAME_CHR( character ) ) return;
3890     pchr = ChrList.lst + character;
3891 
3892     pcap = pro_get_pcap( profile );
3893     if ( NULL == pcap ) return;
3894 
3895     origin = pchr->ai.owner;
3896     facing_z   = pchr->ori.facing_z;
3897     for ( cnt = 0; cnt < pcap->gopoofprt_amount; cnt++ )
3898     {
3899         spawn_one_particle( pchr->pos_old, facing_z, profile, pcap->gopoofprt_lpip,
3900                             ( CHR_REF )MAX_CHR, GRIP_LAST, pchr->team, origin, ( PRT_REF )MAX_PRT, cnt, ( CHR_REF )MAX_CHR );
3901 
3902         facing_z += pcap->gopoofprt_facingadd;
3903     }
3904 }
3905 
3906 //--------------------------------------------------------------------------------------------
ai_state_spawn(ai_state_t * pself,const CHR_REF index,const PRO_REF iobj,Uint16 rank)3907 void ai_state_spawn( ai_state_t * pself, const CHR_REF index, const PRO_REF iobj, Uint16 rank )
3908 {
3909     chr_t * pchr;
3910     pro_t * ppro;
3911     cap_t * pcap;
3912 
3913     pself = ai_state_ctor( pself );
3914 
3915     if ( NULL == pself || !DEFINED_CHR( index ) ) return;
3916     pchr = ChrList.lst + index;
3917 
3918     // a character cannot be spawned without a valid profile
3919     if ( !LOADED_PRO( iobj ) ) return;
3920     ppro = ProList.lst + iobj;
3921 
3922     // a character cannot be spawned without a valid cap
3923     pcap = pro_get_pcap( iobj );
3924     if ( NULL == pcap ) return;
3925 
3926     pself->index      = index;
3927     pself->type       = ppro->iai;
3928     pself->alert      = ALERTIF_SPAWNED;
3929     pself->state      = pcap->state_override;
3930     pself->content    = pcap->content_override;
3931     pself->passage    = 0;
3932     pself->target     = index;
3933     pself->owner      = index;
3934     pself->child      = index;
3935     pself->target_old = index;
3936 
3937     pself->bumplast   = index;
3938     pself->hitlast    = index;
3939 
3940     pself->order_counter = rank;
3941     pself->order_value   = 0;
3942 }
3943 
3944 //--------------------------------------------------------------------------------------------
chr_get_environment(chr_t * pchr)3945 bool_t chr_get_environment( chr_t * pchr )
3946 {
3947     if ( NULL == pchr ) return bfalse;
3948 
3949     move_one_character_get_environment( pchr );
3950 
3951     return btrue;
3952 }
3953 
3954 //--------------------------------------------------------------------------------------------
3955 //--------------------------------------------------------------------------------------------
chr_config_do_init(chr_t * pchr)3956 chr_t * chr_config_do_init( chr_t * pchr )
3957 {
3958     CHR_REF  ichr;
3959     CAP_REF  icap;
3960     TEAM_REF loc_team;
3961     int      tnc, iteam, kursechance;
3962 
3963     cap_t * pcap;
3964     fvec3_t pos_tmp;
3965 
3966     if ( NULL == pchr ) return NULL;
3967     ichr = GET_INDEX_PCHR( pchr );
3968 
3969     // get the character profile pointer
3970     pcap = pro_get_pcap( pchr->spawn_data.profile );
3971     if ( NULL == pcap )
3972     {
3973         log_debug( "chr_config_do_init() - cannot initialize character.\n" );
3974 
3975         return NULL;
3976     }
3977 
3978     // get the character profile index
3979     icap = pro_get_icap( pchr->spawn_data.profile );
3980 
3981     // turn the character on here. you can't fail to spawn after this point.
3982     POBJ_ACTIVATE( pchr, pcap->name );
3983 
3984     // make a copy of the data in pchr->spawn_data.pos
3985     pos_tmp = pchr->spawn_data.pos;
3986 
3987     // download all the values from the character pchr->spawn_data.profile
3988     chr_download_cap( pchr, pcap );
3989 
3990     // Make sure the pchr->spawn_data.team is valid
3991     loc_team = pchr->spawn_data.team;
3992     iteam = REF_TO_INT( loc_team );
3993     iteam = CLIP( iteam, 0, TEAM_MAX );
3994     loc_team = ( TEAM_REF )iteam;
3995 
3996     // IMPORTANT!!!
3997     pchr->missilehandler = ichr;
3998 
3999     // Set up model stuff
4000     pchr->profile_ref   = pchr->spawn_data.profile;
4001     pchr->basemodel_ref = pchr->spawn_data.profile;
4002 
4003     // Kurse state
4004     if ( pcap->isitem )
4005     {
4006         IPair loc_rand = {1, 100};
4007 
4008         kursechance = pcap->kursechance;
4009         if ( cfg.difficulty >= GAME_HARD )                        kursechance *= 2.0f;  // Hard mode doubles chance for Kurses
4010         if ( cfg.difficulty < GAME_NORMAL && kursechance != 100 ) kursechance *= 0.5f;  // Easy mode halves chance for Kurses
4011         pchr->iskursed = ( generate_irand_pair( loc_rand ) <= kursechance );
4012     }
4013 
4014     // AI stuff
4015     ai_state_spawn( &( pchr->ai ), ichr, pchr->profile_ref, TeamStack.lst[loc_team].morale );
4016 
4017     // Team stuff
4018     pchr->team     = loc_team;
4019     pchr->baseteam = loc_team;
4020     if ( !pchr->invictus )  TeamStack.lst[loc_team].morale++;
4021 
4022     // Firstborn becomes the leader
4023     if ( TeamStack.lst[loc_team].leader == NOLEADER )
4024     {
4025         TeamStack.lst[loc_team].leader = ichr;
4026     }
4027 
4028     // Skin
4029     if ( NO_SKIN_OVERRIDE != pcap->skin_override )
4030     {
4031         // override the value passed into the function from spawn.txt
4032         // with the value from the expansion in data.txt
4033         pchr->spawn_data.skin = pchr->skin;
4034     }
4035     if ( pchr->spawn_data.skin >= ProList.lst[pchr->spawn_data.profile].skins )
4036     {
4037         // place this here so that the random number generator advances
4038         // no matter the state of ProList.lst[pchr->spawn_data.profile].skins... Eases
4039         // possible synch problems with other systems?
4040         int irand = RANDIE;
4041 
4042         pchr->spawn_data.skin = 0;
4043         if ( 0 != ProList.lst[pchr->spawn_data.profile].skins )
4044         {
4045             pchr->spawn_data.skin = irand % ProList.lst[pchr->spawn_data.profile].skins;
4046         }
4047     }
4048     pchr->skin = pchr->spawn_data.skin;
4049 
4050     // fix the pchr->spawn_data.skin-related parameters, in case there was some funy business with overriding
4051     // the pchr->spawn_data.skin from the data.txt file
4052     if ( pchr->spawn_data.skin != pchr->skin )
4053     {
4054         pchr->skin = pchr->spawn_data.skin;
4055 
4056         pchr->defense = pcap->defense[pchr->skin];
4057         for ( tnc = 0; tnc < DAMAGE_COUNT; tnc++ )
4058         {
4059             pchr->damage_modifier[tnc] = pcap->damage_modifier[tnc][pchr->skin];
4060         }
4061 
4062         chr_set_maxaccel( pchr, pcap->maxaccel[pchr->skin] );
4063     }
4064 
4065     // override the default behavior for an "easy" game
4066     if ( cfg.difficulty < GAME_NORMAL )
4067     {
4068         pchr->life = pchr->lifemax;
4069         pchr->mana = pchr->manamax;
4070     }
4071 
4072     // Character size and bumping
4073     pchr->fat_goto      = pchr->fat;
4074     pchr->fat_goto_time = 0;
4075 
4076     // grab all of the environment information
4077     chr_get_environment( pchr );
4078 
4079     chr_set_pos( pchr, pos_tmp.v );
4080 
4081     pchr->pos_stt  = pos_tmp;
4082     pchr->pos_old  = pos_tmp;
4083 
4084     pchr->ori.facing_z     = pchr->spawn_data.facing;
4085     pchr->ori_old.facing_z = pchr->ori.facing_z;
4086 
4087     // Name the character
4088     if ( CSTR_END == pchr->spawn_data.name[0] )
4089     {
4090         // Generate a random pchr->spawn_data.name
4091         snprintf( pchr->Name, SDL_arraysize( pchr->Name ), "%s", pro_create_chop( pchr->spawn_data.profile ) );
4092     }
4093     else
4094     {
4095         // A pchr->spawn_data.name has been given
4096         tnc = 0;
4097 
4098         while ( tnc < MAXCAPNAMESIZE - 1 )
4099         {
4100             pchr->Name[tnc] = pchr->spawn_data.name[tnc];
4101             tnc++;
4102         }
4103 
4104         pchr->Name[tnc] = CSTR_END;
4105     }
4106 
4107     // Particle attachments
4108     for ( tnc = 0; tnc < pcap->attachedprt_amount; tnc++ )
4109     {
4110         spawn_one_particle( pchr->pos, pchr->ori.facing_z, pchr->profile_ref, pcap->attachedprt_lpip,
4111                             ichr, GRIP_LAST + tnc, pchr->team, ichr, ( PRT_REF )MAX_PRT, tnc, ( CHR_REF )MAX_CHR );
4112     }
4113 
4114     // is the object part of a shop's inventory?
4115     if ( pchr->isitem )
4116     {
4117         SHOP_REF ishop;
4118 
4119         // Items that are spawned inside shop passages are more expensive than normal
4120         pchr->isshopitem = bfalse;
4121         for ( ishop = 0; ishop < ShopStack.count; ishop++ )
4122         {
4123             // Make sure the owner is not dead
4124             if ( SHOP_NOOWNER == ShopStack.lst[ishop].owner ) continue;
4125 
4126             if ( object_is_in_passage( ShopStack.lst[ishop].passage, pchr->pos.x, pchr->pos.y, pchr->bump_1.size ) )
4127             {
4128                 pchr->isshopitem = btrue;               // Full value
4129                 pchr->iskursed   = bfalse;              // Shop items are never kursed
4130                 pchr->nameknown  = btrue;
4131                 break;
4132             }
4133         }
4134     }
4135 
4136     /// ZF@> override the shopitem flag if the item is known to be valuable
4137     /// BB@> this prevents (essentially) all books from being able to be burned
4138     //if ( pcap->isvaluable )
4139     //{
4140     //    pchr->isshopitem = btrue;
4141     //}
4142 
4143     // initalize the character instance
4144     chr_instance_spawn( &( pchr->inst ), pchr->spawn_data.profile, pchr->spawn_data.skin );
4145     chr_update_matrix( pchr, btrue );
4146 
4147     // determine whether the object is hidden
4148     chr_update_hide( pchr );
4149 
4150     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, btrue );
4151 
4152 #if defined(_DEBUG) && defined(DEBUG_WAYPOINTS)
4153     if ( DEFINED_CHR( pchr->attachedto ) && CHR_INFINITE_WEIGHT != pchr->phys.weight && !pchr->safe_valid )
4154     {
4155         log_warning( "spawn_one_character() - \n\tinitial spawn position <%f,%f> is \"inside\" a wall. Wall normal is <%f,%f>\n",
4156                      pchr->pos.x, pchr->pos.y, nrm.x, nrm.y );
4157     }
4158 #endif
4159 
4160     return pchr;
4161 }
4162 
4163 //--------------------------------------------------------------------------------------------
chr_config_do_active(chr_t * pchr)4164 chr_t * chr_config_do_active( chr_t * pchr )
4165 {
4166     cap_t * pcap;
4167     int     ripand;
4168     CHR_REF ichr;
4169     float water_level = 0.0f;
4170 
4171     if ( NULL == pchr ) return pchr;
4172     ichr = GET_REF_PCHR( pchr );
4173 
4174     //then do status updates
4175     chr_update_hide( pchr );
4176 
4177     //Don't do items that are in inventory
4178     if ( pchr->pack.is_packed ) return pchr;
4179 
4180     pcap = pro_get_pcap( pchr->profile_ref );
4181     if ( NULL == pcap ) return pchr;
4182 
4183     water_level = water.layer[0].z + water.layer[0].amp;
4184     if ( cfg.twolayerwater_allowed )
4185     {
4186         int cnt;
4187 
4188         for ( cnt = 1; cnt < MAXWATERLAYER; cnt++ )
4189         {
4190             water_level = MAX( water_level, water.layer[cnt].z + water.layer[cnt].amp );
4191         }
4192     }
4193 
4194     // do the character interaction with water
4195     if ( !pchr->is_hidden && pchr->pos.z < water_level && ( 0 != mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_WATER ) ) )
4196     {
4197         // do splash and ripple
4198         if ( !pchr->enviro.inwater )
4199         {
4200             // Splash
4201             fvec3_t vtmp = VECT3( pchr->pos.x, pchr->pos.y, water_level + RAISE );
4202 
4203             spawn_one_particle_global( vtmp, ATK_FRONT, PIP_SPLASH, 0 );
4204 
4205             if ( water.is_water )
4206             {
4207                 SET_BIT( pchr->ai.alert, ALERTIF_INWATER );
4208             }
4209         }
4210         else
4211         {
4212             // Ripples
4213             if ( !INGAME_CHR( pchr->attachedto ) && pcap->ripple && pchr->pos.z + pchr->chr_min_cv.maxs[OCT_Z] + RIPPLETOLERANCE > water_level && pchr->pos.z + pchr->chr_min_cv.mins[OCT_Z] < water_level )
4214             {
4215                 int ripple_suppression;
4216 
4217                 // suppress ripples if we are far below the surface
4218                 ripple_suppression = water_level - ( pchr->pos.z + pchr->chr_min_cv.maxs[OCT_Z] );
4219                 ripple_suppression = ( 4 * ripple_suppression ) / RIPPLETOLERANCE;
4220                 ripple_suppression = CLIP( ripple_suppression, 0, 4 );
4221 
4222                 // make more ripples if we are moving
4223                 ripple_suppression -= (( int )pchr->vel.x != 0 ) | (( int )pchr->vel.y != 0 );
4224 
4225                 if ( ripple_suppression > 0 )
4226                 {
4227                     ripand = ~(( ~RIPPLEAND ) << ripple_suppression );
4228                 }
4229                 else
4230                 {
4231                     ripand = RIPPLEAND >> ( -ripple_suppression );
4232                 }
4233 
4234                 if ( 0 == (( update_wld + pchr->obj_base.guid ) & ripand ) && pchr->pos.z < water_level && pchr->alive )
4235                 {
4236                     fvec3_t   vtmp = VECT3( pchr->pos.x, pchr->pos.y, water_level );
4237 
4238                     spawn_one_particle_global( vtmp, ATK_FRONT, PIP_RIPPLE, 0 );
4239                 }
4240             }
4241 
4242             if ( water.is_water && HAS_NO_BITS( update_wld, 7 ) )
4243             {
4244                 pchr->jumpready = btrue;
4245                 pchr->jumpnumber = 1;
4246             }
4247         }
4248 
4249         pchr->enviro.inwater  = btrue;
4250     }
4251     else
4252     {
4253         pchr->enviro.inwater = bfalse;
4254     }
4255 
4256     // the following functions should not be done the first time through the update loop
4257     if ( 0 == update_wld ) return pchr;
4258 
4259     //---- Do timers and such
4260 
4261     // reduce attack cooldowns
4262     if ( pchr->reload_timer > 0 ) pchr->reload_timer--;
4263 
4264     // decrement the dismount timer
4265     if ( pchr->dismount_timer > 0 ) pchr->dismount_timer--;
4266 
4267     if ( 0 == pchr->dismount_timer )
4268     {
4269         pchr->dismount_object = ( CHR_REF )MAX_CHR;
4270     }
4271 
4272     // Down that ol' damage timer
4273     if ( pchr->damage_timer > 0 ) pchr->damage_timer--;
4274 
4275     // Do "Be careful!" delay
4276     if ( pchr->careful_timer > 0 ) pchr->careful_timer--;
4277 
4278     // Texture movement
4279     pchr->inst.uoffset += pchr->uoffvel;
4280     pchr->inst.voffset += pchr->voffvel;
4281 
4282     // Do stats once every second
4283     if ( clock_chr_stat >= ONESECOND )
4284     {
4285         // check for a level up
4286         do_level_up( ichr );
4287 
4288         // do the mana and life regen for "living" characters
4289         if ( pchr->alive )
4290         {
4291             int manaregen = 0;
4292             int liferegen = 0;
4293             get_chr_regeneration( pchr, &liferegen, &manaregen );
4294 
4295             pchr->mana += manaregen;
4296             pchr->mana = MAX( 0, MIN( pchr->mana, pchr->manamax ) );
4297 
4298             pchr->life += liferegen;
4299             pchr->life = MAX( 1, MIN( pchr->life, pchr->lifemax ) );
4300         }
4301 
4302         // countdown confuse effects
4303         if ( pchr->grog_timer > 0 )
4304         {
4305             pchr->grog_timer--;
4306         }
4307 
4308         if ( pchr->daze_timer > 0 )
4309         {
4310             pchr->daze_timer--;
4311         }
4312 
4313         // possibly gain/lose darkvision
4314         update_chr_darkvision( ichr );
4315     }
4316 
4317     pchr = resize_one_character( pchr );
4318 
4319     // update some special skills
4320     pchr->see_kurse_level  = MAX( pchr->see_kurse_level,  chr_get_skill( pchr, MAKE_IDSZ( 'C', 'K', 'U', 'R' ) ) );
4321     pchr->darkvision_level = MAX( pchr->darkvision_level, chr_get_skill( pchr, MAKE_IDSZ( 'D', 'A', 'R', 'K' ) ) );
4322 
4323     return pchr;
4324 }
4325 
4326 //--------------------------------------------------------------------------------------------
4327 //--------------------------------------------------------------------------------------------
chr_config_construct(chr_t * pchr,int max_iterations)4328 chr_t * chr_config_construct( chr_t * pchr, int max_iterations )
4329 {
4330     int          iterations;
4331     obj_data_t * pbase;
4332 
4333     if ( NULL == pchr ) return NULL;
4334 
4335     pbase = POBJ_GET_PBASE( pchr );
4336     if ( !pbase->allocated ) return NULL;
4337 
4338     // if the character is already beyond this stage, deconstruct it and start over
4339     if ( pbase->state > ( int )( ego_object_constructing + 1 ) )
4340     {
4341         chr_t * tmp_chr = chr_config_deconstruct( pchr, max_iterations );
4342         if ( tmp_chr == pchr ) return NULL;
4343     }
4344 
4345     iterations = 0;
4346     while ( NULL != pchr && pbase->state <= ego_object_constructing && iterations < max_iterations )
4347     {
4348         chr_t * ptmp = chr_run_config( pchr );
4349         if ( ptmp != pchr ) return NULL;
4350         iterations++;
4351     }
4352 
4353     return pchr;
4354 }
4355 
4356 //--------------------------------------------------------------------------------------------
chr_config_initialize(chr_t * pchr,int max_iterations)4357 chr_t * chr_config_initialize( chr_t * pchr, int max_iterations )
4358 {
4359     int          iterations;
4360     obj_data_t * pbase;
4361 
4362     if ( NULL == pchr )  return NULL;
4363 
4364     pbase = POBJ_GET_PBASE( pchr );
4365     if ( !pbase->allocated ) return NULL;
4366 
4367     // if the character is already beyond this stage, deconstruct it and start over
4368     if ( pbase->state > ( int )( ego_object_initializing + 1 ) )
4369     {
4370         chr_t * tmp_chr = chr_config_deconstruct( pchr, max_iterations );
4371         if ( tmp_chr == pchr ) return NULL;
4372     }
4373 
4374     iterations = 0;
4375     while ( NULL != pchr && pbase->state <= ego_object_initializing && iterations < max_iterations )
4376     {
4377         chr_t * ptmp = chr_run_config( pchr );
4378         if ( ptmp != pchr ) return NULL;
4379         iterations++;
4380     }
4381 
4382     return pchr;
4383 }
4384 
4385 //--------------------------------------------------------------------------------------------
chr_config_activate(chr_t * pchr,int max_iterations)4386 chr_t * chr_config_activate( chr_t * pchr, int max_iterations )
4387 {
4388     int          iterations;
4389     obj_data_t * pbase;
4390 
4391     if ( NULL == pchr ) return NULL;
4392 
4393     pbase = POBJ_GET_PBASE( pchr );
4394     if ( !pbase->allocated ) return NULL;
4395 
4396     // if the character is already beyond this stage, deconstruct it and start over
4397     if ( pbase->state > ( int )( ego_object_active + 1 ) )
4398     {
4399         chr_t * tmp_chr = chr_config_deconstruct( pchr, max_iterations );
4400         if ( tmp_chr == pchr ) return NULL;
4401     }
4402 
4403     iterations = 0;
4404     while ( NULL != pchr && pbase->state < ego_object_active && iterations < max_iterations )
4405     {
4406         chr_t * ptmp = chr_run_config( pchr );
4407         if ( ptmp != pchr ) return NULL;
4408         iterations++;
4409     }
4410 
4411     EGOBOO_ASSERT( pbase->state == ego_object_active );
4412     if ( pbase->state == ego_object_active )
4413     {
4414         ChrList_add_used( GET_INDEX_PCHR( pchr ) );
4415     }
4416 
4417     return pchr;
4418 }
4419 
4420 //--------------------------------------------------------------------------------------------
chr_config_deinitialize(chr_t * pchr,int max_iterations)4421 chr_t * chr_config_deinitialize( chr_t * pchr, int max_iterations )
4422 {
4423     int          iterations;
4424     obj_data_t * pbase;
4425 
4426     if ( NULL == pchr ) return NULL;
4427 
4428     pbase = POBJ_GET_PBASE( pchr );
4429     if ( !pbase->allocated ) return NULL;
4430 
4431     // if the character is already beyond this stage, deinitialize it
4432     if ( pbase->state > ( int )( ego_object_deinitializing + 1 ) )
4433     {
4434         return pchr;
4435     }
4436     else if ( pbase->state < ego_object_deinitializing )
4437     {
4438         pbase->state = ego_object_deinitializing;
4439     }
4440 
4441     iterations = 0;
4442     while ( NULL != pchr && pbase->state <= ego_object_deinitializing && iterations < max_iterations )
4443     {
4444         chr_t * ptmp = chr_run_config( pchr );
4445         if ( ptmp != pchr ) return NULL;
4446         iterations++;
4447     }
4448 
4449     return pchr;
4450 }
4451 
4452 //--------------------------------------------------------------------------------------------
chr_config_deconstruct(chr_t * pchr,int max_iterations)4453 chr_t * chr_config_deconstruct( chr_t * pchr, int max_iterations )
4454 {
4455     int          iterations;
4456     obj_data_t * pbase;
4457 
4458     if ( NULL == pchr ) return NULL;
4459 
4460     pbase = POBJ_GET_PBASE( pchr );
4461     if ( !pbase->allocated ) return NULL;
4462 
4463     // if the character is already beyond this stage, do nothing
4464     if ( pbase->state > ( int )( ego_object_destructing + 1 ) )
4465     {
4466         return pchr;
4467     }
4468     else if ( pbase->state < ego_object_deinitializing )
4469     {
4470         // make sure that you deinitialize before destructing
4471         pbase->state = ego_object_deinitializing;
4472     }
4473 
4474     iterations = 0;
4475     while ( NULL != pchr && pbase->state <= ego_object_destructing && iterations < max_iterations )
4476     {
4477         chr_t * ptmp = chr_run_config( pchr );
4478         if ( ptmp != pchr ) return NULL;
4479         iterations++;
4480     }
4481 
4482     return pchr;
4483 }
4484 
4485 //--------------------------------------------------------------------------------------------
4486 //--------------------------------------------------------------------------------------------
chr_run_config(chr_t * pchr)4487 chr_t * chr_run_config( chr_t * pchr )
4488 {
4489     obj_data_t * pbase;
4490 
4491     if ( NULL == pchr ) return NULL;
4492 
4493     pbase = POBJ_GET_PBASE( pchr );
4494     if ( !pbase->allocated ) return NULL;
4495 
4496     // set the object to deinitialize if it is not "dangerous" and if was requested
4497     if ( pbase->kill_me )
4498     {
4499         if ( pbase->state > ego_object_constructing && pbase->state < ego_object_deinitializing )
4500         {
4501             pbase->state = ego_object_deinitializing;
4502         }
4503 
4504         pbase->kill_me = bfalse;
4505     }
4506 
4507     switch ( pbase->state )
4508     {
4509         default:
4510         case ego_object_invalid:
4511             pchr = NULL;
4512             break;
4513 
4514         case ego_object_constructing:
4515             pchr = chr_config_ctor( pchr );
4516             break;
4517 
4518         case ego_object_initializing:
4519             pchr = chr_config_init( pchr );
4520             break;
4521 
4522         case ego_object_active:
4523             pchr = chr_config_active( pchr );
4524             break;
4525 
4526         case ego_object_deinitializing:
4527             pchr = chr_config_deinit( pchr );
4528             break;
4529 
4530         case ego_object_destructing:
4531             pchr = chr_config_dtor( pchr );
4532             break;
4533 
4534         case ego_object_waiting:
4535         case ego_object_terminated:
4536             /* do nothing */
4537             break;
4538     }
4539 
4540     if ( NULL == pchr )
4541     {
4542         pbase->update_guid = INVALID_UPDATE_GUID;
4543     }
4544     else if ( ego_object_active == pbase->state )
4545     {
4546         pbase->update_guid = ChrList.update_guid;
4547     }
4548 
4549     return pchr;
4550 }
4551 
4552 //--------------------------------------------------------------------------------------------
chr_config_ctor(chr_t * pchr)4553 chr_t * chr_config_ctor( chr_t * pchr )
4554 {
4555     /// @details BB@> initialize the character data to safe values
4556     ///     since we use memset(..., 0, ...), all = 0, = false, and = 0.0f
4557     ///     statements are redundant
4558 
4559     obj_data_t * pbase;
4560 
4561     // grab the base object
4562     if ( NULL == pchr ) return NULL;
4563     pbase = POBJ_GET_PBASE( pchr );
4564 
4565     // if we aren't in the correct state, abort.
4566     if ( !STATE_CONSTRUCTING_PBASE( pbase ) ) return pchr;
4567 
4568     pchr = chr_ctor( pchr );
4569     if ( NULL == pchr ) return pchr;
4570 
4571     // we are done constructing. move on to initializing.
4572     pchr->obj_base.state = ego_object_initializing;
4573 
4574     return pchr;
4575 }
4576 
4577 //--------------------------------------------------------------------------------------------
chr_config_init(chr_t * pchr)4578 chr_t * chr_config_init( chr_t * pchr )
4579 {
4580     obj_data_t * pbase;
4581 
4582     if ( NULL == pchr ) return NULL;
4583     pbase = POBJ_GET_PBASE( pchr );
4584 
4585     if ( !STATE_INITIALIZING_PBASE( pbase ) ) return pchr;
4586 
4587     pchr = chr_config_do_init( pchr );
4588     if ( NULL == pchr ) return NULL;
4589 
4590     if ( 0 == chr_loop_depth )
4591     {
4592         pchr->obj_base.on = btrue;
4593     }
4594     else
4595     {
4596         ChrList_add_activation( GET_INDEX_PPRT( pchr ) );
4597     }
4598 
4599     pbase->state = ego_object_active;
4600 
4601     return pchr;
4602 }
4603 
4604 //--------------------------------------------------------------------------------------------
chr_config_active(chr_t * pchr)4605 chr_t * chr_config_active( chr_t * pchr )
4606 {
4607     // there's nothing to configure if the object is active...
4608 
4609     obj_data_t * pbase;
4610 
4611     if ( NULL == pchr ) return NULL;
4612 
4613     pbase = POBJ_GET_PBASE( pchr );
4614 
4615     if ( !pbase->allocated ) return NULL;
4616     if ( !STATE_ACTIVE_PBASE( pbase ) ) return pchr;
4617 
4618     POBJ_END_SPAWN( pchr );
4619 
4620     pchr = chr_config_do_active( pchr );
4621 
4622     return pchr;
4623 }
4624 
4625 //--------------------------------------------------------------------------------------------
chr_config_deinit(chr_t * pchr)4626 chr_t * chr_config_deinit( chr_t * pchr )
4627 {
4628     /// @details BB@> deinitialize the character data
4629 
4630     obj_data_t * pbase;
4631 
4632     if ( NULL == pchr ) return NULL;
4633 
4634     pbase = POBJ_GET_PBASE( pchr );
4635     if ( !STATE_DEINITIALIZING_PBASE( pbase ) ) return pchr;
4636 
4637     POBJ_END_SPAWN( pchr );
4638 
4639     pbase->state = ego_object_destructing;
4640     pbase->on    = bfalse;
4641 
4642     return pchr;
4643 }
4644 
4645 //--------------------------------------------------------------------------------------------
chr_config_dtor(chr_t * pchr)4646 chr_t * chr_config_dtor( chr_t * pchr )
4647 {
4648     /// @details BB@> deinitialize the character data
4649 
4650     obj_data_t * pbase;
4651 
4652     if ( NULL == pchr ) return NULL;
4653 
4654     pbase = POBJ_GET_PBASE( pchr );
4655     if ( !STATE_DESTRUCTING_PBASE( pbase ) ) return pchr;
4656 
4657     POBJ_END_SPAWN( pchr );
4658 
4659     return chr_dtor( pchr );
4660 }
4661 
4662 //--------------------------------------------------------------------------------------------
4663 //--------------------------------------------------------------------------------------------
spawn_one_character(fvec3_t pos,const PRO_REF profile,const TEAM_REF team,Uint8 skin,FACING_T facing,const char * name,const CHR_REF override)4664 CHR_REF spawn_one_character( fvec3_t pos, const PRO_REF profile, const TEAM_REF team,
4665                              Uint8 skin, FACING_T facing, const char *name, const CHR_REF override )
4666 {
4667     /// @details ZZ@> This function spawns a character and returns the character's index number
4668     ///               if it worked, MAX_CHR otherwise
4669 
4670     CHR_REF   ichr;
4671     chr_t   * pchr;
4672     cap_t   * pcap;
4673     pro_t   * ppro;
4674 
4675     // fix a "bad" name
4676     if ( NULL == name ) name = "";
4677 
4678     if ( profile >= MAX_PROFILE )
4679     {
4680         log_warning( "spawn_one_character() - profile value too large %d out of %d\n", REF_TO_INT( profile ), MAX_PROFILE );
4681         return ( CHR_REF )MAX_CHR;
4682     }
4683 
4684     if ( !LOADED_PRO( profile ) )
4685     {
4686         if ( profile > PMod->importamount * MAXIMPORTPERPLAYER )
4687         {
4688             log_warning( "spawn_one_character() - trying to spawn using invalid profile %d\n", REF_TO_INT( profile ) );
4689         }
4690         return ( CHR_REF )MAX_CHR;
4691     }
4692     ppro = ProList.lst + profile;
4693 
4694     if ( !LOADED_CAP( ppro->icap ) )
4695     {
4696         log_debug( "spawn_one_character() - invalid character profile %d\n", ppro->icap );
4697         return ( CHR_REF )MAX_CHR;
4698     }
4699     pcap = CapStack.lst + ppro->icap;
4700 
4701     // count all the requests for this character type
4702     pcap->request_count++;
4703 
4704     // allocate a new character
4705     ichr = ChrList_allocate( override );
4706     if ( !DEFINED_CHR( ichr ) )
4707     {
4708         log_warning( "spawn_one_character() - failed to spawn character (invalid index number %d?)\n", REF_TO_INT( ichr ) );
4709         return ( CHR_REF )MAX_CHR;
4710     }
4711     pchr = ChrList.lst + ichr;
4712 
4713     POBJ_BEGIN_SPAWN( pchr );
4714 
4715     // just set the spawn info
4716     pchr->spawn_data.pos      = pos;
4717     pchr->spawn_data.profile  = profile;
4718     pchr->spawn_data.team     = team;
4719     pchr->spawn_data.skin     = skin;
4720     pchr->spawn_data.facing   = facing;
4721     strncpy( pchr->spawn_data.name, name, SDL_arraysize( pchr->spawn_data.name ) );
4722     pchr->spawn_data.override = override;
4723 
4724     // actually force the character to spawn
4725     pchr = chr_config_activate( pchr, 100 );
4726 
4727     // count all the successful spawns of this character
4728     if ( NULL != pchr )
4729     {
4730         POBJ_END_SPAWN( pchr );
4731         pcap->create_count++;
4732     }
4733 
4734 #if defined(DEBUG_OBJECT_SPAWN) && defined(_DEBUG)
4735     {
4736         CAP_REF icap = pro_get_icap( profile );
4737         log_debug( "spawn_one_character() - slot: %i, index: %i, name: %s, class: %s\n", REF_TO_INT( profile ), REF_TO_INT( ichr ), name, CapStack.lst[icap].classname );
4738     }
4739 #endif
4740 
4741     return ichr;
4742 }
4743 
4744 //--------------------------------------------------------------------------------------------
respawn_character(const CHR_REF character)4745 void respawn_character( const CHR_REF character )
4746 {
4747     /// @details ZZ@> This function respawns a character
4748 
4749     int old_attached_prt_count, new_attached_prt_count;
4750 
4751     chr_t * pchr;
4752     cap_t * pcap;
4753 
4754     if ( !INGAME_CHR( character ) || ChrList.lst[character].alive ) return;
4755     pchr = ChrList.lst + character;
4756 
4757     pcap = chr_get_pcap( character );
4758     if ( NULL == pcap ) return;
4759 
4760     old_attached_prt_count = number_of_attached_particles( character );
4761 
4762     spawn_poof( character, pchr->profile_ref );
4763     disaffirm_attached_particles( character );
4764 
4765     pchr->alive = btrue;
4766     pchr->bore_timer = BORETIME;
4767     pchr->careful_timer = CAREFULTIME;
4768     pchr->life = pchr->lifemax;
4769     pchr->mana = pchr->manamax;
4770     chr_set_pos( pchr, pchr->pos_stt.v );
4771     pchr->vel.x = 0;
4772     pchr->vel.y = 0;
4773     pchr->vel.z = 0;
4774     pchr->team = pchr->baseteam;
4775     pchr->canbecrushed = bfalse;
4776     pchr->ori.map_facing_y = MAP_TURN_OFFSET;  // These two mean on level surface
4777     pchr->ori.map_facing_x = MAP_TURN_OFFSET;
4778     if ( NOLEADER == TeamStack.lst[pchr->team].leader )  TeamStack.lst[pchr->team].leader = character;
4779     if ( !pchr->invictus )  TeamStack.lst[pchr->baseteam].morale++;
4780 
4781     // start the character out in the "dance" animation
4782     chr_start_anim( pchr, ACTION_DA, btrue, btrue );
4783 
4784     // reset all of the bump size information
4785     {
4786         float old_fat = pchr->fat;
4787         chr_init_size( pchr, pcap );
4788         chr_set_fat( pchr, old_fat );
4789     }
4790 
4791     pchr->platform        = pcap->platform;
4792     pchr->canuseplatforms = pcap->canuseplatforms;
4793     pchr->flyheight       = pcap->flyheight;
4794     pchr->phys.bumpdampen = pcap->bumpdampen;
4795 
4796     pchr->ai.alert = ALERTIF_CLEANEDUP;
4797     pchr->ai.target = character;
4798     pchr->ai.timer  = 0;
4799 
4800     pchr->grog_timer = 0;
4801     pchr->daze_timer = 0;
4802 
4803     // Let worn items come back
4804     PACK_BEGIN_LOOP( ipacked, pchr->pack.next )
4805     {
4806         if ( INGAME_CHR( ipacked ) && ChrList.lst[ipacked].isequipped )
4807         {
4808             ChrList.lst[ipacked].isequipped = bfalse;
4809             SET_BIT( chr_get_pai( ipacked )->alert, ALERTIF_PUTAWAY ); // same as ALERTIF_ATLASTWAYPOINT
4810         }
4811     }
4812     PACK_END_LOOP( ipacked );
4813 
4814     // re-initialize the instance
4815     chr_instance_spawn( &( pchr->inst ), pchr->profile_ref, pchr->skin );
4816     chr_update_matrix( pchr, btrue );
4817 
4818     // determine whether the object is hidden
4819     chr_update_hide( pchr );
4820 
4821     if ( !pchr->is_hidden )
4822     {
4823         reaffirm_attached_particles( character );
4824         new_attached_prt_count = number_of_attached_particles( character );
4825     }
4826 
4827     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, btrue );
4828 }
4829 
4830 //--------------------------------------------------------------------------------------------
chr_change_skin(const CHR_REF character,int skin)4831 int chr_change_skin( const CHR_REF character, int skin )
4832 {
4833     chr_t * pchr;
4834     pro_t * ppro;
4835     mad_t * pmad;
4836     chr_instance_t * pinst;
4837     TX_REF new_texture = TX_WATER_TOP;
4838 
4839     if ( !INGAME_CHR( character ) ) return 0;
4840     pchr  = ChrList.lst + character;
4841     pinst = &( pchr->inst );
4842 
4843     ppro = chr_get_ppro( character );
4844 
4845     pmad = pro_get_pmad( pchr->profile_ref );
4846     if ( NULL == pmad )
4847     {
4848         // make sure that the instance has a valid imad
4849         if ( NULL != ppro && !LOADED_MAD( pinst->imad ) )
4850         {
4851             if ( chr_instance_set_mad( pinst, ppro->imad ) )
4852             {
4853                 chr_update_collision_size( pchr, btrue );
4854             }
4855             pmad = chr_get_pmad( character );
4856         }
4857     }
4858 
4859     if ( NULL == pmad )
4860     {
4861         pchr->skin     = 0;
4862         pinst->texture = TX_WATER_TOP;
4863     }
4864     else
4865     {
4866         new_texture = ( TX_REF )TX_WATER_TOP;
4867 
4868         // do the best we can to change the skin
4869         if ( NULL == ppro || 0 == ppro->skins )
4870         {
4871             ppro->skins = 1;
4872             ppro->tex_ref[0] = TX_WATER_TOP;
4873 
4874             skin  = 0;
4875             new_texture = TX_WATER_TOP;
4876         }
4877         else
4878         {
4879             if ( skin > ppro->skins )
4880             {
4881                 skin = 0;
4882             }
4883 
4884             new_texture = ppro->tex_ref[skin];
4885         }
4886 
4887         pchr->skin = skin;
4888     }
4889 
4890     chr_instance_set_texture( pinst, new_texture );
4891 
4892     // If the we are respawning a player, then the camera needs to be reset
4893     // if ( VALID_PLA( pchr->is_which_player ) )
4894     // {
4895     //     camera_reset_target( PCamera, PMesh );
4896     // }
4897 
4898     return pchr->skin;
4899 }
4900 
4901 //--------------------------------------------------------------------------------------------
change_armor(const CHR_REF character,int skin)4902 int change_armor( const CHR_REF character, int skin )
4903 {
4904     /// @details ZZ@> This function changes the armor of the character
4905 
4906     ENC_REF ienc_now, ienc_nxt;
4907     size_t  ienc_count;
4908 
4909     int     iTmp;
4910     cap_t * pcap;
4911     chr_t * pchr;
4912 
4913     if ( !INGAME_CHR( character ) ) return 0;
4914     pchr = ChrList.lst + character;
4915 
4916     // cleanup the enchant list
4917     cleanup_character_enchants( pchr );
4918 
4919     // Remove armor enchantments
4920     ienc_now = pchr->firstenchant;
4921     ienc_count = 0;
4922     while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
4923     {
4924         ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
4925 
4926         enchant_remove_set( ienc_now, SETSLASHMODIFIER );
4927         enchant_remove_set( ienc_now, SETCRUSHMODIFIER );
4928         enchant_remove_set( ienc_now, SETPOKEMODIFIER );
4929         enchant_remove_set( ienc_now, SETHOLYMODIFIER );
4930         enchant_remove_set( ienc_now, SETEVILMODIFIER );
4931         enchant_remove_set( ienc_now, SETFIREMODIFIER );
4932         enchant_remove_set( ienc_now, SETICEMODIFIER );
4933         enchant_remove_set( ienc_now, SETZAPMODIFIER );
4934 
4935         ienc_now = ienc_nxt;
4936         ienc_count++;
4937     }
4938     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
4939 
4940     // Change the skin
4941     pcap = chr_get_pcap( character );
4942     skin = chr_change_skin( character, skin );
4943 
4944     // Change stats associated with skin
4945     pchr->defense = pcap->defense[skin];
4946 
4947     for ( iTmp = 0; iTmp < DAMAGE_COUNT; iTmp++ )
4948     {
4949         pchr->damage_modifier[iTmp] = pcap->damage_modifier[iTmp][skin];
4950     }
4951 
4952     // set the character's maximum acceleration
4953     chr_set_maxaccel( pchr, pcap->maxaccel[skin] );
4954 
4955     // cleanup the enchant list
4956     cleanup_character_enchants( pchr );
4957 
4958     // Reset armor enchantments
4959     /// @todo These should really be done in reverse order ( Start with last enchant ), but
4960     /// I don't care at this point !!!BAD!!!
4961     ienc_now = pchr->firstenchant;
4962     ienc_count = 0;
4963     while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
4964     {
4965         PRO_REF ipro = enc_get_ipro( ienc_now );
4966 
4967         ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
4968 
4969         if ( LOADED_PRO( ipro ) )
4970         {
4971             EVE_REF ieve = pro_get_ieve( ipro );
4972 
4973             enchant_apply_set( ienc_now, SETSLASHMODIFIER, ipro );
4974             enchant_apply_set( ienc_now, SETCRUSHMODIFIER, ipro );
4975             enchant_apply_set( ienc_now, SETPOKEMODIFIER,  ipro );
4976             enchant_apply_set( ienc_now, SETHOLYMODIFIER,  ipro );
4977             enchant_apply_set( ienc_now, SETEVILMODIFIER,  ipro );
4978             enchant_apply_set( ienc_now, SETFIREMODIFIER,  ipro );
4979             enchant_apply_set( ienc_now, SETICEMODIFIER,   ipro );
4980             enchant_apply_set( ienc_now, SETZAPMODIFIER,   ipro );
4981 
4982             enchant_apply_add( ienc_now, ADDACCEL,         ieve );
4983             enchant_apply_add( ienc_now, ADDDEFENSE,       ieve );
4984         }
4985 
4986         ienc_now = ienc_nxt;
4987         ienc_count++;
4988     }
4989     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
4990 
4991     return skin;
4992 }
4993 
4994 //--------------------------------------------------------------------------------------------
change_character_full(const CHR_REF ichr,const PRO_REF profile,Uint8 skin,Uint8 leavewhich)4995 void change_character_full( const CHR_REF ichr, const PRO_REF profile, Uint8 skin, Uint8 leavewhich )
4996 {
4997     /// @details ZF@> This function polymorphs a character permanently so that it can be exported properly
4998     /// A character turned into a frog with this function will also export as a frog!
4999 
5000     MAD_REF imad_old, imad_new;
5001 
5002     if ( !LOADED_PRO( profile ) ) return;
5003 
5004     imad_new = pro_get_imad( profile );
5005     if ( !LOADED_MAD( imad_new ) ) return;
5006 
5007     imad_old = chr_get_imad( ichr );
5008     if ( !LOADED_MAD( imad_old ) ) return;
5009 
5010     // copy the new name
5011     strncpy( MadStack.lst[imad_old].name, MadStack.lst[imad_new].name, SDL_arraysize( MadStack.lst[imad_old].name ) );
5012 
5013     // change their model
5014     change_character( ichr, profile, skin, leavewhich );
5015 
5016     // set the base model to the new model, too
5017     ChrList.lst[ichr].basemodel_ref = profile;
5018 }
5019 
5020 //--------------------------------------------------------------------------------------------
set_weapongrip(const CHR_REF iitem,const CHR_REF iholder,Uint16 vrt_off)5021 bool_t set_weapongrip( const CHR_REF iitem, const CHR_REF iholder, Uint16 vrt_off )
5022 {
5023     int i;
5024 
5025     bool_t needs_update;
5026     Uint16 grip_verts[GRIP_VERTS];
5027 
5028     matrix_cache_t * mcache;
5029     chr_t * pitem;
5030 
5031     needs_update = bfalse;
5032 
5033     if ( !INGAME_CHR( iitem ) ) return bfalse;
5034     pitem = ChrList.lst + iitem;
5035     mcache = &( pitem->inst.matrix_cache );
5036 
5037     // is the item attached to this valid holder?
5038     if ( pitem->attachedto != iholder ) return bfalse;
5039 
5040     needs_update  = btrue;
5041 
5042     if ( GRIP_VERTS == get_grip_verts( grip_verts, iholder, vrt_off ) )
5043     {
5044         //---- detect any changes in the matrix_cache data
5045 
5046         needs_update  = bfalse;
5047 
5048         if ( iholder != mcache->grip_chr || pitem->attachedto != iholder )
5049         {
5050             needs_update  = btrue;
5051         }
5052 
5053         if ( pitem->inwhich_slot != mcache->grip_slot )
5054         {
5055             needs_update  = btrue;
5056         }
5057 
5058         // check to see if any of the
5059         for ( i = 0; i < GRIP_VERTS; i++ )
5060         {
5061             if ( grip_verts[i] != mcache->grip_verts[i] )
5062             {
5063                 needs_update = btrue;
5064                 break;
5065             }
5066         }
5067     }
5068 
5069     if ( needs_update )
5070     {
5071         // cannot create the matrix, therefore the current matrix must be invalid
5072         mcache->matrix_valid = bfalse;
5073 
5074         mcache->grip_chr  = iholder;
5075         mcache->grip_slot = pitem->inwhich_slot;
5076 
5077         for ( i = 0; i < GRIP_VERTS; i++ )
5078         {
5079             mcache->grip_verts[i] = grip_verts[i];
5080         }
5081     }
5082 
5083     return btrue;
5084 }
5085 
5086 //--------------------------------------------------------------------------------------------
change_character(const CHR_REF ichr,const PRO_REF profile_new,Uint8 skin,Uint8 leavewhich)5087 void change_character( const CHR_REF ichr, const PRO_REF profile_new, Uint8 skin, Uint8 leavewhich )
5088 {
5089     /// @details ZZ@> This function polymorphs a character, changing stats, dropping weapons
5090 
5091     int tnc;
5092     CHR_REF item_ref, item;
5093     chr_t * pchr;
5094 
5095     pro_t * pobj_new;
5096     cap_t * pcap_new;
5097     mad_t * pmad_new;
5098 
5099     int old_attached_prt_count, new_attached_prt_count;
5100 
5101     if ( !LOADED_PRO( profile_new ) || !INGAME_CHR( ichr ) ) return;
5102     pchr = ChrList.lst + ichr;
5103 
5104     old_attached_prt_count = number_of_attached_particles( ichr );
5105 
5106     if ( !LOADED_PRO( profile_new ) ) return;
5107     pobj_new = ProList.lst + profile_new;
5108 
5109     pcap_new = pro_get_pcap( profile_new );
5110     pmad_new = pro_get_pmad( profile_new );
5111 
5112     // Drop left weapon
5113     item_ref = pchr->holdingwhich[SLOT_LEFT];
5114     if ( INGAME_CHR( item_ref ) && ( !pcap_new->slotvalid[SLOT_LEFT] || pcap_new->ismount ) )
5115     {
5116         detach_character_from_mount( item_ref, btrue, btrue );
5117         detach_character_from_platform( ChrList.lst + item_ref );
5118 
5119         if ( pchr->ismount )
5120         {
5121             fvec3_t tmp_pos;
5122 
5123             ChrList.lst[item_ref].vel.z    = DISMOUNTZVEL;
5124             ChrList.lst[item_ref].jump_timer = JUMPDELAY;
5125 
5126             tmp_pos = chr_get_pos( ChrList.lst + item_ref );
5127             tmp_pos.z += DISMOUNTZVEL;
5128             chr_set_pos( ChrList.lst + item_ref, tmp_pos.v );
5129         }
5130     }
5131 
5132     // Drop right weapon
5133     item_ref = pchr->holdingwhich[SLOT_RIGHT];
5134     if ( INGAME_CHR( item_ref ) && !pcap_new->slotvalid[SLOT_RIGHT] )
5135     {
5136         detach_character_from_mount( item_ref, btrue, btrue );
5137         detach_character_from_platform( ChrList.lst + item_ref );
5138 
5139         if ( pchr->ismount )
5140         {
5141             fvec3_t tmp_pos;
5142 
5143             ChrList.lst[item_ref].vel.z    = DISMOUNTZVEL;
5144             ChrList.lst[item_ref].jump_timer = JUMPDELAY;
5145 
5146             tmp_pos = chr_get_pos( ChrList.lst + item_ref );
5147             tmp_pos.z += DISMOUNTZVEL;
5148             chr_set_pos( ChrList.lst + item_ref, tmp_pos.v );
5149         }
5150     }
5151 
5152     // Remove particles
5153     disaffirm_attached_particles( ichr );
5154 
5155     // clean up the enchant list before doing anything
5156     cleanup_character_enchants( pchr );
5157 
5158     // Remove enchantments
5159     if ( leavewhich == ENC_LEAVE_FIRST )
5160     {
5161         // Remove all enchantments except top one
5162         if ( MAX_ENC != pchr->firstenchant )
5163         {
5164             ENC_REF ienc_now, ienc_nxt;
5165             size_t  ienc_count;
5166 
5167             ienc_now = EncList.lst[pchr->firstenchant].nextenchant_ref;
5168             ienc_count = 0;
5169             while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
5170             {
5171                 ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
5172 
5173                 remove_enchant( ienc_now, NULL );
5174 
5175                 ienc_now = ienc_nxt;
5176                 ienc_count++;
5177             }
5178             if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
5179         }
5180     }
5181     else if ( ENC_LEAVE_NONE == leavewhich )
5182     {
5183         // Remove all enchantments
5184         disenchant_character( ichr );
5185     }
5186 
5187     // Stuff that must be set
5188     pchr->profile_ref  = profile_new;
5189     pchr->stoppedby = pcap_new->stoppedby;
5190     pchr->life_heal  = pcap_new->life_heal;
5191     pchr->manacost  = pcap_new->manacost;
5192 
5193     // Ammo
5194     pchr->ammomax = pcap_new->ammomax;
5195     pchr->ammo    = pcap_new->ammo;
5196 
5197     // Gender
5198     if ( pcap_new->gender != GENDER_RANDOM )  // GENDER_RANDOM means keep old gender
5199     {
5200         pchr->gender = pcap_new->gender;
5201     }
5202 
5203     // Sound effects
5204     for ( tnc = 0; tnc < SOUND_COUNT; tnc++ )
5205     {
5206         pchr->sound_index[tnc] = pcap_new->sound_index[tnc];
5207     }
5208 
5209     // AI stuff
5210     chr_set_ai_state( pchr, 0 );
5211     pchr->ai.type           = pobj_new->iai;
5212     pchr->ai.timer          = 0;
5213     pchr->turnmode          = TURNMODE_VELOCITY;
5214 
5215     latch_init( &( pchr->latch ) );
5216 
5217     // Flags
5218     pchr->stickybutt      = pcap_new->stickybutt;
5219     pchr->openstuff       = pcap_new->canopenstuff;
5220     pchr->transferblend   = pcap_new->transferblend;
5221     pchr->platform        = pcap_new->platform;
5222     pchr->canuseplatforms = pcap_new->canuseplatforms;
5223     pchr->isitem          = pcap_new->isitem;
5224     pchr->invictus        = pcap_new->invictus;
5225     pchr->ismount         = pcap_new->ismount;
5226     pchr->cangrabmoney    = pcap_new->cangrabmoney;
5227     pchr->jump_timer        = JUMPDELAY;
5228     pchr->alpha_base       = pcap_new->alpha;
5229     pchr->light_base       = pcap_new->light;
5230 
5231     // change the skillz, too, jack!
5232     idsz_map_copy( pcap_new->skills, SDL_arraysize( pcap_new->skills ), pchr->skills );
5233     pchr->darkvision_level = chr_get_skill( pchr, MAKE_IDSZ( 'D', 'A', 'R', 'K' ) );
5234     pchr->see_invisible_level = pcap_new->see_invisible_level;
5235 
5236     /// @note BB@> changing this could be disasterous, in case you can't un-morph youself???
5237     /// pchr->canusearcane          = pcap_new->canusearcane;
5238     /// @note ZF@> No, we want this, I have specifically scripted morph books to handle unmorphing
5239     /// even if you cannot cast arcane spells. Some morph spells specifically morph the player
5240     /// into a fighter or a tech user, but as a balancing factor prevents other spellcasting.
5241 
5242     // Character size and bumping
5243     // set the character size so that the new model is the same size as the old model
5244     // the model will then morph its size to the correct size over time
5245     {
5246         float old_fat = pchr->fat;
5247         float new_fat;
5248 
5249         if ( 0.0f == pchr->bump.size )
5250         {
5251             new_fat = pcap_new->size;
5252         }
5253         else
5254         {
5255             new_fat = ( pcap_new->bump_size * pcap_new->size ) / pchr->bump.size;
5256         }
5257 
5258         // Spellbooks should stay the same size, even if their spell effect cause changes in size
5259         if ( pchr->profile_ref == SPELLBOOK ) new_fat = old_fat = 1.00f;
5260 
5261         // copy all the cap size info over, as normal
5262         chr_init_size( pchr, pcap_new );
5263 
5264         // make the model's size congruent
5265         if ( 0.0f != new_fat && new_fat != old_fat )
5266         {
5267             chr_set_fat( pchr, new_fat );
5268             pchr->fat_goto      = old_fat;
5269             pchr->fat_goto_time = SIZETIME;
5270         }
5271         else
5272         {
5273             chr_set_fat( pchr, old_fat );
5274             pchr->fat_goto      = old_fat;
5275             pchr->fat_goto_time = 0;
5276         }
5277     }
5278 
5279     //Physics
5280     pchr->phys.bumpdampen     = pcap_new->bumpdampen;
5281 
5282     if ( CAP_INFINITE_WEIGHT == pcap_new->weight )
5283     {
5284         pchr->phys.weight = CHR_INFINITE_WEIGHT;
5285     }
5286     else
5287     {
5288         Uint32 itmp = pcap_new->weight * pchr->fat * pchr->fat * pchr->fat;
5289         pchr->phys.weight = MIN( itmp, CHR_MAX_WEIGHT );
5290     }
5291 
5292     // Image rendering
5293     pchr->uoffvel = pcap_new->uoffvel;
5294     pchr->voffvel = pcap_new->voffvel;
5295 
5296     // Movement
5297     pchr->anim_speed_sneak = pcap_new->anim_speed_sneak;
5298     pchr->anim_speed_walk  = pcap_new->anim_speed_walk;
5299     pchr->anim_speed_run   = pcap_new->anim_speed_run;
5300 
5301     // initialize the instance
5302     chr_instance_spawn( &( pchr->inst ), profile_new, skin );
5303     chr_update_matrix( pchr, btrue );
5304 
5305     // Action stuff that must be down after chr_instance_spawn()
5306     chr_instance_set_action_ready( &( pchr->inst ), bfalse );
5307     chr_instance_set_action_keep( &( pchr->inst ), bfalse );
5308     chr_instance_set_action_loop( &( pchr->inst ), bfalse );
5309     if ( pchr->alive )
5310     {
5311         chr_play_action( pchr, ACTION_DA, bfalse );
5312     }
5313     else
5314     {
5315         chr_play_action( pchr, ACTION_KA + generate_randmask( 0, 3 ), bfalse );
5316         chr_instance_set_action_keep( &( pchr->inst ), btrue );
5317     }
5318 
5319     // Set the skin after changing the model in chr_instance_spawn()
5320     change_armor( ichr, skin );
5321 
5322     // Must set the wepon grip AFTER the model is changed in chr_instance_spawn()
5323     if ( INGAME_CHR( pchr->attachedto ) )
5324     {
5325         set_weapongrip( ichr, pchr->attachedto, slot_to_grip_offset( pchr->inwhich_slot ) );
5326     }
5327 
5328     item = pchr->holdingwhich[SLOT_LEFT];
5329     if ( INGAME_CHR( item ) )
5330     {
5331         EGOBOO_ASSERT( ChrList.lst[item].attachedto == ichr );
5332         set_weapongrip( item, ichr, GRIP_LEFT );
5333     }
5334 
5335     item = pchr->holdingwhich[SLOT_RIGHT];
5336     if ( INGAME_CHR( item ) )
5337     {
5338         EGOBOO_ASSERT( ChrList.lst[item].attachedto == ichr );
5339         set_weapongrip( item, ichr, GRIP_RIGHT );
5340     }
5341 
5342     // determine whether the object is hidden
5343     chr_update_hide( pchr );
5344 
5345     // Reaffirm them particles...
5346     pchr->reaffirm_damagetype = pcap_new->attachedprt_reaffirm_damagetype;
5347     //reaffirm_attached_particles( ichr );              /// @note ZF@> so that books dont burn when dropped
5348     new_attached_prt_count = number_of_attached_particles( ichr );
5349 
5350     ai_state_set_changed( &( pchr->ai ) );
5351 
5352     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, btrue );
5353 }
5354 
5355 //--------------------------------------------------------------------------------------------
cost_mana(const CHR_REF character,int amount,const CHR_REF killer)5356 bool_t cost_mana( const CHR_REF character, int amount, const CHR_REF killer )
5357 {
5358     /// @details ZZ@> This function takes mana from a character ( or gives mana ),
5359     ///    and returns btrue if the character had enough to pay, or bfalse
5360     ///    otherwise. This can kill a character in hard mode.
5361 
5362     int mana_final;
5363     bool_t mana_paid;
5364 
5365     chr_t * pchr;
5366 
5367     if ( !INGAME_CHR( character ) ) return bfalse;
5368     pchr = ChrList.lst + character;
5369 
5370     mana_paid  = bfalse;
5371     mana_final = pchr->mana - amount;
5372 
5373     if ( mana_final < 0 )
5374     {
5375         int mana_debt = -mana_final;
5376 
5377         pchr->mana = 0;
5378 
5379         if ( pchr->canchannel )
5380         {
5381             pchr->life -= mana_debt;
5382 
5383             if ( pchr->life <= 0 && cfg.difficulty >= GAME_HARD )
5384             {
5385                 kill_character( character, !INGAME_CHR( killer ) ? character : killer, bfalse );
5386             }
5387 
5388             mana_paid = btrue;
5389         }
5390     }
5391     else
5392     {
5393         int mana_surplus = 0;
5394 
5395         pchr->mana = mana_final;
5396 
5397         if ( mana_final > pchr->manamax )
5398         {
5399             mana_surplus = mana_final - pchr->manamax;
5400             pchr->mana   = pchr->manamax;
5401         }
5402 
5403         // allow surplus mana to go to health if you can channel?
5404         if ( pchr->canchannel && mana_surplus > 0 )
5405         {
5406             // use some factor, divide by 2
5407             heal_character( GET_REF_PCHR( pchr ), killer, mana_surplus << 1, btrue );
5408         }
5409 
5410         mana_paid = btrue;
5411 
5412     }
5413 
5414     return mana_paid;
5415 }
5416 
5417 //--------------------------------------------------------------------------------------------
switch_team(const CHR_REF character,const TEAM_REF team)5418 void switch_team( const CHR_REF character, const TEAM_REF team )
5419 {
5420     /// @details ZZ@> This function makes a character join another team...
5421     chr_t *pchr;
5422 
5423     if ( !INGAME_CHR( character ) || team >= TEAM_MAX || team < 0 ) return;
5424     pchr = ChrList.lst + character;
5425 
5426     // keep track of how many live ones we have on any team
5427     if ( !pchr->invictus )
5428     {
5429         if ( chr_get_pteam_base( character )->morale > 0 ) chr_get_pteam_base( character )->morale--;
5430         TeamStack.lst[team].morale++;
5431     }
5432 
5433     /*
5434     // change our current team if we are not a item or a mount
5435 
5436     /*
5437     // change our current team if we are not a item or a mount
5438     if (( !pchr->ismount || !INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) ) &&
5439         ( !pchr->isitem  || !INGAME_CHR( pchr->attachedto ) ) )
5440     {
5441         pchr->team = team;
5442     }
5443     */
5444 
5445     // actually change our team
5446     pchr->baseteam = team;
5447     pchr->team = team;
5448 
5449     //change our mount team as well
5450     if ( INGAME_CHR( pchr->attachedto ) )
5451     {
5452         chr_t *pmount = ChrList.lst + pchr->attachedto;
5453         if ( pmount->ismount ) pmount->team = team;
5454     }
5455     //change our mount team as well
5456     if ( INGAME_CHR( pchr->attachedto ) )
5457     {
5458         chr_t *pmount = ChrList.lst + pchr->attachedto;
5459         if ( pmount->ismount ) pmount->team = team;
5460     }
5461 
5462     // update the team of anything we are holding as well
5463     if ( INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) )
5464     {
5465         ChrList.lst[pchr->holdingwhich[SLOT_LEFT]].team = team;
5466     }
5467     if ( INGAME_CHR( pchr->holdingwhich[SLOT_RIGHT] ) )
5468     {
5469         ChrList.lst[pchr->holdingwhich[SLOT_RIGHT]].team = team;
5470     }
5471     // update the team of anything we are holding as well
5472     if ( INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) )
5473     {
5474         ChrList.lst[pchr->holdingwhich[SLOT_LEFT]].team = team;
5475     }
5476     if ( INGAME_CHR( pchr->holdingwhich[SLOT_RIGHT] ) )
5477     {
5478         ChrList.lst[pchr->holdingwhich[SLOT_RIGHT]].team = team;
5479     }
5480 
5481     // we are the new leader if there isn't one already
5482     if ( TeamStack.lst[team].leader == NOLEADER )
5483     {
5484         TeamStack.lst[team].leader = character;
5485     }
5486 }
5487 
5488 //--------------------------------------------------------------------------------------------
issue_clean(const CHR_REF character)5489 void issue_clean( const CHR_REF character )
5490 {
5491     /// @details ZZ@> This function issues a clean up order to all teammates
5492 
5493     TEAM_REF team;
5494 
5495     if ( !INGAME_CHR( character ) ) return;
5496 
5497     team = chr_get_iteam( character );
5498 
5499     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
5500     {
5501         if ( team != chr_get_iteam( cnt ) ) continue;
5502 
5503         if ( !pchr->alive )
5504         {
5505             pchr->ai.timer  = update_wld + 2;  // Don't let it think too much...
5506         }
5507 
5508         SET_BIT( pchr->ai.alert, ALERTIF_CLEANEDUP );
5509     }
5510     CHR_END_LOOP();
5511 }
5512 
5513 //--------------------------------------------------------------------------------------------
restock_ammo(const CHR_REF character,IDSZ idsz)5514 int restock_ammo( const CHR_REF character, IDSZ idsz )
5515 {
5516     /// @details ZZ@> This function restocks the characters ammo, if it needs ammo and if
5517     ///    either its parent or type idsz match the given idsz.  This
5518     ///    function returns the amount of ammo given.
5519 
5520     int amount;
5521 
5522     chr_t * pchr;
5523 
5524     if ( !INGAME_CHR( character ) ) return 0;
5525     pchr = ChrList.lst + character;
5526 
5527     amount = 0;
5528     if ( chr_is_type_idsz( character, idsz ) )
5529     {
5530         if ( ChrList.lst[character].ammo < ChrList.lst[character].ammomax )
5531         {
5532             amount = ChrList.lst[character].ammomax - ChrList.lst[character].ammo;
5533             ChrList.lst[character].ammo = ChrList.lst[character].ammomax;
5534         }
5535     }
5536 
5537     return amount;
5538 }
5539 
5540 //--------------------------------------------------------------------------------------------
chr_get_skill(chr_t * pchr,IDSZ whichskill)5541 int chr_get_skill( chr_t *pchr, IDSZ whichskill )
5542 {
5543     /// @details ZF@> This returns the skill level for the specified skill or 0 if the character doesn't
5544     ///                  have the skill. Also checks the skill IDSZ.
5545     IDSZ_node_t *pskill;
5546 
5547     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
5548 
5549     //Any [NONE] IDSZ returns always "true"
5550     if ( IDSZ_NONE == whichskill ) return 1;
5551 
5552     //Do not allow poison or backstab skill if we are restricted by code of conduct
5553     if ( MAKE_IDSZ( 'P', 'O', 'I', 'S' ) == whichskill || MAKE_IDSZ( 's', 'T', 'A', 'B' ) == whichskill )
5554     {
5555         if ( NULL != idsz_map_get( pchr->skills, SDL_arraysize( pchr->skills ), MAKE_IDSZ( 'C', 'O', 'D', 'E' ) ) )
5556         {
5557             return 0;
5558         }
5559     }
5560 
5561     // First check the character Skill ID matches
5562     // Then check for expansion skills too.
5563     if ( chr_get_idsz( pchr->ai.index, IDSZ_SKILL )  == whichskill )
5564     {
5565         return 1;
5566     }
5567 
5568     // Simply return the skill level if we have the skill
5569     pskill = idsz_map_get( pchr->skills, SDL_arraysize( pchr->skills ), whichskill );
5570     if ( pskill != NULL )
5571     {
5572         return pskill->level;
5573     }
5574 
5575     // Truesight allows reading
5576     if ( MAKE_IDSZ( 'R', 'E', 'A', 'D' ) == whichskill )
5577     {
5578         pskill = idsz_map_get( pchr->skills, SDL_arraysize( pchr->skills ), MAKE_IDSZ( 'C', 'K', 'U', 'R' ) );
5579         if ( pskill != NULL && pchr->see_invisible_level > 0 )
5580         {
5581             return pchr->see_invisible_level + pskill->level;
5582         }
5583     }
5584 
5585     //Skill not found
5586     return 0;
5587 }
5588 
5589 //--------------------------------------------------------------------------------------------
5590 //--------------------------------------------------------------------------------------------
update_chr_darkvision(const CHR_REF character)5591 bool_t update_chr_darkvision( const CHR_REF character )
5592 {
5593     /// @detalis BB@> as an offset to negative status effects like things like poisoning, a
5594     ///               character gains darkvision ability the more they are "poisoned".
5595     ///               True poisoning can be removed by [HEAL] and tints the character green
5596 
5597     ENC_REF ienc_now, ienc_nxt;
5598     size_t  ienc_count;
5599 
5600     eve_t * peve;
5601     int life_regen = 0;
5602 
5603     chr_t * pchr;
5604 
5605     if ( !INGAME_CHR( character ) ) return bfalse;
5606     pchr = ChrList.lst + character;
5607 
5608     // cleanup the enchant list
5609     cleanup_character_enchants( pchr );
5610 
5611     // grab the life loss due poison to determine how much darkvision a character has earned, he he he!
5612     // clean up the enchant list before doing anything
5613     ienc_now = pchr->firstenchant;
5614     ienc_count = 0;
5615     while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
5616     {
5617         ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
5618         peve = enc_get_peve( ienc_now );
5619 
5620         //Is it true poison?
5621         if ( NULL != peve && MAKE_IDSZ( 'H', 'E', 'A', 'L' ) == peve->removedbyidsz )
5622         {
5623             life_regen += EncList.lst[ienc_now].target_life;
5624             if ( EncList.lst[ienc_now].owner_ref == pchr->ai.index ) life_regen += EncList.lst[ienc_now].owner_life;
5625         }
5626 
5627         ienc_now = ienc_nxt;
5628         ienc_count++;
5629     }
5630     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
5631 
5632     if ( life_regen < 0 )
5633     {
5634         int tmp_level = ( 10 * -life_regen ) / pchr->lifemax;                        //Darkvision gained by poison
5635         int base_level = chr_get_skill( pchr, MAKE_IDSZ( 'D', 'A', 'R', 'K' ) );    //Natural darkvision
5636 
5637         //Use the better of the two darkvision abilities
5638         pchr->darkvision_level = MAX( base_level, tmp_level );
5639     }
5640 
5641     return btrue;
5642 }
5643 
5644 //--------------------------------------------------------------------------------------------
update_all_characters()5645 void update_all_characters()
5646 {
5647     /// @details ZZ@> This function updates stats and such for every character
5648 
5649     CHR_REF ichr;
5650 
5651     for ( ichr = 0; ichr < MAX_CHR; ichr++ )
5652     {
5653         chr_run_config( ChrList.lst + ichr );
5654     }
5655 
5656     // fix the stat timer
5657     if ( clock_chr_stat >= ONESECOND )
5658     {
5659         // Reset the clock
5660         clock_chr_stat -= ONESECOND;
5661     }
5662 
5663 }
5664 
5665 //--------------------------------------------------------------------------------------------
5666 //--------------------------------------------------------------------------------------------
move_one_character_get_environment(chr_t * pchr)5667 void move_one_character_get_environment( chr_t * pchr )
5668 {
5669 
5670     float   grid_level, water_level;
5671     chr_t * pplatform = NULL;
5672 
5673     chr_environment_t * penviro;
5674 
5675     if ( !ACTIVE_PCHR( pchr ) ) return;
5676     penviro = &( pchr->enviro );
5677 
5678     // determine if the character is standing on a platform
5679     pplatform = NULL;
5680     if ( INGAME_CHR( pchr->onwhichplatform_ref ) )
5681     {
5682         pplatform = ChrList.lst + pchr->onwhichplatform_ref;
5683     }
5684 
5685     //---- character "floor" level
5686     grid_level = get_mesh_level( PMesh, pchr->pos.x, pchr->pos.y, bfalse );
5687     water_level = get_mesh_level( PMesh, pchr->pos.x, pchr->pos.y, btrue );
5688 
5689     // chr_set_enviro_grid_level() sets up the reflection level and reflection matrix
5690     chr_set_enviro_grid_level( pchr, grid_level );
5691 
5692     penviro->grid_lerp  = ( pchr->pos.z - grid_level ) / PLATTOLERANCE;
5693     penviro->grid_lerp  = CLIP( penviro->grid_lerp, 0.0f, 1.0f );
5694 
5695     penviro->water_level = water_level;
5696     penviro->water_lerp  = ( pchr->pos.z - water_level ) / PLATTOLERANCE;
5697     penviro->water_lerp  = CLIP( penviro->water_lerp, 0.0f, 1.0f );
5698 
5699     // The actual level of the floor underneath the character.
5700     if ( NULL != pplatform )
5701     {
5702         penviro->floor_level = pplatform->pos.z + pplatform->chr_min_cv.maxs[OCT_Z];
5703     }
5704     else
5705     {
5706         penviro->floor_level = pchr->waterwalk ? water_level : grid_level;
5707     }
5708 
5709     //---- The actual level of the characer.
5710     //     Estimate platform attachment from whatever is in the onwhichplatform_ref variable from the
5711     //     last loop
5712     penviro->level = penviro->floor_level;
5713     if ( NULL != pplatform )
5714     {
5715         penviro->level = pplatform->pos.z + pplatform->chr_min_cv.maxs[OCT_Z];
5716     }
5717 
5718     //---- The flying height of the character, the maximum of tile level, platform level and water level
5719     if ( 0 != mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_WATER ) )
5720     {
5721         penviro->fly_level = MAX( penviro->level, water.surface_level );
5722     }
5723 
5724     if ( penviro->fly_level < 0 )
5725     {
5726         penviro->fly_level = 0;  // fly above pits...
5727     }
5728 
5729     // set the zlerp after we have done everything to the particle's level we care to
5730     penviro->zlerp = ( pchr->pos.z - penviro->level ) / PLATTOLERANCE;
5731     penviro->zlerp = CLIP( penviro->zlerp, 0.0f, 1.0f );
5732 
5733     penviro->grounded = ( 0 == pchr->flyheight ) && ( penviro->zlerp < 0.25f );
5734 
5735     //---- the "twist" of the floor
5736     penviro->grid_twist = TWIST_FLAT;
5737     if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
5738     {
5739         penviro->grid_twist = PMesh->gmem.grid_list[pchr->onwhichgrid].twist;
5740     }
5741 
5742     // the "watery-ness" of whatever water might be here
5743     penviro->is_watery = water.is_water && penviro->inwater;
5744     penviro->is_slippy = !penviro->is_watery && ( 0 != mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_SLIPPY ) );
5745 
5746     //---- traction
5747     penviro->traction = 1.0f;
5748     if ( 0 != pchr->flyheight )
5749     {
5750         // any traction factor here
5751         /* traction = ??; */
5752     }
5753     else if ( NULL != pplatform )
5754     {
5755         // in case the platform is tilted
5756         // unfortunately platforms are attached in the collision section
5757         // which occurs after the movement section.
5758 
5759         fvec3_t   platform_up;
5760 
5761         chr_getMatUp( pplatform, platform_up.v );
5762         fvec3_self_normalize( platform_up.v );
5763 
5764         penviro->traction = ABS( platform_up.z ) * ( 1.0f - penviro->zlerp ) + 0.25f * penviro->zlerp;
5765 
5766         if ( penviro->is_slippy )
5767         {
5768             penviro->traction /= 4.00f * hillslide * ( 1.0f - penviro->zlerp ) + 1.0f * penviro->zlerp;
5769         }
5770     }
5771     else if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) )
5772     {
5773         penviro->traction = ABS( map_twist_nrm[penviro->grid_twist].z ) * ( 1.0f - penviro->zlerp ) + 0.25f * penviro->zlerp;
5774 
5775         if ( penviro->is_slippy )
5776         {
5777             penviro->traction /= 4.00f * hillslide * ( 1.0f - penviro->zlerp ) + 1.0f * penviro->zlerp;
5778         }
5779     }
5780 
5781     //---- the friction of the fluid we are in
5782     if ( penviro->is_watery )
5783     {
5784         penviro->fluid_friction_vrt  = waterfriction;
5785         penviro->fluid_friction_hrz = waterfriction;
5786     }
5787     else
5788     {
5789         penviro->fluid_friction_hrz = penviro->air_friction;       // like real-life air friction
5790         penviro->fluid_friction_vrt  = penviro->air_friction;
5791     }
5792 
5793     //---- friction
5794     penviro->friction_hrz       = 1.0f;
5795     if ( 0 != pchr->flyheight )
5796     {
5797         if ( pchr->platform )
5798         {
5799             // override the z friction for platforms.
5800             // friction in the z direction will make the bouncing stop
5801             penviro->fluid_friction_vrt = 1.0f;
5802         }
5803     }
5804     else
5805     {
5806         // Make the characters slide
5807         float temp_friction_xy = noslipfriction;
5808         if ( mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) && penviro->is_slippy )
5809         {
5810             // It's slippy all right...
5811             temp_friction_xy = slippyfriction;
5812         }
5813 
5814         penviro->friction_hrz = penviro->zlerp * 1.0f + ( 1.0f - penviro->zlerp ) * temp_friction_xy;
5815     }
5816 
5817     //---- jump stuff
5818     if ( 0 != pchr->flyheight )
5819     {
5820         // Flying
5821         pchr->jumpready = bfalse;
5822     }
5823     else
5824     {
5825         // Character is in the air
5826         pchr->jumpready = penviro->grounded;
5827 
5828         // Down jump timer
5829         if (( INGAME_CHR( pchr->attachedto ) || pchr->jumpready || pchr->jumpnumber > 0 ) && pchr->jump_timer > 0 ) pchr->jump_timer--;
5830 
5831         // Do ground hits
5832         if ( penviro->grounded && pchr->vel.z < -STOPBOUNCING && pchr->hitready )
5833         {
5834             SET_BIT( pchr->ai.alert, ALERTIF_HITGROUND );
5835             pchr->hitready = bfalse;
5836         }
5837 
5838         // Special considerations for slippy surfaces
5839         if ( penviro->is_slippy )
5840         {
5841             if ( map_twist_flat[penviro->grid_twist] )
5842             {
5843                 // Reset jumping on flat areas of slippiness
5844                 if ( penviro->grounded && 0 == pchr->jump_timer )
5845                 {
5846                     pchr->jumpnumber = pchr->jumpnumberreset;
5847                 }
5848             }
5849         }
5850         else if ( penviro->grounded && 0 == pchr->jump_timer )
5851         {
5852             // Reset jumping
5853             pchr->jumpnumber = pchr->jumpnumberreset;
5854         }
5855     }
5856 
5857     // add in something for the "ground speed"
5858     if ( NULL == pplatform )
5859     {
5860         fvec3_self_clear( penviro->floor_speed.v );
5861     }
5862     else
5863     {
5864         fvec3_base_copy( penviro->floor_speed.v, pplatform->vel.v );
5865     }
5866 
5867 }
5868 
5869 //--------------------------------------------------------------------------------------------
move_one_character_do_floor_friction(chr_t * pchr)5870 void move_one_character_do_floor_friction( chr_t * pchr )
5871 {
5872     /// @details BB@> Friction is complicated when you want to have sliding characters :P
5873 
5874     float temp_friction_xy;
5875     fvec3_t   vup, floor_acc, fric, fric_floor;
5876     chr_environment_t * penviro;
5877 
5878     if ( !ACTIVE_PCHR( pchr ) ) return;
5879     penviro = &( pchr->enviro );
5880 
5881     if ( 0 != pchr->flyheight ) return;
5882 
5883     // assume the best
5884     fvec3_self_clear( floor_acc.v );
5885     temp_friction_xy = 1.0f;
5886     vup.x = 0.0f; vup.y = 0.0f; vup.z = 1.0f;
5887 
5888     // figure out the acceleration due to the current "floor"
5889     if ( INGAME_CHR( pchr->onwhichplatform_ref ) )
5890     {
5891         chr_t * pplat = ChrList.lst + pchr->onwhichplatform_ref;
5892 
5893         temp_friction_xy = platstick;
5894 
5895         chr_getMatUp( pplat, vup.v );
5896     }
5897     else if ( !pchr->alive || pchr->isitem )
5898     {
5899         temp_friction_xy = 0.5f;
5900 
5901         if ( TWIST_FLAT != penviro->grid_twist )
5902         {
5903             vup = map_twist_nrm[penviro->grid_twist];
5904         }
5905 
5906     }
5907     else
5908     {
5909         temp_friction_xy = penviro->friction_hrz;
5910 
5911         if ( TWIST_FLAT != penviro->grid_twist )
5912         {
5913             vup = map_twist_nrm[penviro->grid_twist];
5914         }
5915     }
5916 
5917     floor_acc = fvec3_sub( penviro->floor_speed.v, pchr->vel.v );
5918     fvec3_self_scale( floor_acc.v, ( 1.0f - penviro->zlerp ) );
5919 
5920     // reduce the volountary acceleration peopendicular to the direction of motion?
5921     if ( fvec3_length_abs( floor_acc.v ) > 0.0f )
5922     {
5923         fvec3_t acc_para, acc_perp;
5924         fvec3_t   vfront;
5925 
5926         // get the direction of motion
5927         mat_getChrForward( pchr->inst.matrix.v, vfront.v );
5928         fvec3_self_normalize( vfront.v );
5929 
5930         // decompose the acceleration into parallel and perpendicular components
5931         fvec3_decompose( floor_acc.v, vfront.v, acc_para.v, acc_perp.v );
5932 
5933         // re-compose the acceleration with 1/2 of the perpendicular taken away
5934         floor_acc = fvec3_scale( acc_perp.v, 0.5f );
5935         fvec3_self_sum( floor_acc.v, acc_para.v );
5936     }
5937 
5938     // the first guess about the floor friction
5939     fric_floor = fvec3_scale( floor_acc.v, penviro->traction * ( 1.0f - temp_friction_xy ) );
5940 
5941     // the total "friction" with to the floor
5942     fric = fvec3_add( fric_floor.v, penviro->acc.v );
5943 
5944     // limit the friction to whatever is horizontal to the mesh
5945     if ( 1.0f == ABS( vup.z ) )
5946     {
5947         fric.z      = 0.0f;
5948         floor_acc.z = 0.0f;
5949     }
5950     else
5951     {
5952         fvec3_t acc_perp, acc_para;
5953 
5954         fvec3_decompose( fric.v, vup.v, acc_perp.v, acc_para.v );
5955         fric = acc_para;
5956 
5957         fvec3_decompose( floor_acc.v, vup.v, acc_perp.v, acc_para.v );
5958         floor_acc = acc_para;
5959     }
5960 
5961     // test to see if the player has any more friction left?
5962     penviro->is_slipping = ( ABS( fric.x ) + ABS( fric.y ) + ABS( fric.z ) > penviro->friction_hrz );
5963 
5964     if ( penviro->is_slipping )
5965     {
5966         penviro->traction *= 0.5f;
5967         temp_friction_xy  = SQRT( temp_friction_xy );
5968 
5969         // the first guess about the floor friction
5970         fric_floor = fvec3_scale( floor_acc.v, penviro->traction * ( 1.0f - temp_friction_xy ) );
5971     }
5972 
5973     //apply the floor friction
5974     fvec3_self_sum( pchr->vel.v, fric_floor.v );
5975 
5976     // Apply fluid friction from last time
5977     pchr->vel.x += -pchr->vel.x * ( 1.0f - penviro->fluid_friction_hrz );
5978     pchr->vel.y += -pchr->vel.y * ( 1.0f - penviro->fluid_friction_hrz );
5979     pchr->vel.z += -pchr->vel.z * ( 1.0f - penviro->fluid_friction_vrt );
5980 }
5981 
5982 //--------------------------------------------------------------------------------------------
move_one_character_do_voluntary(chr_t * pchr)5983 void move_one_character_do_voluntary( chr_t * pchr )
5984 {
5985     // do voluntary motion
5986 
5987     float dvx, dvy;
5988     float maxspeed;
5989     float dv2;
5990     float new_ax, new_ay;
5991     CHR_REF ichr;
5992     bool_t sneak_mode_active = bfalse;
5993 
5994     if ( !ACTIVE_PCHR( pchr ) ) return;
5995 
5996     ichr = GET_REF_PCHR( pchr );
5997 
5998     if ( !pchr->alive ) return;
5999 
6000     fvec2_base_copy( pchr->enviro.new_v.v, pchr->vel.v );
6001 
6002     if ( INGAME_CHR( pchr->attachedto ) ) return;
6003 
6004     dvx = dvy = 0.0f;
6005     new_ax = new_ay = 0.0f;
6006 
6007     // Character latches for generalized movement
6008     dvx = pchr->latch.x;
6009     dvy = pchr->latch.y;
6010 
6011     // Reverse movements for daze
6012     if ( pchr->daze_timer > 0 )
6013     {
6014         dvx = -dvx;
6015         dvy = -dvy;
6016     }
6017 
6018     // Switch x and y for grog
6019     if ( pchr->grog_timer > 0 )
6020     {
6021         SWAP( float, dvx, dvy );
6022     }
6023 
6024     // this is the maximum speed that a character could go under the v2.22 system
6025     maxspeed = pchr->maxaccel * airfriction / ( 1.0f - airfriction );
6026 
6027     sneak_mode_active = bfalse;
6028     if ( VALID_PLA( pchr->is_which_player ) )
6029     {
6030         // determine whether the user is hitting the "sneak button"
6031         player_t * ppla = PlaStack.lst + pchr->is_which_player;
6032 
6033         if ( HAS_SOME_BITS( ppla->device.bits, INPUT_BITS_KEYBOARD ) )
6034         {
6035             // use the shift keys to enter sneak mode
6036             sneak_mode_active = SDLKEYDOWN( SDLK_LSHIFT ) || SDLKEYDOWN( SDLK_RSHIFT );
6037         }
6038     }
6039 
6040     pchr->enviro.new_v.x = pchr->enviro.new_v.y = 0.0f;
6041     if ( ABS( dvx ) + ABS( dvy ) > 0.05f )
6042     {
6043         PLA_REF ipla = pchr->is_which_player;
6044 
6045         dv2 = dvx * dvx + dvy * dvy;
6046 
6047         if ( VALID_PLA( ipla ) )
6048         {
6049             player_t * ppla;
6050             bool_t sneak_mode_active;
6051 
6052             float dv = POW( dv2, 0.25f );
6053 
6054             ppla = PlaStack.lst + ipla;
6055 
6056             // determine whether the character is sneaking
6057             if ( !HAS_SOME_BITS( ppla->device.bits, INPUT_BITS_KEYBOARD ) )
6058             {
6059                 sneak_mode_active = ( dv2 < 1.0f / 9.0f );
6060             }
6061 
6062             pchr->enviro.new_v.x = maxspeed * dvx / dv;
6063             pchr->enviro.new_v.y = maxspeed * dvy / dv;
6064         }
6065         else
6066         {
6067             float scale = 1.0f;
6068 
6069             if ( dv2 < 1.0f )
6070             {
6071                 scale = POW( dv2, 0.25f );
6072             }
6073 
6074             scale /= POW( dv2, 0.5f );
6075 
6076             pchr->enviro.new_v.x = dvx * maxspeed * scale;
6077             pchr->enviro.new_v.y = dvy * maxspeed * scale;
6078         }
6079     }
6080 
6081     if ( sneak_mode_active )
6082     {
6083         // sneak mode
6084         pchr->maxaccel      = pchr->maxaccel_reset * 0.33f;
6085         pchr->movement_bits = CHR_MOVEMENT_BITS_SNEAK | CHR_MOVEMENT_BITS_STOP;
6086     }
6087     else
6088     {
6089         // non-sneak mode
6090         pchr->movement_bits = ( unsigned )( ~CHR_MOVEMENT_BITS_SNEAK );
6091         pchr->maxaccel      = pchr->maxaccel_reset;
6092     }
6093 
6094     // do platform friction
6095     if ( INGAME_CHR( pchr->onwhichplatform_ref ) )
6096     {
6097         chr_t * pplat = ChrList.lst + pchr->onwhichplatform_ref;
6098 
6099         new_ax += ( pplat->vel.x + pchr->enviro.new_v.x - pchr->vel.x );
6100         new_ay += ( pplat->vel.y + pchr->enviro.new_v.y - pchr->vel.y );
6101     }
6102     else
6103     {
6104         new_ax += ( pchr->enviro.new_v.x - pchr->vel.x );
6105         new_ay += ( pchr->enviro.new_v.y - pchr->vel.y );
6106     }
6107 
6108     new_ax *= pchr->enviro.traction;
6109     new_ay *= pchr->enviro.traction;
6110 
6111     new_ax = CLIP( new_ax, -pchr->maxaccel, pchr->maxaccel );
6112     new_ay = CLIP( new_ay, -pchr->maxaccel, pchr->maxaccel );
6113 
6114     //Figure out how to turn around
6115     if ( 0 != pchr->maxaccel )
6116         switch ( pchr->turnmode )
6117         {
6118                 // Get direction from ACTUAL change in velocity
6119             default:
6120             case TURNMODE_VELOCITY:
6121                 {
6122                     if ( ABS( dvx ) > TURNSPD || ABS( dvy ) > TURNSPD )
6123                     {
6124                         if ( VALID_PLA( pchr->is_which_player ) )
6125                         {
6126                             // Players turn quickly
6127                             pchr->ori.facing_z += terp_dir( pchr->ori.facing_z, vec_to_facing( dvx , dvy ), 2 );
6128                         }
6129                         else
6130                         {
6131                             // AI turn slowly
6132                             pchr->ori.facing_z += terp_dir( pchr->ori.facing_z, vec_to_facing( dvx , dvy ), 8 );
6133                         }
6134                     }
6135                 }
6136                 break;
6137 
6138                 // Get direction from the DESIRED change in velocity
6139             case TURNMODE_WATCH:
6140                 {
6141                     if (( ABS( dvx ) > WATCHMIN || ABS( dvy ) > WATCHMIN ) )
6142                     {
6143                         pchr->ori.facing_z += terp_dir( pchr->ori.facing_z, vec_to_facing( dvx , dvy ), 8 );
6144                     }
6145                 }
6146                 break;
6147 
6148                 // Face the target
6149             case TURNMODE_WATCHTARGET:
6150                 {
6151                     if ( ichr != pchr->ai.target )
6152                     {
6153                         pchr->ori.facing_z += terp_dir( pchr->ori.facing_z, vec_to_facing( ChrList.lst[pchr->ai.target].pos.x - pchr->pos.x , ChrList.lst[pchr->ai.target].pos.y - pchr->pos.y ), 8 );
6154                     }
6155                 }
6156                 break;
6157 
6158                 // Otherwise make it spin
6159             case TURNMODE_SPIN:
6160                 {
6161                     pchr->ori.facing_z += SPINRATE;
6162                 }
6163                 break;
6164 
6165         }
6166 
6167     if ( chr_get_framefx( pchr ) & MADFX_STOP )
6168     {
6169         new_ax = 0;
6170         new_ay = 0;
6171     }
6172     else
6173     {
6174         pchr->vel.x += new_ax;
6175         pchr->vel.y += new_ay;
6176     }
6177 }
6178 
6179 //--------------------------------------------------------------------------------------------
chr_do_latch_attack(chr_t * pchr,slot_t which_slot)6180 bool_t chr_do_latch_attack( chr_t * pchr, slot_t which_slot )
6181 {
6182     chr_t * pweapon;
6183     cap_t * pweapon_cap;
6184     CHR_REF ichr, iweapon;
6185     MAD_REF imad;
6186 
6187     int    base_action, hand_action, action;
6188     bool_t action_valid, allowedtoattack;
6189 
6190     bool_t retval = bfalse;
6191 
6192     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
6193     ichr = GET_REF_PCHR( pchr );
6194 
6195     imad = chr_get_imad( ichr );
6196 
6197     if ( which_slot < 0 || which_slot >= SLOT_COUNT ) return bfalse;
6198 
6199     // Which iweapon?
6200     iweapon = pchr->holdingwhich[which_slot];
6201     if ( !INGAME_CHR( iweapon ) )
6202     {
6203         // Unarmed means character itself is the iweapon
6204         iweapon = ichr;
6205     }
6206     pweapon     = ChrList.lst + iweapon;
6207     pweapon_cap = chr_get_pcap( iweapon );
6208 
6209     //No need to continue if we have an attack cooldown
6210     if ( 0 != pweapon->reload_timer ) return bfalse;
6211 
6212     // grab the iweapon's action
6213     base_action = pweapon_cap->weaponaction;
6214     hand_action = randomize_action( base_action, which_slot );
6215 
6216     // see if the character can play this action
6217     action       = mad_get_action_ref( imad, hand_action );
6218     action_valid = ( ACTION_COUNT != action );
6219 
6220     // Can it do it?
6221     allowedtoattack = btrue;
6222 
6223     // First check if reload time and action is okay
6224     if ( !action_valid )
6225     {
6226         allowedtoattack = bfalse;
6227     }
6228     else
6229     {
6230         // Then check if a skill is needed
6231         if ( pweapon_cap->needskillidtouse )
6232         {
6233             if ( !chr_get_skill( pchr, chr_get_idsz( iweapon, IDSZ_SKILL ) ) )
6234             {
6235                 allowedtoattack = bfalse;
6236             }
6237         }
6238     }
6239 
6240     // Don't allow users with kursed weapon in the other hand to use longbows
6241     if ( allowedtoattack && ACTION_IS_TYPE( action, L ) )
6242     {
6243         CHR_REF test_weapon;
6244         test_weapon = pchr->holdingwhich[which_slot == SLOT_LEFT ? SLOT_RIGHT : SLOT_LEFT];
6245         if ( INGAME_CHR( test_weapon ) )
6246         {
6247             chr_t * weapon;
6248             weapon     = ChrList.lst + test_weapon;
6249             if ( weapon->iskursed ) allowedtoattack = bfalse;
6250         }
6251     }
6252 
6253     if ( !allowedtoattack )
6254     {
6255         // This character can't use this iweapon
6256         pweapon->reload_timer = 50;
6257         if ( pchr->StatusList_on || cfg.dev_mode )
6258         {
6259             // Tell the player that they can't use this iweapon
6260             debug_printf( "%s can't use this item...", chr_get_name( GET_REF_PCHR( pchr ), CHRNAME_ARTICLE | CHRNAME_CAPITAL ) );
6261         }
6262         return bfalse;
6263     }
6264 
6265     if ( ACTION_DA == action )
6266     {
6267         allowedtoattack = bfalse;
6268         if ( 0 == pweapon->reload_timer )
6269         {
6270             SET_BIT( pweapon->ai.alert, ALERTIF_USED );
6271         }
6272     }
6273 
6274     // deal with your mount (which could steal your attack)
6275     if ( allowedtoattack )
6276     {
6277         // Rearing mount
6278         CHR_REF mount = pchr->attachedto;
6279 
6280         if ( INGAME_CHR( mount ) )
6281         {
6282             chr_t * pmount = ChrList.lst + mount;
6283             cap_t * pmount_cap = chr_get_pcap( mount );
6284 
6285             // let the mount steal the rider's attack
6286             if ( !pmount_cap->ridercanattack ) allowedtoattack = bfalse;
6287 
6288             // can the mount do anything?
6289             if ( pmount->ismount && pmount->alive )
6290             {
6291                 // can the mount be told what to do?
6292                 if ( !VALID_PLA( pmount->is_which_player ) && pmount->inst.action_ready )
6293                 {
6294                     if ( !ACTION_IS_TYPE( action, P ) || !pmount_cap->ridercanattack )
6295                     {
6296                         chr_play_action( pmount, generate_randmask( ACTION_UA, 1 ), bfalse );
6297                         SET_BIT( pmount->ai.alert, ALERTIF_USED );
6298                         pchr->ai.lastitemused = mount;
6299 
6300                         retval = btrue;
6301                     }
6302                 }
6303             }
6304         }
6305     }
6306 
6307     // Attack button
6308     if ( allowedtoattack )
6309     {
6310         if ( pchr->inst.action_ready && action_valid )
6311         {
6312             // Check mana cost
6313             bool_t mana_paid = cost_mana( ichr, pweapon->manacost, iweapon );
6314 
6315             if ( mana_paid )
6316             {
6317                 Uint32 action_madfx = 0;
6318 
6319                 // Check life healing
6320                 pchr->life += pweapon->life_heal;
6321                 if ( pchr->life > pchr->lifemax )  pchr->life = pchr->lifemax;
6322 
6323                 // randomize the action
6324                 action = randomize_action( action, which_slot );
6325 
6326                 // make sure it is valid
6327                 action = mad_get_action_ref( imad, action );
6328 
6329                 // grab the MADFX_* flags for this action
6330                 action_madfx = mad_get_action_ref( imad, action );
6331 
6332                 if ( ACTION_IS_TYPE( action, P ) )
6333                 {
6334                     // we must set parry actions to be interrupted by anything
6335                     chr_play_action( pchr, action, btrue );
6336                 }
6337                 else
6338                 {
6339                     float chr_dex = FP8_TO_INT( pchr->dexterity );
6340 
6341                     chr_play_action( pchr, action, bfalse );
6342 
6343                     // Make the weapon animate the attack as well as the character holding it
6344                     if ( HAS_NO_BITS( action, MADFX_ACTLEFT | MADFX_ACTRIGHT ) )
6345                     {
6346                         if ( iweapon != ichr )
6347                         {
6348                             // the attacking character has no bits in the animation telling it
6349                             // to use the weapon, so we play the animation here
6350 
6351                             // Make the iweapon attack too
6352                             chr_play_action( pweapon, ACTION_MJ, bfalse );
6353                         }
6354                     }
6355 
6356                     //Determine the attack speed (how fast we play the animation)
6357                     pchr->inst.rate = 0.25f;                       //base attack speed
6358                     pchr->inst.rate += chr_dex / 20;                    //+0.25f for every 5 dexterity
6359 
6360                     //Add some reload time as a true limit to attacks per second
6361                     //Dexterity decreases the reload time for all weapons. We could allow other stats like intelligence
6362                     //reduce reload time for spells or gonnes here.
6363                     if ( !pweapon_cap->attack_fast )
6364                     {
6365                         int base_reload_time = -chr_dex;
6366                         if ( ACTION_IS_TYPE( action, U ) ) base_reload_time += 50;          //Unarmed  (Fists)
6367                         else if ( ACTION_IS_TYPE( action, T ) ) base_reload_time += 45;     //Thrust   (Spear)
6368                         else if ( ACTION_IS_TYPE( action, C ) ) base_reload_time += 75;     //Chop     (Axe)
6369                         else if ( ACTION_IS_TYPE( action, S ) ) base_reload_time += 55;     //Slice    (Sword)
6370                         else if ( ACTION_IS_TYPE( action, B ) ) base_reload_time += 60;     //Bash     (Mace)
6371                         else if ( ACTION_IS_TYPE( action, L ) ) base_reload_time += 50;     //Longbow  (Longbow)
6372                         else if ( ACTION_IS_TYPE( action, X ) ) base_reload_time += 100;    //Xbow     (Crossbow)
6373                         else if ( ACTION_IS_TYPE( action, F ) ) base_reload_time += 50;     //Flinged  (Unused)
6374 
6375                         //it is possible to have so high dex to eliminate all reload time
6376                         if ( base_reload_time > 0 ) pweapon->reload_timer += base_reload_time;
6377                     }
6378                 }
6379 
6380                 // let everyone know what we did
6381                 pchr->ai.lastitemused = iweapon;
6382                 if ( iweapon == ichr || HAS_NO_BITS( action, MADFX_ACTLEFT | MADFX_ACTRIGHT ) )
6383                 {
6384                     SET_BIT( pweapon->ai.alert, ALERTIF_USED );
6385                 }
6386 
6387                 retval = btrue;
6388             }
6389         }
6390     }
6391 
6392     //Reset boredom timer if the attack succeeded
6393     if ( retval )
6394     {
6395         pchr->bore_timer = BORETIME;
6396     }
6397 
6398     return retval;
6399 }
6400 
6401 //--------------------------------------------------------------------------------------------
chr_do_latch_button(chr_t * pchr)6402 bool_t chr_do_latch_button( chr_t * pchr )
6403 {
6404     /// @details BB@> Character latches for generalized buttons
6405 
6406     CHR_REF ichr;
6407     ai_state_t * pai;
6408 
6409     CHR_REF item;
6410     bool_t attack_handled;
6411 
6412     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
6413     ichr = GET_REF_PCHR( pchr );
6414 
6415     pai = &( pchr->ai );
6416 
6417     if ( !pchr->alive || 0 == pchr->latch.b ) return btrue;
6418 
6419     if ( HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_JUMP ) && 0 == pchr->jump_timer )
6420     {
6421         int ijump;
6422         cap_t * pcap;
6423 
6424         //Jump from our mount
6425         if ( INGAME_CHR( pchr->attachedto ) )
6426         {
6427             fvec3_t tmp_pos;
6428 
6429             detach_character_from_mount( ichr, btrue, btrue );
6430             detach_character_from_platform( ChrList.lst + ichr );
6431 
6432             pchr->jump_timer = JUMPDELAY;
6433             if ( 0 != pchr->flyheight )
6434             {
6435                 pchr->vel.z += DISMOUNTZVELFLY;
6436             }
6437             else
6438             {
6439                 pchr->vel.z += DISMOUNTZVEL;
6440             }
6441 
6442             tmp_pos = chr_get_pos( pchr );
6443             tmp_pos.z += pchr->vel.z;
6444             chr_set_pos( pchr, tmp_pos.v );
6445 
6446             if ( pchr->jumpnumberreset != JUMPINFINITE && 0 != pchr->jumpnumber )
6447                 pchr->jumpnumber--;
6448 
6449             // Play the jump sound
6450             pcap = pro_get_pcap( pchr->profile_ref );
6451             if ( NULL != pcap )
6452             {
6453                 ijump = pro_get_pcap( pchr->profile_ref )->sound_index[SOUND_JUMP];
6454                 if ( VALID_SND( ijump ) )
6455                 {
6456                     sound_play_chunk( pchr->pos, chr_get_chunk_ptr( pchr, ijump ) );
6457                 }
6458             }
6459 
6460         }
6461 
6462         //Normal jump
6463         else if ( 0 != pchr->jumpnumber && 0 == pchr->flyheight )
6464         {
6465             if ( 1 != pchr->jumpnumberreset || pchr->jumpready )
6466             {
6467 
6468                 // Make the character jump
6469                 pchr->hitready = btrue;
6470                 if ( pchr->enviro.inwater || pchr->enviro.is_slippy )
6471                 {
6472                     pchr->jump_timer = JUMPDELAY * 4;         //To prevent 'bunny jumping' in water
6473                     pchr->vel.z += WATERJUMP;
6474                 }
6475                 else
6476                 {
6477                     pchr->jump_timer = JUMPDELAY;
6478                     pchr->vel.z += pchr->jump_power * 1.5f;
6479                 }
6480 
6481                 pchr->jumpready = bfalse;
6482                 if ( pchr->jumpnumberreset != JUMPINFINITE ) pchr->jumpnumber--;
6483 
6484                 // Set to jump animation if not doing anything better
6485                 if ( pchr->inst.action_ready )
6486                 {
6487                     chr_play_action( pchr, ACTION_JA, btrue );
6488                 }
6489 
6490                 // Play the jump sound (Boing!)
6491                 pcap = pro_get_pcap( pchr->profile_ref );
6492                 if ( NULL != pcap )
6493                 {
6494                     ijump = pcap->sound_index[SOUND_JUMP];
6495                     if ( VALID_SND( ijump ) )
6496                     {
6497                         sound_play_chunk( pchr->pos, chr_get_chunk_ptr( pchr, ijump ) );
6498                     }
6499                 }
6500             }
6501         }
6502 
6503     }
6504     if ( HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_ALTLEFT ) && pchr->inst.action_ready && 0 == pchr->reload_timer )
6505     {
6506         //pchr->latch.b &= ~LATCHBUTTON_ALTLEFT;
6507 
6508         pchr->reload_timer = GRABDELAY;
6509         if ( !INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) )
6510         {
6511             // Grab left
6512             chr_play_action( pchr, ACTION_ME, bfalse );
6513         }
6514         else
6515         {
6516             // Drop left
6517             chr_play_action( pchr, ACTION_MA, bfalse );
6518         }
6519     }
6520     if ( HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_ALTRIGHT ) && pchr->inst.action_ready && 0 == pchr->reload_timer )
6521     {
6522         //pchr->latch.b &= ~LATCHBUTTON_ALTRIGHT;
6523 
6524         pchr->reload_timer = GRABDELAY;
6525         if ( !INGAME_CHR( pchr->holdingwhich[SLOT_RIGHT] ) )
6526         {
6527             // Grab right
6528             chr_play_action( pchr, ACTION_MF, bfalse );
6529         }
6530         else
6531         {
6532             // Drop right
6533             chr_play_action( pchr, ACTION_MB, bfalse );
6534         }
6535     }
6536     if ( HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_PACKLEFT ) && pchr->inst.action_ready && 0 == pchr->reload_timer )
6537     {
6538         //pchr->latch.b &= ~LATCHBUTTON_PACKLEFT;
6539 
6540         pchr->reload_timer = PACKDELAY;
6541         item = pchr->holdingwhich[SLOT_LEFT];
6542 
6543         if ( INGAME_CHR( item ) )
6544         {
6545             chr_t * pitem = ChrList.lst + item;
6546 
6547             if (( pitem->iskursed || pro_get_pcap( pitem->profile_ref )->istoobig ) && !pro_get_pcap( pitem->profile_ref )->isequipment )
6548             {
6549                 // The item couldn't be put away
6550                 SET_BIT( pitem->ai.alert, ALERTIF_NOTPUTAWAY );
6551                 if ( VALID_PLA( pchr->is_which_player ) )
6552                 {
6553                     if ( pro_get_pcap( pitem->profile_ref )->istoobig )
6554                     {
6555                         debug_printf( "%s is too big to be put away...", chr_get_name( item, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ) );
6556                     }
6557                     else if ( pitem->iskursed )
6558                     {
6559                         debug_printf( "%s is sticky...", chr_get_name( item, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ) );
6560                     }
6561                 }
6562             }
6563             else
6564             {
6565                 // Put the item into the pack
6566                 inventory_add_item( item, ichr );
6567             }
6568         }
6569         else
6570         {
6571             // Get a new one out and put it in hand
6572             inventory_get_item( ichr, GRIP_LEFT, bfalse );
6573         }
6574 
6575         // Make it take a little time
6576         chr_play_action( pchr, ACTION_MG, bfalse );
6577     }
6578     if ( HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_PACKRIGHT ) && pchr->inst.action_ready && 0 == pchr->reload_timer )
6579     {
6580         //pchr->latch.b &= ~LATCHBUTTON_PACKRIGHT;
6581 
6582         pchr->reload_timer = PACKDELAY;
6583         item = pchr->holdingwhich[SLOT_RIGHT];
6584         if ( INGAME_CHR( item ) )
6585         {
6586             chr_t * pitem     = ChrList.lst + item;
6587             cap_t * pitem_cap = chr_get_pcap( item );
6588 
6589             if (( pitem->iskursed || pitem_cap->istoobig ) && !pitem_cap->isequipment )
6590             {
6591                 // The item couldn't be put away
6592                 SET_BIT( pitem->ai.alert, ALERTIF_NOTPUTAWAY );
6593                 if ( VALID_PLA( pchr->is_which_player ) )
6594                 {
6595                     if ( pitem_cap->istoobig )
6596                     {
6597                         debug_printf( "%s is too big to be put away...", chr_get_name( item, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ) );
6598                     }
6599                     else if ( pitem->iskursed )
6600                     {
6601                         debug_printf( "%s is sticky...", chr_get_name( item, CHRNAME_ARTICLE | CHRNAME_DEFINITE | CHRNAME_CAPITAL ) );
6602                     }
6603                 }
6604             }
6605             else
6606             {
6607                 // Put the item into the pack
6608                 inventory_add_item( item, ichr );
6609             }
6610         }
6611         else
6612         {
6613             // Get a new one out and put it in hand
6614             inventory_get_item( ichr, GRIP_RIGHT, bfalse );
6615         }
6616 
6617         // Make it take a little time
6618         chr_play_action( pchr, ACTION_MG, bfalse );
6619     }
6620 
6621     // LATCHBUTTON_LEFT and LATCHBUTTON_RIGHT are mutually exclusive
6622     attack_handled = bfalse;
6623     if ( !attack_handled && HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_LEFT ) && 0 == pchr->reload_timer )
6624     {
6625         //pchr->latch.b &= ~LATCHBUTTON_LEFT;
6626         attack_handled = chr_do_latch_attack( pchr, SLOT_LEFT );
6627     }
6628     if ( !attack_handled && HAS_SOME_BITS( pchr->latch.b, LATCHBUTTON_RIGHT ) && 0 == pchr->reload_timer )
6629     {
6630         //pchr->latch.b &= ~LATCHBUTTON_RIGHT;
6631 
6632         attack_handled = chr_do_latch_attack( pchr, SLOT_RIGHT );
6633     }
6634 
6635     return btrue;
6636 }
6637 
6638 //--------------------------------------------------------------------------------------------
move_one_character_do_z_motion(chr_t * pchr)6639 void move_one_character_do_z_motion( chr_t * pchr )
6640 {
6641     if ( !ACTIVE_PCHR( pchr ) ) return;
6642 
6643     //---- do z acceleration
6644     if ( 0 != pchr->flyheight )
6645     {
6646         pchr->vel.z += ( pchr->enviro.fly_level + pchr->flyheight - pchr->pos.z ) * FLYDAMPEN;
6647     }
6648     else if (
6649         pchr->enviro.is_slippy && ( pchr->enviro.grid_twist != TWIST_FLAT ) &&
6650         ( CHR_INFINITE_WEIGHT != pchr->phys.weight )  && ( pchr->enviro.grid_lerp <= pchr->enviro.zlerp ) )
6651     {
6652         // Slippy hills make characters slide
6653 
6654         fvec3_t   gperp;    // gravity perpendicular to the mesh
6655         fvec3_t   gpara;    // gravity parallel      to the mesh (what pushes you)
6656 
6657         // RELATIVE TO THE GRID, since you might be riding a platform!
6658         float     loc_zlerp = pchr->enviro.grid_lerp;
6659 
6660         gpara.x = map_twistvel_x[pchr->enviro.grid_twist];
6661         gpara.y = map_twistvel_y[pchr->enviro.grid_twist];
6662         gpara.z = map_twistvel_z[pchr->enviro.grid_twist];
6663 
6664         gperp.x = 0       - gpara.x;
6665         gperp.y = 0       - gpara.y;
6666         gperp.z = gravity - gpara.z;
6667 
6668         pchr->vel.x += gpara.x * ( 1.0f - loc_zlerp ) + gperp.x * loc_zlerp;
6669         pchr->vel.y += gpara.y * ( 1.0f - loc_zlerp ) + gperp.y * loc_zlerp;
6670         pchr->vel.z += gpara.z * ( 1.0f - loc_zlerp ) + gperp.z * loc_zlerp;
6671     }
6672     else
6673     {
6674         pchr->vel.z += pchr->enviro.zlerp * gravity;
6675     }
6676 }
6677 
6678 //--------------------------------------------------------------------------------------------
chr_update_safe_raw(chr_t * pchr)6679 bool_t chr_update_safe_raw( chr_t * pchr )
6680 {
6681     bool_t retval = bfalse;
6682 
6683     BIT_FIELD hit_a_wall;
6684     float pressure;
6685 
6686     if ( !ALLOCATED_PCHR( pchr ) ) return bfalse;
6687 
6688     hit_a_wall = chr_hit_wall( pchr, NULL, NULL, &pressure, NULL );
6689     if (( 0 == hit_a_wall ) && ( 0.0f == pressure ) )
6690     {
6691         pchr->safe_valid = btrue;
6692         pchr->safe_pos   = chr_get_pos( pchr );
6693         pchr->safe_time  = update_wld;
6694         pchr->safe_grid  = mesh_get_grid( PMesh, pchr->pos.x, pchr->pos.y );
6695 
6696         retval = btrue;
6697     }
6698 
6699     return retval;
6700 }
6701 
6702 //--------------------------------------------------------------------------------------------
chr_update_safe(chr_t * pchr,bool_t force)6703 bool_t chr_update_safe( chr_t * pchr, bool_t force )
6704 {
6705     Uint32 new_grid;
6706     bool_t retval = bfalse;
6707     bool_t needs_update = bfalse;
6708 
6709     if ( !ALLOCATED_PCHR( pchr ) ) return bfalse;
6710 
6711     if ( force || !pchr->safe_valid )
6712     {
6713         needs_update = btrue;
6714     }
6715     else
6716     {
6717         new_grid = mesh_get_grid( PMesh, pchr->pos.x, pchr->pos.y );
6718 
6719         if ( INVALID_TILE == new_grid )
6720         {
6721             if ( ABS( pchr->pos.x - pchr->safe_pos.x ) > GRID_FSIZE ||
6722                  ABS( pchr->pos.y - pchr->safe_pos.y ) > GRID_FSIZE )
6723             {
6724                 needs_update = btrue;
6725             }
6726         }
6727         else if ( new_grid != pchr->safe_grid )
6728         {
6729             needs_update = btrue;
6730         }
6731     }
6732 
6733     if ( needs_update )
6734     {
6735         retval = chr_update_safe_raw( pchr );
6736     }
6737 
6738     return retval;
6739 }
6740 
6741 //--------------------------------------------------------------------------------------------
chr_get_safe(chr_t * pchr,fvec3_base_t pos_v)6742 bool_t chr_get_safe( chr_t * pchr, fvec3_base_t pos_v )
6743 {
6744     bool_t found = bfalse;
6745     fvec3_t loc_pos;
6746 
6747     if ( !ALLOCATED_PCHR( pchr ) ) return bfalse;
6748 
6749     // DO NOT require objects that are spawning in a module to have a
6750     // valid position at spawn-time. For instance, if a suit of armor is
6751     // spawned in a closed hallway, don't complain.
6752     if ( activate_spawn_file_active )
6753     {
6754         fvec3_base_copy( pos_v, chr_get_pos_v( pchr ) );
6755         return btrue;
6756     }
6757 
6758     // handle optional parameters
6759     if ( NULL == pos_v ) pos_v = loc_pos.v;
6760 
6761     if ( !found && pchr->safe_valid )
6762     {
6763         if ( !chr_hit_wall( pchr, NULL, NULL, NULL, NULL ) )
6764         {
6765             found = btrue;
6766             memmove( pos_v, pchr->safe_pos.v, sizeof( fvec3_base_t ) );
6767         }
6768     }
6769 
6770     if ( !found )
6771     {
6772         breadcrumb_t * bc;
6773 
6774         bc = breadcrumb_list_last_valid( &( pchr->crumbs ) );
6775 
6776         if ( NULL != bc )
6777         {
6778             found = btrue;
6779             memmove( pos_v, bc->pos.v, sizeof( fvec3_base_t ) );
6780         }
6781     }
6782 
6783     // maybe there is one last fallback after this? we could check the character's current position?
6784     if ( !found )
6785     {
6786         log_debug( "Uh oh! We could not find a valid non-wall position for %s!\n", chr_get_pcap( pchr->ai.index )->name );
6787     }
6788 
6789     return found;
6790 }
6791 
6792 //--------------------------------------------------------------------------------------------
chr_update_breadcrumb_raw(chr_t * pchr)6793 bool_t chr_update_breadcrumb_raw( chr_t * pchr )
6794 {
6795     breadcrumb_t bc;
6796     bool_t retval = bfalse;
6797 
6798     if ( !ALLOCATED_PCHR( pchr ) ) return bfalse;
6799 
6800     breadcrumb_init_chr( &bc, pchr );
6801 
6802     if ( bc.valid )
6803     {
6804         retval = breadcrumb_list_add( &( pchr->crumbs ), &bc );
6805     }
6806 
6807     return retval;
6808 }
6809 
6810 //--------------------------------------------------------------------------------------------
chr_update_breadcrumb(chr_t * pchr,bool_t force)6811 bool_t chr_update_breadcrumb( chr_t * pchr, bool_t force )
6812 {
6813     Uint32 new_grid;
6814     bool_t retval = bfalse;
6815     bool_t needs_update = bfalse;
6816     breadcrumb_t * bc_ptr, bc;
6817 
6818     if ( !ALLOCATED_PCHR( pchr ) ) return bfalse;
6819 
6820     bc_ptr = breadcrumb_list_last_valid( &( pchr->crumbs ) );
6821     if ( NULL == bc_ptr )
6822     {
6823         force  = btrue;
6824         bc_ptr = &bc;
6825         breadcrumb_init_chr( bc_ptr, pchr );
6826     }
6827 
6828     if ( force )
6829     {
6830         needs_update = btrue;
6831     }
6832     else
6833     {
6834         new_grid = mesh_get_grid( PMesh, pchr->pos.x, pchr->pos.y );
6835 
6836         if ( INVALID_TILE == new_grid )
6837         {
6838             if ( ABS( pchr->pos.x - bc_ptr->pos.x ) > GRID_FSIZE ||
6839                  ABS( pchr->pos.y - bc_ptr->pos.y ) > GRID_FSIZE )
6840             {
6841                 needs_update = btrue;
6842             }
6843         }
6844         else if ( new_grid != bc_ptr->grid )
6845         {
6846             needs_update = btrue;
6847         }
6848     }
6849 
6850     if ( needs_update )
6851     {
6852         retval = chr_update_breadcrumb_raw( pchr );
6853     }
6854 
6855     return retval;
6856 }
6857 
6858 //--------------------------------------------------------------------------------------------
chr_get_last_breadcrumb(chr_t * pchr)6859 breadcrumb_t * chr_get_last_breadcrumb( chr_t * pchr )
6860 {
6861     if ( !ALLOCATED_PCHR( pchr ) ) return NULL;
6862 
6863     if ( 0 == pchr->crumbs.count ) return NULL;
6864 
6865     return breadcrumb_list_last_valid( &( pchr->crumbs ) );
6866 }
6867 
6868 //--------------------------------------------------------------------------------------------
move_one_character_integrate_motion_attached(chr_t * pchr)6869 bool_t move_one_character_integrate_motion_attached( chr_t * pchr )
6870 {
6871     Uint32 chr_update;
6872 
6873     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
6874 
6875     // make a timer that is individual for each object
6876     chr_update = pchr->obj_base.guid + update_wld;
6877 
6878     if ( 0 == ( chr_update & 7 ) )
6879     {
6880         chr_update_safe( pchr, btrue );
6881     }
6882 
6883     return btrue;
6884 }
6885 
6886 //--------------------------------------------------------------------------------------------
move_one_character_integrate_motion(chr_t * pchr)6887 bool_t move_one_character_integrate_motion( chr_t * pchr )
6888 {
6889     /// @details BB@> Figure out the next position of the character.
6890     ///    Include collisions with the mesh in this step.
6891 
6892     CHR_REF  ichr;
6893     ai_state_t * pai;
6894 
6895     float   bumpdampen;
6896     bool_t  needs_test, updated_2d;
6897 
6898     fvec3_t tmp_pos;
6899 
6900     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
6901 
6902     if ( ACTIVE_CHR( pchr->attachedto ) )
6903     {
6904         return move_one_character_integrate_motion_attached( pchr );
6905     }
6906 
6907     tmp_pos = chr_get_pos( pchr );
6908 
6909     pai = &( pchr->ai );
6910     ichr = pai->index;
6911 
6912     bumpdampen = CLIP( pchr->phys.bumpdampen, 0.0f, 1.0f );
6913     bumpdampen = ( bumpdampen + 1.0f ) / 2.0f;
6914 
6915     // interaction with the mesh
6916     //if ( ABS( pchr->vel.z ) > 0.0f )
6917     {
6918         const float vert_offset = RAISE * 0.25f;
6919         float grid_level = pchr->enviro.grid_level + vert_offset;
6920 
6921         tmp_pos.z += pchr->vel.z;
6922         LOG_NAN( tmp_pos.z );
6923         if ( tmp_pos.z < grid_level )
6924         {
6925             if ( ABS( pchr->vel.z ) < STOPBOUNCING )
6926             {
6927                 pchr->vel.z = 0.0f;
6928                 tmp_pos.z = grid_level;
6929             }
6930             else
6931             {
6932                 if ( pchr->vel.z < 0.0f )
6933                 {
6934                     float diff = grid_level - tmp_pos.z;
6935 
6936                     pchr->vel.z *= -pchr->phys.bumpdampen;
6937                     diff        *= -pchr->phys.bumpdampen;
6938 
6939                     tmp_pos.z = MAX( tmp_pos.z + diff, grid_level );
6940                 }
6941                 else
6942                 {
6943                     tmp_pos.z = grid_level;
6944                 }
6945             }
6946         }
6947     }
6948 
6949     // fixes to the z-position
6950     if ( 0.0f != pchr->flyheight )
6951     {
6952         if ( tmp_pos.z < 0.0f ) tmp_pos.z = 0.0f;  // Don't fall in pits...
6953     }
6954 
6955     updated_2d = bfalse;
6956     needs_test = bfalse;
6957 
6958     // interaction with the grid flags
6959     updated_2d = bfalse;
6960     needs_test = bfalse;
6961     //if ( ABS( pchr->vel.x ) + ABS( pchr->vel.y ) > 0.0f )
6962     {
6963         mesh_wall_data_t wdata;
6964 
6965         float old_x, old_y, new_x, new_y;
6966 
6967         old_x = tmp_pos.x; LOG_NAN( old_x );
6968         old_y = tmp_pos.y; LOG_NAN( old_y );
6969 
6970         new_x = old_x + pchr->vel.x; LOG_NAN( new_x );
6971         new_y = old_y + pchr->vel.y; LOG_NAN( new_y );
6972 
6973         tmp_pos.x = new_x;
6974         tmp_pos.y = new_y;
6975 
6976         if ( EMPTY_BIT_FIELD == chr_test_wall( pchr, tmp_pos.v, &wdata ) )
6977         {
6978             updated_2d = btrue;
6979         }
6980         else
6981         {
6982             fvec2_t nrm;
6983             float   pressure;
6984             bool_t diff_function_called = bfalse;
6985 
6986             chr_hit_wall( pchr, tmp_pos.v, nrm.v, &pressure, &wdata );
6987 
6988             // how is the character hitting the wall?
6989             if ( 0.0f != pressure )
6990             {
6991                 bool_t         found_nrm  = bfalse;
6992                 bool_t         found_safe = bfalse;
6993                 fvec3_t        safe_pos   = ZERO_VECT3;
6994 
6995                 bool_t         found_diff = bfalse;
6996                 fvec2_t        diff       = ZERO_VECT2;
6997 
6998                 breadcrumb_t * bc         = NULL;
6999 
7000                 // try to get the correct "outward" pressure from nrm
7001                 if ( !found_nrm && ABS( nrm.x ) + ABS( nrm.y ) > 0.0f )
7002                 {
7003                     found_nrm = btrue;
7004                 }
7005 
7006                 if ( !found_diff && pchr->safe_valid )
7007                 {
7008                     if ( !found_safe )
7009                     {
7010                         found_safe = btrue;
7011                         safe_pos   = pchr->safe_pos;
7012                     }
7013 
7014                     diff.x = pchr->safe_pos.x - pchr->pos.x;
7015                     diff.y = pchr->safe_pos.y - pchr->pos.y;
7016 
7017                     if ( ABS( diff.x ) + ABS( diff.y ) > 0.0f )
7018                     {
7019                         found_diff = btrue;
7020                     }
7021                 }
7022 
7023                 // try to get a diff from a breadcrumb
7024                 if ( !found_diff )
7025                 {
7026                     bc = chr_get_last_breadcrumb( pchr );
7027 
7028                     if ( NULL != bc && bc->valid )
7029                     {
7030                         if ( !found_safe )
7031                         {
7032                             found_safe = btrue;
7033                             safe_pos   = pchr->safe_pos;
7034                         }
7035 
7036                         diff.x = bc->pos.x - pchr->pos.x;
7037                         diff.y = bc->pos.y - pchr->pos.y;
7038 
7039                         if ( ABS( diff.x ) + ABS( diff.y ) > 0.0f )
7040                         {
7041                             found_diff = btrue;
7042                         }
7043                     }
7044                 }
7045 
7046                 // try to get a normal from the mesh_get_diff() function
7047                 if ( !found_nrm )
7048                 {
7049                     fvec2_t diff;
7050 
7051                     diff = chr_get_mesh_diff( pchr, tmp_pos.v, pressure );
7052                     diff_function_called = btrue;
7053 
7054                     nrm.x = diff.x;
7055                     nrm.y = diff.y;
7056 
7057                     if ( ABS( nrm.x ) + ABS( nrm.y ) > 0.0f )
7058                     {
7059                         found_nrm = btrue;
7060                     }
7061                 }
7062 
7063                 if ( !found_diff )
7064                 {
7065                     // try to get the diff from the character velocity
7066                     diff.x = pchr->vel.x;
7067                     diff.y = pchr->vel.y;
7068 
7069                     // make sure that the diff is in the same direction as the velocity
7070                     if ( fvec2_dot_product( diff.v, nrm.v ) < 0.0f )
7071                     {
7072                         diff.x *= -1.0f;
7073                         diff.y *= -1.0f;
7074                     }
7075 
7076                     if ( ABS( diff.x ) + ABS( diff.y ) > 0.0f )
7077                     {
7078                         found_diff = btrue;
7079                     }
7080                 }
7081 
7082                 if ( !found_nrm )
7083                 {
7084                     // After all of our best efforts, we can't generate a normal to the wall.
7085                     // This can happen if the object is completely inside a wall,
7086                     // (like if it got pushed in there) or if a passage closed around it.
7087                     // Just teleport the character to a "safe" position.
7088 
7089                     if ( !found_safe && NULL == bc )
7090                     {
7091                         bc = chr_get_last_breadcrumb( pchr );
7092 
7093                         if ( NULL != bc && bc->valid )
7094                         {
7095                             found_safe = btrue;
7096                             safe_pos   = pchr->safe_pos;
7097                         }
7098                     }
7099 
7100                     if ( !found_safe )
7101                     {
7102                         // the only safe position is the spawn point???
7103                         found_safe = btrue;
7104                         safe_pos = pchr->pos_stt;
7105                     }
7106 
7107                     tmp_pos = safe_pos;
7108                 }
7109                 else if ( found_diff && found_nrm )
7110                 {
7111                     const float tile_fraction = 0.1f;
7112                     float ftmp, dot, pressure_old, pressure_new;
7113                     fvec3_t save_pos;
7114                     float nrm2;
7115 
7116                     fvec2_t v_perp = ZERO_VECT2;
7117                     fvec2_t diff_perp = ZERO_VECT2;
7118 
7119                     nrm2 = fvec2_dot_product( nrm.v, nrm.v );
7120 
7121                     save_pos = tmp_pos;
7122 
7123                     // make the diff point "out"
7124                     dot = fvec2_dot_product( diff.v, nrm.v );
7125                     if ( dot < 0.0f )
7126                     {
7127                         diff.x *= -1.0f;
7128                         diff.y *= -1.0f;
7129                         dot    *= -1.0f;
7130                     }
7131 
7132                     // find the part of the diff that is parallel to the normal
7133                     diff_perp.x = nrm.x * dot / nrm2;
7134                     diff_perp.y = nrm.y * dot / nrm2;
7135 
7136                     // normalize the diff_perp so that it is at most tile_fraction of a grid in any direction
7137                     ftmp = MAX( ABS( diff_perp.x ), ABS( diff_perp.y ) );
7138                     if ( 0 == ftmp ) ftmp = 1.00f;                    //EGOBOO_ASSERT(ftmp > 0.0f);
7139 
7140                     diff_perp.x *= tile_fraction * GRID_FSIZE / ftmp;
7141                     diff_perp.y *= tile_fraction * GRID_FSIZE / ftmp;
7142 
7143                     // try moving the character
7144                     tmp_pos.x += diff_perp.x * pressure;
7145                     tmp_pos.y += diff_perp.y * pressure;
7146 
7147                     // determine whether the pressure is less at this location
7148                     pressure_old = chr_get_mesh_pressure( pchr, save_pos.v );
7149                     pressure_new = chr_get_mesh_pressure( pchr, tmp_pos.v );
7150 
7151                     if ( pressure_new < pressure_old )
7152                     {
7153                         // !!success!!
7154                         needs_test = ( tmp_pos.x != save_pos.x ) || ( tmp_pos.y != save_pos.y );
7155                     }
7156                     else
7157                     {
7158                         // !!failure!! restore the saved position
7159                         tmp_pos = save_pos;
7160                     }
7161 
7162                     dot = fvec2_dot_product( pchr->vel.v, nrm.v );
7163                     if ( dot < 0.0f )
7164                     {
7165                         float bumpdampen;
7166                         cap_t * pcap = chr_get_pcap( GET_REF_PCHR( pchr ) );
7167 
7168                         bumpdampen = 0.0f;
7169                         if ( NULL == pcap )
7170                         {
7171                             bumpdampen = pcap->bumpdampen;
7172                         }
7173                         v_perp.x = nrm.x * dot / nrm2;
7174                         v_perp.y = nrm.y * dot / nrm2;
7175 
7176                         pchr->vel.x += - ( 1.0f + bumpdampen ) * v_perp.x * pressure;
7177                         pchr->vel.y += - ( 1.0f + bumpdampen ) * v_perp.y * pressure;
7178                     }
7179                 }
7180             }
7181         }
7182     }
7183 
7184     chr_set_pos( pchr, tmp_pos.v );
7185 
7186     // we need to test the validity of the current position every 8 frames or so,
7187     // no matter what
7188     if ( !needs_test )
7189     {
7190         // make a timer that is individual for each object
7191         Uint32 chr_update = pchr->obj_base.guid + update_wld;
7192 
7193         needs_test = ( 0 == ( chr_update & 7 ) );
7194     }
7195 
7196     if ( needs_test || updated_2d )
7197     {
7198         chr_update_safe( pchr, needs_test );
7199     }
7200 
7201     return btrue;
7202 }
7203 
7204 //--------------------------------------------------------------------------------------------
chr_handle_madfx(chr_t * pchr)7205 bool_t chr_handle_madfx( chr_t * pchr )
7206 {
7207     ///@details This handles special commands an animation frame might execute, for example
7208     ///         grabbing stuff or spawning attack particles.
7209 
7210     CHR_REF ichr;
7211     Uint32 framefx;
7212 
7213     if ( NULL == pchr ) return bfalse;
7214 
7215     framefx = chr_get_framefx( pchr );
7216     if ( 0 == framefx ) return btrue;
7217 
7218     ichr    = GET_REF_PCHR( pchr );
7219 
7220     // Check frame effects
7221     if ( HAS_SOME_BITS( framefx, MADFX_ACTLEFT ) )
7222     {
7223         character_swipe( ichr, SLOT_LEFT );
7224     }
7225 
7226     if ( HAS_SOME_BITS( framefx, MADFX_ACTRIGHT ) )
7227     {
7228         character_swipe( ichr, SLOT_RIGHT );
7229     }
7230 
7231     if ( HAS_SOME_BITS( framefx, MADFX_GRABLEFT ) )
7232     {
7233         character_grab_stuff( ichr, GRIP_LEFT, bfalse );
7234     }
7235 
7236     if ( HAS_SOME_BITS( framefx, MADFX_GRABRIGHT ) )
7237     {
7238         character_grab_stuff( ichr, GRIP_RIGHT, bfalse );
7239     }
7240 
7241     if ( HAS_SOME_BITS( framefx, MADFX_CHARLEFT ) )
7242     {
7243         character_grab_stuff( ichr, GRIP_LEFT, btrue );
7244     }
7245 
7246     if ( HAS_SOME_BITS( framefx, MADFX_CHARRIGHT ) )
7247     {
7248         character_grab_stuff( ichr, GRIP_RIGHT, btrue );
7249     }
7250 
7251     if ( HAS_SOME_BITS( framefx, MADFX_DROPLEFT ) )
7252     {
7253         detach_character_from_mount( pchr->holdingwhich[SLOT_LEFT], bfalse, btrue );
7254     }
7255 
7256     if ( HAS_SOME_BITS( framefx, MADFX_DROPRIGHT ) )
7257     {
7258         detach_character_from_mount( pchr->holdingwhich[SLOT_RIGHT], bfalse, btrue );
7259     }
7260 
7261     if ( HAS_SOME_BITS( framefx, MADFX_POOF ) && !VALID_PLA( pchr->is_which_player ) )
7262     {
7263         pchr->ai.poof_time = update_wld;
7264     }
7265 
7266     //Do footfall sound effect
7267     if ( cfg.sound_footfall && HAS_SOME_BITS( framefx, MADFX_FOOTFALL ) )
7268     {
7269         cap_t * pcap = pro_get_pcap( pchr->profile_ref );
7270         if ( NULL != pcap )
7271         {
7272             int ifoot = pcap->sound_index[SOUND_FOOTFALL];
7273             if ( VALID_SND( ifoot ) )
7274             {
7275                 sound_play_chunk( pchr->pos, chr_get_chunk_ptr( pchr, ifoot ) );
7276             }
7277         }
7278     }
7279 
7280     return btrue;
7281 }
7282 
7283 struct s_chr_anim_data
7284 {
7285     bool_t allowed;
7286     int    action;
7287     int    lip;
7288     float  speed;
7289 };
7290 typedef struct s_chr_anim_data chr_anim_data_t;
7291 
cmp_chr_anim_data(void const * vp_lhs,void const * vp_rhs)7292 int cmp_chr_anim_data( void const * vp_lhs, void const * vp_rhs )
7293 {
7294     /// @details BB@> Sort MOD REF values based on the rank of the module that they point to.
7295     ///               Trap all stupid values.
7296 
7297     chr_anim_data_t * plhs = ( chr_anim_data_t * )vp_lhs;
7298     chr_anim_data_t * prhs = ( chr_anim_data_t * )vp_rhs;
7299 
7300     int retval = 0;
7301 
7302     if ( NULL == plhs && NULL == prhs )
7303     {
7304         return 0;
7305     }
7306     else if ( NULL == plhs )
7307     {
7308         return 1;
7309     }
7310     else if ( NULL == prhs )
7311     {
7312         return -1;
7313     }
7314 
7315     retval = ( int )prhs->allowed - ( int )plhs->allowed;
7316     if ( 0 != retval ) return retval;
7317 
7318     retval = SGN( plhs->speed - prhs->speed );
7319 
7320     return retval;
7321 }
7322 
7323 //--------------------------------------------------------------------------------------------
set_character_animation_rate(chr_t * pchr)7324 float set_character_animation_rate( chr_t * pchr )
7325 {
7326     /// @details ZZ@> Get running, walking, sneaking, or dancing, from speed
7327     ///
7328     /// BB@> added automatic calculation of variable animation rates for movement animations
7329 
7330     float  speed;
7331     bool_t can_be_interrupted;
7332     bool_t is_walk_type;
7333     int    cnt, anim_count;
7334     int    action, lip;
7335     bool_t found;
7336 
7337     chr_anim_data_t anim_info[CHR_MOVEMENT_COUNT];
7338 
7339     MD2_Frame_t * pframe_nxt;
7340 
7341     chr_instance_t * pinst;
7342     mad_t          * pmad;
7343     CHR_REF          ichr;
7344 
7345     // set the character speed to zero
7346     speed = 0;
7347 
7348     if ( NULL == pchr ) return 1.0f;
7349     pinst = &( pchr->inst );
7350     ichr  = GET_REF_PCHR( pchr );
7351 
7352     // if the action is set to keep then do nothing
7353     can_be_interrupted = !pinst->action_keep;
7354     if ( !can_be_interrupted ) return pinst->rate = 1.0f;
7355 
7356     // dont change the rate if it is an attack animation
7357     if ( character_is_attacking( pchr ) )  return pinst->rate;
7358 
7359     // if the character is mounted or sitting, base the rate off of the mounr
7360     if ( INGAME_CHR( pchr->attachedto ) && (( ACTION_MI == pinst->action_which ) || ( ACTION_MH == pinst->action_which ) ) )
7361     {
7362         // just copy the rate from the mount
7363         pinst->rate = ChrList.lst[pchr->attachedto].inst.rate;
7364         return pinst->rate;
7365     }
7366 
7367     // if the animation is not a walking-type animation, ignore the variable animation rates
7368     // and the automatic determination of the walk animation
7369     // "dance" is walking with zero speed
7370     is_walk_type = ACTION_IS_TYPE( pinst->action_which, D ) || ACTION_IS_TYPE( pinst->action_which, W );
7371     if ( !is_walk_type ) return pinst->rate = 1.0f;
7372 
7373     // if the action cannot be changed on the at this time, there's nothing to do.
7374     // keep the same animation rate
7375     if ( !pinst->action_ready )
7376     {
7377         if ( 0.0f == pinst->rate ) pinst->rate = 1.0f;
7378         return pinst->rate;
7379     }
7380 
7381     // go back to a base animation rate, in case the next frame is not a
7382     // "variable speed frame"
7383     pinst->rate = 1.0f;
7384 
7385     // for non-flying objects, you have to be touching the ground
7386     if ( !pchr->enviro.grounded && 0 == pchr->flyheight ) return pinst->rate;
7387 
7388     // get the model
7389     pmad = chr_get_pmad( ichr );
7390     if ( NULL == pmad ) return pinst->rate;
7391 
7392     //---- set up the anim_info structure
7393     anim_info[CHR_MOVEMENT_STOP ].speed = 0;
7394     anim_info[CHR_MOVEMENT_SNEAK].speed = pchr->anim_speed_sneak;
7395     anim_info[CHR_MOVEMENT_WALK ].speed = pchr->anim_speed_walk;
7396     anim_info[CHR_MOVEMENT_RUN  ].speed = pchr->anim_speed_run;
7397 
7398     if ( 0 != pchr->flyheight )
7399     {
7400         // for flying characters, you have to flap like crazy to stand still and
7401         // do nothing to move quickly
7402         anim_info[CHR_MOVEMENT_STOP ].action = ACTION_WC;
7403         anim_info[CHR_MOVEMENT_SNEAK].action = ACTION_WB;
7404         anim_info[CHR_MOVEMENT_WALK ].action = ACTION_WA;
7405         anim_info[CHR_MOVEMENT_RUN  ].action = ACTION_DA;
7406     }
7407     else
7408     {
7409         anim_info[CHR_MOVEMENT_STOP ].action = ACTION_DA;
7410         anim_info[CHR_MOVEMENT_SNEAK].action = ACTION_WA;
7411         anim_info[CHR_MOVEMENT_WALK ].action = ACTION_WB;
7412         anim_info[CHR_MOVEMENT_RUN  ].action = ACTION_WC;
7413     }
7414 
7415     anim_info[CHR_MOVEMENT_STOP ].lip = 0;
7416     anim_info[CHR_MOVEMENT_SNEAK].lip = LIPWA;
7417     anim_info[CHR_MOVEMENT_WALK ].lip = LIPWB;
7418     anim_info[CHR_MOVEMENT_RUN  ].lip = LIPWC;
7419 
7420     // set up the arrays that are going to
7421     // determine whether the various movements are allowed
7422     for ( cnt = 0; cnt < CHR_MOVEMENT_COUNT; cnt++ )
7423     {
7424         anim_info[cnt].allowed = HAS_SOME_BITS( pchr->movement_bits, 1 << cnt );
7425     }
7426 
7427     if ( ACTION_WA != pmad->action_map[ACTION_WA] )
7428     {
7429         // no specific walk animation exists
7430         anim_info[CHR_MOVEMENT_SNEAK].allowed = bfalse;
7431 
7432         //ZF> small fix here, if there is no sneak animation, try to default to normal walk with reduced animation speed
7433         if ( HAS_SOME_BITS( pchr->movement_bits, CHR_MOVEMENT_BITS_SNEAK ) )
7434         {
7435             anim_info[CHR_MOVEMENT_WALK].allowed = btrue;
7436             anim_info[CHR_MOVEMENT_WALK].speed *= 2;
7437         }
7438     }
7439 
7440     if ( ACTION_WB != pmad->action_map[ACTION_WB] )
7441     {
7442         // no specific walk animation exists
7443         anim_info[CHR_MOVEMENT_WALK].allowed = bfalse;
7444     }
7445 
7446     if ( ACTION_WC != pmad->action_map[ACTION_WC] )
7447     {
7448         // no specific walk animation exists
7449         anim_info[CHR_MOVEMENT_RUN].allowed = bfalse;
7450     }
7451 
7452     // sort the allowed movement(s) data
7453     qsort( anim_info, CHR_MOVEMENT_COUNT, sizeof( chr_anim_data_t ), cmp_chr_anim_data );
7454 
7455     // count the allowed movements
7456     for ( cnt = 0, anim_count = 0; cnt < CHR_MOVEMENT_COUNT; cnt++ )
7457     {
7458         if ( anim_info[cnt].allowed ) anim_count++;
7459     }
7460 
7461     // nothing to be done
7462     if ( 0 == anim_count )
7463     {
7464         return pinst->rate;
7465     }
7466 
7467     // estimate our speed
7468     if ( 0 != pchr->flyheight )
7469     {
7470         // for flying objects, the speed is the actual speed
7471         speed = ABS( pchr->vel.x ) + ABS( pchr->vel.y ) + ABS( pchr->vel.z );
7472     }
7473     else
7474     {
7475         // for non-flying objects, we use the intended speed
7476 
7477         if ( pchr->enviro.is_slipping )
7478         {
7479             // the character is slipping as on ice. make their little legs move based on
7480             // their intended speed, for comic effect! :)
7481             speed = fvec2_length_abs( pchr->enviro.new_v.v );
7482         }
7483         else
7484         {
7485             // new_v.x, new_v.y is the speed before any latches are applied
7486             speed = fvec2_length_abs( pchr->enviro.new_v.v );
7487         }
7488     }
7489 
7490     if ( pchr->fat != 0.0f ) speed /= pchr->fat;
7491 
7492     // handle a special case
7493     if ( 1 == anim_count )
7494     {
7495         if ( 0.0f != anim_info[0].speed )
7496         {
7497             pinst->rate = speed / anim_info[0].speed ;
7498         }
7499 
7500         return pinst->rate;
7501     }
7502 
7503     // search for the correct animation
7504     action = ACTION_DA;
7505     lip    = 0;
7506     found  = bfalse;
7507     for ( cnt = 0; cnt < anim_count - 1; cnt++ )
7508     {
7509         float speed_mid = 0.5f * ( anim_info[cnt].speed + anim_info[cnt+1].speed );
7510 
7511         // make a special case for dance animation(s)
7512         if ( 0.0f == anim_info[cnt].speed && speed <= 1e-3 )
7513         {
7514             found = btrue;
7515         }
7516         else
7517         {
7518             found = ( speed < speed_mid );
7519         }
7520 
7521         if ( found )
7522         {
7523             action = anim_info[cnt].action;
7524             lip    = anim_info[cnt].lip;
7525             if ( 0.0f != anim_info[cnt].speed )
7526             {
7527                 pinst->rate = speed / anim_info[cnt].speed;
7528             }
7529             break;
7530         }
7531     }
7532 
7533     if ( !found )
7534     {
7535         action = anim_info[cnt].action;
7536         lip    = anim_info[cnt].lip;
7537         if ( 0.0f != anim_info[cnt].speed )
7538         {
7539             pinst->rate = speed / anim_info[cnt].speed;
7540         }
7541         found = btrue;
7542     }
7543 
7544     if ( !found )
7545     {
7546         return pinst->rate;
7547     }
7548 
7549     pframe_nxt  = chr_instnce_get_frame_nxt( &( pchr->inst ) );
7550 
7551     if ( ACTION_DA == action )
7552     {
7553         // Do standstill
7554 
7555         // handle boredom
7556         pchr->bore_timer--;
7557         if ( pchr->bore_timer < 0 )
7558         {
7559             int tmp_action, rand_val;
7560 
7561             SET_BIT( pchr->ai.alert, ALERTIF_BORED );
7562             pchr->bore_timer = BORETIME;
7563 
7564             // set the action to "bored", which is ACTION_DB, ACTION_DC, or ACTION_DD
7565             rand_val   = RANDIE;
7566             tmp_action = mad_get_action_ref( pinst->imad, ACTION_DB + ( rand_val % 3 ) );
7567             chr_start_anim( pchr, tmp_action, btrue, btrue );
7568         }
7569         else
7570         {
7571             // if the current action is not ACTION_D* switch to ACTION_DA
7572             if ( !ACTION_IS_TYPE( pinst->action_which, D ) )
7573             {
7574                 // get an appropriate version of the boredom action
7575                 int tmp_action = mad_get_action_ref( pinst->imad, ACTION_DA );
7576 
7577                 // start the animation
7578                 chr_start_anim( pchr, tmp_action, btrue, btrue );
7579             }
7580         }
7581     }
7582     else
7583     {
7584         int tmp_action = mad_get_action_ref( pinst->imad, action );
7585         if ( ACTION_COUNT != tmp_action )
7586         {
7587             if ( pinst->action_which != tmp_action )
7588             {
7589                 chr_set_anim( pchr, tmp_action, pmad->frameliptowalkframe[lip][pframe_nxt->framelip], btrue, btrue );
7590             }
7591 
7592             // "loop" the action
7593             chr_instance_set_action_next( pinst, tmp_action );
7594         }
7595     }
7596 
7597     pinst->rate = CLIP( pinst->rate, 0.1f, 10.0f );
7598 
7599     return pinst->rate;
7600 }
7601 
7602 //--------------------------------------------------------------------------------------------
character_is_attacking(chr_t * pchr)7603 bool_t character_is_attacking( chr_t *pchr )
7604 {
7605     return pchr->inst.action_which >= ACTION_UA && pchr->inst.action_which <= ACTION_FD;
7606 }
7607 
7608 //--------------------------------------------------------------------------------------------
move_one_character_do_animation(chr_t * pchr)7609 void move_one_character_do_animation( chr_t * pchr )
7610 {
7611     // Animate the character.
7612     // Right now there are 50/4 = 12.5 animation frames per second
7613 
7614     float flip_diff, flip_next;
7615 
7616     chr_instance_t * pinst;
7617     CHR_REF          ichr;
7618 
7619     if ( NULL == pchr ) return;
7620     ichr  = GET_REF_PCHR( pchr );
7621     pinst = &( pchr->inst );
7622 
7623     flip_diff  = 0.25f * pinst->rate;
7624 
7625     flip_next = chr_instance_get_remaining_flip( pinst );
7626 
7627     while ( flip_next > 0.0f && flip_diff >= flip_next )
7628     {
7629         flip_diff -= flip_next;
7630 
7631         chr_instance_update_one_lip( pinst );
7632 
7633         // handle frame FX for the new frame
7634         if ( 3 == pinst->ilip )
7635         {
7636             chr_handle_madfx( pchr );
7637         }
7638 
7639         if ( 4 == pinst->ilip )
7640         {
7641             if ( rv_success != chr_increment_frame( pchr ) )
7642             {
7643                 log_warning( "chr_increment_frame() did not succeed" );
7644             }
7645         }
7646 
7647         if ( pinst->ilip > 4 )
7648         {
7649             log_warning( "chr_increment_frame() - invalid ilip\n" );
7650             pinst->ilip = 0;
7651             break;
7652         }
7653 
7654         flip_next = chr_instance_get_remaining_flip( pinst );
7655     }
7656 
7657     if ( flip_diff > 0.0f )
7658     {
7659         int ilip_old = pinst->ilip;
7660 
7661         chr_instance_update_one_flip( pinst, flip_diff );
7662 
7663         if ( ilip_old != pinst->ilip )
7664         {
7665             // handle frame FX for the new frame
7666             if ( 3 == pinst->ilip )
7667             {
7668                 chr_handle_madfx( pchr );
7669             }
7670 
7671             if ( 4 == pinst->ilip )
7672             {
7673                 if ( rv_success != chr_increment_frame( pchr ) )
7674                 {
7675                     log_warning( "chr_increment_frame() did not succeed" );
7676                 }
7677             }
7678 
7679             if ( pinst->ilip > 4 )
7680             {
7681                 log_warning( "chr_increment_frame() - invalid ilip\n" );
7682                 pinst->ilip = 0;
7683             }
7684         }
7685     }
7686 
7687     set_character_animation_rate( pchr );
7688 }
7689 
7690 //--------------------------------------------------------------------------------------------
move_one_character(chr_t * pchr)7691 void move_one_character( chr_t * pchr )
7692 {
7693     if ( !ACTIVE_PCHR( pchr ) ) return;
7694 
7695     if ( pchr->pack.is_packed ) return;
7696 
7697     // save the velocity and acceleration from the last time-step
7698     pchr->enviro.vel = fvec3_sub( pchr->pos.v, pchr->pos_old.v );
7699     pchr->enviro.acc = fvec3_sub( pchr->vel.v, pchr->vel_old.v );
7700 
7701     // Character's old location
7702     pchr->pos_old          = chr_get_pos( pchr );
7703     pchr->vel_old          = pchr->vel;
7704     pchr->ori_old.facing_z = pchr->ori.facing_z;
7705 
7706     fvec2_base_copy( pchr->enviro.new_v.v, pchr->vel.v );
7707 
7708     move_one_character_get_environment( pchr );
7709 
7710     // do friction with the floor before voluntary motion
7711     move_one_character_do_floor_friction( pchr );
7712 
7713     move_one_character_do_voluntary( pchr );
7714 
7715     chr_do_latch_button( pchr );
7716 
7717     move_one_character_do_z_motion( pchr );
7718 
7719     move_one_character_integrate_motion( pchr );
7720 
7721     move_one_character_do_animation( pchr );
7722 
7723     // Characters with sticky butts lie on the surface of the mesh
7724     if ( pchr->stickybutt || !pchr->alive )
7725     {
7726         float fkeep = ( 7 + pchr->enviro.zlerp ) / 8.0f;
7727         float fnew  = ( 1 - pchr->enviro.zlerp ) / 8.0f;
7728 
7729         if ( fnew > 0 )
7730         {
7731             pchr->ori.map_facing_x = pchr->ori.map_facing_x * fkeep + map_twist_x[pchr->enviro.grid_twist] * fnew;
7732             pchr->ori.map_facing_y = pchr->ori.map_facing_y * fkeep + map_twist_y[pchr->enviro.grid_twist] * fnew;
7733         }
7734     }
7735 }
7736 
7737 //--------------------------------------------------------------------------------------------
move_all_characters(void)7738 void move_all_characters( void )
7739 {
7740     /// @details ZZ@> This function handles character physics
7741 
7742     chr_stoppedby_tests = 0;
7743 
7744     // Move every character
7745     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
7746     {
7747         // prime the environment
7748         pchr->enviro.air_friction = air_friction;
7749         pchr->enviro.ice_friction = ice_friction;
7750 
7751         move_one_character( pchr );
7752     }
7753     CHR_END_LOOP();
7754 
7755     // The following functions need to be called any time you actually change a charcter's position
7756     keep_weapons_with_holders();
7757     attach_all_particles();
7758     update_all_character_matrices();
7759 }
7760 
7761 //--------------------------------------------------------------------------------------------
cleanup_all_characters()7762 void cleanup_all_characters()
7763 {
7764     CHR_REF cnt;
7765 
7766     // Do poofing
7767     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
7768     {
7769         chr_t * pchr;
7770         bool_t time_out;
7771 
7772         if ( !ALLOCATED_CHR( cnt ) ) continue;
7773         pchr = ChrList.lst + cnt;
7774 
7775         time_out = ( pchr->ai.poof_time >= 0 ) && ( pchr->ai.poof_time <= ( Sint32 )update_wld );
7776         if ( !WAITING_PBASE( POBJ_GET_PBASE( pchr ) ) && !time_out ) continue;
7777 
7778         // detach the character from the game
7779         cleanup_one_character( pchr );
7780 
7781         // free the character's inventory
7782         free_inventory_in_game( cnt );
7783 
7784         // free the character
7785         free_one_character_in_game( cnt );
7786     }
7787 }
7788 
7789 //--------------------------------------------------------------------------------------------
bump_all_characters_update_counters()7790 void bump_all_characters_update_counters()
7791 {
7792     CHR_REF cnt;
7793 
7794     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
7795     {
7796         obj_data_t * pbase;
7797 
7798         pbase = POBJ_GET_PBASE( ChrList.lst + cnt );
7799         if ( !ACTIVE_PBASE( pbase ) ) continue;
7800 
7801         pbase->update_count++;
7802     }
7803 }
7804 
7805 //--------------------------------------------------------------------------------------------
is_invictus_direction(FACING_T direction,const CHR_REF character,Uint16 effects)7806 bool_t is_invictus_direction( FACING_T direction, const CHR_REF character, Uint16 effects )
7807 {
7808     FACING_T left, right;
7809 
7810     chr_t * pchr;
7811     cap_t * pcap;
7812     mad_t * pmad;
7813 
7814     bool_t  is_invictus;
7815 
7816     if ( !INGAME_CHR( character ) ) return btrue;
7817     pchr = ChrList.lst + character;
7818 
7819     pmad = chr_get_pmad( character );
7820     if ( NULL == pmad ) return btrue;
7821 
7822     pcap = chr_get_pcap( character );
7823     if ( NULL == pcap ) return btrue;
7824 
7825     // if the invictus flag is set, we are invictus
7826     if ( pchr->invictus ) return btrue;
7827 
7828     // if the effect is shield piercing, ignore shielding
7829     if ( HAS_SOME_BITS( effects, DAMFX_NBLOC ) ) return bfalse;
7830 
7831     // if the character's frame is invictus, then check the angles
7832     if ( HAS_SOME_BITS( chr_get_framefx( pchr ), MADFX_INVICTUS ) )
7833     {
7834         //I Frame
7835         direction -= pcap->iframefacing;
7836         left       = ( FACING_T )(( int )0x00010000L - ( int )pcap->iframeangle );
7837         right      = pcap->iframeangle;
7838 
7839         // If using shield, use the shield invictus instead
7840         if ( ACTION_IS_TYPE( pchr->inst.action_which, P ) )
7841         {
7842             bool_t parry_left = ( pchr->inst.action_which < ACTION_PC );
7843 
7844             // Using a shield?
7845             if ( parry_left )
7846             {
7847                 // Check left hand
7848                 cap_t * pcap_tmp = chr_get_pcap( pchr->holdingwhich[SLOT_LEFT] );
7849                 if ( NULL != pcap )
7850                 {
7851                     left  = ( FACING_T )(( int )0x00010000L - ( int )pcap_tmp->iframeangle );
7852                     right = pcap_tmp->iframeangle;
7853                 }
7854             }
7855             else
7856             {
7857                 // Check right hand
7858                 cap_t * pcap_tmp = chr_get_pcap( pchr->holdingwhich[SLOT_RIGHT] );
7859                 if ( NULL != pcap )
7860                 {
7861                     left  = ( FACING_T )(( int )0x00010000L - ( int )pcap_tmp->iframeangle );
7862                     right = pcap_tmp->iframeangle;
7863                 }
7864             }
7865         }
7866     }
7867     else
7868     {
7869         // N Frame
7870         direction -= pcap->nframefacing;
7871         left       = ( FACING_T )(( int )0x00010000L - ( int )pcap->nframeangle );
7872         right      = pcap->nframeangle;
7873     }
7874 
7875     // Check that direction
7876     is_invictus = bfalse;
7877     if ( direction <= left && direction <= right )
7878     {
7879         is_invictus = btrue;
7880     }
7881 
7882     return is_invictus;
7883 }
7884 
7885 //--------------------------------------------------------------------------------------------
slot_to_grip_offset(slot_t slot)7886 grip_offset_t slot_to_grip_offset( slot_t slot )
7887 {
7888     int retval = GRIP_ORIGIN;
7889 
7890     retval = ( slot + 1 ) * GRIP_VERTS;
7891 
7892     return ( grip_offset_t )retval;
7893 }
7894 
7895 //--------------------------------------------------------------------------------------------
grip_offset_to_slot(grip_offset_t grip_off)7896 slot_t grip_offset_to_slot( grip_offset_t grip_off )
7897 {
7898     slot_t retval = SLOT_LEFT;
7899 
7900     if ( 0 != grip_off % GRIP_VERTS )
7901     {
7902         // this does not correspond to a valid slot
7903         // coerce it to the "default" slot
7904         retval = SLOT_LEFT;
7905     }
7906     else
7907     {
7908         int islot = (( int )grip_off / GRIP_VERTS ) - 1;
7909 
7910         // coerce the slot number to fit within the valid range
7911         islot = CLIP( islot, 0, SLOT_COUNT );
7912 
7913         retval = ( slot_t ) islot;
7914     }
7915 
7916     return retval;
7917 }
7918 
7919 //--------------------------------------------------------------------------------------------
init_slot_idsz()7920 void init_slot_idsz()
7921 {
7922     inventory_idsz[INVEN_PACK]  = IDSZ_NONE;
7923     inventory_idsz[INVEN_NECK]  = MAKE_IDSZ( 'N', 'E', 'C', 'K' );
7924     inventory_idsz[INVEN_WRIS]  = MAKE_IDSZ( 'W', 'R', 'I', 'S' );
7925     inventory_idsz[INVEN_FOOT]  = MAKE_IDSZ( 'F', 'O', 'O', 'T' );
7926 }
7927 
7928 //--------------------------------------------------------------------------------------------
ai_add_order(ai_state_t * pai,Uint32 value,Uint16 counter)7929 bool_t ai_add_order( ai_state_t * pai, Uint32 value, Uint16 counter )
7930 {
7931     bool_t retval;
7932 
7933     if ( NULL == pai ) return bfalse;
7934 
7935     // this function is only truely valid if there is no other order
7936     retval = HAS_NO_BITS( pai->alert, ALERTIF_ORDERED );
7937 
7938     SET_BIT( pai->alert, ALERTIF_ORDERED );
7939     pai->order_value   = value;
7940     pai->order_counter = counter;
7941 
7942     return retval;
7943 }
7944 
7945 //--------------------------------------------------------------------------------------------
chr_add_billboard(const CHR_REF ichr,Uint32 lifetime_secs)7946 BBOARD_REF chr_add_billboard( const CHR_REF ichr, Uint32 lifetime_secs )
7947 {
7948     /// @details BB@> Attach a basic billboard to a character. You set the billboard texture
7949     ///     at any time after this. Returns the index of the billboard or INVALID_BILLBOARD
7950     ///     if the allocation fails.
7951     ///
7952     ///    must be called with a valid character, so be careful if you call this function from within
7953     ///    spawn_one_character()
7954 
7955     chr_t * pchr;
7956 
7957     if ( !INGAME_CHR( ichr ) ) return ( BBOARD_REF )INVALID_BILLBOARD;
7958     pchr = ChrList.lst + ichr;
7959 
7960     if ( INVALID_BILLBOARD != pchr->ibillboard )
7961     {
7962         BillboardList_free_one( REF_TO_INT( pchr->ibillboard ) );
7963         pchr->ibillboard = INVALID_BILLBOARD;
7964     }
7965 
7966     pchr->ibillboard = BillboardList_get_free( lifetime_secs );
7967 
7968     // attachr the billboard to the character
7969     if ( INVALID_BILLBOARD != pchr->ibillboard )
7970     {
7971         billboard_data_t * pbb = BillboardList.lst + pchr->ibillboard;
7972 
7973         pbb->ichr = ichr;
7974     }
7975 
7976     return pchr->ibillboard;
7977 }
7978 
7979 //--------------------------------------------------------------------------------------------
chr_make_text_billboard(const CHR_REF ichr,const char * txt,const SDL_Color text_color,const GLXvector4f tint,int lifetime_secs,BIT_FIELD opt_bits)7980 billboard_data_t * chr_make_text_billboard( const CHR_REF ichr, const char * txt, const SDL_Color text_color, const GLXvector4f tint, int lifetime_secs, BIT_FIELD opt_bits )
7981 {
7982     chr_t            * pchr;
7983     billboard_data_t * pbb;
7984     int                rv;
7985 
7986     BBOARD_REF ibb = ( BBOARD_REF )INVALID_BILLBOARD;
7987 
7988     if ( !INGAME_CHR( ichr ) ) return NULL;
7989     pchr = ChrList.lst + ichr;
7990 
7991     // create a new billboard or override the old billboard
7992     ibb = chr_add_billboard( ichr, lifetime_secs );
7993     if ( INVALID_BILLBOARD == ibb ) return NULL;
7994 
7995     pbb = BillboardList_get_ptr( pchr->ibillboard );
7996     if ( NULL == pbb ) return pbb;
7997 
7998     rv = billboard_data_printf_ttf( pbb, ui_getFont(), text_color, "%s", txt );
7999 
8000     if ( rv < 0 )
8001     {
8002         pchr->ibillboard = INVALID_BILLBOARD;
8003         BillboardList_free_one( REF_TO_INT( ibb ) );
8004         pbb = NULL;
8005     }
8006     else
8007     {
8008         // copy the tint over
8009         memmove( pbb->tint, tint, sizeof( GLXvector4f ) );
8010 
8011         if ( HAS_SOME_BITS( opt_bits, bb_opt_randomize_pos ) )
8012         {
8013             // make a random offset from the character
8014             pbb->offset[XX] = ((( rand() << 1 ) - RAND_MAX ) / ( float )RAND_MAX ) * GRID_FSIZE / 5.0f;
8015             pbb->offset[YY] = ((( rand() << 1 ) - RAND_MAX ) / ( float )RAND_MAX ) * GRID_FSIZE / 5.0f;
8016             pbb->offset[ZZ] = ((( rand() << 1 ) - RAND_MAX ) / ( float )RAND_MAX ) * GRID_FSIZE / 5.0f;
8017         }
8018 
8019         if ( HAS_SOME_BITS( opt_bits, bb_opt_randomize_vel ) )
8020         {
8021             // make the text fly away in a random direction
8022             pbb->offset_add[XX] += ((( rand() << 1 ) - RAND_MAX ) / ( float )RAND_MAX ) * 2.0f * GRID_FSIZE / lifetime_secs / TARGET_UPS;
8023             pbb->offset_add[YY] += ((( rand() << 1 ) - RAND_MAX ) / ( float )RAND_MAX ) * 2.0f * GRID_FSIZE / lifetime_secs / TARGET_UPS;
8024             pbb->offset_add[ZZ] += ((( rand() << 1 ) - RAND_MAX ) / ( float )RAND_MAX ) * 2.0f * GRID_FSIZE / lifetime_secs / TARGET_UPS;
8025         }
8026 
8027         if ( HAS_SOME_BITS( opt_bits, bb_opt_fade ) )
8028         {
8029             // make the billboard fade to transparency
8030             pbb->tint_add[AA] = -1.0f / lifetime_secs / TARGET_UPS;
8031         }
8032 
8033         if ( HAS_SOME_BITS( opt_bits, bb_opt_burn ) )
8034         {
8035             float minval, maxval;
8036 
8037             minval = MIN( MIN( pbb->tint[RR], pbb->tint[GG] ), pbb->tint[BB] );
8038             maxval = MAX( MAX( pbb->tint[RR], pbb->tint[GG] ), pbb->tint[BB] );
8039 
8040             if ( pbb->tint[RR] != maxval )
8041             {
8042                 pbb->tint_add[RR] = -pbb->tint[RR] / lifetime_secs / TARGET_UPS;
8043             }
8044 
8045             if ( pbb->tint[GG] != maxval )
8046             {
8047                 pbb->tint_add[GG] = -pbb->tint[GG] / lifetime_secs / TARGET_UPS;
8048             }
8049 
8050             if ( pbb->tint[BB] != maxval )
8051             {
8052                 pbb->tint_add[BB] = -pbb->tint[BB] / lifetime_secs / TARGET_UPS;
8053             }
8054         }
8055     }
8056 
8057     return pbb;
8058 }
8059 
8060 //--------------------------------------------------------------------------------------------
chr_get_name(const CHR_REF ichr,Uint32 bits)8061 const char * chr_get_name( const CHR_REF ichr, Uint32 bits )
8062 {
8063     static STRING szName;
8064 
8065     if ( !DEFINED_CHR( ichr ) )
8066     {
8067         // the default name
8068         strncpy( szName, "Unknown", SDL_arraysize( szName ) );
8069     }
8070     else
8071     {
8072         chr_t * pchr = ChrList.lst + ichr;
8073         cap_t * pcap = pro_get_pcap( pchr->profile_ref );
8074 
8075         if ( pchr->nameknown )
8076         {
8077             snprintf( szName, SDL_arraysize( szName ), "%s", pchr->Name );
8078         }
8079         else if ( NULL != pcap )
8080         {
8081             char lTmp;
8082 
8083             if ( 0 != ( bits & CHRNAME_ARTICLE ) )
8084             {
8085                 const char * article;
8086 
8087                 if ( 0 != ( bits & CHRNAME_DEFINITE ) )
8088                 {
8089                     article = "the";
8090                 }
8091                 else
8092                 {
8093                     lTmp = toupper( pcap->classname[0] );
8094 
8095                     if ( 'A' == lTmp || 'E' == lTmp || 'I' == lTmp || 'O' == lTmp || 'U' == lTmp )
8096                     {
8097                         article = "an";
8098                     }
8099                     else
8100                     {
8101                         article = "a";
8102                     }
8103                 }
8104 
8105                 snprintf( szName, SDL_arraysize( szName ), "%s %s", article, pcap->classname );
8106             }
8107             else
8108             {
8109                 snprintf( szName, SDL_arraysize( szName ), "%s", pcap->classname );
8110             }
8111         }
8112         else
8113         {
8114             strncpy( szName, "Invalid", SDL_arraysize( szName ) );
8115         }
8116     }
8117 
8118     if ( 0 != ( bits & CHRNAME_CAPITAL ) )
8119     {
8120         // capitalize the name ?
8121         szName[0] = toupper( szName[0] );
8122     }
8123 
8124     return szName;
8125 }
8126 
8127 //--------------------------------------------------------------------------------------------
chr_get_dir_name(const CHR_REF ichr)8128 const char * chr_get_dir_name( const CHR_REF ichr )
8129 {
8130     static STRING buffer = EMPTY_CSTR;
8131     chr_t * pchr;
8132 
8133     strncpy( buffer, "/debug", SDL_arraysize( buffer ) );
8134 
8135     if ( !DEFINED_CHR( ichr ) ) return buffer;
8136     pchr = ChrList.lst + ichr;
8137 
8138     if ( !LOADED_PRO( pchr->profile_ref ) )
8139     {
8140         char * sztmp;
8141 
8142         EGOBOO_ASSERT( bfalse );
8143 
8144         // copy the character's data.txt path
8145         strncpy( buffer, pchr->obj_base._name, SDL_arraysize( buffer ) );
8146 
8147         // the name should be "...some path.../data.txt"
8148         // grab the path
8149 
8150         sztmp = strstr( buffer, "/\\" );
8151         if ( NULL != sztmp ) *sztmp = CSTR_END;
8152     }
8153     else
8154     {
8155         pro_t * ppro = ProList.lst + pchr->profile_ref;
8156 
8157         // copy the character's data.txt path
8158         strncpy( buffer, ppro->name, SDL_arraysize( buffer ) );
8159     }
8160 
8161     return buffer;
8162 }
8163 
8164 //--------------------------------------------------------------------------------------------
chr_update_collision_size(chr_t * pchr,bool_t update_matrix)8165 egoboo_rv chr_update_collision_size( chr_t * pchr, bool_t update_matrix )
8166 {
8167     ///< @detalis BB@> use this function to update the pchr->chr_max_cv and  pchr->chr_min_cv with
8168     ///<       values that reflect the best possible collision volume
8169     ///<
8170     ///< @note This function takes quite a bit of time, so it must only be called when the
8171     ///< vertices change because of an animation or because the matrix changes.
8172     ///<
8173     ///< @todo it might be possible to cache the src[] array used in this function.
8174     ///< if the matrix changes, then you would not need to recalculate this data...
8175 
8176     int       vcount;   // the actual number of vertices, in case the object is square
8177     fvec4_t   src[16];  // for the upper and lower octagon points
8178     fvec4_t   dst[16];  // for the upper and lower octagon points
8179 
8180     int cnt;
8181     oct_bb_t bsrc, bdst, bmin;
8182 
8183     mad_t * pmad;
8184     cap_t * pcap;
8185 
8186     if ( !DEFINED_PCHR( pchr ) ) return rv_error;
8187 
8188     // re-initialize the collision volumes
8189     oct_bb_ctor( &( pchr->chr_min_cv ) );
8190     oct_bb_ctor( &( pchr->chr_max_cv ) );
8191     for ( cnt = 0; cnt < SLOT_COUNT; cnt++ )
8192     {
8193         oct_bb_ctor( pchr->slot_cv + cnt );
8194     }
8195 
8196     pcap = pro_get_pcap( pchr->profile_ref );
8197     if ( NULL == pcap ) return rv_error;
8198 
8199     pmad = chr_get_pmad( GET_REF_PCHR( pchr ) );
8200     if ( NULL == pmad ) return rv_error;
8201 
8202     // make sure the matrix is updated properly
8203     if ( update_matrix )
8204     {
8205         // call chr_update_matrix() but pass in a false value to prevent a recursize call
8206         if ( rv_error == chr_update_matrix( pchr, bfalse ) )
8207         {
8208             return rv_error;
8209         }
8210     }
8211 
8212     // make sure the bounding box is calculated properly
8213     if ( rv_error == chr_instance_update_bbox( &( pchr->inst ) ) )
8214     {
8215         return rv_error;
8216     }
8217 
8218     // convert the point cloud in the GLvertex array (pchr->inst.vrt_lst) to
8219     // a level 1 bounding box. Subtract off the position of the character
8220     memcpy( &bsrc, &( pchr->inst.bbox ), sizeof( bsrc ) );
8221 
8222     // convert the corners of the level 1 bounding box to a point cloud
8223     vcount = oct_bb_to_points( &bsrc, src, 16 );
8224 
8225     // transform the new point cloud
8226     TransformVertices( &( pchr->inst.matrix ), src, dst, vcount );
8227 
8228     // convert the new point cloud into a level 1 bounding box
8229     points_to_oct_bb( &bdst, dst, vcount );
8230 
8231     //---- set the bounding boxes
8232     oct_bb_copy( &( pchr->chr_min_cv ), &bdst );
8233     oct_bb_copy( &( pchr->chr_max_cv ), &bdst );
8234 
8235     oct_bb_set_bumper( &bmin, pchr->bump );
8236 
8237     // only use pchr->bump.size if it was overridden in data.txt through the [MODL] expansion
8238     if ( pcap->bump_override_size )
8239     {
8240         oct_bb_self_intersection_index( &( pchr->chr_min_cv ), &bmin, OCT_X );
8241         oct_bb_self_intersection_index( &( pchr->chr_min_cv ), &bmin, OCT_Y );
8242 
8243         oct_bb_self_union_index( &( pchr->chr_max_cv ), &bmin, OCT_X );
8244         oct_bb_self_union_index( &( pchr->chr_max_cv ), &bmin, OCT_Y );
8245     }
8246 
8247     // only use pchr->bump.size_big if it was overridden in data.txt through the [MODL] expansion
8248     if ( pcap->bump_override_sizebig )
8249     {
8250         oct_bb_self_intersection_index( &( pchr->chr_min_cv ), &bmin, OCT_XY );
8251         oct_bb_self_intersection_index( &( pchr->chr_min_cv ), &bmin, OCT_YX );
8252 
8253         oct_bb_self_union_index( &( pchr->chr_max_cv ), &bmin, OCT_XY );
8254         oct_bb_self_union_index( &( pchr->chr_max_cv ), &bmin, OCT_YX );
8255     }
8256 
8257     // only use pchr->bump.height if it was overridden in data.txt through the [MODL] expansion
8258     if ( pcap->bump_override_height )
8259     {
8260         oct_bb_self_intersection_index( &( pchr->chr_min_cv ), &bmin, OCT_Z );
8261 
8262         oct_bb_self_union_index( &( pchr->chr_max_cv ), &bmin, OCT_Z );
8263     }
8264 
8265     //// raise the upper bound for platforms
8266     //if ( pchr->platform )
8267     //{
8268     //    pchr->chr_max_cv.maxs[OCT_Z] += PLATTOLERANCE;
8269     //}
8270 
8271     // calculate collision volumes for various slots
8272     for ( cnt = 0; cnt < SLOT_COUNT; cnt++ )
8273     {
8274         if ( !pcap->slotvalid[ cnt ] ) continue;
8275 
8276         chr_calc_grip_cv( pchr, GRIP_LEFT, pchr->slot_cv + cnt, NULL, NULL, bfalse );
8277 
8278         oct_bb_self_union( &( pchr->chr_max_cv ), pchr->slot_cv + cnt );
8279     }
8280 
8281     // convert the level 1 bounding box to a level 0 bounding box
8282     oct_bb_downgrade( &bdst, pchr->bump_stt, pchr->bump, &( pchr->bump_1 ), NULL );
8283 
8284     return rv_success;
8285 }
8286 
8287 //--------------------------------------------------------------------------------------------
describe_value(float value,float maxval,int * rank_ptr)8288 const char* describe_value( float value, float maxval, int * rank_ptr )
8289 {
8290     /// @details ZF@> This converts a stat number into a more descriptive word
8291 
8292     static STRING retval;
8293 
8294     float fval;
8295     int local_rank;
8296 
8297     if ( NULL == rank_ptr ) rank_ptr = &local_rank;
8298 
8299     if ( cfg.feedback == FEEDBACK_NUMBER )
8300     {
8301         snprintf( retval, SDL_arraysize( retval ), "%2.1f", value );
8302         return retval;
8303     }
8304 
8305     fval = ( 0 == maxval ) ? 1.0f : value / maxval;
8306 
8307     *rank_ptr = -5;
8308     strcpy( retval, "Unknown" );
8309 
8310     if ( fval >= .83 )        { strcpy( retval, "Godlike!" );   *rank_ptr =  8; }
8311     else if ( fval >= .66 ) { strcpy( retval, "Ultimate" );   *rank_ptr =  7; }
8312     else if ( fval >= .56 ) { strcpy( retval, "Epic" );       *rank_ptr =  6; }
8313     else if ( fval >= .50 ) { strcpy( retval, "Powerful" );   *rank_ptr =  5; }
8314     else if ( fval >= .43 ) { strcpy( retval, "Heroic" );     *rank_ptr =  4; }
8315     else if ( fval >= .36 ) { strcpy( retval, "Very High" );  *rank_ptr =  3; }
8316     else if ( fval >= .30 ) { strcpy( retval, "High" );       *rank_ptr =  2; }
8317     else if ( fval >= .23 ) { strcpy( retval, "Good" );       *rank_ptr =  1; }
8318     else if ( fval >= .17 ) { strcpy( retval, "Average" );    *rank_ptr =  0; }
8319     else if ( fval >= .11 ) { strcpy( retval, "Pretty Low" ); *rank_ptr = -1; }
8320     else if ( fval >= .07 ) { strcpy( retval, "Bad" );        *rank_ptr = -2; }
8321     else if ( fval >  .00 ) { strcpy( retval, "Terrible" );   *rank_ptr = -3; }
8322     else                    { strcpy( retval, "None" );       *rank_ptr = -4; }
8323 
8324     return retval;
8325 }
8326 
8327 //--------------------------------------------------------------------------------------------
describe_damage(float value,float maxval,int * rank_ptr)8328 const char* describe_damage( float value, float maxval, int * rank_ptr )
8329 {
8330     /// @details ZF@> This converts a damage value into a more descriptive word
8331 
8332     static STRING retval;
8333 
8334     float fval;
8335     int local_rank;
8336 
8337     if ( NULL == rank_ptr ) rank_ptr = &local_rank;
8338 
8339     if ( cfg.feedback == FEEDBACK_NUMBER )
8340     {
8341         snprintf( retval, SDL_arraysize( retval ), "%2.1f", FP8_TO_FLOAT( value ) );
8342         return retval;
8343     }
8344 
8345     fval = ( 0 == maxval ) ? 1.0f : value / maxval;
8346 
8347     *rank_ptr = -5;
8348     strcpy( retval, "Unknown" );
8349 
8350     if ( fval >= 1.50f )      { strcpy( retval, "Annihilation!" ); *rank_ptr =  5; }
8351     else if ( fval >= 1.00f ) { strcpy( retval, "Overkill!" );     *rank_ptr =  4; }
8352     else if ( fval >= 0.80f ) { strcpy( retval, "Deadly" );        *rank_ptr =  3; }
8353     else if ( fval >= 0.70f ) { strcpy( retval, "Crippling" );     *rank_ptr =  2; }
8354     else if ( fval >= 0.50f ) { strcpy( retval, "Devastating" );   *rank_ptr =  1; }
8355     else if ( fval >= 0.25f ) { strcpy( retval, "Hurtful" );       *rank_ptr =  0; }
8356     else if ( fval >= 0.10f ) { strcpy( retval, "A Scratch" );     *rank_ptr = -1; }
8357     else if ( fval >= 0.05f ) { strcpy( retval, "Ticklish" );      *rank_ptr = -2; }
8358     else if ( fval >= 0.00f ) { strcpy( retval, "Meh..." );        *rank_ptr = -3; }
8359 
8360     return retval;
8361 }
8362 
8363 //--------------------------------------------------------------------------------------------
describe_wounds(float max,float current)8364 const char* describe_wounds( float max, float current )
8365 {
8366     /// @details ZF@> This tells us how badly someone is injured
8367 
8368     static STRING retval;
8369     float fval;
8370 
8371     //Is it already dead?
8372     if ( current <= 0 )
8373     {
8374         strcpy( retval, "Dead!" );
8375         return retval;
8376     }
8377 
8378     //Calculate the percentage
8379     if ( 0 == max ) return NULL;
8380     fval = ( current / max ) * 100;
8381 
8382     if ( cfg.feedback == FEEDBACK_NUMBER )
8383     {
8384         snprintf( retval, SDL_arraysize( retval ), "%2.1f", FP8_TO_FLOAT( current ) );
8385         return retval;
8386     }
8387 
8388     strcpy( retval, "Uninjured" );
8389     if ( fval <= 5 )            strcpy( retval, "Almost Dead!" );
8390     else if ( fval <= 10 )        strcpy( retval, "Near Death" );
8391     else if ( fval <= 25 )        strcpy( retval, "Mortally Wounded" );
8392     else if ( fval <= 40 )        strcpy( retval, "Badly Wounded" );
8393     else if ( fval <= 60 )        strcpy( retval, "Injured" );
8394     else if ( fval <= 75 )        strcpy( retval, "Hurt" );
8395     else if ( fval <= 90 )        strcpy( retval, "Bruised" );
8396     else if ( fval < 100 )        strcpy( retval, "Scratched" );
8397 
8398     return retval;
8399 }
8400 
8401 //--------------------------------------------------------------------------------------------
chr_get_icon_ref(const CHR_REF item)8402 TX_REF chr_get_icon_ref( const CHR_REF item )
8403 {
8404     /// @details BB@> Get the index to the icon texture (in TxTexture) that is supposed to be used with this object.
8405     ///               If none can be found, return the index to the texture of the null icon.
8406 
8407     size_t iskin;
8408     TX_REF icon_ref = ( TX_REF )ICON_NULL;
8409     bool_t is_spell_fx, is_book, draw_book;
8410 
8411     cap_t * pitem_cap;
8412     chr_t * pitem;
8413     pro_t * pitem_pro;
8414 
8415     if ( !DEFINED_CHR( item ) ) return icon_ref;
8416     pitem = ChrList.lst + item;
8417 
8418     if ( !LOADED_PRO( pitem->profile_ref ) ) return icon_ref;
8419     pitem_pro = ProList.lst + pitem->profile_ref;
8420 
8421     pitem_cap = pro_get_pcap( pitem->profile_ref );
8422     if ( NULL == pitem_cap ) return icon_ref;
8423 
8424     // what do we need to draw?
8425     is_spell_fx = ( NO_SKIN_OVERRIDE != pitem_cap->spelleffect_type );     // the value of spelleffect_type == the skin of the book or -1 for not a spell effect
8426     is_book     = ( SPELLBOOK == pitem->profile_ref );
8427     draw_book   = ( is_book || ( is_spell_fx && !pitem->draw_icon ) /*|| ( is_spell_fx && MAX_CHR != pitem->attachedto )*/ ) && ( bookicon_count > 0 ); //>ZF> uncommented a part because this caused a icon bug when you were morphed and mounted
8428 
8429     if ( !draw_book )
8430     {
8431         iskin = pitem->skin;
8432 
8433         icon_ref = pitem_pro->ico_ref[iskin];
8434     }
8435     else if ( draw_book )
8436     {
8437         iskin = 0;
8438 
8439         if ( NO_SKIN_OVERRIDE != pitem_cap->spelleffect_type )
8440         {
8441             iskin = pitem_cap->spelleffect_type;
8442         }
8443         else if ( NO_SKIN_OVERRIDE != pitem_cap->skin_override )
8444         {
8445             iskin = pitem_cap->skin_override;
8446         }
8447 
8448         iskin = CLIP( iskin, 0, bookicon_count );
8449 
8450         icon_ref = bookicon_ref[ iskin ];
8451     }
8452 
8453     return icon_ref;
8454 }
8455 
8456 //--------------------------------------------------------------------------------------------
8457 //--------------------------------------------------------------------------------------------
init_all_cap()8458 void init_all_cap()
8459 {
8460     /// @details BB@> initialize every character profile in the game
8461 
8462     CAP_REF cnt;
8463 
8464     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
8465     {
8466         cap_init( CapStack.lst + cnt );
8467     }
8468 }
8469 
8470 //--------------------------------------------------------------------------------------------
release_all_cap()8471 void release_all_cap()
8472 {
8473     /// @details BB@> release every character profile in the game
8474 
8475     CAP_REF cnt;
8476 
8477     for ( cnt = 0; cnt < MAX_PROFILE; cnt++ )
8478     {
8479         release_one_cap( cnt );
8480     };
8481 }
8482 
8483 //--------------------------------------------------------------------------------------------
release_one_cap(const CAP_REF icap)8484 bool_t release_one_cap( const CAP_REF icap )
8485 {
8486     /// @details BB@> release any allocated data and return all values to safe values
8487 
8488     cap_t * pcap;
8489 
8490     if ( !VALID_CAP_RANGE( icap ) ) return bfalse;
8491     pcap = CapStack.lst + icap;
8492 
8493     if ( !pcap->loaded ) return btrue;
8494 
8495     cap_init( pcap );
8496 
8497     pcap->loaded  = bfalse;
8498     pcap->name[0] = CSTR_END;
8499 
8500     return btrue;
8501 }
8502 
8503 //--------------------------------------------------------------------------------------------
reset_teams()8504 void reset_teams()
8505 {
8506     /// @details ZZ@> This function makes everyone hate everyone else
8507 
8508     TEAM_REF teama, teamb;
8509 
8510     for ( teama = 0; teama < TEAM_MAX; teama++ )
8511     {
8512         // Make the team hate everyone
8513         for ( teamb = 0; teamb < TEAM_MAX; teamb++ )
8514         {
8515             TeamStack.lst[teama].hatesteam[REF_TO_INT( teamb )] = btrue;
8516         }
8517 
8518         // Make the team like itself
8519         TeamStack.lst[teama].hatesteam[REF_TO_INT( teama )] = bfalse;
8520 
8521         // Set defaults
8522         TeamStack.lst[teama].leader = NOLEADER;
8523         TeamStack.lst[teama].sissy = 0;
8524         TeamStack.lst[teama].morale = 0;
8525     }
8526 
8527     // Keep the null team neutral
8528     for ( teama = 0; teama < TEAM_MAX; teama++ )
8529     {
8530         TeamStack.lst[teama].hatesteam[TEAM_NULL] = bfalse;
8531         TeamStack.lst[( TEAM_REF )TEAM_NULL].hatesteam[REF_TO_INT( teama )] = bfalse;
8532     }
8533 }
8534 
8535 //--------------------------------------------------------------------------------------------
chr_teleport(const CHR_REF ichr,float x,float y,float z,FACING_T facing_z)8536 bool_t chr_teleport( const CHR_REF ichr, float x, float y, float z, FACING_T facing_z )
8537 {
8538     /// @details BB@> Determine whether the character can be teleported to the specified location
8539     ///               and do it, if possible. Success returns btrue, failure returns bfalse;
8540 
8541     chr_t  * pchr;
8542     FACING_T facing_old, facing_new;
8543     fvec3_t  pos_old, pos_new;
8544     bool_t   retval;
8545 
8546     if ( !INGAME_CHR( ichr ) ) return bfalse;
8547     pchr = ChrList.lst + ichr;
8548 
8549     if ( x < 0.0f || x > PMesh->gmem.edge_x ) return bfalse;
8550     if ( y < 0.0f || y > PMesh->gmem.edge_y ) return bfalse;
8551 
8552     pos_old  = chr_get_pos( pchr );
8553     facing_old = pchr->ori.facing_z;
8554 
8555     pos_new.x  = x;
8556     pos_new.y  = y;
8557     pos_new.z  = z;
8558     facing_new = facing_z;
8559 
8560     if ( chr_hit_wall( pchr, pos_new.v, NULL, NULL, NULL ) )
8561     {
8562         // No it didn't...
8563         chr_set_pos( pchr, pos_old.v );
8564         pchr->ori.facing_z = facing_old;
8565 
8566         retval = bfalse;
8567     }
8568     else
8569     {
8570         // Yeah!  It worked!
8571 
8572         // update the old position
8573         pchr->pos_old          = pos_new;
8574         pchr->ori_old.facing_z = facing_new;
8575 
8576         // update the new position
8577         chr_set_pos( pchr, pos_new.v );
8578         pchr->ori.facing_z = facing_new;
8579 
8580         if ( !detach_character_from_mount( ichr, btrue, bfalse ) )
8581         {
8582             // detach_character_from_mount() updates the character matrix unless it is not mounted
8583             chr_update_matrix( pchr, btrue );
8584         }
8585 
8586         retval = btrue;
8587     }
8588 
8589     return retval;
8590 }
8591 
8592 //--------------------------------------------------------------------------------------------
chr_request_terminate(const CHR_REF ichr)8593 bool_t chr_request_terminate( const CHR_REF ichr )
8594 {
8595     /// @details BB@> Mark this character for deletion
8596 
8597     if ( !DEFINED_CHR( ichr ) ) return bfalse;
8598 
8599     POBJ_REQUEST_TERMINATE( ChrList.lst + ichr );
8600 
8601     return btrue;
8602 }
8603 
8604 //--------------------------------------------------------------------------------------------
chr_update_hide(chr_t * pchr)8605 chr_t * chr_update_hide( chr_t * pchr )
8606 {
8607     /// @details BB@> Update the hide state of the character. Should be called every time that
8608     ///               the state variable in an ai_state_t structure is updated
8609 
8610     Sint8 hide;
8611     cap_t * pcap;
8612 
8613     if ( !DEFINED_PCHR( pchr ) ) return pchr;
8614 
8615     hide = NOHIDE;
8616     pcap = chr_get_pcap( GET_REF_PCHR( pchr ) );
8617     if ( NULL != pcap )
8618     {
8619         hide = pcap->hidestate;
8620     }
8621 
8622     pchr->is_hidden = bfalse;
8623     if ( hide != NOHIDE && hide == pchr->ai.state )
8624     {
8625         pchr->is_hidden = btrue;
8626     }
8627 
8628     return pchr;
8629 }
8630 
8631 //--------------------------------------------------------------------------------------------
ai_state_set_changed(ai_state_t * pai)8632 bool_t ai_state_set_changed( ai_state_t * pai )
8633 {
8634     /// @details BB@> do something tricky here
8635 
8636     bool_t retval = bfalse;
8637 
8638     if ( NULL == pai ) return bfalse;
8639 
8640     if ( HAS_NO_BITS( pai->alert, ALERTIF_CHANGED ) )
8641     {
8642         SET_BIT( pai->alert, ALERTIF_CHANGED );
8643         retval = btrue;
8644     }
8645 
8646     if ( !pai->changed )
8647     {
8648         pai->changed = btrue;
8649         retval = btrue;
8650     }
8651 
8652     return retval;
8653 }
8654 
8655 //--------------------------------------------------------------------------------------------
chr_matrix_valid(chr_t * pchr)8656 bool_t chr_matrix_valid( chr_t * pchr )
8657 {
8658     /// @details BB@> Determine whether the character has a valid matrix
8659 
8660     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
8661 
8662     // both the cache and the matrix need to be valid
8663     return pchr->inst.matrix_cache.valid && pchr->inst.matrix_cache.matrix_valid;
8664 }
8665 
8666 //--------------------------------------------------------------------------------------------
8667 //--------------------------------------------------------------------------------------------
get_grip_verts(Uint16 grip_verts[],const CHR_REF imount,int vrt_offset)8668 int get_grip_verts( Uint16 grip_verts[], const CHR_REF imount, int vrt_offset )
8669 {
8670     /// @details BB@> Fill the grip_verts[] array from the mount's data.
8671     ///     Return the number of vertices found.
8672 
8673     Uint32  i;
8674     int vrt_count, tnc;
8675 
8676     chr_t * pmount;
8677     mad_t * pmount_mad;
8678 
8679     if ( NULL == grip_verts ) return 0;
8680 
8681     // set all the vertices to a "safe" value
8682     for ( i = 0; i < GRIP_VERTS; i++ )
8683     {
8684         grip_verts[i] = 0xFFFF;
8685     }
8686 
8687     if ( !INGAME_CHR( imount ) ) return 0;
8688     pmount = ChrList.lst + imount;
8689 
8690     pmount_mad = chr_get_pmad( imount );
8691     if ( NULL == pmount_mad ) return 0;
8692 
8693     if ( 0 == pmount->inst.vrt_count ) return 0;
8694 
8695     //---- set the proper weapongrip vertices
8696     tnc = ( int )pmount->inst.vrt_count - ( int )vrt_offset;
8697 
8698     // if the starting vertex is less than 0, just take the first vertex
8699     if ( tnc < 0 )
8700     {
8701         grip_verts[0] = 0;
8702         return 1;
8703     }
8704 
8705     vrt_count = 0;
8706     for ( i = 0; i < GRIP_VERTS; i++ )
8707     {
8708         if ( tnc + i < pmount->inst.vrt_count )
8709         {
8710             grip_verts[i] = tnc + i;
8711             vrt_count++;
8712         }
8713         else
8714         {
8715             grip_verts[i] = 0xFFFF;
8716         }
8717     }
8718 
8719     return vrt_count;
8720 }
8721 
8722 //--------------------------------------------------------------------------------------------
chr_get_matrix_cache(chr_t * pchr,matrix_cache_t * mc_tmp)8723 bool_t chr_get_matrix_cache( chr_t * pchr, matrix_cache_t * mc_tmp )
8724 {
8725     /// @details BB@> grab the matrix cache data for a given character and put it into mc_tmp.
8726 
8727     bool_t handled;
8728     CHR_REF itarget, ichr;
8729 
8730     if ( NULL == mc_tmp ) return bfalse;
8731     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
8732     ichr = GET_REF_PCHR( pchr );
8733 
8734     handled = bfalse;
8735     itarget = ( CHR_REF )MAX_CHR;
8736 
8737     // initialize xome parameters in case we fail
8738     mc_tmp->valid     = bfalse;
8739     mc_tmp->type_bits = MAT_UNKNOWN;
8740 
8741     mc_tmp->self_scale.x = mc_tmp->self_scale.y = mc_tmp->self_scale.z = pchr->fat;
8742 
8743     // handle the overlay first of all
8744     if ( !handled && pchr->is_overlay && ichr != pchr->ai.target && INGAME_CHR( pchr->ai.target ) )
8745     {
8746         // this will pretty much fail the cmp_matrix_cache() every time...
8747 
8748         chr_t * ptarget = ChrList.lst + pchr->ai.target;
8749 
8750         // make sure we have the latst info from the target
8751         chr_update_matrix( ptarget, btrue );
8752 
8753         // grab the matrix cache into from the character we are overlaying
8754         memcpy( mc_tmp, &( ptarget->inst.matrix_cache ), sizeof( matrix_cache_t ) );
8755 
8756         // just in case the overlay's matrix cannot be corrected
8757         // then treat it as if it is not an overlay
8758         handled = mc_tmp->valid;
8759     }
8760 
8761     // this will happen if the overlay "failed" or for any non-overlay character
8762     if ( !handled )
8763     {
8764         // assume that the "target" of the MAT_CHARACTER data will be the character itself
8765         itarget = GET_REF_PCHR( pchr );
8766 
8767         //---- update the MAT_WEAPON data
8768         if ( DEFINED_CHR( pchr->attachedto ) )
8769         {
8770             chr_t * pmount = ChrList.lst + pchr->attachedto;
8771 
8772             // make sure we have the latst info from the target
8773             chr_update_matrix( pmount, btrue );
8774 
8775             // just in case the mounts's matrix cannot be corrected
8776             // then treat it as if it is not mounted... yuck
8777             if ( pmount->inst.matrix_cache.matrix_valid )
8778             {
8779                 mc_tmp->valid     = btrue;
8780                 SET_BIT( mc_tmp->type_bits, MAT_WEAPON );        // add in the weapon data
8781 
8782                 mc_tmp->grip_chr  = pchr->attachedto;
8783                 mc_tmp->grip_slot = pchr->inwhich_slot;
8784                 get_grip_verts( mc_tmp->grip_verts, pchr->attachedto, slot_to_grip_offset( pchr->inwhich_slot ) );
8785 
8786                 itarget = pchr->attachedto;
8787             }
8788         }
8789 
8790         //---- update the MAT_CHARACTER data
8791         if ( DEFINED_CHR( itarget ) )
8792         {
8793             chr_t * ptarget = ChrList.lst + itarget;
8794 
8795             mc_tmp->valid   = btrue;
8796             SET_BIT( mc_tmp->type_bits, MAT_CHARACTER );  // add in the MAT_CHARACTER-type data for the object we are "connected to"
8797 
8798             mc_tmp->rotate.x = CLIP_TO_16BITS( ptarget->ori.map_facing_x - MAP_TURN_OFFSET );
8799             mc_tmp->rotate.y = CLIP_TO_16BITS( ptarget->ori.map_facing_y - MAP_TURN_OFFSET );
8800             mc_tmp->rotate.z = ptarget->ori.facing_z;
8801 
8802             mc_tmp->pos = chr_get_pos( ptarget );
8803 
8804             mc_tmp->grip_scale.x = mc_tmp->grip_scale.y = mc_tmp->grip_scale.z = ptarget->fat;
8805         }
8806     }
8807 
8808     return mc_tmp->valid;
8809 }
8810 
8811 //--------------------------------------------------------------------------------------------
convert_grip_to_local_points(chr_t * pholder,Uint16 grip_verts[],fvec4_t dst_point[])8812 int convert_grip_to_local_points( chr_t * pholder, Uint16 grip_verts[], fvec4_t dst_point[] )
8813 {
8814     /// @details ZZ@> a helper function for apply_one_weapon_matrix()
8815 
8816     int cnt, point_count;
8817 
8818     if ( NULL == grip_verts || NULL == dst_point ) return 0;
8819 
8820     if ( !ACTIVE_PCHR( pholder ) ) return 0;
8821 
8822     // count the valid weapon connection dst_points
8823     point_count = 0;
8824     for ( cnt = 0; cnt < GRIP_VERTS; cnt++ )
8825     {
8826         if ( 0xFFFF != grip_verts[cnt] )
8827         {
8828             point_count++;
8829         }
8830     }
8831 
8832     // do the best we can
8833     if ( 0 == point_count )
8834     {
8835         // punt! attach to origin
8836         dst_point[0].x = pholder->pos.x;
8837         dst_point[0].y = pholder->pos.y;
8838         dst_point[0].z = pholder->pos.z;
8839         dst_point[0].w = 1;
8840 
8841         point_count = 1;
8842     }
8843     else
8844     {
8845         // update the grip vertices
8846         chr_instance_update_grip_verts( &( pholder->inst ), grip_verts, GRIP_VERTS );
8847 
8848         // copy the vertices into dst_point[]
8849         for ( point_count = 0, cnt = 0; cnt < GRIP_VERTS; cnt++, point_count++ )
8850         {
8851             Uint16 vertex = grip_verts[cnt];
8852 
8853             if ( 0xFFFF == vertex ) continue;
8854 
8855             dst_point[point_count].x = pholder->inst.vrt_lst[vertex].pos[XX];
8856             dst_point[point_count].y = pholder->inst.vrt_lst[vertex].pos[YY];
8857             dst_point[point_count].z = pholder->inst.vrt_lst[vertex].pos[ZZ];
8858             dst_point[point_count].w = 1.0f;
8859         }
8860     }
8861 
8862     return point_count;
8863 }
8864 
8865 //--------------------------------------------------------------------------------------------
convert_grip_to_global_points(const CHR_REF iholder,Uint16 grip_verts[],fvec4_t dst_point[])8866 int convert_grip_to_global_points( const CHR_REF iholder, Uint16 grip_verts[], fvec4_t   dst_point[] )
8867 {
8868     /// @details ZZ@> a helper function for apply_one_weapon_matrix()
8869 
8870     chr_t *   pholder;
8871     int       point_count;
8872     fvec4_t   src_point[GRIP_VERTS];
8873 
8874     if ( !INGAME_CHR( iholder ) ) return 0;
8875     pholder = ChrList.lst + iholder;
8876 
8877     // find the grip points in the character's "local" or "body-fixed" coordinates
8878     point_count = convert_grip_to_local_points( pholder, grip_verts, src_point );
8879 
8880     if ( 0 == point_count ) return 0;
8881 
8882     // use the math function instead of rolling out own
8883     TransformVertices( &( pholder->inst.matrix ), src_point, dst_point, point_count );
8884 
8885     return point_count;
8886 }
8887 
8888 //--------------------------------------------------------------------------------------------
apply_one_weapon_matrix(chr_t * pweap,matrix_cache_t * mc_tmp)8889 bool_t apply_one_weapon_matrix( chr_t * pweap, matrix_cache_t * mc_tmp )
8890 {
8891     /// @details ZZ@> Request that the data in the matrix cache be used to create a "character matrix".
8892     ///               i.e. a matrix that is not being held by anything.
8893 
8894     fvec4_t   nupoint[GRIP_VERTS];
8895     int       iweap_points;
8896 
8897     chr_t * pholder;
8898     matrix_cache_t * pweap_mcache;
8899 
8900     if ( NULL == mc_tmp || !mc_tmp->valid || 0 == ( MAT_WEAPON & mc_tmp->type_bits ) ) return bfalse;
8901 
8902     if ( !DEFINED_PCHR( pweap ) ) return bfalse;
8903     pweap_mcache = &( pweap->inst.matrix_cache );
8904 
8905     if ( !INGAME_CHR( mc_tmp->grip_chr ) ) return bfalse;
8906     pholder = ChrList.lst + mc_tmp->grip_chr;
8907 
8908     // make sure that the matrix is invalid incase of an error
8909     pweap_mcache->matrix_valid = bfalse;
8910 
8911     // grab the grip points in world coordinates
8912     iweap_points = convert_grip_to_global_points( mc_tmp->grip_chr, mc_tmp->grip_verts, nupoint );
8913 
8914     if ( 4 == iweap_points )
8915     {
8916         // Calculate weapon's matrix based on positions of grip points
8917         // chrscale is recomputed at time of attachment
8918         pweap->inst.matrix = FourPoints( nupoint[0].v, nupoint[1].v, nupoint[2].v, nupoint[3].v, mc_tmp->self_scale.z );
8919 
8920         // update the weapon position
8921         chr_set_pos( pweap, nupoint[3].v );
8922 
8923         memcpy( &( pweap->inst.matrix_cache ), mc_tmp, sizeof( matrix_cache_t ) );
8924 
8925         pweap_mcache->matrix_valid = btrue;
8926     }
8927     else if ( iweap_points > 0 )
8928     {
8929         // cannot find enough vertices. punt.
8930         // ignore the shape of the grip and just stick the character to the single mount point
8931 
8932         // update the character position
8933         chr_set_pos( pweap, nupoint[0].v );
8934 
8935         // make sure we have the right data
8936         chr_get_matrix_cache( pweap, mc_tmp );
8937 
8938         // add in the appropriate mods
8939         // this is a hybrid character and weapon matrix
8940         SET_BIT( mc_tmp->type_bits, MAT_CHARACTER );
8941 
8942         // treat it like a normal character matrix
8943         apply_one_character_matrix( pweap, mc_tmp );
8944     }
8945 
8946     return pweap_mcache->matrix_valid;
8947 }
8948 
8949 //--------------------------------------------------------------------------------------------
apply_one_character_matrix(chr_t * pchr,matrix_cache_t * mc_tmp)8950 bool_t apply_one_character_matrix( chr_t * pchr, matrix_cache_t * mc_tmp )
8951 {
8952     /// @details ZZ@> Request that the matrix cache data be used to create a "weapon matrix".
8953     ///               i.e. a matrix that is attached to a specific grip.
8954 
8955     if ( NULL == mc_tmp ) return bfalse;
8956 
8957     // only apply character matrices using this function
8958     if ( 0 == ( MAT_CHARACTER & mc_tmp->type_bits ) ) return bfalse;
8959 
8960     pchr->inst.matrix_cache.matrix_valid = bfalse;
8961 
8962     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
8963 
8964     if ( pchr->stickybutt )
8965     {
8966         pchr->inst.matrix = ScaleXYZRotateXYZTranslate_SpaceFixed( mc_tmp->self_scale.x, mc_tmp->self_scale.y, mc_tmp->self_scale.z,
8967                             TO_TURN( mc_tmp->rotate.z ), TO_TURN( mc_tmp->rotate.x ), TO_TURN( mc_tmp->rotate.y ),
8968                             mc_tmp->pos.x, mc_tmp->pos.y, mc_tmp->pos.z );
8969     }
8970     else
8971     {
8972         pchr->inst.matrix = ScaleXYZRotateXYZTranslate_BodyFixed( mc_tmp->self_scale.x, mc_tmp->self_scale.y, mc_tmp->self_scale.z,
8973                             TO_TURN( mc_tmp->rotate.z ), TO_TURN( mc_tmp->rotate.x ), TO_TURN( mc_tmp->rotate.y ),
8974                             mc_tmp->pos.x, mc_tmp->pos.y, mc_tmp->pos.z );
8975     }
8976 
8977     memcpy( &( pchr->inst.matrix_cache ), mc_tmp, sizeof( matrix_cache_t ) );
8978 
8979     pchr->inst.matrix_cache.matrix_valid = btrue;
8980 
8981     return btrue;
8982 }
8983 
8984 //--------------------------------------------------------------------------------------------
apply_reflection_matrix(chr_instance_t * pinst,float grid_level)8985 bool_t apply_reflection_matrix( chr_instance_t * pinst, float grid_level )
8986 {
8987     /// @detalis BB@> Generate the extra data needed to display a reflection for this character
8988 
8989     if ( NULL == pinst ) return bfalse;
8990 
8991     // invalidate the current matrix
8992     pinst->ref.matrix_valid = bfalse;
8993 
8994     // actually flip the matrix
8995     if ( pinst->matrix_cache.valid )
8996     {
8997         pinst->ref.matrix = pinst->matrix;
8998 
8999         pinst->ref.matrix.CNV( 0, 2 ) = -pinst->ref.matrix.CNV( 0, 2 );
9000         pinst->ref.matrix.CNV( 1, 2 ) = -pinst->ref.matrix.CNV( 1, 2 );
9001         pinst->ref.matrix.CNV( 2, 2 ) = -pinst->ref.matrix.CNV( 2, 2 );
9002         pinst->ref.matrix.CNV( 3, 2 ) = 2 * grid_level - pinst->ref.matrix.CNV( 3, 2 );
9003 
9004         pinst->ref.matrix_valid = btrue;
9005 
9006         // fix the reflection
9007         chr_instance_update_ref( pinst, grid_level, bfalse );
9008     }
9009 
9010     return pinst->ref.matrix_valid;
9011 }
9012 
9013 //--------------------------------------------------------------------------------------------
apply_matrix_cache(chr_t * pchr,matrix_cache_t * mc_tmp)9014 bool_t apply_matrix_cache( chr_t * pchr, matrix_cache_t * mc_tmp )
9015 {
9016     /// @detalis BB@> request that the info in the matrix cache mc_tmp, be used to
9017     ///               make a matrix for the character pchr.
9018 
9019     bool_t applied = bfalse;
9020 
9021     if ( !DEFINED_PCHR( pchr ) ) return bfalse;
9022     if ( NULL == mc_tmp || !mc_tmp->valid ) return bfalse;
9023 
9024     if ( 0 != ( MAT_WEAPON & mc_tmp->type_bits ) )
9025     {
9026         if ( DEFINED_CHR( mc_tmp->grip_chr ) )
9027         {
9028             applied = apply_one_weapon_matrix( pchr, mc_tmp );
9029         }
9030         else
9031         {
9032             matrix_cache_t * mcache = &( pchr->inst.matrix_cache );
9033 
9034             // !!!the mc_tmp was mis-labeled as a MAT_WEAPON!!!
9035             make_one_character_matrix( GET_REF_PCHR( pchr ) );
9036 
9037             // recover the matrix_cache values from the character
9038             SET_BIT( mcache->type_bits, MAT_CHARACTER );
9039             if ( mcache->matrix_valid )
9040             {
9041                 mcache->valid     = btrue;
9042                 mcache->type_bits = MAT_CHARACTER;
9043 
9044                 mcache->self_scale.x =
9045                     mcache->self_scale.y =
9046                         mcache->self_scale.z = pchr->fat;
9047 
9048                 mcache->grip_scale = mcache->self_scale;
9049 
9050                 mcache->rotate.x = CLIP_TO_16BITS( pchr->ori.map_facing_x - MAP_TURN_OFFSET );
9051                 mcache->rotate.y = CLIP_TO_16BITS( pchr->ori.map_facing_y - MAP_TURN_OFFSET );
9052                 mcache->rotate.z = pchr->ori.facing_z;
9053 
9054                 mcache->pos = chr_get_pos( pchr );
9055 
9056                 applied = btrue;
9057             }
9058         }
9059     }
9060     else if ( 0 != ( MAT_CHARACTER & mc_tmp->type_bits ) )
9061     {
9062         applied = apply_one_character_matrix( pchr, mc_tmp );
9063     }
9064 
9065     if ( applied )
9066     {
9067         apply_reflection_matrix( &( pchr->inst ), pchr->enviro.grid_level );
9068     }
9069 
9070     return applied;
9071 }
9072 
9073 //--------------------------------------------------------------------------------------------
cmp_matrix_cache(const void * vlhs,const void * vrhs)9074 int cmp_matrix_cache( const void * vlhs, const void * vrhs )
9075 {
9076     /// @details BB@> check for differences between the data pointed to
9077     ///     by vlhs and vrhs, assuming that they point to matrix_cache_t data.
9078     ///
9079     ///    The function is implemented this way so that in pronciple
9080     ///    if could be used in a function like qsort().
9081     ///
9082     ///    We could almost certainly make something easier and quicker by
9083     ///    using the function memcmp()
9084 
9085     int   itmp, cnt;
9086     float ftmp;
9087 
9088     matrix_cache_t * plhs = ( matrix_cache_t * )vlhs;
9089     matrix_cache_t * prhs = ( matrix_cache_t * )vrhs;
9090 
9091     // handle problems with pointers
9092     if ( plhs == prhs )
9093     {
9094         return 0;
9095     }
9096     else if ( NULL == plhs )
9097     {
9098         return 1;
9099     }
9100     else if ( NULL == prhs )
9101     {
9102         return -1;
9103     }
9104 
9105     // handle one of both if the matrix caches being invalid
9106     if ( !plhs->valid && !prhs->valid )
9107     {
9108         return 0;
9109     }
9110     else if ( !plhs->valid )
9111     {
9112         return 1;
9113     }
9114     else if ( !prhs->valid )
9115     {
9116         return -1;
9117     }
9118 
9119     // handle differences in the type
9120     itmp = plhs->type_bits - prhs->type_bits;
9121     if ( 0 != itmp ) goto cmp_matrix_cache_end;
9122 
9123     //---- check for differences in the MAT_WEAPON data
9124     if ( HAS_SOME_BITS( plhs->type_bits, MAT_WEAPON ) )
9125     {
9126         itmp = ( signed )REF_TO_INT( plhs->grip_chr ) - ( signed )REF_TO_INT( prhs->grip_chr );
9127         if ( 0 != itmp ) goto cmp_matrix_cache_end;
9128 
9129         itmp = ( signed )plhs->grip_slot - ( signed )prhs->grip_slot;
9130         if ( 0 != itmp ) goto cmp_matrix_cache_end;
9131 
9132         for ( cnt = 0; cnt < GRIP_VERTS; cnt++ )
9133         {
9134             itmp = ( signed )plhs->grip_verts[cnt] - ( signed )prhs->grip_verts[cnt];
9135             if ( 0 != itmp ) goto cmp_matrix_cache_end;
9136         }
9137 
9138         // handle differences in the scale of our mount
9139         for ( cnt = 0; cnt < 3; cnt ++ )
9140         {
9141             ftmp = plhs->grip_scale.v[cnt] - prhs->grip_scale.v[cnt];
9142             if ( 0.0f != ftmp ) { itmp = SGN( ftmp ); goto cmp_matrix_cache_end; }
9143         }
9144     }
9145 
9146     //---- check for differences in the MAT_CHARACTER data
9147     if ( HAS_SOME_BITS( plhs->type_bits, MAT_CHARACTER ) )
9148     {
9149         // handle differences in the "Euler" rotation angles in 16-bit form
9150         for ( cnt = 0; cnt < 3; cnt++ )
9151         {
9152             ftmp = plhs->rotate.v[cnt] - prhs->rotate.v[cnt];
9153             if ( 0.0f != ftmp ) { itmp = SGN( ftmp ); goto cmp_matrix_cache_end; }
9154         }
9155 
9156         // handle differences in the translate vector
9157         for ( cnt = 0; cnt < 3; cnt++ )
9158         {
9159             ftmp = plhs->pos.v[cnt] - prhs->pos.v[cnt];
9160             if ( 0.0f != ftmp ) { itmp = SGN( ftmp ); goto cmp_matrix_cache_end; }
9161         }
9162     }
9163 
9164     //---- check for differences in the shared data
9165     if ( HAS_SOME_BITS( plhs->type_bits, MAT_WEAPON ) || HAS_SOME_BITS( plhs->type_bits, MAT_CHARACTER ) )
9166     {
9167         // handle differences in our own scale
9168         for ( cnt = 0; cnt < 3; cnt ++ )
9169         {
9170             ftmp = plhs->self_scale.v[cnt] - prhs->self_scale.v[cnt];
9171             if ( 0.0f != ftmp ) { itmp = SGN( ftmp ); goto cmp_matrix_cache_end; }
9172         }
9173     }
9174 
9175     // if it got here, the data is all the same
9176     itmp = 0;
9177 
9178 cmp_matrix_cache_end:
9179 
9180     return SGN( itmp );
9181 }
9182 
9183 //--------------------------------------------------------------------------------------------
matrix_cache_needs_update(chr_t * pchr,matrix_cache_t * pmc)9184 egoboo_rv matrix_cache_needs_update( chr_t * pchr, matrix_cache_t * pmc )
9185 {
9186     /// @details BB@> determine whether a matrix cache has become invalid and needs to be updated
9187 
9188     matrix_cache_t local_mc;
9189     bool_t needs_cache_update;
9190 
9191     if ( !DEFINED_PCHR( pchr ) ) return rv_error;
9192 
9193     if ( NULL == pmc ) pmc = &local_mc;
9194 
9195     // get the matrix data that is supposed to be used to make the matrix
9196     chr_get_matrix_cache( pchr, pmc );
9197 
9198     // compare that data to the actual data used to make the matrix
9199     needs_cache_update = ( 0 != cmp_matrix_cache( pmc, &( pchr->inst.matrix_cache ) ) );
9200 
9201     return needs_cache_update ? rv_success : rv_fail;
9202 }
9203 
9204 //--------------------------------------------------------------------------------------------
chr_update_matrix(chr_t * pchr,bool_t update_size)9205 egoboo_rv chr_update_matrix( chr_t * pchr, bool_t update_size )
9206 {
9207     /// @details BB@> Do everything necessary to set the current matrix for this character.
9208     ///     This might include recursively going down the list of this character's mounts, etc.
9209     ///
9210     ///     Return btrue if a new matrix is applied to the character, bfalse otherwise.
9211 
9212     egoboo_rv      retval;
9213     bool_t         needs_update = bfalse;
9214     bool_t         applied      = bfalse;
9215     matrix_cache_t mc_tmp;
9216     matrix_cache_t *pchr_mc = NULL;
9217 
9218     if ( !DEFINED_PCHR( pchr ) ) return rv_error;
9219     pchr_mc = &( pchr->inst.matrix_cache );
9220 
9221     // recursively make sure that any mount matrices are updated
9222     if ( DEFINED_CHR( pchr->attachedto ) )
9223     {
9224         egoboo_rv attached_update = rv_error;
9225 
9226         attached_update = chr_update_matrix( ChrList.lst + pchr->attachedto, btrue );
9227 
9228         // if this fails, we should probably do something...
9229         if ( rv_error == attached_update )
9230         {
9231             // there is an error so this matrix is not defined and no readon to go farther
9232             pchr_mc->matrix_valid = bfalse;
9233             return attached_update;
9234         }
9235         else if ( rv_success == attached_update )
9236         {
9237             // the holder/mount matrix has changed.
9238             // this matrix is no longer valid.
9239             pchr_mc->matrix_valid = bfalse;
9240         }
9241     }
9242 
9243     // does the matrix cache need an update at all?
9244     retval = matrix_cache_needs_update( pchr, &mc_tmp );
9245     if ( rv_error == retval ) return rv_error;
9246     needs_update = ( rv_success == retval );
9247 
9248     // Update the grip vertices no matter what (if they are used)
9249     if ( HAS_SOME_BITS( mc_tmp.type_bits, MAT_WEAPON ) && INGAME_CHR( mc_tmp.grip_chr ) )
9250     {
9251         egoboo_rv grip_retval;
9252         chr_t   * ptarget = ChrList.lst + mc_tmp.grip_chr;
9253 
9254         // has that character changes its animation?
9255         grip_retval = ( egoboo_rv )chr_instance_update_grip_verts( &( ptarget->inst ), mc_tmp.grip_verts, GRIP_VERTS );
9256 
9257         if ( rv_error   == grip_retval ) return rv_error;
9258         if ( rv_success == grip_retval ) needs_update = btrue;
9259     }
9260 
9261     // if it is not the same, make a new matrix with the new data
9262     applied = bfalse;
9263     if ( needs_update )
9264     {
9265         // we know the matrix is not valid
9266         pchr_mc->matrix_valid = bfalse;
9267 
9268         applied = apply_matrix_cache( pchr, &mc_tmp );
9269     }
9270 
9271     if ( applied && update_size )
9272     {
9273         // call chr_update_collision_size() but pass in a false value to prevent a recursize call
9274         chr_update_collision_size( pchr, bfalse );
9275     }
9276 
9277     return applied ? rv_success : rv_fail;
9278 }
9279 
9280 //--------------------------------------------------------------------------------------------
ai_state_set_bumplast(ai_state_t * pself,const CHR_REF ichr)9281 bool_t ai_state_set_bumplast( ai_state_t * pself, const CHR_REF ichr )
9282 {
9283     /// @details BB@> bumping into a chest can initiate whole loads of update messages.
9284     ///     Try to throttle the rate that new "bump" messages can be passed to the ai
9285 
9286     if ( NULL == pself ) return bfalse;
9287 
9288     if ( !INGAME_CHR( ichr ) ) return bfalse;
9289 
9290     // 5 bumps per second?
9291     if ( pself->bumplast != ichr ||  update_wld > pself->bumplast_time + TARGET_UPS / 5 )
9292     {
9293         pself->bumplast_time = update_wld;
9294         SET_BIT( pself->alert, ALERTIF_BUMPED );
9295     }
9296     pself->bumplast = ichr;
9297 
9298     return btrue;
9299 }
9300 
9301 //--------------------------------------------------------------------------------------------
9302 //--------------------------------------------------------------------------------------------
chr_has_inventory_idsz(const CHR_REF ichr,IDSZ idsz,bool_t equipped,CHR_REF * pack_last)9303 CHR_REF chr_has_inventory_idsz( const CHR_REF ichr, IDSZ idsz, bool_t equipped, CHR_REF * pack_last )
9304 {
9305     /// @details BB@> check the pack a matching item
9306 
9307     bool_t matches_equipped;
9308     CHR_REF item, tmp_var;
9309     chr_t * pchr;
9310 
9311     if ( !INGAME_CHR( ichr ) ) return ( CHR_REF )MAX_CHR;
9312     pchr = ChrList.lst + ichr;
9313 
9314     // make sure that pack_last points to something
9315     if ( NULL == pack_last ) pack_last = &tmp_var;
9316 
9317     item = ( CHR_REF )MAX_CHR;
9318 
9319     *pack_last = GET_REF_PCHR( pchr );
9320 
9321     PACK_BEGIN_LOOP( ipacked, pchr->pack.next )
9322     {
9323         matches_equipped = ( !equipped || ( INGAME_CHR( ipacked ) && ChrList.lst[ipacked].isequipped ) );
9324 
9325         if ( chr_is_type_idsz( ipacked, idsz ) && matches_equipped )
9326         {
9327             item = ipacked;
9328             break;
9329         }
9330 
9331         *pack_last = ipacked;
9332     }
9333     PACK_END_LOOP( ipacked );
9334 
9335     return item;
9336 }
9337 
9338 //--------------------------------------------------------------------------------------------
chr_holding_idsz(const CHR_REF ichr,IDSZ idsz)9339 CHR_REF chr_holding_idsz( const CHR_REF ichr, IDSZ idsz )
9340 {
9341     /// @details BB@> check the character's hands for a matching item
9342 
9343     bool_t found;
9344     CHR_REF item, tmp_item;
9345     chr_t * pchr;
9346 
9347     if ( !INGAME_CHR( ichr ) ) return ( CHR_REF )MAX_CHR;
9348     pchr = ChrList.lst + ichr;
9349 
9350     item = ( CHR_REF )MAX_CHR;
9351     found = bfalse;
9352 
9353     if ( !found )
9354     {
9355         // Check right hand. technically a held item cannot be equipped...
9356         tmp_item = pchr->holdingwhich[SLOT_RIGHT];
9357 
9358         if ( chr_is_type_idsz( tmp_item, idsz ) )
9359         {
9360             found = btrue;
9361             item = tmp_item;
9362         }
9363     }
9364 
9365     if ( !found )
9366     {
9367         // Check left hand. technically a held item cannot be equipped...
9368         tmp_item = pchr->holdingwhich[SLOT_LEFT];
9369 
9370         if ( chr_is_type_idsz( tmp_item, idsz ) )
9371         {
9372             found = btrue;
9373             item = tmp_item;
9374         }
9375     }
9376 
9377     return item;
9378 }
9379 
9380 //--------------------------------------------------------------------------------------------
chr_has_item_idsz(const CHR_REF ichr,IDSZ idsz,bool_t equipped,CHR_REF * pack_last)9381 CHR_REF chr_has_item_idsz( const CHR_REF ichr, IDSZ idsz, bool_t equipped, CHR_REF * pack_last )
9382 {
9383     /// @detalis BB@> is ichr holding an item matching idsz, or is such an item in his pack?
9384     ///               return the index of the found item, or MAX_CHR if not found. Also return
9385     ///               the previous pack item in *pack_last, or MAX_CHR if it was not in a pack.
9386 
9387     bool_t found;
9388     CHR_REF item, tmp_var;
9389     chr_t * pchr;
9390 
9391     if ( !INGAME_CHR( ichr ) ) return ( CHR_REF )MAX_CHR;
9392     pchr = ChrList.lst + ichr;
9393 
9394     // make sure that pack_last points to something
9395     if ( NULL == pack_last ) pack_last = &tmp_var;
9396 
9397     // Check the pack
9398     *pack_last = ( CHR_REF )MAX_CHR;
9399     item       = ( CHR_REF )MAX_CHR;
9400     found      = bfalse;
9401 
9402     if ( !found )
9403     {
9404         item = chr_holding_idsz( ichr, idsz );
9405         found = INGAME_CHR( item );
9406     }
9407 
9408     if ( !found )
9409     {
9410         item = chr_has_inventory_idsz( ichr, idsz, equipped, pack_last );
9411         found = INGAME_CHR( item );
9412     }
9413 
9414     return item;
9415 }
9416 
9417 //--------------------------------------------------------------------------------------------
chr_can_see_invis(const chr_t * pchr,const chr_t * pobj)9418 bool_t chr_can_see_invis( const chr_t * pchr, const chr_t * pobj )
9419 {
9420     /// @detalis BB@> can ichr see iobj?
9421 
9422     int     alpha;
9423 
9424     if ( NULL == pchr || NULL == pobj ) return bfalse;
9425 
9426     /// @note ZF@> Invictus characters can always see through darkness (spells, items, quest handlers, etc.)
9427     if ( pchr->invictus ) return btrue;
9428 
9429     alpha = pobj->inst.alpha;
9430     if ( 0 != pchr->see_invisible_level )
9431     {
9432         alpha = get_alpha( alpha, exp( 0.32f * ( float )pchr->see_invisible_level ) );
9433     }
9434     alpha = CLIP( alpha, 0, 255 );
9435 
9436     return alpha >= INVISIBLE;
9437 }
9438 
9439 //--------------------------------------------------------------------------------------------
chr_can_see_dark(const chr_t * pchr,const chr_t * pobj)9440 bool_t chr_can_see_dark( const chr_t * pchr, const chr_t * pobj )
9441 {
9442     /// @detalis BB@> can ichr see iobj?
9443 
9444     cap_t * pcap;
9445 
9446     int     light, self_light, enviro_light;
9447 
9448     if ( NULL == pchr || NULL == pobj ) return bfalse;
9449 
9450     enviro_light = ( pobj->inst.alpha * pobj->inst.max_light ) * INV_FF;
9451     self_light   = ( pobj->inst.light == 255 ) ? 0 : pobj->inst.light;
9452     light        = MAX( enviro_light, self_light );
9453 
9454     if ( 0 != pchr->darkvision_level )
9455     {
9456         light *= exp( 0.32f * ( float )pchr->darkvision_level );
9457     }
9458 
9459     // Scenery, spells and quest objects can always see through darkness
9460     // Checking pchr->invictus is not enough, since that could be temporary
9461     // and not indicate the appropriate objects
9462     pcap = pro_get_pcap( pchr->profile_ref );
9463     if ( NULL != pcap )
9464     {
9465         if ( pcap->invictus ) light = INVISIBLE;
9466     }
9467 
9468     return light >= INVISIBLE;
9469 }
9470 
9471 //--------------------------------------------------------------------------------------------
chr_can_see_object(const CHR_REF ichr,const CHR_REF iobj)9472 bool_t chr_can_see_object( const CHR_REF ichr, const CHR_REF iobj )
9473 {
9474     /// @detalis BB@> can ichr see iobj?
9475 
9476     chr_t * pchr, * pobj;
9477 
9478     bool_t too_dark, too_invis;
9479 
9480     if ( !INGAME_CHR( ichr ) ) return bfalse;
9481     pchr = ChrList.lst + ichr;
9482 
9483     if ( !INGAME_CHR( iobj ) ) return bfalse;
9484     pobj = ChrList.lst + iobj;
9485 
9486     too_dark  = !chr_can_see_dark( pchr, pobj );
9487     too_invis = !chr_can_see_invis( pchr, pobj );
9488 
9489     return !too_dark && !too_invis;
9490 }
9491 
9492 //--------------------------------------------------------------------------------------------
chr_get_price(const CHR_REF ichr)9493 int chr_get_price( const CHR_REF ichr )
9494 {
9495     /// @detalis BB@> determine the correct price for an item
9496 
9497     CAP_REF icap;
9498     Uint16  iskin;
9499     float   price;
9500 
9501     chr_t * pchr;
9502     cap_t * pcap;
9503 
9504     if ( !INGAME_CHR( ichr ) ) return 0;
9505     pchr = ChrList.lst + ichr;
9506 
9507     // Make sure spell books are priced according to their spell and not the book itself
9508     if ( pchr->profile_ref == SPELLBOOK )
9509     {
9510         icap = pro_get_icap( pchr->basemodel_ref );
9511         iskin = 0;
9512     }
9513     else
9514     {
9515         icap  = pro_get_icap( pchr->profile_ref );
9516         iskin = pchr->skin;
9517     }
9518 
9519     if ( !LOADED_CAP( icap ) ) return 0;
9520     pcap = CapStack.lst + icap;
9521 
9522     price = ( float ) pcap->skincost[iskin];
9523 
9524     // Items spawned in shops are more valuable
9525     if ( !pchr->isshopitem ) price *= 0.5f;
9526 
9527     // base the cost on the number of items/charges
9528     if ( pcap->isstackable )
9529     {
9530         price *= pchr->ammo;
9531     }
9532     else if ( pcap->isranged && pchr->ammo < pchr->ammomax )
9533     {
9534         if ( 0 != pchr->ammo )
9535         {
9536             price *= ( float ) pchr->ammo / ( float ) pchr->ammomax;
9537         }
9538     }
9539 
9540     return ( int )price;
9541 }
9542 
9543 //--------------------------------------------------------------------------------------------
chr_set_floor_level(chr_t * pchr,float level)9544 void chr_set_floor_level( chr_t * pchr, float level )
9545 {
9546     if ( !DEFINED_PCHR( pchr ) ) return;
9547 
9548     if ( level != pchr->enviro.floor_level )
9549     {
9550         pchr->enviro.floor_level = level;
9551     }
9552 }
9553 
9554 //--------------------------------------------------------------------------------------------
chr_set_redshift(chr_t * pchr,int rs)9555 void chr_set_redshift( chr_t * pchr, int rs )
9556 {
9557     if ( !DEFINED_PCHR( pchr ) ) return;
9558 
9559     pchr->inst.redshift = rs;
9560 
9561     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, bfalse );
9562 }
9563 
9564 //--------------------------------------------------------------------------------------------
chr_set_grnshift(chr_t * pchr,int gs)9565 void chr_set_grnshift( chr_t * pchr, int gs )
9566 {
9567     if ( !DEFINED_PCHR( pchr ) ) return;
9568 
9569     pchr->inst.grnshift = gs;
9570 
9571     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, bfalse );
9572 }
9573 
9574 //--------------------------------------------------------------------------------------------
chr_set_blushift(chr_t * pchr,int bs)9575 void chr_set_blushift( chr_t * pchr, int bs )
9576 {
9577     if ( !DEFINED_PCHR( pchr ) ) return;
9578 
9579     pchr->inst.blushift = bs;
9580 
9581     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, bfalse );
9582 }
9583 
9584 //--------------------------------------------------------------------------------------------
chr_set_sheen(chr_t * pchr,int sheen)9585 void chr_set_sheen( chr_t * pchr, int sheen )
9586 {
9587     if ( !DEFINED_PCHR( pchr ) ) return;
9588 
9589     pchr->inst.sheen = sheen;
9590 
9591     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, bfalse );
9592 }
9593 
9594 //--------------------------------------------------------------------------------------------
chr_set_alpha(chr_t * pchr,int alpha)9595 void chr_set_alpha( chr_t * pchr, int alpha )
9596 {
9597     if ( !DEFINED_PCHR( pchr ) ) return;
9598 
9599     pchr->inst.alpha = CLIP( alpha, 0, 255 );
9600 
9601     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, bfalse );
9602 }
9603 
9604 //--------------------------------------------------------------------------------------------
chr_set_light(chr_t * pchr,int light)9605 void chr_set_light( chr_t * pchr, int light )
9606 {
9607     if ( !DEFINED_PCHR( pchr ) ) return;
9608 
9609     pchr->inst.light = CLIP( light, 0, 255 );
9610 
9611     //This prevents players from becoming completely invisible
9612     if ( VALID_PLA( pchr->is_which_player ) )  pchr->inst.light = MAX( 128, pchr->inst.light );
9613 
9614     chr_instance_update_ref( &( pchr->inst ), pchr->enviro.grid_level, bfalse );
9615 }
9616 
9617 //--------------------------------------------------------------------------------------------
chr_instance_get_tint(chr_instance_t * pinst,GLfloat * tint,BIT_FIELD bits)9618 void chr_instance_get_tint( chr_instance_t * pinst, GLfloat * tint, BIT_FIELD bits )
9619 {
9620     int i;
9621     float weight_sum;
9622     GLXvector4f local_tint;
9623 
9624     int local_alpha;
9625     int local_light;
9626     int local_sheen;
9627     int local_redshift;
9628     int local_grnshift;
9629     int local_blushift;
9630 
9631     if ( NULL == tint ) tint = local_tint;
9632 
9633     if ( HAS_SOME_BITS( bits, CHR_REFLECT ) )
9634     {
9635         // this is a reflection, use the reflected parameters
9636         local_alpha    = pinst->ref.alpha;
9637         local_light    = pinst->ref.light;
9638         local_sheen    = pinst->ref.sheen;
9639         local_redshift = pinst->ref.redshift;
9640         local_grnshift = pinst->ref.grnshift;
9641         local_blushift = pinst->ref.blushift;
9642     }
9643     else
9644     {
9645         // this is NOT a reflection, use the normal parameters
9646         local_alpha    = pinst->alpha;
9647         local_light    = pinst->light;
9648         local_sheen    = pinst->sheen;
9649         local_redshift = pinst->redshift;
9650         local_grnshift = pinst->grnshift;
9651         local_blushift = pinst->blushift;
9652     }
9653 
9654     // modify these values based on local character abilities
9655     local_alpha = get_alpha( local_alpha, local_stats.seeinvis_mag );
9656     local_light = get_light( local_light, local_stats.seedark_mag );
9657 
9658     // clear out the tint
9659     weight_sum = 0;
9660     for ( i = 0; i < 4; i++ ) tint[i] = 0;
9661 
9662     if ( HAS_SOME_BITS( bits, CHR_SOLID ) )
9663     {
9664         // solid characters are not blended onto the canvas
9665         // the alpha channel is not important
9666         weight_sum += 1.0f;
9667 
9668         tint[RR] += 1.0f / ( 1 << local_redshift );
9669         tint[GG] += 1.0f / ( 1 << local_grnshift );
9670         tint[BB] += 1.0f / ( 1 << local_blushift );
9671         tint[AA] += 1.0f;
9672     }
9673 
9674     if ( HAS_SOME_BITS( bits, CHR_ALPHA ) )
9675     {
9676         // alpha characters are blended onto the canvas using the alpha channel
9677         // the alpha channel is not important
9678         weight_sum += 1.0f;
9679 
9680         tint[RR] += 1.0f / ( 1 << local_redshift );
9681         tint[GG] += 1.0f / ( 1 << local_grnshift );
9682         tint[BB] += 1.0f / ( 1 << local_blushift );
9683         tint[AA] += local_alpha * INV_FF;
9684     }
9685 
9686     if ( HAS_SOME_BITS( bits, CHR_LIGHT ) )
9687     {
9688         // alpha characters are blended onto the canvas by adding their color
9689         // the more black the colors, the less visible the character
9690         // the alpha channel is not important
9691 
9692         weight_sum += 1.0f;
9693 
9694         if ( local_light < 255 )
9695         {
9696             tint[RR] += local_light * INV_FF / ( 1 << local_redshift );
9697             tint[GG] += local_light * INV_FF / ( 1 << local_grnshift );
9698             tint[BB] += local_light * INV_FF / ( 1 << local_blushift );
9699         }
9700 
9701         tint[AA] += 1.0f;
9702     }
9703 
9704     if ( HAS_SOME_BITS( bits, CHR_PHONG ) )
9705     {
9706         // phong is essentially the same as light, but it is the
9707         // sheen that sets the effect
9708 
9709         float amount;
9710 
9711         weight_sum += 1.0f;
9712 
9713         amount = ( CLIP( local_sheen, 0, 15 ) << 4 ) / 240.0f;
9714 
9715         tint[RR] += amount;
9716         tint[GG] += amount;
9717         tint[BB] += amount;
9718         tint[AA] += 1.0f;
9719     }
9720 
9721     // average the tint
9722     if ( weight_sum != 0.0f && weight_sum != 1.0f )
9723     {
9724         for ( i = 0; i < 4; i++ )
9725         {
9726             tint[i] /= weight_sum;
9727         }
9728     }
9729 }
9730 
9731 //--------------------------------------------------------------------------------------------
chr_get_lowest_attachment(const CHR_REF ichr,bool_t non_item)9732 CHR_REF chr_get_lowest_attachment( const CHR_REF ichr, bool_t non_item )
9733 {
9734     /// @details BB@> Find the lowest attachment for a given object.
9735     ///               This was basically taken from the script function scr_set_TargetToLowestTarget()
9736     ///
9737     ///               You should be able to find the holder of a weapon by specifying non_item == btrue
9738     ///
9739     ///               To prevent possible loops in the data structures, use a counter to limit
9740     ///               the depth of the search, and make sure that ichr != ChrList.lst[object].attachedto
9741 
9742     int cnt;
9743     CHR_REF original_object, object, object_next;
9744 
9745     if ( !INGAME_CHR( ichr ) ) return ( CHR_REF )MAX_CHR;
9746 
9747     original_object = object = ichr;
9748     for ( cnt = 0, object = ichr; cnt < MAX_CHR && INGAME_CHR( object ); cnt++ )
9749     {
9750         object_next = ChrList.lst[object].attachedto;
9751 
9752         if ( non_item && !ChrList.lst[object].isitem )
9753         {
9754             break;
9755         }
9756 
9757         // check for a list with a loop. shouldn't happen, but...
9758         if ( !INGAME_CHR( object_next ) || object_next == original_object )
9759         {
9760             break;
9761         }
9762 
9763         object = object_next;
9764     }
9765 
9766     return object;
9767 }
9768 
9769 //--------------------------------------------------------------------------------------------
chr_can_mount(const CHR_REF ichr_a,const CHR_REF ichr_b)9770 bool_t chr_can_mount( const CHR_REF ichr_a, const CHR_REF ichr_b )
9771 {
9772     bool_t is_valid_rider_a, is_valid_mount_b, has_ride_anim;
9773     int action_mi;
9774 
9775     chr_t * pchr_a, * pchr_b;
9776     cap_t * pcap_a, * pcap_b;
9777 
9778     // make sure that A is valid
9779     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
9780     pchr_a = ChrList.lst + ichr_a;
9781 
9782     pcap_a = chr_get_pcap( ichr_a );
9783     if ( NULL == pcap_a ) return bfalse;
9784 
9785     // make sure that B is valid
9786     if ( !INGAME_CHR( ichr_b ) ) return bfalse;
9787     pchr_b = ChrList.lst + ichr_b;
9788 
9789     pcap_b = chr_get_pcap( ichr_b );
9790     if ( NULL == pcap_b ) return bfalse;
9791 
9792     action_mi = mad_get_action_ref( chr_get_imad( ichr_a ), ACTION_MI );
9793     has_ride_anim = ( ACTION_COUNT != action_mi && !ACTION_IS_TYPE( action_mi, D ) );
9794 
9795     is_valid_rider_a = !pchr_a->isitem && pchr_a->alive && ( 0 == pchr_a->flyheight ) &&
9796                        !INGAME_CHR( pchr_a->attachedto ) && has_ride_anim;
9797 
9798     is_valid_mount_b = pchr_b->ismount && pchr_b->alive &&
9799                        pcap_b->slotvalid[SLOT_LEFT] && !INGAME_CHR( pchr_b->holdingwhich[SLOT_LEFT] );
9800 
9801     return is_valid_rider_a && is_valid_mount_b;
9802 }
9803 
9804 //--------------------------------------------------------------------------------------------
chr_get_framefx(chr_t * pchr)9805 Uint32 chr_get_framefx( chr_t * pchr )
9806 {
9807     if ( !DEFINED_PCHR( pchr ) ) return 0;
9808 
9809     return chr_instance_get_framefx( &( pchr->inst ) );
9810 };
9811 
9812 //--------------------------------------------------------------------------------------------
chr_invalidate_child_instances(chr_t * pchr)9813 egoboo_rv chr_invalidate_child_instances( chr_t * pchr )
9814 {
9815     int cnt;
9816 
9817     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9818 
9819     // invalidate vlst_cache of everything in this character's holdingwhich array
9820     for ( cnt = 0; cnt < SLOT_COUNT; cnt++ )
9821     {
9822         CHR_REF iitem = pchr->holdingwhich[cnt];
9823         if ( !INGAME_CHR( iitem ) ) continue;
9824 
9825         // invalidate the matrix_cache
9826         ChrList.lst[iitem].inst.matrix_cache.valid = bfalse;
9827     }
9828 
9829     return rv_success;
9830 }
9831 
9832 //--------------------------------------------------------------------------------------------
chr_set_action(chr_t * pchr,int action,bool_t action_ready,bool_t override_action)9833 egoboo_rv chr_set_action( chr_t * pchr, int action, bool_t action_ready, bool_t override_action )
9834 {
9835     egoboo_rv retval;
9836 
9837     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9838 
9839     retval = ( egoboo_rv )chr_instance_set_action( &( pchr->inst ), action, action_ready, override_action );
9840     if ( rv_success != retval ) return retval;
9841 
9842     // if the instance is invalid, invalidate everything that depends on this object
9843     if ( !pchr->inst.save.valid )
9844     {
9845         chr_invalidate_child_instances( pchr );
9846     }
9847 
9848     return retval;
9849 }
9850 
9851 //--------------------------------------------------------------------------------------------
chr_start_anim(chr_t * pchr,int action,bool_t action_ready,bool_t override_action)9852 egoboo_rv chr_start_anim( chr_t * pchr, int action, bool_t action_ready, bool_t override_action )
9853 {
9854     egoboo_rv retval;
9855 
9856     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9857 
9858     retval = ( egoboo_rv )chr_instance_start_anim( &( pchr->inst ), action, action_ready, override_action );
9859     if ( rv_success != retval ) return retval;
9860 
9861     // if the instance is invalid, invalidate everything that depends on this object
9862     if ( !pchr->inst.save.valid )
9863     {
9864         chr_invalidate_child_instances( pchr );
9865     }
9866 
9867     return retval;
9868 }
9869 
9870 //--------------------------------------------------------------------------------------------
chr_set_anim(chr_t * pchr,int action,int frame,bool_t action_ready,bool_t override_action)9871 egoboo_rv chr_set_anim( chr_t * pchr, int action, int frame, bool_t action_ready, bool_t override_action )
9872 {
9873     egoboo_rv retval;
9874 
9875     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9876 
9877     retval = ( egoboo_rv )chr_instance_set_anim( &( pchr->inst ), action, frame, action_ready, override_action );
9878     if ( rv_success != retval ) return retval;
9879 
9880     // if the instance is invalid, invalidate everything that depends on this object
9881     if ( !pchr->inst.save.valid )
9882     {
9883         chr_invalidate_child_instances( pchr );
9884     }
9885 
9886     return retval;
9887 }
9888 
9889 //--------------------------------------------------------------------------------------------
chr_increment_action(chr_t * pchr)9890 egoboo_rv chr_increment_action( chr_t * pchr )
9891 {
9892     egoboo_rv retval;
9893 
9894     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9895 
9896     retval = ( egoboo_rv )chr_instance_increment_action( &( pchr->inst ) );
9897     if ( rv_success != retval ) return retval;
9898 
9899     // if the instance is invalid, invalidate everything that depends on this object
9900     if ( !pchr->inst.save.valid )
9901     {
9902         chr_invalidate_child_instances( pchr );
9903     }
9904 
9905     return retval;
9906 }
9907 
9908 //--------------------------------------------------------------------------------------------
chr_increment_frame(chr_t * pchr)9909 egoboo_rv chr_increment_frame( chr_t * pchr )
9910 {
9911     egoboo_rv retval;
9912     mad_t * pmad;
9913     int mount_action;
9914     CHR_REF imount;
9915     bool_t needs_keep;
9916 
9917     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9918     imount = pchr->attachedto;
9919 
9920     pmad = chr_get_pmad( GET_REF_PCHR( pchr ) );
9921     if ( NULL == pmad ) return rv_error;
9922 
9923     // do we need to keep this animation?
9924     needs_keep = bfalse;
9925 
9926     if ( !INGAME_CHR( imount ) )
9927     {
9928         imount = ( CHR_REF )MAX_CHR;
9929         mount_action = ACTION_DA;
9930     }
9931     else
9932     {
9933         // determine what kind of action we are going to substitute for a riding character
9934         if ( INGAME_CHR( pchr->holdingwhich[SLOT_LEFT] ) || INGAME_CHR( pchr->holdingwhich[SLOT_RIGHT] ) )
9935         {
9936             // if the character is holding anything, make the animation
9937             // ACTION_MH == "sitting" so that it does not look so silly
9938 
9939             mount_action = mad_get_action_ref( pchr->inst.imad, ACTION_MH );
9940             if ( ACTION_MH != mount_action )
9941             {
9942                 // no real sitting animation. set the animation to keep
9943                 needs_keep = btrue;
9944             }
9945         }
9946         else
9947         {
9948             // if it is not holding anything, go for the riding animation
9949             mount_action = mad_get_action_ref( pchr->inst.imad, ACTION_MI );
9950             if ( ACTION_MI != mount_action )
9951             {
9952                 // no real riding animation. set the animation to keep
9953                 needs_keep = btrue;
9954             }
9955         }
9956     }
9957 
9958     retval = ( egoboo_rv )chr_instance_increment_frame( &( pchr->inst ), pmad, imount, mount_action );
9959     if ( rv_success != retval ) return retval;
9960 
9961     // BB@> this did not work as expected...
9962     // set keep if needed
9963     //if ( needs_keep )
9964     //{
9965     //    chr_instance_set_action_keep( &( pchr->inst ), btrue );
9966     //}
9967 
9968     // if the instance is invalid, invalidate everything that depends on this object
9969     if ( !pchr->inst.save.valid )
9970     {
9971         chr_invalidate_child_instances( pchr );
9972     }
9973 
9974     return retval;
9975 }
9976 
9977 //--------------------------------------------------------------------------------------------
chr_play_action(chr_t * pchr,int action,bool_t action_ready)9978 egoboo_rv chr_play_action( chr_t * pchr, int action, bool_t action_ready )
9979 {
9980     egoboo_rv retval;
9981 
9982     if ( !ACTIVE_PCHR( pchr ) ) return rv_error;
9983 
9984     retval = ( egoboo_rv )chr_instance_play_action( &( pchr->inst ), action, action_ready );
9985     if ( rv_success != retval ) return retval;
9986 
9987     // if the instance is invalid, invalidate everything that depends on this object
9988     if ( !pchr->inst.save.valid )
9989     {
9990         chr_invalidate_child_instances( pchr );
9991     }
9992 
9993     return retval;
9994 }
9995 
9996 //--------------------------------------------------------------------------------------------
chr_get_imad(const CHR_REF ichr)9997 MAD_REF chr_get_imad( const CHR_REF ichr )
9998 {
9999     chr_t * pchr;
10000 
10001     if ( !DEFINED_CHR( ichr ) ) return ( MAD_REF )MAX_MAD;
10002     pchr = ChrList.lst + ichr;
10003 
10004     // try to repair a bad model if it exists
10005     if ( !LOADED_MAD( pchr->inst.imad ) )
10006     {
10007         MAD_REF imad_tmp = pro_get_imad( pchr->profile_ref );
10008         if ( LOADED_MAD( imad_tmp ) )
10009         {
10010             if ( chr_instance_set_mad( &( pchr->inst ), imad_tmp ) )
10011             {
10012                 chr_update_collision_size( pchr, btrue );
10013             }
10014         }
10015         if ( !LOADED_MAD( pchr->inst.imad ) ) return ( MAD_REF )MAX_MAD;
10016     }
10017 
10018     return pchr->inst.imad;
10019 }
10020 
10021 //--------------------------------------------------------------------------------------------
chr_get_pmad(const CHR_REF ichr)10022 mad_t * chr_get_pmad( const CHR_REF ichr )
10023 {
10024     chr_t * pchr;
10025 
10026     if ( !DEFINED_CHR( ichr ) ) return NULL;
10027     pchr = ChrList.lst + ichr;
10028 
10029     // try to repair a bad model if it exists
10030     if ( !LOADED_MAD( pchr->inst.imad ) )
10031     {
10032         MAD_REF imad_tmp = pro_get_imad( pchr->profile_ref );
10033         if ( LOADED_MAD( imad_tmp ) )
10034         {
10035             chr_instance_set_mad( &( pchr->inst ), imad_tmp );
10036         }
10037     }
10038 
10039     if ( !LOADED_MAD( pchr->inst.imad ) ) return NULL;
10040 
10041     return MadStack.lst + pchr->inst.imad;
10042 }
10043 
10044 //--------------------------------------------------------------------------------------------
10045 //--------------------------------------------------------------------------------------------
chr_update_pos(chr_t * pchr)10046 bool_t chr_update_pos( chr_t * pchr )
10047 {
10048     if ( !ALLOCATED_PCHR( pchr ) ) return bfalse;
10049 
10050     pchr->onwhichgrid   = mesh_get_grid( PMesh, pchr->pos.x, pchr->pos.y );
10051     pchr->onwhichblock  = mesh_get_block( PMesh, pchr->pos.x, pchr->pos.y );
10052 
10053     // update whether the current character position is safe
10054     chr_update_safe( pchr, bfalse );
10055 
10056     // update the breadcrumb list
10057     chr_update_breadcrumb( pchr, bfalse );
10058 
10059     return btrue;
10060 }
10061 
10062 //--------------------------------------------------------------------------------------------
chr_set_pos(chr_t * pchr,fvec3_base_t pos)10063 bool_t chr_set_pos( chr_t * pchr, fvec3_base_t pos )
10064 {
10065     bool_t retval = bfalse;
10066 
10067     if ( !ALLOCATED_PCHR( pchr ) ) return retval;
10068 
10069     retval = btrue;
10070 
10071     if (( pos[kX] != pchr->pos.v[kX] ) || ( pos[kY] != pchr->pos.v[kY] ) || ( pos[kZ] != pchr->pos.v[kZ] ) )
10072     {
10073         memmove( pchr->pos.v, pos, sizeof( fvec3_base_t ) );
10074 
10075         retval = chr_update_pos( pchr );
10076     }
10077 
10078     return retval;
10079 }
10080 
10081 //--------------------------------------------------------------------------------------------
chr_set_maxaccel(chr_t * pchr,float new_val)10082 bool_t chr_set_maxaccel( chr_t * pchr, float new_val )
10083 {
10084     bool_t retval = bfalse;
10085     float ftmp;
10086 
10087     if ( !ALLOCATED_PCHR( pchr ) ) return retval;
10088 
10089     ftmp = pchr->maxaccel / pchr->maxaccel_reset;
10090     pchr->maxaccel_reset = new_val;
10091     pchr->maxaccel = ftmp * pchr->maxaccel_reset;
10092 
10093     return btrue;
10094 }
10095 
10096 //--------------------------------------------------------------------------------------------
chr_set_ai_state(chr_t * pchr,int state)10097 chr_t * chr_set_ai_state( chr_t * pchr, int state )
10098 {
10099     if ( !DEFINED_PCHR( pchr ) ) return pchr;
10100 
10101     pchr->ai.state = state;
10102 
10103     chr_update_hide( pchr );
10104 
10105     return pchr;
10106 }
10107 
10108 //--------------------------------------------------------------------------------------------
chr_calc_grip_cv(chr_t * pmount,int grip_offset,oct_bb_t * grip_cv_ptr,fvec3_base_t grip_origin,fvec3_base_t grip_up,bool_t shift_origin)10109 bool_t chr_calc_grip_cv( chr_t * pmount, int grip_offset, oct_bb_t * grip_cv_ptr, fvec3_base_t grip_origin, fvec3_base_t grip_up, bool_t shift_origin )
10110 {
10111     /// @details BB@> use a standard size for the grip
10112 
10113     // take the character size from the adventurer model
10114     const float default_chr_height = 88.0f;
10115     const float default_chr_radius = 22.0f;
10116 
10117     int              cnt;
10118     chr_instance_t * pmount_inst;
10119     oct_bb_t         tmp_cv = OCT_BB_INIT_VALS;
10120 
10121     int     grip_count;
10122     Uint16  grip_verts[GRIP_VERTS];
10123     fvec4_t grip_points[GRIP_VERTS];
10124     fvec4_t grip_nupoints[GRIP_VERTS];
10125     bumper_t bmp;
10126 
10127     if ( !DEFINED_PCHR( pmount ) ) return bfalse;
10128 
10129     // alias this variable for notation simplicity
10130     pmount_inst = &( pmount->inst );
10131 
10132     // tune the grip radius
10133     bmp.size     = default_chr_radius * pmount->fat * 0.75f;
10134     bmp.height   = default_chr_height * pmount->fat * 2.00f;
10135     bmp.size_big = bmp.size * SQRT_TWO;
10136 
10137     oct_bb_set_bumper( &tmp_cv, bmp );
10138 
10139     // move the vertical bounding box down a little
10140     tmp_cv.mins[OCT_Z] -= bmp.height * 0.25f;
10141     tmp_cv.maxs[OCT_Z] -= bmp.height * 0.25f;
10142 
10143     // get appropriate vertices for this model's grip
10144     {
10145         // do the automatic vertex update
10146         int vert_stt = ( signed )( pmount_inst->vrt_count ) - ( signed )grip_offset;
10147         if ( vert_stt < 0 ) return bfalse;
10148 
10149         if ( rv_error == chr_instance_update_vertices( pmount_inst, vert_stt, vert_stt + grip_offset, bfalse ) )
10150         {
10151             grip_count = 0;
10152             for ( cnt = 0; cnt < GRIP_VERTS; cnt++ )
10153             {
10154                 grip_verts[cnt] = 0xFFFF;
10155             }
10156         }
10157         else
10158         {
10159             // calculate the grip vertices
10160             for ( grip_count = 0, cnt = 0; cnt < GRIP_VERTS && ( size_t )( vert_stt + cnt ) < pmount_inst->vrt_count; grip_count++, cnt++ )
10161             {
10162                 grip_verts[cnt] = vert_stt + cnt;
10163             }
10164             for ( /* nothing */ ; cnt < GRIP_VERTS; cnt++ )
10165             {
10166                 grip_verts[cnt] = 0xFFFF;
10167             }
10168         }
10169 
10170         // calculate grip_origin and grip_up
10171         if ( 4 == grip_count )
10172         {
10173             // Calculate grip point locations with linear interpolation and other silly things
10174             convert_grip_to_local_points( pmount, grip_verts, grip_points );
10175         }
10176         else if ( grip_count > 0 )
10177         {
10178             // Calculate grip point locations with linear interpolation and other silly things
10179             convert_grip_to_local_points( pmount, grip_verts, grip_points );
10180 
10181             if ( grip_count < 2 )
10182             {
10183                 fvec4_self_clear( grip_points[2].v );
10184                 grip_points[2].y = 1.0f;
10185             }
10186 
10187             if ( grip_count < 3 )
10188             {
10189                 fvec4_self_clear( grip_points[3].v );
10190                 grip_points[3].z = 1.0f;
10191             }
10192         }
10193         else if ( 0 == grip_count )
10194         {
10195             // choose the location point at the model's origin and axis aligned
10196 
10197             for ( cnt = 0; cnt < 4; cnt++ )
10198             {
10199                 fvec4_self_clear( grip_points[cnt].v );
10200             }
10201 
10202             grip_points[1].x = 1.0f;
10203             grip_points[2].y = 1.0f;
10204             grip_points[3].z = 1.0f;
10205         }
10206 
10207         // fix the 4th component depending on the whether we shift the origin of the cv
10208         if ( !shift_origin )
10209         {
10210             for ( cnt = 0; cnt < grip_count; cnt++ )
10211             {
10212                 grip_points[cnt].w = 0.0f;
10213             }
10214         }
10215     }
10216 
10217     // transform the vertices to calculate the grip_vecs[]
10218     if ( NULL == grip_up )
10219     {
10220         // we only need one vertex
10221         TransformVertices( &( pmount_inst->matrix ), grip_points, grip_nupoints, 1 );
10222     }
10223     else
10224     {
10225         // transform all the vertices
10226         TransformVertices( &( pmount_inst->matrix ), grip_points, grip_nupoints, GRIP_VERTS );
10227     }
10228 
10229     // find the up vector, if needed
10230     if ( NULL != grip_up )
10231     {
10232         fvec3_t grip_vecs[3];
10233 
10234         // determine the grip vectors
10235         for ( cnt = 0; cnt < 3; cnt++ )
10236         {
10237             grip_vecs[cnt] = fvec3_sub( grip_nupoints[cnt + 1].v, grip_nupoints[0].v );
10238         }
10239 
10240         // grab the grip's "up" vector
10241         fvec3_base_assign( grip_up, fvec3_normalize( grip_vecs[2].v ) );
10242     }
10243 
10244     // save the origin, if necessary
10245     if ( NULL != grip_origin )
10246     {
10247         fvec3_base_copy( grip_origin, grip_nupoints[0].v );
10248     }
10249 
10250     // add in the "origin" of the grip, if necessary
10251     if ( NULL != grip_cv_ptr )
10252     {
10253         oct_bb_add_fvec3( &tmp_cv, grip_nupoints[0].v, grip_cv_ptr );
10254     }
10255 
10256     return btrue;
10257 }
10258 
10259