aboutsummaryrefslogtreecommitdiff
path: root/src/parsec/g_sfx.cpp
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
commitc068f22329d5cc722622a2183bbb22eef2093df7 (patch)
tree12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec/g_sfx.cpp
downloadopenparsec-c068f22329d5cc722622a2183bbb22eef2093df7.tar.xz
openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.zip
initial importHEADmain
Diffstat (limited to 'src/parsec/g_sfx.cpp')
-rw-r--r--src/parsec/g_sfx.cpp716
1 files changed, 716 insertions, 0 deletions
diff --git a/src/parsec/g_sfx.cpp b/src/parsec/g_sfx.cpp
new file mode 100644
index 0000000..df2a9d9
--- /dev/null
+++ b/src/parsec/g_sfx.cpp
@@ -0,0 +1,716 @@
+/*
+ * PARSEC - Special Effects
+ *
+ * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:25 $
+ *
+ * Orginally written by:
+ * Copyright (c) Markus Hadwiger <msh@parsec.org> 1997-1999
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// C library
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// compilation flags/debug support
+#include "config.h"
+#include "debug.h"
+
+// general definitions
+#include "general.h"
+#include "objstruc.h"
+#include "od_props.h"
+
+// global externals
+#include "globals.h"
+
+// subsystem headers
+#include "net_defs.h"
+#include "vid_defs.h"
+
+// mathematics header
+#include "utl_math.h"
+
+// particle types
+#include "parttype.h"
+
+// local module header
+#include "g_sfx.h"
+
+// proprietary module headers
+#include "con_aux.h"
+#include "e_color.h"
+#include "e_record.h"
+#include "h_supp.h"
+#include "obj_ctrl.h"
+#include "part_api.h"
+#include "part_def.h"
+
+
+// flags
+#define NO_SIZE_BOUNDS
+
+
+// particle size boundaries
+#ifdef NO_SIZE_BOUNDS
+ #define PRTSB_EXPLOSION PRT_NO_SIZEBOUND
+ #define PRTSB_ENERGYFIELD PRT_NO_SIZEBOUND
+ #define PRTSB_PROTSHIELD PRT_NO_SIZEBOUND
+ #define PRTSB_INVULSHIELD PRT_NO_SIZEBOUND
+#else
+ #define PRTSB_EXPLOSION partbitmap_size_bound
+ #define PRTSB_ENERGYFIELD partbitmap_size_bound
+ #define PRTSB_PROTSHIELD partbitmap_size_bound
+ #define PRTSB_INVULSHIELD partbitmap_size_bound
+#endif
+
+// geometry of particle explosion of ship
+#define PARTICLE_EXPLOSION_STARTRADIUS FIXED_TO_GEOMV( 0xa00 )
+#define PARTICLE_EXPLOSION_NUMPARTICLES 150
+#define PARTICLE_EXPLOSION_DURATION 400 //250 //400
+
+// properties of energy field
+#define ENERGY_FIELD_INITIAL_RADIUS FLOAT_TO_GEOMV( 0.0625f )
+#define ENERGY_FIELD_LIFETIME 10000
+
+// properties of protection shield
+#define PROTSHIELD_BITMAP BM_SHIELD2
+#define PROTSHIELD_COLOR 228
+#define PROTSHIELD_REF_Z SPHERE_REF_Z
+
+// if ship is hit its shield is displayed for this period of time
+#define SHIELD_FLASH_DURATION 40
+
+// properties of invulnerability shield
+#define MEGASHIELD_BITMAP BM_SHIELD1
+#define MEGASHIELD_COLOR 123
+#define MEGASHIELD_REF_Z SPHERE_REF_Z
+#define MEGASHIELD_LIFETIME MEGASHIELD_STRENGTH * DEFAULT_REFFRAME_FREQUENCY //20000
+
+// properties of propulsion fumes
+#define PROPFUMES_BITMAP BM_PROPFUMES1
+#define PROPFUMES_COLOR 3
+#define PROPFUMES_REF_Z 60.0f
+#define PROPFUMES_SIZE_BOUNDARY 2
+#define PROPFUMES_SCATTER_BOUNDARY 2
+#define PROPFUMES_DRIFT_SPEED 0.0016f
+
+
+
+// reference z values for particles -------------------------------------------
+//
+float sphere_explosion_ref_z = 1.0f;
+float protshield_ref_z = 1.0f;
+float megashield_ref_z = 1.0f;
+float propfumes_ref_z = 1.0f;
+
+
+// init reference z values for particles according to resolution --------------
+//
+void SFX_InitParticleSizes( float resoscale )
+{
+ sphere_explosion_ref_z = resoscale * SPHERE_EXPLOSION_REF_Z;
+ protshield_ref_z = resoscale * PROTSHIELD_REF_Z;
+ megashield_ref_z = resoscale * MEGASHIELD_REF_Z;
+ propfumes_ref_z = resoscale * PROPFUMES_REF_Z;
+}
+
+
+// create particle explosion; meant to be called only externally --------------
+//
+void SFX_ParticleExplosion( ShipObject *shippo )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ // calc explosion origin
+ Vertex3 origin;
+ FetchTVector( shippo->ObjPosition, &origin );
+
+ // some particle appearance properties
+ int bitmap = SPHERE_EXPLOSION_BM_INDX;
+ int color = SPHERE_EXPLOSION_COLOR;
+ float ref_z = sphere_explosion_ref_z;
+ int sizebound = PRTSB_EXPLOSION;
+ int lifetime = PARTICLE_EXPLOSION_DURATION;
+
+ pextinfo_s extinfo;
+ pextinfo_s *pextinfo = NULL;
+
+ if ( AUX_EXPLOSION_PARTICLES_EXTINFO ) {
+
+ // alter basic attributes
+ ref_z *= 2;
+ bitmap = iter_texrgba | iter_specularadd;
+// sizebound = PRT_NO_SIZEBOUND;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_pflare02();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo = &extinfo;
+ PRT_InitParticleExtInfo( pextinfo, pdef, NULL, NULL );
+ }
+
+ pdrwinfo_s drawinfo;
+ drawinfo.bmindx = bitmap;
+ drawinfo.pcolor = color;
+ drawinfo.ref_z = ref_z;
+ drawinfo.extinfo = pextinfo;
+ drawinfo.sizebnd = sizebound;
+
+ pcluster_s *cluster =
+ PRT_CreateParticleSphereObject( origin,
+ PARTICLE_EXPLOSION_STARTRADIUS,
+ SAT_EXPLODING,
+ PARTICLE_EXPLOSION_NUMPARTICLES,
+ lifetime, &drawinfo,
+ GetObjectOwner( shippo ) );
+}
+
+
+// create an energy field -----------------------------------------------------
+//
+void SFX_CreateEnergyField( Vertex3& origin )
+{
+ // some particle appearance properties
+ int bitmap = SPHERE_BM_INDX;
+ int color = SPHERE_PARTICLE_COLOR;
+extern float sphere_ref_z;
+ float ref_z = sphere_ref_z;
+ int sizebound = PRTSB_ENERGYFIELD;
+ int lifetime = ENERGY_FIELD_LIFETIME;
+
+ pextinfo_s extinfo;
+ pextinfo_s *pextinfo = NULL;
+
+ if ( AUX_ENERGYFIELD_PARTICLES_EXTINFO ) {
+
+ // alter basic attributes
+ ref_z *= 4;
+ bitmap = iter_texrgba | iter_specularadd;
+// sizebound = PRT_NO_SIZEBOUND;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_pflare03();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo = &extinfo;
+ PRT_InitParticleExtInfo( pextinfo, pdef, NULL, NULL );
+ }
+
+ pdrwinfo_s drawinfo;
+ drawinfo.bmindx = bitmap;
+ drawinfo.pcolor = color;
+ drawinfo.ref_z = ref_z;
+ drawinfo.extinfo = pextinfo;
+ drawinfo.sizebnd = sizebound;
+
+ pcluster_s *cluster =
+ PRT_CreateParticleSphereObject( origin,
+ ENERGY_FIELD_INITIAL_RADIUS,
+ SAT_ENERGYFIELD_SPHERE,
+ SPHERE_PARTICLES,
+ lifetime, &drawinfo,
+ LocalPlayerId );
+ // maintain particle extra count
+ CurrentNumPrtExtras++;
+}
+
+
+// show protective shield surrounding a ship for a short period of time -------
+//
+void SFX_FlashProtectiveShield( ShipObject *shippo )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ // dont't flash shield if already any shield-cluster attached
+ if ( PRT_ObjectHasAttachedClustersOfType( shippo, SAT_NO_ANIMATION ) )
+ return;
+
+ // some particle appearance properties
+ int bitmap = PROTSHIELD_BITMAP;
+ int color = PROTSHIELD_COLOR;
+ float ref_z = protshield_ref_z;
+ int sizebound = PRTSB_PROTSHIELD;
+ int lifetime = SHIELD_FLASH_DURATION;
+
+ pextinfo_s extinfo;
+ pextinfo_s *pextinfo = NULL;
+
+ if ( AUX_PROTSHIELD_PARTICLES_EXTINFO ) {
+
+ // alter basic attributes
+ ref_z *= 4;
+ bitmap = iter_texrgba | iter_specularadd;
+// sizebound = PRT_NO_SIZEBOUND;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_pflare04();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo = &extinfo;
+ PRT_InitParticleExtInfo( pextinfo, pdef, NULL, NULL );
+ }
+
+ pdrwinfo_s drawinfo;
+ drawinfo.bmindx = bitmap;
+ drawinfo.pcolor = color;
+ drawinfo.ref_z = ref_z;
+ drawinfo.extinfo = pextinfo;
+ drawinfo.sizebnd = sizebound;
+
+ // create ship centered particle sphere without animation
+ basesphere_pcluster_s *cluster =
+ PRT_CreateObjectCenteredSphere( shippo, shippo->BoundingSphere,
+ SAT_NO_ANIMATION,
+ SPHERE_PARTICLES,
+ lifetime, &drawinfo,
+ GetObjectOwner( shippo ) );
+}
+
+
+// create particle sphere for invulnerability shield (megashield) -------------
+//
+PRIVATE
+basesphere_pcluster_s *CreateMegaSphere( ShipObject *shippo )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ // some particle appearance properties
+ int bitmap = MEGASHIELD_BITMAP;
+ int color = MEGASHIELD_COLOR;
+ float ref_z = megashield_ref_z;
+ int sizebound = PRTSB_INVULSHIELD;
+ int lifetime = MEGASHIELD_LIFETIME;
+
+ pextinfo_s extinfo;
+ pextinfo_s *pextinfo = NULL;
+
+ if ( AUX_MEGASHIELD_PARTICLES_EXTINFO ) {
+
+ // alter basic attributes
+ ref_z *= 4;
+ bitmap = iter_texrgba | iter_specularadd;
+// sizebound = PRT_NO_SIZEBOUND;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_pflare02();//05();
+ if ( pdef == NULL )
+ return NULL;
+
+ // create pextinfo
+ pextinfo = &extinfo;
+ PRT_InitParticleExtInfo( pextinfo, pdef, NULL, NULL );
+ }
+
+ pdrwinfo_s drawinfo;
+ drawinfo.bmindx = bitmap;
+ drawinfo.pcolor = color;
+ drawinfo.ref_z = ref_z;
+ drawinfo.extinfo = pextinfo;
+ drawinfo.sizebnd = sizebound;
+
+ basesphere_pcluster_s *cluster =
+ PRT_CreateObjectCenteredSphere( shippo, shippo->BoundingSphere,
+ SAT_MEGASHIELD_SPHERE,
+ SPHERE_PARTICLES,
+ lifetime, &drawinfo,
+ GetObjectOwner( shippo ) );
+ return cluster;
+}
+
+
+// enable invulnerability function (mega-shield) ------------------------------
+//
+int SFX_EnableInvulnerabilityShield( ShipObject *shippo )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ if ( NET_ConnectedPEER() ) {
+ // check if enough space in RE_List
+ if ( !NET_RmEvAllowed( RE_PARTICLEOBJECT ) )
+ return FALSE;
+ }
+
+ basesphere_pcluster_s *attached = (basesphere_pcluster_s *) PRT_ObjectHasAttachedClustersOfType( shippo, SAT_MEGASHIELD_SPHERE );
+
+ // don't attach more than one, only reset lifetime
+ if ( attached != NULL ) {
+ attached->lifetime = attached->max_life;
+ return TRUE;
+ }
+
+ // create particle sphere
+ CreateMegaSphere( shippo );
+
+ if ( NET_ConnectedPEER() ) {
+ // insert remote event
+ Vertex3 dummyorigin;
+ NET_RmEvParticleObject( POBJ_MEGASHIELD, dummyorigin );
+ }
+
+ return TRUE;
+}
+
+
+// fetch invulnerability shield and info --------------------------------------
+//
+basesphere_pcluster_s *SFX_FetchInvulnerabilityShield( ShipObject *shippo, float *strength )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ basesphere_pcluster_s *cluster = (basesphere_pcluster_s *)
+ PRT_ObjectHasAttachedClustersOfType( shippo, SAT_MEGASHIELD_SPHERE );
+
+ if ( cluster == NULL ) {
+ return NULL;
+ }
+
+ if ( strength != NULL ) {
+ *strength = (float)cluster->lifetime / (float)cluster->max_life;
+ }
+
+ return cluster;
+}
+
+
+// enable invulnerability function (mega-shield) for remote players -----------
+//
+void SFX_RemoteEnableInvulnerabilityShield( int owner )
+{
+ // fetch pointer to remote player's ship
+ ShipObject *shippo = NET_FetchOwnersShip( owner );
+ ASSERT( shippo != NULL );
+ ASSERT( shippo != MyShip );
+
+ basesphere_pcluster_s *attached = (basesphere_pcluster_s *)
+ PRT_ObjectHasAttachedClustersOfType( shippo, SAT_MEGASHIELD_SPHERE );
+
+ // don't attach more than one, only reset lifetime
+ if ( attached != NULL ) {
+ attached->lifetime = MEGASHIELD_LIFETIME;
+ return;
+ }
+
+ // create particle sphere
+ CreateMegaSphere( shippo );
+}
+
+
+// create hull impact particles -----------------------------------------------
+//
+void SFX_HullImpact( ShipObject *shippo, Vertex3 *impact, Plane3 *plane )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( impact != NULL );
+ ASSERT( plane != NULL );
+
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ // some particle appearance properties
+ int bitmap = PROTSHIELD_BITMAP;
+ int color = PROTSHIELD_COLOR;
+ float ref_z = protshield_ref_z;
+ int sizebound = PRTSB_PROTSHIELD;
+ int lifetime = 20000; //SHIELD_FLASH_DURATION;
+
+ pextinfo_s extinfo;
+ pextinfo_s *pextinfo = NULL;
+
+ if ( AUX_PROTSHIELD_PARTICLES_EXTINFO ) {
+
+ // alter basic attributes
+ ref_z *= 4;
+ bitmap = iter_texrgba | iter_specularadd;
+// sizebound = PRT_NO_SIZEBOUND;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_pflare04();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo = &extinfo;
+ PRT_InitParticleExtInfo( pextinfo, pdef, NULL, NULL );
+ }
+
+ Vertex3 world_space;
+ MtxVctMUL( shippo->ObjPosition, impact, &world_space );
+
+ Vector3 dirvec_obj;
+ dirvec_obj.X = plane->X;
+ dirvec_obj.Y = plane->Y;
+ dirvec_obj.Z = plane->Z;
+
+ Vector3 dirvec;
+ MtxVctMULt( shippo->ObjPosition, &dirvec_obj, &dirvec );
+
+ geomv_t speed = FLOAT_TO_GEOMV( 0.01 );
+ dirvec.X = GEOMV_MUL( dirvec.X, speed );
+ dirvec.Y = GEOMV_MUL( dirvec.Y, speed );
+ dirvec.Z = GEOMV_MUL( dirvec.Z, speed );
+
+ particle_s particle;
+ PRT_InitParticle( particle, bitmap, color, sizebound,
+ ref_z, &world_space, &dirvec,
+ lifetime, GetObjectOwner( shippo ), pextinfo );
+ PRT_CreateLinearParticle( particle );
+}
+
+
+// create propulsion fumes for spaceship --------------------------------------
+//
+void SFX_CreatePropulsionParticles( ShipObject *shippo )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+//TEST:
+//FIXME:
+//////////////////////
+return;
+//////////////////////
+
+ int bitmap = PROPFUMES_BITMAP;
+ int color = PROPFUMES_COLOR;
+ float ref_z = propfumes_ref_z;
+
+ // alter basic attributes
+// ref_z /= 2;
+ bitmap = iter_rgbtexa | iter_alphablend;
+ color = 0;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_psmoke02();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo_s extinfo;
+ PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL );
+
+ // first particle ---
+ Vertex3 object_space;
+ object_space.X = shippo->Fume_X[ 0 ];
+ object_space.Y = shippo->Fume_Y;
+ object_space.Z = shippo->Fume_Z;
+
+ Vertex3 world_space;
+ MtxVctMUL( shippo->ObjPosition, &object_space, &world_space );
+
+ Vector3 dirvec;
+ fixed_t speed = shippo->FumeSpeed;
+ DirVctMUL( shippo->ObjPosition, FIXED_TO_GEOMV( speed ), &dirvec );
+
+ particle_s particle;
+ PRT_InitParticle( particle, bitmap, color, PROPFUMES_SIZE_BOUNDARY,
+ ref_z, &world_space, &dirvec,
+ shippo->FumeLifeTime, LocalPlayerId,
+// NULL );
+ &extinfo );
+ PRT_CreateLinearParticle( particle );
+
+ // second particle ---
+ object_space.X = shippo->Fume_X[ 3 ];
+ MtxVctMUL( shippo->ObjPosition, &object_space, &world_space );
+
+ PRT_InitParticle( particle, bitmap, color, PROPFUMES_SIZE_BOUNDARY,
+ ref_z, &world_space, &dirvec,
+ shippo->FumeLifeTime, LocalPlayerId,
+// NULL );
+ &extinfo );
+ PRT_CreateLinearParticle( particle );
+}
+
+
+// create propulsion fumes for missiles ---------------------------------------
+//
+void SFX_MissilePropulsion( MissileObject *missile )
+{
+ ASSERT( missile != NULL );
+ ASSERT( OBJECT_TYPE_MISSILE( missile ) );
+
+ int bitmap = PROPFUMES_BITMAP;
+ int color = PROPFUMES_COLOR;
+ float ref_z = propfumes_ref_z;
+ int sizebound = PROPFUMES_SIZE_BOUNDARY;
+ int lifetime = PROP_FUME_LIFETIME;
+
+ // alter basic attributes
+// ref_z /= 2;
+ bitmap = iter_rgbtexa | iter_alphablend;
+ color = 0;
+ sizebound = PRT_NO_SIZEBOUND;
+ lifetime = 1800;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_psmoke01();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo_s extinfo;
+ PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL );
+
+ //TODO:
+ // make propulsion framerate independent
+
+ // create particle ---
+ Vertex3 object_space;
+ int x = ( PROPFUMES_SCATTER_BOUNDARY ) - ( RAND() & ( PROPFUMES_SCATTER_BOUNDARY * 2 ) );
+ int y = ( PROPFUMES_SCATTER_BOUNDARY ) - ( RAND() & ( PROPFUMES_SCATTER_BOUNDARY * 2 ) );
+ object_space.X = INT_TO_GEOMV( x );
+ object_space.Y = INT_TO_GEOMV( y );
+ object_space.Z = GEOMV_0; //PROP_FUME_START_Z;
+
+ Vertex3 world_space;
+ MtxVctMUL( missile->ObjPosition, &object_space, &world_space );
+
+ Vertex3 drift;
+ object_space.X = FLOAT_TO_GEOMV( PROPFUMES_DRIFT_SPEED * x );
+ object_space.Y = FLOAT_TO_GEOMV( PROPFUMES_DRIFT_SPEED * y );
+ object_space.Z = GEOMV_0; //PROP_FUME_START_Z;
+
+ MtxVctMULt( missile->ObjPosition, &object_space, &drift );
+
+ Vector3 dirvec;
+ fixed_t speed = PROP_FUME_SPEED; //missile->Speed/6;
+
+ DirVctMUL( missile->ObjPosition, FIXED_TO_GEOMV( speed ), &dirvec );
+ dirvec.X += drift.X;
+ dirvec.Y += drift.Y;
+ dirvec.Z += drift.Z;
+
+ particle_s particle;
+ PRT_InitParticle( particle, bitmap, color, sizebound,
+ ref_z, &world_space, &dirvec,
+ lifetime, LocalPlayerId,
+// NULL );
+ &extinfo );
+ PRT_CreateLinearParticle( particle );
+}
+
+
+// create stargate ------------------------------------------------------------
+//
+void SFX_CreateStargate( ShipObject *shippo )
+{
+ ASSERT( shippo != NULL );
+ ASSERT( OBJECT_TYPE_SHIP( shippo ) );
+
+ // basic attributes
+ float ref_z = 2000.0f;
+ int bitmap = iter_texrgba | iter_specularadd;
+ int color = 0;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_stargate();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo_s extinfo;
+ PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL );
+
+ // position in object space
+ Vertex3 object_space;
+ object_space.X = GEOMV_0;
+ object_space.Y = GEOMV_0;
+ object_space.Z = INT_TO_GEOMV( ( shippo == MyShip ) ? 50 : 0 );
+
+ Vertex3 world_space;
+ MtxVctMUL( shippo->ObjPosition, &object_space, &world_space );
+
+ Vector3 dirvec;
+ dirvec.X = GEOMV_0;
+ dirvec.Y = GEOMV_0;
+ dirvec.Z = GEOMV_0;
+
+ particle_s particle;
+ PRT_InitParticle( particle, bitmap, color, PRT_NO_SIZEBOUND,
+ ref_z, &world_space, &dirvec,
+ 32*40*2, LocalPlayerId,
+ &extinfo );
+ PRT_CreateLinearParticle( particle );
+}
+
+
+// extra generation animation -------------------------------------------------
+//
+void SFX_CreateExtra( ExtraObject *extra )
+{
+ ASSERT( extra != NULL );
+ ASSERT( OBJECT_TYPE_EXTRA( extra ) );
+
+ // basic attributes
+ float ref_z = 350.0f;
+ int bitmap = iter_texrgba | iter_specularadd;
+ int color = 0;
+
+ // fetch pdef
+ pdef_s *pdef = PDEF_extragen();
+ if ( pdef == NULL )
+ return;
+
+ // create pextinfo
+ pextinfo_s extinfo;
+ PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL );
+
+ // position in object space
+ Vertex3 object_space;
+ object_space.X = GEOMV_0;
+ object_space.Y = GEOMV_0;
+ object_space.Z = GEOMV_0;
+
+ Vertex3 world_space;
+ MtxVctMUL( extra->ObjPosition, &object_space, &world_space );
+
+ Vector3 dirvec;
+ dirvec.X = GEOMV_0;
+ dirvec.Y = GEOMV_0;
+ dirvec.Z = GEOMV_0;
+
+ particle_s particle;
+ PRT_InitParticle( particle, bitmap, color, PRT_NO_SIZEBOUND,
+ ref_z, &world_space, &dirvec,
+ 32*40*2, LocalPlayerId,
+ &extinfo );
+ PRT_CreateLinearParticle( particle );
+}
+
+
+// extra vanishing animation --------------------------------------------------
+//
+void SFX_VanishExtra( ExtraObject *extra )
+{
+ SFX_CreateExtra( extra );
+}
+
+
+