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