/* * PARSEC - World representation * * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ * * Orginally written by: * Copyright (c) Clemens Beer 2002 * * 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 #include #include #include // compilation flags/debug support #include "config.h" #include "debug.h" // general definitions #include "general.h" #include "objstruc.h" // global externals #include "globals.h" // subsystem & headers #include "net_defs.h" // mathematics header #include "utl_math.h" // local module header #include "parttype.h" #include "e_world.h" // proprietary module headers #include "net_game_sv.h" #include "net_util.h" #include "e_simulator.h" #include "e_colldet.h" #include "g_player.h" #include "g_main_sv.h" // local module header #include "obj_cust.h" // flags ---------------------------------------------------------------------- // //#define HACKED_SHIPOBJECT_INSTANTIATION // undefine macros used for transition ---------------------------------------- // #undef CreateObject #undef PShipObjects #undef LaserObjects #undef MisslObjects #undef ExtraObjects #undef CustmObjects #define PShipObjects m_PShipObjects #define LaserObjects m_LaserObjects #define MisslObjects m_MisslObjects #define ExtraObjects m_ExtraObjects #define CustmObjects m_CustmObjects // flag if initialization of particle system already done --------------------- // static int particle_init_done = FALSE; // reference z values for particles ------------------------------------------- // float sphere_ref_z = 1.0f; float lightning_ref_z = 1.0f; float photon_ref_z = 1.0f; void LinearParticleCollision( linear_pcluster_s *cluster, int pid ); // flags #define USE_Z_BUFFER // use z buffer to draw particles //#define STORE_MAX_Z_VALS // log max/min z values //#define USER_SPHERE_CREATION // enable user sphere creation (console) #define DO_CLUSTER_CULLING // enable culling of whole clusters #define USE_NEW_CULLING_CODE // use new culling function //#define USE_BOUNDING_BOX_CULLING // cull bounding box instead of sphere #define ENABLE_PARTICLE_TRIANGLES // enable triangle drawing for particles // bitmap size below which it is replaced by a square of a single color #define BITMAP_SIZE_BOUNDARY 3 // typical lower boundary of particle bitmap size int partbitmap_size_bound = BITMAP_SIZE_BOUNDARY; pcluster_s *Particles = NULL; pcluster_s *CurLinearCluster = NULL; pcluster_s *CustomDrawCluster = NULL; // array of registered particle definitions ----------------------------------- // PUBLIC int NumParticleDefinitions = 0; PUBLIC pdefref_s ParticleDefinitions[ MAX_PARTICLE_DEFS ]; // ---------------------------------------------------------------------------- // int E_World::_CountGenObjects( GenObject* walklist ) { int listlength; listlength = 0; for ( ; walklist; walklist = walklist->NextObj ) listlength++; return listlength; } // free memory occupied by an object ------------------------------------------ // void E_World::_FreeObjectMem( GenObject* objectpo ) { //NOTE: // this function should only be used // from within OBJ_xx modules. ASSERT( objectpo != NULL ); switch ( objectpo->ObjectType & TYPELISTMASK ) { case PSHIP_LIST_NO: // ensure duration weapons are properly killed //KillDurationWeapons( (ShipObject *) objectpo ); break; case LASER_LIST_NO: // no special handling break; case MISSL_LIST_NO: // no special handling break; case EXTRA_LIST_NO: // for extras maintain count m_nCurrentNumExtras--; break; case CUSTM_LIST_NO: // for custom objects invoke destructor _InvokeCustomDestructor( objectpo ); break; } // free attached particle clusters #ifdef _SERVER_HAS_PARTICLE_CODE //FIXME: CBX: uncomment this, when particle subsys is integrated into servercode PRT_FreeAttachedClusterList( objectpo ); #endif // _SERVER_HAS_PARTICLE_CODE #ifndef INSTANTIATE_FACE_STRUCTURES /*//FIXME: MSH ???? // non-virtual objects may have additional instance data dword objclass = objectpo->ObjectClass; if ( objclass != CLASS_ID_INVALID ) { // make sure we take the base lod Face* basefaces = objectpo->FaceList; if ( objectpo->NumLodObjects > 0 ) { ASSERT( objectpo->LodObjects != NULL ); basefaces = objectpo->LodObjects[ 0 ].LodObject->FaceList; } // free instanced face data if attached separately ASSERT( objclass < NumObjClasses ); if ( basefaces != ObjClasses[ objclass ]->FaceList ) { FREEMEM( basefaces ); } } */ #endif FREEMEM( objectpo ); } // invoke constructor of custom object ---------------------------------------- // void E_World::_InvokeCustomConstructor( GenObject* objectpo ) { ASSERT( OBJECT_TYPE_CUSTOM( objectpo ) ); CustomObject* cobj = (CustomObject *) objectpo; if ( cobj->callback_instant != NULL ) { (*cobj->callback_instant)( cobj ); } } // invoke destructor of custom object ----------------------------------------- // void E_World::_InvokeCustomDestructor( GenObject* objectpo ) { ASSERT( OBJECT_TYPE_CUSTOM( objectpo ) ); CustomObject* cobj = (CustomObject *) objectpo; if ( cobj->callback_destroy != NULL ) { (*cobj->callback_destroy)( cobj ); } } // kill all instances of passed in object class (search only one list) -------- // int E_World::_KillClassInstancesFromList( int objclass, GenObject *listpo ) { ASSERT( listpo != NULL ); #ifdef PARSEC_CLIENT ASSERT( listpo != MyShip ); #endif // PARSEC_CLIENT GenObject *delpo = listpo; for ( int killcount = 0; ; killcount++ ) { while ( ( delpo->NextObj != NULL ) && ( delpo->NextObj->ObjectClass != (dword)objclass ) ) delpo = delpo->NextObj; if ( delpo->NextObj == NULL ) return killcount; ASSERT( delpo->NextObj ); #ifdef PARSEC_CLIENT ASSERT( delpo->NextObj != MyShip ); #endif // PARSEC_CLIENT GenObject *temppo = delpo->NextObj->NextObj; FreeObjectMem( delpo->NextObj ); delpo->NextObj = temppo; } // never reached ASSERT( 0 ); } // standard ctor -------------------------------------------------------------- // E_World::E_World() { m_PShipObjects = NULL; m_LaserObjects = NULL; m_MisslObjects = NULL; m_ExtraObjects = NULL; m_CustmObjects = NULL; m_last_summoned_objectid = 0; // current and maximum number of extras m_nCurrentNumExtras = 0; m_nCurrentNumPrtExtras = 0; //MaxNumExtras = 20; NextObjNumber = 1; MaxNumShots = 0; NumShots = 0; _InitObjCtrl(); } // correct pointers that have moved after object was instantiated ------------- // void E_World::OBJ_CorrectObjectInstance( GenObject *dstobj, GenObject *srcobj ) { ASSERT( dstobj != NULL ); ASSERT( srcobj != NULL ); #ifdef PARSEC_CLIENT ptrdiff_t pdiff = (char*)dstobj - (char*)srcobj; if ( dstobj->FaceAnimStates != NULL ) { dstobj->FaceAnimStates = (FaceAnimState *) ( (char*)dstobj->FaceAnimStates + pdiff ); } if ( dstobj->VtxAnimStates != NULL ) { dstobj->VtxAnimStates = (VtxAnimState *) ( (char*)dstobj->VtxAnimStates + pdiff ); } #endif // PARSEC_CLIENT } // kill object contained in specific object list (search only one list) ------- // int E_World::KillSpecificObject( dword objno, GenObject *listpo ) { ASSERT( listpo != NULL ); #ifdef PARSEC_CLIENT ASSERT( listpo != MyShip ); #endif // PARSEC_CLIENT GenObject *delpo = listpo; while ( ( delpo->NextObj != NULL ) && ( delpo->NextObj->ObjectNumber != objno ) ) delpo = delpo->NextObj; if ( delpo->NextObj == NULL ) return 0; ASSERT( delpo->NextObj ); #ifdef PARSEC_CLIENT ASSERT( delpo->NextObj != MyShip ); #endif // PARSEC_CLIENT GenObject *temppo = delpo->NextObj->NextObj; FreeObjectMem( delpo->NextObj ); delpo->NextObj = temppo; return 1; } // kill a specific ship object from a list ------------------------------------ // int E_World::KillSpecificShipObject( dword objno ) { return KillSpecificObject( objno, m_PShipObjects ); } // return pointer to first "real" ship in ship-objects list ------------------- // ShipObject* E_World::FetchFirstShip() { // fetch head of ship-objects list ShipObject* head = (ShipObject *) m_PShipObjects->NextObj; #ifdef PARSEC_CLIENT // if local ship is head of list return next object if ( head == MyShip ) { head = (ShipObject *) head->NextObj; } #endif // PARSEC_CLIENT return head; } // fetch object contained in specific object list ----------------------------- // GenObject* E_World::FetchSpecificObject( dword objno, GenObject* listpo ) { ASSERT( listpo != NULL ); GenObject* scan = listpo->NextObj; while ( ( scan != NULL ) && ( scan->ObjectNumber != objno ) ) scan = scan->NextObj; #ifdef PARSEC_CLIENT // prevent fetching of local ship if ( scan == MyShip ) scan = NULL; #endif // PARSEC_CLIENT // return found object or NULL return scan; } // fetch object via object id (search in all object lists) -------------------- // GenObject* E_World::FetchObject( dword objno ) { GenObject* obj; obj = FetchSpecificObject( objno, m_PShipObjects ); if ( obj != NULL ) return obj; obj = FetchSpecificObject( objno, m_LaserObjects ); if ( obj != NULL ) return obj; obj = FetchSpecificObject( objno, m_MisslObjects ); if ( obj != NULL ) return obj; obj = FetchSpecificObject( objno, m_ExtraObjects ); if ( obj != NULL ) return obj; obj = FetchSpecificObject( objno, m_CustmObjects ); return obj; } // free memory occupied by an object ------------------------------------------ // void E_World::FreeObjectMem( GenObject *objectpo ) { //NOTE: // this function should only be used // from within OBJ_xx modules. ASSERT( objectpo != NULL ); switch ( objectpo->ObjectType & TYPELISTMASK ) { case PSHIP_LIST_NO: #ifdef PARSEC_CLIENT // ensure duration weapons are properly killed KillDurationWeapons( (ShipObject *) objectpo ); #endif // PARSEC_CLIENT break; case LASER_LIST_NO: // no special handling break; case MISSL_LIST_NO: // no special handling break; case EXTRA_LIST_NO: // for extras maintain count m_nCurrentNumExtras--; break; case CUSTM_LIST_NO: // for custom objects invoke destructor _InvokeCustomDestructor( objectpo ); break; } #ifdef PARSEC_CLIENT // free attached particle clusters PRT_FreeAttachedClusterList( objectpo ); #ifndef INSTANTIATE_FACE_STRUCTURES /*//FIXME: // non-virtual objects may have additional instance data dword objclass = objectpo->ObjectClass; if ( objclass != CLASS_ID_INVALID ) { // make sure we take the base lod Face *basefaces = objectpo->FaceList; if ( objectpo->NumLodObjects > 0 ) { ASSERT( objectpo->LodObjects != NULL ); basefaces = objectpo->LodObjects[ 0 ].LodObject->FaceList; } // free instanced face data if attached separately ASSERT( objclass < NumObjClasses ); if ( basefaces != ObjClasses[ objclass ]->FaceList ) { FREEMEM( basefaces ); } } */ #endif // INSTANTIATE_FACE_STRUCTURES #endif // PARSEC_CLIENT FREEMEM( objectpo ); } // free all object memory blocks contained in specific list ------------------- // int E_World::FreeObjList( GenObject* listpo ) { //NOTE: // in addition to FreeObjects() this is also // used by CON_ACT::AcDestroyObjectList(). ASSERT( listpo != NULL ); #ifdef PARSEC_CLIENT ASSERT( listpo != MyShip ); #endif // PARSEC_CLIENT GenObject* delpo = listpo; while ( delpo->NextObj != NULL ) { ASSERT( delpo->NextObj ); #ifdef PARSEC_CLIENT ASSERT( delpo->NextObj != MyShip ); #endif // PARSEC_CLIENT GenObject* temppo = delpo->NextObj->NextObj; _FreeObjectMem( delpo->NextObj ); delpo->NextObj = temppo; } return 1; } // free all object memory blocks ---------------------------------------------- // int E_World::FreeObjects() { //NOTE: // this function is called every time the // game loop is entered (G_BOOT::GameInit()) // and by CON_ACT::AcPrepareDataRestore() to // remove all objects. int rc = 0; rc += FreeObjList( m_PShipObjects ); rc += FreeObjList( m_LaserObjects ); rc += FreeObjList( m_MisslObjects ); rc += FreeObjList( m_ExtraObjects ); rc += FreeObjList( m_CustmObjects ); return ( rc == 5 ); } // free all objects and particles --------------------------------------------- // void E_World::KillAllObjects() { //NOTE: // the order of the following calls is crucial. // 3-D objects MUST BE freed before particle objects. FreeObjects(); #ifdef _SERVER_HAS_PARTICLE_CODE FreeParticles(); #endif // _SERVER_HAS_PARTICLE_CODE } // kill all instances of passed in object class (search in all object lists) -- // int E_World::KillClassInstances( int objclass ) { //NOTE: // this function is used by CON_LOAD::ConLoadObject() // to remove objects prior to overloading them. int killcount = 0; killcount += _KillClassInstancesFromList( objclass, m_PShipObjects ); killcount += _KillClassInstancesFromList( objclass, m_LaserObjects ); killcount += _KillClassInstancesFromList( objclass, m_MisslObjects ); killcount += _KillClassInstancesFromList( objclass, m_ExtraObjects ); killcount += _KillClassInstancesFromList( objclass, m_CustmObjects ); return killcount; } // ---------------------------------------------------------------------------- //FIXME: does this belong here ? void E_World::IncreaseShotCounter() { NumShots++; if ( NumShots > MaxNumShots ) { MaxNumShots = NumShots; } } // ---------------------------------------------------------------------------- //FIXME: does this belong here ? void E_World::DecreaseShotCounter() { NumShots--; } // init object maintenance structures ----------------------------------------- // void E_World::_InitObjCtrl() { //NOTE: (client) // this function gets called exactly once, // from G_BOOT::GameBoot(). //NOTE: (server) // this function gets called exactly once, // from E_World::E_World() static int init_valid = TRUE; if ( init_valid ) { init_valid = FALSE; // allocate dummy heads of object lists PShipObjects = (ShipObject *) ALLOCMEM( sizeof( GenObject ) ); LaserObjects = (LaserObject *) ALLOCMEM( sizeof( GenObject ) ); MisslObjects = (MissileObject *) ALLOCMEM( sizeof( GenObject ) ); ExtraObjects = (ExtraObject *) ALLOCMEM( sizeof( GenObject ) ); CustmObjects = (CustomObject *) ALLOCMEM( sizeof( GenObject ) ); #ifdef PARSEC_SERVER // check if memory allocation failed if ( ( PShipObjects == NULL ) || ( LaserObjects == NULL ) || ( MisslObjects == NULL ) || ( ExtraObjects == NULL ) || ( CustmObjects == NULL ) ) { OUTOFMEM( 0 ); } #else // !PARSEC_SERVER VObjList = (GenObject *) ALLOCMEM( sizeof( GenObject ) ); // check if memory allocation failed if ( ( PShipObjects == NULL ) || ( LaserObjects == NULL ) || ( MisslObjects == NULL ) || ( ExtraObjects == NULL ) || ( CustmObjects == NULL ) || ( VObjList == NULL ) ) { OUTOFMEM( 0 ); } #endif // !PARSEC_SERVER // initialize dummy heads of object lists memset( PShipObjects, 0, sizeof( GenObject ) ); memset( LaserObjects, 0, sizeof( GenObject ) ); memset( MisslObjects, 0, sizeof( GenObject ) ); memset( ExtraObjects, 0, sizeof( GenObject ) ); memset( CustmObjects, 0, sizeof( GenObject ) ); #ifdef PARSEC_CLIENT memset( VObjList, 0, sizeof( GenObject ) ); #endif // PARSEC_CLIENT } else { // never happens ASSERT( 0 ); } } // create virtual (type-only) instance of custom object ----------------------- // CustomObject *E_World::CreateVirtualObject( dword objtypeid, dword dwOwner ) { ASSERT( TYPEID_TYPE_CUSTOM( objtypeid ) ); if ( !TYPEID_TYPE_CUSTOM( objtypeid ) ) return NULL; // create new object header size_t custsize = OBJ_FetchCustomTypeSize( objtypeid ); CustomObject *newinstance = (CustomObject *) ALLOCMEM( custsize ); if ( newinstance == NULL ) OUTOFMEM( "no mem for custom object." ); // GenObject::NumVerts==0 ensures object // will be skipped by BuildVisList() memset( newinstance, 0, custsize ); // init fields that must be valid newinstance->ObjectType = objtypeid; newinstance->ObjectClass = CLASS_ID_INVALID; newinstance->InstanceSize = custsize; // would normally have been done on class loading OBJ_InitCustomType( newinstance ); // set object number newinstance->ObjectNumber = NextObjNumber; newinstance->HostObjNumber = CreateGlobalObjId( NextObjNumber, dwOwner ); NextObjNumber++; // invoke constructor if ( newinstance->callback_instant != NULL ) { (*newinstance->callback_instant)( newinstance ); } // append object at head of list newinstance->NextObj = CustmObjects->NextObj; CustmObjects->NextObj = newinstance; return newinstance; } // create object of class at position with orientation GenObject* E_World::CreateObject( int objclass, const Xmatrx startmatrx, dword dwOwner ) { #ifdef HACKED_SHIPOBJECT_INSTANTIATION GenObject* newinstance = (ShipObject*)ALLOCMEM( sizeof( ShipObject ) ); return newinstance; #else // !HACKED_SHIPOBJECT_INSTANTIATION ASSERT( objclass >= 0 ); //ASSERT( objclass < NumObjClasses ); // check if class number is valid //if ( ( objclass < 0 ) || ( objclass >= NumObjClasses ) ) // return NULL; size_t allocinstancesize = ObjClasses[ objclass ]->InstanceSize; #ifdef INSTANTIATE_FACE_STRUCTURES // reserve space for copy of facelist allocinstancesize += ObjClasses[ objclass ]->NumFaces * sizeof( Face ); #endif // INSTANTIATE_FACE_STRUCTURES // create new object header GenObject *newinstance = (GenObject *) ALLOCMEM( allocinstancesize ); if ( newinstance == NULL ) { OUTOFMEM( "no mem for object instance." ); } // fill header with class data memcpy( newinstance, ObjClasses[ objclass ], ObjClasses[ objclass ]->InstanceSize ); OBJ_CorrectObjectInstance( newinstance, ObjClasses[ objclass ] ); #ifdef INSTANTIATE_FACE_STRUCTURES // make copy of facelist Face *instfacelist = (Face *) ( (char*)newinstance + newinstance->InstanceSize ); memcpy( instfacelist, newinstance->FaceList,newinstance->NumFaces * sizeof( Face ) ); newinstance->FaceList = instfacelist; #endif // INSTANTIATE_FACE_STRUCTURES #endif // !HACKED_SHIPOBJECT_INSTANTIATION // set object number //newinstance->ObjectNumber = NextObjNumber; newinstance->HostObjNumber = CreateGlobalObjId( NextObjNumber, dwOwner ); // no distinction between local and global id newinstance->ObjectNumber = newinstance->HostObjNumber; NextObjNumber++; // set start matrix memcpy( newinstance->ObjPosition, startmatrx, sizeof( Xmatrx ) ); // link header into list of existing objects of this type GenObject *listhead; switch ( newinstance->ObjectType & TYPELISTMASK ) { case PSHIP_LIST_NO: listhead = PShipObjects; #ifdef PARSEC_CLIENT listhead = ( PShipObjects->NextObj == MyShip ) ? PShipObjects->NextObj : PShipObjects; // create stargate for newly created spaceship //FIXME: gamecode if ( !disable_ship_creation_stargate ) { SFX_CreateStargate( (ShipObject *) newinstance ); } #endif // PARSEC_CLIENT break; case LASER_LIST_NO: listhead = LaserObjects; break; case MISSL_LIST_NO: listhead = MisslObjects; #ifdef PARSEC_CLIENT // create missile trail CreateVaporTrail( newinstance ); #endif // PARSEC_CLIENT break; case EXTRA_LIST_NO: listhead = ExtraObjects; #ifdef PARSEC_CLIENT // create generation animation SFX_CreateExtra( (ExtraObject *) newinstance ); // for extras maintain count #endif // PARSEC_CLIENT m_nCurrentNumExtras++; break; case CUSTM_LIST_NO: listhead = CustmObjects; // for custom objects invoke constructor _InvokeCustomConstructor( newinstance ); break; default: PANIC( 0 ); } #ifdef PARSEC_CLIENT // attach static particles that are // part of the object instance if ( AUX_ATTACH_OBJECT_PARTICLES ) { //FIXME: gamecode OBJ_AttachClassParticles( newinstance ); } #endif // PARSEC_CLIENT // append object at head of list newinstance->NextObj = listhead->NextObj; listhead->NextObj = newinstance; return newinstance; } //Particle land // allocate new particle cluster and insert into list ------------------------- // // init particle system ------------------------------------------------------- // void E_World::InitParticleSystem() { ASSERT( !particle_init_done ); if ( !particle_init_done ) { // create single element for dummy head and tail if ( ( Particles = (pcluster_s *) ALLOCMEM( sizeof( pcluster_s ) ) ) == NULL ) OUTOFMEM( 0 ); // set pointers thusly Particles->next = (pcluster_s *) ( (char*)Particles + sizeof( pcluster_s* ) ); Particles->prec = NULL; Particles->rep = (particle_s *) Particles; // initially there are no elements and no allocated storage Particles->numel = 0; Particles->maxnumel = 0; particle_init_done = TRUE; } } pcluster_s * E_World::PRT_NewCluster (dword type,int numelements, size_t auxstorage) { ASSERT( numelements > 0 ); // calc size of cluster header according to type int headsiz; switch ( type & CT_TYPEMASK ) { case CT_CONSTANT_VELOCITY: headsiz = sizeof( linear_pcluster_s ); break; case CT_LIGHTNING: headsiz = sizeof( lightning_pcluster_s ); break; case CT_OBJECTCENTERED_SPHERE: headsiz = sizeof( basesphere_pcluster_s ); break; case CT_PHOTON_SPHERE: headsiz = sizeof( photon_sphere_pcluster_s ); break; case CT_PARTICLE_SPHERE: headsiz = sizeof( sphereobj_pcluster_s ); break; case CT_CALLBACK_TRAJECTORY: headsiz = sizeof( callback_pcluster_s ); break; case CT_CUSTOMDRAW: headsiz = sizeof( customdraw_pcluster_s ); break; case CT_GENOBJECT_PARTICLES: headsiz = sizeof( genobject_pcluster_s ); break; default: PANIC( 0 ); } pcluster_s *temp = (pcluster_s *) ALLOCMEM( headsiz ); if ( temp == NULL ) OUTOFMEM( 0 ); // clear cluster header (including inherited fields!) memset( temp, 0, headsiz ); // insert cluster at head of cluster list temp->next = Particles->next; temp->prec = Particles; temp->next->prec = temp; temp->prec->next = temp; // size of basic particle structures size_t storesiz = sizeof( particle_s ) * numelements; // size of extinfo if desired if ( type & CT_EXTINFO_STORAGE ) storesiz += sizeof( pextinfo_s ) * numelements; // size of auxiliary info size_t auxofs = storesiz; if ( auxstorage > 0 ) storesiz += auxstorage + sizeof( pusrinfo_s ); // allocate storage for elements of cluster temp->rep = (particle_s *) ALLOCMEM( storesiz ); temp->type = type; temp->numel = 0; temp->maxnumel = numelements; ASSERT( temp->rep != NULL ); if ( temp->rep == NULL ) OUTOFMEM( 0 ); // init particle storage of cluster to zero memset( temp->rep, 0, storesiz ); // init auxstorage header if allocated if ( auxstorage > 0 ) { pusrinfo_s *uinfo = (pusrinfo_s *) ( (char*)temp->rep + auxofs ); uinfo->infovalid = FALSE; uinfo->blocksize = auxstorage; temp->userinfo = uinfo; } // add to global maximum number of elements Particles->maxnumel += numelements; // caller has to cast this pointer to its actual type! return temp; } // set members of particle structure and write into preexisting cluster ------- // void E_World::PRT_InitClusterParticle (pcluster_s* cluster,int pid,int bitmap,int color, int sizebound,float refz,Vertex3* position,Vector3* velocity,int lifetime,int owner,pextinfo_s* extinfo) { ASSERT( pid < cluster->maxnumel ); //NOTE: // the specified cluster member (particle) // simply gets overwritten!! PRT_InitParticle(cluster->rep[ pid ],color,sizebound,refz,position,velocity,lifetime,owner,extinfo ); // increase number of elements cluster->numel++; Particles->numel++; } // set members of particle structure ------------------------------------------ // void E_World::PRT_InitParticle (particle_s& particle,int color,int sizebound,float refz,Vertex3* position,Vector3* velocity, int lifetime,int owner,pextinfo_s* extinfo) { ASSERT( position != NULL ); particle.owner = owner; particle.flags = PARTICLE_ACTIVE; particle.lifetime = lifetime; particle.extinfo = extinfo; //particle.bitmap = bitmap; particle.color = color; particle.sizebound = sizebound; particle.ref_z = refz; particle.position = *position; if ( velocity ) { particle.velocity = *velocity; } else { particle.velocity.X = particle.velocity.Y = particle.velocity.Z = 0; } } // create new particle with linear animation (insert into next free slot) ----- // particle_s * E_World::PRT_CreateLinearParticle (particle_s& particle) { // try to reuse already allocated cluster if ( CurLinearCluster != NULL ) { ASSERT( ( CurLinearCluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ); if ( CurLinearCluster->numel < CurLinearCluster->maxnumel ) { // copy particle struct into available slot in free cluster particle_s *pmem = CurLinearCluster->rep + CurLinearCluster->numel; *pmem = particle; // check whether extinfo attached if ( particle.extinfo != NULL ) { if ( ( CurLinearCluster->type & CT_EXTINFO_STORAGE ) == 0 ) { ASSERT( 0 ); goto allocnew; } // copy over extinfo pextinfo_s *curextinfo = (pextinfo_s *)( CurLinearCluster->rep + CurLinearCluster->maxnumel ); curextinfo += CurLinearCluster->numel; memcpy( curextinfo, particle.extinfo, sizeof( pextinfo_s ) ); // set new extinfo pointer pmem->extinfo = curextinfo; } // increase number of cluster elements CurLinearCluster->numel++; // return new particle location return pmem; } } allocnew: // always allocate clusters with storage for extinfo! dword clustertype = CT_CONSTANT_VELOCITY | CT_EXTINFO_STORAGE; int numelements = DEFAULT_CLUSTER_SIZE; // create new cluster for linear particles CurLinearCluster = PRT_NewCluster( clustertype, numelements, 0 ); // set callback to default linear_pcluster_s *lincluster = (linear_pcluster_s *) CurLinearCluster; ASSERT( lincluster->callback == NULL ); lincluster->callback = LinearParticleCollision; //NOTE: // field bdsphere is zero. // insert recursively (tail rec) return PRT_CreateLinearParticle( particle ); } void E_World::PAN_AnimateParticles() { // walk list of clusters for ( pcluster_s *cluster = Particles->next; cluster->next; ) { // save next pointer to allow animation functions deletion of clusters pcluster_s *nextcluster = cluster->next; // behavior animation: // call trajectory animation function according to type identifier if ( ( cluster->type & CT_HINT_NO_POSITIONAL_ANIMATION ) == 0 ) { AnimateClusterBehavior( cluster ); } TheGameCollDet->CheckEnergyField(cluster); // advance to next cluster (previously remembered) cluster = nextcluster; #if defined( DEBUG ) && defined( PROTECT_ANIM_CLUSTER_WALK ) //NOTE: // if an animation function has killed a cluster that was not // the cluster the function has been called for, the pointer // to the next cluster in the list may already be invalid. // assert that next cluster is still part of the list pcluster_s *tcl; for ( tcl = Particles->next; tcl; tcl = tcl->next ) if ( tcl == cluster ) break; ASSERT( tcl != NULL ); #endif } } // particle behavior animation: call trajectory animation function ------------ // void E_World::AnimateClusterBehavior( pcluster_s *cluster ) { ASSERT( cluster != NULL ); // call trajectory animation function according to type identifier switch ( cluster->type & CT_TYPEENUMERATIONMASK ) { case ( CT_CONSTANT_VELOCITY & CT_TYPEENUMERATIONMASK ): CalcConstantVelocityAnimation( (linear_pcluster_s *) cluster ); break; case ( CT_LIGHTNING & CT_TYPEENUMERATIONMASK ): CalcLightningAnimation( (lightning_pcluster_s *) cluster ); break; case ( CT_OBJECTCENTERED_SPHERE & CT_TYPEENUMERATIONMASK ): // CalcObjectCenteredSphereAnimation( (basesphere_pcluster_s *) cluster ); break; case ( CT_PHOTON_SPHERE & CT_TYPEENUMERATIONMASK ): // already called in G_SUPP::MaintainDurationWeapons() //CalcPhotonSphereAnimation( (photon_sphere_pcluster_s *) cluster ); break; case ( CT_PARTICLE_SPHERE & CT_TYPEENUMERATIONMASK ): CalcSphereObjectAnimation( (sphereobj_pcluster_s *) cluster ); break; case ( CT_CALLBACK_TRAJECTORY & CT_TYPEENUMERATIONMASK ): CalcCallbackTrajectoryAnimation( (callback_pcluster_s *) cluster ); break; case ( CT_CUSTOMDRAW & CT_TYPEENUMERATIONMASK ): // CalcCustomDrawAnimation( (customdraw_pcluster_s *) cluster ); break; case ( CT_GENOBJECT_PARTICLES & CT_TYPEENUMERATIONMASK ): // CalcGenObjectAnimation( (genobject_pcluster_s *) cluster ); break; } } void E_World::CalcLightningAnimation( lightning_pcluster_s *cluster ) { ASSERT( cluster != NULL ); ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_LIGHTNING ); if ( ( cluster->framecount -= TheSimulator->GetThisFrameRefFrames() ) < 0 ) { cluster->framecount = cluster->sizzlespeed; GenObject *shippo = cluster->baseobject; ASSERT( shippo != NULL ); ASSERT( OBJECT_TYPE_SHIP( shippo ) ); Xmatrx tmatrx; #if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) MakeIdMatrx( tmatrx ); #else MakeNonTranslationMatrx( &shippo->ObjPosition, &tmatrx ); #endif Vertex3 startpos; #if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) startpos = cluster->beamstart1; #else MtxVctMUL( shippo->ObjPosition, &cluster->beamstart1, &startpos ); #endif SetLightningParticlePosition( cluster, cluster->rep, startpos, tmatrx ); #if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) startpos = cluster->beamstart2; #else MtxVctMUL( shippo->ObjPosition, &cluster->beamstart2, &startpos ); #endif SetLightningParticlePosition( cluster, cluster->rep + LIGHTNING_LENGTH, startpos, tmatrx ); } } // flags //#define ENERGYFIELD_DESTRUCTION_MESSAGE #define DO_BRUTEFORCE_CDETECTION // do collision detection for lightning #define PROTECT_ANIM_CLUSTER_WALK // lightning segment deviation/variation #define MAX_LIGHTNING_SEG_DEVIATION 1120 #define LIGHTNING_SEG_DEVIATION_FAC 0x10 // reference z of lightning impact particles #define IMPACT_PARTICLES_REF_Z 3.6f void E_World::SetLightningParticlePosition( lightning_pcluster_s *cluster, particle_s particles[], Vertex3& current, Xmatrx tmatrx ) { ASSERT( cluster != NULL ); // set position of initial particle particles[ 0 ].position.X = current.X; particles[ 0 ].position.Y = current.Y; particles[ 0 ].position.Z = current.Z; // iteratively calculate position of subsequent particles for ( int pid = 1; pid < LIGHTNING_LENGTH; pid++ ) { Vertex3 deviation; deviation.X = FIXED_TO_GEOMV( ( (long)( RAND() % MAX_LIGHTNING_SEG_DEVIATION ) - MAX_LIGHTNING_SEG_DEVIATION / 2 ) * LIGHTNING_SEG_DEVIATION_FAC ); deviation.Y = FIXED_TO_GEOMV( ( (long)( RAND() % MAX_LIGHTNING_SEG_DEVIATION ) - MAX_LIGHTNING_SEG_DEVIATION / 2 ) * LIGHTNING_SEG_DEVIATION_FAC ); deviation.Z = FLOAT_TO_GEOMV( 1.0 ); Vertex3 segvec; MtxVctMUL( tmatrx, &deviation, &segvec ); current.X += segvec.X * 2; current.Y += segvec.Y * 2; current.Z += segvec.Z * 2; particles[ pid ].position.X = current.X; particles[ pid ].position.Y = current.Y; particles[ pid ].position.Z = current.Z; #ifdef DO_BRUTEFORCE_CDETECTION ASSERT( cluster->baseobject != NULL ); Vertex3 cpos; MtxVctMUL( cluster->baseobject->ObjPosition, &particles[ pid ].position, &cpos ); if ( TheGameCollDet->CheckLightningParticleShipCollision( cpos, particles[ pid ].owner ) ) { while ( pid < LIGHTNING_LENGTH ) { particles[ pid ].flags &= ~PARTICLE_ACTIVE; pid++; } return; } particles[ pid ].flags |= PARTICLE_ACTIVE; #endif } } // calculate position advance of constant velocity particles ------------------ // void E_World::CalcConstantVelocityAnimation( linear_pcluster_s *cluster ) { ASSERT( cluster != NULL ); ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ); // advance all particles in cluster along their // local velocity vectors and maintain lifetime int numactive = 0; for ( int curp = 0; curp < cluster->numel; curp++ ) { // skip already inactive particles if ( ( cluster->rep[ curp ].flags & PARTICLE_ACTIVE ) == 0 ) { continue; } else { numactive++; } // maintain lifetime if ( ( cluster->rep[ curp ].lifetime -= TheSimulator->GetThisFrameRefFrames() ) < 0 ) DisableParticle( cluster, curp ); // move along vector Vector3 advvec; advvec.X = cluster->rep[ curp ].velocity.X * TheSimulator->GetThisFrameRefFrames(); advvec.Y = cluster->rep[ curp ].velocity.Y * TheSimulator->GetThisFrameRefFrames(); advvec.Z = cluster->rep[ curp ].velocity.Z * TheSimulator->GetThisFrameRefFrames(); cluster->rep[ curp ].position.X += advvec.X; cluster->rep[ curp ].position.Y += advvec.Y; cluster->rep[ curp ].position.Z += advvec.Z; // invoke callback for cluster/particle if ( cluster->rep[ curp ].flags & PARTICLE_COLLISION ) { if ( cluster->callback != NULL ) { (*cluster->callback)( cluster, curp ); } } } // remove cluster if no active particles contained anymore if ( numactive == 0 ) { PRT_DeleteCluster( cluster ); } } // disable a single particle in a specified cluster --------------------------- // void E_World::DisableParticle( pcluster_s *cluster, int pid ) { //NOTE: // individual particle lifetime is currently only // used by linear and customdraw particles. all other // clusters maintain only wholesale cluster lifetime. ASSERT( cluster != NULL ); ASSERT( ( ( cluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ) || ( ( cluster->type & CT_TYPEMASK ) == CT_CUSTOMDRAW ) ); cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; } // delete entire particle cluster --------------------------------------------- // void E_World::PRT_DeleteCluster (pcluster_s* cluster) { ASSERT( cluster != NULL ); // maintain global cluster pointers if ( CurLinearCluster == cluster ) CurLinearCluster = NULL; if ( CustomDrawCluster == cluster ) CustomDrawCluster = NULL; // if cluster is object-relative remove from list if contained if ( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ) PRT_RemoveClusterFromAttachedList( (objectbase_pcluster_s *) cluster ); // sub from global current/maximum number of elements Particles->numel -= cluster->numel; Particles->maxnumel -= cluster->maxnumel; // unlink cluster from list cluster->prec->next = cluster->next; cluster->next->prec = cluster->prec; // free storage (cluster header and particle storage) FREEMEM( cluster->rep ); FREEMEM( cluster ); MSGOUT("E_World::PRT_DeleteCluster(): Free'd particle cluster"); } // remove cluster from its attachment list if contained in any ---------------- // void E_World::PRT_RemoveClusterFromAttachedList (objectbase_pcluster_s* cluster) { ASSERT( cluster != NULL ); ASSERT( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); ASSERT( cluster->baseobject != NULL ); objectbase_pcluster_s *scan = cluster->baseobject->AttachedPClusters; objectbase_pcluster_s *precnode = NULL; for ( ; scan; scan = scan->attachlist ) { ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); ASSERT( scan->baseobject == cluster->baseobject ); if ( scan == cluster ) { if ( precnode ) precnode->attachlist = scan->attachlist; else cluster->baseobject->AttachedPClusters = scan->attachlist; return; } precnode = scan; } } // create particle object comprising a sphere --------------------------------- // sphereobj_pcluster_s* E_World::PRT_CreateParticleSphereObject ( Vertex3& origin, // origin of particle object geomv_t radius, // radius of sphere (also used for bounding) int animtype, // animation type (SAT_xx) int clustersiz, // cluster size (number of particles in sphere) int lifetime, // lifetime of sphere (each sphere particle) pdrwinfo_s* pdinfo, // particle appearance (drawing) info int owner // owner id (remote player id) ) { // check if animation type is allowed for particle sphere objects if ( ( animtype & SAT_VALID_FOR_PSPHERE_OBJECT ) == 0 ) { ASSERT( 0 ); return NULL; } // fetch pdefinfo if supplied int bitmapindx = pdinfo ? pdinfo->bmindx : 1; int pcolor = pdinfo ? pdinfo->pcolor : SPHERE_PARTICLE_COLOR; float refz = pdinfo ? pdinfo->ref_z : sphere_ref_z; int sizebound = pdinfo ? pdinfo->sizebnd : partbitmap_size_bound; // determine sphere's shape int spheretype = animtype & SAT_SPHERE_TYPE_MASK; // determine number of cluster elements int allocsiz = ( animtype & SAT_NEEDS_REFCOORDS_MASK ) ? clustersiz * 2 : clustersiz; // fetch extinfo if supplied pextinfo_s *extinfo = pdinfo ? pdinfo->extinfo : NULL; // determine cluster hints dword hints = CT_HINT_PARTICLES_IDENTICAL | CT_CLUSTER_GLOBAL_EXTINFO; // determine cluster type dword clustertype = CT_PARTICLE_SPHERE | hints; if ( extinfo != NULL ) { clustertype |= CT_EXTINFO_STORAGE | CT_HINT_PARTICLES_HAVE_EXTINFO; } // create new cluster sphereobj_pcluster_s *cluster = (sphereobj_pcluster_s *) PRT_NewCluster( clustertype, allocsiz, 0 ); // fill in basic fields cluster->bdsphere = radius; cluster->origin = origin; cluster->animtype = animtype; cluster->lifetime = lifetime; cluster->max_life = lifetime; // fill in additional fields switch ( animtype & SAT_BASIC_ANIM_MASK ) { case SAT_ROTATING: cluster->rot.pitch = SPHERE_ROT_PITCH; cluster->rot.yaw = SPHERE_ROT_YAW; cluster->rot.roll = SPHERE_ROT_ROLL; break; case SAT_EXPLODING: cluster->expl.speed = SPHERE_EXPLOSION_SPEED; break; case SAT_PULSATING: cluster->puls.amplitude = SPHERE_PULSE_AMPLITUDE; cluster->puls.midradius = radius; cluster->puls.frequency = SPHERE_PULSE_FREQUENCY; cluster->puls.current_t = BAMS_DEG0; cluster->puls.pitch = SPHERE_ROT_PITCH; cluster->puls.yaw = SPHERE_ROT_YAW; cluster->puls.roll = SPHERE_ROT_ROLL; break; case SAT_CONTRACTING: cluster->cont.speed = SPHERE_CONTRACT_SPEED; cluster->cont.expandtime = CONTRACTING_SPHERE_EXPANSION_TIME; cluster->cont.pitch = SPHERE_ROT_PITCH; cluster->cont.yaw = SPHERE_ROT_YAW; cluster->cont.roll = SPHERE_ROT_ROLL; break; } // set particle properties int curp = 0; for ( curp = 0; curp < clustersiz; curp++ ) { Vertex3 particlepos; CalcSphereParticlePosition( particlepos, radius, spheretype ); // copy extinfo into cluster pextinfo_s *curextinfo = NULL; if ( extinfo != NULL ) { curextinfo = (pextinfo_s *)( cluster->rep + allocsiz ) + curp; memcpy( curextinfo, extinfo, sizeof( pextinfo_s ) ); } // init particle in cluster PRT_InitClusterParticle( cluster, curp, bitmapindx, pcolor, sizebound, refz, &particlepos, NULL, INFINITE_LIFETIME, owner, curextinfo ); } // make second copy of particles if required by animation type if ( animtype & SAT_NEEDS_REFCOORDS_MASK ) { for ( int refp = 0; curp < clustersiz * 2; curp++, refp++ ) { cluster->rep[ curp ] = cluster->rep[ refp ]; } } //NOTE: // the optional second copy of all particles is not drawn // because the number of cluster elements is not increased // for them. (the particles are simply copied into the cluster!) // therefore, this block of particles is inactive and invisible // and can safely be used for reference purposes. // note also that the duplicate extinfo pointers pose no problem // for the exact same reason. return cluster; } // calc init position for particle stochastically placed on spherical surface - // void E_World::CalcSphereParticlePosition( Vertex3& position, geomv_t radius, int spheretype ) { Vertex3 sphereloc; sphereloc.X = 0; sphereloc.Y = 0; sphereloc.Z = radius; Xmatrx rotmatrx; MakeIdMatrx( rotmatrx ); if ( spheretype == SAT_SPHERETYPE_NORMAL ) { bams_t pitch = RAND() * 2 - BAMS_DEG180; bams_t yaw = RAND() * 2 - BAMS_DEG180; CamRotX( rotmatrx, pitch ); CamRotY( rotmatrx, yaw ); } else if ( spheretype == SAT_SPHERETYPE_DISC ) { int r = ( RAND() >> 10 ) & 1; if ( r == 0 ) { sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x0c0000 ); bams_t pitch = RAND() * 2 - BAMS_DEG180; CamRotX( rotmatrx, pitch ); } else { sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x150000 ); bams_t pitch = RAND() * 2 - BAMS_DEG180; CamRotX( rotmatrx, pitch ); } } else if ( spheretype == SAT_SPHERETYPE_DISCWITHCORE ) { int r = RAND() & 3; if ( r == 0 ) { sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x0c0000 ); bams_t pitch = RAND() * 2 - BAMS_DEG180; CamRotX( rotmatrx, pitch ); } else if ( r == 1 ) { sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x150000 ); bams_t pitch = RAND() * 2 - BAMS_DEG180; CamRotX( rotmatrx, pitch ); } else { bams_t pitch = RAND() * 2 - BAMS_DEG180; bams_t yaw = RAND() * 2 - BAMS_DEG180; CamRotX( rotmatrx, pitch ); CamRotY( rotmatrx, yaw ); } } MtxVctMUL( rotmatrx, &sphereloc, &position ); } // create an energy field ----------------------------------------------------- // void E_World::SFX_CreateEnergyField( Vertex3& origin ) { // some particle appearance properties int bitmap = 1; 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 *pextinfo = NULL; pdrwinfo_s drawinfo; drawinfo.bmindx = bitmap; drawinfo.pcolor = color; drawinfo.ref_z = ref_z; drawinfo.extinfo = pextinfo; drawinfo.sizebnd = sizebound; PRT_CreateParticleSphereObject( origin, ENERGY_FIELD_INITIAL_RADIUS, SAT_ENERGYFIELD_SPHERE, SPHERE_PARTICLES, lifetime, &drawinfo, PLAYERID_ANONYMOUS); // MSGOUT("E_World::SFX_CreateEnergField() X:%.2f Y:%.2f Z:%.2f",origin.X,origin.Y,origin.Z); // maintain particle extra count m_nCurrentNumPrtExtras++; } // ---------------------------------------------------------------------------- // int SWARM_rand() { nextrand = nextrand * 1103515245 + 12345; return((nextrand >> 16) & 0x7FFF); } // ---------------------------------------------------------------------------- // void SWARM_srand( unsigned int seed ) { nextrand = seed; } // ---------------------------------------------------------------------------- // GenObject* E_World::SWARM_Init( int owner, Vertex3 *origin, ShipObject *targetpo, int randseed ) { static pextinfo_s extinfo; // set rand seed SWARM_srand( randseed ); size_t swarmsiz = sizeof( swarm_state_s ); swarmsiz += sizeof( Point3h_f ) * NUM_PARTICLES; swarmsiz += 3 * sizeof( geomv_t ) * NUM_PARTICLES * TIME_POSITIONS; swarmsiz += 3 * sizeof( geomv_t ) * NUM_PARTICLES; // create new particle cluster with auxstorage for swarm dword clustertype = CT_CALLBACK_TRAJECTORY | CT_EXTINFO_STORAGE; callback_pcluster_s *cluster = (callback_pcluster_s *) PRT_NewCluster( clustertype, NUM_PARTICLES, swarmsiz ); ASSERT( cluster != NULL ); // init custom fields cluster->callback = (callback_pcluster_fpt) SWARM_TimedAnimate; // set performance hints cluster->type |= CT_HINT_PARTICLES_IDENTICAL; cluster->type |= CT_HINT_PARTICLES_HAVE_EXTINFO; cluster->type |= CT_HINT_NO_APPEARANCE_ANIMATION; cluster->userinfo->infovalid = TRUE; char *auxstorage = (char *) &cluster->userinfo[ 1 ]; // get pointer to auxstorage swarm_state_s *swarm = (swarm_state_s *) auxstorage; auxstorage += sizeof( swarm_state_s ); swarm->num = NUM_PARTICLES; swarm->pos = (Point3h_f *) auxstorage; auxstorage += sizeof( Point3h_f ) * NUM_PARTICLES; swarm->x = (geomv_t *) auxstorage; auxstorage += sizeof( geomv_t ) * swarm->num * TIME_POSITIONS; swarm->y = (geomv_t *) auxstorage; auxstorage += sizeof( geomv_t ) * swarm->num * TIME_POSITIONS; swarm->z = (geomv_t *) auxstorage;; auxstorage += sizeof( geomv_t ) * swarm->num * TIME_POSITIONS; swarm->xv = (geomv_t *) auxstorage;; auxstorage += sizeof( geomv_t ) * NUM_PARTICLES; swarm->yv = (geomv_t *) auxstorage;; auxstorage += sizeof( geomv_t ) * NUM_PARTICLES; swarm->zv = (geomv_t *) auxstorage;; // store pointer to object for use in animation function swarm->targetpo = targetpo; // init dummy object position swarm->dummyobj.ObjPosition[ 0 ][ 3 ] = origin->X; swarm->dummyobj.ObjPosition[ 1 ][ 3 ] = origin->Y; swarm->dummyobj.ObjPosition[ 2 ][ 3 ] = origin->Z; // init excess time swarm->timerest = 0; // init target position swarm->tx[ 0 ] = targetpo->ObjPosition[ 0 ][ 3 ]; swarm->ty[ 0 ] = targetpo->ObjPosition[ 1 ][ 3 ]; swarm->tz[ 0 ] = targetpo->ObjPosition[ 2 ][ 3 ]; swarm->tx[ 1 ] = swarm->tx[ 0 ]; swarm->ty[ 1 ] = swarm->ty[ 0 ]; swarm->tz[ 1 ] = swarm->tz[ 0 ]; swarm->tx[ 2 ] = swarm->tx[ 0 ]; swarm->ty[ 2 ] = swarm->ty[ 0 ]; swarm->tz[ 2 ] = swarm->tz[ 0 ]; // get pointer to ship of owner ShipObject *shippo = FetchFirstShip(); while ( shippo && ( shippo->HostObjNumber != ShipHostObjId( owner ) ) ) { shippo = (ShipObject *) shippo->NextObj; } Vector3 dirvec; if ( shippo != NULL ) { DirVctMUL( shippo->ObjPosition, FLOAT_TO_GEOMV( 1.5f ), &dirvec ); } else { dirvec.X = FLOAT_TO_GEOMV( 1.5f ); dirvec.Y = FLOAT_TO_GEOMV( 1.5f ); dirvec.Z = FLOAT_TO_GEOMV( 1.5f ); } // init particle positions and velocities int pid = 0; for ( pid = 0; pid < swarm->num; pid++ ) { X( 0, pid ) = origin->X; X( 1, pid ) = X( 0, pid ); Y( 0, pid ) = origin->Y; Y( 1, pid ) = Y( 0, pid ); Z( 0, pid ) = origin->Z; Z( 1, pid ) = Z( 0, pid ); swarm->xv[ pid ] = dirvec.X; swarm->yv[ pid ] = dirvec.Y; swarm->zv[ pid ] = dirvec.Z; // swarm->xv[ pid ] = GEOMV_DIV( ( balance_rand( 100 ) * PARTICLE_VELOCITY ), // 100 * PARTICLE_VELOCITY ); // swarm->yv[ pid ] = GEOMV_DIV( ( balance_rand( 100 ) * PARTICLE_VELOCITY ), // 100 * PARTICLE_VELOCITY ); // swarm->zv[ pid ] = GEOMV_DIV( ( balance_rand( 100 ) * PARTICLE_VELOCITY ), // 100 * PARTICLE_VELOCITY ); } /* // create particles pdef_s *pdef = PRT_AcquireParticleDefinition( "swarm1", NULL ); if ( pdef == NULL ) { return NULL; } // create extinfo PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL ); */ float cur_particle_resoscale = 1.0f; float ref_z = cur_particle_resoscale * PARTICLE_REFZ; int bitmap = iter_texrgba | iter_specularadd; int color = 0; // create all particles of swarm for ( pid = 0; pid < swarm->num; pid++ ) { Vector3 pos; pos.X = X( 0, pid ); pos.Y = Y( 0, pid ); pos.Z = Z( 0, pid ); // init the particles and hook them up with cluster PRT_InitClusterParticle( cluster, pid, bitmap, color, PRT_NO_SIZEBOUND, ref_z, &pos, NULL, PARTICLE_LIFETIME + balance_rand( LIFETIME_VARIANCE ), owner, &extinfo ); } return &swarm->dummyobj; } // ---------------------------------------------------------------------------- // int E_World::SWARM_Animate( callback_pcluster_s* cluster ) { ASSERT( cluster != NULL ); // get pointer to auxstorage swarm_state_s *swarm = (swarm_state_s *) &cluster->userinfo[ 1 ]; // age the target arrays swarm->tx[ 2 ] = swarm->tx[ 1 ]; swarm->ty[ 2 ] = swarm->ty[ 1 ]; swarm->tz[ 2 ] = swarm->tz[ 1 ]; swarm->tx[ 1 ] = swarm->tx[ 0 ]; swarm->ty[ 1 ] = swarm->ty[ 0 ]; swarm->tz[ 1 ] = swarm->tz[ 0 ]; // check of target is still available ShipObject *shippo = (ShipObject *) FetchFirstShip(); while ( shippo && ( shippo != swarm->targetpo ) ) shippo = (ShipObject *) shippo->NextObj; // update target position if ( ( shippo != NULL ) ) { swarm->tx[ 0 ] = swarm->targetpo->ObjPosition[ 0 ][ 3 ]; swarm->ty[ 0 ] = swarm->targetpo->ObjPosition[ 1 ][ 3 ]; swarm->tz[ 0 ] = swarm->targetpo->ObjPosition[ 2 ][ 3 ]; } // avoid settling swarm->xv[ SWARM_rand() % swarm->num ] += balance_rand( 3 ); swarm->yv[ SWARM_rand() % swarm->num ] += balance_rand( 3 ); swarm->zv[ SWARM_rand() % swarm->num ] += balance_rand( 3 ); int numactive = 0; int refposset = FALSE; // update all particles for ( int pid = 0; pid < swarm->num; pid++ ) { particle_s* curparticle = &cluster->rep[ pid ]; // skip already inactive particles if ( ( curparticle->flags & PARTICLE_ACTIVE ) == 0 ) { continue; } // maintain lifetime if ( ( curparticle->lifetime -= ANIM_TIMESLICE ) < 0 ) { curparticle->flags &= ~PARTICLE_ACTIVE; continue; } // count as still active numactive++; // age the particle arrays X( 2, pid ) = X( 1, pid ); Y( 2, pid ) = Y( 1, pid ); Z( 2, pid ) = Z( 1, pid ); X( 1, pid ) = X( 0, pid ); Y( 1, pid ) = Y( 0, pid ); Z( 1, pid ) = Z( 0, pid ); Vector3 delta; // accelerate to target position delta.X = swarm->tx[ 1 ] - X( 1, pid ); delta.Y = swarm->ty[ 1 ] - Y( 1, pid ); delta.Z = swarm->tz[ 1 ] - Z( 1, pid ); geomv_t distance = VctLenX( &delta ); if ( distance == GEOMV_0 ) distance = GEOMV_1; swarm->xv[ pid ] += GEOMV_DIV( GEOMV_MUL( delta.X, PARTICLE_ACCEL ) , distance ); swarm->yv[ pid ] += GEOMV_DIV( GEOMV_MUL( delta.Y, PARTICLE_ACCEL ) , distance ); swarm->zv[ pid ] += GEOMV_DIV( GEOMV_MUL( delta.Z, PARTICLE_ACCEL ) , distance ); // speed limit checks if ( swarm->xv[ pid ] > PARTICLE_VELOCITY ) swarm->xv[ pid ] = PARTICLE_VELOCITY; if ( swarm->xv[ pid ] < - PARTICLE_VELOCITY ) swarm->xv[ pid ] = - PARTICLE_VELOCITY; if ( swarm->yv[ pid ] > PARTICLE_VELOCITY ) swarm->yv[ pid ] = PARTICLE_VELOCITY; if ( swarm->yv[ pid ] < - PARTICLE_VELOCITY ) swarm->yv[ pid ] = - PARTICLE_VELOCITY; if ( swarm->zv[ pid ] > PARTICLE_VELOCITY ) swarm->zv[ pid ] = PARTICLE_VELOCITY; if ( swarm->zv[ pid ] < - PARTICLE_VELOCITY ) swarm->zv[ pid ] = - PARTICLE_VELOCITY; // move X( 0, pid ) = X( 1, pid ) + swarm->xv[ pid ] * ANIM_TIMESLICE; Y( 0, pid ) = Y( 1, pid ) + swarm->yv[ pid ] * ANIM_TIMESLICE; Z( 0, pid ) = Z( 1, pid ) + swarm->zv[ pid ] * ANIM_TIMESLICE; // fill the position list swarm->pos[ pid ].X = X( 0, pid ); swarm->pos[ pid ].Y = Y( 0, pid ); swarm->pos[ pid ].Z = Z( 0, pid ); // update particle positions curparticle->position.X = swarm->pos[ pid ].X; curparticle->position.Y = swarm->pos[ pid ].Y; curparticle->position.Z = swarm->pos[ pid ].Z; if ( !refposset ) { swarm->dummyobj.ObjPosition[ 0 ][ 3 ] = cluster->rep[ pid ].position.X; swarm->dummyobj.ObjPosition[ 1 ][ 3 ] = cluster->rep[ pid ].position.Y; swarm->dummyobj.ObjPosition[ 2 ][ 3 ] = cluster->rep[ pid ].position.Z; refposset = TRUE; } // check for collision with other ships ShipObject *walkships = FetchFirstShip(); for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { // prevent collision with owner of particle if ( ( GetObjectOwner( walkships ) == (dword)curparticle->owner ) ) continue; if ( !TheGameCollDet->PRT_ParticleInBoundingSphere( walkships, curparticle->position ) ) continue; #ifdef PARTICLES_DIE_AFTER_FIRST_COLLISION // disable particle curparticle->flags &= ~PARTICLE_ACTIVE; numactive--; #endif TheGameCollDet->OBJ_ShipSwarmDamage( walkships, curparticle->owner ); if ( walkships->CurDamage > walkships->MaxDamage ) { for ( int ppid = 0; ppid < swarm->num; ppid++ ) { refframe_t newlifetime = LIFETIME_AFTEREXPL + balance_rand( LIFETIME_VARIANCE ); if ( cluster->rep[ ppid ].lifetime >= newlifetime ) cluster->rep[ ppid ].lifetime = newlifetime; } } } } // destory swarm if no active particles contained anymore if ( numactive == 0 ) { PRT_DeleteCluster( cluster ); return FALSE; } return TRUE; } // invoke callback function for animation of generic callback clusters -------- // void E_World::CalcCallbackTrajectoryAnimation( callback_pcluster_s *cluster ) { ASSERT( cluster != NULL ); ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CALLBACK_TRAJECTORY ); ASSERT( cluster->callback != NULL ); // invoke callback function if ( cluster->callback != NULL ) { cluster->callback( cluster ); } //NOTE: // the callback function is invoked for the // entire cluster, not single particles. } // calc animation of autonomous particle sphere ------------------------------- // PRIVATE void E_World::CalcSphereObjectAnimation( sphereobj_pcluster_s *cluster ) { ASSERT( cluster != NULL ); ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_PARTICLE_SPHERE ); if ( cluster->lifetime > 0 ) { refframe_t refframes = TheSimulator->GetThisFrameRefFrames(); if ( ( cluster->lifetime -= refframes ) < 0 ) { refframes += cluster->lifetime; } // handle optional particle auto-depletion if ( cluster->animtype & SAT_AUTO_DEPLETE_PARTICLES ) { ASSERT( cluster->lifetime <= cluster->max_life ); float strength = (float)cluster->lifetime / cluster->max_life; int maxnumel = cluster->maxnumel; if ( cluster->animtype & SAT_NEEDS_REFCOORDS_MASK ) maxnumel /= 2; int numel = (int)(maxnumel * strength); if ( numel < cluster->maxnumel ) { cluster->numel = numel; } } // perform basic animation switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { case SAT_ROTATING: { bams_t pitch = cluster->rot.pitch * TheSimulator->GetThisFrameRefFrames(); bams_t yaw = cluster->rot.yaw * TheSimulator->GetThisFrameRefFrames(); bams_t roll = cluster->rot.roll * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereParticleRotation( cluster->rep[ pid ].position, pitch, yaw, roll ); } } break; case SAT_EXPLODING: { geomv_t speed = cluster->expl.speed * refframes; for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereParticleExplosion( cluster->rep[ pid ].position, speed ); } // update cluster radius cluster->bdsphere += speed; } break; case SAT_PULSATING: { sincosval_s sincosv; GetSinCos( cluster->puls.current_t, &sincosv ); geomv_t pulseval = GEOMV_MUL( sincosv.sinval, cluster->puls.amplitude ); bams_t pitch = cluster->puls.pitch * TheSimulator->GetThisFrameRefFrames(); bams_t yaw = cluster->puls.yaw * TheSimulator->GetThisFrameRefFrames(); bams_t roll = cluster->puls.roll * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereParticleRotation( cluster->rep[ pid + cluster->numel ].position, pitch, yaw, roll ); CalcSpherePulse( cluster->rep[ pid ].position, cluster->rep[ pid + cluster->numel ].position, pulseval ); } // update cluster radius cluster->bdsphere = cluster->puls.midradius + pulseval; cluster->puls.current_t += cluster->puls.frequency * TheSimulator->GetThisFrameRefFrames(); } break; case SAT_CONTRACTING: { bams_t pitch = cluster->cont.pitch * TheSimulator->GetThisFrameRefFrames(); bams_t yaw = cluster->cont.yaw * TheSimulator->GetThisFrameRefFrames(); bams_t roll = cluster->cont.roll * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereParticleRotation( cluster->rep[ pid ].position, pitch, yaw, roll ); } if ( cluster->cont.expandtime > 0 ) { cluster->cont.expandtime -= TheSimulator->GetThisFrameRefFrames(); geomv_t contraction = cluster->cont.speed * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereContraction( cluster->rep[ pid ].position, -contraction ); } // update cluster radius cluster->bdsphere += contraction; } } break; } } else { // animation type-dependent behavior after lifetime is spent switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { case SAT_CONTRACTING: { bams_t pitch = cluster->cont.pitch * TheSimulator->GetThisFrameRefFrames(); bams_t yaw = cluster->cont.yaw * TheSimulator->GetThisFrameRefFrames(); bams_t roll = cluster->cont.roll * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereParticleRotation( cluster->rep[ pid ].position, pitch, yaw, roll ); } int delit = 0; { geomv_t contraction = cluster->cont.speed * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { delit += CalcSphereContraction( cluster->rep[ pid ].position, contraction ); } // update cluster radius cluster->bdsphere -= contraction; } if ( delit ) { if ( cluster->animtype & SAT_DECREMENT_EXTRA_COUNTER ) { m_nCurrentNumPrtExtras--; } PRT_DeleteCluster( cluster ); } } break; default: PRT_DeleteCluster( cluster ); } } } // calc rotation of sphere particle ------------------------------------------- // void E_World::CalcSphereParticleRotation( Vertex3& position, bams_t pitch, bams_t yaw, bams_t roll ) { Xmatrx rotmatrx; MakeIdMatrx( rotmatrx ); CamRotX( rotmatrx, pitch ); CamRotY( rotmatrx, yaw ); CamRotZ( rotmatrx, roll ); Vertex3 rotpos; MtxVctMUL( rotmatrx, &position, &rotpos ); position = rotpos; } // calc contraction of sphere ------------------------------------------------- // int E_World::CalcSphereContraction( Vertex3& position, geomv_t speed ) { // check for null vector geomv_t vx = position.X; geomv_t vy = position.Y; geomv_t vz = position.Z; ABS_GEOMV( vx ); ABS_GEOMV( vy ); ABS_GEOMV( vz ); if ( ( vx <= GEOMV_VANISHING ) && ( vy <= GEOMV_VANISHING ) && ( vz <= GEOMV_VANISHING ) ) { return TRUE; } // create direction unit-vector Vector3 vec = position; NormVctX( &vec ); int sign_x = ( position.X < 0 ) ? -1 : 1; int sign_y = ( position.Y < 0 ) ? -1 : 1; int sign_z = ( position.Z < 0 ) ? -1 : 1; position.X -= GEOMV_MUL( vec.X, speed ); position.Y -= GEOMV_MUL( vec.Y, speed ); position.Z -= GEOMV_MUL( vec.Z, speed ); int asign_x = ( position.X < 0 ) ? -1 : 1; int asign_y = ( position.Y < 0 ) ? -1 : 1; int asign_z = ( position.Z < 0 ) ? -1 : 1; int collapsed = FALSE; if ( sign_x != asign_x ) collapsed = TRUE; if ( sign_y != asign_y ) collapsed = TRUE; if ( sign_z != asign_z ) collapsed = TRUE; return collapsed; } lightning_pcluster_s* E_World::CreateLightningParticles( ShipObject *shippo, int owner ) { ASSERT( shippo != NULL ); // create appearance int bitmap = 1; int color = 1; float ref_z = lightning_ref_z; int sizebound = partbitmap_size_bound; // create cluster dword clustertype = CT_LIGHTNING; lightning_pcluster_s *cluster = (lightning_pcluster_s *) PRT_NewCluster( clustertype, LIGHTNING_LENGTH * 2, 0 ); // fill in basic fields cluster->animtype = SAT_LIGHTNING; cluster->sizzlespeed = LIGHTNING_SIZZLE_SPEED; Vertex3 startpos; startpos.X = shippo->Beam_X[ 0 ]; startpos.Y = shippo->Beam_Y; startpos.Z = shippo->Beam_Z; cluster->beamstart1 = startpos; startpos.X = shippo->Beam_X[ 3 ]; cluster->beamstart2 = startpos; // create particles int curp = 0; for ( curp = 0; curp < LIGHTNING_LENGTH; curp++ ) PRT_InitClusterParticle( cluster, curp, bitmap, color, sizebound, ref_z, &cluster->beamstart1, NULL, INFINITE_LIFETIME, owner, NULL ); for ( ; curp < LIGHTNING_LENGTH * 2; curp++ ) PRT_InitClusterParticle( cluster, curp, bitmap, color, sizebound, ref_z, &cluster->beamstart2, NULL, INFINITE_LIFETIME, owner, NULL ); // attach lightning particle cluster to ship PRT_AttachClusterToObject( shippo, cluster ); return cluster; } // attach cluster to genobject (insert at head of attachment list) ------------ // void E_World::PRT_AttachClusterToObject( GenObject* genobjpo, objectbase_pcluster_s* cluster ) { ASSERT( genobjpo != NULL ); ASSERT( cluster != NULL ); ASSERT( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); // set reference to genobject cluster->baseobject = genobjpo; // insert at head of list cluster->attachlist = genobjpo->AttachedPClusters; genobjpo->AttachedPClusters = cluster; } // delete all clusters of certain type attached to a genobject ---------------- // int E_World::PRT_DeleteAttachedClustersOfType( const GenObject * genobjpo, int animtype ) { ASSERT( genobjpo != NULL ); // count number of removed clusters int numremoved = 0; objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; objectbase_pcluster_s *precnode = NULL; while ( scan != NULL ) { ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); if ( scan->animtype == animtype ) { if ( precnode ) precnode->attachlist = scan->attachlist; else ((GenObject*)genobjpo)->AttachedPClusters = scan->attachlist; pcluster_s *temp = scan; scan = scan->attachlist; PRT_DeleteCluster_NoListRemoval( temp ); numremoved++; continue; } precnode = scan; scan = scan->attachlist; } // return number of removed clusters return numremoved; } // delete entire particle cluster (don't check any attachment lists) ---------- // void E_World::PRT_DeleteCluster_NoListRemoval( pcluster_s* cluster ) { ASSERT( cluster != NULL ); // maintain global cluster pointers if ( CurLinearCluster == cluster ) CurLinearCluster = NULL; if ( CustomDrawCluster == cluster ) CustomDrawCluster = NULL; // sub from global current/maximum number of elements Particles->numel -= cluster->numel; Particles->maxnumel -= cluster->maxnumel; // unlink cluster from list cluster->prec->next = cluster->next; cluster->next->prec = cluster->prec; // free storage (cluster header and particle storage) FREEMEM( cluster->rep ); FREEMEM( cluster ); } // calculate pulsating sphere ------------------------------------------------- // void E_World::CalcSpherePulse( Vertex3& position, Vertex3& pulsebase, geomv_t pulseval ) { // create direction unit-vector Vector3 vec = pulsebase; NormVctX( &vec ); // pulseval is a sine or cosine function and determines // the deviation from the base position (radius) position.X = pulsebase.X + GEOMV_MUL( vec.X, pulseval ); position.Y = pulsebase.Y + GEOMV_MUL( vec.Y, pulseval ); position.Z = pulsebase.Z + GEOMV_MUL( vec.Z, pulseval ); } // calc explosion trajectory of sphere particle ------------------------------- // void E_World::CalcSphereParticleExplosion( Vertex3& position, geomv_t speed ) { Vector3 vec = position; NormVctX( &vec ); position.X += GEOMV_MUL( vec.X, speed ); position.Y += GEOMV_MUL( vec.Y, speed ); position.Z += GEOMV_MUL( vec.Z, speed ); } // acquire already registered particle definition via its id ------------------ // pdef_s * E_World::PRT_AcquireParticleDefinitionById( int pdefid // id of particle definition ) { // ensure id is valid if ( ( pdefid < 0 ) || ( pdefid >= NumParticleDefinitions ) ) return NULL; // fetch from table pdef_s *pdef = ParticleDefinitions[ pdefid ].def; ASSERT( pdef != NULL ); return pdef; }; // acquire already registered particle definition via its unique name --------- // pdef_s * E_World::PRT_AcquireParticleDefinition( const char* pdefname, // unique name for particle definition int* retpdefid // id of returned particle definition ) { ASSERT( pdefname != NULL ); // scan all registered particle definitions for ( int curdef = 0; curdef < NumParticleDefinitions; curdef++ ) { if ( strcmp( ParticleDefinitions[ curdef ].defname, pdefname ) == 0 ) { // return id if desired if ( retpdefid != NULL ) *retpdefid = curdef; // return pointer to pdef return ParticleDefinitions[ curdef ].def; } } return NULL; }; // create particle sphere for photon cannon ----------------------------------- // photon_sphere_pcluster_s * E_World::CreatePhotonSphere( ShipObject *shippo ) { ASSERT( shippo != NULL ); ASSERT( OBJECT_TYPE_SHIP( shippo ) ); pextinfo_s extinfo; /* // fetch pdef static int pdefid = -1; pdef_s *pdef = ( pdefid != -1 ) ? PRT_AcquireParticleDefinitionById( pdefid ) : PRT_AcquireParticleDefinition( "photon1", &pdefid ); if ( pdef == NULL ) { MSGOUT( "photon particles invalid." ); return NULL; } // create extinfo pextinfo_s extinfo; //PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL ); */ // determine cluster type and hints dword clustertype = CT_PHOTON_SPHERE; clustertype |= CT_EXTINFO_STORAGE; clustertype |= CT_CLUSTER_GLOBAL_EXTINFO; clustertype |= CT_HINT_PARTICLES_HAVE_EXTINFO; clustertype |= CT_HINT_PARTICLES_IDENTICAL; // create new cluster int clustersiz = PHOTON_SPHERE_PARTICLES; photon_sphere_pcluster_s *cluster = (photon_sphere_pcluster_s *) PRT_NewCluster( clustertype, clustersiz, 0 ); // fill in basic fields cluster->animtype = SAT_PHOTON; cluster->bdsphere = shippo->BoundingSphere; cluster->contraction_time = PHOTON_CONTRACTION_TIME; cluster->cur_contraction_time = 0; cluster->contraction_speed = PHOTON_CONTRACTION_SPEED; cluster->max_loading_time = PHOTON_MAX_LOADING_TIME; cluster->firing = FALSE; cluster->numloads = PHOTON_NUMLOADS; cluster->alive = 0; cluster->center.X = 0; cluster->center.Y = 0; cluster->center.Z = 0; cluster->pitch = PHOTON_ROT_PITCH; cluster->yaw = PHOTON_ROT_YAW; cluster->roll = PHOTON_ROT_ROLL; // set particle properties for ( int curp = 0; curp < clustersiz; curp++ ) { Vertex3 particlepos; CalcSphereParticlePosition( particlepos, shippo->BoundingSphere, SAT_SPHERETYPE_NORMAL ); // copy extinfo into cluster pextinfo_s *curextinfo = (pextinfo_s *)( cluster->rep + clustersiz ) + curp; memcpy( curextinfo, &extinfo, sizeof( pextinfo_s ) ); // init particle in cluster cluster->rep[ curp ].owner = GetObjectOwner( shippo ); cluster->rep[ curp ].flags = PARTICLE_ACTIVE; cluster->rep[ curp ].lifetime = INFINITE_LIFETIME; cluster->rep[ curp ].extinfo = curextinfo; cluster->rep[ curp ].bitmap = iter_texrgba | iter_specularadd; cluster->rep[ curp ].color = PHOTON_COLOR; cluster->rep[ curp ].sizebound = PRT_NO_SIZEBOUND; cluster->rep[ curp ].ref_z = photon_ref_z; cluster->rep[ curp ].position = particlepos; cluster->rep[ curp ].velocity.X = GEOMV_0; cluster->rep[ curp ].velocity.Y = GEOMV_0; cluster->rep[ curp ].velocity.Z = GEOMV_0; // increase number of elements Particles->numel++; } // at least one particle has to be visible cluster->numel = 1; // attach sphere's particle cluster to object PRT_AttachClusterToObject( shippo, (objectbase_pcluster_s *)cluster ); return cluster; } // calc animation of photon sphere -------------------------------------------- // void E_World::CalcPhotonSphereAnimation( photon_sphere_pcluster_s *cluster ) { ASSERT( cluster != NULL ); ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_PHOTON_SPHERE ); ShipObject *shippo = (ShipObject *) cluster->baseobject; float part_prf = ( cluster->max_loading_time / cluster->maxnumel ); Vertex3 old_center; geomv_t contraction; if ( !cluster->firing ) { // determine maximum remaining loading time int working_time = cluster->max_loading_time - cluster->alive; // determine actual loading time if ( working_time > TheSimulator->GetThisFrameRefFrames() ) { working_time = TheSimulator->GetThisFrameRefFrames(); } cluster->alive += working_time; dword energy_consumption = shippo->CurEnergyFrac + ( working_time * PHOTON_ENERGY_CONSUMPTION ); G_Player* pPlayer = TheGame->GetPlayer( GetObjectOwner( shippo ) ); // check if enough energy to build if ( (dword)shippo->CurEnergy < ( MIN_PHOTON_ENERGY + energy_consumption >> 16 ) ) { cluster->alive = (int)(cluster->alive -( working_time - ( shippo->CurEnergy - MIN_PHOTON_ENERGY ) / FIXED_TO_FLOAT( PHOTON_ENERGY_CONSUMPTION ) )); shippo->CurEnergy = MIN_PHOTON_ENERGY - 1; pPlayer->_WFX_DeactivatePhoton(); } else { shippo->CurEnergyFrac = ( energy_consumption & 0xffff ); shippo->CurEnergy -= ( energy_consumption >> 16 ); } // calculate number of visible particles if ( ( cluster->alive < cluster->max_loading_time ) ) { cluster->numel = (int)( cluster->alive / part_prf ); if ( cluster->numel == 0 ) { // at least one particle must be visible cluster->numel = 1; } } else { cluster->numel = cluster->maxnumel; //NOTE: // CurScreenRefFrames get added some lines below anyway, // so we must not add them here cluster->cur_contraction_time -= working_time; pPlayer->_WFX_DeactivatePhoton(); } } if ( cluster->firing ) { // increase counter cluster->cur_contraction_time += TheSimulator->GetThisFrameRefFrames(); // create linear particles // number of particles in full load int single_load = ( cluster->maxnumel / cluster->numloads ); int old_numel = cluster->numel; // number of full loads to be created int numloads = ( cluster->cur_contraction_time / cluster->contraction_time ); // minimize on full loads available if ( numloads > ( old_numel / single_load ) ) { numloads = ( old_numel / single_load ); } // set particle properties int color = PHOTON_COLOR; float ref_z = photon_ref_z; int sizebound = PRT_NO_SIZEBOUND; int lifetime = shippo->PhotonLifeTime; int playerid = GetObjectOwner( shippo ); pextinfo_s extinfo; /* // fetch pdef static int pdefid = -1; pdef_s *pdef = ( pdefid != -1 ) ? PRT_AcquireParticleDefinitionById( pdefid ) : PRT_AcquireParticleDefinition( "photon1", &pdefid ); // create extinfo pextinfo_s extinfo; // PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL ); */ // set speed and direction vector Vector3 dirvec; fixed_t speed = shippo->PhotonSpeed + shippo->CurSpeed; DirVctMUL( shippo->ObjPosition, FIXED_TO_GEOMV( speed ), &dirvec ); // radius of load geomv_t radius = shippo->BoundingSphere - GEOMV_MUL( cluster->contraction_speed, cluster->contraction_time ); fixed_t timefrm; fixed_t timepos; Vertex3 object_space; int cur_load = 0; // create full loads for ( cur_load = 0; cur_load < numloads; cur_load++ ) { timefrm = cluster->cur_contraction_time - ( ( cur_load + 1 ) * cluster->contraction_time ); timepos = timefrm * shippo->PhotonSpeed; // create one full frame set back because the current frame will // be added by the linear particle animation code in the same frame timepos -= speed * TheSimulator->GetThisFrameRefFrames(); for ( int i = 0 ; i < single_load; i++ ) { CalcSphereParticlePosition( object_space, radius, SAT_SPHERETYPE_NORMAL ); object_space.Z += FIXED_TO_GEOMV( 0x10000 * cluster->contraction_time ) + FIXED_TO_GEOMV( timepos ); Vertex3 world_space; MtxVctMUL( shippo->ObjPosition, &object_space, &world_space ); particle_s particle; PRT_InitParticle( particle, color, sizebound, ref_z, &world_space, &dirvec, lifetime, playerid, &extinfo ); particle.flags |= PARTICLE_COLLISION; particle.flags |= PARTICLE_IS_PHOTON; PRT_CreateLinearParticle( particle ); } } // check if last load should be created if ( ( cluster->cur_contraction_time / cluster->contraction_time ) > ( old_numel / single_load ) ) { timefrm = cluster->cur_contraction_time - ( ( cur_load + 1 ) * cluster->contraction_time ); timepos = timefrm * shippo->PhotonSpeed; // create one full frame set back because the current frame will // be added by the linear particle animation code in the same frame timepos -= speed * TheSimulator->GetThisFrameRefFrames(); for ( int i = 0 ; i < ( old_numel % single_load ); i++ ) { CalcSphereParticlePosition( object_space, radius, SAT_SPHERETYPE_NORMAL ); object_space.Z += FIXED_TO_GEOMV( 0x10000 * cluster->contraction_time ) + FIXED_TO_GEOMV( timepos ); Vertex3 world_space; MtxVctMUL( shippo->ObjPosition, &object_space, &world_space ); particle_s particle; PRT_InitParticle( particle, color, sizebound, ref_z, &world_space, &dirvec, lifetime, playerid, &extinfo ); particle.flags |= PARTICLE_COLLISION; particle.flags |= PARTICLE_IS_PHOTON; PRT_CreateLinearParticle( particle ); } } // calculate remaining visible sphere particles cluster->numel -= ( cluster->cur_contraction_time / cluster->contraction_time ) * single_load; if ( cluster->numel < 0 ) { cluster->numel = 0; } int next_numel = cluster->numel - single_load; if ( next_numel < 0 ) { next_numel = 0; } if ( cluster->numel == 0 ) { PRT_DeleteCluster(cluster); } else { // contract and rotate appropriate amount of particles // set old and new center values old_center.X = cluster->center.X; old_center.Y = cluster->center.Y; old_center.Z = ( cluster->cur_contraction_time / cluster->contraction_time ) ? 0 : cluster->center.Z; cluster->center.Z = FIXED_TO_GEOMV( 0x10000 * ( cluster->cur_contraction_time % cluster->contraction_time ) ); contraction = ( cluster->cur_contraction_time < cluster->contraction_time ) ? ( cluster->contraction_speed * TheSimulator->GetThisFrameRefFrames() ) : ( cluster->contraction_speed * ( cluster->cur_contraction_time % cluster->contraction_time ) ); bams_t pitch = cluster->pitch * TheSimulator->GetThisFrameRefFrames(); bams_t yaw = cluster->yaw * TheSimulator->GetThisFrameRefFrames(); bams_t roll = cluster->roll * TheSimulator->GetThisFrameRefFrames(); int pid = 0; for ( pid = ( cluster->numel - 1 ); pid >= next_numel; pid-- ) { cluster->rep[ pid ].position.X -= old_center.X; cluster->rep[ pid ].position.Y -= old_center.Y; cluster->rep[ pid ].position.Z -= old_center.Z; //FIXME ?: // particles may not be contained in // ship-bounding-sphere any more CalcSphereParticleRotation( cluster->rep[ pid ].position, pitch, yaw, roll ); CalcSphereContraction( cluster->rep[ pid ].position, contraction ); // move particles forward cluster->rep[ pid ].position.X += cluster->center.X; cluster->rep[ pid ].position.Y += cluster->center.Y; cluster->rep[ pid ].position.Z += cluster->center.Z; } for ( pid = next_numel - 1; pid >= 0; pid-- ) { CalcSphereParticleRotation( cluster->rep[ pid ].position, pitch, yaw, roll ); } // update cluster radius ? cluster->bdsphere -= contraction; } cluster->cur_contraction_time %= cluster->contraction_time; } else { bams_t pitch = cluster->pitch * TheSimulator->GetThisFrameRefFrames(); bams_t yaw = cluster->yaw * TheSimulator->GetThisFrameRefFrames(); bams_t roll = cluster->roll * TheSimulator->GetThisFrameRefFrames(); for ( int pid = 0; pid < cluster->numel; pid++ ) { CalcSphereParticleRotation( cluster->rep[ pid ].position, pitch, yaw, roll ); } } } // check if genobject has attached particle clusters of a certain type -------- // objectbase_pcluster_s * E_World::PRT_ObjectHasAttachedClustersOfType( GenObject* genobjpo, int animtype ) { ASSERT( genobjpo != NULL ); objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; for ( ; scan; scan = scan->attachlist ) { ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); if ( scan->animtype == animtype ) return scan; } return NULL; } void LinearParticleCollision( linear_pcluster_s *cluster, int pid ) { TheGameCollDet->LinearParticleCollision(cluster, pid); } // ---------------------------------------------------------------------------- // void SWARM_TimedAnimate( callback_pcluster_s* cluster ) { ASSERT( cluster != NULL ); if ( TheWorld->SWARM_Animate( cluster ) == FALSE ) return; }