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 particle.c
21 /// @brief Manages particle systems.
22 
23 #include "particle.inl"
24 
25 #include "PrtList.h"
26 
27 #include "log.h"
28 #include "sound.h"
29 #include "camera.h"
30 #include "mesh.inl"
31 #include "game.h"
32 #include "mesh.h"
33 #include "obj_BSP.h"
34 
35 #include "egoboo_setup.h"
36 #include "egoboo_fileutil.h"
37 #include "egoboo_strutil.h"
38 #include "egoboo.h"
39 
40 #include "egoboo_mem.h"
41 
42 #include "enchant.inl"
43 #include "mad.h"
44 #include "profile.inl"
45 
46 //--------------------------------------------------------------------------------------------
47 //--------------------------------------------------------------------------------------------
48 #define PRT_TRANS 0x80
49 
50 const float buoyancy_friction = 0.2f;          // how fast does a "cloud-like" object slow down?
51 
52 //--------------------------------------------------------------------------------------------
53 //--------------------------------------------------------------------------------------------
54 int prt_stoppedby_tests = 0;
55 int prt_pressure_tests = 0;
56 
57 INSTANTIATE_STACK( ACCESS_TYPE_NONE, pip_t, PipStack, MAX_PIP );
58 
59 //--------------------------------------------------------------------------------------------
60 //--------------------------------------------------------------------------------------------
61 static bool_t  prt_free( prt_t * pprt );
62 
63 static prt_t * prt_config_ctor( prt_t * pprt );
64 static prt_t * prt_config_init( prt_t * pprt );
65 static prt_t * prt_config_active( prt_t * pprt );
66 static prt_t * prt_config_deinit( prt_t * pprt );
67 static prt_t * prt_config_dtor( prt_t * pprt );
68 
69 static prt_t * prt_config_do_init( prt_t * pprt );
70 static prt_t * prt_config_do_active( prt_t * pprt );
71 static prt_t * prt_config_do_deinit( prt_t * pprt );
72 
73 int prt_do_end_spawn( const PRT_REF iprt );
74 int prt_do_contspawn( prt_bundle_t * pbdl_prt );
75 prt_bundle_t * prt_do_bump_damage( prt_bundle_t * pbdl_prt );
76 
77 prt_bundle_t * prt_update_animation( prt_bundle_t * pbdl_prt );
78 prt_bundle_t * prt_update_dynalight( prt_bundle_t * pbdl_prt );
79 prt_bundle_t * prt_update_timers( prt_bundle_t * pbdl_prt );
80 prt_bundle_t * prt_update_do_water( prt_bundle_t * pbdl_prt );
81 prt_bundle_t * prt_update_ingame( prt_bundle_t * pbdl_prt );
82 prt_bundle_t * prt_update_ghost( prt_bundle_t * pbdl_prt );
83 prt_bundle_t * prt_update( prt_bundle_t * pbdl_prt );
84 
85 //--------------------------------------------------------------------------------------------
86 //--------------------------------------------------------------------------------------------
prt_free(prt_t * pprt)87 bool_t prt_free( prt_t * pprt )
88 {
89     if ( !ALLOCATED_PPRT( pprt ) ) return bfalse;
90 
91     // do not allow this if you are inside a particle loop
92     EGOBOO_ASSERT( 0 == prt_loop_depth );
93 
94     if ( TERMINATED_PPRT( pprt ) ) return btrue;
95 
96     // deallocate any dynamic data
97     BSP_leaf_dtor( &( pprt->bsp_leaf ) );
98 
99     return btrue;
100 }
101 
102 //--------------------------------------------------------------------------------------------
prt_ctor(prt_t * pprt)103 prt_t * prt_ctor( prt_t * pprt )
104 {
105     /// BB@> Set all particle parameters to safe values.
106     ///      @details The c equivalent of the particle prt::new() function.
107 
108     obj_data_t save_base;
109     obj_data_t * base_ptr;
110 
111     // save the base object data, do not construct it with this function.
112     if ( NULL == pprt ) return NULL;
113     base_ptr = POBJ_GET_PBASE( pprt );
114 
115     memcpy( &save_base, base_ptr, sizeof( save_base ) );
116 
117     memset( pprt, 0, sizeof( *pprt ) );
118 
119     // restore the base object data
120     memcpy( base_ptr, &save_base, sizeof( save_base ) );
121 
122     // reset the base counters
123     base_ptr->update_count = 0;
124     base_ptr->frame_count = 0;
125 
126     // "no lifetime" = "eternal"
127     pprt->lifetime           = ( size_t )( ~0 );
128     pprt->lifetime_remaining = pprt->lifetime;
129     pprt->frames_remaining   = ( size_t )( ~0 );
130 
131     pprt->pip_ref      = MAX_PIP;
132     pprt->profile_ref  = MAX_PROFILE;
133 
134     pprt->attachedto_ref = ( CHR_REF )MAX_CHR;
135     pprt->owner_ref      = ( CHR_REF )MAX_CHR;
136     pprt->target_ref     = ( CHR_REF )MAX_CHR;
137     pprt->parent_ref     = MAX_PRT;
138     pprt->parent_guid    = 0xFFFFFFFF;
139 
140     pprt->onwhichplatform_ref    = ( CHR_REF )MAX_CHR;
141     pprt->onwhichplatform_update = 0;
142     pprt->targetplatform_ref     = ( CHR_REF )MAX_CHR;
143 
144     // initialize the bsp node for this particle
145     BSP_leaf_ctor( &( pprt->bsp_leaf ), 3, pprt, BSP_LEAF_PRT );
146     pprt->bsp_leaf.index = GET_INDEX_PPRT( pprt );
147 
148     // initialize the physics
149     phys_data_ctor( &( pprt->phys ) );
150 
151     pprt->obj_base.state = ego_object_initializing;
152 
153     return pprt;
154 }
155 
156 //--------------------------------------------------------------------------------------------
prt_dtor(prt_t * pprt)157 prt_t * prt_dtor( prt_t * pprt )
158 {
159     if ( NULL == pprt ) return pprt;
160 
161     // destruct/free any allocated data
162     prt_free( pprt );
163 
164     // Destroy the base object.
165     // Sets the state to ego_object_terminated automatically.
166     POBJ_TERMINATE( pprt );
167 
168     return pprt;
169 }
170 
171 //--------------------------------------------------------------------------------------------
172 //--------------------------------------------------------------------------------------------
prt_count_free()173 int prt_count_free()
174 {
175     return PrtList.free_count;
176 }
177 
178 //--------------------------------------------------------------------------------------------
179 //--------------------------------------------------------------------------------------------
play_particle_sound(const PRT_REF particle,Sint8 sound)180 void play_particle_sound( const PRT_REF particle, Sint8 sound )
181 {
182     /// ZZ@> This function plays a sound effect for a particle
183 
184     prt_t * pprt;
185 
186     if ( !DEFINED_PRT( particle ) ) return;
187     pprt = PrtList.lst + particle;
188 
189     if ( !VALID_SND( sound ) ) return;
190 
191     if ( LOADED_PRO( pprt->profile_ref ) )
192     {
193         sound_play_chunk( prt_get_pos( pprt ), pro_get_chunk( pprt->profile_ref, sound ) );
194     }
195     else
196     {
197         sound_play_chunk( prt_get_pos( pprt ), g_wavelist[sound] );
198     }
199 }
200 
201 //--------------------------------------------------------------------------------------------
end_one_particle_now(const PRT_REF particle)202 const PRT_REF end_one_particle_now( const PRT_REF particle )
203 {
204     // this turns the particle into a ghost
205 
206     PRT_REF retval;
207 
208     if ( !ALLOCATED_PRT( particle ) ) return ( PRT_REF )MAX_PRT;
209 
210     retval = particle;
211     if ( prt_request_terminate( particle ) )
212     {
213         retval = ( PRT_REF )MAX_PRT;
214     }
215 
216     return retval;
217 }
218 
219 //--------------------------------------------------------------------------------------------
end_one_particle_in_game(const PRT_REF particle)220 const PRT_REF end_one_particle_in_game( const PRT_REF particle )
221 {
222     /// @details ZZ@> this function causes the game to end a particle
223     ///               and mark it as a ghost.
224 
225     CHR_REF child;
226 
227     // does the particle have valid data?
228     if ( DEFINED_PRT( particle ) )
229     {
230         prt_t * pprt = PrtList.lst + particle;
231         pip_t * ppip = prt_get_ppip( particle );
232 
233         // the object is waiting to be killed, so
234         // do all of the end of life care for the particle
235         prt_do_end_spawn( particle );
236 
237         if ( SPAWNNOCHARACTER != pprt->spawncharacterstate )
238         {
239             child = spawn_one_character( prt_get_pos( pprt ), pprt->profile_ref, pprt->team, 0, pprt->facing, NULL, ( CHR_REF )MAX_CHR );
240             if ( DEFINED_CHR( child ) )
241             {
242                 chr_t * pchild = ChrList.lst + child;
243 
244                 chr_set_ai_state( pchild , pprt->spawncharacterstate );
245                 pchild->ai.owner = pprt->owner_ref;
246             }
247         }
248 
249         if ( NULL != ppip )
250         {
251             play_particle_sound( particle, ppip->end_sound );
252         }
253     }
254 
255     return end_one_particle_now( particle );
256 }
257 
258 //--------------------------------------------------------------------------------------------
259 //--------------------------------------------------------------------------------------------
prt_config_do_init(prt_t * pprt)260 prt_t * prt_config_do_init( prt_t * pprt )
261 {
262     PRT_REF            iprt;
263     pip_t            * ppip;
264     prt_spawn_data_t * pdata;
265 
266     int     velocity;
267     fvec3_t vel;
268     float   tvel;
269     int     offsetfacing = 0, newrand;
270     int     prt_lifetime;
271     fvec3_t tmp_pos;
272     Uint16  turn;
273     float   loc_spdlimit;
274 
275     FACING_T loc_facing;
276     CHR_REF loc_chr_origin;
277 
278     if ( NULL == pprt ) return NULL;
279     pdata = &( pprt->spawn_data );
280     iprt  = GET_INDEX_PPRT( pprt );
281 
282     // Convert from local pdata->ipip to global pdata->ipip
283     if ( !LOADED_PIP( pdata->ipip ) )
284     {
285         log_debug( "spawn_one_particle() - cannot spawn particle with invalid pip == %d (owner == %d(\"%s\"), profile == %d(\"%s\"))\n",
286                    REF_TO_INT( pdata->ipip ), REF_TO_INT( pdata->chr_origin ), DEFINED_CHR( pdata->chr_origin ) ? ChrList.lst[pdata->chr_origin].Name : "INVALID",
287                    REF_TO_INT( pdata->iprofile ), LOADED_PRO( pdata->iprofile ) ? ProList.lst[pdata->iprofile].name : "INVALID" );
288 
289         return NULL;
290     }
291     ppip = PipStack.lst + pdata->ipip;
292 
293     // let the object be activated
294     POBJ_ACTIVATE( pprt, ppip->name );
295 
296     // make some local copies of the spawn data
297     loc_facing     = pdata->facing;
298 
299     // Save a version of the position for local use.
300     // In cpp, will be passed by reference, so we do not want to alter the
301     // components of the original vector.
302     tmp_pos = pdata->pos;
303 
304     // try to get an idea of who our owner is even if we are
305     // given bogus info
306     loc_chr_origin = pdata->chr_origin;
307     if ( !DEFINED_CHR( pdata->chr_origin ) && DEFINED_PRT( pdata->prt_origin ) )
308     {
309         loc_chr_origin = prt_get_iowner( pdata->prt_origin, 0 );
310     }
311 
312     pprt->pip_ref     = pdata->ipip;
313     pprt->profile_ref = pdata->iprofile;
314     pprt->team        = pdata->team;
315     pprt->owner_ref   = loc_chr_origin;
316     pprt->parent_ref  = pdata->prt_origin;
317     pprt->parent_guid = ALLOCATED_PRT( pdata->prt_origin ) ? PrtList.lst[pdata->prt_origin].obj_base.guid : (( Uint32 )( ~0 ) );
318     pprt->damagetype  = ppip->damagetype;
319     pprt->lifedrain   = ppip->lifedrain;
320     pprt->manadrain   = ppip->manadrain;
321 
322     // Lighting and sound
323     pprt->dynalight    = ppip->dynalight;
324     pprt->dynalight.on = bfalse;
325     if ( 0 == pdata->multispawn )
326     {
327         pprt->dynalight.on = ppip->dynalight.mode;
328         if ( DYNA_MODE_LOCAL == ppip->dynalight.mode )
329         {
330             pprt->dynalight.on = DYNA_MODE_OFF;
331         }
332     }
333 
334     // Set character attachments ( pdata->chr_attach==MAX_CHR means none )
335     pprt->attachedto_ref     = pdata->chr_attach;
336     pprt->attachedto_vrt_off = pdata->vrt_offset;
337 
338     // Correct loc_facing
339     loc_facing += ppip->facing_pair.base;
340 
341     // Targeting...
342     vel.z = 0;
343 
344     pprt->offset.z = generate_randmask( ppip->spacing_vrt_pair.base, ppip->spacing_vrt_pair.rand ) - ( ppip->spacing_vrt_pair.rand >> 1 );
345     tmp_pos.z += pprt->offset.z;
346     velocity = generate_randmask( ppip->vel_hrz_pair.base, ppip->vel_hrz_pair.rand );
347     pprt->target_ref = pdata->oldtarget;
348     if ( ppip->newtargetonspawn )
349     {
350         if ( ppip->targetcaster )
351         {
352             // Set the target to the caster
353             pprt->target_ref = loc_chr_origin;
354         }
355         else
356         {
357             // Find a target
358             pprt->target_ref = prt_find_target( pdata->pos.x, pdata->pos.y, pdata->pos.z, loc_facing, pdata->ipip, pdata->team, loc_chr_origin, pdata->oldtarget );
359             if ( DEFINED_CHR( pprt->target_ref ) && !ppip->homing )
360             {
361                 /// @note ZF@> ?What does this do?!
362                 /// @note BB@> glouseangle is the angle found in prt_find_target()
363                 loc_facing -= glouseangle;
364 
365             }
366 
367             // Correct loc_facing for dexterity...
368             offsetfacing = 0;
369             if ( ChrList.lst[loc_chr_origin].dexterity < PERFECTSTAT )
370             {
371                 // Correct loc_facing for randomness
372                 offsetfacing  = generate_randmask( 0, ppip->facing_pair.rand ) - ( ppip->facing_pair.rand >> 1 );
373                 offsetfacing  = ( offsetfacing * ( PERFECTSTAT - ChrList.lst[loc_chr_origin].dexterity ) ) / PERFECTSTAT;
374             }
375 
376             if ( 0.0f != ppip->zaimspd )
377             {
378                 if ( DEFINED_CHR( pprt->target_ref ) )
379                 {
380                     // These aren't velocities...  This is to do aiming on the Z axis
381                     if ( velocity > 0 )
382                     {
383                         vel.x = ChrList.lst[pprt->target_ref].pos.x - pdata->pos.x;
384                         vel.y = ChrList.lst[pprt->target_ref].pos.y - pdata->pos.y;
385                         tvel = SQRT( vel.x * vel.x + vel.y * vel.y ) / velocity;  // This is the number of steps...
386                         if ( tvel > 0.0f )
387                         {
388                             // This is the vel.z alteration
389                             vel.z = ( ChrList.lst[pprt->target_ref].pos.z + ( ChrList.lst[pprt->target_ref].bump.height * 0.5f ) - tmp_pos.z ) / tvel;
390                         }
391                     }
392                 }
393                 else
394                 {
395                     vel.z = 0.5f * ppip->zaimspd;
396                 }
397             }
398 
399             vel.z = CLIP( vel.z, -0.5f * ppip->zaimspd, ppip->zaimspd );
400         }
401 
402         // Does it go away?
403         if ( !DEFINED_CHR( pprt->target_ref ) && ppip->needtarget )
404         {
405             end_one_particle_in_game( iprt );
406             return NULL;
407         }
408 
409         // Start on top of target
410         if ( DEFINED_CHR( pprt->target_ref ) && ppip->startontarget )
411         {
412             tmp_pos.x = ChrList.lst[pprt->target_ref].pos.x;
413             tmp_pos.y = ChrList.lst[pprt->target_ref].pos.y;
414         }
415     }
416     else
417     {
418         // Correct loc_facing for randomness
419         offsetfacing = generate_randmask( 0,  ppip->facing_pair.rand ) - ( ppip->facing_pair.rand >> 1 );
420     }
421     loc_facing += offsetfacing;
422     pprt->facing = loc_facing;
423 
424     // this is actually pointing in the opposite direction?
425     turn = TO_TURN( loc_facing );
426 
427     // Location data from arguments
428     newrand = generate_randmask( ppip->spacing_hrz_pair.base, ppip->spacing_hrz_pair.rand );
429     pprt->offset.x = -turntocos[ turn ] * newrand;
430     pprt->offset.y = -turntosin[ turn ] * newrand;
431 
432     tmp_pos.x += pprt->offset.x;
433     tmp_pos.y += pprt->offset.y;
434 
435     tmp_pos.x = CLIP( tmp_pos.x, 0, PMesh->gmem.edge_x - 2 );
436     tmp_pos.y = CLIP( tmp_pos.y, 0, PMesh->gmem.edge_y - 2 );
437 
438     prt_set_pos( pprt, tmp_pos.v );
439     pprt->pos_old  = tmp_pos;
440     pprt->pos_stt  = tmp_pos;
441 
442     // Velocity data
443     vel.x = -turntocos[ turn ] * velocity;
444     vel.y = -turntosin[ turn ] * velocity;
445     vel.z += generate_randmask( ppip->vel_vrt_pair.base, ppip->vel_vrt_pair.rand ) - ( ppip->vel_vrt_pair.rand >> 1 );
446     pprt->vel = pprt->vel_old = pprt->vel_stt = vel;
447 
448     // Template values
449     pprt->bump_size_stt = ppip->bump_size;
450     pprt->type          = ppip->type;
451 
452     // Image data
453     pprt->rotate        = generate_irand_pair( ppip->rotate_pair );
454     pprt->rotate_add    = ppip->rotate_add;
455 
456     pprt->size_stt      = ppip->size_base;
457     pprt->size_add      = ppip->size_add;
458 
459     pprt->image_stt     = INT_TO_FP8( ppip->image_base );
460     pprt->image_add     = generate_irand_pair( ppip->image_add );
461     pprt->image_max     = INT_TO_FP8( ppip->numframes );
462 
463     // figure out the actual particle lifetime
464     prt_lifetime        = ppip->end_time;
465     if ( ppip->end_lastframe && 0 != pprt->image_add )
466     {
467         if ( ppip->end_time <= 0 )
468         {
469             // Part time is set to 1 cycle
470             int frames = ( pprt->image_max / pprt->image_add ) - 1;
471             prt_lifetime = frames;
472         }
473         else
474         {
475             // Part time is used to give number of cycles
476             int frames = (( pprt->image_max / pprt->image_add ) - 1 );
477             prt_lifetime = ppip->end_time * frames;
478         }
479     }
480 
481     // "no lifetime" = "eternal"
482     if ( prt_lifetime <= 0 )
483     {
484         pprt->lifetime           = ( size_t )( ~0 );
485         pprt->lifetime_remaining = pprt->lifetime;
486         pprt->is_eternal         = btrue;
487     }
488     else
489     {
490         // the lifetime is really supposed tp be in terms of frames, but
491         // to keep the number of updates stable, the frames could lag.
492         // sooo... we just rescale the prt_lifetime so that it will work with the
493         // updates and cross our fingers
494         pprt->lifetime           = CEIL(( float ) prt_lifetime * ( float )TARGET_UPS / ( float )TARGET_FPS );
495         pprt->lifetime_remaining = pprt->lifetime;
496     }
497 
498     // make the particle display AT LEAST one frame, regardless of how many updates
499     // it has or when someone requests for it to terminate
500     pprt->frames_remaining = MAX( 1, prt_lifetime );
501 
502     // Damage stuff
503     range_to_pair( ppip->damage, &( pprt->damage ) );
504 
505     // Spawning data
506     pprt->contspawn_timer = ppip->contspawn_delay;
507     if ( 0 != pprt->contspawn_timer )
508     {
509         pprt->contspawn_timer = 1;
510         if ( DEFINED_CHR( pprt->attachedto_ref ) )
511         {
512             pprt->contspawn_timer++; // Because attachment takes an update before it happens
513         }
514     }
515 
516     // the end-spawn data. determine the
517     pprt->endspawn_amount    = ppip->endspawn_amount;
518     pprt->endspawn_facingadd = ppip->endspawn_facingadd;
519     pprt->endspawn_lpip      = ppip->endspawn_lpip;
520 
521     // Sound effect
522     play_particle_sound( iprt, ppip->soundspawn );
523 
524     // set up the particle transparency
525     pprt->inst.alpha = 0xFF;
526     switch ( pprt->inst.type )
527     {
528         case SPRITE_SOLID: break;
529         case SPRITE_ALPHA: pprt->inst.alpha = PRT_TRANS; break;
530         case SPRITE_LIGHT: break;
531     }
532 
533     // is the spawn location safe?
534     if ( 0 == prt_hit_wall( pprt, tmp_pos.v, NULL, NULL, NULL ) )
535     {
536         pprt->safe_pos   = tmp_pos;
537         pprt->safe_valid = btrue;
538         pprt->safe_grid  = pprt->onwhichgrid;
539     }
540 
541     // get an initial value for the is_homing variable
542     pprt->is_homing = ppip->homing && !DEFINED_CHR( pprt->attachedto_ref );
543 
544     // estimate some parameters for buoyancy and air resistance
545     loc_spdlimit = ppip->spdlimit;
546 
547     {
548         const float buoyancy_min       = 0.0f;
549         const float buoyancy_max       = 2.0f * ABS( STANDARD_GRAVITY );
550         const float air_resistance_min = 0.0f;
551         const float air_resistance_max = 1.0f;
552 
553         // find the buoyancy, assuming that the air_resistance of the particle
554         // is equal to air_friction at standard gravity
555         pprt->buoyancy = -loc_spdlimit * ( 1.0f - air_friction ) - STANDARD_GRAVITY;
556         pprt->buoyancy = CLIP( pprt->buoyancy, buoyancy_min, buoyancy_max );
557 
558         // reduce the buoyancy if the particle falls
559         if ( loc_spdlimit > 0.0f ) pprt->buoyancy *= 0.5f;
560 
561         // determine if there is any left-over air resistance
562         pprt->air_resistance  = 1.0f - ( pprt->buoyancy + STANDARD_GRAVITY ) / -loc_spdlimit;
563         pprt->air_resistance = CLIP( pprt->air_resistance, air_resistance_min, air_resistance_max );
564 
565         pprt->air_resistance /= air_friction;
566         pprt->air_resistance = CLIP( pprt->air_resistance, 0.0f, 1.0f );
567     }
568 
569     pprt->spawncharacterstate = SPAWNNOCHARACTER;
570 
571     prt_set_size( pprt, ppip->size_base );
572 
573 #if defined(_DEBUG) && defined(DEBUG_PRT_LIST)
574 
575     // some code to track all allocated particles, where they came from, how long they are going to last,
576     // what they are being used for...
577     log_debug( "spawn_one_particle() - spawned a particle %d\n"
578                "\tupdate == %d, last update == %d, frame == %d, minimum frame == %d\n"
579                "\towner == %d(\"%s\")\n"
580                "\tpip == %d(\"%s\")\n"
581                "\t\t%s"
582                "\tprofile == %d(\"%s\")\n"
583                "\n",
584                iprt,
585                update_wld, pprt->lifetime, game_frame_all, pprt->safe_time,
586                loc_chr_origin, DEFINED_CHR( loc_chr_origin ) ? ChrList.lst[loc_chr_origin].Name : "INVALID",
587                pdata->ipip, ( NULL != ppip ) ? ppip->name : "INVALID", ( NULL != ppip ) ? ppip->comment : "",
588                pdata->iprofile, LOADED_PRO( pdata->iprofile ) ? ProList.lst[pdata->iprofile].name : "INVALID" );
589 #endif
590 
591     if ( MAX_CHR != pprt->attachedto_ref )
592     {
593         prt_bundle_t prt_bdl;
594 
595         prt_bundle_set( &prt_bdl, pprt );
596 
597         attach_one_particle( &prt_bdl );
598     }
599 
600     return pprt;
601 }
602 
603 //--------------------------------------------------------------------------------------------
prt_config_do_active(prt_t * pprt)604 prt_t * prt_config_do_active( prt_t * pprt )
605 {
606     // is there ever a reason to change the state?
607 
608     return pprt;
609 }
610 
611 //--------------------------------------------------------------------------------------------
prt_config_do_deinit(prt_t * pprt)612 prt_t * prt_config_do_deinit( prt_t * pprt )
613 {
614     if ( NULL == pprt ) return pprt;
615 
616     // go to the next state
617     pprt->obj_base.state = ego_object_destructing;
618 
619     return pprt;
620 }
621 
622 //--------------------------------------------------------------------------------------------
623 //--------------------------------------------------------------------------------------------
prt_config_construct(prt_t * pprt,int max_iterations)624 prt_t * prt_config_construct( prt_t * pprt, int max_iterations )
625 {
626     int          iterations;
627     obj_data_t * base_ptr;
628 
629     if ( NULL == pprt ) return NULL;
630 
631     base_ptr = POBJ_GET_PBASE( pprt );
632     if ( !base_ptr->allocated ) return NULL;
633 
634     // if the particle is already beyond this stage, deconstruct it and start over
635     if ( base_ptr->state > ( int )( ego_object_constructing + 1 ) )
636     {
637         prt_t * tmp_prt = prt_config_deconstruct( pprt, max_iterations );
638         if ( tmp_prt == pprt ) return NULL;
639     }
640 
641     iterations = 0;
642     while ( NULL != pprt && base_ptr->state <= ego_object_constructing && iterations < max_iterations )
643     {
644         prt_t * ptmp = prt_run_config( pprt );
645         if ( ptmp != pprt ) return NULL;
646         iterations++;
647     }
648 
649     return pprt;
650 }
651 
652 //--------------------------------------------------------------------------------------------
prt_config_initialize(prt_t * pprt,int max_iterations)653 prt_t * prt_config_initialize( prt_t * pprt, int max_iterations )
654 {
655     int          iterations;
656     obj_data_t * base_ptr;
657 
658     if ( NULL == pprt ) return NULL;
659 
660     base_ptr = POBJ_GET_PBASE( pprt );
661     if ( !base_ptr->allocated ) return NULL;
662 
663     // if the particle is already beyond this stage, deconstruct it and start over
664     if ( base_ptr->state > ( int )( ego_object_initializing + 1 ) )
665     {
666         prt_t * tmp_prt = prt_config_deconstruct( pprt, max_iterations );
667         if ( tmp_prt == pprt ) return NULL;
668     }
669 
670     iterations = 0;
671     while ( NULL != pprt && base_ptr->state <= ego_object_initializing && iterations < max_iterations )
672     {
673         prt_t * ptmp = prt_run_config( pprt );
674         if ( ptmp != pprt ) return NULL;
675         iterations++;
676     }
677 
678     return pprt;
679 }
680 
681 //--------------------------------------------------------------------------------------------
prt_config_activate(prt_t * pprt,int max_iterations)682 prt_t * prt_config_activate( prt_t * pprt, int max_iterations )
683 {
684     int          iterations;
685     obj_data_t * base_ptr;
686 
687     if ( NULL == pprt ) return NULL;
688 
689     base_ptr = POBJ_GET_PBASE( pprt );
690     if ( !base_ptr->allocated ) return NULL;
691 
692     // if the particle is already beyond this stage, deconstruct it and start over
693     if ( base_ptr->state > ( int )( ego_object_active + 1 ) )
694     {
695         prt_t * tmp_prt = prt_config_deconstruct( pprt, max_iterations );
696         if ( tmp_prt == pprt ) return NULL;
697     }
698 
699     iterations = 0;
700     while ( NULL != pprt && base_ptr->state < ego_object_active && iterations < max_iterations )
701     {
702         prt_t * ptmp = prt_run_config( pprt );
703         if ( ptmp != pprt ) return NULL;
704         iterations++;
705     }
706 
707     EGOBOO_ASSERT( base_ptr->state == ego_object_active );
708     if ( base_ptr->state == ego_object_active )
709     {
710         PrtList_add_used( GET_INDEX_PPRT( pprt ) );
711     }
712 
713     return pprt;
714 }
715 
716 //--------------------------------------------------------------------------------------------
prt_config_deinitialize(prt_t * pprt,int max_iterations)717 prt_t * prt_config_deinitialize( prt_t * pprt, int max_iterations )
718 {
719     int          iterations;
720     obj_data_t * base_ptr;
721 
722     if ( NULL == pprt ) return NULL;
723     base_ptr = POBJ_GET_PBASE( pprt );
724 
725     if ( !base_ptr->allocated ) return NULL;
726 
727     // if the particle is already beyond this stage, deinitialize it
728     if ( base_ptr->state > ( int )( ego_object_deinitializing + 1 ) )
729     {
730         return pprt;
731     }
732     else if ( base_ptr->state < ego_object_deinitializing )
733     {
734         base_ptr->state = ego_object_deinitializing;
735     }
736 
737     iterations = 0;
738     while ( NULL != pprt && base_ptr->state <= ego_object_deinitializing && iterations < max_iterations )
739     {
740         prt_t * ptmp = prt_run_config( pprt );
741         if ( ptmp != pprt ) return NULL;
742         iterations++;
743     }
744 
745     return pprt;
746 }
747 
748 //--------------------------------------------------------------------------------------------
prt_config_deconstruct(prt_t * pprt,int max_iterations)749 prt_t * prt_config_deconstruct( prt_t * pprt, int max_iterations )
750 {
751     int          iterations;
752     obj_data_t * base_ptr;
753 
754     if ( NULL == pprt ) return NULL;
755     base_ptr = POBJ_GET_PBASE( pprt );
756 
757     if ( !base_ptr->allocated ) return NULL;
758 
759     // if the particle is already beyond this stage, deconstruct it
760     if ( base_ptr->state > ( int )( ego_object_destructing + 1 ) )
761     {
762         return pprt;
763     }
764     else if ( base_ptr->state < ego_object_destructing )
765     {
766         // make sure that you deinitialize before destructing
767         base_ptr->state = ego_object_deinitializing;
768     }
769 
770     iterations = 0;
771     while ( NULL != pprt && base_ptr->state <= ego_object_destructing && iterations < max_iterations )
772     {
773         prt_t * ptmp = prt_run_config( pprt );
774         if ( ptmp != pprt ) return NULL;
775         iterations++;
776     }
777 
778     return pprt;
779 }
780 
781 //--------------------------------------------------------------------------------------------
782 //--------------------------------------------------------------------------------------------
prt_run_config(prt_t * pprt)783 prt_t * prt_run_config( prt_t * pprt )
784 {
785     obj_data_t * base_ptr;
786 
787     if ( NULL == pprt ) return NULL;
788     base_ptr = POBJ_GET_PBASE( pprt );
789 
790     if ( !base_ptr->allocated ) return NULL;
791 
792     // set the object to deinitialize if it is not "dangerous" and if was requested
793     if ( base_ptr->kill_me )
794     {
795         if ( !TERMINATED_PBASE( base_ptr ) )
796         {
797             if ( base_ptr->state < ego_object_deinitializing )
798             {
799                 base_ptr->state = ego_object_deinitializing;
800             }
801         }
802 
803         base_ptr->kill_me = bfalse;
804     }
805 
806     switch ( base_ptr->state )
807     {
808         default:
809         case ego_object_invalid:
810             pprt = NULL;
811             break;
812 
813         case ego_object_constructing:
814             pprt = prt_config_ctor( pprt );
815             break;
816 
817         case ego_object_initializing:
818             pprt = prt_config_init( pprt );
819             break;
820 
821         case ego_object_active:
822             pprt = prt_config_active( pprt );
823             break;
824 
825         case ego_object_deinitializing:
826             pprt = prt_config_deinit( pprt );
827             break;
828 
829         case ego_object_destructing:
830             pprt = prt_config_dtor( pprt );
831             break;
832 
833         case ego_object_waiting:
834         case ego_object_terminated:
835             /* do nothing */
836             break;
837     }
838 
839     if ( NULL == pprt )
840     {
841         base_ptr->update_guid = INVALID_UPDATE_GUID;
842     }
843     else if ( ego_object_active == base_ptr->state )
844     {
845         base_ptr->update_guid = PrtList.update_guid;
846     }
847 
848     return pprt;
849 }
850 
851 //--------------------------------------------------------------------------------------------
prt_config_ctor(prt_t * pprt)852 prt_t * prt_config_ctor( prt_t * pprt )
853 {
854     obj_data_t * base_ptr;
855 
856     // grab the base object
857     if ( NULL == pprt ) return NULL;
858     base_ptr = POBJ_GET_PBASE( pprt );
859 
860     // if we aren't in the correct state, abort.
861     if ( !STATE_CONSTRUCTING_PBASE( base_ptr ) ) return pprt;
862 
863     return prt_ctor( pprt );
864 }
865 
866 //--------------------------------------------------------------------------------------------
prt_config_init(prt_t * pprt)867 prt_t * prt_config_init( prt_t * pprt )
868 {
869     obj_data_t * base_ptr;
870 
871     if ( NULL == pprt ) return NULL;
872 
873     base_ptr = POBJ_GET_PBASE( pprt );
874     if ( !STATE_INITIALIZING_PBASE( base_ptr ) ) return pprt;
875 
876     pprt = prt_config_do_init( pprt );
877     if ( NULL == pprt ) return NULL;
878 
879     if ( 0 == prt_loop_depth )
880     {
881         pprt->obj_base.on = btrue;
882     }
883     else
884     {
885         PrtList_add_activation( GET_INDEX_PPRT( pprt ) );
886     }
887 
888     base_ptr->state = ego_object_active;
889 
890     return pprt;
891 }
892 
893 //--------------------------------------------------------------------------------------------
prt_config_active(prt_t * pprt)894 prt_t * prt_config_active( prt_t * pprt )
895 {
896     // there's nothing to configure if the object is active...
897 
898     obj_data_t * base_ptr;
899 
900     if ( NULL == pprt ) return NULL;
901 
902     base_ptr = POBJ_GET_PBASE( pprt );
903     if ( !base_ptr->allocated ) return NULL;
904 
905     if ( !STATE_ACTIVE_PBASE( base_ptr ) ) return pprt;
906 
907     POBJ_END_SPAWN( pprt );
908 
909     pprt = prt_config_do_active( pprt );
910 
911     return pprt;
912 }
913 
914 //--------------------------------------------------------------------------------------------
prt_config_deinit(prt_t * pprt)915 prt_t * prt_config_deinit( prt_t * pprt )
916 {
917     /// @details BB@> deinitialize the character data
918 
919     obj_data_t * base_ptr;
920 
921     if ( NULL == pprt ) return NULL;
922 
923     base_ptr = POBJ_GET_PBASE( pprt );
924     if ( !STATE_DEINITIALIZING_PBASE( base_ptr ) ) return pprt;
925 
926     POBJ_END_SPAWN( pprt );
927 
928     pprt = prt_config_do_deinit( pprt );
929 
930     return pprt;
931 }
932 
933 //--------------------------------------------------------------------------------------------
prt_config_dtor(prt_t * pprt)934 prt_t * prt_config_dtor( prt_t * pprt )
935 {
936     obj_data_t * base_ptr;
937 
938     if ( NULL == pprt ) return NULL;
939 
940     base_ptr = POBJ_GET_PBASE( pprt );
941     if ( !STATE_DESTRUCTING_PBASE( base_ptr ) ) return pprt;
942 
943     POBJ_END_SPAWN( pprt );
944 
945     return prt_dtor( pprt );
946 }
947 
948 //--------------------------------------------------------------------------------------------
949 //--------------------------------------------------------------------------------------------
spawn_one_particle(fvec3_t pos,FACING_T facing,const PRO_REF iprofile,int pip_index,const CHR_REF chr_attach,Uint16 vrt_offset,const TEAM_REF team,const CHR_REF chr_origin,const PRT_REF prt_origin,int multispawn,const CHR_REF oldtarget)950 PRT_REF spawn_one_particle( fvec3_t pos, FACING_T facing, const PRO_REF iprofile, int pip_index,
951                             const CHR_REF chr_attach, Uint16 vrt_offset, const TEAM_REF team,
952                             const CHR_REF chr_origin, const PRT_REF prt_origin, int multispawn, const CHR_REF oldtarget )
953 {
954     /// @details ZZ@> This function spawns a new particle.
955     ///               Returns the index of that particle or MAX_PRT on a failure.
956 
957     PIP_REF ipip;
958     PRT_REF iprt;
959 
960     prt_t * pprt;
961     pip_t * ppip;
962 
963     // Convert from local ipip to global ipip
964     ipip = pro_get_ipip( iprofile, pip_index );
965 
966     if ( !LOADED_PIP( ipip ) )
967     {
968         log_debug( "spawn_one_particle() - cannot spawn particle with invalid pip == %d (owner == %d(\"%s\"), profile == %d(\"%s\"))\n",
969                    REF_TO_INT( ipip ), REF_TO_INT( chr_origin ), INGAME_CHR( chr_origin ) ? ChrList.lst[chr_origin].Name : "INVALID",
970                    REF_TO_INT( iprofile ), LOADED_PRO( iprofile ) ? ProList.lst[iprofile].name : "INVALID" );
971 
972         return ( PRT_REF )MAX_PRT;
973     }
974     ppip = PipStack.lst + ipip;
975 
976     // count all the requests for this particle type
977     ppip->request_count++;
978 
979     iprt = PrtList_allocate( ppip->force );
980     if ( !DEFINED_PRT( iprt ) )
981     {
982 #if defined(_DEBUG) && defined(DEBUG_PRT_LIST)
983         log_debug( "spawn_one_particle() - cannot allocate a particle owner == %d(\"%s\"), pip == %d(\"%s\"), profile == %d(\"%s\")\n",
984                    chr_origin, INGAME_CHR( chr_origin ) ? ChrList.lst[chr_origin].Name : "INVALID",
985                    ipip, LOADED_PIP( ipip ) ? PipStack.lst[ipip].name : "INVALID",
986                    iprofile, LOADED_PRO( iprofile ) ? ProList.lst[iprofile].name : "INVALID" );
987 #endif
988 
989         return ( PRT_REF )MAX_PRT;
990     }
991     pprt = PrtList.lst + iprt;
992 
993     POBJ_BEGIN_SPAWN( pprt );
994 
995     pprt->spawn_data.pos        = pos;
996     pprt->spawn_data.facing     = facing;
997     pprt->spawn_data.iprofile   = iprofile;
998     pprt->spawn_data.ipip       = ipip;
999 
1000     pprt->spawn_data.chr_attach = chr_attach;
1001     pprt->spawn_data.vrt_offset = vrt_offset;
1002     pprt->spawn_data.team       = team;
1003 
1004     pprt->spawn_data.chr_origin = chr_origin;
1005     pprt->spawn_data.prt_origin = prt_origin;
1006     pprt->spawn_data.multispawn = multispawn;
1007     pprt->spawn_data.oldtarget  = oldtarget;
1008 
1009     // actually force the character to spawn
1010     pprt = prt_config_activate( pprt, 100 );
1011 
1012     // count all the successful spawns of this particle
1013     if ( NULL != pprt )
1014     {
1015         POBJ_END_SPAWN( pprt );
1016         ppip->create_count++;
1017     }
1018 
1019     return iprt;
1020 }
1021 //--------------------------------------------------------------------------------------------
prt_get_mesh_pressure(prt_t * pprt,float test_pos[])1022 float prt_get_mesh_pressure( prt_t * pprt, float test_pos[] )
1023 {
1024     float retval = 0.0f;
1025     BIT_FIELD  stoppedby;
1026     pip_t      * ppip;
1027 
1028     if ( !DEFINED_PPRT( pprt ) ) return retval;
1029 
1030     if ( !LOADED_PIP( pprt->pip_ref ) ) return retval;
1031     ppip = PipStack.lst + pprt->pip_ref;
1032 
1033     stoppedby = MPDFX_IMPASS;
1034     if ( 0 != ppip->bump_money ) SET_BIT( stoppedby, MPDFX_WALL );
1035 
1036     // deal with the optional parameters
1037     if ( NULL == test_pos ) test_pos = prt_get_pos_v( pprt );
1038     if ( NULL == test_pos ) return 0;
1039 
1040     mesh_mpdfx_tests = 0;
1041     mesh_bound_tests = 0;
1042     mesh_pressure_tests = 0;
1043     {
1044         retval = mesh_get_pressure( PMesh, test_pos, 0.0f, stoppedby );
1045     }
1046     prt_stoppedby_tests += mesh_mpdfx_tests;
1047     prt_pressure_tests += mesh_pressure_tests;
1048 
1049     return retval;
1050 }
1051 
1052 //--------------------------------------------------------------------------------------------
prt_get_mesh_diff(prt_t * pprt,float test_pos[],float center_pressure)1053 fvec2_t prt_get_mesh_diff( prt_t * pprt, float test_pos[], float center_pressure )
1054 {
1055     fvec2_t     retval = ZERO_VECT2;
1056     float       radius;
1057     BIT_FIELD   stoppedby;
1058     pip_t      * ppip;
1059 
1060     if ( !DEFINED_PPRT( pprt ) ) return retval;
1061 
1062     if ( !LOADED_PIP( pprt->pip_ref ) ) return retval;
1063     ppip = PipStack.lst + pprt->pip_ref;
1064 
1065     stoppedby = MPDFX_IMPASS;
1066     if ( 0 != ppip->bump_money ) SET_BIT( stoppedby, MPDFX_WALL );
1067 
1068     // deal with the optional parameters
1069     if ( NULL == test_pos ) test_pos = prt_get_pos_v( pprt );
1070     if ( NULL == test_pos ) return retval;
1071 
1072     // calculate the radius based on whether the particle is on camera
1073     radius = 0.0f;
1074     if ( mesh_grid_is_valid( PMesh, pprt->onwhichgrid ) )
1075     {
1076         if ( PMesh->tmem.tile_list[ pprt->onwhichgrid ].inrenderlist )
1077         {
1078             radius = pprt->bump_real.size;
1079         }
1080     }
1081 
1082     mesh_mpdfx_tests = 0;
1083     mesh_bound_tests = 0;
1084     mesh_pressure_tests = 0;
1085     {
1086         retval = mesh_get_diff( PMesh, test_pos, radius, center_pressure, stoppedby );
1087     }
1088     prt_stoppedby_tests += mesh_mpdfx_tests;
1089     prt_pressure_tests += mesh_pressure_tests;
1090 
1091     return retval;
1092 }
1093 
1094 //--------------------------------------------------------------------------------------------
prt_hit_wall(prt_t * pprt,const float test_pos[],float nrm[],float * pressure,mesh_wall_data_t * pdata)1095 BIT_FIELD prt_hit_wall( prt_t * pprt, const float test_pos[], float nrm[], float * pressure, mesh_wall_data_t * pdata )
1096 {
1097     /// @details ZZ@> This function returns nonzero if the particle hit a wall that the
1098     ///    particle is not allowed to cross
1099 
1100     BIT_FIELD  retval;
1101     BIT_FIELD  stoppedby;
1102     pip_t      * ppip;
1103 
1104     if ( !DEFINED_PPRT( pprt ) ) return 0;
1105 
1106     if ( !LOADED_PIP( pprt->pip_ref ) ) return 0;
1107     ppip = PipStack.lst + pprt->pip_ref;
1108 
1109     stoppedby = MPDFX_IMPASS;
1110     if ( 0 != ppip->bump_money ) SET_BIT( stoppedby, MPDFX_WALL );
1111 
1112     // deal with the optional parameters
1113     if ( NULL == test_pos ) test_pos = prt_get_pos_v( pprt );
1114     if ( NULL == test_pos ) return 0;
1115 
1116     mesh_mpdfx_tests = 0;
1117     mesh_bound_tests = 0;
1118     mesh_pressure_tests = 0;
1119     {
1120         retval = mesh_hit_wall( PMesh, test_pos, 0.0f, stoppedby, nrm, pressure, pdata );
1121     }
1122     prt_stoppedby_tests += mesh_mpdfx_tests;
1123     prt_pressure_tests += mesh_pressure_tests;
1124 
1125     return retval;
1126 }
1127 
1128 //--------------------------------------------------------------------------------------------
prt_test_wall(prt_t * pprt,const float test_pos[],mesh_wall_data_t * pdata)1129 BIT_FIELD prt_test_wall( prt_t * pprt, const float test_pos[], mesh_wall_data_t * pdata )
1130 {
1131     /// @details ZZ@> This function returns nonzero if the particle hit a wall that the
1132     ///    particle is not allowed to cross
1133 
1134     BIT_FIELD retval;
1135     pip_t * ppip;
1136     BIT_FIELD  stoppedby;
1137 
1138     if ( !ACTIVE_PPRT( pprt ) ) return EMPTY_BIT_FIELD;
1139 
1140     if ( !LOADED_PIP( pprt->pip_ref ) ) return bfalse;
1141     ppip = PipStack.lst + pprt->pip_ref;
1142 
1143     stoppedby = MPDFX_IMPASS;
1144     if ( 0 != ppip->bump_money ) SET_BIT( stoppedby, MPDFX_WALL );
1145 
1146     // handle optional parameters
1147     if ( NULL == test_pos ) test_pos = prt_get_pos_v( pprt );
1148     if ( NULL == test_pos ) return EMPTY_BIT_FIELD;
1149 
1150     // do the wall test
1151     mesh_mpdfx_tests = 0;
1152     mesh_bound_tests = 0;
1153     mesh_pressure_tests = 0;
1154     {
1155         retval = mesh_test_wall( PMesh, test_pos, 0.0f, stoppedby, pdata );
1156     }
1157     prt_stoppedby_tests += mesh_mpdfx_tests;
1158     prt_pressure_tests += mesh_pressure_tests;
1159 
1160     return retval;
1161 }
1162 
1163 //--------------------------------------------------------------------------------------------
update_all_particles()1164 void update_all_particles()
1165 {
1166     /// @details BB@> main loop for updating particles. Do not use the
1167     ///               PRT_BEGIN_LOOP_* macro.
1168     ///               Converted all the update functions to the prt_run_config() paradigm.
1169 
1170     PRT_REF iprt;
1171     prt_bundle_t prt_bdl;
1172 
1173     // activate any particles might have been generated last update in an in-active state
1174     for ( iprt = 0; iprt < maxparticles; iprt++ )
1175     {
1176         if ( !ALLOCATED_PRT( iprt ) ) continue;
1177 
1178         prt_bundle_set( &prt_bdl, PrtList.lst + iprt );
1179 
1180         prt_update( &prt_bdl );
1181     }
1182 }
1183 
1184 //--------------------------------------------------------------------------------------------
prt_set_level(prt_t * pprt,float level)1185 void prt_set_level( prt_t * pprt, float level )
1186 {
1187     float loc_height;
1188 
1189     if ( !DISPLAY_PPRT( pprt ) ) return;
1190 
1191     pprt->enviro.level = level;
1192 
1193     loc_height = prt_get_scale( pprt ) * MAX( FP8_TO_FLOAT( pprt->size ), pprt->offset.z * 0.5f );
1194 
1195     pprt->enviro.adj_level = pprt->enviro.level;
1196     pprt->enviro.adj_floor = pprt->enviro.floor_level;
1197 
1198     pprt->enviro.adj_level += loc_height;
1199     pprt->enviro.adj_floor += loc_height;
1200 
1201     // set the zlerp after we have done everything to the particle's level we care to
1202     pprt->enviro.zlerp = ( pprt->pos.z - pprt->enviro.adj_level ) / PLATTOLERANCE;
1203     pprt->enviro.zlerp = CLIP( pprt->enviro.zlerp, 0.0f, 1.0f );
1204 }
1205 
1206 //--------------------------------------------------------------------------------------------
1207 //--------------------------------------------------------------------------------------------
move_one_particle_get_environment(prt_bundle_t * pbdl_prt)1208 prt_bundle_t * move_one_particle_get_environment( prt_bundle_t * pbdl_prt )
1209 {
1210     /// @details BB@> A helper function that gets all of the information about the particle's
1211     ///               environment (like friction, etc.) that will be necessary for the other
1212     ///               move_one_particle_*() functions to work
1213 
1214     Uint32 itile;
1215     float loc_level = 0.0f;
1216 
1217     prt_t             * loc_pprt;
1218     prt_environment_t * penviro;
1219 
1220     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
1221     loc_pprt = pbdl_prt->prt_ptr;
1222     penviro  = &( loc_pprt->enviro );
1223 
1224     //---- character "floor" level
1225     penviro->floor_level = mesh_get_level( PMesh, loc_pprt->pos.x, loc_pprt->pos.y );
1226     penviro->level       = penviro->floor_level;
1227 
1228     //---- The actual level of the characer.
1229     //     Estimate platform attachment from whatever is in the onwhichplatform_ref variable from the
1230     //     last loop
1231     loc_level = penviro->floor_level;
1232     if ( INGAME_CHR( loc_pprt->onwhichplatform_ref ) )
1233     {
1234         loc_level = MAX( penviro->floor_level, ChrList.lst[loc_pprt->onwhichplatform_ref].pos.z + ChrList.lst[loc_pprt->onwhichplatform_ref].chr_min_cv.maxs[OCT_Z] );
1235     }
1236     prt_set_level( loc_pprt, loc_level );
1237 
1238     //---- the "twist" of the floor
1239     penviro->twist = TWIST_FLAT;
1240     itile              = INVALID_TILE;
1241     if ( INGAME_CHR( loc_pprt->onwhichplatform_ref ) )
1242     {
1243         // this only works for 1 level of attachment
1244         itile = ChrList.lst[loc_pprt->onwhichplatform_ref].onwhichgrid;
1245     }
1246     else
1247     {
1248         itile = loc_pprt->onwhichgrid;
1249     }
1250 
1251     if ( mesh_grid_is_valid( PMesh, itile ) )
1252     {
1253         penviro->twist = PMesh->gmem.grid_list[itile].twist;
1254     }
1255 
1256     // the "watery-ness" of whatever water might be here
1257     penviro->is_watery = water.is_water && penviro->inwater;
1258     penviro->is_slippy = !penviro->is_watery && ( 0 != mesh_test_fx( PMesh, loc_pprt->onwhichgrid, MPDFX_SLIPPY ) );
1259 
1260     //---- traction
1261     penviro->traction = 1.0f;
1262     if ( loc_pprt->is_homing )
1263     {
1264         // any traction factor here
1265         /* traction = ??; */
1266     }
1267     else if ( INGAME_CHR( loc_pprt->onwhichplatform_ref ) )
1268     {
1269         // in case the platform is tilted
1270         // unfortunately platforms are attached in the collision section
1271         // which occurs after the movement section.
1272 
1273         fvec3_t   platform_up;
1274 
1275         chr_getMatUp( ChrList.lst + loc_pprt->onwhichplatform_ref, platform_up.v );
1276         platform_up = fvec3_normalize( platform_up.v );
1277 
1278         penviro->traction = ABS( platform_up.z ) * ( 1.0f - penviro->zlerp ) + 0.25f * penviro->zlerp;
1279 
1280         if ( penviro->is_slippy )
1281         {
1282             penviro->traction /= hillslide * ( 1.0f - penviro->zlerp ) + 1.0f * penviro->zlerp;
1283         }
1284     }
1285     else if ( mesh_grid_is_valid( PMesh, loc_pprt->onwhichgrid ) )
1286     {
1287         penviro->traction = ABS( map_twist_nrm[penviro->twist].z ) * ( 1.0f - penviro->zlerp ) + 0.25f * penviro->zlerp;
1288 
1289         if ( penviro->is_slippy )
1290         {
1291             penviro->traction /= hillslide * ( 1.0f - penviro->zlerp ) + 1.0f * penviro->zlerp;
1292         }
1293     }
1294 
1295     //---- the friction of the fluid we are in
1296     if ( penviro->is_watery )
1297     {
1298         penviro->fluid_friction_vrt  = waterfriction;
1299         penviro->fluid_friction_hrz = waterfriction;
1300     }
1301     else
1302     {
1303         penviro->fluid_friction_hrz = penviro->air_friction;       // like real-life air friction
1304         penviro->fluid_friction_vrt  = penviro->air_friction;
1305     }
1306 
1307     //---- friction
1308     penviro->friction_hrz = 1.0f;
1309     if ( !loc_pprt->is_homing )
1310     {
1311         // Make the characters slide
1312         float temp_friction_xy = noslipfriction;
1313         if ( mesh_grid_is_valid( PMesh, loc_pprt->onwhichgrid ) && penviro->is_slippy )
1314         {
1315             // It's slippy all right...
1316             temp_friction_xy = slippyfriction;
1317         }
1318 
1319         penviro->friction_hrz = penviro->zlerp * 1.0f + ( 1.0f - penviro->zlerp ) * temp_friction_xy;
1320     }
1321 
1322     return pbdl_prt;
1323 }
1324 
1325 //--------------------------------------------------------------------------------------------
move_one_particle_do_fluid_friction(prt_bundle_t * pbdl_prt)1326 prt_bundle_t * move_one_particle_do_fluid_friction( prt_bundle_t * pbdl_prt )
1327 {
1328     /// @details BB@> A helper function that computes particle friction with the floor
1329     ///
1330     /// @note this is pretty much ripped from the character version of this function and may
1331     ///       contain some features that are not necessary for any particles that are actually in game.
1332     ///       For instance, the only particles that is under their own control are the homing particles
1333     ///       but they do not have friction with the mesh, but that case is still treated in the code below.
1334 
1335     prt_t             * loc_pprt;
1336     pip_t             * loc_ppip;
1337     prt_environment_t * loc_penviro;
1338     phys_data_t       * loc_pphys;
1339 
1340     fvec3_t fluid_acc;
1341     float loc_fluid_friction;
1342 
1343     if ( NULL == pbdl_prt ) return NULL;
1344     loc_pprt    = pbdl_prt->prt_ptr;
1345     loc_ppip    = pbdl_prt->pip_ptr;
1346     loc_penviro = &( loc_pprt->enviro );
1347     loc_pphys   = &( loc_pprt->phys );
1348 
1349     // if the particle is a homing-type particle, ignore friction
1350     if ( SPRITE_LIGHT == loc_pprt->type ) return pbdl_prt;
1351 
1352     // assume no acceleration
1353     fvec3_self_clear( fluid_acc.v );
1354 
1355     // Light isn't affected by fluid velocity
1356     loc_fluid_friction = loc_penviro->fluid_friction_hrz * loc_pprt->air_resistance;
1357 
1358     if ( loc_pprt->inwater )
1359     {
1360         fluid_acc = fvec3_sub( waterspeed.v, loc_pprt->vel.v );
1361         fvec3_self_scale( fluid_acc.v, 1.0f - loc_fluid_friction );
1362     }
1363     else
1364     {
1365         fluid_acc = fvec3_sub( windspeed.v, loc_pprt->vel.v );
1366         fvec3_self_scale( fluid_acc.v, 1.0f - loc_fluid_friction );
1367     }
1368 
1369     // Apply fluid friction for all particles
1370     if ( loc_pprt->buoyancy > 0.0f )
1371     {
1372         float buoyancy_friction = air_friction * loc_pprt->air_resistance;
1373 
1374         // this is a buoyant particle, like smoke
1375         if ( loc_pprt->inwater )
1376         {
1377             float water_friction = POW( buoyancy_friction, 2.0f );
1378 
1379             fluid_acc.x += ( waterspeed.x - loc_pprt->vel.x ) * ( 1.0f - water_friction );
1380             fluid_acc.y += ( waterspeed.y - loc_pprt->vel.y ) * ( 1.0f - water_friction );
1381             fluid_acc.z += ( waterspeed.z - loc_pprt->vel.z ) * ( 1.0f - water_friction );
1382         }
1383         else
1384         {
1385             fluid_acc.x += ( windspeed.x - loc_pprt->vel.x ) * ( 1.0f - buoyancy_friction );
1386             fluid_acc.y += ( windspeed.y - loc_pprt->vel.y ) * ( 1.0f - buoyancy_friction );
1387             fluid_acc.z += ( windspeed.z - loc_pprt->vel.z ) * ( 1.0f - buoyancy_friction );
1388         }
1389     }
1390     else
1391     {
1392         // this is a normal particle
1393         if ( loc_pprt->inwater )
1394         {
1395             fluid_acc.x += ( waterspeed.x - loc_pprt->vel.x ) * ( 1.0f - loc_penviro->fluid_friction_hrz * loc_pprt->air_resistance );
1396             fluid_acc.y += ( waterspeed.y - loc_pprt->vel.y ) * ( 1.0f - loc_penviro->fluid_friction_hrz * loc_pprt->air_resistance );
1397             fluid_acc.z += ( waterspeed.z - loc_pprt->vel.z ) * ( 1.0f - loc_penviro->fluid_friction_vrt * loc_pprt->air_resistance );
1398         }
1399         else
1400         {
1401             fluid_acc.x += ( windspeed.x - loc_pprt->vel.x ) * ( 1.0f - loc_penviro->fluid_friction_hrz * loc_pprt->air_resistance );
1402             fluid_acc.y += ( windspeed.y - loc_pprt->vel.y ) * ( 1.0f - loc_penviro->fluid_friction_hrz * loc_pprt->air_resistance );
1403             fluid_acc.z += ( windspeed.z - loc_pprt->vel.z ) * ( 1.0f - loc_penviro->fluid_friction_vrt * loc_pprt->air_resistance );
1404         }
1405     }
1406 
1407     loc_pprt->vel.x += fluid_acc.x;
1408     loc_pprt->vel.y += fluid_acc.y;
1409     loc_pprt->vel.z += fluid_acc.z;
1410 
1411     return pbdl_prt;
1412 }
1413 
1414 //--------------------------------------------------------------------------------------------
move_one_particle_do_floor_friction(prt_bundle_t * pbdl_prt)1415 prt_bundle_t * move_one_particle_do_floor_friction( prt_bundle_t * pbdl_prt )
1416 {
1417     /// @details BB@> A helper function that computes particle friction with the floor
1418     ///
1419     /// @note this is pretty much ripped from the character version of this function and may
1420     ///       contain some features that are not necessary for any particles that are actually in game.
1421     ///       For instance, the only particles that is under their own control are the homing particles
1422     ///       but they do not have friction with the mesh, but that case is still treated in the code below.
1423 
1424     float temp_friction_xy;
1425     fvec3_t   vup, floor_acc, fric, fric_floor;
1426 
1427     prt_t             * loc_pprt;
1428     pip_t             * loc_ppip;
1429     prt_environment_t * penviro;
1430 
1431     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
1432     loc_pprt = pbdl_prt->prt_ptr;
1433     loc_ppip = pbdl_prt->pip_ptr;
1434     penviro  = &( loc_pprt->enviro );
1435 
1436     // if the particle is homing in on something, ignore friction
1437     if ( loc_pprt->is_homing ) return pbdl_prt;
1438 
1439     // limit floor friction effects to solid objects
1440     if ( SPRITE_SOLID != loc_pprt->type )  return pbdl_prt;
1441 
1442     // figure out the acceleration due to the current "floor"
1443     floor_acc.x = floor_acc.y = floor_acc.z = 0.0f;
1444     temp_friction_xy = 1.0f;
1445     if ( INGAME_CHR( loc_pprt->onwhichplatform_ref ) )
1446     {
1447         chr_t * pplat = ChrList.lst + loc_pprt->onwhichplatform_ref;
1448 
1449         temp_friction_xy = platstick;
1450 
1451         floor_acc.x = pplat->vel.x - pplat->vel_old.x;
1452         floor_acc.y = pplat->vel.y - pplat->vel_old.y;
1453         floor_acc.z = pplat->vel.z - pplat->vel_old.z;
1454 
1455         chr_getMatUp( pplat, vup.v );
1456     }
1457     else
1458     {
1459         temp_friction_xy = 0.5f;
1460         floor_acc.x = -loc_pprt->vel.x;
1461         floor_acc.y = -loc_pprt->vel.y;
1462         floor_acc.z = -loc_pprt->vel.z;
1463 
1464         if ( TWIST_FLAT == penviro->twist )
1465         {
1466             vup.x = vup.y = 0.0f;
1467             vup.z = 1.0f;
1468         }
1469         else
1470         {
1471             vup = map_twist_nrm[penviro->twist];
1472         }
1473     }
1474 
1475     // the first guess about the floor friction
1476     fric_floor.x = floor_acc.x * ( 1.0f - penviro->zlerp ) * ( 1.0f - temp_friction_xy ) * penviro->traction;
1477     fric_floor.y = floor_acc.y * ( 1.0f - penviro->zlerp ) * ( 1.0f - temp_friction_xy ) * penviro->traction;
1478     fric_floor.z = floor_acc.z * ( 1.0f - penviro->zlerp ) * ( 1.0f - temp_friction_xy ) * penviro->traction;
1479 
1480     // the total "friction" due to the floor
1481     fric.x = fric_floor.x + penviro->acc.x;
1482     fric.y = fric_floor.y + penviro->acc.y;
1483     fric.z = fric_floor.z + penviro->acc.z;
1484 
1485     //---- limit the friction to whatever is horizontal to the mesh
1486     if ( TWIST_FLAT == penviro->twist )
1487     {
1488         floor_acc.z = 0.0f;
1489         fric.z      = 0.0f;
1490     }
1491     else
1492     {
1493         float ftmp;
1494         fvec3_t   vup = map_twist_nrm[penviro->twist];
1495 
1496         ftmp = fvec3_dot_product( floor_acc.v, vup.v );
1497 
1498         floor_acc.x -= ftmp * vup.x;
1499         floor_acc.y -= ftmp * vup.y;
1500         floor_acc.z -= ftmp * vup.z;
1501 
1502         ftmp = fvec3_dot_product( fric.v, vup.v );
1503 
1504         fric.x -= ftmp * vup.x;
1505         fric.y -= ftmp * vup.y;
1506         fric.z -= ftmp * vup.z;
1507     }
1508 
1509     // test to see if the player has any more friction left?
1510     penviro->is_slipping = ( ABS( fric.x ) + ABS( fric.y ) + ABS( fric.z ) > penviro->friction_hrz );
1511 
1512     if ( penviro->is_slipping )
1513     {
1514         penviro->traction *= 0.5f;
1515         temp_friction_xy  = SQRT( temp_friction_xy );
1516 
1517         fric_floor.x = floor_acc.x * ( 1.0f - penviro->zlerp ) * ( 1.0f - temp_friction_xy ) * penviro->traction;
1518         fric_floor.y = floor_acc.y * ( 1.0f - penviro->zlerp ) * ( 1.0f - temp_friction_xy ) * penviro->traction;
1519         fric_floor.z = floor_acc.z * ( 1.0f - penviro->zlerp ) * ( 1.0f - temp_friction_xy ) * penviro->traction;
1520     }
1521 
1522     //apply the floor friction
1523     loc_pprt->vel.x += fric_floor.x;
1524     loc_pprt->vel.y += fric_floor.y;
1525     loc_pprt->vel.z += fric_floor.z;
1526 
1527     return pbdl_prt;
1528 }
1529 
1530 //--------------------------------------------------------------------------------------------
move_one_particle_do_homing(prt_bundle_t * pbdl_prt)1531 prt_bundle_t * move_one_particle_do_homing( prt_bundle_t * pbdl_prt )
1532 {
1533     chr_t * ptarget;
1534 
1535     prt_t             * loc_pprt;
1536     PRT_REF             loc_iprt;
1537     pip_t             * loc_ppip;
1538     prt_environment_t * penviro;
1539 
1540     int       ival;
1541     float     vlen, min_length, uncertainty;
1542     fvec3_t   vdiff, vdither;
1543 
1544     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
1545     loc_pprt = pbdl_prt->prt_ptr;
1546     loc_iprt = pbdl_prt->prt_ref;
1547     loc_ppip = pbdl_prt->pip_ptr;
1548     penviro  = &( loc_pprt->enviro );
1549 
1550     // is the particle a homing type?
1551     if ( !loc_ppip->homing ) return pbdl_prt;
1552 
1553     // the particle update function is supposed to turn homing off if the particle looses its target
1554     if ( !loc_pprt->is_homing ) return pbdl_prt;
1555 
1556     // the loc_pprt->is_homing variable is supposed to track the following, but it could have lost synch by this point
1557     if ( INGAME_CHR( loc_pprt->attachedto_ref ) || !INGAME_CHR( loc_pprt->target_ref ) ) return pbdl_prt;
1558 
1559     // grab a pointer to the target
1560     ptarget = ChrList.lst + loc_pprt->target_ref;
1561 
1562     vdiff = fvec3_sub( ptarget->pos.v, prt_get_pos_v( loc_pprt ) );
1563     vdiff.z += ptarget->bump.height * 0.5f;
1564 
1565     min_length = ( 2 * 5 * 256 * ChrList.lst[loc_pprt->owner_ref].wisdom ) / PERFECTBIG;
1566 
1567     // make a little incertainty about the target
1568     uncertainty = 256 - ( 256 * ChrList.lst[loc_pprt->owner_ref].intelligence ) / PERFECTBIG;
1569 
1570     ival = RANDIE;
1571     vdither.x = ((( float ) ival / 0x8000 ) - 1.0f )  * uncertainty;
1572 
1573     ival = RANDIE;
1574     vdither.y = ((( float ) ival / 0x8000 ) - 1.0f )  * uncertainty;
1575 
1576     ival = RANDIE;
1577     vdither.z = ((( float ) ival / 0x8000 ) - 1.0f )  * uncertainty;
1578 
1579     // take away any dithering along the direction of motion of the particle
1580     vlen = fvec3_dot_product( loc_pprt->vel.v, loc_pprt->vel.v );
1581     if ( vlen > 0.0f )
1582     {
1583         float vdot = fvec3_dot_product( vdither.v, loc_pprt->vel.v ) / vlen;
1584 
1585         vdither.x -= vdot * vdiff.x / vlen;
1586         vdither.y -= vdot * vdiff.y / vlen;
1587         vdither.z -= vdot * vdiff.z / vlen;
1588     }
1589 
1590     // add in the dithering
1591     vdiff.x += vdither.x;
1592     vdiff.y += vdither.y;
1593     vdiff.z += vdither.z;
1594 
1595     // Make sure that vdiff doesn't ever get too small.
1596     // That just makes the particle slooooowww down when it approaches the target.
1597     // Do a real kludge here. this should be a lot faster than a square root, but ...
1598     vlen = ABS( vdiff.x ) + ABS( vdiff.y ) + ABS( vdiff.z );
1599     if ( vlen != 0.0f )
1600     {
1601         float factor = min_length / vlen;
1602 
1603         vdiff.x *= factor;
1604         vdiff.y *= factor;
1605         vdiff.z *= factor;
1606     }
1607 
1608     loc_pprt->vel.x = ( loc_pprt->vel.x + vdiff.x * loc_ppip->homingaccel ) * loc_ppip->homingfriction;
1609     loc_pprt->vel.y = ( loc_pprt->vel.y + vdiff.y * loc_ppip->homingaccel ) * loc_ppip->homingfriction;
1610     loc_pprt->vel.z = ( loc_pprt->vel.z + vdiff.z * loc_ppip->homingaccel ) * loc_ppip->homingfriction;
1611 
1612     return pbdl_prt;
1613 }
1614 
1615 //--------------------------------------------------------------------------------------------
move_one_particle_do_z_motion(prt_bundle_t * pbdl_prt)1616 prt_bundle_t * move_one_particle_do_z_motion( prt_bundle_t * pbdl_prt )
1617 {
1618     /// @details BB@> A helper function that does gravitational acceleration and buoyancy
1619 
1620     float loc_zlerp;
1621 
1622     prt_t             * loc_pprt;
1623     PRT_REF             loc_iprt;
1624     pip_t             * loc_ppip;
1625     prt_environment_t * penviro;
1626 
1627     fvec3_t z_motion_acc;
1628 
1629     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
1630     loc_pprt = pbdl_prt->prt_ptr;
1631     loc_iprt = pbdl_prt->prt_ref;
1632     loc_ppip = pbdl_prt->pip_ptr;
1633     penviro  = &( loc_pprt->enviro );
1634 
1635     /// @note ZF@> We really can't do gravity for Light! A lot of magical effects and attacks in the game depend on being able
1636     ///            to move forward in a straight line without being dragged down into the dust!
1637     /// @note BB@> however, the fireball particle is light, and without gravity it will never bounce on the
1638     ///            ground as it is supposed to
1639     if ( /* loc_pprt->type == SPRITE_LIGHT || */ loc_pprt->is_homing || INGAME_CHR( loc_pprt->attachedto_ref ) ) return pbdl_prt;
1640 
1641     loc_zlerp = CLIP( penviro->zlerp, 0.0f, 1.0f );
1642 
1643     fvec3_self_clear( z_motion_acc.v );
1644 
1645     // Do particle buoyancy. This is kinda BS the way it is calculated
1646     if ( loc_pprt->buoyancy > 0.01f )
1647     {
1648         float loc_buoyancy = loc_pprt->buoyancy  + ( STANDARD_GRAVITY - gravity );
1649 
1650         if ( loc_zlerp < 1.0f )
1651         {
1652             // the particle is close to the ground
1653             if ( loc_pprt->buoyancy + gravity < 0.0f )
1654             {
1655                 // the particle is not bouyant enough to hold itself up.
1656                 // this means that the normal force will overcome it as it gets close to the ground
1657                 // and the force needs to disappear close to the ground
1658                 loc_buoyancy *= loc_zlerp;
1659             }
1660             else
1661             {
1662                 // the particle floats up in the air. it does not reduce its upward
1663                 // acceleration as we get closer to the floor.
1664                 loc_buoyancy += ( 1.0f - loc_zlerp ) * gravity;
1665             }
1666         }
1667 
1668         z_motion_acc.z += loc_buoyancy;
1669     }
1670 
1671     // do gravity
1672     if ( penviro->is_slippy && ( TWIST_FLAT != penviro->twist ) && loc_zlerp < 1.0f )
1673     {
1674         // hills make particles slide
1675 
1676         fvec3_t   gperp;    // gravity perpendicular to the mesh
1677         fvec3_t   gpara;    // gravity parallel      to the mesh (what pushes you)
1678 
1679         gpara.x = map_twistvel_x[penviro->twist];
1680         gpara.y = map_twistvel_y[penviro->twist];
1681         gpara.z = map_twistvel_z[penviro->twist];
1682 
1683         gperp.x = 0       - gpara.x;
1684         gperp.y = 0       - gpara.y;
1685         gperp.z = gravity - gpara.z;
1686 
1687         z_motion_acc.x += gpara.x * ( 1.0f - loc_zlerp ) + gperp.x * loc_zlerp;
1688         z_motion_acc.y += gpara.y * ( 1.0f - loc_zlerp ) + gperp.y * loc_zlerp;
1689         z_motion_acc.z += gpara.z * ( 1.0f - loc_zlerp ) + gperp.z * loc_zlerp;
1690     }
1691     else
1692     {
1693         z_motion_acc.z += loc_zlerp * gravity;
1694     }
1695 
1696     loc_pprt->vel.x += z_motion_acc.x;
1697     loc_pprt->vel.y += z_motion_acc.y;
1698     loc_pprt->vel.z += z_motion_acc.z;
1699 
1700     return pbdl_prt;
1701 }
1702 
1703 //--------------------------------------------------------------------------------------------
move_one_particle_integrate_motion_attached(prt_bundle_t * pbdl_prt)1704 prt_bundle_t * move_one_particle_integrate_motion_attached( prt_bundle_t * pbdl_prt )
1705 {
1706     /// @details BB@> A helper function that figures out the next valid position of the particle.
1707     ///               Collisions with the mesh are included in this step.
1708 
1709     float loc_level;
1710     bool_t hit_a_floor, hit_a_wall, needs_test, updated_2d;
1711     fvec3_t nrm_total;
1712     fvec3_t tmp_pos;
1713 
1714     prt_t             * loc_pprt;
1715     PRT_REF             loc_iprt;
1716     pip_t             * loc_ppip;
1717     prt_environment_t * penviro;
1718 
1719     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
1720     loc_pprt = pbdl_prt->prt_ptr;
1721     loc_iprt = pbdl_prt->prt_ref;
1722     loc_ppip = pbdl_prt->pip_ptr;
1723     penviro  = &( loc_pprt->enviro );
1724 
1725     // if the particle is not still in "display mode" there is no point in going on
1726     if ( !DISPLAY_PPRT( loc_pprt ) ) return pbdl_prt;
1727 
1728     // capture the particle position
1729     tmp_pos = prt_get_pos( loc_pprt );
1730 
1731     // only deal with attached particles
1732     if ( MAX_CHR == loc_pprt->attachedto_ref ) return pbdl_prt;
1733 
1734     hit_a_floor = bfalse;
1735     hit_a_wall  = bfalse;
1736     nrm_total.x = nrm_total.y = nrm_total.z = 0;
1737 
1738     loc_level = penviro->adj_level;
1739 
1740     // Move the particle
1741     if ( tmp_pos.z < loc_level )
1742     {
1743         hit_a_floor = btrue;
1744     }
1745 
1746     if ( hit_a_floor )
1747     {
1748         // Play the sound for hitting the floor [FSND]
1749         play_particle_sound( loc_iprt, loc_ppip->end_sound_floor );
1750     }
1751 
1752     // handle the collision
1753     if ( hit_a_floor && loc_ppip->end_ground )
1754     {
1755         end_one_particle_in_game( pbdl_prt->prt_ref );
1756         return NULL;
1757     }
1758 
1759     // interaction with the mesh walls
1760     hit_a_wall = bfalse;
1761     updated_2d = bfalse;
1762     needs_test = bfalse;
1763     if ( ABS( loc_pprt->vel.x ) + ABS( loc_pprt->vel.y ) > 0.0f )
1764     {
1765         mesh_wall_data_t wdata;
1766 
1767         if ( EMPTY_BIT_FIELD != prt_test_wall( loc_pprt, tmp_pos.v, &wdata ) )
1768         {
1769             Uint32  hit_bits;
1770             fvec2_t nrm;
1771             float   pressure;
1772 
1773             // how is the character hitting the wall?
1774             hit_bits = prt_hit_wall( loc_pprt, tmp_pos.v, nrm.v, &pressure, &wdata );
1775 
1776             if ( 0 != hit_bits )
1777             {
1778                 hit_a_wall = btrue;
1779             }
1780         }
1781     }
1782 
1783     // handle the sounds
1784     if ( hit_a_wall )
1785     {
1786         // Play the sound for hitting the floor [FSND]
1787         play_particle_sound( loc_iprt, loc_ppip->end_sound_wall );
1788     }
1789 
1790     // handle the collision
1791     if ( hit_a_wall && ( loc_ppip->end_wall || loc_ppip->end_bump ) )
1792     {
1793         end_one_particle_in_game( pbdl_prt->prt_ref );
1794         return NULL;
1795     }
1796 
1797     prt_set_pos( loc_pprt, tmp_pos.v );
1798 
1799     return pbdl_prt;
1800 }
1801 
1802 //--------------------------------------------------------------------------------------------
move_one_particle_integrate_motion(prt_bundle_t * pbdl_prt)1803 prt_bundle_t * move_one_particle_integrate_motion( prt_bundle_t * pbdl_prt )
1804 {
1805     /// @details BB@> A helper function that figures out the next valid position of the particle.
1806     ///               Collisions with the mesh are included in this step.
1807 
1808     float ftmp, loc_level;
1809     bool_t hit_a_floor, hit_a_wall, needs_test, updated_2d;
1810     bool_t touch_a_floor, touch_a_wall;
1811     fvec3_t nrm_total;
1812     fvec3_t tmp_pos;
1813 
1814     prt_t             * loc_pprt;
1815     PRT_REF             loc_iprt;
1816     pip_t             * loc_ppip;
1817     prt_environment_t * penviro;
1818 
1819     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
1820     loc_pprt = pbdl_prt->prt_ptr;
1821     loc_iprt = pbdl_prt->prt_ref;
1822     loc_ppip = pbdl_prt->pip_ptr;
1823     penviro  = &( loc_pprt->enviro );
1824 
1825     // if the particle is not still in "display mode" there is no point in going on
1826     if ( !DISPLAY_PPRT( loc_pprt ) ) return pbdl_prt;
1827 
1828     // capture the position
1829     tmp_pos = prt_get_pos( loc_pprt );
1830 
1831     // no point in doing this if the particle thinks it's attached
1832     if ( MAX_CHR != loc_pprt->attachedto_ref )
1833     {
1834         return move_one_particle_integrate_motion_attached( pbdl_prt );
1835     }
1836 
1837     hit_a_floor   = bfalse;
1838     hit_a_wall    = bfalse;
1839     touch_a_floor = bfalse;
1840     touch_a_wall  = bfalse;
1841     nrm_total.x = nrm_total.y = nrm_total.z = 0.0f;
1842 
1843     loc_level = penviro->adj_level;
1844 
1845     // Move the particle
1846     ftmp = tmp_pos.z;
1847     tmp_pos.z += loc_pprt->vel.z;
1848     LOG_NAN( tmp_pos.z );
1849     if ( tmp_pos.z < loc_level )
1850     {
1851         fvec3_t floor_nrm = VECT3( 0, 0, 1 );
1852         float vel_dot;
1853         fvec3_t vel_perp, vel_para;
1854         Uint8 tmp_twist = TWIST_FLAT;
1855 
1856         touch_a_floor = btrue;
1857 
1858         tmp_twist = cartman_get_fan_twist( PMesh, loc_pprt->onwhichgrid );
1859 
1860         if ( TWIST_FLAT != tmp_twist )
1861         {
1862             floor_nrm = map_twist_nrm[penviro->twist];
1863         }
1864 
1865         vel_dot = fvec3_dot_product( floor_nrm.v, loc_pprt->vel.v );
1866         if ( 0.0f == vel_dot )
1867         {
1868             fvec3_self_clear( vel_perp.v );
1869             vel_para = loc_pprt->vel;
1870         }
1871         else
1872         {
1873             vel_perp.x = floor_nrm.x * vel_dot;
1874             vel_perp.y = floor_nrm.y * vel_dot;
1875             vel_perp.z = floor_nrm.z * vel_dot;
1876 
1877             vel_para.x = loc_pprt->vel.x - vel_perp.x;
1878             vel_para.y = loc_pprt->vel.y - vel_perp.y;
1879             vel_para.z = loc_pprt->vel.z - vel_perp.z;
1880         }
1881 
1882         if ( vel_dot < - STOPBOUNCINGPART )
1883         {
1884             // the particle will bounce
1885             nrm_total.x += floor_nrm.x;
1886             nrm_total.y += floor_nrm.y;
1887             nrm_total.z += floor_nrm.z;
1888 
1889             // take reflection in the floor into account when computing the new level
1890             tmp_pos.z = loc_level + ( loc_level - ftmp ) * loc_ppip->dampen + 0.1f;
1891 
1892             hit_a_floor = btrue;
1893         }
1894         else if ( vel_dot > 0.0f )
1895         {
1896             // the particle is not bouncing, it is just at the wrong height
1897             tmp_pos.z = loc_level + 0.1f;
1898         }
1899         else
1900         {
1901             // the particle is in the "stop bouncing zone"
1902             tmp_pos.z     = loc_level + 0.1f;
1903             loc_pprt->vel = vel_para;
1904         }
1905     }
1906 
1907     // handle the sounds
1908     if ( hit_a_floor )
1909     {
1910         // Play the sound for hitting the floor [FSND]
1911         play_particle_sound( loc_iprt, loc_ppip->end_sound_floor );
1912     }
1913 
1914     // handle the collision
1915     if ( touch_a_floor && loc_ppip->end_ground )
1916     {
1917         end_one_particle_in_game( pbdl_prt->prt_ref );
1918         return NULL;
1919     }
1920 
1921     // interaction with the mesh walls
1922     hit_a_wall = bfalse;
1923     updated_2d = bfalse;
1924     needs_test = bfalse;
1925     if ( ABS( loc_pprt->vel.x ) + ABS( loc_pprt->vel.y ) > 0.0f )
1926     {
1927         mesh_wall_data_t wdata;
1928 
1929         float old_x, old_y, new_x, new_y;
1930 
1931         old_x = tmp_pos.x; LOG_NAN( old_x );
1932         old_y = tmp_pos.y; LOG_NAN( old_y );
1933 
1934         new_x = old_x + loc_pprt->vel.x; LOG_NAN( new_x );
1935         new_y = old_y + loc_pprt->vel.y; LOG_NAN( new_y );
1936 
1937         tmp_pos.x = new_x;
1938         tmp_pos.y = new_y;
1939 
1940         if ( EMPTY_BIT_FIELD == prt_test_wall( loc_pprt, tmp_pos.v, &wdata ) )
1941         {
1942             updated_2d = btrue;
1943         }
1944         else
1945         {
1946             BIT_FIELD  hit_bits;
1947             fvec2_t nrm;
1948             float   pressure;
1949 
1950             // how is the character hitting the wall?
1951             hit_bits = prt_hit_wall( loc_pprt, tmp_pos.v, nrm.v, &pressure, &wdata );
1952 
1953             if ( 0 != hit_bits )
1954             {
1955                 touch_a_wall = btrue;
1956 
1957                 tmp_pos.x = old_x;
1958                 tmp_pos.y = old_y;
1959 
1960                 nrm_total.x += nrm.x;
1961                 nrm_total.y += nrm.y;
1962 
1963                 hit_a_wall = ( fvec2_dot_product( loc_pprt->vel.v, nrm.v ) < 0.0f );
1964             }
1965         }
1966     }
1967 
1968     // handle the sounds
1969     if ( hit_a_wall )
1970     {
1971         // Play the sound for hitting the wall [WSND]
1972         play_particle_sound( loc_iprt, loc_ppip->end_sound_wall );
1973     }
1974 
1975     // handle the collision
1976     if ( touch_a_wall && ( loc_ppip->end_wall /*|| loc_ppip->end_bump*/ ) )
1977     {
1978         end_one_particle_in_game( pbdl_prt->prt_ref );
1979         return NULL;
1980     }
1981 
1982     // do the reflections off the walls and floors
1983     if ( !INGAME_CHR( loc_pprt->attachedto_ref ) && ( hit_a_wall || hit_a_floor ) )
1984     {
1985         if (( hit_a_wall && ( loc_pprt->vel.x * nrm_total.x + loc_pprt->vel.y * nrm_total.y ) < 0.0f ) ||
1986             ( hit_a_floor && ( loc_pprt->vel.z * nrm_total.z ) < 0.0f ) )
1987         {
1988             float vdot;
1989             fvec3_t   vpara, vperp;
1990 
1991             nrm_total = fvec3_normalize( nrm_total.v );
1992 
1993             vdot  = fvec3_dot_product( nrm_total.v, loc_pprt->vel.v );
1994 
1995             vperp.x = nrm_total.x * vdot;
1996             vperp.y = nrm_total.y * vdot;
1997             vperp.z = nrm_total.z * vdot;
1998 
1999             vpara.x = loc_pprt->vel.x - vperp.x;
2000             vpara.y = loc_pprt->vel.y - vperp.y;
2001             vpara.z = loc_pprt->vel.z - vperp.z;
2002 
2003             // we can use the impulse to determine how much velocity to kill in the parallel direction
2004             //imp.x = vperp.x * (1.0f + loc_ppip->dampen);
2005             //imp.y = vperp.y * (1.0f + loc_ppip->dampen);
2006             //imp.z = vperp.z * (1.0f + loc_ppip->dampen);
2007 
2008             // do the reflection
2009             vperp.x *= -loc_ppip->dampen;
2010             vperp.y *= -loc_ppip->dampen;
2011             vperp.z *= -loc_ppip->dampen;
2012 
2013             // fake the friction, for now
2014             if ( 0.0f != nrm_total.y || 0.0f != nrm_total.z )
2015             {
2016                 vpara.x *= loc_ppip->dampen;
2017             }
2018 
2019             if ( 0.0f != nrm_total.x || 0.0f != nrm_total.z )
2020             {
2021                 vpara.y *= loc_ppip->dampen;
2022             }
2023 
2024             if ( 0.0f != nrm_total.x || 0.0f != nrm_total.y )
2025             {
2026                 vpara.z *= loc_ppip->dampen;
2027             }
2028 
2029             // add the components back together
2030             loc_pprt->vel.x = vpara.x + vperp.x;
2031             loc_pprt->vel.y = vpara.y + vperp.y;
2032             loc_pprt->vel.z = vpara.z + vperp.z;
2033         }
2034 
2035         if ( nrm_total.z != 0.0f && loc_pprt->vel.z < STOPBOUNCINGPART )
2036         {
2037             // this is the very last bounce
2038             loc_pprt->vel.z = 0.0f;
2039             tmp_pos.z = loc_level + 0.0001f;
2040         }
2041 
2042         if ( hit_a_wall )
2043         {
2044             float fx, fy;
2045 
2046             // fix the facing
2047             facing_to_vec( loc_pprt->facing, &fx, &fy );
2048 
2049             if ( 0.0f != nrm_total.x )
2050             {
2051                 fx *= -1;
2052             }
2053 
2054             if ( 0.0f != nrm_total.y )
2055             {
2056                 fy *= -1;
2057             }
2058 
2059             loc_pprt->facing = vec_to_facing( fx, fy );
2060         }
2061     }
2062 
2063     if ( loc_pprt->is_homing && tmp_pos.z < 0 )
2064     {
2065         tmp_pos.z = 0;  // Don't fall in pits...
2066     }
2067 
2068     if ( loc_ppip->rotatetoface )
2069     {
2070         if ( ABS( loc_pprt->vel.x ) + ABS( loc_pprt->vel.y ) > 1e-6 )
2071         {
2072             // use velocity to find the angle
2073             loc_pprt->facing = vec_to_facing( loc_pprt->vel.x, loc_pprt->vel.y );
2074         }
2075         else if ( INGAME_CHR( loc_pprt->target_ref ) )
2076         {
2077             chr_t * ptarget =  ChrList.lst +  loc_pprt->target_ref;
2078 
2079             // face your target
2080             loc_pprt->facing = vec_to_facing( ptarget->pos.x - tmp_pos.x , ptarget->pos.y - tmp_pos.y );
2081         }
2082     }
2083 
2084     prt_set_pos( loc_pprt, tmp_pos.v );
2085 
2086     return pbdl_prt;
2087 }
2088 
2089 //--------------------------------------------------------------------------------------------
move_one_particle(prt_bundle_t * pbdl_prt)2090 bool_t move_one_particle( prt_bundle_t * pbdl_prt )
2091 {
2092     /// @details BB@> The master function for controlling a particle's motion
2093 
2094     prt_t             * loc_pprt;
2095     prt_environment_t * penviro;
2096 
2097     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2098     loc_pprt = pbdl_prt->prt_ptr;
2099     penviro  = &( loc_pprt->enviro );
2100 
2101     if ( !DISPLAY_PPRT( loc_pprt ) ) return bfalse;
2102 
2103     // if the particle is hidden it is frozen in time. do nothing.
2104     if ( loc_pprt->is_hidden ) return bfalse;
2105 
2106     // save the acceleration from the last time-step
2107     penviro->acc = fvec3_sub( loc_pprt->vel.v, loc_pprt->vel_old.v );
2108 
2109     // determine the actual velocity for attached particles
2110     if ( INGAME_CHR( loc_pprt->attachedto_ref ) )
2111     {
2112         loc_pprt->vel = fvec3_sub( prt_get_pos_v( loc_pprt ), loc_pprt->pos_old.v );
2113     }
2114 
2115     // Particle's old location
2116     loc_pprt->pos_old = prt_get_pos( loc_pprt );
2117     loc_pprt->vel_old = loc_pprt->vel;
2118 
2119     // what is the local environment like?
2120     pbdl_prt = move_one_particle_get_environment( pbdl_prt );
2121     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2122 
2123     // wind, current, and other fluid friction effects
2124     pbdl_prt = move_one_particle_do_fluid_friction( pbdl_prt );
2125     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2126 
2127     // do friction with the floor before voluntary motion
2128     pbdl_prt = move_one_particle_do_floor_friction( pbdl_prt );
2129     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2130 
2131     pbdl_prt = move_one_particle_do_homing( pbdl_prt );
2132     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2133 
2134     pbdl_prt = move_one_particle_do_z_motion( pbdl_prt );
2135     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2136 
2137     pbdl_prt = move_one_particle_integrate_motion( pbdl_prt );
2138     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return bfalse;
2139 
2140     return btrue;
2141 }
2142 
2143 //--------------------------------------------------------------------------------------------
move_all_particles(void)2144 void move_all_particles( void )
2145 {
2146     /// @details ZZ@> This is the particle physics function
2147 
2148     prt_stoppedby_tests = 0;
2149 
2150     // move every particle
2151     PRT_BEGIN_LOOP_DISPLAY( cnt, prt_bdl )
2152     {
2153         // prime the environment
2154         prt_bdl.prt_ptr->enviro.air_friction = air_friction;
2155         prt_bdl.prt_ptr->enviro.ice_friction = ice_friction;
2156 
2157         move_one_particle( &prt_bdl );
2158     }
2159     PRT_END_LOOP();
2160 }
2161 
2162 //--------------------------------------------------------------------------------------------
2163 //--------------------------------------------------------------------------------------------
particle_system_begin()2164 void particle_system_begin()
2165 {
2166     /// @details ZZ@> This function sets up particle data
2167 
2168     // Reset the allocation table
2169     PrtList_init();
2170 
2171     init_all_pip();
2172 }
2173 
2174 //--------------------------------------------------------------------------------------------
particle_system_end()2175 void particle_system_end()
2176 {
2177     release_all_pip();
2178 
2179     PrtList_dtor();
2180 }
2181 
2182 //--------------------------------------------------------------------------------------------
spawn_bump_particles(const CHR_REF character,const PRT_REF particle)2183 int spawn_bump_particles( const CHR_REF character, const PRT_REF particle )
2184 {
2185     /// @details ZZ@> This function is for catching characters on fire and such
2186 
2187     int      cnt, bs_count;
2188     float    x, y, z;
2189     FACING_T facing;
2190     int      amount;
2191     FACING_T direction;
2192     float    fsin, fcos;
2193 
2194     pip_t * ppip;
2195     chr_t * pchr;
2196     mad_t * pmad;
2197     prt_t * pprt;
2198     cap_t * pcap;
2199 
2200     if ( !INGAME_PRT( particle ) ) return 0;
2201     pprt = PrtList.lst + particle;
2202 
2203     if ( !LOADED_PIP( pprt->pip_ref ) ) return 0;
2204     ppip = PipStack.lst + pprt->pip_ref;
2205 
2206     // no point in going on, is there?
2207     if ( 0 == ppip->bumpspawn_amount && !ppip->spawnenchant ) return 0;
2208     amount = ppip->bumpspawn_amount;
2209 
2210     if ( !INGAME_CHR( character ) ) return 0;
2211     pchr = ChrList.lst + character;
2212 
2213     pmad = chr_get_pmad( character );
2214     if ( NULL == pmad ) return 0;
2215 
2216     pcap = pro_get_pcap( pchr->profile_ref );
2217     if ( NULL == pcap ) return 0;
2218 
2219     bs_count = 0;
2220 
2221     // Only damage if hitting from proper direction
2222     direction = vec_to_facing( pprt->vel.x , pprt->vel.y );
2223     direction = ATK_BEHIND + ( pchr->ori.facing_z - direction );
2224 
2225     // Check that direction
2226     if ( !is_invictus_direction( direction, character, ppip->damfx ) )
2227     {
2228         Uint8 damage_modifier;
2229 
2230         // Spawn new enchantments
2231         if ( ppip->spawnenchant )
2232         {
2233             spawn_one_enchant( pprt->owner_ref, character, ( CHR_REF )MAX_CHR, ( ENC_REF )MAX_ENC, pprt->profile_ref );
2234         }
2235 
2236         // Spawn particles - this has been modded to maximize the visual effect
2237         // on a given target. It is not the most optimal solution for lots of particles
2238         // spawning. Thst would probably be to make the distance calculations and then
2239         // to quicksort the list and choose the n closest points.
2240         //
2241         // however, it seems that the bump particles in game rarely attach more than
2242         // one bump particle
2243 
2244         damage_modifier = ( pprt->damagetype >= DAMAGE_COUNT ) ? 0 : pchr->damage_modifier[pprt->damagetype];
2245 
2246         if ( amount != 0 && !pcap->resistbumpspawn && !pchr->invictus && 0 != GET_DAMAGE_RESIST( damage_modifier ) )
2247         {
2248             int grip_verts, vertices;
2249             int slot_count;
2250 
2251             slot_count = 0;
2252             if ( pcap->slotvalid[SLOT_LEFT] ) slot_count++;
2253             if ( pcap->slotvalid[SLOT_RIGHT] ) slot_count++;
2254 
2255             if ( 0 == slot_count )
2256             {
2257                 grip_verts = 1;  // always at least 1?
2258             }
2259             else
2260             {
2261                 grip_verts = GRIP_VERTS * slot_count;
2262             }
2263 
2264             vertices = ( int )pchr->inst.vrt_count - ( int )grip_verts;
2265             vertices = MAX( 0, vertices );
2266 
2267             if ( vertices != 0 )
2268             {
2269                 PRT_REF *vertex_occupied;
2270                 float   *vertex_distance;
2271                 float    dist;
2272                 TURN_T   turn;
2273 
2274                 vertex_occupied = EGOBOO_NEW_ARY( PRT_REF, vertices );
2275                 vertex_distance = EGOBOO_NEW_ARY( float,   vertices );
2276 
2277                 // this could be done more easily with a quicksort....
2278                 // but I guess it doesn't happen all the time
2279                 dist = fvec3_dist_abs( prt_get_pos_v( pprt ), chr_get_pos_v( pchr ) );
2280 
2281                 // clear the occupied list
2282                 z = pprt->pos.z - pchr->pos.z;
2283                 facing = pprt->facing - pchr->ori.facing_z;
2284                 turn   = TO_TURN( facing );
2285                 fsin = turntosin[ turn ];
2286                 fcos = turntocos[ turn ];
2287                 x = dist * fcos;
2288                 y = dist * fsin;
2289 
2290                 // prepare the array values
2291                 for ( cnt = 0; cnt < vertices; cnt++ )
2292                 {
2293                     dist = ABS( x - pchr->inst.vrt_lst[vertices-cnt-1].pos[XX] ) +
2294                            ABS( y - pchr->inst.vrt_lst[vertices-cnt-1].pos[YY] ) +
2295                            ABS( z - pchr->inst.vrt_lst[vertices-cnt-1].pos[ZZ] );
2296 
2297                     vertex_distance[cnt] = dist;
2298                     vertex_occupied[cnt] = MAX_PRT;
2299                 }
2300 
2301                 // determine if some of the vertex sites are already occupied
2302                 PRT_BEGIN_LOOP_ACTIVE( iprt, prt_bdl )
2303                 {
2304                     if ( character != prt_bdl.prt_ptr->attachedto_ref ) continue;
2305 
2306                     if ( prt_bdl.prt_ptr->attachedto_vrt_off >= 0 && prt_bdl.prt_ptr->attachedto_vrt_off < vertices )
2307                     {
2308                         vertex_occupied[prt_bdl.prt_ptr->attachedto_vrt_off] = prt_bdl.prt_ref;
2309                     }
2310                 }
2311                 PRT_END_LOOP()
2312 
2313                 // Find best vertices to attach the particles to
2314                 for ( cnt = 0; cnt < amount; cnt++ )
2315                 {
2316                     PRT_REF bs_part;
2317                     Uint32  bestdistance;
2318                     int     bestvertex;
2319 
2320                     bestvertex   = 0;
2321                     bestdistance = 0xFFFFFFFF;         //Really high number
2322 
2323                     for ( cnt = 0; cnt < vertices; cnt++ )
2324                     {
2325                         if ( vertex_occupied[cnt] != MAX_PRT )
2326                             continue;
2327 
2328                         if ( vertex_distance[cnt] < bestdistance )
2329                         {
2330                             bestdistance = vertex_distance[cnt];
2331                             bestvertex   = cnt;
2332                         }
2333                     }
2334 
2335                     bs_part = spawn_one_particle( pchr->pos, pchr->ori.facing_z, pprt->profile_ref, ppip->bumpspawn_lpip,
2336                                                   character, bestvertex + 1, pprt->team, pprt->owner_ref, particle, cnt, character );
2337 
2338                     if ( DEFINED_PRT( bs_part ) )
2339                     {
2340                         vertex_occupied[bestvertex] = bs_part;
2341                         PrtList.lst[bs_part].is_bumpspawn = btrue;
2342                         bs_count++;
2343                     }
2344                 }
2345                 //}
2346                 //else
2347                 //{
2348                 //    // Multiple particles are attached to character
2349                 //    for ( cnt = 0; cnt < amount; cnt++ )
2350                 //    {
2351                 //        int irand = RANDIE;
2352 
2353                 //        bs_part = spawn_one_particle( pchr->pos, pchr->ori.facing_z, pprt->profile_ref, ppip->bumpspawn_lpip,
2354                 //                            character, irand % vertices, pprt->team, pprt->owner_ref, particle, cnt, character );
2355 
2356                 //        if( DEFINED_PRT(bs_part) )
2357                 //        {
2358                 //            PrtList.lst[bs_part].is_bumpspawn = btrue;
2359                 //            bs_count++;
2360                 //        }
2361                 //    }
2362                 //}
2363 
2364                 EGOBOO_DELETE_ARY( vertex_occupied );
2365                 EGOBOO_DELETE_ARY( vertex_distance );
2366             }
2367         }
2368     }
2369 
2370     return bs_count;
2371 }
2372 
2373 //--------------------------------------------------------------------------------------------
prt_is_over_water(const PRT_REF iprt)2374 bool_t prt_is_over_water( const PRT_REF iprt )
2375 {
2376     /// ZZ@> This function returns btrue if the particle is over a water tile
2377     Uint32 fan;
2378 
2379     if ( !ALLOCATED_PRT( iprt ) ) return bfalse;
2380 
2381     fan = mesh_get_grid( PMesh, PrtList.lst[iprt].pos.x, PrtList.lst[iprt].pos.y );
2382     if ( mesh_grid_is_valid( PMesh, fan ) )
2383     {
2384         if ( 0 != mesh_test_fx( PMesh, fan, MPDFX_WATER ) )  return btrue;
2385     }
2386 
2387     return bfalse;
2388 }
2389 
2390 //--------------------------------------------------------------------------------------------
PipStack_get_free()2391 PIP_REF PipStack_get_free()
2392 {
2393     PIP_REF retval = ( PIP_REF )MAX_PIP;
2394 
2395     if ( PipStack.count < MAX_PIP )
2396     {
2397         retval = PipStack.count;
2398         PipStack.count++;
2399     }
2400 
2401     return retval;
2402 }
2403 
2404 //--------------------------------------------------------------------------------------------
load_one_particle_profile_vfs(const char * szLoadName,const PIP_REF pip_override)2405 PIP_REF load_one_particle_profile_vfs( const char *szLoadName, const PIP_REF pip_override )
2406 {
2407     /// @details ZZ@> This function loads a particle template, returning bfalse if the file wasn't
2408     ///    found
2409 
2410     PIP_REF ipip;
2411     pip_t * ppip;
2412 
2413     ipip = ( PIP_REF ) MAX_PIP;
2414     if ( VALID_PIP_RANGE( pip_override ) )
2415     {
2416         release_one_pip( pip_override );
2417         ipip = pip_override;
2418     }
2419     else
2420     {
2421         ipip = PipStack_get_free();
2422     }
2423 
2424     if ( !VALID_PIP_RANGE( ipip ) )
2425     {
2426         return ( PIP_REF )MAX_PIP;
2427     }
2428     ppip = PipStack.lst + ipip;
2429 
2430     if ( NULL == load_one_pip_file_vfs( szLoadName, ppip ) )
2431     {
2432         return ( PIP_REF )MAX_PIP;
2433     }
2434 
2435     ppip->end_sound = CLIP( ppip->end_sound, INVALID_SOUND, MAX_WAVE );
2436     ppip->soundspawn = CLIP( ppip->soundspawn, INVALID_SOUND, MAX_WAVE );
2437 
2438     return ipip;
2439 }
2440 
2441 //--------------------------------------------------------------------------------------------
reset_particles()2442 void reset_particles( /* const char* modname */ )
2443 {
2444     /// @details ZZ@> This resets all particle data and reads in the coin and water particles
2445 
2446     const char *loadpath;
2447 
2448     release_all_local_pips();
2449     release_all_pip();
2450 
2451     // Load in the standard global particles ( the coins for example )
2452     loadpath = "mp_data/1money.txt";
2453     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_COIN1 ) )
2454     {
2455         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2456     }
2457 
2458     loadpath = "mp_data/5money.txt";
2459     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_COIN5 ) )
2460     {
2461         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2462     }
2463 
2464     loadpath = "mp_data/25money.txt";
2465     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_COIN25 ) )
2466     {
2467         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2468     }
2469 
2470     loadpath = "mp_data/100money.txt";
2471     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_COIN100 ) )
2472     {
2473         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2474     }
2475 
2476     loadpath = "mp_data/200money.txt";
2477     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_GEM200 ) )
2478     {
2479         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2480     }
2481 
2482     loadpath = "mp_data/500money.txt";
2483     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_GEM500 ) )
2484     {
2485         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2486     }
2487 
2488     loadpath = "mp_data/1000money.txt";
2489     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_GEM1000 ) )
2490     {
2491         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2492     }
2493 
2494     loadpath = "mp_data/2000money.txt";
2495     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_GEM2000 ) )
2496     {
2497         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2498     }
2499 
2500     // Load module specific information
2501     loadpath = "mp_data/weather4.txt";
2502     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_WEATHER4 ) )
2503     {
2504         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2505     }
2506 
2507     loadpath = "mp_data/weather5.txt";
2508     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_WEATHER5 ) )
2509     {
2510         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2511     }
2512 
2513     loadpath = "mp_data/splash.txt";
2514     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_SPLASH ) )
2515     {
2516         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2517     }
2518 
2519     loadpath = "mp_data/ripple.txt";
2520     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_RIPPLE ) )
2521     {
2522         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2523     }
2524 
2525     // This is also global...
2526     loadpath = "mp_data/defend.txt";
2527     if ( MAX_PIP == load_one_particle_profile_vfs( loadpath, ( PIP_REF )PIP_DEFEND ) )
2528     {
2529         log_error( "Data file was not found! (\"%s\")\n", loadpath );
2530     }
2531 
2532     PipStack.count = GLOBAL_PIP_COUNT;
2533 }
2534 
2535 //--------------------------------------------------------------------------------------------
2536 //--------------------------------------------------------------------------------------------
init_all_pip()2537 void init_all_pip()
2538 {
2539     PIP_REF cnt;
2540 
2541     for ( cnt = 0; cnt < MAX_PIP; cnt++ )
2542     {
2543         pip_init( PipStack.lst + cnt );
2544     }
2545 
2546     // Reset the pip stack "pointer"
2547     PipStack.count = 0;
2548 }
2549 
2550 //--------------------------------------------------------------------------------------------
release_all_pip()2551 void release_all_pip()
2552 {
2553     PIP_REF cnt;
2554     int tnc;
2555     int max_request;
2556 
2557     max_request = 0;
2558     for ( cnt = 0, tnc = 0; cnt < MAX_PIP; cnt++ )
2559     {
2560         if ( LOADED_PIP( cnt ) )
2561         {
2562             pip_t * ppip = PipStack.lst + cnt;
2563 
2564             max_request = MAX( max_request, ppip->request_count );
2565             tnc++;
2566         }
2567     }
2568 
2569     if ( tnc > 0 && max_request > 0 )
2570     {
2571         FILE * ftmp = fopen( vfs_resolveWriteFilename( "/debug/pip_usage.txt" ), "w" );
2572         if ( NULL != ftmp )
2573         {
2574             fprintf( ftmp, "List of used pips\n\n" );
2575 
2576             for ( cnt = 0; cnt < MAX_PIP; cnt++ )
2577             {
2578                 if ( LOADED_PIP( cnt ) )
2579                 {
2580                     pip_t * ppip = PipStack.lst + cnt;
2581                     fprintf( ftmp, "index == %d\tname == \"%s\"\tcreate_count == %d\trequest_count == %d\n", REF_TO_INT( cnt ), ppip->name, ppip->create_count, ppip->request_count );
2582                 }
2583             }
2584 
2585             fflush( ftmp );
2586 
2587             fclose( ftmp );
2588 
2589             for ( cnt = 0; cnt < MAX_PIP; cnt++ )
2590             {
2591                 release_one_pip( cnt );
2592             }
2593         }
2594     }
2595 }
2596 
2597 //--------------------------------------------------------------------------------------------
release_one_pip(const PIP_REF ipip)2598 bool_t release_one_pip( const PIP_REF ipip )
2599 {
2600     pip_t * ppip;
2601 
2602     if ( !VALID_PIP_RANGE( ipip ) ) return bfalse;
2603     ppip = PipStack.lst + ipip;
2604 
2605     if ( !ppip->loaded ) return btrue;
2606 
2607     pip_init( ppip );
2608 
2609     ppip->loaded  = bfalse;
2610     ppip->name[0] = CSTR_END;
2611 
2612     return btrue;
2613 }
2614 
2615 //--------------------------------------------------------------------------------------------
prt_request_terminate(const PRT_REF iprt)2616 bool_t prt_request_terminate( const PRT_REF iprt )
2617 {
2618     /// @details BB@> Tell the game to get rid of this object and treat it
2619     ///               as if it was already dead
2620     ///
2621     /// @note prt_request_terminate() will force the game to
2622     ///       (eventually) call end_one_particle_in_game() on this particle
2623 
2624     prt_t * pprt;
2625     bool_t  is_visible;
2626 
2627     if ( !ALLOCATED_PRT( iprt ) || TERMINATED_PRT( iprt ) ) return bfalse;
2628 
2629     pprt = PrtList.lst + iprt;
2630 
2631     is_visible =
2632         pprt->size > 0 &&
2633         !pprt->is_hidden &&
2634         pprt->inst.alpha > 0.0f;
2635 
2636     if ( is_visible && 0 == pprt->obj_base.frame_count )
2637     {
2638         // turn the particle into a ghost
2639         pprt->is_ghost = btrue;
2640     }
2641     else
2642     {
2643         // the particle has already been seen or is not visible, so just
2644         // terminate it, as normal
2645         POBJ_REQUEST_TERMINATE( PrtList.lst + iprt );
2646     }
2647 
2648     return btrue;
2649 }
2650 
2651 //--------------------------------------------------------------------------------------------
prt_do_end_spawn(const PRT_REF iprt)2652 int prt_do_end_spawn( const PRT_REF iprt )
2653 {
2654     int endspawn_count = 0;
2655     prt_t * pprt;
2656 
2657     if ( !ALLOCATED_PRT( iprt ) ) return endspawn_count;
2658 
2659     pprt = PrtList.lst + iprt;
2660 
2661     // Spawn new particles if time for old one is up
2662     if ( pprt->endspawn_amount > 0 && LOADED_PRO( pprt->profile_ref ) && pprt->endspawn_lpip > -1 )
2663     {
2664         FACING_T facing;
2665         int      tnc;
2666 
2667         facing = pprt->facing;
2668         for ( tnc = 0; tnc < pprt->endspawn_amount; tnc++ )
2669         {
2670             // we have determined the absolute pip reference when the particle was spawned
2671             // so, set the profile reference to (PRO_REF)MAX_PROFILE, so that the
2672             // value of pprt->endspawn_lpip will be used directly
2673             PRT_REF spawned_prt = spawn_one_particle( pprt->pos_old, facing, pprt->profile_ref, pprt->endspawn_lpip,
2674                                   ( CHR_REF )MAX_CHR, GRIP_LAST, pprt->team, prt_get_iowner( iprt, 0 ), iprt, tnc, pprt->target_ref );
2675 
2676             if ( DEFINED_PRT( spawned_prt ) )
2677             {
2678                 endspawn_count++;
2679             }
2680 
2681             facing += pprt->endspawn_facingadd;
2682         }
2683 
2684         // we have already spawned these particles, so set this amount to
2685         // zero in case we are not actually calling end_one_particle_in_game()
2686         // this time around.
2687         pprt->endspawn_amount = 0;
2688     }
2689 
2690     return endspawn_count;
2691 }
2692 
2693 //--------------------------------------------------------------------------------------------
cleanup_all_particles()2694 void cleanup_all_particles()
2695 {
2696     PRT_REF iprt;
2697 
2698     // do end-of-life care for particles. Must iterate over all particles since the
2699     // number of particles could change inside this list
2700     for ( iprt = 0; iprt < maxparticles; iprt++ )
2701     {
2702         prt_t * pprt;
2703         obj_data_t * base_ptr;
2704 
2705         pprt = PrtList.lst + iprt;
2706 
2707         base_ptr = POBJ_GET_PBASE( pprt );
2708         if ( !FLAG_ALLOCATED_PBASE( base_ptr ) ) continue;
2709 
2710         if ( TERMINATED_PBASE( base_ptr ) )
2711         {
2712             // now that the object is in the "killed" state,
2713             // actually put it back into the free store
2714             PrtList_free_one( GET_REF_PPRT( pprt ) );
2715         }
2716         else if ( STATE_WAITING_PBASE( base_ptr ) )
2717         {
2718             // do everything to end the particle in-game (spawn secondary particles,
2719             // play end sound, etc.) amd mark it with kill_me
2720             end_one_particle_in_game( iprt );
2721         }
2722     }
2723 }
2724 
2725 //--------------------------------------------------------------------------------------------
bump_all_particles_update_counters()2726 void bump_all_particles_update_counters()
2727 {
2728     PRT_REF cnt;
2729 
2730     for ( cnt = 0; cnt < maxparticles; cnt++ )
2731     {
2732         obj_data_t * base_ptr;
2733 
2734         base_ptr = POBJ_GET_PBASE( PrtList.lst + cnt );
2735         if ( !ACTIVE_PBASE( base_ptr ) ) continue;
2736 
2737         base_ptr->update_count++;
2738     }
2739 }
2740 
2741 //--------------------------------------------------------------------------------------------
2742 //--------------------------------------------------------------------------------------------
prt_do_bump_damage(prt_bundle_t * pbdl_prt)2743 prt_bundle_t * prt_do_bump_damage( prt_bundle_t * pbdl_prt )
2744 {
2745     // apply damage from  attatched bump particles (about once a second)
2746 
2747     CHR_REF ichr, iholder;
2748     Uint32  update_count;
2749     IPair   local_damage;
2750     int     max_damage, actual_damage;
2751 
2752     prt_t * loc_pprt;
2753     pip_t * loc_ppip;
2754     chr_t * loc_pchr;
2755 
2756     bool_t skewered_by_arrow;
2757     bool_t has_vulnie;
2758     bool_t is_immolated_by;
2759     bool_t no_protection_from;
2760 
2761     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
2762     loc_pprt = pbdl_prt->prt_ptr;
2763     loc_ppip = pbdl_prt->pip_ptr;
2764 
2765     // this is often set to zero when the particle hits something
2766     max_damage = ABS( loc_pprt->damage.base ) + ABS( loc_pprt->damage.rand );
2767 
2768     // wait until the right time
2769     update_count = update_wld + loc_pprt->obj_base.guid;
2770     if ( 0 != ( update_count & 31 ) ) return pbdl_prt;
2771 
2772     // do nothing if the particle is hidden
2773     // ZF> This is already checked in prt_update_ingame()
2774     //if ( loc_pprt->is_hidden ) return;
2775 
2776     // we must be attached to something
2777     if ( !INGAME_CHR( loc_pprt->attachedto_ref ) ) return pbdl_prt;
2778 
2779     ichr     = loc_pprt->attachedto_ref;
2780     loc_pchr = ChrList.lst + loc_pprt->attachedto_ref;
2781 
2782     // find out who is holding the owner of this object
2783     iholder = chr_get_lowest_attachment( ichr, btrue );
2784     if ( MAX_CHR == iholder ) iholder = ichr;
2785 
2786     // do nothing if you are attached to your owner
2787     if (( MAX_CHR != loc_pprt->owner_ref ) && ( iholder == loc_pprt->owner_ref || ichr == loc_pprt->owner_ref ) ) return pbdl_prt;
2788 
2789     //---- only do damage in certain cases:
2790 
2791     // 1) the particle has the DAMFX_ARRO bit
2792     skewered_by_arrow = HAS_SOME_BITS( loc_ppip->damfx, DAMFX_ARRO );
2793 
2794     // 2) the character is vulnerable to this damage type
2795     has_vulnie = chr_has_vulnie( GET_REF_PCHR( loc_pchr ), loc_pprt->profile_ref );
2796 
2797     // 3) the character is "lit on fire" by the particle damage type
2798     is_immolated_by = ( loc_pprt->damagetype < DAMAGE_COUNT && loc_pchr->reaffirm_damagetype == loc_pprt->damagetype );
2799 
2800     // 4) the character has no protection to the particle
2801     no_protection_from = ( 0 != max_damage ) && ( loc_pprt->damagetype < DAMAGE_COUNT ) && ( 0 == loc_pchr->damage_modifier[loc_pprt->damagetype] );
2802 
2803     if ( !skewered_by_arrow && !has_vulnie && !is_immolated_by && !no_protection_from )
2804     {
2805         return pbdl_prt;
2806     }
2807 
2808     if ( has_vulnie || is_immolated_by )
2809     {
2810         // the damage is the maximum damage over and over again until the particle dies
2811         range_to_pair( loc_ppip->damage, &local_damage );
2812     }
2813     else if ( no_protection_from )
2814     {
2815         // take a portion of whatever damage remains
2816         local_damage = loc_pprt->damage;
2817     }
2818     else
2819     {
2820         range_to_pair( loc_ppip->damage, &local_damage );
2821 
2822         local_damage.base /= 2;
2823         local_damage.rand /= 2;
2824 
2825         // distribute 1/2 of the maximum damage over the particle's lifetime
2826         if ( !loc_pprt->is_eternal )
2827         {
2828             // how many 32 update cycles will this particle live through?
2829             int cycles = loc_pprt->lifetime / 32;
2830 
2831             if ( cycles > 1 )
2832             {
2833                 local_damage.base /= cycles;
2834                 local_damage.rand /= cycles;
2835             }
2836         }
2837     }
2838 
2839     //---- special effects
2840     if ( loc_ppip->allowpush && 0 == loc_ppip->vel_hrz_pair.base )
2841     {
2842         // Make character limp
2843         ChrList.lst[ichr].vel.x *= 0.5f;
2844         ChrList.lst[ichr].vel.y *= 0.5f;
2845     }
2846 
2847     //---- do the damage
2848     actual_damage = damage_character( ichr, ATK_BEHIND, local_damage, loc_pprt->damagetype, loc_pprt->team, loc_pprt->owner_ref, loc_ppip->damfx, bfalse );
2849 
2850     // adjust any remaining particle damage
2851     if ( loc_pprt->damage.base > 0 )
2852     {
2853         loc_pprt->damage.base -= actual_damage;
2854         loc_pprt->damage.base  = MAX( 0, loc_pprt->damage.base );
2855 
2856         // properly scale the random amount
2857         loc_pprt->damage.rand  = ABS( loc_ppip->damage.to - loc_ppip->damage.from ) * loc_pprt->damage.base / loc_ppip->damage.from;
2858     }
2859 
2860     return pbdl_prt;
2861 }
2862 
2863 //--------------------------------------------------------------------------------------------
prt_do_contspawn(prt_bundle_t * pbdl_prt)2864 int prt_do_contspawn( prt_bundle_t * pbdl_prt )
2865 {
2866     /// Spawn new particles if continually spawning
2867 
2868     int      spawn_count = 0;
2869     FACING_T facing;
2870     unsigned tnc;
2871 
2872     prt_t             * loc_pprt;
2873     pip_t             * loc_ppip;
2874 
2875     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return spawn_count;
2876     loc_pprt = pbdl_prt->prt_ptr;
2877     loc_ppip = pbdl_prt->pip_ptr;
2878 
2879     if ( loc_ppip->contspawn_amount <= 0 || -1 == loc_ppip->contspawn_lpip )
2880     {
2881         return spawn_count;
2882     }
2883 
2884     if ( loc_pprt->contspawn_timer > 0 ) return spawn_count;
2885 
2886     // reset the spawn timer
2887     loc_pprt->contspawn_timer = loc_ppip->contspawn_delay;
2888 
2889     facing = loc_pprt->facing;
2890     for ( tnc = 0; tnc < loc_ppip->contspawn_amount; tnc++ )
2891     {
2892         PRT_REF prt_child = spawn_one_particle( prt_get_pos( loc_pprt ), facing, loc_pprt->profile_ref, loc_ppip->contspawn_lpip,
2893                                                 ( CHR_REF )MAX_CHR, GRIP_LAST, loc_pprt->team, loc_pprt->owner_ref, pbdl_prt->prt_ref, tnc, loc_pprt->target_ref );
2894 
2895         if ( DEFINED_PRT( prt_child ) )
2896         {
2897             // Inherit velocities from the particle we were spawned from, but only if it wasn't attached to something
2898 
2899             // ZF> I have disabled this at the moment. This is what caused the erratic particle movement for the Adventurer Torch
2900             // BB> taking out the test works, though  I should have checked vs. loc_pprt->attached_ref, anyway,
2901             //     since we already specified that the particle is not attached in the function call :P
2902             //if( !ACTIVE_CHR( loc_pprt->attachedto_ref ) )
2903             /*{
2904                 PrtList.lst[prt_child].vel.x += loc_pprt->vel.x;
2905                 PrtList.lst[prt_child].vel.y += loc_pprt->vel.y;
2906                 PrtList.lst[prt_child].vel.z += loc_pprt->vel.z;
2907             }*/
2908             // ZF> I have again disabled this. Is this really needed? It wasn't implemented before and causes
2909             //     many, many, many issues with all particles around the game.
2910 
2911             //Keep count of how many were actually spawned
2912             spawn_count++;
2913         }
2914 
2915         facing += loc_ppip->contspawn_facingadd;
2916     }
2917 
2918     return spawn_count;
2919 }
2920 
2921 //--------------------------------------------------------------------------------------------
2922 //--------------------------------------------------------------------------------------------
prt_update_do_water(prt_bundle_t * pbdl_prt)2923 prt_bundle_t * prt_update_do_water( prt_bundle_t * pbdl_prt )
2924 {
2925     /// handle the particle interaction with water
2926 
2927     bool_t inwater;
2928 
2929     prt_t             * loc_pprt;
2930     pip_t             * loc_ppip;
2931     prt_environment_t * penviro;
2932 
2933     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
2934     loc_pprt = pbdl_prt->prt_ptr;
2935     loc_ppip = pbdl_prt->pip_ptr;
2936     penviro  = &( loc_pprt->enviro );
2937 
2938     inwater = ( pbdl_prt->prt_ptr->pos.z < water.surface_level ) && ( 0 != mesh_test_fx( PMesh, pbdl_prt->prt_ptr->onwhichgrid, MPDFX_WATER ) );
2939 
2940     if ( inwater && water.is_water && pbdl_prt->pip_ptr->end_water )
2941     {
2942         // Check for disaffirming character
2943         if ( INGAME_CHR( pbdl_prt->prt_ptr->attachedto_ref ) && pbdl_prt->prt_ptr->owner_ref == pbdl_prt->prt_ptr->attachedto_ref )
2944         {
2945             // Disaffirm the whole character
2946             disaffirm_attached_particles( pbdl_prt->prt_ptr->attachedto_ref );
2947         }
2948         else
2949         {
2950             // destroy the particle
2951             end_one_particle_in_game( pbdl_prt->prt_ref );
2952             return NULL;
2953         }
2954     }
2955     else if ( inwater )
2956     {
2957         bool_t  spawn_valid     = bfalse;
2958         int     global_pip_index = -1;
2959         fvec3_t vtmp            = VECT3( pbdl_prt->prt_ptr->pos.x, pbdl_prt->prt_ptr->pos.y, water.surface_level );
2960 
2961         if ( MAX_CHR == pbdl_prt->prt_ptr->owner_ref && ( PIP_SPLASH == pbdl_prt->prt_ptr->pip_ref || PIP_RIPPLE == pbdl_prt->prt_ptr->pip_ref ) )
2962         {
2963             /* do not spawn anything for a splash or a ripple */
2964             spawn_valid = bfalse;
2965         }
2966         else
2967         {
2968             if ( !pbdl_prt->prt_ptr->inwater )
2969             {
2970                 if ( SPRITE_SOLID == pbdl_prt->prt_ptr->type )
2971                 {
2972                     global_pip_index = PIP_SPLASH;
2973                 }
2974                 else
2975                 {
2976                     global_pip_index = PIP_RIPPLE;
2977                 }
2978                 spawn_valid = btrue;
2979             }
2980             else
2981             {
2982                 if ( SPRITE_SOLID == pbdl_prt->prt_ptr->type && !INGAME_CHR( pbdl_prt->prt_ptr->attachedto_ref ) )
2983                 {
2984                     // only spawn ripples if you are touching the water surface!
2985                     if ( pbdl_prt->prt_ptr->pos.z + pbdl_prt->prt_ptr->bump_real.height > water.surface_level && pbdl_prt->prt_ptr->pos.z - pbdl_prt->prt_ptr->bump_real.height < water.surface_level )
2986                     {
2987                         int ripand = ~(( ~RIPPLEAND ) << 1 );
2988                         if ( 0 == (( update_wld + pbdl_prt->prt_ptr->obj_base.guid ) & ripand ) )
2989                         {
2990 
2991                             spawn_valid = btrue;
2992                             global_pip_index = PIP_RIPPLE;
2993                         }
2994                     }
2995                 }
2996             }
2997         }
2998 
2999         if ( spawn_valid )
3000         {
3001             // Splash for particles is just a ripple
3002             spawn_one_particle_global( vtmp, 0, global_pip_index, 0 );
3003         }
3004 
3005         pbdl_prt->prt_ptr->inwater  = btrue;
3006     }
3007     else
3008     {
3009         pbdl_prt->prt_ptr->inwater = bfalse;
3010     }
3011 
3012     return pbdl_prt;
3013 }
3014 
3015 //--------------------------------------------------------------------------------------------
prt_update_animation(prt_bundle_t * pbdl_prt)3016 prt_bundle_t * prt_update_animation( prt_bundle_t * pbdl_prt )
3017 {
3018     /// animate the particle
3019 
3020     prt_t             * loc_pprt;
3021     pip_t             * loc_ppip;
3022 
3023     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3024     loc_pprt = pbdl_prt->prt_ptr;
3025     loc_ppip = pbdl_prt->pip_ptr;
3026 
3027     loc_pprt->image = loc_pprt->image + loc_pprt->image_add;
3028     if ( loc_pprt->image >= loc_pprt->image_max ) loc_pprt->image = 0;
3029 
3030     // rotate the particle
3031     loc_pprt->rotate += loc_pprt->rotate_add;
3032 
3033     // update the particle size
3034     if ( 0 != loc_pprt->size_add )
3035     {
3036         int size_new;
3037 
3038         // resize the paricle
3039         size_new = loc_pprt->size + loc_pprt->size_add;
3040         size_new = CLIP( size_new, 0, 0xFFFF );
3041 
3042         prt_set_size( loc_pprt, size_new );
3043     }
3044 
3045     // spin the particle
3046     loc_pprt->facing += loc_ppip->facingadd;
3047 
3048     return pbdl_prt;
3049 }
3050 
3051 //--------------------------------------------------------------------------------------------
prt_update_dynalight(prt_bundle_t * pbdl_prt)3052 prt_bundle_t * prt_update_dynalight( prt_bundle_t * pbdl_prt )
3053 {
3054     prt_t             * loc_pprt;
3055     pip_t             * loc_ppip;
3056 
3057     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3058     loc_pprt = pbdl_prt->prt_ptr;
3059     loc_ppip = pbdl_prt->pip_ptr;
3060 
3061     // Change dyna light values
3062     if ( loc_pprt->dynalight.level > 0 )
3063     {
3064         loc_pprt->dynalight.level += loc_ppip->dynalight.level_add;
3065         if ( loc_pprt->dynalight.level < 0 ) loc_pprt->dynalight.level = 0;
3066     }
3067     else if ( loc_pprt->dynalight.level < 0 )
3068     {
3069         // try to guess what should happen for negative lighting
3070         loc_pprt->dynalight.level += loc_ppip->dynalight.level_add;
3071         if ( loc_pprt->dynalight.level > 0 ) loc_pprt->dynalight.level = 0;
3072     }
3073     else
3074     {
3075         loc_pprt->dynalight.level += loc_ppip->dynalight.level_add;
3076     }
3077 
3078     loc_pprt->dynalight.falloff += loc_ppip->dynalight.falloff_add;
3079 
3080     return pbdl_prt;
3081 }
3082 
3083 //--------------------------------------------------------------------------------------------
prt_update_timers(prt_bundle_t * pbdl_prt)3084 prt_bundle_t * prt_update_timers( prt_bundle_t * pbdl_prt )
3085 {
3086     prt_t             * loc_pprt;
3087 
3088     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3089     loc_pprt = pbdl_prt->prt_ptr;
3090 
3091     // down the remaining lifetime of the particle
3092     if ( loc_pprt->lifetime_remaining > 0 ) loc_pprt->lifetime_remaining--;
3093 
3094     // down the continuous spawn timer
3095     if ( loc_pprt->contspawn_timer > 0 ) loc_pprt->contspawn_timer--;
3096 
3097     return pbdl_prt;
3098 }
3099 
3100 //--------------------------------------------------------------------------------------------
prt_update_ingame(prt_bundle_t * pbdl_prt)3101 prt_bundle_t * prt_update_ingame( prt_bundle_t * pbdl_prt )
3102 {
3103     /// @details BB@> update everything about a particle that does not depend on collisions
3104     ///               or interactions with characters
3105 
3106     obj_data_t * base_ptr;
3107     prt_t             * loc_pprt;
3108     pip_t             * loc_ppip;
3109 
3110     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3111     loc_pprt = pbdl_prt->prt_ptr;
3112     loc_ppip = pbdl_prt->pip_ptr;
3113     base_ptr = POBJ_GET_PBASE( loc_pprt );
3114 
3115     // determine whether the pbdl_prt->prt_ref is hidden
3116     loc_pprt->is_hidden = bfalse;
3117     if ( INGAME_CHR( loc_pprt->attachedto_ref ) )
3118     {
3119         loc_pprt->is_hidden = ChrList.lst[loc_pprt->attachedto_ref].is_hidden;
3120     }
3121 
3122     // nothing to do if the particle is hidden
3123     if ( loc_pprt->is_hidden ) return pbdl_prt;
3124 
3125     // clear out the attachment if the character doesn't exist at all
3126     if ( !DEFINED_CHR( loc_pprt->attachedto_ref ) )
3127     {
3128         loc_pprt->attachedto_ref = ( CHR_REF )MAX_CHR;
3129     }
3130 
3131     // figure out where the particle is on the mesh and update the particle states
3132     {
3133         // determine whether the pbdl_prt->prt_ref is hidden
3134         loc_pprt->is_hidden = bfalse;
3135         if ( INGAME_CHR( loc_pprt->attachedto_ref ) )
3136         {
3137             loc_pprt->is_hidden = ChrList.lst[loc_pprt->attachedto_ref].is_hidden;
3138         }
3139 
3140         loc_pprt->is_homing = loc_ppip->homing && !INGAME_CHR( loc_pprt->attachedto_ref ) && INGAME_CHR( loc_pprt->target_ref );
3141     }
3142 
3143     // figure out where the particle is on the mesh and update pbdl_prt->prt_ref states
3144     pbdl_prt = prt_update_do_water( pbdl_prt );
3145     if ( NULL == pbdl_prt || NULL == loc_pprt ) return pbdl_prt;
3146 
3147     // the following functions should not be done the first time through the update loop
3148     if ( 0 == update_wld ) return pbdl_prt;
3149 
3150     pbdl_prt = prt_update_animation( pbdl_prt );
3151     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3152 
3153     pbdl_prt = prt_update_dynalight( pbdl_prt );
3154     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3155 
3156     pbdl_prt = prt_update_timers( pbdl_prt );
3157     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3158 
3159     prt_do_contspawn( pbdl_prt );
3160     if ( NULL == pbdl_prt->prt_ptr ) return NULL;
3161 
3162     pbdl_prt = prt_do_bump_damage( pbdl_prt );
3163     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3164 
3165     base_ptr->update_count++;
3166 
3167     // If the particle is done updating, remove it from the game, but do not kill it
3168     if ( !loc_pprt->is_eternal && ( base_ptr->update_count > 0 && 0 == loc_pprt->lifetime_remaining ) )
3169     {
3170         end_one_particle_in_game( pbdl_prt->prt_ref );
3171     }
3172 
3173     return pbdl_prt;
3174 }
3175 
3176 //--------------------------------------------------------------------------------------------
prt_update_ghost(prt_bundle_t * pbdl_prt)3177 prt_bundle_t * prt_update_ghost( prt_bundle_t * pbdl_prt )
3178 {
3179     /// @details BB@> handle the case where the particle is still being diaplayed, but is no longer
3180     ///               in the game
3181 
3182     bool_t prt_visible;
3183 
3184     obj_data_t * base_ptr;
3185     prt_t             * loc_pprt;
3186     pip_t             * loc_ppip;
3187 
3188     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3189     loc_pprt = pbdl_prt->prt_ptr;
3190     loc_ppip = pbdl_prt->pip_ptr;
3191     base_ptr = POBJ_GET_PBASE( loc_pprt );
3192 
3193     // is this the right function?
3194     if ( !loc_pprt->is_ghost )
3195         return pbdl_prt;
3196 
3197     // is the prt visible
3198     prt_visible = ( loc_pprt->size > 0 ) && ( loc_pprt->inst.alpha > 0 ) && !loc_pprt->is_hidden;
3199 
3200     // are we done?
3201     if ( !prt_visible || base_ptr->frame_count > 0 )
3202     {
3203         prt_request_terminate( pbdl_prt->prt_ref );
3204         return NULL;
3205     }
3206 
3207     // clear out the attachment if the character doesn't exist at all
3208     if ( !DEFINED_CHR( loc_pprt->attachedto_ref ) )
3209     {
3210         loc_pprt->attachedto_ref = ( CHR_REF )MAX_CHR;
3211     }
3212 
3213     // determine whether the pbdl_prt->prt_ref is hidden
3214     loc_pprt->is_hidden = bfalse;
3215     if ( INGAME_CHR( loc_pprt->attachedto_ref ) )
3216     {
3217         loc_pprt->is_hidden = ChrList.lst[loc_pprt->attachedto_ref].is_hidden;
3218     }
3219 
3220     loc_pprt->is_homing = loc_ppip->homing && !INGAME_CHR( loc_pprt->attachedto_ref ) && INGAME_CHR( loc_pprt->target_ref );
3221 
3222     // the following functions should not be done the first time through the update loop
3223     if ( 0 == update_wld ) return pbdl_prt;
3224 
3225     pbdl_prt = prt_update_animation( pbdl_prt );
3226     if ( NULL == pbdl_prt || NULL == loc_pprt ) return NULL;
3227 
3228     pbdl_prt = prt_update_dynalight( pbdl_prt );
3229     if ( NULL == pbdl_prt || NULL == loc_pprt ) return NULL;
3230 
3231     if ( !loc_pprt->is_hidden )
3232     {
3233         base_ptr->update_count++;
3234     }
3235 
3236     return pbdl_prt;
3237 }
3238 
3239 //--------------------------------------------------------------------------------------------
prt_update(prt_bundle_t * pbdl_prt)3240 prt_bundle_t * prt_update( prt_bundle_t * pbdl_prt )
3241 {
3242     obj_data_t        * loc_pbase;
3243     prt_t             * loc_pprt, * tmp_pprt;
3244     pip_t             * loc_ppip;
3245     prt_environment_t * penviro;
3246 
3247     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return NULL;
3248     loc_pprt = pbdl_prt->prt_ptr;
3249     loc_ppip = pbdl_prt->pip_ptr;
3250     penviro  = &( loc_pprt->enviro );
3251     loc_pbase = POBJ_GET_PBASE( loc_pprt );
3252 
3253     // do the next step in the particle configuration
3254     tmp_pprt = prt_run_config( pbdl_prt->prt_ptr );
3255     if ( NULL == tmp_pprt ) { prt_bundle_ctor( pbdl_prt ); return NULL; }
3256 
3257     if ( tmp_pprt != pbdl_prt->prt_ptr )
3258     {
3259         // "new" particle, so re-validate the bundle
3260         prt_bundle_set( pbdl_prt, pbdl_prt->prt_ptr );
3261     }
3262 
3263     // if the bundle is no longer valid, return
3264     if ( NULL == pbdl_prt->prt_ptr || NULL == pbdl_prt->pip_ptr ) return pbdl_prt;
3265 
3266     // if the particle is no longer allocated, return
3267     if ( !ALLOCATED_PPRT( pbdl_prt->prt_ptr ) ) return pbdl_prt;
3268 
3269     // handle different particle states differently
3270     if ( loc_pprt->is_ghost )
3271     {
3272         // the particle is not on
3273         pbdl_prt = prt_update_ghost( pbdl_prt );
3274     }
3275     else
3276     {
3277         // the particle is on
3278         pbdl_prt = prt_update_ingame( pbdl_prt );
3279     }
3280 
3281     return pbdl_prt;
3282 }
3283 
3284 //--------------------------------------------------------------------------------------------
3285 //--------------------------------------------------------------------------------------------
prt_update_safe_raw(prt_t * pprt)3286 bool_t prt_update_safe_raw( prt_t * pprt )
3287 {
3288     bool_t retval = bfalse;
3289 
3290     BIT_FIELD hit_a_wall;
3291     float  pressure;
3292 
3293     if ( !ALLOCATED_PPRT( pprt ) ) return bfalse;
3294 
3295     hit_a_wall = prt_hit_wall( pprt, NULL, NULL, &pressure, NULL );
3296     if (( 0 == hit_a_wall ) && ( 0.0f == pressure ) )
3297     {
3298         pprt->safe_valid = btrue;
3299         pprt->safe_pos   = prt_get_pos( pprt );
3300         pprt->safe_time  = update_wld;
3301         pprt->safe_grid  = mesh_get_grid( PMesh, pprt->pos.x, pprt->pos.y );
3302 
3303         retval = btrue;
3304     }
3305 
3306     return retval;
3307 }
3308 
3309 //--------------------------------------------------------------------------------------------
prt_update_safe(prt_t * pprt,bool_t force)3310 bool_t prt_update_safe( prt_t * pprt, bool_t force )
3311 {
3312     Uint32 new_grid;
3313     bool_t retval = bfalse;
3314     bool_t needs_update = bfalse;
3315 
3316     if ( !ALLOCATED_PPRT( pprt ) ) return bfalse;
3317 
3318     if ( force || !pprt->safe_valid )
3319     {
3320         needs_update = btrue;
3321     }
3322     else
3323     {
3324         new_grid = mesh_get_grid( PMesh, pprt->pos.x, pprt->pos.y );
3325 
3326         if ( INVALID_TILE == new_grid )
3327         {
3328             if ( ABS( pprt->pos.x - pprt->safe_pos.x ) > GRID_FSIZE ||
3329                  ABS( pprt->pos.y - pprt->safe_pos.y ) > GRID_FSIZE )
3330             {
3331                 needs_update = btrue;
3332             }
3333         }
3334         else if ( new_grid != pprt->safe_grid )
3335         {
3336             needs_update = btrue;
3337         }
3338     }
3339 
3340     if ( needs_update )
3341     {
3342         retval = prt_update_safe_raw( pprt );
3343     }
3344 
3345     return retval;
3346 }
3347 
3348 //--------------------------------------------------------------------------------------------
3349 //--------------------------------------------------------------------------------------------
prt_update_pos(prt_t * pprt)3350 bool_t prt_update_pos( prt_t * pprt )
3351 {
3352     if ( !ALLOCATED_PPRT( pprt ) ) return bfalse;
3353 
3354     pprt->onwhichgrid  = mesh_get_grid( PMesh, pprt->pos.x, pprt->pos.y );
3355     pprt->onwhichblock = mesh_get_block( PMesh, pprt->pos.x, pprt->pos.y );
3356 
3357     // update whether the current character position is safe
3358     prt_update_safe( pprt, bfalse );
3359 
3360     // update the breadcrumb list (does not exist for particles )
3361     // prt_update_breadcrumb( pprt, bfalse );
3362 
3363     return btrue;
3364 }
3365 
3366 //--------------------------------------------------------------------------------------------
prt_set_pos(prt_t * pprt,fvec3_base_t pos)3367 bool_t prt_set_pos( prt_t * pprt, fvec3_base_t pos )
3368 {
3369     bool_t retval = bfalse;
3370 
3371     if ( !ALLOCATED_PPRT( pprt ) ) return retval;
3372 
3373     retval = btrue;
3374 
3375     if (( pos[kX] != pprt->pos.v[kX] ) || ( pos[kY] != pprt->pos.v[kY] ) || ( pos[kZ] != pprt->pos.v[kZ] ) )
3376     {
3377         memmove( pprt->pos.v, pos, sizeof( fvec3_base_t ) );
3378 
3379         retval = prt_update_pos( pprt );
3380     }
3381 
3382     return retval;
3383 }
3384