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