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 collision.c
21 /// @brief The code that handles collisions between in-game objects
22 /// @details
23 
24 #include "collision.h"
25 #include "obj_BSP.h"
26 
27 #include "log.h"
28 #include "hash.h"
29 #include "game.h"
30 #include "SDL_extensions.h"
31 
32 #include "char.inl"
33 #include "particle.inl"
34 #include "enchant.inl"
35 #include "profile.inl"
36 #include "physics.inl"
37 
38 //--------------------------------------------------------------------------------------------
39 //--------------------------------------------------------------------------------------------
40 
41 #define MAKE_HASH(AA,BB)         CLIP_TO_08BITS( ((AA) * 0x0111 + 0x006E) + ((BB) * 0x0111 + 0x006E) )
42 
43 #define CHR_MAX_COLLISIONS       (MAX_CHR*8 + MAX_PRT)
44 #define COLLISION_HASH_NODES     (CHR_MAX_COLLISIONS*2)
45 #define COLLISION_LIST_SIZE      256
46 
47 //--------------------------------------------------------------------------------------------
48 //--------------------------------------------------------------------------------------------
49 
50 /// data block used to communicate between the different "modules" governing the character-particle collision
51 struct s_chr_prt_collsion_data
52 {
53     // object parameters
54     CHR_REF ichr;
55     chr_t * pchr;
56     cap_t * pcap;
57 
58     PRT_REF iprt;
59     prt_t * pprt;
60     pip_t * ppip;
61 
62     //---- collision parameters
63 
64     // true collisions
65     bool_t    int_min;
66     float     depth_min;
67 
68     // hit-box collisions
69     bool_t    int_max;
70     float     depth_max;
71 
72     // platform interactions
73     //bool_t    int_plat;
74     //float     plat_lerp;
75 
76     bool_t    is_impact;
77     bool_t    is_pressure;
78     bool_t    is_collision;
79     float     dot;
80     fvec3_t   nrm;
81 
82     // collision modifications
83     bool_t   mana_paid;
84     int      max_damage, actual_damage;
85     fvec3_t  vdiff, vdiff_para, vdiff_perp;
86     float    block_factor;
87 
88     // collision reaction
89     fvec3_t vimpulse;                      ///< the velocity impulse
90     fvec3_t pimpulse;                      ///< the position impulse
91     bool_t  terminate_particle;
92     bool_t  prt_bumps_chr;
93     bool_t  prt_damages_chr;
94 };
95 
96 typedef struct s_chr_prt_collsion_data chr_prt_collsion_data_t;
97 
98 //--------------------------------------------------------------------------------------------
99 
100 /// one element of the data for partitioning character and particle positions
101 struct s_bumplist
102 {
103     size_t   chrnum;                  // Number on the block
104     CHR_REF  chr;                     // For character collisions
105 
106     size_t   prtnum;                  // Number on the block
107     CHR_REF  prt;                     // For particle collisions
108 };
109 typedef struct s_bumplist bumplist_t;
110 
111 //--------------------------------------------------------------------------------------------
112 //--------------------------------------------------------------------------------------------
113 
114 static bool_t add_chr_chr_interaction( CHashList_t * pclst, const CHR_REF ichr_a, const CHR_REF ichr_b, CoNode_ary_t * pcn_lst, HashNode_ary_t * phn_lst );
115 static bool_t add_chr_prt_interaction( CHashList_t * pclst, const CHR_REF ichr_a, const PRT_REF iprt_b, CoNode_ary_t * pcn_lst, HashNode_ary_t * phn_lst );
116 
117 static bool_t detect_chr_chr_interaction_valid( const CHR_REF ichr_a, const CHR_REF ichr_b );
118 static bool_t detect_chr_prt_interaction_valid( const CHR_REF ichr_a, const PRT_REF iprt_b );
119 
120 //static bool_t detect_chr_chr_interaction( const CHR_REF ichr_a, const CHR_REF ichr_b );
121 //static bool_t detect_chr_prt_interaction( const CHR_REF ichr_a, const PRT_REF iprt_b );
122 
123 static bool_t do_chr_platform_detection( const CHR_REF ichr_a, const CHR_REF ichr_b );
124 static bool_t do_prt_platform_detection( const CHR_REF ichr_a, const PRT_REF iprt_b );
125 
126 static bool_t fill_interaction_list( CHashList_t * pclst, CoNode_ary_t * pcn_lst, HashNode_ary_t * phn_lst );
127 static bool_t fill_bumplists();
128 
129 static bool_t bump_all_platforms( CoNode_ary_t * pcn_ary );
130 static bool_t bump_all_mounts( CoNode_ary_t * pcn_ary );
131 static bool_t bump_all_collisions( CoNode_ary_t * pcn_ary );
132 
133 static bool_t bump_one_mount( const CHR_REF ichr_a, const CHR_REF ichr_b );
134 static bool_t do_chr_platform_physics( chr_t * pitem, chr_t * pplat );
135 static float  estimate_chr_prt_normal( chr_t * pchr, prt_t * pprt, fvec3_base_t nrm, fvec3_base_t vdiff );
136 static bool_t do_chr_chr_collision( CoNode_t * d );
137 
138 static bool_t do_chr_prt_collision_init( CHR_REF ichr, PRT_REF iprt, chr_prt_collsion_data_t * pdata );
139 static bool_t do_chr_prt_collision_deflect( chr_prt_collsion_data_t * pdata );
140 static bool_t do_chr_prt_collision_recoil( chr_prt_collsion_data_t * pdata );
141 static bool_t do_chr_prt_collision_damage( chr_prt_collsion_data_t * pdata );
142 static bool_t do_chr_prt_collision_impulse( chr_prt_collsion_data_t * pdata );
143 static bool_t do_chr_prt_collision_bump( chr_prt_collsion_data_t * pdata );
144 static bool_t do_chr_prt_collision_handle_bump( chr_prt_collsion_data_t * pdata );
145 static bool_t do_chr_prt_collision( CoNode_t * d );
146 
147 IMPLEMENT_DYNAMIC_ARY( CoNode_ary,   CoNode_t );
148 IMPLEMENT_DYNAMIC_ARY( HashNode_ary, hash_node_t );
149 
150 //--------------------------------------------------------------------------------------------
151 //--------------------------------------------------------------------------------------------
152 //static bumplist_t bumplist[MAXMESHFAN/16];
153 
154 static CHashList_t   * _CHashList_ptr = NULL;
155 static HashNode_ary_t  _hn_ary;                 ///< the available hash_node_t collision nodes for the CHashList_t
156 static CoNode_ary_t    _co_ary;                 ///< the available CoNode_t    data pointed to by the hash_node_t nodes
157 static BSP_leaf_pary_t _coll_leaf_lst;
158 static CoNode_ary_t    _coll_node_lst;
159 
160 static bool_t _collision_hash_initialized = bfalse;
161 static bool_t _collision_system_initialized = bfalse;
162 
163 int CHashList_inserted = 0;
164 
165 //--------------------------------------------------------------------------------------------
166 //--------------------------------------------------------------------------------------------
collision_system_begin()167 bool_t collision_system_begin()
168 {
169     if ( !_collision_system_initialized )
170     {
171         if ( NULL == CoNode_ary_ctor( &_co_ary, CHR_MAX_COLLISIONS ) ) goto collision_system_begin_fail;
172 
173         if ( NULL == HashNode_ary_ctor( &_hn_ary, COLLISION_HASH_NODES ) ) goto collision_system_begin_fail;
174 
175         if ( NULL == BSP_leaf_pary_ctor( &_coll_leaf_lst, COLLISION_LIST_SIZE ) ) goto collision_system_begin_fail;
176 
177         if ( NULL == CoNode_ary_ctor( &_coll_node_lst, COLLISION_LIST_SIZE ) ) goto collision_system_begin_fail;
178 
179         _collision_system_initialized = btrue;
180     }
181 
182     return btrue;
183 
184 collision_system_begin_fail:
185 
186     CoNode_ary_dtor( &_co_ary );
187     HashNode_ary_dtor( &_hn_ary );
188     BSP_leaf_pary_dtor( &_coll_leaf_lst );
189     CoNode_ary_dtor( &_coll_node_lst );
190 
191     _collision_system_initialized = bfalse;
192 
193     log_error( "Cannot initialize the collision system" );
194 
195     return bfalse;
196 }
197 
198 //--------------------------------------------------------------------------------------------
collision_system_end()199 void collision_system_end()
200 {
201     if ( _collision_hash_initialized )
202     {
203         hash_list_destroy( &_CHashList_ptr );
204         _collision_hash_initialized = bfalse;
205     }
206     _CHashList_ptr = NULL;
207 
208     if ( _collision_system_initialized )
209     {
210         CoNode_ary_dtor( &_co_ary );
211         HashNode_ary_dtor( &_hn_ary );
212         BSP_leaf_pary_dtor( &_coll_leaf_lst );
213         CoNode_ary_dtor( &_coll_node_lst );
214 
215         _collision_system_initialized = bfalse;
216     }
217 }
218 
219 //--------------------------------------------------------------------------------------------
220 //--------------------------------------------------------------------------------------------
CoNode_ctor(CoNode_t * n)221 CoNode_t * CoNode_ctor( CoNode_t * n )
222 {
223     if ( NULL == n ) return n;
224 
225     // clear all data
226     memset( n, 0, sizeof( *n ) );
227 
228     // the "colliding" objects
229     n->chra = ( CHR_REF )MAX_CHR;
230     n->prta = MAX_PRT;
231 
232     // the "collided with" objects
233     n->chrb  = ( CHR_REF )MAX_CHR;
234     n->prtb  = MAX_PRT;
235     n->tileb = FANOFF;
236 
237     // intialize the time
238     n->tmin = n->tmax = -1.0f;
239 
240     return n;
241 }
242 
243 //--------------------------------------------------------------------------------------------
CoNode_generate_hash(CoNode_t * coll)244 Uint8 CoNode_generate_hash( CoNode_t * coll )
245 {
246     REF_T AA, BB;
247 
248     AA = ( Uint32 )( ~0 );
249     if ( VALID_CHR_RANGE( coll->chra ) )
250     {
251         AA = REF_TO_INT( coll->chra );
252     }
253     else if ( VALID_PRT_RANGE( coll->prta ) )
254     {
255         AA = REF_TO_INT( coll->prta );
256     }
257 
258     BB = ( Uint32 )( ~0 );
259     if ( VALID_CHR_RANGE( coll->chrb ) )
260     {
261         BB = REF_TO_INT( coll->chrb );
262     }
263     else if ( VALID_PRT_RANGE( coll->prtb ) )
264     {
265         BB = REF_TO_INT( coll->prtb );
266     }
267     else if ( FANOFF != coll->tileb )
268     {
269         BB = coll->tileb;
270     }
271 
272     return MAKE_HASH( AA, BB );
273 }
274 
275 //--------------------------------------------------------------------------------------------
CoNode_cmp(const void * vleft,const void * vright)276 int CoNode_cmp( const void * vleft, const void * vright )
277 {
278     int   itmp;
279     float ftmp;
280 
281     CoNode_t * pleft  = ( CoNode_t * )vleft;
282     CoNode_t * pright = ( CoNode_t * )vright;
283 
284     // sort by initial time first
285     ftmp = pleft->tmin - pright->tmin;
286     if ( ftmp <= 0.0f ) return -1;
287     else if ( ftmp >= 0.0f ) return 1;
288 
289     // sort by final time second
290     ftmp = pleft->tmax - pright->tmax;
291     if ( ftmp <= 0.0f ) return -1;
292     else if ( ftmp >= 0.0f ) return 1;
293 
294     itmp = ( signed )REF_TO_INT( pleft->chra ) - ( signed )REF_TO_INT( pright->chra );
295     if ( 0 != itmp ) return itmp;
296 
297     itmp = ( signed )REF_TO_INT( pleft->prta ) - ( signed )REF_TO_INT( pright->prta );
298     if ( 0 != itmp ) return itmp;
299 
300     itmp = ( signed )REF_TO_INT( pleft->chrb ) - ( signed )REF_TO_INT( pright->chrb );
301     if ( 0 != itmp ) return itmp;
302 
303     itmp = ( signed )REF_TO_INT( pleft->prtb ) - ( signed )REF_TO_INT( pright->prtb );
304     if ( 0 != itmp ) return itmp;
305 
306     itmp = ( signed )pleft->tileb - ( signed )pright->tileb;
307     if ( 0 != itmp ) return itmp;
308 
309     return 0;
310 }
311 
312 //--------------------------------------------------------------------------------------------
CoNode_cmp_unique(const void * vleft,const void * vright)313 int CoNode_cmp_unique( const void * vleft, const void * vright )
314 {
315     int   itmp;
316 
317     CoNode_t * pleft  = ( CoNode_t * )vleft;
318     CoNode_t * pright = ( CoNode_t * )vright;
319 
320     // don't compare the times
321 
322     itmp = ( signed )REF_TO_INT( pleft->chra ) - ( signed )REF_TO_INT( pright->chra );
323     if ( 0 != itmp ) return itmp;
324 
325     itmp = ( signed )REF_TO_INT( pleft->prta ) - ( signed )REF_TO_INT( pright->prta );
326     if ( 0 != itmp ) return itmp;
327 
328     itmp = ( signed )REF_TO_INT( pleft->chrb ) - ( signed )REF_TO_INT( pright->chrb );
329     if ( 0 != itmp ) return itmp;
330 
331     itmp = ( signed )REF_TO_INT( pleft->prtb ) - ( signed )REF_TO_INT( pright->prtb );
332     if ( 0 != itmp ) return itmp;
333 
334     itmp = ( signed )pleft->tileb - ( signed )pright->tileb;
335     if ( 0 != itmp ) return itmp;
336 
337     return 0;
338 }
339 
340 //--------------------------------------------------------------------------------------------
CoNode_matches(CoNode_t * pleft,CoNode_t * pright)341 int CoNode_matches( CoNode_t * pleft, CoNode_t * pright )
342 {
343     CoNode_t right_rev;
344 
345     if ( 0 == CoNode_cmp_unique( pleft, pright ) ) return btrue;
346 
347     // make a reversed version of pright
348     right_rev.tmin = pright->tmin;
349     right_rev.tmax = pright->tmax;
350     right_rev.chra = pright->chrb;
351     right_rev.prta = pright->prtb;
352     right_rev.chrb = pright->chra;
353     right_rev.prtb = pright->prta;
354     right_rev.tileb  = pright->tileb;
355 
356     if ( 0 == CoNode_cmp_unique( pleft, &right_rev ) ) return btrue;
357 
358     return bfalse;
359 }
360 
361 //--------------------------------------------------------------------------------------------
362 //--------------------------------------------------------------------------------------------
CHashList_ctor(CHashList_t * pchlst,int size)363 CHashList_t * CHashList_ctor( CHashList_t * pchlst, int size )
364 {
365     return hash_list_ctor( pchlst, size );
366 }
367 
368 //--------------------------------------------------------------------------------------------
CHashList_dtor(CHashList_t * pchlst)369 CHashList_t * CHashList_dtor( CHashList_t * pchlst )
370 {
371     return hash_list_dtor( pchlst );
372 }
373 
374 //--------------------------------------------------------------------------------------------
CHashList_get_Instance(int size)375 CHashList_t * CHashList_get_Instance( int size )
376 {
377     /// @details BB@> allows access to a "private" CHashList singleton object. This will automatically
378     ///               initialze the _Colist_singleton and (almost) prevent anything from messing up
379     ///               the initialization.
380 
381     // make sure that the collsion system was started
382     collision_system_begin();
383 
384     // if the _CHashList_ptr doesn't exist, create it (and initialize it)
385     if ( NULL == _CHashList_ptr )
386     {
387         _CHashList_ptr              = hash_list_create( size );
388         _collision_hash_initialized = ( NULL != _CHashList_ptr );
389     }
390 
391     // it the pointer exists, but it (somehow) not initialized, do the initialization
392     if ( NULL != _CHashList_ptr && !_collision_hash_initialized )
393     {
394         _CHashList_ptr              = CHashList_ctor( _CHashList_ptr, size );
395         _collision_hash_initialized = ( NULL != _CHashList_ptr );
396     }
397 
398     return _collision_hash_initialized ? _CHashList_ptr : NULL;
399 }
400 
401 //--------------------------------------------------------------------------------------------
CHashList_insert_unique(CHashList_t * pchlst,CoNode_t * pdata,CoNode_ary_t * free_cdata,HashNode_ary_t * free_hnodes)402 bool_t CHashList_insert_unique( CHashList_t * pchlst, CoNode_t * pdata, CoNode_ary_t * free_cdata, HashNode_ary_t * free_hnodes )
403 {
404     Uint32 hashval = 0;
405     CoNode_t * d;
406 
407     hash_node_t * hn;
408     bool_t found;
409     size_t count;
410 
411     if ( NULL == pchlst || NULL == pdata ) return bfalse;
412 
413     // find the hash value for this interaction
414     hashval = CoNode_generate_hash( pdata );
415 
416     found = bfalse;
417     count = hash_list_get_count( pchlst, hashval );
418     if ( count > 0 )
419     {
420         int k;
421 
422         // this hash already exists. check to see if the binary collision exists, too
423         hn = hash_list_get_node( pchlst, hashval );
424         for ( k = 0; k < count; k++ )
425         {
426             if ( !CoNode_matches(( CoNode_t * )( hn->data ), pdata ) )
427             {
428                 found = btrue;
429                 break;
430             }
431         }
432     }
433 
434     // insert this collision
435     if ( !found )
436     {
437         size_t old_count;
438         hash_node_t * old_head, * new_head, * hn;
439 
440         // pick a free collision data
441         d = CoNode_ary_pop_back( free_cdata );
442 
443         // fill it in
444         *d = *pdata;
445 
446         // generate a new hash node
447         hn = HashNode_ary_pop_back( free_hnodes );
448 
449         // link the hash node to the free CoNode
450         hn->data = d;
451 
452         // insert the node at the front of the collision list for this hash
453         old_head = hash_list_get_node( pchlst, hashval );
454         new_head = hash_node_insert_before( old_head, hn );
455         hash_list_set_node( pchlst, hashval, new_head );
456 
457         // add 1 to the count at this hash
458         old_count = hash_list_get_count( pchlst, hashval );
459         hash_list_set_count( pchlst, hashval, old_count + 1 );
460     }
461 
462     return !found;
463 }
464 
465 //--------------------------------------------------------------------------------------------
466 //--------------------------------------------------------------------------------------------
get_chr_mass(chr_t * pchr,float * wt)467 bool_t get_chr_mass( chr_t * pchr, float * wt )
468 {
469     /// @details BB@> calculate a "mass" for an object, taking into account possible infinite masses.
470 
471     float loc_wta;
472 
473     if ( !ACTIVE_PCHR( pchr ) ) return bfalse;
474 
475     // handle oprtional parameters
476     if ( NULL == wt ) wt = &loc_wta;
477 
478     if ( CHR_INFINITE_WEIGHT == pchr->phys.weight )
479     {
480         *wt = -( float )CHR_INFINITE_WEIGHT;
481     }
482     else if ( 0.0f == pchr->phys.bumpdampen )
483     {
484         *wt = -( float )CHR_INFINITE_WEIGHT;
485     }
486     else
487     {
488         *wt = pchr->phys.weight / pchr->phys.bumpdampen;
489     }
490 
491     return btrue;
492 }
493 
494 //--------------------------------------------------------------------------------------------
get_prt_mass(prt_t * pprt,chr_t * pchr,float * wt)495 bool_t get_prt_mass( prt_t * pprt, chr_t * pchr, float * wt )
496 {
497     /// @details BB@> calculate a "mass" for each object, taking into account possible infinite masses.
498 
499     float loc_wprt;
500 
501     if ( NULL == pprt || NULL == pchr ) return bfalse;
502 
503     if ( NULL == wt ) wt = &loc_wprt;
504 
505     // determine an approximate mass for the particle
506     if ( 0.0f == pprt->phys.bumpdampen )
507     {
508         *wt = -( float )CHR_INFINITE_WEIGHT;
509     }
510     else if ( DEFINED_CHR( pprt->attachedto_ref ) )
511     {
512         if ( CHR_INFINITE_WEIGHT == pprt->phys.weight )
513         {
514             *wt = -( float )CHR_INFINITE_WEIGHT;
515         }
516         else
517         {
518             *wt = pprt->phys.weight / pprt->phys.bumpdampen;
519         }
520     }
521     else
522     {
523         float max_damage = ABS( pprt->damage.base ) + ABS( pprt->damage.rand );
524 
525         *wt = 1.0f;
526 
527         if ( 0 == max_damage )
528         {
529             // this is a particle like the wind particles in the whirlwind
530             // make the particle have some kind of predictable constant effect
531             // relative to any character;
532             *wt = pchr->phys.weight / 10.0f;
533         }
534         else
535         {
536             // determine an "effective mass" for the particle, based on it's max damage
537             // and velocity
538 
539             float prt_vel2;
540             float prt_ke;
541 
542             fvec3_t vdiff = fvec3_sub( pprt->vel.v, pchr->vel.v );
543 
544             // the damage is basically like the kinetic energy of the particle
545             prt_vel2 = fvec3_dot_product( vdiff.v, vdiff.v );
546 
547             // It can happen that a damage particle can hit something
548             // at almost zero velocity, which would make for a huge "effective mass".
549             // by making a reasonable "minimum velocity", we limit the maximum mass to
550             // something reasonable
551             prt_vel2 = MAX( 100.0f, prt_vel2 );
552 
553             // get the "kinetic energy" from the damage
554             prt_ke = 3.0f * max_damage;
555 
556             // the faster the particle is going, the smaller the "mass" it
557             // needs to do the damage
558             *wt = prt_ke / ( 0.5f * prt_vel2 );
559         }
560 
561         *wt /= pprt->phys.bumpdampen;
562     }
563 
564     return btrue;
565 }
566 
567 //--------------------------------------------------------------------------------------------
get_recoil_factors(float wta,float wtb,float * recoil_a,float * recoil_b)568 void get_recoil_factors( float wta, float wtb, float * recoil_a, float * recoil_b )
569 {
570     float loc_recoil_a, loc_recoil_b;
571 
572     if ( NULL == recoil_a ) recoil_a = &loc_recoil_a;
573     if ( NULL == recoil_b ) recoil_b = &loc_recoil_b;
574 
575     if ( wta >= ( float )CHR_INFINITE_WEIGHT ) wta = -( float )CHR_INFINITE_WEIGHT;
576     if ( wtb >= ( float )CHR_INFINITE_WEIGHT ) wtb = -( float )CHR_INFINITE_WEIGHT;
577 
578     if ( wta < 0.0f && wtb < 0.0f )
579     {
580         *recoil_a = 0.5f;
581         *recoil_b = 0.5f;
582     }
583     else if ( wta == wtb )
584     {
585         *recoil_a = 0.5f;
586         *recoil_b = 0.5f;
587     }
588     else if ( wta < 0.0f || 0.0f == wtb )
589     {
590         *recoil_a = 0.0f;
591         *recoil_b = 1.0f;
592     }
593     else if ( wtb < 0.0f || 0.0f == wta )
594     {
595         *recoil_a = 1.0f;
596         *recoil_b = 0.0f;
597     }
598     else
599     {
600         *recoil_a = wtb / ( wta + wtb );
601         *recoil_b = wta / ( wta + wtb );
602     }
603 }
604 
605 //--------------------------------------------------------------------------------------------
606 //--------------------------------------------------------------------------------------------
detect_chr_chr_interaction_valid(const CHR_REF ichr_a,const CHR_REF ichr_b)607 bool_t detect_chr_chr_interaction_valid( const CHR_REF ichr_a, const CHR_REF ichr_b )
608 {
609     chr_t *pchr_a, *pchr_b;
610 
611     // Don't interact with self
612     if ( ichr_a == ichr_b ) return bfalse;
613 
614     // Ignore invalid characters
615     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
616     pchr_a = ChrList.lst + ichr_a;
617 
618     // Ignore invalid characters
619     if ( !INGAME_CHR( ichr_b ) ) return bfalse;
620     pchr_b = ChrList.lst + ichr_b;
621 
622     // "non-interacting" objects interact with platforms
623     if (( 0 == pchr_a->bump.size && !pchr_b->platform ) ||
624         ( 0 == pchr_b->bump.size && !pchr_a->platform ) )
625     {
626         return bfalse;
627     }
628 
629     // reject characters that are hidden
630     if ( pchr_a->is_hidden || pchr_b->is_hidden ) return bfalse;
631 
632     // don't interact with your mount, or your held items
633     if ( ichr_a == pchr_b->attachedto || ichr_b == pchr_a->attachedto ) return bfalse;
634 
635     // handle the dismount exception
636     if ( pchr_a->dismount_timer > 0 && pchr_a->dismount_object == ichr_b ) return bfalse;
637     if ( pchr_b->dismount_timer > 0 && pchr_b->dismount_object == ichr_a ) return bfalse;
638 
639     return btrue;
640 }
641 
642 //--------------------------------------------------------------------------------------------
detect_chr_prt_interaction_valid(const CHR_REF ichr_a,const PRT_REF iprt_b)643 bool_t detect_chr_prt_interaction_valid( const CHR_REF ichr_a, const PRT_REF iprt_b )
644 {
645     chr_t * pchr_a;
646     prt_t * pprt_b;
647 
648     // Ignore invalid characters
649     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
650     pchr_a = ChrList.lst + ichr_a;
651 
652     // Ignore invalid characters
653     if ( !INGAME_PRT( iprt_b ) ) return bfalse;
654     pprt_b = PrtList.lst + iprt_b;
655 
656     // reject characters that are hidden
657     if ( pchr_a->is_hidden || pprt_b->is_hidden ) return bfalse;
658 
659     // particles don't "collide" with anything they are attached to.
660     // that only happes through doing bump particle damamge
661     if ( ichr_a == pprt_b->attachedto_ref ) return bfalse;
662 
663     // don't interact if there is no interaction...
664     // the particles and characters should not have been added to the list unless they
665     // are valid for collision
666 
667     return btrue;
668 }
669 
670 //--------------------------------------------------------------------------------------------
add_chr_chr_interaction(CHashList_t * pchlst,const CHR_REF ichr_a,const CHR_REF ichr_b,CoNode_ary_t * pcn_lst,HashNode_ary_t * phn_lst)671 bool_t add_chr_chr_interaction( CHashList_t * pchlst, const CHR_REF ichr_a, const CHR_REF ichr_b, CoNode_ary_t * pcn_lst, HashNode_ary_t * phn_lst )
672 {
673     Uint32 hashval = 0;
674     int count;
675     bool_t found;
676 
677     hash_node_t * n;
678     CoNode_t    * d;
679 
680     if ( NULL == pchlst || NULL == pcn_lst || NULL == phn_lst ) return bfalse;
681 
682     // there is no situation in the game where we allow characters to interact with themselves
683     if ( ichr_a == ichr_b ) return bfalse;
684 
685     // create a hash that is order-independent
686     hashval = MAKE_HASH( REF_TO_INT( ichr_a ), REF_TO_INT( ichr_b ) );
687 
688     found = bfalse;
689     count = pchlst->subcount[hashval];
690     if ( count > 0 )
691     {
692         int i;
693 
694         // this hash already exists. check to see if the binary collision exists, too
695         n = pchlst->sublist[hashval];
696         for ( i = 0; i < count; i++ )
697         {
698             d = ( CoNode_t * )( n->data );
699 
700             // make sure to test both orders
701             if (( d->chra == ichr_a && d->chrb == ichr_b ) || ( d->chra == ichr_b && d->chrb == ichr_a ) )
702             {
703                 found = btrue;
704                 break;
705             }
706         }
707     }
708 
709     // insert this collision
710     if ( !found )
711     {
712         // pick a free collision data
713         EGOBOO_ASSERT( CoNode_ary_get_top( pcn_lst ) < CHR_MAX_COLLISIONS );
714         d = CoNode_ary_pop_back( pcn_lst );
715 
716         // fill it in
717         CoNode_ctor( d );
718         d->chra = ichr_a;
719         d->chrb = ichr_b;
720 
721         // generate a new hash node
722         EGOBOO_ASSERT( HashNode_ary_get_top( phn_lst ) < COLLISION_HASH_NODES );
723         n = HashNode_ary_pop_back( phn_lst );
724 
725         hash_node_ctor( n, ( void* )d );
726 
727         // insert the node
728         pchlst->subcount[hashval]++;
729         pchlst->sublist[hashval] = hash_node_insert_before( pchlst->sublist[hashval], n );
730     }
731 
732     return !found;
733 }
734 
735 //--------------------------------------------------------------------------------------------
add_chr_prt_interaction(CHashList_t * pchlst,const CHR_REF ichr_a,const PRT_REF iprt_b,CoNode_ary_t * pcn_lst,HashNode_ary_t * phn_lst)736 bool_t add_chr_prt_interaction( CHashList_t * pchlst, const CHR_REF ichr_a, const PRT_REF iprt_b, CoNode_ary_t * pcn_lst, HashNode_ary_t * phn_lst )
737 {
738     bool_t found;
739     int    count;
740     Uint32 hashval = 0;
741 
742     hash_node_t * n;
743     CoNode_t    * d;
744 
745     if ( NULL == pchlst ) return bfalse;
746 
747     // create a hash that is order-independent
748     hashval = MAKE_HASH( REF_TO_INT( ichr_a ), REF_TO_INT( iprt_b ) );
749 
750     found = bfalse;
751     count = pchlst->subcount[hashval];
752     if ( count > 0 )
753     {
754         int i ;
755 
756         // this hash already exists. check to see if the binary collision exists, too
757         n = pchlst->sublist[hashval];
758         for ( i = 0; i < count; i++ )
759         {
760             d = ( CoNode_t * )( n->data );
761             if ( d->chra == ichr_a && d->prtb == iprt_b )
762             {
763                 found = btrue;
764                 break;
765             }
766         }
767     }
768 
769     // insert this collision
770     if ( !found )
771     {
772         // pick a free collision data
773         EGOBOO_ASSERT( CoNode_ary_get_top( pcn_lst ) < CHR_MAX_COLLISIONS );
774         d = CoNode_ary_pop_back( pcn_lst );
775 
776         // fill it in
777         CoNode_ctor( d );
778         d->chra = ichr_a;
779         d->prtb = iprt_b;
780 
781         // generate a new hash node
782         EGOBOO_ASSERT( HashNode_ary_get_top( phn_lst ) < COLLISION_HASH_NODES );
783         n = HashNode_ary_pop_back( phn_lst );
784 
785         hash_node_ctor( n, ( void* )d );
786 
787         // insert the node
788         pchlst->subcount[hashval]++;
789         pchlst->sublist[hashval] = hash_node_insert_before( pchlst->sublist[hashval], n );
790     }
791 
792     return !found;
793 }
794 
795 //--------------------------------------------------------------------------------------------
fill_interaction_list(CHashList_t * pchlst,CoNode_ary_t * cn_lst,HashNode_ary_t * hn_lst)796 bool_t fill_interaction_list( CHashList_t * pchlst, CoNode_ary_t * cn_lst, HashNode_ary_t * hn_lst )
797 {
798     int              cnt;
799     int              reaffirmation_count;
800     int              reaffirmation_list[DAMAGE_COUNT];
801     ego_mpd_info_t * mi;
802     BSP_aabb_t       tmp_aabb;
803 
804     if ( NULL == pchlst || NULL == cn_lst || NULL == hn_lst ) return bfalse;
805 
806     mi = &( PMesh->info );
807 
808     // allocate a BSP_aabb_t once, to be shared for all collision tests
809     BSP_aabb_ctor( &tmp_aabb, 2 );
810 
811     // renew the CoNode_t hash table.
812     hash_list_renew( pchlst );
813 
814     // initialize the reaffirmation counters
815     reaffirmation_count = 0;
816     for ( cnt = 0; cnt < DAMAGE_COUNT; cnt++ )
817     {
818         reaffirmation_list[cnt] = 0;
819     }
820 
821     //---- find the character/particle interactions
822 
823     // Find the character-character interactions. Use the ChrList.used_ref, for a change
824     CHashList_inserted = 0;
825     CHR_BEGIN_LOOP_ACTIVE( ichr_a, pchr_a )
826     {
827         oct_bb_t   tmp_oct;
828 
829         // ignore in-accessible objects
830         if ( pchr_a->pack.is_packed || pchr_a->is_hidden ) continue;
831 
832         // keep track of how many objects use reaffirmation, and what kinds of reaffirmation
833         if ( pchr_a->reaffirm_damagetype < DAMAGE_COUNT )
834         {
835             cap_t * pcap = pro_get_pcap( pchr_a->profile_ref );
836             if ( NULL != pcap && pcap->attachedprt_amount > 0 )
837             {
838                 // we COULD use number_of_attached_particles() to determin if the
839                 // character is full of particles, BUT since it scans through the
840                 // entire particle list I don't think it's worth it
841 
842                 reaffirmation_count++;
843                 reaffirmation_list[pchr_a->reaffirm_damagetype]++;
844             }
845         }
846 
847         // use the object velocity to figure out where the volume that the object will occupy during this
848         // update
849         phys_expand_chr_bb( pchr_a, 0.0f, 1.0f, &tmp_oct );
850 
851         // convert the oct_bb_t to a correct BSP_aabb_t
852         BSP_aabb_from_oct_bb( &tmp_aabb, &tmp_oct );
853 
854         // find all collisions with other characters and particles
855         _coll_leaf_lst.top = 0;
856         obj_BSP_collide( &( chr_BSP_root ), &tmp_aabb, &_coll_leaf_lst );
857 
858         // transfer valid _coll_leaf_lst entries to pchlst entries
859         // and sort them by their initial times
860         if ( _coll_leaf_lst.top > 0 )
861         {
862             int j;
863 
864             for ( j = 0; j < _coll_leaf_lst.top; j++ )
865             {
866                 BSP_leaf_t * pleaf;
867                 CoNode_t    tmp_codata;
868                 bool_t      do_insert;
869                 BIT_FIELD   test_platform;
870 
871                 pleaf = _coll_leaf_lst.ary[j];
872                 if ( NULL == pleaf ) continue;
873 
874                 do_insert = bfalse;
875 
876                 if ( BSP_LEAF_CHR == pleaf->data_type )
877                 {
878                     // collided with a character
879                     CHR_REF ichr_b = ( CHR_REF )( pleaf->index );
880 
881                     // do some logic on this to determine whether the collision is valid
882                     if ( detect_chr_chr_interaction_valid( ichr_a, ichr_b ) )
883                     {
884                         chr_t * pchr_b = ChrList.lst + ichr_b;
885 
886                         CoNode_ctor( &tmp_codata );
887 
888                         // do a simple test, since I do not want to resolve the cap_t for these objects here
889                         test_platform = EMPTY_BIT_FIELD;
890                         if ( pchr_a->platform && pchr_b->canuseplatforms ) SET_BIT( test_platform, PHYS_PLATFORM_OBJ1 );
891                         if ( pchr_b->platform && pchr_a->canuseplatforms ) SET_BIT( test_platform, PHYS_PLATFORM_OBJ2 );
892 
893                         // detect a when the possible collision occurred
894                         if ( phys_intersect_oct_bb( &( pchr_a->chr_min_cv ), chr_get_pos_v( pchr_a ), pchr_a->vel.v, &( pchr_b->chr_min_cv ), chr_get_pos_v( pchr_b ), pchr_b->vel.v, test_platform, &( tmp_codata.cv ), &( tmp_codata.tmin ), &( tmp_codata.tmax ) ) )
895                         {
896                             tmp_codata.chra = ichr_a;
897                             tmp_codata.chrb = ichr_b;
898 
899                             do_insert = btrue;
900                         }
901                     }
902                 }
903                 else
904                 {
905                     // how did we get here?
906                     log_warning( "fill_interaction_list() - found non-character in the character BSP\n" );
907                 }
908 
909                 if ( do_insert )
910                 {
911                     if ( CHashList_insert_unique( pchlst, &tmp_codata, cn_lst, hn_lst ) )
912                     {
913                         CHashList_inserted++;
914                     }
915                 }
916             }
917         }
918 
919         _coll_leaf_lst.top = 0;
920         obj_BSP_collide( &( prt_BSP_root ), &tmp_aabb, &_coll_leaf_lst );
921         if ( _coll_leaf_lst.top > 0 )
922         {
923             int j;
924 
925             for ( j = 0; j < _coll_leaf_lst.top; j++ )
926             {
927                 BSP_leaf_t * pleaf;
928                 CoNode_t    tmp_codata;
929                 bool_t      do_insert;
930                 BIT_FIELD   test_platform;
931 
932                 pleaf = _coll_leaf_lst.ary[j];
933                 if ( NULL == pleaf ) continue;
934 
935                 do_insert = bfalse;
936 
937                 if ( BSP_LEAF_PRT == pleaf->data_type )
938                 {
939                     // collided with a particle
940                     PRT_REF iprt_b = ( PRT_REF )( pleaf->index );
941 
942                     // do some logic on this to determine whether the collision is valid
943                     if ( detect_chr_prt_interaction_valid( ichr_a, iprt_b ) )
944                     {
945                         prt_t * pprt_b = PrtList.lst + iprt_b;
946 
947                         CoNode_ctor( &tmp_codata );
948 
949                         // do a simple test, since I do not want to resolve the cap_t for these objects here
950                         test_platform = pchr_a->platform ? PHYS_PLATFORM_OBJ1 : 0;
951 
952                         // detect a when the possible collision occurred
953                         if ( phys_intersect_oct_bb( &( pchr_a->chr_max_cv ), chr_get_pos_v( pchr_a ), pchr_a->vel.v, &( pprt_b->prt_max_cv ), prt_get_pos_v( pprt_b ), pprt_b->vel.v, test_platform, &( tmp_codata.cv ), &( tmp_codata.tmin ), &( tmp_codata.tmax ) ) )
954                         {
955                             tmp_codata.chra = ichr_a;
956                             tmp_codata.prtb = iprt_b;
957 
958                             do_insert = btrue;
959                         }
960                     }
961                 }
962                 else
963                 {
964                     // how did we get here?
965                     log_warning( "fill_interaction_list() - found non-particle in the particle BSP\n" );
966                 }
967 
968                 if ( do_insert )
969                 {
970                     if ( CHashList_insert_unique( pchlst, &tmp_codata, cn_lst, hn_lst ) )
971                     {
972                         CHashList_inserted++;
973                     }
974                 }
975             }
976         }
977     }
978     CHR_END_LOOP();
979 
980     //---- find some specialized character-particle interactions
981     //     namely particles that end-bump or particles that reaffirm characters
982 
983     PRT_BEGIN_LOOP_ACTIVE( iprt, bdl )
984     {
985         oct_bb_t   tmp_oct;
986         bool_t     can_reaffirm, needs_bump;
987 
988         // if the particle is in the BSP, then it has already had it's chance to collide
989         if ( bdl.prt_ptr->bsp_leaf.inserted ) continue;
990 
991         // does the particle potentially reaffirm a character?
992         can_reaffirm = ( bdl.prt_ptr->damagetype < DAMAGE_COUNT ) && ( 0 != reaffirmation_list[bdl.prt_ptr->damagetype] );
993 
994         // does the particle end_bump or end_ground?
995         needs_bump = bdl.pip_ptr->end_bump || bdl.pip_ptr->end_ground;
996 
997         if ( !can_reaffirm && !needs_bump ) continue;
998 
999         // use the object velocity to figure out where the volume that the object will occupy during this
1000         // update
1001         phys_expand_prt_bb( bdl.prt_ptr, 0.0f, 1.0f, &tmp_oct );
1002 
1003         // convert the oct_bb_t to a correct BSP_aabb_t
1004         BSP_aabb_from_oct_bb( &tmp_aabb, &tmp_oct );
1005 
1006         // find all collisions with characters
1007         _coll_leaf_lst.top = 0;
1008         obj_BSP_collide( &( chr_BSP_root ), &tmp_aabb, &_coll_leaf_lst );
1009 
1010         // transfer valid _coll_leaf_lst entries to pchlst entries
1011         // and sort them by their initial times
1012         if ( _coll_leaf_lst.top > 0 )
1013         {
1014             int          j;
1015             CoNode_t     tmp_codata;
1016             BIT_FIELD    test_platform;
1017             CHR_REF      ichr_a = MAX_CHR;
1018             BSP_leaf_t * pleaf = NULL;
1019             bool_t       do_insert = bfalse;
1020 
1021             for ( j = 0; j < _coll_leaf_lst.top; j++ )
1022             {
1023                 pleaf = _coll_leaf_lst.ary[j];
1024                 if ( NULL == pleaf ) continue;
1025 
1026                 ichr_a = ( CHR_REF )( pleaf->index );
1027 
1028                 do_insert = bfalse;
1029 
1030                 if ( BSP_LEAF_CHR == pleaf->data_type && VALID_CHR_RANGE( ichr_a ) )
1031                 {
1032                     // collided with a character
1033                     bool_t loc_reaffirms     = can_reaffirm;
1034                     bool_t loc_needs_bump    = needs_bump;
1035                     bool_t interaction_valid = bfalse;
1036 
1037                     chr_t * pchr_a = ChrList.lst + ichr_a;
1038 
1039                     // can this particle affect the character through reaffirmation
1040                     if ( loc_reaffirms )
1041                     {
1042                         // does this interaction support affirmation?
1043                         if ( bdl.prt_ptr->damagetype != pchr_a->reaffirm_damagetype )
1044                         {
1045                             loc_reaffirms = bfalse;
1046                         }
1047 
1048                         // if it is already attached to this character, no more reaffirmation
1049                         if ( bdl.prt_ptr->attachedto_ref == ichr_a )
1050                         {
1051                             loc_reaffirms = bfalse;
1052                         }
1053                     }
1054 
1055                     // you can't be bumped by items that you are attached to
1056                     if ( loc_needs_bump && bdl.prt_ptr->attachedto_ref == ichr_a )
1057                     {
1058                         loc_needs_bump = bfalse;
1059                     }
1060 
1061                     // can this character affect this particle through bumping?
1062                     if ( loc_needs_bump )
1063                     {
1064                         // the valid bump interactions
1065                         bool_t end_money  = ( bdl.pip_ptr->bump_money > 0 ) && pchr_a->cangrabmoney;
1066                         bool_t end_bump   = ( bdl.pip_ptr->end_bump ) && ( 0 != pchr_a->bump_stt.size );
1067                         bool_t end_ground = ( bdl.pip_ptr->end_ground ) && (( 0 != pchr_a->bump_stt.size ) || pchr_a->platform );
1068 
1069                         if ( !end_money && !end_bump && !end_ground )
1070                         {
1071                             loc_needs_bump = bfalse;
1072                         }
1073                     }
1074 
1075                     // do a little more logic on this to determine whether the collision is valid
1076                     interaction_valid = bfalse;
1077                     if ( loc_reaffirms || loc_needs_bump )
1078                     {
1079                         if ( detect_chr_prt_interaction_valid( ichr_a, bdl.prt_ref ) )
1080                         {
1081                             interaction_valid = btrue;
1082                         }
1083                         else
1084                         {
1085                             interaction_valid = bfalse;
1086                         }
1087                     }
1088 
1089                     // only do the more expensive calculation if the
1090                     // particle can interact with the object
1091                     if ( interaction_valid )
1092                     {
1093                         CoNode_ctor( &tmp_codata );
1094 
1095                         // do a simple test, since I do not want to resolve the cap_t for these objects here
1096                         test_platform = EMPTY_BIT_FIELD;
1097                         if ( pchr_a->platform && ( SPRITE_SOLID == bdl.prt_ptr->type ) ) SET_BIT( test_platform, PHYS_PLATFORM_OBJ1 );
1098 
1099                         // detect a when the possible collision occurred
1100                         if ( phys_intersect_oct_bb( &( pchr_a->chr_min_cv ), chr_get_pos_v( pchr_a ), pchr_a->vel.v, &( bdl.prt_ptr->prt_max_cv ), prt_get_pos_v( bdl.prt_ptr ), bdl.prt_ptr->vel.v, test_platform, &( tmp_codata.cv ), &( tmp_codata.tmin ), &( tmp_codata.tmax ) ) )
1101                         {
1102 
1103                             tmp_codata.chra = ichr_a;
1104                             tmp_codata.prtb = bdl.prt_ref;
1105 
1106                             do_insert = btrue;
1107                         }
1108                     }
1109                 }
1110                 else if ( BSP_LEAF_PRT == pleaf->data_type )
1111                 {
1112                     // this should never happen
1113                 }
1114 
1115                 if ( do_insert )
1116                 {
1117                     if ( CHashList_insert_unique( pchlst, &tmp_codata, cn_lst, hn_lst ) )
1118                     {
1119                         CHashList_inserted++;
1120                     }
1121                 }
1122             }
1123         }
1124     }
1125     PRT_END_LOOP();
1126 
1127     // do this manually in C
1128     BSP_aabb_dtor( &tmp_aabb );
1129 
1130     return btrue;
1131 }
1132 
1133 //--------------------------------------------------------------------------------------------
fill_bumplists()1134 bool_t fill_bumplists()
1135 {
1136     /// @details BB@> Fill in the obj_BSP_t for this frame
1137     ///
1138     /// @note do not use obj_BSP_clear every frame, because the number of pre-allocated nodes can be quite large.
1139     /// Instead, just remove the nodes from the tree, fill the tree, and then prune any empty branches/leaves
1140 
1141     // empty out the BSP node lists
1142     chr_BSP_clear();
1143     prt_BSP_clear();
1144 
1145     // fill up the BSP list based on the current locations
1146     chr_BSP_fill();
1147     prt_BSP_fill();
1148 
1149     // remove empty branches from the tree
1150     if ( 63 == ( game_frame_all & 63 ) )
1151     {
1152         BSP_tree_prune( &( chr_BSP_root.tree ) );
1153         BSP_tree_prune( &( prt_BSP_root.tree ) );
1154     }
1155 
1156     return btrue;
1157 }
1158 
1159 //--------------------------------------------------------------------------------------------
do_chr_platform_detection(const CHR_REF ichr_a,const CHR_REF ichr_b)1160 bool_t do_chr_platform_detection( const CHR_REF ichr_a, const CHR_REF ichr_b )
1161 {
1162     chr_t * pchr_a, * pchr_b;
1163 
1164     bool_t platform_a, platform_b;
1165 
1166     oct_vec_t odepth;
1167     bool_t collide_x  = bfalse;
1168     bool_t collide_y  = bfalse;
1169     bool_t collide_xy = bfalse;
1170     bool_t collide_yx = bfalse;
1171     bool_t collide_z  = bfalse;
1172     bool_t chara_on_top;
1173 
1174     // make sure that A is valid
1175     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
1176     pchr_a = ChrList.lst + ichr_a;
1177 
1178     // make sure that B is valid
1179     if ( !INGAME_CHR( ichr_b ) ) return bfalse;
1180     pchr_b = ChrList.lst + ichr_b;
1181 
1182     // if you are mounted, only your mount is affected by platforms
1183     if ( INGAME_CHR( pchr_a->attachedto ) || INGAME_CHR( pchr_b->attachedto ) ) return bfalse;
1184 
1185     // only check possible object-platform interactions
1186     platform_a = pchr_b->canuseplatforms && pchr_a->platform;
1187     platform_b = pchr_a->canuseplatforms && pchr_b->platform;
1188     if ( !platform_a && !platform_b ) return bfalse;
1189 
1190     //---- since we are doing bump_all_mounts() before bump_all_platforms()
1191     // mount detection is done before platform attachment, and these lines of code
1192     // aren't needed
1193     //
1194     //// If we can mount this platform, skip it
1195     //mount_a = chr_can_mount( ichr_b, ichr_a );
1196     //if ( mount_a && pchr_a->enviro.level < pchr_b->pos.z + pchr_b->bump.height + PLATTOLERANCE )
1197     //    return bfalse;
1198     //
1199     //// If we can mount this platform, skip it
1200     //mount_b = chr_can_mount( ichr_a, ichr_b );
1201     //if ( mount_b && pchr_b->enviro.level < pchr_a->pos.z + pchr_a->bump.height + PLATTOLERANCE )
1202     //    return bfalse;
1203 
1204     odepth[OCT_Z]  = MIN( pchr_b->chr_min_cv.maxs[OCT_Z] + pchr_b->pos.z, pchr_a->chr_min_cv.maxs[OCT_Z] + pchr_a->pos.z ) -
1205                      MAX( pchr_b->chr_min_cv.mins[OCT_Z] + pchr_b->pos.z, pchr_a->chr_min_cv.mins[OCT_Z] + pchr_a->pos.z );
1206 
1207     collide_z  = ( odepth[OCT_Z] > -PLATTOLERANCE && odepth[OCT_Z] < PLATTOLERANCE );
1208 
1209     if ( !collide_z ) return bfalse;
1210 
1211     // initialize the overlap depths
1212     odepth[OCT_X] = odepth[OCT_Y] = odepth[OCT_XY] = odepth[OCT_YX] = 0.0f;
1213 
1214     // determine how the characters can be attached
1215     chara_on_top = btrue;
1216     odepth[OCT_Z] = 2 * PLATTOLERANCE;
1217     if ( platform_a && platform_b )
1218     {
1219         float depth_a, depth_b;
1220 
1221         depth_a = ( pchr_b->pos.z + pchr_b->chr_min_cv.maxs[OCT_Z] ) - ( pchr_a->pos.z + pchr_a->chr_min_cv.mins[OCT_Z] );
1222         depth_b = ( pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] ) - ( pchr_b->pos.z + pchr_b->chr_min_cv.mins[OCT_Z] );
1223 
1224         odepth[OCT_Z] = MIN( pchr_b->pos.z + pchr_b->chr_min_cv.maxs[OCT_Z], pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] ) -
1225                         MAX( pchr_b->pos.z + pchr_b->chr_min_cv.mins[OCT_Z], pchr_a->pos.z + pchr_a->chr_min_cv.mins[OCT_Z] );
1226 
1227         chara_on_top = ABS( odepth[OCT_Z] - depth_a ) < ABS( odepth[OCT_Z] - depth_b );
1228 
1229         // the collision is determined by the platform size
1230         if ( chara_on_top )
1231         {
1232             // size of a doesn't matter
1233             odepth[OCT_X]  = MIN(( pchr_b->chr_min_cv.maxs[OCT_X] + pchr_b->pos.x ) - pchr_a->pos.x,
1234                                  pchr_a->pos.x - ( pchr_b->chr_min_cv.mins[OCT_X] + pchr_b->pos.x ) );
1235 
1236             odepth[OCT_Y]  = MIN(( pchr_b->chr_min_cv.maxs[OCT_Y] + pchr_b->pos.y ) -  pchr_a->pos.y,
1237                                  pchr_a->pos.y - ( pchr_b->chr_min_cv.mins[OCT_Y] + pchr_b->pos.y ) );
1238 
1239             odepth[OCT_XY] = MIN(( pchr_b->chr_min_cv.maxs[OCT_XY] + ( pchr_b->pos.x + pchr_b->pos.y ) ) - ( pchr_a->pos.x + pchr_a->pos.y ),
1240                                  ( pchr_a->pos.x + pchr_a->pos.y ) - ( pchr_b->chr_min_cv.mins[OCT_XY] + ( pchr_b->pos.x + pchr_b->pos.y ) ) );
1241 
1242             odepth[OCT_YX] = MIN(( pchr_b->chr_min_cv.maxs[OCT_YX] + ( -pchr_b->pos.x + pchr_b->pos.y ) ) - ( -pchr_a->pos.x + pchr_a->pos.y ),
1243                                  ( -pchr_a->pos.x + pchr_a->pos.y ) - ( pchr_b->chr_min_cv.mins[OCT_YX] + ( -pchr_b->pos.x + pchr_b->pos.y ) ) );
1244         }
1245         else
1246         {
1247             // size of b doesn't matter
1248 
1249             odepth[OCT_X]  = MIN(( pchr_a->chr_min_cv.maxs[OCT_X] + pchr_a->pos.x ) - pchr_b->pos.x,
1250                                  pchr_b->pos.x - ( pchr_a->chr_min_cv.mins[OCT_X] + pchr_a->pos.x ) );
1251 
1252             odepth[OCT_Y]  = MIN(( pchr_a->chr_min_cv.maxs[OCT_Y] + pchr_a->pos.y ) -  pchr_b->pos.y,
1253                                  pchr_b->pos.y - ( pchr_a->chr_min_cv.mins[OCT_Y] + pchr_a->pos.y ) );
1254 
1255             odepth[OCT_XY] = MIN(( pchr_a->chr_min_cv.maxs[OCT_XY] + ( pchr_a->pos.x + pchr_a->pos.y ) ) - ( pchr_b->pos.x + pchr_b->pos.y ),
1256                                  ( pchr_b->pos.x + pchr_b->pos.y ) - ( pchr_a->chr_min_cv.mins[OCT_XY] + ( pchr_a->pos.x + pchr_a->pos.y ) ) );
1257 
1258             odepth[OCT_YX] = MIN(( pchr_a->chr_min_cv.maxs[OCT_YX] + ( -pchr_a->pos.x + pchr_a->pos.y ) ) - ( -pchr_b->pos.x + pchr_b->pos.y ),
1259                                  ( -pchr_b->pos.x + pchr_b->pos.y ) - ( pchr_a->chr_min_cv.mins[OCT_YX] + ( -pchr_a->pos.x + pchr_a->pos.y ) ) );
1260         }
1261     }
1262     else if ( platform_a )
1263     {
1264         chara_on_top = bfalse;
1265         odepth[OCT_Z] = ( pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] ) - ( pchr_b->pos.z + pchr_b->chr_min_cv.mins[OCT_Z] );
1266 
1267         // size of b doesn't matter
1268 
1269         odepth[OCT_X]  = MIN(( pchr_a->chr_min_cv.maxs[OCT_X] + pchr_a->pos.x ) - pchr_b->pos.x,
1270                              pchr_b->pos.x - ( pchr_a->chr_min_cv.mins[OCT_X] + pchr_a->pos.x ) );
1271 
1272         odepth[OCT_Y]  = MIN(( pchr_a->chr_min_cv.maxs[OCT_Y] + pchr_a->pos.y ) -  pchr_b->pos.y,
1273                              pchr_b->pos.y - ( pchr_a->chr_min_cv.mins[OCT_Y] + pchr_a->pos.y ) );
1274 
1275         odepth[OCT_XY] = MIN(( pchr_a->chr_min_cv.maxs[OCT_XY] + ( pchr_a->pos.x + pchr_a->pos.y ) ) - ( pchr_b->pos.x + pchr_b->pos.y ),
1276                              ( pchr_b->pos.x + pchr_b->pos.y ) - ( pchr_a->chr_min_cv.mins[OCT_XY] + ( pchr_a->pos.x + pchr_a->pos.y ) ) );
1277 
1278         odepth[OCT_YX] = MIN(( pchr_a->chr_min_cv.maxs[OCT_YX] + ( -pchr_a->pos.x + pchr_a->pos.y ) ) - ( -pchr_b->pos.x + pchr_b->pos.y ),
1279                              ( -pchr_b->pos.x + pchr_b->pos.y ) - ( pchr_a->chr_min_cv.mins[OCT_YX] + ( -pchr_a->pos.x + pchr_a->pos.y ) ) );
1280     }
1281     else if ( platform_b )
1282     {
1283         chara_on_top = btrue;
1284         odepth[OCT_Z] = ( pchr_b->pos.z + pchr_b->chr_min_cv.maxs[OCT_Z] ) - ( pchr_a->pos.z + pchr_a->chr_min_cv.mins[OCT_Z] );
1285 
1286         // size of a doesn't matter
1287         odepth[OCT_X]  = MIN(( pchr_b->chr_min_cv.maxs[OCT_X] + pchr_b->pos.x ) - pchr_a->pos.x,
1288                              pchr_a->pos.x - ( pchr_b->chr_min_cv.mins[OCT_X] + pchr_b->pos.x ) );
1289 
1290         odepth[OCT_Y]  = MIN( pchr_b->chr_min_cv.maxs[OCT_Y] + ( pchr_b->pos.y -  pchr_a->pos.y ),
1291                               ( pchr_a->pos.y - pchr_b->chr_min_cv.mins[OCT_Y] ) + pchr_b->pos.y );
1292 
1293         odepth[OCT_XY] = MIN(( pchr_b->chr_min_cv.maxs[OCT_XY] + ( pchr_b->pos.x + pchr_b->pos.y ) ) - ( pchr_a->pos.x + pchr_a->pos.y ),
1294                              ( pchr_a->pos.x + pchr_a->pos.y ) - ( pchr_b->chr_min_cv.mins[OCT_XY] + ( pchr_b->pos.x + pchr_b->pos.y ) ) );
1295 
1296         odepth[OCT_YX] = MIN(( pchr_b->chr_min_cv.maxs[OCT_YX] + ( -pchr_b->pos.x + pchr_b->pos.y ) ) - ( -pchr_a->pos.x + pchr_a->pos.y ),
1297                              ( -pchr_a->pos.x + pchr_a->pos.y ) - ( pchr_b->chr_min_cv.mins[OCT_YX] + ( -pchr_b->pos.x + pchr_b->pos.y ) ) );
1298 
1299     }
1300 
1301     collide_x  = odepth[OCT_X]  > 0.0f;
1302     collide_y  = odepth[OCT_Y]  > 0.0f;
1303     collide_xy = odepth[OCT_XY] > 0.0f;
1304     collide_yx = odepth[OCT_YX] > 0.0f;
1305     collide_z  = ( odepth[OCT_Z] > -PLATTOLERANCE && odepth[OCT_Z] < PLATTOLERANCE );
1306 
1307     if ( collide_x && collide_y && collide_xy && collide_yx && collide_z )
1308     {
1309         // check for the best possible attachment
1310         if ( chara_on_top )
1311         {
1312             if ( pchr_b->pos.z + pchr_b->chr_min_cv.maxs[OCT_Z] > pchr_a->targetplatform_level )
1313             {
1314                 // set, but do not attach the platforms yet
1315                 pchr_a->targetplatform_level = pchr_b->pos.z + pchr_b->chr_min_cv.maxs[OCT_Z];
1316                 pchr_a->targetplatform_ref   = ichr_b;
1317             }
1318         }
1319         else
1320         {
1321             if ( pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] > pchr_b->targetplatform_level )
1322             {
1323                 // set, but do not attach the platforms yet
1324                 pchr_b->targetplatform_level = pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z];
1325                 pchr_b->targetplatform_ref   = ichr_a;
1326             }
1327         }
1328     }
1329 
1330     return btrue;
1331 }
1332 
1333 //--------------------------------------------------------------------------------------------
do_prt_platform_detection(const CHR_REF ichr_a,const PRT_REF iprt_b)1334 bool_t do_prt_platform_detection( const CHR_REF ichr_a, const PRT_REF iprt_b )
1335 {
1336     chr_t * pchr_a;
1337     prt_t * pprt_b;
1338 
1339     bool_t platform_a;
1340 
1341     oct_vec_t odepth;
1342     bool_t collide_x  = bfalse;
1343     bool_t collide_y  = bfalse;
1344     bool_t collide_xy = bfalse;
1345     bool_t collide_yx = bfalse;
1346     bool_t collide_z  = bfalse;
1347 
1348     // make sure that A is valid
1349     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
1350     pchr_a = ChrList.lst + ichr_a;
1351 
1352     // make sure that B is valid
1353     if ( !INGAME_PRT( iprt_b ) ) return bfalse;
1354     pprt_b = PrtList.lst + iprt_b;
1355 
1356     // if you are mounted, only your mount is affected by platforms
1357     if ( INGAME_CHR( pchr_a->attachedto ) || INGAME_CHR( pprt_b->attachedto_ref ) ) return bfalse;
1358 
1359     // only check possible object-platform interactions
1360     platform_a = /* pprt_b->canuseplatforms && */ pchr_a->platform;
1361     if ( !platform_a ) return bfalse;
1362 
1363     odepth[OCT_Z]  = MIN( pprt_b->prt_max_cv.maxs[OCT_Z] + pprt_b->pos.z, pchr_a->chr_min_cv.maxs[OCT_Z] + pchr_a->pos.z ) -
1364                      MAX( pprt_b->prt_max_cv.mins[OCT_Z] + pprt_b->pos.z, pchr_a->chr_min_cv.mins[OCT_Z] + pchr_a->pos.z );
1365 
1366     collide_z  = ( odepth[OCT_Z] > -PLATTOLERANCE && odepth[OCT_Z] < PLATTOLERANCE );
1367 
1368     if ( !collide_z ) return bfalse;
1369 
1370     // initialize the overlap depths
1371     odepth[OCT_X] = odepth[OCT_Y] = odepth[OCT_XY] = odepth[OCT_YX] = 0.0f;
1372 
1373     // determine how the characters can be attached
1374     odepth[OCT_Z] = ( pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] ) - ( pprt_b->pos.z + pprt_b->prt_max_cv.mins[OCT_Z] );
1375 
1376     // size of b doesn't matter
1377 
1378     odepth[OCT_X]  = MIN(( pchr_a->chr_min_cv.maxs[OCT_X] + pchr_a->pos.x ) - pprt_b->pos.x,
1379                          pprt_b->pos.x - ( pchr_a->chr_min_cv.mins[OCT_X] + pchr_a->pos.x ) );
1380 
1381     odepth[OCT_Y]  = MIN(( pchr_a->chr_min_cv.maxs[OCT_Y] + pchr_a->pos.y ) -  pprt_b->pos.y,
1382                          pprt_b->pos.y - ( pchr_a->chr_min_cv.mins[OCT_Y] + pchr_a->pos.y ) );
1383 
1384     odepth[OCT_XY] = MIN(( pchr_a->chr_min_cv.maxs[OCT_XY] + ( pchr_a->pos.x + pchr_a->pos.y ) ) - ( pprt_b->pos.x + pprt_b->pos.y ),
1385                          ( pprt_b->pos.x + pprt_b->pos.y ) - ( pchr_a->chr_min_cv.mins[OCT_XY] + ( pchr_a->pos.x + pchr_a->pos.y ) ) );
1386 
1387     odepth[OCT_YX] = MIN(( pchr_a->chr_min_cv.maxs[OCT_YX] + ( -pchr_a->pos.x + pchr_a->pos.y ) ) - ( -pprt_b->pos.x + pprt_b->pos.y ),
1388                          ( -pprt_b->pos.x + pprt_b->pos.y ) - ( pchr_a->chr_min_cv.mins[OCT_YX] + ( -pchr_a->pos.x + pchr_a->pos.y ) ) );
1389 
1390     collide_x  = odepth[OCT_X]  > 0.0f;
1391     collide_y  = odepth[OCT_Y]  > 0.0f;
1392     collide_xy = odepth[OCT_XY] > 0.0f;
1393     collide_yx = odepth[OCT_YX] > 0.0f;
1394     collide_z  = ( odepth[OCT_Z] > -PLATTOLERANCE && odepth[OCT_Z] < PLATTOLERANCE );
1395 
1396     if ( collide_x && collide_y && collide_xy && collide_yx && collide_z )
1397     {
1398         // check for the best possible attachment
1399         if ( pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] > pprt_b->targetplatform_level )
1400         {
1401             // set, but do not attach the platforms yet
1402             pprt_b->targetplatform_level = pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z];
1403             pprt_b->targetplatform_ref   = ichr_a;
1404         }
1405     }
1406 
1407     return btrue;
1408 }
1409 
1410 //--------------------------------------------------------------------------------------------
bump_all_objects()1411 void bump_all_objects()
1412 {
1413     /// @details ZZ@> This function sets handles characters hitting other characters or particles
1414 
1415     CHashList_t * pchlst;
1416     size_t        co_node_count;
1417 
1418     // create a collision hash table that can keep track of 512
1419     // binary collisions per frame
1420     pchlst = CHashList_get_Instance( -1 );
1421     if ( NULL == pchlst )
1422     {
1423         log_error( "bump_all_objects() - cannot access the CHashList_t singleton" );
1424     }
1425 
1426     // set up the collision node array
1427     _co_ary.top = _co_ary.alloc;
1428 
1429     // set up the hash node array
1430     _hn_ary.top = _hn_ary.alloc;
1431 
1432     // fill up the BSP structures
1433     fill_bumplists();
1434 
1435     // use the BSP structures to detect possible binary interactions
1436     fill_interaction_list( pchlst, &_co_ary, &_hn_ary );
1437 
1438     // convert the CHashList_t into a CoNode_ary_t and sort
1439     co_node_count = hash_list_count_nodes( pchlst );
1440 
1441     if ( co_node_count > 0 )
1442     {
1443         hash_list_iterator_t it;
1444 
1445         _coll_node_lst.top = 0;
1446 
1447         hash_list_iterator_ctor( &it );
1448         hash_list_iterator_set_begin( &it, pchlst );
1449         for ( /* nothing */; !hash_list_iterator_done( &it, pchlst ); hash_list_iterator_next( &it, pchlst ) )
1450         {
1451             CoNode_t * ptr = ( CoNode_t * )hash_list_iterator_ptr( &it );
1452             if ( NULL == ptr ) break;
1453 
1454             CoNode_ary_push_back( &_coll_node_lst, *ptr );
1455         }
1456 
1457         if ( _coll_node_lst.top > 1 )
1458         {
1459             // arrange the actual nodes by time order
1460             qsort( _coll_node_lst.ary, _coll_node_lst.top, sizeof( CoNode_t ), CoNode_cmp );
1461         }
1462 
1463         // handle interaction with mounts
1464         // put this before platforms, otherwise pointing is just too hard
1465         bump_all_mounts( &_coll_node_lst );
1466 
1467         // handle interaction with platforms
1468         bump_all_platforms( &_coll_node_lst );
1469 
1470         // handle all the collisions
1471         bump_all_collisions( &_coll_node_lst );
1472     }
1473 
1474     // The following functions need to be called any time you actually change a charcter's position
1475     keep_weapons_with_holders();
1476     attach_all_particles();
1477     update_all_character_matrices();
1478 }
1479 
1480 //--------------------------------------------------------------------------------------------
bump_all_platforms(CoNode_ary_t * pcn_ary)1481 bool_t bump_all_platforms( CoNode_ary_t * pcn_ary )
1482 {
1483     /// @details BB@> Detect all character and particle interactions with platforms, then attach them.
1484     ///
1485     /// @note it is important to only attach the character to a platform once, so its
1486     ///  weight does not get applied to multiple platforms
1487     ///
1488     /// @note the function move_one_character_get_environment() has already been called from within the
1489     ///  move_one_character() function, so the environment has already been determined this round
1490 
1491     int        cnt;
1492     CoNode_t * d;
1493 
1494     if ( NULL == pcn_ary ) return bfalse;
1495 
1496     //---- Detect all platform attachments
1497     for ( cnt = 0; cnt < pcn_ary->top; cnt++ )
1498     {
1499         d = pcn_ary->ary + cnt;
1500 
1501         // only look at character-platform or particle-platform interactions interactions
1502         if ( MAX_PRT != d->prta && MAX_PRT != d->prtb ) continue;
1503 
1504         if ( MAX_CHR != d->chra && MAX_CHR != d->chrb )
1505         {
1506             do_chr_platform_detection( d->chra, d->chrb );
1507         }
1508         else if ( MAX_CHR != d->chra && MAX_PRT != d->prtb )
1509         {
1510             do_prt_platform_detection( d->chra, d->prtb );
1511         }
1512         if ( MAX_PRT != d->prta && MAX_CHR != d->chrb )
1513         {
1514             do_prt_platform_detection( d->chrb, d->prta );
1515         }
1516     }
1517 
1518     //---- Do the actual platform attachments.
1519 
1520     // Doing the attachments after detecting the best platform
1521     // prevents an object from attaching it to multiple platforms as it
1522     // is still trying to find the best one
1523     for ( cnt = 0; cnt < pcn_ary->top; cnt++ )
1524     {
1525         d = pcn_ary->ary + cnt;
1526 
1527         // only look at character-character interactions
1528         //if ( MAX_PRT != d->prta && MAX_PRT != d->prtb ) continue;
1529 
1530         if ( MAX_CHR != d->chra && MAX_CHR != d->chrb )
1531         {
1532             if ( INGAME_CHR( d->chra ) && INGAME_CHR( d->chrb ) )
1533             {
1534                 if ( ChrList.lst[d->chra].targetplatform_ref == d->chrb )
1535                 {
1536                     attach_chr_to_platform( ChrList.lst + d->chra, ChrList.lst + d->chrb );
1537                 }
1538                 else if ( ChrList.lst[d->chrb].targetplatform_ref == d->chra )
1539                 {
1540                     attach_chr_to_platform( ChrList.lst + d->chrb, ChrList.lst + d->chra );
1541                 }
1542 
1543             }
1544         }
1545         else if ( MAX_CHR != d->chra && MAX_PRT != d->prtb )
1546         {
1547             if ( INGAME_CHR( d->chra ) && INGAME_PRT( d->prtb ) )
1548             {
1549                 if ( PrtList.lst[d->prtb].targetplatform_ref == d->chra )
1550                 {
1551                     attach_prt_to_platform( PrtList.lst + d->prtb, ChrList.lst + d->chra );
1552                 }
1553             }
1554         }
1555         else if ( MAX_CHR != d->chrb && MAX_PRT != d->prta )
1556         {
1557             if ( INGAME_CHR( d->chrb ) && INGAME_PRT( d->prta ) )
1558             {
1559                 if ( PrtList.lst[d->prta].targetplatform_ref == d->chrb )
1560                 {
1561                     attach_prt_to_platform( PrtList.lst + d->prta, ChrList.lst + d->chrb );
1562                 }
1563             }
1564         }
1565     }
1566 
1567     //---- remove any bad platforms
1568 
1569     // attach_prt_to_platform() erases targetplatform_ref, so any character with
1570     // (MAX_CHR != targetplatform_ref) must not be connected to a platform at all
1571     CHR_BEGIN_LOOP_ACTIVE( ichr, pchr )
1572     {
1573         if ( MAX_CHR != pchr->onwhichplatform_ref && pchr->onwhichplatform_update < update_wld )
1574         {
1575             detach_character_from_platform( pchr );
1576         }
1577     }
1578     CHR_END_LOOP();
1579 
1580     // attach_prt_to_platform() erases targetplatform_ref, so any particle with
1581     // (MAX_CHR != targetplatform_ref) must not be connected to a platform at all
1582     PRT_BEGIN_LOOP_DISPLAY( iprt, bdl_prt )
1583     {
1584         if ( MAX_CHR != bdl_prt.prt_ptr->onwhichplatform_ref && bdl_prt.prt_ptr->onwhichplatform_update < update_wld )
1585         {
1586             detach_particle_from_platform( bdl_prt.prt_ptr );
1587         }
1588     }
1589     PRT_END_LOOP();
1590 
1591     return btrue;
1592 }
1593 
1594 //--------------------------------------------------------------------------------------------
bump_all_mounts(CoNode_ary_t * pcn_ary)1595 bool_t bump_all_mounts( CoNode_ary_t * pcn_ary )
1596 {
1597     /// @details BB@> Detect all character interactions with mounts, then attach them.
1598 
1599     int        cnt;
1600     CoNode_t * d;
1601 
1602     if ( NULL == pcn_ary ) return bfalse;
1603 
1604     // Do mounts
1605     for ( cnt = 0; cnt < pcn_ary->top; cnt++ )
1606     {
1607         d = pcn_ary->ary + cnt;
1608 
1609         // only look at character-character interactions
1610         if ( MAX_CHR == d->chra || MAX_CHR == d->chrb ) continue;
1611 
1612         bump_one_mount( d->chra, d->chrb );
1613     }
1614 
1615     return btrue;
1616 }
1617 
1618 //--------------------------------------------------------------------------------------------
bump_all_collisions(CoNode_ary_t * pcn_ary)1619 bool_t bump_all_collisions( CoNode_ary_t * pcn_ary )
1620 {
1621     /// @details BB@> Detect all character-character and character-particle collsions (with exclusions
1622     ///               for the mounts and platforms found in the previous steps)
1623 
1624     int        cnt;
1625 
1626     // blank the accumulators
1627     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
1628     {
1629         fvec3_self_clear( pchr->phys.apos_plat.v );
1630         fvec3_self_clear( pchr->phys.apos_coll.v );
1631         fvec3_self_clear( pchr->phys.avel.v );
1632     }
1633     CHR_END_LOOP();
1634 
1635     PRT_BEGIN_LOOP_ACTIVE( cnt, prt_bdl )
1636     {
1637         fvec3_self_clear( prt_bdl.prt_ptr->phys.apos_plat.v );
1638         fvec3_self_clear( prt_bdl.prt_ptr->phys.apos_coll.v );
1639         fvec3_self_clear( prt_bdl.prt_ptr->phys.avel.v );
1640     }
1641     PRT_END_LOOP();
1642 
1643     // do all interactions
1644     for ( cnt = 0; cnt < pcn_ary->top; cnt++ )
1645     {
1646         bool_t handled = bfalse;
1647 
1648         // use this form of the function call so that we could add more modules or
1649         // rearrange them without needing to change anything
1650         if ( !handled )
1651         {
1652             handled = do_chr_chr_collision( pcn_ary->ary + cnt );
1653         }
1654 
1655         if ( !handled )
1656         {
1657             handled = do_chr_prt_collision( pcn_ary->ary + cnt );
1658         }
1659     }
1660 
1661     // accumulate the accumulators
1662     CHR_BEGIN_LOOP_ACTIVE( ichr, pchr )
1663     {
1664         float tmpx, tmpy, tmpz;
1665         float bump_str;
1666         bool_t position_updated = bfalse;
1667         fvec3_t max_apos;
1668 
1669         fvec3_t tmp_pos = chr_get_pos( pchr );
1670 
1671         bump_str = 1.0f;
1672         if ( INGAME_CHR( pchr->attachedto ) )
1673         {
1674             bump_str = 0.0f;
1675         }
1676 
1677         // do the "integration" of the accumulated accelerations
1678         fvec3_self_sum( pchr->vel.v, pchr->phys.avel.v );
1679 
1680         position_updated = bfalse;
1681 
1682         max_apos = fvec3_add( pchr->phys.apos_plat.v, pchr->phys.apos_coll.v );
1683         max_apos.x = CLIP( max_apos.x, -GRID_FSIZE, GRID_FSIZE );
1684         max_apos.y = CLIP( max_apos.y, -GRID_FSIZE, GRID_FSIZE );
1685         max_apos.z = CLIP( max_apos.z, -GRID_FSIZE, GRID_FSIZE );
1686 
1687         // do the "integration" on the position
1688         if ( ABS( max_apos.x ) > 0.0f )
1689         {
1690             tmpx = tmp_pos.x;
1691             tmp_pos.x += max_apos.x;
1692             if ( EMPTY_BIT_FIELD != chr_test_wall( pchr, tmp_pos.v, NULL ) )
1693             {
1694                 // restore the old values
1695                 tmp_pos.x = tmpx;
1696             }
1697             else
1698             {
1699                 //pchr->vel.x += pchr->phys.apos_coll.x * bump_str;
1700                 position_updated = btrue;
1701             }
1702         }
1703 
1704         if ( ABS( max_apos.y ) > 0.0f )
1705         {
1706             tmpy = tmp_pos.y;
1707             tmp_pos.y += max_apos.y;
1708             if ( EMPTY_BIT_FIELD != chr_test_wall( pchr, tmp_pos.v, NULL ) )
1709             {
1710                 // restore the old values
1711                 tmp_pos.y = tmpy;
1712             }
1713             else
1714             {
1715                 //pchr->vel.y += pchr->phys.apos_coll.y * bump_str;
1716                 position_updated = btrue;
1717             }
1718         }
1719 
1720         if ( ABS( max_apos.z ) > 0.0f )
1721         {
1722             tmpz = tmp_pos.z;
1723             tmp_pos.z += max_apos.z;
1724             if ( tmp_pos.z < pchr->enviro.floor_level )
1725             {
1726                 // restore the old values
1727                 tmp_pos.z = pchr->enviro.floor_level;
1728                 if ( pchr->vel.z < 0 )
1729                 {
1730                     cap_t * pcap = chr_get_pcap( ichr );
1731                     if ( NULL != pcap )
1732                     {
1733                         pchr->vel.z += -( 1.0f + pcap->dampen ) * pchr->vel.z;
1734                     }
1735                 }
1736                 position_updated = btrue;
1737             }
1738             else
1739             {
1740                 //pchr->vel.z += pchr->phys.apos_coll.z * bump_str;
1741                 position_updated = btrue;
1742             }
1743         }
1744 
1745         if ( position_updated )
1746         {
1747             chr_set_pos( pchr, tmp_pos.v );
1748         }
1749     }
1750     CHR_END_LOOP();
1751 
1752     // accumulate the accumulators
1753     PRT_BEGIN_LOOP_ACTIVE( iprt, bdl )
1754     {
1755         float tmpx, tmpy, tmpz;
1756         float bump_str;
1757         bool_t position_updated = bfalse;
1758         fvec3_t max_apos;
1759 
1760         fvec3_t tmp_pos = prt_get_pos( bdl.prt_ptr );
1761 
1762         bump_str = 1.0f;
1763         if ( INGAME_CHR( bdl.prt_ptr->attachedto_ref ) )
1764         {
1765             bump_str = 0.0f;
1766         }
1767 
1768         // do the "integration" of the accumulated accelerations
1769         fvec3_self_sum( bdl.prt_ptr->vel.v, bdl.prt_ptr->phys.avel.v );
1770 
1771         position_updated = bfalse;
1772 
1773         max_apos = fvec3_add( bdl.prt_ptr->phys.apos_plat.v, bdl.prt_ptr->phys.apos_coll.v );
1774         max_apos.x = CLIP( max_apos.x, -GRID_FSIZE, GRID_FSIZE );
1775         max_apos.y = CLIP( max_apos.y, -GRID_FSIZE, GRID_FSIZE );
1776         max_apos.z = CLIP( max_apos.z, -GRID_FSIZE, GRID_FSIZE );
1777 
1778         // do the "integration" on the position
1779         if ( ABS( max_apos.x ) > 0.0f )
1780         {
1781             tmpx = tmp_pos.x;
1782             tmp_pos.x += max_apos.x;
1783             if ( EMPTY_BIT_FIELD != prt_test_wall( bdl.prt_ptr, tmp_pos.v, NULL ) )
1784             {
1785                 // restore the old values
1786                 tmp_pos.x = tmpx;
1787             }
1788             else
1789             {
1790                 //bdl.prt_ptr->vel.x += bdl.prt_ptr->phys.apos_coll.x * bump_str;
1791                 position_updated = btrue;
1792             }
1793         }
1794 
1795         if ( ABS( max_apos.y ) > 0.0f )
1796         {
1797             tmpy = tmp_pos.y;
1798             tmp_pos.y += max_apos.y;
1799             if ( EMPTY_BIT_FIELD != prt_test_wall( bdl.prt_ptr, tmp_pos.v, NULL ) )
1800             {
1801                 // restore the old values
1802                 tmp_pos.y = tmpy;
1803             }
1804             else
1805             {
1806                 //bdl.prt_ptr->vel.y += bdl.prt_ptr->phys.apos_coll.y * bump_str;
1807                 position_updated = btrue;
1808             }
1809         }
1810 
1811         if ( ABS( max_apos.z ) > 0.0f )
1812         {
1813             tmpz = tmp_pos.z;
1814             tmp_pos.z += max_apos.z;
1815             if ( tmp_pos.z < bdl.prt_ptr->enviro.floor_level )
1816             {
1817                 // restore the old values
1818                 tmp_pos.z = bdl.prt_ptr->enviro.floor_level;
1819                 if ( bdl.prt_ptr->vel.z < 0 )
1820                 {
1821                     if ( LOADED_PIP( bdl.prt_ptr->pip_ref ) )
1822                     {
1823                         pip_t * ppip = PipStack.lst + bdl.prt_ptr->pip_ref;
1824                         bdl.prt_ptr->vel.z += -( 1.0f + ppip->dampen ) * bdl.prt_ptr->vel.z;
1825                     }
1826                     else
1827                     {
1828                         bdl.prt_ptr->vel.z += -( 1.0f + 0.5f ) * bdl.prt_ptr->vel.z;
1829                     }
1830                 }
1831                 position_updated = btrue;
1832             }
1833             else
1834             {
1835                 //bdl.prt_ptr->vel.z += bdl.prt_ptr->phys.apos_coll.z * bump_str;
1836                 position_updated = btrue;
1837             }
1838         }
1839 
1840         // Change the direction of the particle
1841         if ( bdl.pip_ptr->rotatetoface )
1842         {
1843             // Turn to face new direction
1844             bdl.prt_ptr->facing = vec_to_facing( bdl.prt_ptr->vel.x , bdl.prt_ptr->vel.y );
1845         }
1846 
1847         if ( position_updated )
1848         {
1849             prt_set_pos( bdl.prt_ptr, tmp_pos.v );
1850         }
1851     }
1852     PRT_END_LOOP();
1853 
1854     // blank the accumulators
1855     CHR_BEGIN_LOOP_ACTIVE( cnt, pchr )
1856     {
1857         fvec3_self_clear( pchr->phys.apos_plat.v );
1858         fvec3_self_clear( pchr->phys.apos_coll.v );
1859         fvec3_self_clear( pchr->phys.avel.v );
1860     }
1861     CHR_END_LOOP();
1862 
1863     PRT_BEGIN_LOOP_ACTIVE( cnt, prt_bdl )
1864     {
1865         fvec3_self_clear( prt_bdl.prt_ptr->phys.apos_plat.v );
1866         fvec3_self_clear( prt_bdl.prt_ptr->phys.apos_coll.v );
1867         fvec3_self_clear( prt_bdl.prt_ptr->phys.avel.v );
1868     }
1869     PRT_END_LOOP();
1870 
1871     return btrue;
1872 }
1873 
1874 //--------------------------------------------------------------------------------------------
bump_one_mount(const CHR_REF ichr_a,const CHR_REF ichr_b)1875 bool_t bump_one_mount( const CHR_REF ichr_a, const CHR_REF ichr_b )
1876 {
1877     fvec3_t vdiff;
1878 
1879     oct_vec_t apos, bpos;
1880 
1881     chr_t * pchr_a, * pchr_b;
1882 
1883     bool_t mount_a, mount_b;
1884 
1885     bool_t mounted = bfalse;
1886     bool_t handled = bfalse;
1887 
1888     // make sure that A is valid
1889     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
1890     pchr_a = ChrList.lst + ichr_a;
1891 
1892     // make sure that B is valid
1893     if ( !INGAME_CHR( ichr_b ) ) return bfalse;
1894     pchr_b = ChrList.lst + ichr_b;
1895 
1896     // find the difference in velocities
1897     vdiff = fvec3_sub( pchr_b->vel.v, pchr_a->vel.v );
1898 
1899     // can either of these objects mount the other?
1900     mount_a = chr_can_mount( ichr_b, ichr_a );
1901     mount_b = chr_can_mount( ichr_a, ichr_b );
1902 
1903     if ( !mount_a && !mount_b ) return bfalse;
1904 
1905     //Ready for position calulations
1906     oct_vec_ctor( apos, chr_get_pos_v( pchr_a ) );
1907     oct_vec_ctor( bpos, chr_get_pos_v( pchr_b ) );
1908 
1909     // assume the worst
1910     mounted = bfalse;
1911 
1912     // mount a on b ?
1913     if ( !mounted && mount_b )
1914     {
1915         oct_bb_t  tmp_cv, saddle_cv;
1916 
1917         //---- find out whether the object is overlapping with the saddle
1918 
1919         // the position of the saddle over the frame
1920         oct_bb_add_fvec3( pchr_b->slot_cv + SLOT_LEFT, chr_get_pos_v( pchr_b ), &tmp_cv );
1921         phys_expand_oct_bb( &tmp_cv, pchr_b->vel.v, 0.0f, 1.0f, &saddle_cv );
1922 
1923         if ( oct_bb_point_inside( &saddle_cv, apos ) )
1924         {
1925             oct_vec_t saddle_pos;
1926             fvec3_t   pdiff;
1927 
1928             oct_bb_get_mids( &saddle_cv, saddle_pos );
1929             pdiff.x = saddle_pos[OCT_X] - apos[OCT_X];
1930             pdiff.y = saddle_pos[OCT_Y] - apos[OCT_Y];
1931             pdiff.z = saddle_pos[OCT_Z] - apos[OCT_Z];
1932 
1933             if ( fvec3_dot_product( pdiff.v, vdiff.v ) >= 0.0f )
1934             {
1935                 // the rider is in a mountable position, don't do any more collisions
1936                 // even if the object is doesn't actually mount
1937                 handled = btrue;
1938 
1939                 if ( rv_success == attach_character_to_mount( ichr_a, ichr_b, GRIP_ONLY ) )
1940                 {
1941                     mounted = INGAME_CHR( pchr_a->attachedto );
1942                 }
1943             }
1944         }
1945     }
1946 
1947     // mount b on a ?
1948     if ( !mounted && mount_a )
1949     {
1950         oct_bb_t  tmp_cv, saddle_cv;
1951 
1952         //---- find out whether the object is overlapping with the saddle
1953 
1954         // the position of the saddle over the frame
1955         oct_bb_add_fvec3( pchr_a->slot_cv + SLOT_LEFT, chr_get_pos_v( pchr_a ), &tmp_cv );
1956         phys_expand_oct_bb( &tmp_cv, pchr_a->vel.v, 0.0f, 1.0f, &saddle_cv );
1957 
1958         if ( oct_bb_point_inside( &saddle_cv, bpos ) )
1959         {
1960             oct_vec_t saddle_pos;
1961             fvec3_t   pdiff;
1962 
1963             oct_bb_get_mids( &saddle_cv, saddle_pos );
1964 
1965             // vdiff is computed as b - a. keep the pdiff in the same sense
1966             pdiff.x = bpos[OCT_X] - saddle_pos[OCT_X];
1967             pdiff.y = bpos[OCT_Y] - saddle_pos[OCT_Y];
1968             pdiff.z = bpos[OCT_Z] - saddle_pos[OCT_Z];
1969 
1970             if ( fvec3_dot_product( pdiff.v, vdiff.v ) >= 0.0f )
1971             {
1972                 // the rider is in a mountable position, don't do any more collisions
1973                 // even if the object is doesn't actually mount
1974                 handled = btrue;
1975 
1976                 if ( rv_success == attach_character_to_mount( ichr_b, ichr_a, GRIP_ONLY ) )
1977                 {
1978                     mounted = INGAME_CHR( pchr_b->attachedto );
1979                 }
1980             }
1981         }
1982     }
1983 
1984     return handled;
1985 }
1986 
1987 //--------------------------------------------------------------------------------------------
do_chr_platform_physics(chr_t * pitem,chr_t * pplat)1988 bool_t do_chr_platform_physics( chr_t * pitem, chr_t * pplat )
1989 {
1990     // we know that ichr_a is a platform and ichr_b is on it
1991     Sint16 rot_a, rot_b;
1992     float lerp_z, vlerp_z;
1993 
1994     if ( !ACTIVE_PCHR( pitem ) ) return bfalse;
1995     if ( !ACTIVE_PCHR( pplat ) ) return bfalse;
1996 
1997     if ( pitem->onwhichplatform_ref != GET_REF_PCHR( pplat ) ) return bfalse;
1998 
1999     // grab the pre-computed zlerp value, and map it to our needs
2000     lerp_z = 1.0f - pitem->enviro.zlerp;
2001 
2002     // if your velocity is going up much faster then the
2003     // platform, there is no need to suck you to the level of the platform
2004     // this was one of the things preventing you from jumping from platforms
2005     // properly
2006     vlerp_z = ABS( pitem->vel.z - pplat->vel.z ) / 5;
2007     vlerp_z  = 1.0f - CLIP( vlerp_z, 0.0f, 1.0f );
2008 
2009     // determine the rotation rates
2010     rot_b = pitem->ori.facing_z - pitem->ori_old.facing_z;
2011     rot_a = pplat->ori.facing_z - pplat->ori_old.facing_z;
2012 
2013     if ( lerp_z == 1.0f )
2014     {
2015         pitem->phys.apos_plat.z += ( pitem->enviro.level - pitem->pos.z ) * 0.125f;
2016         pitem->phys.avel.z += ( pplat->vel.z  - pitem->vel.z ) * 0.25f;
2017         pitem->ori.facing_z      += ( rot_a         - rot_b ) * platstick;
2018     }
2019     else
2020     {
2021         pitem->phys.apos_plat.z += ( pitem->enviro.level - pitem->pos.z ) * 0.125f * lerp_z * vlerp_z;
2022         pitem->phys.avel.z += ( pplat->vel.z  - pitem->vel.z ) * 0.25f * lerp_z * vlerp_z;
2023         pitem->ori.facing_z      += ( rot_a         - rot_b ) * platstick * lerp_z * vlerp_z;
2024     };
2025 
2026     return btrue;
2027 }
2028 
2029 //--------------------------------------------------------------------------------------------
estimate_chr_prt_normal(chr_t * pchr,prt_t * pprt,fvec3_base_t nrm,fvec3_base_t vdiff)2030 float estimate_chr_prt_normal( chr_t * pchr, prt_t * pprt, fvec3_base_t nrm, fvec3_base_t vdiff )
2031 {
2032     fvec3_t collision_size;
2033     float dot;
2034 
2035     collision_size.x = MAX( pchr->chr_max_cv.maxs[OCT_X] - pchr->chr_max_cv.mins[OCT_X], 2.0f * pprt->bump_padded.size );
2036     collision_size.y = MAX( pchr->chr_max_cv.maxs[OCT_Y] - pchr->chr_max_cv.mins[OCT_Y], 2.0f * pprt->bump_padded.size );
2037     collision_size.z = MAX( pchr->chr_max_cv.maxs[OCT_Z] - pchr->chr_max_cv.mins[OCT_Z], 2.0f * pprt->bump_padded.height );
2038 
2039     // estimate the "normal" for the collision, using the center-of-mass difference
2040     nrm[kX] = pprt->pos.x - pchr->pos.x;
2041     nrm[kY] = pprt->pos.y - pchr->pos.y;
2042     nrm[kZ] = pprt->pos.z - ( pchr->pos.z + 0.5f * ( pchr->chr_max_cv.maxs[OCT_Z] + pchr->chr_max_cv.mins[OCT_Z] ) );
2043 
2044     // scale the collision box
2045     nrm[kX] /= collision_size.x;
2046     nrm[kY] /= collision_size.y;
2047     nrm[kZ] /= collision_size.z;
2048 
2049     // scale the normals so that the collision volume will act somewhat like a cylinder
2050     if ( pchr->platform )
2051     {
2052         nrm[kX] *= nrm[kX] * nrm[kX] + nrm[kY] * nrm[kY];
2053         nrm[kY] *= nrm[kX] * nrm[kX] + nrm[kY] * nrm[kY];
2054 
2055         nrm[kZ] *= nrm[kZ] * nrm[kZ];
2056     }
2057 
2058     // reject the reflection request if the particle is moving in the wrong direction
2059     vdiff[kX] = pchr->vel.x - pprt->vel.x;
2060     vdiff[kY] = pchr->vel.y - pprt->vel.y;
2061     vdiff[kZ] = pchr->vel.z - pprt->vel.z;
2062     dot       = fvec3_dot_product( vdiff, nrm );
2063 
2064     // we really never should have the condition that dot > 0, unless the particle is "fast"
2065     if ( dot >= 0.0f )
2066     {
2067         fvec3_t vtmp;
2068 
2069         // If the particle is "fast" relative to the object size, it can happen that the particle
2070         // can be more than halfway through the character before it is detected.
2071 
2072         vtmp.x = vdiff[kX] / collision_size.x;
2073         vtmp.y = vdiff[kY] / collision_size.y;
2074         vtmp.z = vdiff[kZ] / collision_size.z;
2075 
2076         // If it is fast, re-evaluate the normal in a different way
2077         if ( vtmp.x*vtmp.x + vtmp.y*vtmp.y + vtmp.z*vtmp.z > 0.5f*0.5f )
2078         {
2079             // use the old position, which SHOULD be before the collision
2080             // to determine the normal
2081             nrm[kX] = pprt->pos_old.x - pchr->pos_old.x;
2082             nrm[kY] = pprt->pos_old.y - pchr->pos_old.y;
2083             nrm[kZ] = pprt->pos_old.z - ( pchr->pos_old.z + 0.5f * ( pchr->chr_max_cv.maxs[OCT_Z] + pchr->chr_max_cv.mins[OCT_Z] ) );
2084 
2085             // scale the collision box
2086             nrm[kX] /= collision_size.x;
2087             nrm[kY] /= collision_size.y;
2088             nrm[kZ] /= collision_size.z;
2089 
2090             // scale the z-normals so that the collision volume will act somewhat like a cylinder
2091             nrm[kZ] *= nrm[kZ] * nrm[kZ];
2092         }
2093     }
2094 
2095     // assume the function fails
2096     dot = 0.0f;
2097 
2098     // does the normal exist?
2099     if ( ABS( nrm[kX] ) + ABS( nrm[kY] ) + ABS( nrm[kZ] ) > 0.0f )
2100     {
2101         // Make the normal a unit normal
2102         fvec3_t ntmp = fvec3_normalize( nrm );
2103         memcpy( nrm, ntmp.v, sizeof( fvec3_base_t ) );
2104 
2105         // determine the actual dot product
2106         dot = fvec3_dot_product( vdiff, nrm );
2107     }
2108 
2109     return dot;
2110 }
2111 //--------------------------------------------------------------------------------------------
do_chr_chr_collision_pressure_normal(chr_t * pchr_a,chr_t * pchr_b,float exponent,oct_vec_t * podepth,fvec3_base_t nrm,float * tmin)2112 bool_t do_chr_chr_collision_pressure_normal( chr_t * pchr_a, chr_t * pchr_b, float exponent, oct_vec_t * podepth, fvec3_base_t nrm, float * tmin )
2113 {
2114     oct_bb_t otmp_a, otmp_b;
2115 
2116     oct_bb_add_fvec3( &( pchr_a->chr_min_cv ), chr_get_pos_v( pchr_a ), &otmp_a );
2117     oct_bb_add_fvec3( &( pchr_b->chr_min_cv ), chr_get_pos_v( pchr_b ), &otmp_b );
2118 
2119     return phys_estimate_pressure_normal( &otmp_a, &otmp_b, exponent, podepth, nrm, tmin );
2120 }
2121 
2122 //--------------------------------------------------------------------------------------------
do_chr_chr_collision(CoNode_t * d)2123 bool_t do_chr_chr_collision( CoNode_t * d )
2124 {
2125     CHR_REF ichr_a, ichr_b;
2126     chr_t * pchr_a, * pchr_b;
2127     cap_t * pcap_a, * pcap_b;
2128 
2129     float depth_min;
2130     float interaction_strength = 1.0f;
2131 
2132     float wta, wtb;
2133     float recoil_a, recoil_b;
2134 
2135     // object bounding boxes shifted so that they are in the correct place on the map
2136     oct_bb_t map_bb_a, map_bb_b;
2137 
2138     fvec3_t   nrm;
2139     int exponent = 1;
2140 
2141     oct_vec_t odepth;
2142     bool_t    collision = bfalse, bump = bfalse, valid_normal = bfalse;
2143 
2144     if ( NULL == d || MAX_PRT != d->prtb ) return bfalse;
2145     ichr_a = d->chra;
2146     ichr_b = d->chrb;
2147 
2148     // make sure that it is on
2149     if ( !INGAME_CHR( ichr_a ) ) return bfalse;
2150     pchr_a = ChrList.lst + ichr_a;
2151 
2152     pcap_a = chr_get_pcap( ichr_a );
2153     if ( NULL == pcap_a ) return bfalse;
2154 
2155     // make sure that it is on
2156     if ( !INGAME_CHR( ichr_b ) ) return bfalse;
2157     pchr_b = ChrList.lst + ichr_b;
2158 
2159     pcap_b = chr_get_pcap( ichr_b );
2160     if ( NULL == pcap_b ) return bfalse;
2161 
2162     //skip objects that are inside inventories
2163     if ( pchr_a->pack.is_packed || pchr_b->pack.is_packed ) return bfalse;
2164 
2165     // skip all objects that are mounted or attached to something
2166     if ( INGAME_CHR( pchr_a->attachedto ) || INGAME_CHR( pchr_b->attachedto ) ) return bfalse;
2167 
2168     // platform interaction. if the onwhichplatform_ref is set, then
2169     // all collision tests have been met
2170     if ( ichr_a == pchr_b->onwhichplatform_ref )
2171     {
2172         if ( do_chr_platform_physics( pchr_b, pchr_a ) )
2173         {
2174             // this is handled
2175             return btrue;
2176         }
2177     }
2178 
2179     // platform interaction. if the onwhichplatform_ref is set, then
2180     // all collision tests have been met
2181     if ( ichr_b == pchr_a->onwhichplatform_ref )
2182     {
2183         if ( do_chr_platform_physics( pchr_a, pchr_b ) )
2184         {
2185             // this is handled
2186             return btrue;
2187         }
2188     }
2189 
2190     // items can interact with platforms but not with other characters/objects
2191     if ( pchr_a->isitem || pchr_b->isitem ) return bfalse;
2192 
2193     // don't interact with your mount, or your held items
2194     if ( ichr_a == pchr_b->attachedto || ichr_b == pchr_a->attachedto ) return bfalse;
2195 
2196     // don't do anything if there is no interaction strength
2197     if ( 0.0f == pchr_a->bump_stt.size || 0.0f == pchr_b->bump_stt.size ) return bfalse;
2198 
2199     interaction_strength = 1.0f;
2200     interaction_strength *= pchr_a->inst.alpha * INV_FF;
2201     interaction_strength *= pchr_b->inst.alpha * INV_FF;
2202 
2203     // reduce your interaction strength if you have just detached from an object
2204     if ( pchr_a->dismount_object == ichr_b )
2205     {
2206         float dismount_lerp = ( float )pchr_a->dismount_timer / ( float )PHYS_DISMOUNT_TIME;
2207         dismount_lerp = CLIP( dismount_lerp, 0.0f, 1.0f );
2208 
2209         interaction_strength *= dismount_lerp;
2210     }
2211 
2212     if ( pchr_b->dismount_object == ichr_a )
2213     {
2214         float dismount_lerp = ( float )pchr_b->dismount_timer / ( float )PHYS_DISMOUNT_TIME;
2215         dismount_lerp = CLIP( dismount_lerp, 0.0f, 1.0f );
2216 
2217         interaction_strength *= dismount_lerp;
2218     }
2219 
2220     // seriously reduce the interaction_strength with mounts
2221     // this thould allow characters to mount certain mounts a lot easier
2222     if (( pchr_a->ismount && MAX_CHR == pchr_a->holdingwhich[SLOT_LEFT] && !pchr_b->ismount ) ||
2223         ( pchr_b->ismount && MAX_CHR == pchr_b->holdingwhich[SLOT_LEFT] && !pchr_a->ismount ) )
2224     {
2225         interaction_strength *= 0.25;
2226     }
2227 
2228     // reduce the interaction strength with platforms
2229     // that are overlapping with the platform you are actually on
2230     if ( pchr_b->canuseplatforms && pchr_a->platform && MAX_CHR != pchr_b->onwhichplatform_ref && ichr_a != pchr_b->onwhichplatform_ref )
2231     {
2232         float lerp_z = ( pchr_b->pos.z - ( pchr_a->pos.z + pchr_a->chr_min_cv.maxs[OCT_Z] ) ) / PLATTOLERANCE;
2233         lerp_z = CLIP( lerp_z, -1.0f, 1.0f );
2234 
2235         if ( lerp_z >= 0.0f )
2236         {
2237             interaction_strength = 0.0f;
2238         }
2239         else
2240         {
2241             interaction_strength *= -lerp_z;
2242         }
2243     }
2244 
2245     if ( pchr_a->canuseplatforms && pchr_b->platform && MAX_CHR != pchr_a->onwhichplatform_ref && ichr_b != pchr_a->onwhichplatform_ref )
2246     {
2247         float lerp_z = ( pchr_a->pos.z - ( pchr_b->pos.z + pchr_b->chr_min_cv.maxs[OCT_Z] ) ) / PLATTOLERANCE;
2248         lerp_z = CLIP( lerp_z, -1, 1 );
2249 
2250         if ( lerp_z >= 0.0f )
2251         {
2252             interaction_strength = 0.0f;
2253         }
2254         else
2255         {
2256             interaction_strength *= -lerp_z;
2257         }
2258     }
2259 
2260     // shift the character bounding boxes to be centered on their positions
2261     oct_bb_add_fvec3( &( pchr_a->chr_min_cv ), chr_get_pos_v( pchr_a ), &map_bb_a );
2262     oct_bb_add_fvec3( &( pchr_b->chr_min_cv ), chr_get_pos_v( pchr_b ), &map_bb_b );
2263 
2264     // make the object more like a table if there is a platform-like interaction
2265     exponent = 1.0f;
2266     if ( pchr_a->canuseplatforms && pchr_b->platform ) exponent += 2;
2267     if ( pchr_b->canuseplatforms && pchr_a->platform ) exponent += 2;
2268 
2269     // use the info from the collision volume to determine whether the objects are colliding
2270     collision = ( d->tmin > 0.0f );
2271 
2272     // estimate the collision normal at the point of contact
2273     valid_normal = bfalse;
2274     depth_min    = 0.0f;
2275     if ( collision )
2276     {
2277         // find the collision volumes at 10% overlap
2278         oct_bb_t exp1, exp2;
2279 
2280         float tmp_min, tmp_max;
2281 
2282         tmp_min = d->tmin;
2283         tmp_max = d->tmin + ( d->tmax - d->tmin ) * 0.1f;
2284 
2285         // determine the expanded collision volumes for both objects
2286         phys_expand_oct_bb( &map_bb_a, pchr_a->vel.v, tmp_min, tmp_max, &exp1 );
2287         phys_expand_oct_bb( &map_bb_b, pchr_b->vel.v, tmp_min, tmp_max, &exp2 );
2288 
2289         valid_normal = phys_estimate_collision_normal( &exp1, &exp2, exponent, &odepth, nrm.v, &depth_min );
2290     }
2291 
2292     if ( !collision || depth_min <= 0.0f )
2293     {
2294         valid_normal = phys_estimate_pressure_normal( &map_bb_a, &map_bb_b, exponent, &odepth, nrm.v, &depth_min );
2295     }
2296 
2297     if ( depth_min <= 0.0f )
2298         return bfalse;
2299 
2300     // if we can't obtain a valid collision normal, we fail
2301     if ( !valid_normal ) return bfalse;
2302 
2303     //------------------
2304     // do character-character interactions
2305 
2306     // calculate a "mass" for each object, taking into account possible infinite masses
2307     get_chr_mass( pchr_a, &wta );
2308     get_chr_mass( pchr_b, &wtb );
2309 
2310     // make a special exception for interaction between "Mario platforms"
2311     if (( wta < 0.0f && pchr_a->platform ) && ( wtb < 0.0f && pchr_a->platform ) )
2312     {
2313         return bfalse;
2314     }
2315 
2316     // make a special exception for immovable scenery objects
2317     // they can collide, but cannot push each other apart... that might mess up the scenery ;)
2318     if ( !collision && ( wta < 0.0f && 0.0f == pchr_a->maxaccel ) && ( wtb < 0.0f && 0.0f == pchr_b->maxaccel ) )
2319     {
2320         return bfalse;
2321     }
2322 
2323     // determine the relative effect of impulses, given the known weights
2324     get_recoil_factors( wta, wtb, &recoil_a, &recoil_b );
2325 
2326     //---- calculate the character-character interactions
2327     {
2328         const float max_pressure_strength = 0.125f;
2329         const float pressure_strength     = max_pressure_strength * interaction_strength;
2330 
2331         fvec3_t   pdiff_a;
2332 
2333         bool_t need_displacement = bfalse;
2334         bool_t need_velocity = bfalse;
2335 
2336         fvec3_t   vdiff_a;
2337 
2338         if ( depth_min <= 0.0f || collision )
2339         {
2340             need_displacement = bfalse;
2341             fvec3_self_clear( pdiff_a.v );
2342         }
2343         else
2344         {
2345             // add a small amount to the pressure difference so that
2346             // the function will actually separate the objects in a finite number
2347             // of iterations
2348             need_displacement = ( recoil_a > 0.0f ) || ( recoil_b > 0.0f );
2349             pdiff_a = fvec3_scale( nrm.v, depth_min + 1.0f );
2350         }
2351 
2352         // find the relative velocity
2353         vdiff_a = fvec3_sub( pchr_b->vel.v, pchr_a->vel.v );
2354 
2355         need_velocity = bfalse;
2356         if ( fvec3_length_abs( vdiff_a.v ) > 1e-6 )
2357         {
2358             need_velocity = ( recoil_a > 0.0f ) || ( recoil_b > 0.0f );
2359         }
2360 
2361         //---- handle the relative velocity
2362         if ( need_velocity )
2363         {
2364 
2365             // what type of interaction is this? (collision or pressure)
2366             if ( collision )
2367             {
2368                 // !!!! COLLISION !!!!
2369 
2370                 // an actual bump, use impulse to make the objects bounce appart
2371 
2372                 fvec3_t vdiff_para_a, vdiff_perp_a;
2373 
2374                 // generic coefficient of restitution.
2375                 float cr = pchr_a->phys.dampen * pchr_b->phys.dampen;
2376 
2377                 // decompose this relative to the collision normal
2378                 fvec3_decompose( vdiff_a.v, nrm.v, vdiff_perp_a.v, vdiff_para_a.v );
2379 
2380                 if ( recoil_a > 0.0f )
2381                 {
2382                     fvec3_t vimp_a;
2383 
2384                     vimp_a = fvec3_scale( vdiff_perp_a.v, recoil_a * ( 1.0f + cr ) * interaction_strength );
2385 
2386                     fvec3_self_sum( pchr_a->phys.avel.v, vimp_a.v );
2387                 }
2388 
2389                 if ( recoil_b > 0.0f )
2390                 {
2391                     fvec3_t vimp_b;
2392 
2393                     vimp_b = fvec3_scale( vdiff_perp_a.v, -recoil_b * ( 1.0f + cr ) * interaction_strength );
2394 
2395                     fvec3_self_sum( pchr_b->phys.avel.v, vimp_b.v );
2396                 }
2397 
2398                 // this was definitely a bump
2399                 bump = btrue;
2400             }
2401             // ignore the case of both objects having infinite mass
2402             // this is normally due to two scenery objects being too close to each other
2403             else
2404             {
2405                 // !!!! PRESSURE !!!!
2406 
2407                 // not a bump at all. two objects are rubbing against one another
2408                 // and continually overlapping.
2409                 //
2410                 // reduce the relative velocity if the objects are moving towards each other,
2411                 // but ignore it if they are moving away.
2412 
2413                 // use pressure to push them appart. reduce their relative velocities.
2414 
2415                 float     vdot;
2416 
2417                 // are the objects moving towards each other, or appart?
2418                 vdot = fvec3_dot_product( vdiff_a.v, nrm.v );
2419 
2420                 if ( vdot < 0.0f )
2421                 {
2422                     if ( recoil_a > 0.0f )
2423                     {
2424                         fvec3_t vimp_a;
2425 
2426                         vimp_a = fvec3_scale( vdiff_a.v, recoil_a * pressure_strength );
2427 
2428                         fvec3_self_sum( pchr_a->phys.avel.v,      vimp_a.v );
2429                     }
2430 
2431                     if ( recoil_b > 0.0f )
2432                     {
2433                         fvec3_t vimp_b;
2434 
2435                         vimp_b = fvec3_scale( vdiff_a.v, -recoil_b * pressure_strength );
2436 
2437                         fvec3_self_sum( pchr_b->phys.avel.v,      vimp_b.v );
2438                     }
2439                 }
2440 
2441                 // you could "bump" something if you changed your velocity, even if you were still touching
2442                 bump = ( fvec3_dot_product( pchr_a->vel.v, nrm.v ) * fvec3_dot_product( pchr_a->vel_old.v, nrm.v ) < 0 ) ||
2443                        ( fvec3_dot_product( pchr_b->vel.v, nrm.v ) * fvec3_dot_product( pchr_b->vel_old.v, nrm.v ) < 0 );
2444             }
2445 
2446         }
2447 
2448         //---- fix the displacement regardless of what kind of interaction
2449         if ( need_displacement )
2450         {
2451             if ( recoil_a > 0.0f )
2452             {
2453                 fvec3_t pimp_a;
2454 
2455                 pimp_a = fvec3_scale( pdiff_a.v, recoil_a * pressure_strength );
2456 
2457                 fvec3_self_sum( pchr_a->phys.apos_coll.v, pimp_a.v );
2458             }
2459 
2460             if ( recoil_b > 0.0f )
2461             {
2462                 fvec3_t pimp_b;
2463 
2464                 pimp_b = fvec3_scale( pdiff_a.v,  -recoil_b * pressure_strength );
2465 
2466                 fvec3_self_sum( pchr_b->phys.apos_coll.v, pimp_b.v );
2467             }
2468         }
2469 
2470         //// add in the friction due to the "collision"
2471         //// assume coeff of friction of 0.5
2472         //if ( ABS( vimp_a.x ) + ABS( vimp_a.y ) + ABS( vimp_a.z ) > 0.0f &&
2473         //     ABS( vpara_a.x ) + ABS( vpara_a.y ) + ABS( vpara_a.z ) > 0.0f &&
2474         //     pchr_a->dismount_timer <= 0 )
2475         //{
2476         //    float imp, vel, factor;
2477 
2478         //    imp = 0.5f * SQRT( vimp_a.x * vimp_a.x + vimp_a.y * vimp_a.y + vimp_a.z * vimp_a.z );
2479         //    vel = SQRT( vpara_a.x * vpara_a.x + vpara_a.y * vpara_a.y + vpara_a.z * vpara_a.z );
2480 
2481         //    factor = imp / vel;
2482         //    factor = CLIP( factor, 0.0f, 1.0f );
2483 
2484         //    pchr_a->phys.avel.x -= factor * vpara_a.x * interaction_strength;
2485         //    pchr_a->phys.avel.y -= factor * vpara_a.y * interaction_strength;
2486         //    pchr_a->phys.avel.z -= factor * vpara_a.z * interaction_strength;
2487         //    LOG_NAN( pchr_a->phys.avel.z );
2488         //}
2489 
2490         //if ( ABS( vimp_b.x ) + ABS( vimp_b.y ) + ABS( vimp_b.z ) > 0.0f &&
2491         //     ABS( vpara_b.x ) + ABS( vpara_b.y ) + ABS( vpara_b.z ) > 0.0f &&
2492         //     pchr_b->dismount_timer <= 0 )
2493         //{
2494         //    float imp, vel, factor;
2495 
2496         //    imp = 0.5f * SQRT( vimp_b.x * vimp_b.x + vimp_b.y * vimp_b.y + vimp_b.z * vimp_b.z );
2497         //    vel = SQRT( vpara_b.x * vpara_b.x + vpara_b.y * vpara_b.y + vpara_b.z * vpara_b.z );
2498 
2499         //    factor = imp / vel;
2500         //    factor = CLIP( factor, 0.0f, 1.0f );
2501 
2502         //    pchr_b->phys.avel.x -= factor * vpara_b.x * interaction_strength;
2503         //    pchr_b->phys.avel.y -= factor * vpara_b.y * interaction_strength;
2504         //    pchr_b->phys.avel.z -= factor * vpara_b.z * interaction_strength;
2505         //    LOG_NAN( pchr_b->phys.avel.z );
2506         //}
2507     }
2508 
2509     if ( bump )
2510     {
2511         ai_state_set_bumplast( &( pchr_a->ai ), ichr_b );
2512         ai_state_set_bumplast( &( pchr_b->ai ), ichr_a );
2513     }
2514 
2515     return btrue;
2516 }
2517 
2518 //--------------------------------------------------------------------------------------------
2519 //--------------------------------------------------------------------------------------------
2520 //bool_t do_chr_prt_collision_get_depth_base( CoNode_t * d, oct_bb_t * pcv_a, fvec3_base_t vel_a, oct_bb_t * pcv_b, fvec3_base_t vel_b, float exponent, fvec3_base_t nrm, float *depth )
2521 //{
2522 //    oct_vec_t odepth;
2523 //
2524 //    if ( NULL == nrm || NULL == depth ) return bfalse;
2525 //
2526 //    if ( NULL == pcv_a || NULL == pcv_b ) return bfalse;
2527 //
2528 //    if ( d->tmin <= 0.0f || ABS( d->tmin ) > 1e6 || ABS( d->tmax ) > 1e6 )
2529 //    {
2530 //        // the objects are in contact for an extrodinary amount of time
2531 //        // just use the "shortest way out" to find the interaction normal
2532 //
2533 //        phys_estimate_pressure_normal( pcv_a, pcv_b, exponent, &odepth, nrm, depth );
2534 //    }
2535 //    else
2536 //    {
2537 //        oct_bb_t exp1, exp2;
2538 //
2539 //        float tmp_min, tmp_max;
2540 //
2541 //        tmp_min = d->tmin;
2542 //        tmp_max = d->tmin + ( d->tmax - d->tmin ) * 0.1f;
2543 //
2544 //        // determine the expanded collision volumes for both objects
2545 //        phys_expand_oct_bb( pcv_a, vel_a, tmp_min, tmp_max, &exp1 );
2546 //        phys_expand_oct_bb( pcv_b, vel_b, tmp_min, tmp_max, &exp2 );
2547 //
2548 //        phys_estimate_collision_normal( &exp1, &exp2, exponent, &odepth, nrm, depth );
2549 //
2550 //    }
2551 //
2552 //    return *depth > 0.0f;
2553 //}
2554 
2555 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_get_details(CoNode_t * d,chr_prt_collsion_data_t * pdata)2556 bool_t do_chr_prt_collision_get_details( CoNode_t * d, chr_prt_collsion_data_t * pdata )
2557 {
2558     // Get details about the character-particle interaction
2559     //
2560     // We already know that the largest particle cv intersects with the a
2561     // character cv sometime this frame. We need more details to know
2562     // how to handle the collision.
2563 
2564     bool_t handled;
2565 
2566     float exponent;
2567     oct_bb_t cv_chr, cv_prt_max, cv_prt_min;
2568     oct_vec_t odepth;
2569 
2570     if ( NULL == d || NULL == pdata ) return bfalse;
2571 
2572     // make the object more like a table if there is a platform-like interaction
2573     exponent = 1;
2574     if ( SPRITE_SOLID == pdata->pprt->type && pdata->pchr->platform ) exponent += 2;
2575 
2576     // assume the simplest interaction normal
2577     pdata->nrm.x = pdata->nrm.y = 0.0f;
2578     pdata->nrm.z = 1.0f;
2579 
2580     // no valid interactions, yet
2581     handled = bfalse;
2582 
2583     // shift the source bounding boxes to be centered on the given positions
2584     oct_bb_add_fvec3( &( pdata->pchr->chr_min_cv ), chr_get_pos_v( pdata->pchr ), &cv_chr );
2585 
2586     // the smallest particle collision volume
2587     oct_bb_add_fvec3( &( pdata->pprt->prt_min_cv ), prt_get_pos_v( pdata->pprt ), &cv_prt_min );
2588 
2589     // the largest particle collision volume (the hit-box)
2590     oct_bb_add_fvec3( &( pdata->pprt->prt_max_cv ), prt_get_pos_v( pdata->pprt ), &cv_prt_max );
2591 
2592     if ( d->tmin <= 0.0f || ABS( d->tmin ) > 1e6 || ABS( d->tmax ) > 1e6 )
2593     {
2594         // use "pressure" to determine the normal and overlap
2595         phys_estimate_pressure_normal( &cv_prt_min, &cv_chr, exponent, &odepth, pdata->nrm.v, &( pdata->depth_min ) );
2596 
2597         handled = btrue;
2598         if ( d->tmin <= 0.0f )
2599         {
2600             handled = pdata->depth_min > 0.0f;
2601         }
2602 
2603         // tag the type of interaction
2604         pdata->int_min = handled;
2605         pdata->is_pressure = handled;
2606     }
2607     else
2608     {
2609         // find the collision volumes at 10% overlap
2610         oct_bb_t exp1, exp2;
2611 
2612         float tmp_min, tmp_max;
2613 
2614         tmp_min = d->tmin;
2615         tmp_max = d->tmin + ( d->tmax - d->tmin ) * 0.1f;
2616 
2617         // determine the expanded collision volumes for both objects
2618         phys_expand_oct_bb( &cv_prt_min, pdata->pprt->vel.v, tmp_min, tmp_max, &exp1 );
2619         phys_expand_oct_bb( &cv_chr,     pdata->pchr->vel.v, tmp_min, tmp_max, &exp2 );
2620 
2621         // use "collision" to determine the normal and overlap
2622         handled = phys_estimate_collision_normal( &exp1, &exp2, exponent, &odepth, pdata->nrm.v, &( pdata->depth_min ) );
2623 
2624         // tag the type of interaction
2625         pdata->int_min      = handled;
2626         pdata->is_collision = handled;
2627     }
2628 
2629     if ( !handled )
2630     {
2631         if ( d->tmin <= 0.0f || ABS( d->tmin ) > 1e6 || ABS( d->tmax ) > 1e6 )
2632         {
2633             // use "pressure" to determine the normal and overlap
2634             phys_estimate_pressure_normal( &cv_prt_max, &cv_chr, exponent, &odepth, pdata->nrm.v, &( pdata->depth_max ) );
2635 
2636             handled = btrue;
2637             if ( d->tmin <= 0.0f )
2638             {
2639                 handled = pdata->depth_max > 0.0f;
2640             }
2641 
2642             // tag the type of interaction
2643             pdata->int_max     = handled;
2644             pdata->is_pressure = handled;
2645         }
2646         else
2647         {
2648             // find the collision volumes at 10% overlap
2649             oct_bb_t exp1, exp2;
2650 
2651             float tmp_min, tmp_max;
2652 
2653             tmp_min = d->tmin;
2654             tmp_max = d->tmin + ( d->tmax - d->tmin ) * 0.1f;
2655 
2656             // determine the expanded collision volumes for both objects
2657             phys_expand_oct_bb( &cv_prt_max, pdata->pprt->vel.v, tmp_min, tmp_max, &exp1 );
2658             phys_expand_oct_bb( &cv_chr,     pdata->pchr->vel.v, tmp_min, tmp_max, &exp2 );
2659 
2660             // use "collision" to determine the normal and overlap
2661             handled = phys_estimate_collision_normal( &exp1, &exp2, exponent, &odepth, pdata->nrm.v, &( pdata->depth_max ) );
2662 
2663             // tag the type of interaction
2664             pdata->int_max      = handled;
2665             pdata->is_collision = handled;
2666         }
2667     }
2668 
2669     // check for possible platform interactions
2670     //if( pdata->pchr->platform && !INGAME_CHR( pdata->pprt->attachedto_ref ) )
2671     //{
2672     //    // find the interaction strength
2673     //    pdata->plat_lerp = (cv_prt_min.mins[OCT_Z] - cv_chr.maxs[OCT_Z]) / PLATTOLERANCE;
2674     //    pdata->plat_lerp = CLIP( pdata->plat_lerp, -1.0f, 1.0f );
2675 
2676     //    if( pdata->plat_lerp < 1.0f )
2677     //    {
2678     //        bool_t   plat_retval;
2679     //        oct_bb_t plat_cv;
2680 
2681     //        // construct a special collision volume for the platform
2682     //        oct_bb_copy( &plat_cv, &cv_chr );
2683     //        plat_cv.maxs[OCT_Z] += PLATTOLERANCE;
2684 
2685     //        // is there any overlap?
2686     //        plat_retval = get_depth_close_2( &cv_prt_min, &plat_cv, btrue, odepth );
2687 
2688     //        // tag it as a platform interaction
2689     //        pdata->int_plat = plat_retval;
2690 
2691     //        if( !handled && plat_retval )
2692     //        {
2693     //            handled = pdata->int_plat;
2694 
2695     //            // if there is overlap, calculate a normal
2696     //            chr_getMatUp( pdata->pchr, pdata->nrm.v );
2697     //            fvec3_self_normalize( pdata->nrm.v );
2698     //        }
2699     //    }
2700     //}
2701 
2702     return handled;
2703 }
2704 
2705 //--------------------------------------------------------------------------------------------
do_prt_platform_physics(chr_prt_collsion_data_t * pdata)2706 bool_t do_prt_platform_physics( chr_prt_collsion_data_t * pdata )
2707 {
2708     /// @details BB@> handle the particle interaction with a platform it is not attached "on".
2709     ///               @note gravity is not handled here
2710 
2711     bool_t plat_collision = bfalse;
2712     bool_t z_collide, was_z_collide;
2713 
2714     if ( NULL == pdata ) return bfalse;
2715 
2716     // is the platform a platform?
2717     if ( !pdata->pchr->platform ) return bfalse;
2718 
2719     // can the particle interact with it?
2720     if ( INGAME_CHR( pdata->pprt->attachedto_ref ) ) return bfalse;
2721 
2722     // this is handled elsewhere
2723     if ( GET_REF_PCHR( pdata->pchr ) == pdata->pprt->onwhichplatform_ref ) return bfalse;
2724 
2725     // Test to see whether the particle is in the right position to interact with the platform.
2726     // You have to be closer to a platform to interact with it then for a general object,
2727     // but the vertical distance is looser.
2728     plat_collision = test_interaction_close_1( &( pdata->pchr->chr_max_cv ), chr_get_pos_v( pdata->pchr ), pdata->pprt->bump_padded, prt_get_pos_v( pdata->pprt ), btrue );
2729 
2730     if ( !plat_collision ) return bfalse;
2731 
2732     // the only way to get to this point is if the two objects don't collide
2733     // but they are within the PLATTOLERANCE of each other in the z direction
2734     // it is a valid platform. now figure out the physics
2735 
2736     // are they colliding for the first time?
2737     z_collide     = ( pdata->pprt->pos.z < pdata->pchr->pos.z + pdata->pchr->chr_max_cv.maxs[OCT_Z] ) && ( pdata->pprt->pos.z > pdata->pchr->pos.z + pdata->pchr->chr_max_cv.mins[OCT_Z] );
2738     was_z_collide = ( pdata->pprt->pos.z - pdata->pprt->vel.z < pdata->pchr->pos.z + pdata->pchr->chr_max_cv.maxs[OCT_Z] - pdata->pchr->vel.z ) && ( pdata->pprt->pos.z - pdata->pprt->vel.z  > pdata->pchr->pos.z + pdata->pchr->chr_max_cv.mins[OCT_Z] );
2739 
2740     if ( z_collide && !was_z_collide )
2741     {
2742         // Particle is falling onto the platform
2743         pdata->pprt->phys.apos_plat.z += pdata->pchr->pos.z + pdata->pchr->chr_max_cv.maxs[OCT_Z] - pdata->pprt->pos.z;
2744         pdata->pprt->phys.avel.z      += ( pdata->pchr->pos.z - pdata->pprt->vel.z ) * ( 1.0f + pdata->ppip->dampen );
2745 
2746         // This should prevent raindrops from stacking up on the top of trees and other
2747         // objects
2748         if ( pdata->ppip->end_ground && pdata->pchr->platform )
2749         {
2750             pdata->terminate_particle = btrue;
2751         }
2752 
2753         plat_collision = btrue;
2754     }
2755     else if ( z_collide && was_z_collide )
2756     {
2757         // colliding this time and last time. particle is *embedded* in the platform
2758         pdata->pprt->phys.apos_plat.z += pdata->pchr->pos.z + pdata->pchr->chr_max_cv.maxs[OCT_Z] - pdata->pprt->pos.z;
2759 
2760         if ( pdata->pprt->vel.z - pdata->pchr->vel.z < 0 )
2761         {
2762             pdata->pprt->phys.avel.z += pdata->pchr->vel.z * pdata->ppip->dampen + platstick * pdata->pchr->vel.z - pdata->pprt->vel.z;
2763         }
2764         else
2765         {
2766             pdata->pprt->phys.avel.z += pdata->pprt->vel.z * ( 1.0f - platstick ) + pdata->pchr->vel.z * platstick - pdata->pprt->vel.z;
2767         }
2768         pdata->pprt->phys.avel.x += pdata->pprt->vel.x * ( 1.0f - platstick ) + pdata->pchr->vel.x * platstick - pdata->pprt->vel.x;
2769         pdata->pprt->phys.avel.y += pdata->pprt->vel.y * ( 1.0f - platstick ) + pdata->pchr->vel.y * platstick - pdata->pprt->vel.y;
2770 
2771         plat_collision = btrue;
2772     }
2773     else
2774     {
2775         // not colliding this time or last time. particle is just near the platform
2776         float lerp_z = ( pdata->pprt->pos.z - ( pdata->pchr->pos.z + pdata->pchr->chr_max_cv.maxs[OCT_Z] ) ) / PLATTOLERANCE;
2777         lerp_z = CLIP( lerp_z, -1, 1 );
2778 
2779         if ( lerp_z > 0.0f )
2780         {
2781             pdata->pprt->phys.avel.x += ( pdata->pchr->vel.x - pdata->pprt->vel.x ) * platstick * lerp_z;
2782             pdata->pprt->phys.avel.y += ( pdata->pchr->vel.y - pdata->pprt->vel.y ) * platstick * lerp_z;
2783             pdata->pprt->phys.avel.z += ( pdata->pchr->vel.z - pdata->pprt->vel.z ) * platstick * lerp_z;
2784 
2785             plat_collision = btrue;
2786         }
2787     }
2788 
2789     return plat_collision;
2790 }
2791 
2792 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_deflect(chr_prt_collsion_data_t * pdata)2793 bool_t do_chr_prt_collision_deflect( chr_prt_collsion_data_t * pdata )
2794 {
2795     bool_t prt_deflected = bfalse;
2796 
2797     bool_t chr_is_invictus, chr_can_deflect;
2798     bool_t prt_wants_deflection;
2799     FACING_T direction;
2800 
2801     if ( NULL == pdata ) return bfalse;
2802 
2803     /// @note ZF@> Simply ignore characters with invictus for now, it causes some strange effects
2804     if ( pdata->pchr->invictus ) return btrue;
2805 
2806     // find the "attack direction" of the particle
2807     direction = vec_to_facing( pdata->pchr->pos.x - pdata->pprt->pos.x, pdata->pchr->pos.y - pdata->pprt->pos.y );
2808     direction = pdata->pchr->ori.facing_z - direction + ATK_BEHIND;
2809 
2810     // shield block?
2811     chr_is_invictus = is_invictus_direction( direction, GET_REF_PCHR( pdata->pchr ), pdata->ppip->damfx );
2812 
2813     // determine whether the character is magically protected from missile attacks
2814     prt_wants_deflection  = ( MISSILE_NORMAL != pdata->pchr->missiletreatment ) &&
2815                             ( pdata->pprt->owner_ref != GET_REF_PCHR( pdata->pchr ) ) && !pdata->ppip->bump_money;
2816 
2817     chr_can_deflect = ( 0 != pdata->pchr->damage_timer ) && ( pdata->max_damage > 0 );
2818 
2819     // try to deflect the particle
2820     prt_deflected = bfalse;
2821     pdata->mana_paid = bfalse;
2822     if ( chr_is_invictus || ( prt_wants_deflection && chr_can_deflect ) )
2823     {
2824         //Initialize for the billboard
2825         const float lifetime = 3;
2826         SDL_Color text_color = {0xFF, 0xFF, 0xFF, 0xFF};
2827         GLXvector4f tint  = { 0.0f, 0.75f, 1.00f, 1.00f };
2828 
2829         // magically deflect the particle or make a ricochet if the character is invictus
2830         int treatment;
2831 
2832         treatment     = MISSILE_DEFLECT;
2833         prt_deflected = btrue;
2834         if ( prt_wants_deflection )
2835         {
2836             treatment = pdata->pchr->missiletreatment;
2837             pdata->mana_paid = cost_mana( pdata->pchr->missilehandler, pdata->pchr->missilecost << 8, pdata->pprt->owner_ref );
2838             prt_deflected = pdata->mana_paid;
2839         }
2840 
2841         if ( prt_deflected )
2842         {
2843             // Treat the missile
2844             if ( treatment == MISSILE_DEFLECT )
2845             {
2846                 // Deflect the incoming ray off the normal
2847                 pdata->vimpulse.x -= 2.0f * pdata->vdiff_para.x;
2848                 pdata->vimpulse.y -= 2.0f * pdata->vdiff_para.y;
2849                 pdata->vimpulse.z -= 2.0f * pdata->vdiff_para.z;
2850 
2851                 // the ricochet is not guided
2852                 pdata->ppip->homing     = bfalse;
2853             }
2854             else if ( treatment == MISSILE_REFLECT )
2855             {
2856                 // Reflect it back in the direction it came
2857                 pdata->vimpulse.x -= 2.0f * pdata->vdiff.x;
2858                 pdata->vimpulse.y -= 2.0f * pdata->vdiff.y;
2859                 pdata->vimpulse.z -= 2.0f * pdata->vdiff.z;
2860 
2861                 // Change the owner of the missile
2862                 pdata->pprt->team       = pdata->pchr->team;
2863                 pdata->pprt->owner_ref  = GET_REF_PCHR( pdata->pchr );
2864             }
2865 
2866             //Blocked!
2867             spawn_defense_ping( pdata->pchr, pdata->pprt->owner_ref );
2868             chr_make_text_billboard( GET_REF_PCHR( pdata->pchr ), "Blocked!", text_color, tint, lifetime, bb_opt_all );
2869 
2870             //If the attack was blocked by a shield, then check if the block caused a knockback
2871             if ( chr_is_invictus && ACTION_IS_TYPE( pdata->pchr->inst.action_which, P ) )
2872             {
2873                 bool_t using_shield;
2874                 CHR_REF item;
2875 
2876                 // Figure out if we are really using a shield or if it is just a invictus frame
2877                 using_shield = bfalse;
2878                 item         = MAX_CHR;
2879 
2880                 // Check right hand for a shield
2881                 if ( !using_shield )
2882                 {
2883                     item = pdata->pchr->holdingwhich[SLOT_RIGHT];
2884                     if ( INGAME_CHR( item ) && pdata->pchr->ai.lastitemused == item )
2885                     {
2886                         using_shield = btrue;
2887                     }
2888                 }
2889 
2890                 // Check left hand for a shield
2891                 if ( !using_shield )
2892                 {
2893                     item = pdata->pchr->holdingwhich[SLOT_LEFT];
2894                     if ( INGAME_CHR( item ) && pdata->pchr->ai.lastitemused == item )
2895                     {
2896                         using_shield = btrue;
2897                     }
2898                 }
2899 
2900                 // Now we have the block rating and know the enemy
2901                 if ( INGAME_CHR( pdata->pprt->owner_ref ) && using_shield )
2902                 {
2903                     int   total_block_rating;
2904                     IPair rand_pair;
2905 
2906                     chr_t *pshield   = ChrList.lst + item;
2907                     chr_t *pattacker = ChrList.lst + pdata->pprt->owner_ref;
2908 
2909                     //use the character block skill plus the base block rating of the shield and adjust for strength
2910                     total_block_rating = chr_get_skill( pdata->pchr, MAKE_IDSZ( 'B', 'L', 'O', 'C' ) );
2911                     total_block_rating += chr_get_skill( pshield, MAKE_IDSZ( 'B', 'L', 'O', 'C' ) );
2912 
2913                     // -4% per attacker strength
2914                     total_block_rating -= 4 * SFP8_TO_SINT( pattacker->strength );
2915 
2916                     // +2% per defender strength
2917                     total_block_rating += 2 * SFP8_TO_SINT( pdata->pchr->strength );
2918 
2919                     //Now determine the result of the block
2920                     rand_pair.base = 0;
2921                     rand_pair.rand = 100;
2922                     if ( generate_irand_pair( rand_pair ) <= total_block_rating )
2923                     {
2924                         // Defender won, the block holds
2925                         // Add a small stun to the attacker = 40/50 (0.8 seconds)
2926                         pattacker->reload_timer += 40;
2927                     }
2928                     else
2929                     {
2930                         // Attacker broke the block and batters away the shield
2931                         // Time to raise shield again = 40/50 (0.8 seconds)
2932                         pdata->pchr->reload_timer += 40;
2933                         sound_play_chunk( pdata->pchr->pos, g_wavelist[GSND_SHIELDBLOCK] );
2934                     }
2935                 }
2936             }
2937 
2938         }
2939     }
2940 
2941     return prt_deflected;
2942 }
2943 
2944 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_recoil(chr_prt_collsion_data_t * pdata)2945 bool_t do_chr_prt_collision_recoil( chr_prt_collsion_data_t * pdata )
2946 {
2947     /// @details BB@> make the character and particle recoil from the collision
2948     float chr_mass, prt_mass;
2949     float chr_recoil, prt_recoil;
2950 
2951     float attack_factor;
2952 
2953     if ( NULL == pdata ) return bfalse;
2954 
2955     if ( 0.0f == fvec3_length_abs( pdata->vimpulse.v ) &&
2956          0.0f == fvec3_length_abs( pdata->pimpulse.v ) )
2957     {
2958         return btrue;
2959     }
2960 
2961     if ( !pdata->ppip->allowpush ) return bfalse;
2962 
2963     // do the reaction force of the particle on the character
2964 
2965     // determine how much the attack is "felt"
2966     attack_factor = 1.0f;
2967     if ( DAMAGE_CRUSH == pdata->pprt->damagetype )
2968     {
2969         // very blunt type of attack, the maximum effect
2970         attack_factor = 1.0f;
2971     }
2972     else if ( DAMAGE_POKE == pdata->pprt->damagetype )
2973     {
2974         // very focussed type of attack, the minimum effect
2975         attack_factor = 0.5f;
2976     }
2977     else
2978     {
2979         // all other damage types are in the middle
2980         attack_factor = INV_SQRT_TWO;
2981     }
2982 
2983     // get some type of mass info for the particle
2984     get_chr_mass( pdata->pchr, &chr_mass );
2985     get_prt_mass( pdata->pprt, pdata->pchr, &prt_mass );
2986 
2987     // get recoil factors for the masses
2988     get_recoil_factors( chr_mass, prt_mass, &chr_recoil, &prt_recoil );
2989 
2990     // now, we have the particle's impulse and mass
2991     // Do the impulse to the object that was hit
2992     // If the particle was magically deflected, there is no rebound on the target
2993     if ( !pdata->mana_paid )
2994     {
2995         fvec3_t tmp_impulse;
2996 
2997         // calculate the "impulse" to the character
2998         tmp_impulse = fvec3_scale( pdata->vimpulse.v, -chr_recoil * attack_factor * pdata->block_factor );
2999         fvec3_self_sum( pdata->pchr->phys.avel.v, tmp_impulse.v );
3000 
3001         tmp_impulse = fvec3_scale( pdata->pimpulse.v, -chr_recoil * attack_factor * pdata->block_factor );
3002         fvec3_self_sum( pdata->pchr->phys.apos_coll.v, tmp_impulse.v );
3003     }
3004 
3005     // if the particle is attached to a weapon, the particle can force the
3006     // weapon (actually, the weapon's holder) to rebound.
3007     if ( INGAME_CHR( pdata->pprt->attachedto_ref ) )
3008     {
3009         chr_t * pholder;
3010         chr_t * pattached;
3011         CHR_REF iholder;
3012 
3013         // get the attached mass
3014         pattached = ChrList.lst + pdata->pprt->attachedto_ref;
3015 
3016         // assume the worst
3017         pholder = NULL;
3018 
3019         // who is holding the weapon?
3020         iholder = chr_get_lowest_attachment( pdata->pprt->attachedto_ref, bfalse );
3021         if ( INGAME_CHR( iholder ) )
3022         {
3023             pholder = ChrList.lst + iholder;
3024         }
3025         else
3026         {
3027             iholder = chr_get_lowest_attachment( pdata->pprt->owner_ref, bfalse );
3028             if ( INGAME_CHR( iholder ) )
3029             {
3030                 pholder = ChrList.lst + iholder;
3031             }
3032         }
3033 
3034         {
3035             fvec3_t tmp_impulse;
3036 
3037             float holder_mass, total_mass;
3038             float attached_mass;
3039             float tmp_holder_recoil, tmp_prt_recoil, holder_recoil;
3040 
3041             holder_mass = 0.0f;
3042             if (( NULL != pholder ) && ( iholder != pdata->pprt->attachedto_ref ) )
3043             {
3044                 get_chr_mass( pholder, &holder_mass );
3045             }
3046 
3047             get_chr_mass( pattached, &attached_mass );
3048 
3049             total_mass = ABS( holder_mass ) + ABS( attached_mass );
3050             if ( holder_mass < 0.0f ||  attached_mass < 0.0f )
3051             {
3052                 total_mass = -total_mass;
3053             }
3054 
3055             get_recoil_factors( total_mass, prt_mass, &tmp_holder_recoil, &tmp_prt_recoil );
3056 
3057             // get the actual holder recoil
3058             holder_recoil = tmp_holder_recoil * attack_factor;
3059 
3060             // in the SAME direction as the particle
3061             tmp_impulse = fvec3_scale( pdata->vimpulse.v, holder_recoil );
3062             fvec3_self_sum( pholder->phys.avel.v, tmp_impulse.v );
3063 
3064             tmp_impulse = fvec3_scale( pdata->pimpulse.v, holder_recoil );
3065             fvec3_self_sum( pholder->phys.apos_coll.v, tmp_impulse.v );
3066         }
3067     }
3068 
3069     // apply the impulse to the particle velocity
3070     if ( MAX_CHR == pdata->pprt->attachedto_ref )
3071     {
3072         fvec3_t tmp_impulse;
3073 
3074         tmp_impulse = fvec3_scale( pdata->vimpulse.v, prt_recoil );
3075         fvec3_self_sum( pdata->pprt->phys.avel.v, tmp_impulse.v );
3076 
3077         tmp_impulse = fvec3_scale( pdata->pimpulse.v, prt_recoil );
3078         fvec3_self_sum( pdata->pprt->phys.apos_coll.v, tmp_impulse.v );
3079     }
3080 
3081     return btrue;
3082 }
3083 
3084 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_damage(chr_prt_collsion_data_t * pdata)3085 bool_t do_chr_prt_collision_damage( chr_prt_collsion_data_t * pdata )
3086 {
3087     ENC_REF ienc_now, ienc_nxt;
3088     size_t  ienc_count;
3089 
3090     bool_t prt_needs_impact;
3091 
3092     chr_t * powner = NULL;
3093     cap_t * powner_cap = NULL;
3094 
3095     if ( NULL == pdata ) return bfalse;
3096 
3097     if ( INGAME_CHR( pdata->pprt->owner_ref ) )
3098     {
3099         powner = ChrList.lst + pdata->pprt->owner_ref;
3100         powner_cap = pro_get_pcap( powner->profile_ref );
3101     }
3102 
3103     // clean up the enchant list before doing anything
3104     cleanup_character_enchants( pdata->pchr );
3105 
3106     // Check all enchants to see if they are removed
3107     ienc_now = pdata->pchr->firstenchant;
3108     ienc_count = 0;
3109     while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
3110     {
3111         ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
3112 
3113         if ( enc_is_removed( ienc_now, pdata->pprt->profile_ref ) )
3114         {
3115             remove_enchant( ienc_now, NULL );
3116         }
3117 
3118         ienc_now = ienc_nxt;
3119         ienc_count++;
3120     }
3121     if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
3122 
3123     // Steal some life
3124     if ( pdata->pprt->lifedrain > 0 )
3125     {
3126         int val_stt, val_end;
3127         int drain;
3128 
3129         val_stt = pdata->pchr->life;
3130         val_end = MAX( 1, val_stt - pdata->pprt->lifedrain );
3131         drain   = val_stt - val_end;
3132 
3133         // remove the drain from the character that was hit
3134         pdata->pchr->life -= drain;
3135 
3136         // add it to the "caster"
3137         if ( NULL != powner )
3138         {
3139             powner->life = MIN( powner->life + drain, powner->lifemax );
3140         }
3141     }
3142 
3143     // Steal some mana
3144     if ( pdata->pprt->manadrain > 0 )
3145     {
3146         int val_stt, val_end;
3147         int drain;
3148 
3149         val_stt = pdata->pchr->mana;
3150         val_end = MAX( 1, val_stt - pdata->pprt->manadrain );
3151         drain   = val_stt - val_end;
3152 
3153         // remove the drain from the character that was hit
3154         pdata->pchr->mana -= drain;
3155 
3156         // add it to the "caster"
3157         if ( NULL != powner )
3158         {
3159             powner->mana = MIN( powner->mana + drain, powner->manamax );
3160         }
3161     }
3162 
3163     // Do grog
3164     if ( pdata->ppip->grog_time > 0 && pdata->pcap->canbegrogged )
3165     {
3166         SET_BIT( pdata->pchr->ai.alert, ALERTIF_CONFUSED );
3167         pdata->pchr->grog_timer = MAX( pdata->pchr->grog_timer, pdata->ppip->grog_time );
3168     }
3169 
3170     // Do daze
3171     if ( pdata->ppip->daze_time > 0 && pdata->pcap->canbedazed )
3172     {
3173         SET_BIT( pdata->pchr->ai.alert, ALERTIF_CONFUSED );
3174         pdata->pchr->daze_timer = MAX( pdata->pchr->daze_timer, pdata->ppip->daze_time );
3175     }
3176 
3177     //---- Damage the character, if necessary
3178     if ( 0 != ABS( pdata->pprt->damage.base ) + ABS( pdata->pprt->damage.rand ) )
3179     {
3180         prt_needs_impact = pdata->ppip->rotatetoface || INGAME_CHR( pdata->pprt->attachedto_ref );
3181         if ( NULL != powner_cap && powner_cap->isranged ) prt_needs_impact = btrue;
3182 
3183         // DAMFX_ARRO means that it only does damage to the one it's attached to
3184         if ( HAS_NO_BITS( pdata->ppip->damfx, DAMFX_ARRO ) && ( !prt_needs_impact || pdata->is_impact ) )
3185         {
3186             FACING_T direction;
3187             IPair loc_damage = pdata->pprt->damage;
3188 
3189             direction = vec_to_facing( pdata->pprt->vel.x , pdata->pprt->vel.y );
3190             direction = pdata->pchr->ori.facing_z - direction + ATK_BEHIND;
3191 
3192             //These things only apply if the particle has an owner
3193             if ( NULL != powner )
3194             {
3195                 CHR_REF item;
3196 
3197                 // Apply intelligence/wisdom bonus damage for particles with the [IDAM] and [WDAM] expansions (Low ability gives penality)
3198                 // +2% bonus for every point of intelligence and/or wisdom above 14. Below 14 gives -2% instead!
3199                 if ( pdata->ppip->intdamagebonus )
3200                 {
3201                     float percent;
3202                     percent = (( FP8_TO_INT( powner->intelligence ) ) - 14 ) * 2;
3203                     percent /= 100;
3204                     loc_damage.base *= 1.00f + percent;
3205                     loc_damage.rand *= 1.00f + percent;
3206                 }
3207 
3208                 if ( pdata->ppip->wisdamagebonus )
3209                 {
3210                     float percent;
3211                     percent = ( FP8_TO_INT( powner->wisdom ) - 14 ) * 2;
3212                     percent /= 100;
3213                     loc_damage.base *= 1.00f + percent;
3214                     loc_damage.rand *= 1.00f + percent;
3215                 }
3216 
3217                 // Notify the attacker of a scored hit
3218                 SET_BIT( powner->ai.alert, ALERTIF_SCOREDAHIT );
3219                 powner->ai.hitlast = GET_REF_PCHR( pdata->pchr );
3220 
3221                 //Tell the weapons who the attacker hit last
3222                 item = powner->holdingwhich[SLOT_LEFT];
3223                 if ( INGAME_CHR( item ) )
3224                 {
3225                     ChrList.lst[item].ai.hitlast = GET_REF_PCHR( pdata->pchr );
3226                     if ( powner->ai.lastitemused == item ) SET_BIT( ChrList.lst[item].ai.alert, ALERTIF_SCOREDAHIT );
3227                 }
3228 
3229                 item = powner->holdingwhich[SLOT_RIGHT];
3230                 if ( INGAME_CHR( item ) )
3231                 {
3232                     ChrList.lst[item].ai.hitlast = GET_REF_PCHR( pdata->pchr );
3233                     if ( powner->ai.lastitemused == item ) SET_BIT( ChrList.lst[item].ai.alert, ALERTIF_SCOREDAHIT );
3234                 }
3235             }
3236 
3237             // handle vulnerabilities, double the damage
3238             if ( chr_has_vulnie( GET_REF_PCHR( pdata->pchr ), pdata->pprt->profile_ref ) )
3239             {
3240                 //Double the damage
3241                 loc_damage.base = ( loc_damage.base << 1 );
3242                 loc_damage.rand = ( loc_damage.rand << 1 ) | 1;
3243 
3244                 SET_BIT( pdata->pchr->ai.alert, ALERTIF_HITVULNERABLE );
3245             }
3246 
3247             // Damage the character
3248             pdata->actual_damage = damage_character( GET_REF_PCHR( pdata->pchr ), direction, loc_damage, pdata->pprt->damagetype, pdata->pprt->team, pdata->pprt->owner_ref, pdata->ppip->damfx, bfalse );
3249         }
3250     }
3251 
3252     return btrue;
3253 }
3254 
3255 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_impulse(chr_prt_collsion_data_t * pdata)3256 bool_t do_chr_prt_collision_impulse( chr_prt_collsion_data_t * pdata )
3257 {
3258     // estimate the impulse on the particle
3259 
3260     bool_t did_something = bfalse;
3261 
3262     if ( NULL == pdata ) return bfalse;
3263 
3264     if ( !pdata->ppip->allowpush ) return bfalse;
3265 
3266     // the impulse due to particle damage
3267     if ( pdata->is_impact && pdata->prt_damages_chr )
3268     {
3269         int left_over_damage;
3270 
3271         did_something = btrue;
3272 
3273         left_over_damage = 0;
3274         if ( ABS( pdata->actual_damage ) < ABS( pdata->max_damage ) )
3275         {
3276             left_over_damage = ABS( pdata->max_damage ) - ABS( pdata->actual_damage );
3277         }
3278 
3279         if ( 0 == pdata->max_damage )
3280         {
3281             pdata->block_factor = 0.0f;
3282         }
3283         else
3284         {
3285             pdata->block_factor = ( float )left_over_damage / ( float )ABS( pdata->max_damage );
3286             pdata->block_factor = pdata->block_factor / ( 1.0f + pdata->block_factor );
3287         }
3288 
3289         if ( 0.0f == pdata->block_factor )
3290         {
3291             // the simple case (particle comes to a stop)
3292             pdata->vimpulse.x -= pdata->pprt->vel.x;
3293             pdata->vimpulse.y -= pdata->pprt->vel.y;
3294             pdata->vimpulse.z -= pdata->pprt->vel.z;
3295         }
3296         else if ( 0.0f != pdata->dot )
3297         {
3298             float sgn = SGN( pdata->dot );
3299 
3300             pdata->vimpulse.x += -sgn * ( 1.0f + pdata->block_factor ) * pdata->vdiff_perp.x;
3301             pdata->vimpulse.y += -sgn * ( 1.0f + pdata->block_factor ) * pdata->vdiff_perp.y;
3302             pdata->vimpulse.z += -sgn * ( 1.0f + pdata->block_factor ) * pdata->vdiff_perp.z;
3303         }
3304     }
3305 
3306     // the "pressure" impulse due to overlap
3307     if ( pdata->int_min && pdata->depth_min > 0.0f && pdata->ichr != pdata->pprt->owner_ref )
3308     {
3309         fvec3_t tmp_imp;
3310 
3311         // is the normal reversed?
3312         tmp_imp = fvec3_scale( pdata->nrm.v, pdata->depth_min );
3313 
3314         fvec3_self_sum( pdata->pimpulse.v, tmp_imp.v );
3315 
3316         did_something = btrue;
3317     }
3318 
3319     return btrue;
3320 }
3321 
3322 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_bump(chr_prt_collsion_data_t * pdata)3323 bool_t do_chr_prt_collision_bump( chr_prt_collsion_data_t * pdata )
3324 {
3325     bool_t prt_belongs_to_chr;
3326     bool_t prt_hates_chr, prt_attacks_chr, prt_hateonly;
3327     bool_t valid_onlydamagefriendly;
3328     bool_t valid_friendlyfire;
3329     bool_t valid_onlydamagehate;
3330 
3331     if ( NULL == pdata ) return bfalse;
3332 
3333     // always allow valid reaffirmation
3334     if (( pdata->pchr->reaffirm_damagetype < DAMAGE_COUNT ) &&
3335         ( pdata->pprt->damagetype < DAMAGE_COUNT ) &&
3336         ( pdata->pchr->reaffirm_damagetype < pdata->pprt->damagetype ) )
3337     {
3338         return btrue;
3339     }
3340 
3341     // if the particle was deflected, then it can't bump the character
3342     if ( pdata->pchr->invictus || pdata->pprt->attachedto_ref == GET_REF_PCHR( pdata->pchr ) ) return bfalse;
3343 
3344     prt_belongs_to_chr = ( GET_REF_PCHR( pdata->pchr ) == pdata->pprt->owner_ref );
3345 
3346     if ( !prt_belongs_to_chr )
3347     {
3348         // no simple owner relationship. Check for something deeper.
3349         CHR_REF prt_owner = prt_get_iowner( GET_REF_PPRT( pdata->pprt ), 0 );
3350         if ( INGAME_CHR( prt_owner ) )
3351         {
3352             CHR_REF chr_wielder = chr_get_lowest_attachment( GET_REF_PCHR( pdata->pchr ), btrue );
3353             CHR_REF prt_wielder = chr_get_lowest_attachment( prt_owner, btrue );
3354 
3355             if ( !INGAME_CHR( chr_wielder ) ) chr_wielder = GET_REF_PCHR( pdata->pchr );
3356             if ( !INGAME_CHR( prt_wielder ) ) prt_wielder = prt_owner;
3357 
3358             prt_belongs_to_chr = ( chr_wielder == prt_wielder );
3359         }
3360     }
3361 
3362     // does the particle team hate the character's team
3363     prt_hates_chr = team_hates_team( pdata->pprt->team, pdata->pchr->team );
3364 
3365     // Only bump into hated characters?
3366     prt_hateonly = PipStack.lst[pdata->pprt->pip_ref].hateonly;
3367     valid_onlydamagehate = prt_hates_chr && PipStack.lst[pdata->pprt->pip_ref].hateonly;
3368 
3369     // allow neutral particles to attack anything
3370     prt_attacks_chr = prt_hates_chr || (( TEAM_NULL != pdata->pchr->team ) && ( TEAM_NULL == pdata->pprt->team ) );
3371 
3372     // this is the onlydamagefriendly condition from the particle search code
3373     valid_onlydamagefriendly = ( pdata->ppip->onlydamagefriendly && pdata->pprt->team == pdata->pchr->team ) ||
3374                                ( !pdata->ppip->onlydamagefriendly && prt_attacks_chr );
3375 
3376     // I guess "friendly fire" does not mean "self fire", which is a bit unfortunate.
3377     valid_friendlyfire = ( pdata->ppip->friendlyfire && !prt_hates_chr && !prt_belongs_to_chr ) ||
3378                          ( !pdata->ppip->friendlyfire && prt_attacks_chr );
3379 
3380     pdata->prt_bumps_chr =  valid_friendlyfire || valid_onlydamagefriendly || valid_onlydamagehate;
3381 
3382     return pdata->prt_bumps_chr;
3383 }
3384 
3385 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_handle_bump(chr_prt_collsion_data_t * pdata)3386 bool_t do_chr_prt_collision_handle_bump( chr_prt_collsion_data_t * pdata )
3387 {
3388     if ( NULL == pdata || !pdata->prt_bumps_chr ) return bfalse;
3389 
3390     if ( !pdata->prt_bumps_chr ) return bfalse;
3391 
3392     // Catch on fire
3393     spawn_bump_particles( GET_REF_PCHR( pdata->pchr ), GET_REF_PPRT( pdata->pprt ) );
3394 
3395     //handle some special particle interactions
3396     if ( pdata->ppip->end_bump )
3397     {
3398         if ( pdata->ppip->bump_money )
3399         {
3400             chr_t * pcollector = pdata->pchr;
3401 
3402             // Let mounts collect money for their riders
3403             if ( pdata->pchr->ismount && INGAME_CHR( pdata->pchr->holdingwhich[SLOT_LEFT] ) )
3404             {
3405                 pcollector = ChrList.lst + pdata->pchr->holdingwhich[SLOT_LEFT];
3406 
3407                 // if the mount's rider can't get money, the mount gets to keep the money!
3408                 if ( !pcollector->cangrabmoney )
3409                 {
3410                     pcollector = pdata->pchr;
3411                 }
3412             }
3413 
3414             if ( pcollector->cangrabmoney && pcollector->alive && 0 == pcollector->damage_timer && pcollector->money < MAXMONEY )
3415             {
3416                 pcollector->money += pdata->ppip->bump_money;
3417                 pcollector->money = CLIP( pcollector->money, 0, MAXMONEY );
3418 
3419                 // the coin disappears when you pick it up
3420                 pdata->terminate_particle = btrue;
3421             }
3422         }
3423         else
3424         {
3425             // Only hit one character, not several
3426             pdata->terminate_particle = btrue;
3427         }
3428     }
3429 
3430     return btrue;
3431 }
3432 
3433 //--------------------------------------------------------------------------------------------
do_chr_prt_collision_init(CHR_REF ichr,PRT_REF iprt,chr_prt_collsion_data_t * pdata)3434 bool_t do_chr_prt_collision_init( CHR_REF ichr, PRT_REF iprt, chr_prt_collsion_data_t * pdata )
3435 {
3436     if ( NULL == pdata ) return bfalse;
3437 
3438     memset( pdata, 0, sizeof( *pdata ) );
3439 
3440     if ( !INGAME_PRT( iprt ) ) return bfalse;
3441     pdata->iprt = iprt;
3442     pdata->pprt = PrtList.lst + iprt;
3443 
3444     // make sure that it is on
3445     if ( !INGAME_CHR( ichr ) ) return bfalse;
3446     pdata->ichr = ichr;
3447     pdata->pchr = ChrList.lst + ichr;
3448 
3449     // initialize the collision data
3450     pdata->pcap = pro_get_pcap( pdata->pchr->profile_ref );
3451     if ( NULL == pdata->pcap ) return bfalse;
3452 
3453     if ( !LOADED_PIP( pdata->pprt->pip_ref ) ) return bfalse;
3454     pdata->ppip = PipStack.lst + pdata->pprt->pip_ref;
3455 
3456     // estimate the maximum possible "damage" from this particle
3457     // other effects can magnify this number, like vulnerabilities
3458     // or DAMFX_* bits
3459     pdata->max_damage = ABS( pdata->pprt->damage.base ) + ABS( pdata->pprt->damage.rand );
3460 
3461     return btrue;
3462 }
3463 
3464 //--------------------------------------------------------------------------------------------
do_chr_prt_collision(CoNode_t * d)3465 bool_t do_chr_prt_collision( CoNode_t * d )
3466 {
3467     /// @details BB@> this funciton goes through all of the steps to handle character-particle
3468     ///               interactions. A basic interaction has been detected. This needs to be refined
3469     ///               and then handled. The function returns bfalse if the basic interaction was wrong
3470     ///               or if the interaction had no effect.
3471     ///
3472     /// @note This function is a little more complicated than the character-character case because
3473     ///       of the friend-foe logic as well as the damage and other special effects that particles can do.
3474 
3475     bool_t retval = bfalse;
3476 
3477     bool_t prt_deflected;
3478     bool_t prt_can_hit_chr;
3479 
3480     chr_prt_collsion_data_t cn_data;
3481     bool_t intialized;
3482 
3483     // valid node?
3484     if ( NULL == d ) return bfalse;
3485 
3486     if ( MAX_CHR != d->chra && MAX_PRT != d->prtb )
3487     {
3488         // character was first
3489         intialized = do_chr_prt_collision_init( d->chra, d->prtb, &cn_data );
3490     }
3491     else if ( MAX_CHR != d->chrb && MAX_PRT != d->prta )
3492     {
3493         // particle was first
3494         intialized = do_chr_prt_collision_init( d->chrb, d->prta, &cn_data );
3495     }
3496     else
3497     {
3498         // not a valid interaction
3499         intialized = bfalse;
3500 
3501         // in here to keep the compiler from complaining
3502         memset( &cn_data, 0, sizeof( cn_data ) );
3503     }
3504 
3505     if ( !intialized ) return bfalse;
3506 
3507     // ignore dead characters
3508     if ( !cn_data.pchr->alive ) return bfalse;
3509 
3510     // skip objects that are inside inventories
3511     if ( cn_data.pchr->pack.is_packed ) return bfalse;
3512 
3513     // if the particle is attached to this character, ignore a "collision"
3514     if ( MAX_CHR != cn_data.pprt->attachedto_ref && cn_data.ichr == cn_data.pprt->attachedto_ref )
3515     {
3516         return bfalse;
3517     }
3518 
3519     // is there any collision at all?
3520     if ( !do_chr_prt_collision_get_details( d, &cn_data ) )
3521     {
3522         return bfalse;
3523     }
3524     else
3525     {
3526         // help classify impacts
3527 
3528         if ( cn_data.is_pressure )
3529         {
3530             // on the odd chance that we want to use the pressure
3531             // algorithm for an obvious collision....
3532             if ( d->tmin > 0.0f ) cn_data.is_impact = btrue;
3533 
3534             // if, say, a melee attack particle is and already intersects its target
3535             if ( 0 == cn_data.pprt->obj_base.update_count ) cn_data.is_impact = btrue;
3536         }
3537 
3538         if ( cn_data.is_collision )
3539         {
3540             cn_data.is_impact = btrue;
3541         }
3542     }
3543 
3544     // if there is no collision, no point in going farther
3545     if ( !cn_data.int_min && !cn_data.int_max /* && !cn_data.int_plat */ ) return bfalse;
3546 
3547     // if the particle is not actually hitting the object, then limit the
3548     // interaction to 2d
3549     if ( cn_data.int_max && !cn_data.int_min )
3550     {
3551         // do not re-normalize this vector
3552         cn_data.nrm.z = 0.0f;
3553     }
3554 
3555     // find the relative velocity
3556     cn_data.vdiff = fvec3_sub( cn_data.pchr->vel.v, cn_data.pprt->vel.v );
3557 
3558     // decompose the relative velocity parallel and perpendicular to the surface normal
3559     cn_data.dot = fvec3_decompose( cn_data.vdiff.v, cn_data.nrm.v, cn_data.vdiff_perp.v, cn_data.vdiff_para.v );
3560 
3561     // handle particle deflection
3562     prt_deflected = bfalse;
3563     if ( cn_data.int_min || cn_data.int_max )
3564     {
3565         // determine whether the particle is deflected by the character
3566         prt_deflected = do_chr_prt_collision_deflect( &cn_data );
3567         if ( prt_deflected )
3568         {
3569             retval = btrue;
3570         }
3571     }
3572 
3573     // refine the logic for a particle to hit a character
3574     prt_can_hit_chr = do_chr_prt_collision_bump( &cn_data );
3575 
3576     // Torches and such are marked as invulnerable, so the particle is always deflected.
3577     // make a special case for reaffirmation
3578     if (( cn_data.int_min || cn_data.int_max ) && 0 == cn_data.pchr->damage_timer )
3579     {
3580         // Check reaffirmation of particles
3581         if ( cn_data.pchr->reaffirm_damagetype == cn_data.pprt->damagetype )
3582         {
3583             // This prevents items in shops from being burned
3584             if ( !cn_data.pchr->isshopitem )
3585             {
3586                 if ( 0 != reaffirm_attached_particles( cn_data.ichr ) )
3587                 {
3588                     retval = btrue;
3589                 }
3590             }
3591         }
3592     }
3593 
3594     // do "damage" to the character
3595     if (( cn_data.int_min || cn_data.int_max ) && !prt_deflected && 0 == cn_data.pchr->damage_timer && prt_can_hit_chr )
3596     {
3597         // we can't even get to this point if the character is completely invulnerable (invictus)
3598         // or can't be damaged this round
3599         cn_data.prt_damages_chr = do_chr_prt_collision_damage( &cn_data );
3600         if ( cn_data.prt_damages_chr )
3601         {
3602             retval = btrue;
3603         }
3604     }
3605 
3606     // calculate the impulse.
3607     if (( cn_data.int_min || cn_data.int_max ) && cn_data.ppip->allowpush )
3608     {
3609         do_chr_prt_collision_impulse( &cn_data );
3610     }
3611 
3612     // make the character and particle recoil from the collision
3613     if ( fvec3_length_abs( cn_data.vimpulse.v ) > 0.0f ||
3614          fvec3_length_abs( cn_data.pimpulse.v ) > 0.0f )
3615     {
3616         if ( do_chr_prt_collision_recoil( &cn_data ) )
3617         {
3618             retval = btrue;
3619         }
3620     }
3621 
3622     // handle a couple of special cases
3623     if ( cn_data.prt_bumps_chr )
3624     {
3625         if ( do_chr_prt_collision_handle_bump( &cn_data ) )
3626         {
3627             retval = btrue;
3628         }
3629     }
3630 
3631     // platform interaction. do this last, and only if there is no other interaction
3632     //if ( cn_data.int_plat && !cn_data.int_max && !cn_data.int_min )
3633     //{
3634     //    cn_data.int_plat = do_prt_platform_physics( &cn_data );
3635     //}
3636 
3637     // terminate the particle if needed
3638     if ( cn_data.terminate_particle )
3639     {
3640         end_one_particle_in_game( cn_data.iprt );
3641         retval = btrue;
3642     }
3643 
3644     return retval;
3645 }
3646