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