/*============================================================================= Name : univupdate Purpose : updates the universe, main game loop Created 10/4/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include #include #include #include "glinc.h" #include "types.h" #include "debug.h" #include "matrix.h" #include "fastmath.h" #include "linkedlist.h" #include "objtypes.h" #include "spaceobj.h" #include "memory.h" #include "collision.h" #include "physics.h" #include "aitrack.h" #include "aiship.h" #include "commandlayer.h" #include "universe.h" #include "select.h" #include "statscript.h" #include "soundevent.h" #include "color.h" #include "globals.h" #include "flightman.h" #include "univupdate.h" #include "DFGFrigate.h" #include "ships.h" #include "dock.h" #include "mainrgn.h" #include "pieplate.h" #include "sensors.h" #include "file.h" #include "netcheck.h" #include "commandwrap.h" #include "utility.h" #include "singleplayer.h" #include "nis.h" #include "mex.h" #include "mouse.h" #include "aiplayer.h" #include "clouds.h" #include "nebulae.h" #include "blobs.h" #include "strings.h" #include "researchapi.h" #include "tactics.h" #include "infooverlay.h" #include "multiplayergame.h" #include "meshanim.h" #include "alliance.h" #include "damage.h" #include "madLinkIn.h" #include "madLinkInDefs.h" #include "clamp.h" #include "levelload.h" #include "attributes.h" #include "ping.h" #include "teams.h" #include "bounties.h" #include "tutor.h" #include "trademgr.h" #include "crates.h" #include "SaveGame.h" #include "battle.h" #include "randy.h" #include "hs.h" #include "launchmgr.h" #include "ProfileTimers.h" #ifndef HW_Release #if 0 #ifdef gshaw #define DEBUG_COLLISIONS #endif #endif #endif vector defaultshipupvector = { 0.0f, 0.0f, 1.0f }; vector defaultshiprightvector = { 0.0f, -1.0f, 0.0f }; vector defaultshipheadingvector = { 1.0f, 0.0f, 0.0f }; matrix defaultshipmatrix = { 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f }; IDToPtrTable ShipIDToPtr; IDToPtrTable ResourceIDToPtr; IDToPtrTable DerelictIDToPtr; IDToPtrTable MissileIDToPtr; /*============================================================================= Private functions: =============================================================================*/ void setSalvageInfo(SpaceObjRotImpTargGuidanceShipDerelict *object); bool ApplyDamageToTarget(SpaceObjRotImpTarg *target,real32 damagetaken,GunSoundType soundType,sdword damageHow,sdword playerIndex); static void MakeExplosionRockBumpables(Ship *ship,bool scuttle); void pointExplosionInSpace(vector *position,real32 blastRadius,real32 maxDamage,sdword playerIndex); void univCheckShipState(Ship *ship); void univSetupShipForControl(Ship *ship); void univMinorSetupShipForControl(Ship *ship); /****************************************************************************** Private data ******************************************************************************/ bool gameIsEnding=FALSE; /*============================================================================= Tweakables =============================================================================*/ real32 FLY_INTO_WORLD_PERCENT_DIST = 0.7f; udword NUM_STARS = 800; udword NUM_BIG_STARS = 175; color backgroundColor = colRGB(0, 25, 0); real32 MAX_VELOCITY_TANGENT_FACTOR = 1.0f; sdword MIN_COLLISION_DAMAGE = 5; scriptEntry StarTweaks[] = { { "NumStars", scriptSetUdwordCB, &NUM_STARS }, { "NumBigStars", scriptSetUdwordCB, &NUM_BIG_STARS }, { "starMaxColor", scriptSetUdwordCB, &starMaxColor }, { "starMinColor", scriptSetUdwordCB, &starMinColor }, { "starRedVariance", scriptSetUdwordCB, &starRedVariance }, { "starGreenVariance", scriptSetUdwordCB, &starGreenVariance }, { "starBlueVariance", scriptSetUdwordCB, &starBlueVariance }, { NULL, NULL, 0 } }; scriptEntry GalaxyTweaks[] = { { "BackgroundColor", scriptSetRGBCB, &backgroundColor }, { NULL, NULL, 0 } }; void univUpdateObjRotInfo(SpaceObjRot *robj) { #define irobj ((SpaceObjRotImp *)robj) #define shipobj ((Ship *)robj) if (bitTest(robj->flags,SOF_Impactable)) { matMultiplyMatByVec(&irobj->collInfo.collOffset,&irobj->rotinfo.coordsys,&irobj->staticinfo->staticheader.staticCollInfo.collsphereoffset); vecAdd(irobj->collInfo.collPosition,irobj->posinfo.position,irobj->collInfo.collOffset); collUpdateCollRectangle(irobj); if (irobj->objtype == OBJ_ShipType) { matMultiplyMatByVec(&shipobj->engineOffset,&shipobj->rotinfo.coordsys,&((ShipStaticInfo *)shipobj->staticinfo)->engineNozzleOffset[0]); vecAdd(shipobj->enginePosition,shipobj->posinfo.position,shipobj->engineOffset); } } } /*----------------------------------------------------------------------------- Name : univRotateObjPitch Description : rotates an obj about its right vector (producing pitch), correctly rotating collision information, etc. Inputs : obj Outputs : Return : ----------------------------------------------------------------------------*/ void univRotateObjPitch(SpaceObjRot *robj,real32 rot) { matrix rotymat; matrix tempmat; matMakeRotAboutY(&rotymat,(real32)cos(rot),(real32)sin(rot)); tempmat = robj->rotinfo.coordsys; matMultiplyMatByMat(&robj->rotinfo.coordsys,&tempmat,&rotymat); univUpdateObjRotInfo(robj); } /*----------------------------------------------------------------------------- Name : univRotateObjYaw Description : rotates an obj about its up vector (producing yaw), correctly rotating collision information, etc. Inputs : obj Outputs : Return : ----------------------------------------------------------------------------*/ void univRotateObjYaw(SpaceObjRot *robj,real32 rot) { matrix rotxmat; matrix tempmat; matMakeRotAboutX(&rotxmat,(real32)cos(rot),(real32)sin(rot)); tempmat = robj->rotinfo.coordsys; matMultiplyMatByMat(&robj->rotinfo.coordsys,&tempmat,&rotxmat); univUpdateObjRotInfo(robj); } /*----------------------------------------------------------------------------- Name : univRotateObjRoll Description : rotates an obj about its heading vector (producing Roll), correctly rotating collision information, etc. Inputs : obj Outputs : Return : ----------------------------------------------------------------------------*/ void univRotateObjRoll(SpaceObjRot *robj,real32 rot) { matrix rotzmat; matrix tempmat; matMakeRotAboutZ(&rotzmat,(real32)cos(rot),(real32)sin(rot)); tempmat = robj->rotinfo.coordsys; matMultiplyMatByMat(&robj->rotinfo.coordsys,&tempmat,&rotzmat); univUpdateObjRotInfo(robj); } /*----------------------------------------------------------------------------- Name : univInitSpaceObjPosRot Description : initializes the position and rotation info of a space object, including orientation (heading), position Inputs : position, randomOrientation flag Outputs : Return : ----------------------------------------------------------------------------*/ void univInitSpaceObjPosRot(SpaceObj *obj,vector *position,bool randomOrientation) { memset(&(obj->posinfo),0,sizeof(obj->posinfo)); obj->posinfo.position = *position; obj->posinfo.isMoving = FALSE; obj->posinfo.haventCalculatedDist = TRUE; if (bitTest(obj->flags,SOF_Rotatable)) { #define robj ((SpaceObjRot *)obj) memset(&robj->rotinfo,0,sizeof(robj->rotinfo)); robj->rotinfo.coordsys = defaultshipmatrix; if (randomOrientation) { real32 rotaboutx = frandom(2*PI); real32 rotabouty = frandom(2*PI); real32 rotaboutz = frandom(2*PI); matrix rotxmat; matrix rotymat; matrix rotzmat; matrix tempmat; matMakeRotAboutX(&rotxmat,(real32)cos(rotaboutx),(real32)sin(rotaboutx)); matMakeRotAboutY(&rotymat,(real32)cos(rotabouty),(real32)sin(rotabouty)); matMakeRotAboutZ(&rotzmat,(real32)cos(rotaboutz),(real32)sin(rotaboutz)); tempmat = robj->rotinfo.coordsys; matMultiplyMatByMat(&robj->rotinfo.coordsys,&tempmat,&rotxmat); matMultiplyMatByMat(&tempmat,&robj->rotinfo.coordsys,&rotymat); matMultiplyMatByMat(&robj->rotinfo.coordsys,&tempmat,&rotzmat); } univUpdateObjRotInfo((SpaceObjRot *)obj); } } #define IDTOPTR_GROWBATCH 250 void IDToPtrTableInit(IDToPtrTable *table) { table->numEntries = 0; table->objptrs = NULL; } void IDToPtrTableClose(IDToPtrTable *table) { if (table->objptrs) { memFree(table->objptrs); table->objptrs = NULL; } table->numEntries = 0; } void IDToPtrTableReset(IDToPtrTable *table) { IDToPtrTableClose(table); IDToPtrTableInit(table); } void IDToPtrTableObjDied(IDToPtrTable *table,uword ID) { if (ID >= table->numEntries) { return; } table->objptrs[ID] = NULL; // remove reference } SpaceObjPtr IDToPtrTableIDToObj(IDToPtrTable *table,uword ID) { if (ID >= table->numEntries) { return NULL; } return table->objptrs[ID]; } void IDToPtrTableAdd(IDToPtrTable *table,uword ID,SpaceObj *obj) { dbgAssert(obj); if (ID >= table->numEntries) { sdword newnumber = table->numEntries+IDTOPTR_GROWBATCH; #ifdef gshaw if (ID > (table->numEntries+1)) { _asm int 3 } #endif if (table->objptrs) { table->objptrs = memRealloc(table->objptrs,sizeof(SpaceObjPtr) * newnumber,"idtoptrs",0); } else { table->objptrs = memAlloc(sizeof(SpaceObjPtr) * newnumber,"idtoptrs",0); } memset(&table->objptrs[table->numEntries],0,sizeof(SpaceObjPtr)*IDTOPTR_GROWBATCH); table->numEntries += IDTOPTR_GROWBATCH; } dbgAssert(ID < table->numEntries); if (table->objptrs[ID]) { dbgFatalf(DBG_Loc,"Obj with this ID %d already exists",ID); } else { table->objptrs[ID] = obj; } return; } /*============================================================================= Save Game Stuff =============================================================================*/ #pragma warning( 4 : 4047) // turns off "different levels of indirection warning" typedef struct SaveIDToPtrTableStruct { sdword num; sdword ID[1]; } SaveIDToPtrTableStruct; #define sizeofSaveIDToPtrTableStruct(n) (sizeof(SaveIDToPtrTableStruct) + (n-1)*sizeof(sdword)) void SaveIDToPtrTable(IDToPtrTable *table) { sdword num = table->numEntries; SaveChunk *chunk; struct SaveIDToPtrTableStruct *savecontents; sdword i; chunk = CreateChunk(IDTOPTRTABLE,sizeofSaveIDToPtrTableStruct(num),NULL); savecontents = chunkContents(chunk); savecontents->num = num; for (i=0;iID[i] = SpaceObjRegistryGetID(table->objptrs[i]); } SaveThisChunk(chunk); memFree(chunk); } void LoadIDToPtrTable(IDToPtrTable *table) { SaveChunk *chunk; SaveIDToPtrTableStruct *loadcontents; sdword num; sdword i; chunk = LoadNextChunk(); VerifyChunkNoSize(chunk,IDTOPTRTABLE); loadcontents = (SaveIDToPtrTableStruct *)chunkContents(chunk); num = loadcontents->num; dbgAssert(sizeofSaveIDToPtrTableStruct(num) == chunk->contentsSize); table->numEntries = num; if (num == 0) { table->objptrs = NULL; } else { table->objptrs = memAlloc(sizeof(SpaceObjPtr) * num,"idtoptrs",0); } for (i=0;iobjptrs[i] = SpaceObjRegistryGetObj(loadcontents->ID[i]); } memFree(chunk); } #pragma warning( 2 : 4047) // turn back on "different levels of indirection warning" /*============================================================================= Save Game Stuff End =============================================================================*/ void univInitFastNetworkIDLookups(void) { IDToPtrTableInit(&ShipIDToPtr); IDToPtrTableInit(&ResourceIDToPtr); IDToPtrTableInit(&DerelictIDToPtr); IDToPtrTableInit(&MissileIDToPtr); } void univCloseFastNetworkIDLookups(void) { IDToPtrTableClose(&ShipIDToPtr); IDToPtrTableClose(&ResourceIDToPtr); IDToPtrTableClose(&DerelictIDToPtr); IDToPtrTableClose(&MissileIDToPtr); } void univResetFastNetworkIDLookups(void) { IDToPtrTableReset(&ShipIDToPtr); IDToPtrTableReset(&ResourceIDToPtr); IDToPtrTableReset(&DerelictIDToPtr); IDToPtrTableReset(&MissileIDToPtr); } /*----------------------------------------------------------------------------- Name : ShipIDtoShip Description : Converts a ShipID to a pointer to the actual ship Inputs : shipID Outputs : Return : Notes : Later optimize this routine (perhaps use a table look-up) ----------------------------------------------------------------------------*/ Ship *ShipIDtoShip(ShipID shipID,bool considerInsideShips) { Ship *ship = (Ship *)IDToPtrTableIDToObj(&ShipIDToPtr,shipID.shipNumber); if (ship != NULL) { dbgAssert(ship->objtype == OBJ_ShipType); if (!considerInsideShips) { if (ship->flags & SOF_Hide) { ship = NULL; } } } return ship; } /*----------------------------------------------------------------------------- Name : univFindShipIAmInside Description : returns the ship I am inside if I'm inside one, otherwise NULL Inputs : me Outputs : Return : returns the ship I am inside if I'm inside one, otherwise NULL ----------------------------------------------------------------------------*/ Ship *univFindShipIAmInside(Ship *me) { Node *objnode = universe.ShipList.head; Ship *ship; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->shipsInsideMe) { Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship; while (insidenode != NULL) { insideship = (InsideShip *)listGetStructOfNode(insidenode); dbgAssert(insideship->ship->objtype == OBJ_ShipType); if (insideship->ship == me) { return ship; } insidenode = insidenode->next; } } objnode = objnode->next; } return NULL; } /*----------------------------------------------------------------------------- Name : univAmIInsideThisShip Description : returns TRUE if me ship is inside ship Inputs : me,ship Outputs : Return : returns TRUE if me ship is inside ship ----------------------------------------------------------------------------*/ bool univAmIInsideThisShip(Ship *me,Ship *ship) { if (ship->shipsInsideMe) { Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship; while (insidenode != NULL) { insideship = (InsideShip *)listGetStructOfNode(insidenode); dbgAssert(insideship->ship->objtype == OBJ_ShipType); if (insideship->ship == me) { return TRUE; } insidenode = insidenode->next; } } return FALSE; } /*----------------------------------------------------------------------------- Name : ResourceIDtoResourcePtr Description : Converts a resourceID to a pointer to the actual resource Inputs : resourceID Outputs : Return : Notes : Later optimize this routine (perhaps use a table look-up) ----------------------------------------------------------------------------*/ ResourcePtr ResourceIDtoResourcePtr(ResourceID resourceID) { return (Resource *)IDToPtrTableIDToObj(&ResourceIDToPtr,resourceID.resourceNumber); } /*----------------------------------------------------------------------------- Name : DerelictIDtoDerelictPtr Description : Converts a derelictID to a pointer to the actual derelict Inputs : derelictID Outputs : Return : Notes : Later optimize this routine (perhaps use a table look-up) ----------------------------------------------------------------------------*/ DerelictPtr DerelictIDToDerelictPtr(DerelictID derelictID) { return (Derelict *)IDToPtrTableIDToObj(&DerelictIDToPtr,derelictID.derelictNumber); } /*----------------------------------------------------------------------------- Name : MissileIDtoMissilePtr Description : Converts a missileID to a pointer to the actual missile Inputs : missileID Outputs : Return : Notes : Later optimize this routine (perhaps use a table look-up) ----------------------------------------------------------------------------*/ MissilePtr MissileIDtoMissilePtr(MissileID missileID) { return (Missile *)IDToPtrTableIDToObj(&MissileIDToPtr,missileID.missileNumber); } /*----------------------------------------------------------------------------- Name : univAddAsteroid Description : adds an asteroid to the mission sphere at position astpos Inputs : astpos Outputs : Return : ----------------------------------------------------------------------------*/ Asteroid *univAddAsteroid(AsteroidType asteroidtype,vector *astpos) { Asteroid *newAsteroid; dbgAssert(asteroidtype < NUM_ASTEROIDTYPES); newAsteroid = memAlloc(sizeof(Asteroid),"Asteroid",NonVolatile); newAsteroid->resourceID.resourceNumber = universe.resourceNumber; IDToPtrTableAdd(&ResourceIDToPtr,universe.resourceNumber,(SpaceObj *)newAsteroid); universe.resourceNumber++; newAsteroid->objtype = OBJ_AsteroidType; newAsteroid->flags = SOF_Rotatable + SOF_Impactable + SOF_Targetable + SOF_Resource; if (asteroidtype == Asteroid0) newAsteroid->flags |= SOF_DontApplyPhysics; // optimization for Asteroid0 newAsteroid->staticinfo = (ResourceStaticInfo *)&asteroidStaticInfos[asteroidtype]; ClearNode(newAsteroid->renderlink); newAsteroid->collMyBlob = NULL; newAsteroid->currentLOD = 0; newAsteroid->cameraDistanceSquared = REALlyBig; newAsteroid->flashtimer = 0; newAsteroid->lasttimecollided = 0; newAsteroid->attributes = 0; newAsteroid->attributesParam = 0; newAsteroid->resourcevalue = asteroidStaticInfos[asteroidtype].resourcevalue; newAsteroid->resourceNotAccessible = 0; newAsteroid->resourceVolume = NULL; newAsteroid->asteroidtype = asteroidtype; newAsteroid->scaling = 1.0f; newAsteroid->health = newAsteroid->staticinfo->maxhealth; newAsteroid->collInfo.precise = NULL; vecZeroVector(newAsteroid->posinfo.force); vecZeroVector(newAsteroid->rotinfo.torque); univInitSpaceObjPosRot((SpaceObj *)newAsteroid,astpos,TRUE); if (asteroidtype == Asteroid0) { newAsteroid->collMyBlob = NULL; listAddNode(&universe.MinorSpaceObjList,&(newAsteroid->objlink),newAsteroid); } else { collAddSpaceObjToCollBlobs((SpaceObj *)newAsteroid); listAddNode(&universe.SpaceObjList,&(newAsteroid->objlink),newAsteroid); listAddNode(&universe.ResourceList,&(newAsteroid->resourcelink),newAsteroid); listAddNode(&universe.ImpactableList,&(newAsteroid->impactablelink),newAsteroid); } return newAsteroid; } /*----------------------------------------------------------------------------- Name : univAddDerelict Description : adds a derelict to the mission sphere at position pos Inputs : pos Outputs : Return : ----------------------------------------------------------------------------*/ Derelict *univAddDerelict(DerelictType derelicttype,vector *pos) { dbgAssert(derelicttype < NUM_DERELICTTYPES); return(univAddDerelictByStatInfo(derelicttype, (DerelictStaticInfo *)&derelictStaticInfos[derelicttype], pos)); } Derelict *univAddHyperspaceGateAsDerelict(hvector *posAndRotation) { Derelict *derelict = univAddDerelict(HyperspaceGate,(vector *)posAndRotation); univRotateObjYaw((SpaceObjRot *)derelict,posAndRotation->w); bitSet(derelict->flags,SOF_NotBumpable|SOF_DontApplyPhysics); #ifdef gshaw //bitSet(derelict->flags,SOF_ForceVisible); #endif return derelict; } /*----------------------------------------------------------------------------- Name : univAddMissile Description : Adds a missile to the world. This missile will have no owner, no target and it's velocity, position and coordsys must be set up by the owner. Some missile this is! Inputs : race - race of missile to create Outputs : Return : pointer to newly created missile ----------------------------------------------------------------------------*/ Missile *univAddMissile(ShipRace race) { Missile *missile; sdword i; missile = memAlloc(sizeof(Missile),"Missile",0); memset(missile,0,sizeof(Missile)); missile->objtype = OBJ_MissileType; missile->flags = SOF_Rotatable + SOF_Impactable + SOF_NotBumpable + SOF_Targetable; missile->enginePosition.x = 0.0f; missile->enginePosition.y = 0.0f; missile->enginePosition.z = 0.0f; missile->trail = NULL; missile->staticinfo = &missileStaticInfos[race]; missile->maxtraveldist = missile->staticinfo->staticheader.maxvelocity * universe.phystimeelapsed; missile->missileType = MISSILE_Regular; missile->FORCE_DROPPED = FALSE; missile->formationinfo = NULL; missile->trail = mistrailNew(missile->staticinfo->trailStatic, missile); missile->health = ((MissileStaticInfo *)missile->staticinfo)->maxhealth; ClearNode(missile->renderlink); missile->currentLOD = 0; missile->cameraDistanceSquared = 1.0f; //!!! missile->flashtimer = 0; missile->lasttimecollided = 0; missile->attributes = 0; missile->attributesParam = 0; vecZeroVector(missile->posinfo.force); vecZeroVector(missile->rotinfo.torque); for (i=0;inonstatvars.thruststrength[i] = missile->staticinfo->thruststrengthstat[i]; } for(i=0;i< NUM_ROT_DEGOFFREEDOM;i++) { missile->nonstatvars.rotstrength[i] = missile->staticinfo->rotstrengthstat[i]; } for(i=0;i< NUM_TURN_TYPES;i++) { missile->nonstatvars.turnspeed[i] = missile->staticinfo->turnspeedstat[i]; } missile->soundType = GS_MissleLauncher; missile->race = race; missile->timelived = 0.0f; missile->totallifetime = 12.26f; //!!! from the missile destroyer .ship file missile->playerowner = &universe.players[((race == universe.players[0].race)) ? 0 : 1]; missile->owner = NULL; missile->target = NULL; missile->damage = 48.0f; //from the.shp file missile->missileID.missileNumber = universe.missileNumber; if (multiPlayerGame) IDToPtrTableAdd(&MissileIDToPtr,universe.missileNumber,(SpaceObj *)missile); universe.missileNumber++; //position, velocity and coordsys left blank missile->posinfo.isMoving = TRUE; missile->posinfo.haventCalculatedDist = TRUE; collAddSpaceObjToCollBlobs((SpaceObj *)missile); //proper hit effect missile->hitEffect = etgGunEventTable[race][GS_MissleLauncher][EGT_GunHit];//get pointer to hit effect listAddNode(&universe.SpaceObjList,&(missile->objlink),missile); listAddNode(&universe.ImpactableList,&(missile->impactablelink),missile); listAddNode(&universe.MissileList,&(missile->missilelink),missile); return(missile); } /*----------------------------------------------------------------------------- Name : univAddToWorldList Description : Add a derelict to the universe's world list Inputs : world - what object to add to the world list Outputs : Return : void ----------------------------------------------------------------------------*/ void univAddToWorldList(Derelict *world) { sdword index; for (index = 0; index < UNIV_NUMBER_WORLDS; index++) { if (universe.world[index] == NULL) { universe.world[index] = world; return; } } dbgAssert(FALSE); } /*----------------------------------------------------------------------------- Name : univRemoveFromWorldList Description : Remove a derelict reference from the universe's world list Inputs : world - what world we are to remove Outputs : Return : void ----------------------------------------------------------------------------*/ void univRemoveFromWorldList(Derelict *world) { sdword index, j; for (index = 0; index < UNIV_NUMBER_WORLDS; index++) { if (universe.world[index] == world) { if (index < UNIV_NUMBER_WORLDS - 1) { for (j = UNIV_NUMBER_WORLDS - 1; j > index; j--) { if (universe.world[j] != NULL) { universe.world[index] = universe.world[j]; universe.world[j] = NULL; return; } } } universe.world[index] = NULL; break; } } } /*----------------------------------------------------------------------------- Name : univAddDerelictByStatInfo Description : Add a derelict by direct staticinfo pointer rather than just by type. Mostly used for generic objects in NIS's. Inputs : derelictType - type of derelict. Can be outside range of 0..NUM_DERELICTTYPES stat - static info for this derelict. pos - where to create it Outputs : Return : Newly allocated derelict. ----------------------------------------------------------------------------*/ Derelict *univAddDerelictByStatInfo(DerelictType derelictType, DerelictStaticInfo *stat, vector *pos) { Derelict *newDerelict; sdword i; newDerelict = memAlloc(sizeof(Derelict),"Derelict",NonVolatile); newDerelict->derelictID.derelictNumber = universe.derelictNumber; IDToPtrTableAdd(&DerelictIDToPtr,universe.derelictNumber,(SpaceObj *)newDerelict); universe.derelictNumber++; newDerelict->objtype = OBJ_DerelictType; newDerelict->flags = SOF_Rotatable + SOF_Impactable + SOF_Targetable; newDerelict->staticinfo = stat; ClearNode(newDerelict->renderlink); newDerelict->collMyBlob = NULL; newDerelict->renderedLODs = 0; newDerelict->currentLOD = 0; newDerelict->cameraDistanceSquared = REALlyBig; newDerelict->flashtimer = 0; newDerelict->lasttimecollided = 0; newDerelict->attributes = 0; newDerelict->attributesParam = 0; newDerelict->derelicttype = derelictType; newDerelict->health = newDerelict->staticinfo->maxhealth; newDerelict->collInfo.precise = NULL; newDerelict->dockingship = NULL; newDerelict->putOnDoor=FALSE; newDerelict->TractorTrue = FALSE; newDerelict->tractorbeaminfo = 0; newDerelict->tractorbeam_playerowner = NULL; newDerelict->tractorPosinfo[0] = FALSE; newDerelict->tractorPosinfo[1] = FALSE; newDerelict->tractorPosinfo[2] = FALSE; newDerelict->tractorPosinfo[3] = FALSE; newDerelict->aistate = 0; newDerelict->specialFlags = 0; newDerelict->specialFlags2 = 0; newDerelict->clampInfo = NULL; newDerelict->creationTime = universe.totaltimeelapsed; newDerelict->ambientSoundHandle = SOUND_NOTINITED; newDerelict->randomSoundHandle = SOUND_NOTINITED; newDerelict->nextRandomTime = universe.totaltimeelapsed; vecZeroVector(newDerelict->posinfo.force); vecZeroVector(newDerelict->rotinfo.torque); for(i=0;isalvageNumTagged[i] = 0; } for (i=0;inonstatvars.thruststrength[i] = stat->thruststrengthstat[i]; } for(i=0;i< NUM_ROT_DEGOFFREEDOM;i++) { newDerelict->nonstatvars.rotstrength[i] = stat->rotstrengthstat[i]; } for(i=0;i< NUM_TURN_TYPES;i++) { newDerelict->nonstatvars.turnspeed[i] = stat->turnspeedstat[i]; } newDerelict->newDockWithTransfer = NULL; univInitSpaceObjPosRot((SpaceObj *)newDerelict,pos,FALSE); //needed hack to Fix LUKES ridiculous NIS hack if(derelictType < NUM_DERELICTTYPES) { //yes, it is a valid derelict setSalvageInfo((SpaceObjRotImpTargGuidanceShipDerelict *)newDerelict); } else { //fix it for good: stat->salvageStaticInfo = NULL; newDerelict->salvageInfo = NULL; } InitializeNavLights((Ship *)newDerelict); collAddSpaceObjToCollBlobs((SpaceObj *)newDerelict); listAddNode(&universe.SpaceObjList,&(newDerelict->objlink),newDerelict); listAddNode(&universe.DerelictList,&(newDerelict->derelictlink),newDerelict); listAddNode(&universe.ImpactableList,&(newDerelict->impactablelink),newDerelict); if (stat->worldRender) { //if planet-style rendering // bitSet(newDerelict->flags, SOF_ForceVisible); univAddToWorldList(newDerelict); } //find a color scheme for (i = 0; i < MAX_MULTIPLAYER_PLAYERS; i++) { if (stat->teamColor[i]) { newDerelict->colorScheme = i; break; } } //if a crate, report the placement! if (!singlePlayerGame) { if(derelictType == Crate) { cratesReportCratePlacement(newDerelict); } } return newDerelict; } /*----------------------------------------------------------------------------- Name : univAddDustCloud Description : adds a dustcloud to the mission sphere at position cloudpos Inputs : cloudpos Outputs : Return : ----------------------------------------------------------------------------*/ DustCloud *univAddDustCloud(DustCloudType dustcloudtype,vector *cloudpos) { DustCloud* newCloud; dbgAssert(dustcloudtype < NUM_DUSTCLOUDTYPES); newCloud = memAlloc(sizeof(DustCloud),"DustCloud",NonVolatile); newCloud->resourceID.resourceNumber = universe.resourceNumber; IDToPtrTableAdd(&ResourceIDToPtr,universe.resourceNumber,(SpaceObj *)newCloud); universe.resourceNumber++; newCloud->objtype = OBJ_DustType; newCloud->flags = SOF_Rotatable + SOF_Impactable + SOF_Targetable + SOF_Resource + SOF_NotBumpable; newCloud->staticinfo = (ResourceStaticInfo *)&dustcloudStaticInfos[dustcloudtype]; ClearNode(newCloud->renderlink); newCloud->collMyBlob = NULL; newCloud->currentLOD = 0; newCloud->cameraDistanceSquared = REALlyBig; newCloud->flashtimer = 0; newCloud->lasttimecollided = 0; newCloud->attributes = 0; newCloud->attributesParam = 0; newCloud->resourcevalue = dustcloudStaticInfos[dustcloudtype].resourcevalue; newCloud->resourceNotAccessible = 0; newCloud->resourceVolume = NULL; newCloud->dustcloudtype = dustcloudtype; newCloud->scaling = 1.0f; newCloud->health = newCloud->staticinfo->maxhealth; newCloud->collInfo.precise = NULL; newCloud->stub = (void*)cloudCreateSystem(newCloud->staticinfo->staticheader.staticCollInfo.collspheresize, 0.1f); { cloudSystem* csys = (cloudSystem*)newCloud->stub; csys->position = &newCloud->posinfo.position; csys->rotation = &newCloud->rotinfo.coordsys; csys->maxhealth = (sdword) newCloud->health; } vecZeroVector(newCloud->posinfo.force); vecZeroVector(newCloud->rotinfo.torque); univInitSpaceObjPosRot((SpaceObj *)newCloud,cloudpos,TRUE); collAddSpaceObjToCollBlobs((SpaceObj *)newCloud); listAddNode(&universe.SpaceObjList,&(newCloud->objlink),newCloud); listAddNode(&universe.ResourceList,&(newCloud->resourcelink),newCloud); listAddNode(&universe.ImpactableList,&(newCloud->impactablelink),newCloud); return newCloud; } /*----------------------------------------------------------------------------- Name : univAddGasCloud Description : adds a Gascloud to the mission sphere at position cloudpos Inputs : cloudpos Outputs : Return : ----------------------------------------------------------------------------*/ GasCloud *univAddGasCloud(GasCloudType gascloudtype,vector *cloudpos) { GasCloud *newCloud; dbgAssert(gascloudtype < NUM_GASCLOUDTYPES); newCloud = memAlloc(sizeof(GasCloud),"GasCloud",NonVolatile); newCloud->resourceID.resourceNumber = universe.resourceNumber; IDToPtrTableAdd(&ResourceIDToPtr,universe.resourceNumber,(SpaceObj *)newCloud); universe.resourceNumber++; newCloud->objtype = OBJ_GasType; newCloud->flags = SOF_Rotatable + SOF_Impactable + SOF_Targetable + SOF_Resource + SOF_NotBumpable; newCloud->staticinfo = (ResourceStaticInfo *)&gascloudStaticInfos[gascloudtype]; ClearNode(newCloud->renderlink); newCloud->collMyBlob = NULL; newCloud->currentLOD = 0; newCloud->cameraDistanceSquared = REALlyBig; newCloud->flashtimer = 0; newCloud->lasttimecollided = 0; newCloud->attributes = 0; newCloud->attributesParam = 0; newCloud->resourcevalue = gascloudStaticInfos[gascloudtype].resourcevalue; newCloud->resourceNotAccessible = 0; newCloud->resourceVolume = NULL; newCloud->gascloudtype = gascloudtype; newCloud->scaling = 1.0f; newCloud->health = newCloud->staticinfo->maxhealth; newCloud->collInfo.precise = NULL; vecZeroVector(newCloud->posinfo.force); vecZeroVector(newCloud->rotinfo.torque); univInitSpaceObjPosRot((SpaceObj *)newCloud,cloudpos,TRUE); collAddSpaceObjToCollBlobs((SpaceObj *)newCloud); listAddNode(&universe.SpaceObjList,&(newCloud->objlink),newCloud); listAddNode(&universe.ResourceList,&(newCloud->resourcelink),newCloud); listAddNode(&universe.ImpactableList,&(newCloud->impactablelink),newCloud); return newCloud; } /*----------------------------------------------------------------------------- Name : univAddNebula Description : adds a nebula to the mission sphere at position nebpos Inputs : nebpos Outputs : Return : ----------------------------------------------------------------------------*/ Nebula* univAddNebula(NebulaType nebulatype,vector* nebpos, void* stub) { Nebula* newNeb; dbgAssert(nebulatype < NUM_NEBULATYPES); newNeb = memAlloc(sizeof(Nebula), "Nebula", NonVolatile); newNeb->resourceID.resourceNumber = universe.resourceNumber; IDToPtrTableAdd(&ResourceIDToPtr,universe.resourceNumber,(SpaceObj *)newNeb); universe.resourceNumber++; newNeb->objtype = OBJ_NebulaType; newNeb->flags = SOF_Rotatable + SOF_Impactable + SOF_Targetable + SOF_Resource + SOF_NotBumpable; newNeb->staticinfo = (ResourceStaticInfo *)&nebulaStaticInfos[nebulatype]; ClearNode(newNeb->renderlink); newNeb->collMyBlob = NULL; newNeb->currentLOD = 0; newNeb->cameraDistanceSquared = REALlyBig; newNeb->flashtimer = 0; newNeb->lasttimecollided = 0; if (nebAttributes != 0) { newNeb->attributes = nebAttributes; } else { newNeb->attributes = 0; } newNeb->attributesParam = 0; newNeb->resourcevalue = nebulaStaticInfos[nebulatype].resourcevalue; newNeb->resourceNotAccessible = 0; newNeb->resourceVolume = NULL; newNeb->nebulatype = nebulatype; newNeb->scaling = 1.0f; newNeb->health = newNeb->staticinfo->maxhealth; newNeb->collInfo.precise = NULL; newNeb->stub = stub; nebNewNeb(newNeb); vecZeroVector(newNeb->posinfo.force); vecZeroVector(newNeb->rotinfo.torque); univInitSpaceObjPosRot((SpaceObj*)newNeb, nebpos, TRUE); collAddSpaceObjToCollBlobs((SpaceObj *)newNeb); listAddNode(&universe.SpaceObjList, &(newNeb->objlink), newNeb); listAddNode(&universe.ResourceList, &(newNeb->resourcelink), newNeb); listAddNode(&universe.ImpactableList, &(newNeb->impactablelink), newNeb); return newNeb; } /*----------------------------------------------------------------------------- Name : univDeleteEffect Description : Deletes an effect Inputs : effect - pointer to effect to delete Outputs : frees the effect and it's effector list (actually a single memFree operation) Return : ----------------------------------------------------------------------------*/ void univDeleteEffect(Effect *effect) { univRemoveObjFromRenderList((SpaceObj *)effect); listDeleteNode(&effect->objlink); } /*----------------------------------------------------------------------------- Name : univMeshBindingsDupe Description : Duplicate the local static bindings for a ship and fix up gun pointers local to this ship instance. Inputs : shipstaticinfo - staticinfo for this ship newShip - newly allocated ship Outputs : allocates new RAM and copies/fixes up the binding info Return : address of newly allocated RAM or NULL ----------------------------------------------------------------------------*/ shipbindings *univMeshBindingsDupe(ShipStaticInfo *shipstaticinfo, Ship *newShip) { shipbindings *newBindings; mhbinding *bindingListSource; mhlocalbinding *bindingListDest; sdword nLevels, index, nObjects, j; lodinfo *lodInfo = shipstaticinfo->staticheader.LOD; if (shipstaticinfo->hierarchySize == 0) { //do nothing if there is no bindings to dupe return(NULL); } nLevels = lodInfo->nLevels; //record number of LOD's newBindings = memAlloc(shipBindingsLength(nLevels) + //allocate bindings shipstaticinfo->hierarchySize * sizeof(mhlocalbinding), "ShipLocalBindings", NonVolatile); #if MESH_PRE_CALLBACK newBindings->preCallback = NULL; #endif newBindings->postCallback = NULL; //no callbacks needed for guns bindingListDest = (mhlocalbinding *)((ubyte *)newBindings + shipBindingsLength(nLevels)); //start of first binding list for (index = 0; index < nLevels; index++) { //for each LOD if ((lodInfo->level[index].flags & LM_LODType) != LT_Mesh) { newBindings->localBinding[index] = NULL; continue; } newBindings->localBinding[index] = bindingListDest; //store list pointer for this LOD nObjects = ((meshdata *)lodInfo->level[index].pData)->nPolygonObjects; bindingListSource = lodInfo->level[index].hBindings; // memcpy(bindingList, lodInfo->level[index].hBindings,//copy the bindings // nObjects * sizeof(mhbinding)); for (j = 0; j < nObjects; j++) { if (bindingListSource->function != NULL) { //!!! should do a switch statement based upon type, however, //there is only one type for now:guns dbgAssert(newShip->gunInfo != NULL); //using an index set up in setGunBindInfo(), set new gun pointer bindingListDest->function = bindingListSource->function; bindingListDest->userData = &newShip->gunInfo->guns[bindingListSource->userID]; bindingListDest->userID = (sdword)newShip; //also save ship pointer bindingListDest->flags = bindingListSource->flags; bindingListDest->object = bindingListSource->object; } else { bindingListDest->function = NULL; //this is all we need if there is no function } bindingListDest++; bindingListSource++; } // bindingListDest += nObjects; //update location of list } newBindings->frequencyCounter = 0; //counter expires right now dbgAssert((udword)((ubyte *)bindingListDest - (ubyte *)newBindings) == shipBindingsLength(nLevels) + shipstaticinfo->hierarchySize * sizeof(mhlocalbinding)); #if MESH_VERBOSE_LEVEL >= 2 dbgMessagef("\nunivMeshBindingsDupe: localizing bindings 0x%x for ship 0x%x lengh %d", newBindings, newShip, shipBindingsLength(nLevels) + shipstaticinfo->hierarchySize * sizeof(mhlocalbinding)); #endif return(newBindings); } /*----------------------------------------------------------------------------- Name : univMeshBindingsDelete Description : Delete the mesh bindings for a ship about to be deleted. Inputs : ship - ship being deleted Outputs : frees the memory Return : ----------------------------------------------------------------------------*/ void univMeshBindingsDelete(Ship *ship) { if (ship->madBindings && ship->madBindings->nCurrentAnim != MAD_NoAnimation) { //if there's a mesh animation going on ship->bindings = ship->madBindings->saveBindings; //restore old bindings } if (ship->bindings == NULL) { return; } #if MESH_VERBOSE_LEVEL >= 2 dbgMessagef("\nunivMeshBindingsDelete: freeing bindings for ship 0x%x", ship); #endif dbgAssert(ship->bindings != NULL); memFree(ship->bindings); } void InitializeEngineTrails(Ship *newship) { ShipStaticInfo *shipstaticinfo = newship->staticinfo; char engine[5]; sdword idx; sdword i; sdword numTrails; numTrails = 0; strcpy(engine, "EngX"); for (idx = 0; idx < MAX_NUM_TRAILS; idx++) { engine[3] = (char) ('0' + (char)idx); if (mexGetChunk((void*)shipstaticinfo->staticheader.pMexData, "Eng", engine) != 0) { numTrails = idx + 1; } } for (i = 0; i < MAX_NUM_TRAILS; i++) { newship->trail[i] = NULL; } if (shipstaticinfo->trailStatic[0] != NULL) { for (i = 0; i < numTrails; i++) { bool8 second = (bool8) ((i == 0) ? 0 : 1); newship->trail[i] = trailNew(shipstaticinfo->trailStatic[i], (void*)newship, second, (ubyte)i); } } else { //capital ship engine glow for (i = 0; i < numTrails; i++) { if (newship->shiprace == R2) { newship->trail[i] = trailNew(shipstaticinfo->trailStatic[i], (void*)newship, 2, (ubyte)i); } else { newship->trail[i] = trailNew(shipstaticinfo->trailStatic[i], (void*)newship, 3, (ubyte)i); } } } } void univResetNewGimbleGun(Gun *gun) { GunStatic *gunstatic = gun->gunstatic; dbgAssert(gunstatic->guntype == GUN_NewGimble); gun->angle = 0.0f; gun->declination = 0.0f; gun->anglespeed = 0.0f; gun->declinationspeed = 0.0f; gun->curgunorientation = gunstatic->defaultgunorientation; if (bitTest(gunstatic->flags, GF_MultiLevelMatrix)) { gun->curGunOrientationNonConcat = gunstatic->defaultGunOrientationNonConcat; } matGetVectFromMatrixCol3(gun->gunheading,gun->curgunorientation); } void InitializeNavLights(Ship *newship) { ShipStaticInfo *shipstaticinfo = newship->staticinfo; if (shipstaticinfo->navlightStaticInfo != NULL) { NAVLightStaticInfo *navlightstaticinfo = shipstaticinfo->navlightStaticInfo; sdword numNAVLights = navlightstaticinfo->numNAVLights; NAVLightStatic *curnavlightstatic; NAVLight *curnavlight; sdword i; dbgAssert(numNAVLights > 0); newship->navLightInfo = memAlloc(sizeofNavLightInfo(numNAVLights),"navlightInfo",NonVolatile); newship->navLightInfo->numLights = numNAVLights; for (i=0,curnavlight=&newship->navLightInfo->navLights[0],curnavlightstatic=&navlightstaticinfo->navlightstatics[0];inavlightstatic = curnavlightstatic; curnavlight->lightstate = 0; curnavlight->lastTimeFlashed = curnavlightstatic->startdelay; } } else { newship->navLightInfo = NULL; } } void InitializeShowingDamage(Ship* ship) { sdword i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { ship->showingDamage[i][j] = NULL; } } ship->nDamageLights = 0; ship->damageLights = NULL; dmgGetLights(ship); } /*----------------------------------------------------------------------------- Name : univCreateShip Description : creates a ship, but does not add it to the universe Inputs : shiptype, shiprace, shippos, playerowner, built - flag indicating if ship was built or placed in world Outputs : Return : address of ship created ----------------------------------------------------------------------------*/ Ship *univCreateShip(ShipType shiptype,ShipRace shiprace,vector *shippos,struct Player *playerowner,sdword built) { Ship *newship; udword sizespec; udword sizeship = sizeof(Ship), totalsize, madAssedSize; ShipStaticInfo *shipstaticinfo = GetShipStaticInfo(shiptype,shiprace); sdword i; ubyte *extraPointer; dbgAssert(playerowner != NULL); /* #ifndef HW_Release if (playerowner->race != shiprace) { dbgFatal(DBG_Loc,"Illegal race of ship for player"); } #endif */ #ifndef HW_Release if (!bitTest(shipstaticinfo->staticheader.infoFlags, IF_InfoLoaded)) { //if this ships was not loaded properly //#ifdef gshaw dbgFatalf(DBG_Loc,"\n%s\\%s was not loaded properly - stubbing out.", ShipRaceToStr(shiprace), ShipTypeToStr(shiptype)); //#endif dbgMessagef("\n%s\\%s was not loaded properly - stubbing out.", ShipRaceToStr(shiprace), ShipTypeToStr(shiptype)); universeForceDefaultShip = TRUE; InitStatShipInfo(shipstaticinfo, shiptype, shiprace); bitSet(shipstaticinfo->staticheader.infoFlags, IF_InfoLoaded); universeForceDefaultShip = FALSE; } #endif if ((sizespec = shipstaticinfo->custshipheader.sizeofShipSpecifics) != 0) { sizeship += sizespec - sizeof(udword); } totalsize = sizeship; if (shipstaticinfo->staticheader.staticCollInfo.preciseSelection != 0xff) { //account for the local precise selection totalsize += sizeof(PreciseSelection); } if (shipstaticinfo->madStatic != NULL) { //account for the local animation bindings madAssedSize = madAnimSize(shipstaticinfo->staticheader.LOD->nLevels) + //size of the binding info shipstaticinfo->hierarchySize * sizeof(mhlocalbinding) + //size of the actual binding structures shipstaticinfo->madStatic->header->nObjects * sizeof(splinecurve) * 6; //size of spline curves, enough for all objects totalsize += madAssedSize; } newship = memAlloc(totalsize,"Ship",NonVolatile); memset(newship,0,sizeship); // set everything to 0 by default extraPointer = (ubyte *)newship + sizeship; //point to end of ship where extra RAM was allocated newship->sizeofShipSpecifics = sizespec; newship->shipDeCloakTime = -20.0f; //initialize to a negative number so if a battle occurs at time 0.0f or anytime near by, then it will not screw up. newship->objtype = OBJ_ShipType; newship->flags = SOF_Rotatable + SOF_Impactable + SOF_Selectable + SOF_Targetable; if (shiptype == Drone) { newship->flags |= SOF_NotBumpable; } if (shipstaticinfo->staticheader.staticCollInfo.collspheresize > VERYBIGSHIP_SIZE) { newship->flags |= SOF_BigObject; } if (shipstaticinfo->staticheader.staticCollInfo.preciseSelection != 0xff) { // newship->collInfo.precise = (PreciseSelection *)(((ubyte *)newship) + sizeship); newship->collInfo.precise = (PreciseSelection *)extraPointer; extraPointer += sizeof(PreciseSelection); } else { newship->collInfo.precise = NULL; } newship->staticinfo = shipstaticinfo; bitClear(newship->specialFlags,SPECIAL_ParadeNeedTomoveCloser); // ClearNode(newship->renderlink); //need to copy over static info to non static structures so we //can beging fiddling with them... for (i=0;inonstatvars.thruststrength[i] = shipstaticinfo->thruststrengthstat[i]; } for(i=0;i< NUM_ROT_DEGOFFREEDOM;i++) { newship->nonstatvars.rotstrength[i] = shipstaticinfo->rotstrengthstat[i]; } for(i=0;i< NUM_TURN_TYPES;i++) { newship->nonstatvars.turnspeed[i] = shipstaticinfo->turnspeedstat[i]; } newship->shipID.shipNumber = universe.shipNumber; IDToPtrTableAdd(&ShipIDToPtr,universe.shipNumber,(SpaceObj *)newship); universe.shipNumber++; newship->shiptype = shiptype; newship->shiprace = shiprace; newship->tacticstype = Neutral; //default to neutral tactical setting newship->tactics_ordertype = NO_ORDER_NO_ENEMY; //default to no bonuses... newship->tacticsTalk = 0; newship->tacticsNeedToFlightMan = FALSE; newship->dockwaitforNorm=FALSE; newship->putOnDoor=FALSE; newship->tacticsFormationVar1 = FALSE; newship->DodgeTime = 0.0f; newship->isDodging = FALSE; newship->timeCreated = universe.totaltimeelapsed; // newship->currentLOD = 0; //start at highest LOD by default // newship->collMyBlob = NULL; newship->cameraDistanceSquared = REALlyBig; newship->playerowner = playerowner; newship->dockingship = NULL; newship->specialFlags = 0; newship->specialFlags = 0; newship->kamikazeState = 0; newship->visibleToWho = 0; //initialize cloaked visible flag to invisible to everyone! newship->visibleToWhoPreviousFrame = 0; newship->damageModifier = 1.0f; newship->forceCancelDock=FALSE; newship->hyperspacePing=FALSE; newship->gravwellTimeEffect=0.0f; newship->passiveAttackCancelTimer=0.0f; // Alex's Repair Droid stuff... // newship->pRepairDroids = memAlloc(sizeof(RepairDroid), "RDroid", NonVolatile); univInitSpaceObjPosRot((SpaceObj *)newship,shippos,FALSE); newship->tractorbeaminfo = 0; newship->tractorbeam_playerowner = NULL; newship->tractorPosinfo[0] = FALSE; newship->tractorPosinfo[1] = FALSE; newship->tractorPosinfo[2] = FALSE; newship->tractorPosinfo[3] = FALSE; newship->tractorstate = 0; //info variable for tractorbeaming state newship->needNewLeader = FALSE; for(i=0;isalvageNumTagged[i] = 0; } newship->clampInfo = NULL; newship->dontrotateever = FALSE; newship->dontapplyforceever = FALSE; newship->health = shipstaticinfo->maxhealth; newship->fuel = shipstaticinfo->maxfuel; // newship->resources = 0; // newship->aistate = 0; // newship->aistateattack = 0; // newship->hotKeyGroup = 0; newship->shipSinglePlayerGameInfo = spNewShipSinglePlayerGameInfo(); InitializeEngineTrails(newship); InitializeShowingDamage(newship); if (shipstaticinfo->gunStaticInfo != NULL) { sdword numGuns = shipstaticinfo->gunStaticInfo->numGuns; Gun *gun; GunStatic *gunstatic; newship->gunInfo = memAlloc(sizeofGunInfo(numGuns),"gunInfo",NonVolatile); newship->gunInfo->numGuns = numGuns; for (i=0;igunInfo->guns[i]; gunstatic = &shipstaticinfo->gunStaticInfo->gunstatics[i]; gun->gunstatic = gunstatic; switch (gunstatic->guntype) { case GUN_MissileLauncher: gun->numMissiles = gunstatic->maxMissiles; // deliberately fall through case GUN_MineLauncher: gun->numMissiles = gunstatic->maxMissiles; //Use same var for mines case GUN_Gimble: case GUN_Fixed: gun->gunheading = gunstatic->gunnormal; break; case GUN_NewGimble: univResetNewGimbleGun(gun); break; } gun->lasttimefired = 0.0f; gun->burstStartTime = 0.0f; gun->burstState = 0; gun->gimblehandle = SOUND_NOTINITED; } } else { newship->gunInfo = NULL; } setSalvageInfo((SpaceObjRotImpTargGuidanceShipDerelict *)newship); if (shipstaticinfo->dockStaticInfo != NULL) { DockStaticInfo *dockstaticinfo = shipstaticinfo->dockStaticInfo; sdword numDockPoints = dockstaticinfo->numDockPoints; DockStaticPoint *curdockstaticpoint; DockPoint *curdockpoint; dbgAssert(numDockPoints > 0); newship->dockInfo = memAlloc(sizeofDockInfo(numDockPoints),"dockInfo",NonVolatile); newship->dockInfo->busyness = 0; newship->dockInfo->numDockPoints = numDockPoints; for (i=0,curdockpoint=&newship->dockInfo->dockpoints[0],curdockstaticpoint=&dockstaticinfo->dockstaticpoints[0];idockstaticpoint = curdockstaticpoint; curdockpoint->thisDockBusy = 0; } } else { newship->dockInfo = NULL; } InitializeNavLights(newship); if (shipstaticinfo->canBuildShips | shipstaticinfo->canReceiveShipsPermanently) { newship->shipsInsideMe = memAlloc(sizeof(ShipsInsideMe),"ShipsInsideMe",NonVolatile); listInit(&newship->shipsInsideMe->insideList); newship->shipsInsideMe->FightersInsideme = 0; newship->shipsInsideMe->CorvettesInsideme = 0; // listInit(&newship->shipsInsideMe->launchList); } else { newship->shipsInsideMe = NULL; } if (shipstaticinfo->canBuildShips) { cmAddFactory(newship,shipstaticinfo->canBuildBigShips); } /* init the sound variables for this ship */ soundEventInitStruct(&newship->soundevent); if (((ShipStaticInfo *)newship->staticinfo)->custshipheader.CustShipInit) { ((ShipStaticInfo *)newship->staticinfo)->custshipheader.CustShipInit(newship); } //lightning effect stuff newship->lightning[0] = NULL; newship->lightning[1] = NULL; //create the instance version of the hierarchy bindings newship->bindings = univMeshBindingsDupe(shipstaticinfo, newship); if (shipstaticinfo->madStatic != NULL) { newship->madBindings = (madanim *)extraPointer; extraPointer += sizeof(madanim); newship->madBindings->header = shipstaticinfo->madStatic->header; newship->madBindings->nCurrentAnim = -1; newship->madBindings->time = 0.0f; newship->madBindings->totalSize = madAssedSize; madAnimBindingsDupe(newship, shipstaticinfo, FALSE); //ship has mesh animations associated with it //so lets set its inital state. newship->madAnimationFlags = 0; newship->madGunStatus = 0; //gun status //invalid states! newship->madWingStatus = 0; //wing animation status newship->madSpecialStatus = 0; newship->madDoorStatus = MAD_STATUS_DOOR_CLOSED; newship->cuedAnimationIndex = 0; newship->cuedAnimationType = 0; if(built == 1) { madLinkInSetUpInitialBuiltMadState(newship); } else if(built == 0) { madLinkInSetUpInitialPlacedMadState(newship); } else if(built == 2) { //ignore since it wasn't built or placed (nis ship) } else { dbgFatalf(DBG_Loc,"\nUnknown create ship state."); } } //determine what color scheme to use if ((playerowner->playerIndex < MAX_MULTIPLAYER_PLAYERS) && (shipstaticinfo->teamColor[playerowner->playerIndex])) { //if this player's default scheme woudl be a good color newship->colorScheme = playerowner->playerIndex; } else { //else let's find any old color scheme for (i = 0; i < MAX_MULTIPLAYER_PLAYERS; i++) { if (shipstaticinfo->teamColor[i]) { newship->colorScheme = i; break; } } } return newship; } /*----------------------------------------------------------------------------- Name : univAddShip Description : adds a ship to the universe Inputs : shiptype, shiprace, shippos, playerowner Outputs : Return : address of ship added ----------------------------------------------------------------------------*/ Ship *univAddShip(ShipType shiptype,ShipRace shiprace,vector *shippos,struct Player *playerowner,sdword built) { Ship *newship = univCreateShip(shiptype,shiprace,shippos,playerowner,built); collAddSpaceObjToCollBlobs((SpaceObj *)newship); listAddNode(&universe.SpaceObjList,&(newship->objlink),newship); listAddNode(&universe.ShipList,&(newship->shiplink),newship); listAddNode(&universe.ImpactableList,&(newship->impactablelink),newship); //special case - don't want cryotrays to be selectable if (shiptype == CryoTray) { bitClear(newship->flags, SOF_Selectable); } return newship; } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelMinorObjs Description : Exclusive physics updating for what would seem to be Asteroid ZERO's in the single player game? Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelMinorObjs() { Node *objnode; SpaceObj *spaceobj; if ((singlePlayerGame) && (singlePlayerGameInfo.asteroid0sCanMove)) { objnode = universe.MinorSpaceObjList.head; while (objnode != NULL) { spaceobj = (SpaceObj *)listGetStructOfNode(objnode); physUpdateObjPosVelBasic(spaceobj,universe.phystimeelapsed); objnode = objnode->next; } } } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelBullets Description : Exclusive physics updating for bulletes Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelBullets() { Node *bulletnode = universe.BulletList.head; Node *deletenode; Bullet *bullet; bool addEffect = FALSE; etglod *etgLOD; sdword LOD; etgeffectstatic *stat; Effect *effect; while (bulletnode != NULL) { bullet = (Bullet *)listGetStructOfNode(bulletnode); if ((bullet->flags & SOF_DontApplyPhysics) == 0) { if (physUpdateBulletPosVel(bullet,universe.phystimeelapsed)) { //bullet has died so clean it up //turn off sound of beam if (bullet->bulletType == BULLET_Beam) { if ((bullet->owner != NULL) && (bullet->gunowner != NULL)) { soundEventBurstStop(bullet->owner, bullet->gunowner); } } //delete effect associated with bullet effect = bullet->effect; #if ETG_DISABLEABLE if (effect != NULL && etgEffectsEnabled) #else if (effect != NULL) #endif { if (bitTest(((etgeffectstatic*)effect->staticinfo)->specialOps, ESO_SelfDeleting)) { //if the effect can delete itself ((real32 *)effect->variable)[ETG_BulletDurationParam] = effect->timeElapsed; //time it out effect->owner = NULL; bitClear(effect->flags, SOF_AttachPosition); bitClear(effect->flags, SOF_AttachCoordsys); bitClear(effect->flags, SOF_AttachVelocity); } else { etgEffectDelete(effect); univRemoveObjFromRenderList((SpaceObj *)(effect)); listDeleteNode(&effect->objlink); addEffect = TRUE; } } deletenode = bulletnode; bulletnode = bulletnode->next; if(bullet->bulletType == BULLET_SpecialBurst) { etgLOD = etgSpecialPurposeEffectTable[EGT_BURST_EXPLOSION_EFFECT]; if (etgLOD != NULL) { LOD = bullet->currentLOD; if (LOD >= etgLOD->nLevels) { stat = NULL; } else { stat = etgLOD->level[LOD]; } } else { stat = NULL; } if (addEffect) { #if ETG_DISABLEABLE if (stat != NULL && etgEffectsEnabled && !etgFrequencyExceeded(stat)) #else if (stat != NULL && !etgFrequencyExceeded(stat)) #endif { vector velocity; matrix coordsys = bullet->rotinfo.coordsys; velocity.x = 0.0f; velocity.y = 0.0f; velocity.z = 0.0f; etgEffectCreate(stat, NULL, &bullet->posinfo.position, &velocity, &coordsys, 1.0f, EAF_AllButNLips, 0); } } pointExplosionInSpace(&bullet->posinfo.position,burstRadius,bullet->damage,bullet->playerowner->playerIndex); } //Defense Fighter 'hack' to remove its reference if(bitTest(bullet->SpecialEffectFlag, 0x0002)) { //bullet is possibly being targetted by a defense fighter...remove that reference DefenseFighterBulletRemoval(bullet); } bobObjectDied(((SpaceObj *)bullet),&universe.collBlobList); listRemoveNode(&bullet->bulletlink); univRemoveObjFromRenderList(((SpaceObj *)bullet)); listDeleteNode(&bullet->objlink); continue; } } bulletnode = bulletnode->next; } } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelMissiles Description : Exclusive physics updating for misiles Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelMissiles() { Node *misnode = universe.MissileList.head; Missile *missile; bool deleteflag; while (misnode != NULL) { missile = (Missile *)listGetStructOfNode(misnode); if ((missile->flags & SOF_DontApplyPhysics) == 0) { if(missile->missileType == MISSILE_Regular) { //if missileType is a normal missile deleteflag = aishipGuideMissile(missile); } else if(missile->missileType == MISSILE_Mine) { //if missiletype is a mine deleteflag = aishipGuideMine(missile); } else { deleteflag = FALSE; dbgAssert(FALSE); } physUpdateObjPosVelMissile(missile,universe.phystimeelapsed); if ((deleteflag) && ((((SpaceObj *)missile)->flags & SOF_Dead) == 0)) { AddMissileToDeleteMissileList(missile, -1); } } misnode = misnode->next; } } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelDerelicts Description : Exclusive physics updating for derelicts Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelDerelicts() { Node *objnode = universe.DerelictList.head; Derelict *derelict; while (objnode != NULL) { derelict = (Derelict *)listGetStructOfNode(objnode); if ((derelict->flags & SOF_DontApplyPhysics) == 0) { if ((derelict->flags & SOF_NISShip) == 0) { physUpdateObjPosVelDerelicts(derelict,universe.phystimeelapsed); } } objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelResources Description : Exclusive physics updating for resources Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelResources() { Node *objnode = universe.ResourceList.head; Resource *resource; while (objnode != NULL) { resource = (Resource *)listGetStructOfNode(objnode); if ((resource->flags & (SOF_DontApplyPhysics|SOF_NISShip)) == 0) { physUpdateObjPosVelBasic((SpaceObj *)resource,universe.phystimeelapsed); } if (resource->resourceNotAccessible > 0) { resource->resourceNotAccessible--; } objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelShips Description : Exclusive physics updating for ships Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelShips() { Node *objnode = universe.ShipList.head; Ship *ship; growSelectReset(&universe.HousekeepShipList); if(ClampedShipList.selection == NULL) { growSelectInit(&ClampedShipList); } growSelectReset(&ClampedShipList); while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); if ((ship->flags & SOF_DontApplyPhysics) == 0) { if ((ship->flags & SOF_NISShip) == 0) { univCheckShipState(ship); if(bitTest(ship->flags, SOF_Slaveable)) { //object is slaveable if(bitTest(ship->slaveinfo->flags,SF_SLAVE)) { //if object is a slave goto nowdosetupforshipsShipCustomFunc; } else { //object should be a master //update slaves of object now physUpdateObjPosVelShip(ship,universe.phystimeelapsed); dockUpdateSlaves(ship); //update master goto nowdosetupforshipsShipCustomFunc; } } if(((SpaceObjRotImpTargGuidance *)ship)->clampInfo != NULL) { //object has clampinfo, should be updated //updateClampedObject((SpaceObjRotImpTargGuidance *)spaceobj); growSelectAddShip(&ClampedShipList,ship); goto nowdosetupforshipsShipCustomFunc; } physUpdateObjPosVelShip(ship,universe.phystimeelapsed); nowdosetupforshipsShipCustomFunc: univSetupShipForControl(ship); } } else { univMinorSetupShipForControl(ship); } //update mesh animations if (ship->madBindings != NULL) { if(ship->madAnimationFlags & MAD_ANIMATION_NEED_PROC) { madLinkInUpdateMeshAnimations(ship); } else if (ship->madBindings->nCurrentAnim != -1) { madAnimationUpdate(ship, universe.phystimeelapsed); } } objnode = objnode->next; } { Ship *obj; SelectCommand *clampedShipSelection; sdword i; sdword numShips; //update those poor baby clamped ships! clampedShipSelection = ClampedShipList.selection; numShips = clampedShipSelection->numShips; for (i=0;iShipPtr[i]; if (obj) //??????? { if(obj->clampInfo != NULL) { //do this check again because it is possible for a ship to become unclamped at some point down the road... updateClampedObject((SpaceObjRotImpTargGuidance *)obj); } } } } { Ship *obj; SelectCommand *housekeepSelection; sdword i; sdword numShips; housekeepSelection = universe.HousekeepShipList.selection; numShips = housekeepSelection->numShips; for (i=0;iShipPtr[i]; if (obj) { obj->staticinfo->custshipheader.CustShipHousekeep(obj); } } } growSelectReset(&universe.HousekeepShipList); // don't need to keep track of them anymore growSelectReset(&ClampedShipList); // don't need to keep track of them anymore } /*----------------------------------------------------------------------------- Name : univPausedUpdateAllPosVelShips Description : A minor version of the above function that only does update operations appropriate for when the universe is paused by nisUniversePaused. Inputs : void Outputs : Return : void ----------------------------------------------------------------------------*/ void univPausedUpdateAllPosVelShips(void) { Node *objnode = universe.ShipList.head; Ship *ship; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); univMinorSetupShipForControl(ship); objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : univUpdateAllPosVelEffects Description : Exclusive physics updating for Effects - must update effects AFTER anything that can be an effect owner (be on safe side and do it last) Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateAllPosVelEffects() { Node *objnode = universe.effectList.head; Node *deletenode; Effect *effect; while (objnode != NULL) { effect = (Effect *)listGetStructOfNode(objnode); if ((effect->flags & SOF_DontApplyPhysics) == 0) { if (etgEffectUpdate(effect, universe.phystimeelapsed)) { deletenode = objnode; objnode = objnode->next; etgEffectDelete(effect); univRemoveObjFromRenderList((SpaceObj *)effect); listDeleteNode(&effect->objlink); continue; } } objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : MakeNewAsteroidStaticInfo Description : makes a new asteroid staticinfo based on its scaling Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void MakeNewAsteroidStaticInfo(Asteroid *asteroid) { AsteroidStaticInfo *originalAsteroidStaticInfo = &asteroidStaticInfos[asteroid->asteroidtype]; AsteroidStaticInfo *newAsteroidStaticInfo; StaticHeader *newStaticHeader; StaticHeader *originalStaticHeader; StaticCollInfo *newStaticCollInfo; StaticCollInfo *originalStaticCollInfo; real32 scaling = asteroid->scaling; dbgAssert(scaling > 0.0f); if ((asteroid->flags & SOF_StaticInfoIsDynamic) == 0) { asteroid->staticinfo = memAlloc(sizeof(AsteroidStaticInfo),"aststatic",NonVolatile); *((AsteroidStaticInfo *)(asteroid->staticinfo)) = asteroidStaticInfos[asteroid->asteroidtype]; // structure copy asteroid->flags |= SOF_StaticInfoIsDynamic; } newAsteroidStaticInfo = (AsteroidStaticInfo *)asteroid->staticinfo; newAsteroidStaticInfo->harvestableByMultipleShips = (udword)(((real32)originalAsteroidStaticInfo->harvestableByMultipleShips) * scaling); if (newAsteroidStaticInfo->harvestableByMultipleShips == 0) { newAsteroidStaticInfo->harvestableByMultipleShips = 1; } newStaticHeader = &newAsteroidStaticInfo->staticheader; originalStaticHeader = &originalAsteroidStaticInfo->staticheader; newStaticHeader->mass = originalStaticHeader->mass * scaling; newStaticHeader->momentOfInertiaX = originalStaticHeader->momentOfInertiaX * scaling; newStaticHeader->momentOfInertiaY = originalStaticHeader->momentOfInertiaY * scaling; newStaticHeader->momentOfInertiaZ = originalStaticHeader->momentOfInertiaZ * scaling; newStaticHeader->oneOverMass = 1.0f / newStaticHeader->mass; newStaticHeader->oneOverMomentOfInertiaX = 1.0f / newStaticHeader->momentOfInertiaX; newStaticHeader->oneOverMomentOfInertiaY = 1.0f / newStaticHeader->momentOfInertiaY; newStaticHeader->oneOverMomentOfInertiaZ = 1.0f / newStaticHeader->momentOfInertiaZ; newStaticCollInfo = &newStaticHeader->staticCollInfo; originalStaticCollInfo = &originalStaticHeader->staticCollInfo; newStaticCollInfo->originalcollspheresize = originalStaticCollInfo->originalcollspheresize; // DO NOT CHANGE INTENTIONALLY newStaticCollInfo->approxcollspheresize = originalStaticCollInfo->approxcollspheresize * scaling; newStaticCollInfo->avoidcollspheresize = originalStaticCollInfo->avoidcollspheresize * scaling; newStaticCollInfo->avoidcollspherepad = originalStaticCollInfo->avoidcollspherepad * scaling; newStaticCollInfo->collspheresize = originalStaticCollInfo->collspheresize * scaling; newStaticCollInfo->collspheresizeSqr = newStaticCollInfo->collspheresize * newStaticCollInfo->collspheresize; vecScalarMultiply(newStaticCollInfo->collsphereoffset,originalStaticCollInfo->collsphereoffset,scaling); vecScalarMultiply(newStaticCollInfo->collrectoffset,originalStaticCollInfo->collrectoffset,scaling); newStaticCollInfo->forwardlength = originalStaticCollInfo->forwardlength * scaling; newStaticCollInfo->rightlength = originalStaticCollInfo->rightlength * scaling; newStaticCollInfo->uplength = originalStaticCollInfo->uplength * scaling; newStaticCollInfo->diagonallength = originalStaticCollInfo->diagonallength * scaling; collUpdateCollRectangle((SpaceObjRotImp *)asteroid); } /*----------------------------------------------------------------------------- Name : MakeNewDustCloudStaticInfo Description : makes a new dustcloud staticinfo based on its scaling Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void MakeNewDustCloudStaticInfo(DustCloud *dustcloud) { DustCloudStaticInfo *originalDustCloudStaticInfo = &dustcloudStaticInfos[dustcloud->dustcloudtype]; DustCloudStaticInfo *newDustCloudStaticInfo; StaticHeader *newStaticHeader; StaticHeader *originalStaticHeader; StaticCollInfo *newStaticCollInfo; StaticCollInfo *originalStaticCollInfo; real32 scaling = dustcloud->scaling; dbgAssert(scaling > 0.0f); if ((dustcloud->flags & SOF_StaticInfoIsDynamic) == 0) { dustcloud->staticinfo = memAlloc(sizeof(DustCloudStaticInfo),"dcloudstatic",NonVolatile); *((DustCloudStaticInfo *)(dustcloud->staticinfo)) = dustcloudStaticInfos[dustcloud->dustcloudtype]; // structure copy dustcloud->flags |= SOF_StaticInfoIsDynamic; } newDustCloudStaticInfo = (DustCloudStaticInfo *)dustcloud->staticinfo; newDustCloudStaticInfo->harvestableByMultipleShips = (udword)(((real32)originalDustCloudStaticInfo->harvestableByMultipleShips) * scaling); if (newDustCloudStaticInfo->harvestableByMultipleShips == 0) { newDustCloudStaticInfo->harvestableByMultipleShips = 1; } newStaticHeader = &newDustCloudStaticInfo->staticheader; originalStaticHeader = &originalDustCloudStaticInfo->staticheader; newStaticHeader->mass = originalStaticHeader->mass * scaling; newStaticHeader->momentOfInertiaX = originalStaticHeader->momentOfInertiaX * scaling; newStaticHeader->momentOfInertiaY = originalStaticHeader->momentOfInertiaY * scaling; newStaticHeader->momentOfInertiaZ = originalStaticHeader->momentOfInertiaZ * scaling; newStaticHeader->oneOverMass = 1.0f / newStaticHeader->mass; newStaticHeader->oneOverMomentOfInertiaX = 1.0f / newStaticHeader->momentOfInertiaX; newStaticHeader->oneOverMomentOfInertiaY = 1.0f / newStaticHeader->momentOfInertiaY; newStaticHeader->oneOverMomentOfInertiaZ = 1.0f / newStaticHeader->momentOfInertiaZ; newStaticCollInfo = &newStaticHeader->staticCollInfo; originalStaticCollInfo = &originalStaticHeader->staticCollInfo; newStaticCollInfo->originalcollspheresize = originalStaticCollInfo->originalcollspheresize; // DO NOT CHANGE INTENTIONALLY newStaticCollInfo->approxcollspheresize = originalStaticCollInfo->approxcollspheresize * scaling; newStaticCollInfo->avoidcollspheresize = originalStaticCollInfo->avoidcollspheresize * scaling; newStaticCollInfo->avoidcollspherepad = originalStaticCollInfo->avoidcollspherepad * scaling; newStaticCollInfo->collspheresize = originalStaticCollInfo->collspheresize * scaling; newStaticCollInfo->collspheresizeSqr = newStaticCollInfo->collspheresize * newStaticCollInfo->collspheresize; vecScalarMultiply(newStaticCollInfo->collsphereoffset,originalStaticCollInfo->collsphereoffset,scaling); vecScalarMultiply(newStaticCollInfo->collrectoffset,originalStaticCollInfo->collrectoffset,scaling); newStaticCollInfo->forwardlength = originalStaticCollInfo->forwardlength * scaling; newStaticCollInfo->rightlength = originalStaticCollInfo->rightlength * scaling; newStaticCollInfo->uplength = originalStaticCollInfo->uplength * scaling; newStaticCollInfo->diagonallength = originalStaticCollInfo->diagonallength * scaling; collUpdateCollRectangle((SpaceObjRotImp *)dustcloud); } /*----------------------------------------------------------------------------- Name : MakeNewGasCloudStaticInfo Description : makes a new gascloud staticinfo based on its scaling Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void MakeNewGasCloudStaticInfo(GasCloud *gascloud) { GasCloudStaticInfo *originalGasCloudStaticInfo = &gascloudStaticInfos[gascloud->gascloudtype]; GasCloudStaticInfo *newGasCloudStaticInfo; StaticHeader *newStaticHeader; StaticHeader *originalStaticHeader; StaticCollInfo *newStaticCollInfo; StaticCollInfo *originalStaticCollInfo; real32 scaling = gascloud->scaling; dbgAssert(scaling > 0.0f); if ((gascloud->flags & SOF_StaticInfoIsDynamic) == 0) { gascloud->staticinfo = memAlloc(sizeof(GasCloudStaticInfo),"gcloudstatic",NonVolatile); *((GasCloudStaticInfo *)(gascloud->staticinfo)) = gascloudStaticInfos[gascloud->gascloudtype]; // structure copy gascloud->flags |= SOF_StaticInfoIsDynamic; } newGasCloudStaticInfo = (GasCloudStaticInfo *)gascloud->staticinfo; newGasCloudStaticInfo->harvestableByMultipleShips = (udword)(((real32)originalGasCloudStaticInfo->harvestableByMultipleShips) * scaling); if (newGasCloudStaticInfo->harvestableByMultipleShips == 0) { newGasCloudStaticInfo->harvestableByMultipleShips = 1; } newStaticHeader = &newGasCloudStaticInfo->staticheader; originalStaticHeader = &originalGasCloudStaticInfo->staticheader; newStaticHeader->mass = originalStaticHeader->mass * scaling; newStaticHeader->momentOfInertiaX = originalStaticHeader->momentOfInertiaX * scaling; newStaticHeader->momentOfInertiaY = originalStaticHeader->momentOfInertiaY * scaling; newStaticHeader->momentOfInertiaZ = originalStaticHeader->momentOfInertiaZ * scaling; newStaticHeader->oneOverMass = 1.0f / newStaticHeader->mass; newStaticHeader->oneOverMomentOfInertiaX = 1.0f / newStaticHeader->momentOfInertiaX; newStaticHeader->oneOverMomentOfInertiaY = 1.0f / newStaticHeader->momentOfInertiaY; newStaticHeader->oneOverMomentOfInertiaZ = 1.0f / newStaticHeader->momentOfInertiaZ; newStaticCollInfo = &newStaticHeader->staticCollInfo; originalStaticCollInfo = &originalStaticHeader->staticCollInfo; newStaticCollInfo->originalcollspheresize = originalStaticCollInfo->originalcollspheresize; // DO NOT CHANGE INTENTIONALLY newStaticCollInfo->approxcollspheresize = originalStaticCollInfo->approxcollspheresize * scaling; newStaticCollInfo->avoidcollspheresize = originalStaticCollInfo->avoidcollspheresize * scaling; newStaticCollInfo->avoidcollspherepad = originalStaticCollInfo->avoidcollspherepad * scaling; newStaticCollInfo->collspheresize = originalStaticCollInfo->collspheresize * scaling; newStaticCollInfo->collspheresizeSqr = newStaticCollInfo->collspheresize * newStaticCollInfo->collspheresize; vecScalarMultiply(newStaticCollInfo->collsphereoffset,originalStaticCollInfo->collsphereoffset,scaling); vecScalarMultiply(newStaticCollInfo->collrectoffset,originalStaticCollInfo->collrectoffset,scaling); newStaticCollInfo->forwardlength = originalStaticCollInfo->forwardlength * scaling; newStaticCollInfo->rightlength = originalStaticCollInfo->rightlength * scaling; newStaticCollInfo->uplength = originalStaticCollInfo->uplength * scaling; newStaticCollInfo->diagonallength = originalStaticCollInfo->diagonallength * scaling; collUpdateCollRectangle((SpaceObjRotImp *)gascloud); } #if 0 /*----------------------------------------------------------------------------- Name : ObjectsAreMovingCloserTogether Description : determines if objects are moving closer together based on current positions and velocity Inputs : ship1, ship2, curdistsquared (between ship1,ship2) Outputs : Return : TRUE if objects are moving closer together, FALSE otherwise ----------------------------------------------------------------------------*/ bool ObjectsAreMovingCloserTogether(SpaceObjRotImp *obj1,SpaceObjRotImp *obj2,real32 curdistsquared) { vector newpos1,newpos2; vector d1,d2; vector newdist; vecScalarMultiply(d1,obj1->posinfo.velocity,universe.phystimeelapsed); vecScalarMultiply(d2,obj2->posinfo.velocity,universe.phystimeelapsed); vecAdd(newpos1,obj1->collInfo.collPosition,d1); vecAdd(newpos2,obj2->collInfo.collPosition,d2); vecSub(newdist,newpos2,newpos1); if (vecMagnitudeSquared(newdist) < curdistsquared) { return TRUE; } else { return FALSE; } } #endif /*----------------------------------------------------------------------------- Name : ApplyDamageToCollidingObjects Description : Applies collision damage to two colliding objects Inputs : obj1, obj2, distvector, dist Outputs : Return : ----------------------------------------------------------------------------*/ #define HYPERING_IN_DAMAGE twHyperingInDamage #define HYPERED_INTO_DAMAGE twHyperedIntoDamage void ApplyDamageToCollidingObjects(SpaceObjRotImpTarg *obj1,SpaceObjRotImpTarg *obj2,vector *distvector,real32 dist) { real32 damage; real32 damage2; real32 mindamage; real32 maxdamage; StaticInfoHealth *staticobj1; StaticInfoHealth *staticobj2; real32 veltangentfactor; vector obj2VelRelToObj1; if ((obj1->attributes^obj2->attributes) & (ATTRIBUTES_KillerCollDamage|ATTRIBUTES_HeadShotKillerCollDamage)) { // if one or the other but not both objects have KillerCollDamage if (obj1->attributes & (ATTRIBUTES_KillerCollDamage|ATTRIBUTES_HeadShotKillerCollDamage)) { ApplyDamageToTarget(obj1,min(obj2->health,obj1->health),0,DEATH_Killed_By_Collision,99); ApplyDamageToTarget(obj2,(obj1->attributes & ATTRIBUTES_HeadShotKillerCollDamage) ? attrHEADSHOTKillerCollDamage : attrKillerCollDamage,0,DEATH_Killed_By_Collision,99); // and do lots of damage to thing it hit return; } else if (obj2->attributes & (ATTRIBUTES_KillerCollDamage|ATTRIBUTES_HeadShotKillerCollDamage)) { ApplyDamageToTarget(obj2,min(obj2->health,obj1->health),0,DEATH_Killed_By_Collision,99); ApplyDamageToTarget(obj1,(obj2->attributes & ATTRIBUTES_HeadShotKillerCollDamage) ? attrHEADSHOTKillerCollDamage : attrKillerCollDamage,0,DEATH_Killed_By_Collision,99); // and do lots of damage to thing it hit return; } } vecSub(obj2VelRelToObj1,obj2->posinfo.velocity,obj1->posinfo.velocity); veltangentfactor = abs(vecDotProduct(*distvector,obj2VelRelToObj1)); veltangentfactor /= (COLLDAMAGE_VELOCITY_TANGENT_SCALE * dist); // now veltangentfactor is equal to k * |vel| cos(theta) where theta is angle between distvector and obj2VelRelToObj1 damage = min(obj1->health,obj2->health); damage = (damage * veltangentfactor); staticobj1 = (StaticInfoHealth *)obj1->staticinfo; staticobj2 = (StaticInfoHealth *)obj2->staticinfo; mindamage = min(staticobj1->minCollDamage,staticobj2->minCollDamage); maxdamage = min(staticobj1->maxCollDamage,staticobj2->maxCollDamage); if (damage < mindamage) { damage = mindamage; } else if (damage > maxdamage) { damage = maxdamage; } damage2 = damage; if(obj1->objtype == OBJ_ShipType) { if(((Ship *)obj1)->shiptype == P2Mothership) { damage /= 3.0f; } else if (((Ship *)obj1)->shiptype == FloatingCity) { return; // Floating city's are peaceful and magical - don't do any collision damage } else { CommandToDo *obj1command=getShipAndItsCommand(&universe.mainCommandLayer,((Ship *)obj1)); if(obj1command != NULL) { if(obj1command->ordertype.order == COMMAND_MP_HYPERSPACEING) { //if ship is hyperspaceing IN, modify its damage if(obj1command->hyperspaceState == COM_HYPERSPACE_IN) { damage *= HYPERING_IN_DAMAGE; damage2 *= HYPERED_INTO_DAMAGE; } } } } } if(obj2->objtype == OBJ_ShipType) { if(((Ship *)obj2)->shiptype == P2Mothership) { damage2 /= 3.0f; } else if (((Ship *)obj2)->shiptype == FloatingCity) { return; // Floating city's are peaceful and magical - don't do any collision damage } else { CommandToDo *obj2command=getShipAndItsCommand(&universe.mainCommandLayer,((Ship *)obj2)); if(obj2command != NULL) { if(obj2command->ordertype.order == COMMAND_MP_HYPERSPACEING) { //if ship is hyperspaceing IN, modify its damage if(obj2command->hyperspaceState == COM_HYPERSPACE_IN) { damage2 *= HYPERING_IN_DAMAGE; damage *= HYPERED_INTO_DAMAGE; } } } } } ApplyDamageToTarget(obj1,damage,0,DEATH_Killed_By_Collision,99); ApplyDamageToTarget(obj2,damage2,0,DEATH_Killed_By_Collision,99); } bool ObjectsHaveCollidedLately(SpaceObjRotImpTarg *obj1,SpaceObjRotImpTarg *obj2) { if ( ((universe.totaltimeelapsed - obj1->lasttimecollided) < COLLISION_CLEAR_TIME) && ((universe.totaltimeelapsed - obj2->lasttimecollided) < COLLISION_CLEAR_TIME) ) { return TRUE; } obj1->lasttimecollided = universe.totaltimeelapsed; obj2->lasttimecollided = universe.totaltimeelapsed; return FALSE; } bool ShouldDoGlancingCollision(SpaceObjRotImpTarg *fastobj,real32 fastobjvelmag2,SpaceObjRotImpTarg *slowobj,real32 slowobjvelmag2) { // don't do glancing collision for kamikaze stuff if(fastobj->objtype == OBJ_ShipType) if(bitTest(((Ship *)fastobj)->specialFlags,SPECIAL_Kamikaze)) return FALSE; if(slowobj->objtype == OBJ_ShipType) if(bitTest(((Ship *)slowobj)->specialFlags,SPECIAL_Kamikaze)) return FALSE; if ((fastobjvelmag2/slowobjvelmag2) >= GLANCE_COLLISION_VEL_RATIO) { return TRUE; } if (vecDotProduct(fastobj->posinfo.velocity,slowobj->posinfo.velocity) < 0) { return TRUE; } return FALSE; } /*----------------------------------------------------------------------------- Name : ObjectsCollided Description : Handles momentum transferral of objects colliding Inputs : obj1, obj2, distvector, dist, distsquared Outputs : Return : ----------------------------------------------------------------------------*/ void ObjectsCollided(SpaceObjRotImpTarg *obj1,SpaceObjRotImpTarg *obj2,real32 colldist,vector *distvector,real32 dist,real32 distsquared) { vector collforce; vector cforce1; vector cforce2; vector repulseoverlap; real32 temp; real32 repulse = (colldist - dist) * COLLISION_REPULSE_FACTOR / dist; vector up = { 0.0f, 0.0f, 1.0f }; real32 collforcemag; vector tangforce; real32 obj1velmag2; real32 obj2velmag2; real32 slowobjvelmag2; real32 fastobjvelmag2; SpaceObjRotImpTarg *slowobj; SpaceObjRotImpTarg *fastobj; vector fastesttoslowest; vector leftrightdecidevec; #ifdef DEBUG_COLLISIONS dbgMessagef("\n%x and %x collided, Repulsion %f",(udword)obj1,(udword)obj2,repulse); #endif if (!ObjectsHaveCollidedLately(obj1,obj2)) { // decide which ship is fastest ship in collision obj1velmag2 = vecMagnitudeSquared(obj1->posinfo.velocity); obj2velmag2 = vecMagnitudeSquared(obj2->posinfo.velocity); if (obj1velmag2 > obj2velmag2) { fastobj = obj1; fastobjvelmag2 = obj1velmag2; slowobj = obj2; slowobjvelmag2 = obj2velmag2; } else { fastobj = obj2; fastobjvelmag2 = obj2velmag2; slowobj = obj1; slowobjvelmag2 = obj1velmag2; } // collforce = (m2v2 - m1v1)/t vecScalarMultiply(cforce1,obj2->posinfo.velocity,obj2->staticinfo->staticheader.mass); vecScalarMultiply(cforce2,obj1->posinfo.velocity,obj1->staticinfo->staticheader.mass); vecSub(collforce,cforce1,cforce2); vecDivideByScalar(collforce,universe.phystimeelapsed,temp); if (ShouldDoGlancingCollision(fastobj,fastobjvelmag2,slowobj,slowobjvelmag2)) { #ifdef DEBUG_COLLISIONS dbgMessage("\nGlancing Collision"); #endif if ((collforce.x == 0.0f) && (collforce.y == 0.0f)) { collforce.x = 0.1f; // so cross product with up doesn't blow up } collforcemag = fsqrt(vecMagnitudeSquared(collforce)); // make force tangential: vecCrossProduct(tangforce,collforce,up); vecNormalizeToLength(&tangforce,collforcemag); // decide which way to apply tangential force based on if "velocity" vector of fastest obj is left or right // of vector from fastestobj to slowestobj vecSub(fastesttoslowest,slowobj->collInfo.collPosition,fastobj->collInfo.collPosition); vecCrossProduct(leftrightdecidevec,fastesttoslowest,fastobj->posinfo.velocity); if (leftrightdecidevec.z > 0) { vecCopyAndNegate(collforce,tangforce); } else { collforce = tangforce; } } else { #ifdef DEBUG_COLLISIONS dbgMessage("\nXchanging Velocities"); #endif } if (!fastobj->staticinfo->staticheader.immobile) { if(!(fastobj->attributes & ATTRIBUTES_KillerCollDamage)) { vecAddTo(fastobj->posinfo.force,collforce); } } if (!slowobj->staticinfo->staticheader.immobile) { if(!(slowobj->attributes & ATTRIBUTES_KillerCollDamage)) { vecSubFrom(slowobj->posinfo.force,collforce); } } } else { // add a repulsion factor if objects overlap, so they don't stick together vecScalarMultiply(repulseoverlap,*distvector,repulse); if (!obj2->staticinfo->staticheader.immobile) { if(!(obj2->attributes & ATTRIBUTES_KillerCollDamage)) { vecAddTo(obj2->posinfo.force,repulseoverlap); } } if (!obj1->staticinfo->staticheader.immobile) { if(!(obj1->attributes & ATTRIBUTES_KillerCollDamage)) { vecSubFrom(obj1->posinfo.force,repulseoverlap); } } } } /*----------------------------------------------------------------------------- Name : ApplyCareen Description : Applies careen (random spin) to a ship Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ApplyCareen(Ship *ship) { // apply three random torques, to ship, ship->rotinfo.torque.x += frandombetween(YAWCAREENLO,YAWCAREENHI); ship->rotinfo.torque.y += frandombetween(PITCHCAREENLO,PITCHCAREENHI); ship->rotinfo.torque.z += frandombetween(ROLLCAREENLO,ROLLCAREENHI); } void ApplyCareenRotationDirectly(Ship *ship) { StaticHeader *staticheader = &ship->staticinfo->staticheader; vector a; // apply three random torques, to ship, a.x = frandombetween(YAWCRAZYLO,YAWCRAZYHI) * universe.phystimeelapsed; // * staticheader->oneOverMomentOfInertiaX; a.y = frandombetween(PITCHCRAZYLO,PITCHCRAZYHI) * universe.phystimeelapsed; // * staticheader->oneOverMomentOfInertiaY; a.z = frandombetween(ROLLCRAZYLO,ROLLCRAZYHI) * universe.phystimeelapsed; // * staticheader->oneOverMomentOfInertiaZ; // w = w + alpha*t vecAddTo(ship->rotinfo.rotspeed,a); // cap rotation speed vecCapVectorSloppy(&ship->rotinfo.rotspeed,staticheader->maxrot); } /*----------------------------------------------------------------------------- Name : AddShipToDeleteShipList Description : adds ship to the DeleteShip list (for deletion at end of main loop) Inputs : ship, damageType Outputs : Return : ----------------------------------------------------------------------------*/ void AddShipToDeleteShipList(Ship *ship,GunSoundType damageType) { DeleteShip *deleteShip; ShipStaticInfo *shipstaticinfo = (ShipStaticInfo *)ship->staticinfo; ship->flags |= SOF_Dead; if (!shipstaticinfo->staticheader.immobile) { ApplyCareen(ship); } //if (shipstaticinfo->shipclass != CLASS_Fighter) MakeExplosionRockBumpables(ship,(damageType == -1)); // add to delete ship list deleteShip = memAlloc(sizeof(DeleteShip),"DeleteShip",Pyrophoric); deleteShip->ship = ship; if (ship->health <= etgBigDeathFactor[shipstaticinfo->shiprace][shipstaticinfo->shipclass]) { //ship took a really big bong hit deleteShip->deathBy = (udword)etgDeathModeByGunType[damageType]; } else { //if it just barely died, use the small explosion deleteShip->deathBy = EDT_AccumDamage; } listAddNode(&universe.DeleteShipList,&(deleteShip->objlink),deleteShip); } /*----------------------------------------------------------------------------- Name : AddResourceToDeleteResourceList Description : adds resource to the DeleteResource list (for deletion at end of main loop) Inputs : resource Outputs : Return : ----------------------------------------------------------------------------*/ void AddResourceToDeleteResourceList(Resource *resource) { // add this resource to the delete resource list DeleteResource *deleteResource = memAlloc(sizeof(DeleteResource),"DeleteResource",Pyrophoric); resource->flags |= SOF_Dead; deleteResource->resource = resource; listAddNode(&universe.DeleteResourceList,&(deleteResource->objlink),deleteResource); } /*----------------------------------------------------------------------------- Name : AddDerelictToDeleteDerelictList Description : adds derelict to the DeleteDerelict list (for deletion at end of main loop) Inputs : derelict Outputs : Return : ----------------------------------------------------------------------------*/ void AddDerelictToDeleteDerelictList(Derelict *derelict,GunSoundType damageType) { // add this derelict to the delete derelict list DeleteDerelict *deleteDerelict = memAlloc(sizeof(DeleteDerelict),"DeleteDerelict",Pyrophoric); bobObjectDied((SpaceObj *)derelict,&universe.collBlobList);//can't have dead derelicts in the blobs derelict->flags |= SOF_Dead; deleteDerelict->derelict = derelict; if (derelict->health <= etgBigDeathFactorDerelict[derelict->derelicttype]) { //derelict took a really big bong hit deleteDerelict->deathBy = (udword)etgDeathModeByGunType[damageType]; } else { //if it just barely died, use the small explosion deleteDerelict->deathBy = EDT_AccumDamage; } listAddNode(&universe.DeleteDerelictList,&(deleteDerelict->objlink),deleteDerelict); //univRemoveFromWorldList(derelict); //it may be a planet; remove from the planet list if so. } /*----------------------------------------------------------------------------- Name : AddMissileToDeleteMissileList Description : adds missile to the DeleteMissile list (for deletion at end of main loop) Inputs : missile Outputs : Return : ----------------------------------------------------------------------------*/ void AddMissileToDeleteMissileList(Missile *missile,GunSoundType damageType) { // add this missile to the delete missile list DeleteMissile *deleteMissile = memAlloc(sizeof(DeleteMissile),"DeleteMissile",Pyrophoric); missile->flags |= SOF_Dead; deleteMissile->missile = missile; deleteMissile->deathBy = damageType; listAddNode(&universe.DeleteMissileList,&(deleteMissile->objlink),deleteMissile); } /*----------------------------------------------------------------------------- Name : AddTargetToDeleteList Description : adds target (ship, asteroid, missile) to appropriate delete list Inputs : target, soundType (indicating type of death) Outputs : Return : ----------------------------------------------------------------------------*/ void AddTargetToDeleteList(SpaceObjRotImpTarg *target,GunSoundType soundType) { switch (target->objtype) { case OBJ_ShipType: AddShipToDeleteShipList((Ship *)target,soundType); break; case OBJ_AsteroidType: case OBJ_NebulaType: case OBJ_GasType: case OBJ_DustType: AddResourceToDeleteResourceList((Resource *)target); break; case OBJ_MissileType: AddMissileToDeleteMissileList((Missile *)target, soundType); break; case OBJ_DerelictType: AddDerelictToDeleteDerelictList((Derelict *)target, soundType); break; default: dbgAssert(FALSE); break; } } void addSlavesToDeleteList(Ship *master, GunSoundType deathtype) { Node *slavenode; Ship *slave; dbgAssert(master->slaveinfo->flags & SF_MASTER); //only master should have been added at first! slavenode = master->slaveinfo->slaves.head; master->slaveinfo->flags |= SF_DEAD; while(slavenode != NULL) { slave = (Ship *) listGetStructOfNode(slavenode); slave->slaveinfo->flags |= SF_DEAD; if(!(slave->flags & SOF_Dead)) AddTargetToDeleteList((SpaceObjRotImpTarg *)slave,deathtype); slavenode = slavenode->next; } } void ApplyExplosionRockToTarget(SpaceObjRotImp *obj,real32 blaststrength) { obj->rotinfo.torque.x += frandombetween(YAWEXPLOSIONROCKLO,YAWEXPLOSIONROCKHI) * blaststrength; obj->rotinfo.torque.y += frandombetween(PITCHEXPLOSIONROCKLO,PITCHEXPLOSIONROCKHI) * blaststrength; obj->rotinfo.torque.z += frandombetween(ROLLEXPLOSIONROCKLO,ROLLEXPLOSIONROCKHI) * blaststrength; } void ApplyRockToTarget(SpaceObjRotImpTarg *target,real32 projectilemass) { target->rotinfo.torque.x += frandombetween(YAWROCKLO,YAWROCKHI) * projectilemass; target->rotinfo.torque.y += frandombetween(PITCHROCKLO,PITCHROCKHI) * projectilemass; target->rotinfo.torque.z += frandombetween(ROLLROCKLO,ROLLROCKHI) * projectilemass; } /*----------------------------------------------------------------------------- Name : MakeExplosionRockBumpables Description : Make all impactable objects within blast radius get rocked Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void MakeExplosionRockBumpables(Ship *ship,bool scuttle) { Node *objnode = universe.ImpactableList.head; ShipStaticInfo *shipstatic = ship->staticinfo; SpaceObjRotImp *obj; vector distvector; real32 distsquared; real32 dist; real32 blastradius = shipstatic->staticheader.staticCollInfo.collspheresize * shipstatic->blastRadiusShockWave * (scuttle ? BLAST_RADIUS_SCUTTLE_BONUS : 1.0f); real32 blastradiussqr = blastradius * blastradius; real32 blaststrength; real32 damage; real32 damageperc; while (objnode != NULL) { obj = (SpaceObjRotImp *) listGetStructOfNode(objnode); if ((obj->flags & (SOF_Impactable|SOF_NotBumpable)) != (SOF_Impactable)) { goto nextnode; // object must be impactable, bumpable } vecSub(distvector,obj->collInfo.collPosition,ship->collInfo.collPosition); if (!isBetweenInclusive(distvector.x,-blastradius,blastradius)) { goto nextnode; } if (!isBetweenInclusive(distvector.y,-blastradius,blastradius)) { goto nextnode; } if (!isBetweenInclusive(distvector.z,-blastradius,blastradius)) { goto nextnode; } distsquared = vecMagnitudeSquared(distvector); if (distsquared > blastradiussqr) { goto nextnode; } if (obj == (SpaceObjRotImp *)ship) { goto nextnode; } if (obj->staticinfo->staticheader.immobile) { goto nextnode; } // obj is within blast radius, so it should get rocked if (distsquared < 1.0f) { distsquared = 1.0f; } dist = fsqrt(distsquared); blaststrength = 1.0f - (dist / blastradius); vecNormalizeToLength(&distvector,(scuttle ? BLAST_CONSTANT_SCUTTLE : BLAST_CONSTANT) * blaststrength); ApplyExplosionRockToTarget(obj,blaststrength); physApplyForceVectorToObj(obj,distvector); if (obj->flags & SOF_Targetable) { // calculate damage based on ship blowing up real32 health = (shipstatic->blastRadiusDamage != 0.0f) ? shipstatic->blastRadiusDamage : shipstatic->maxhealth; damageperc = (scuttle ? BLAST_DAMAGE_MAX_PERCENT_SCUTTLE : BLAST_DAMAGE_MAX_PERCENT) * blaststrength; damage = health * damageperc; if (damage > 0) { if(ship->playerowner != NULL) ApplyDamageToTarget((SpaceObjRotImpTarg *)obj,damage,0,DEATH_Killed_By_Player_Explosion,ship->playerowner->playerIndex); else ApplyDamageToTarget((SpaceObjRotImpTarg *)obj,damage,0,DEATH_Killed_By_Dead_Player,99); } } nextnode: objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : ApplyDamageToTarget Description : applies damage to target, possible resulting in target being added to the delete list. Inputs : damageHow - Defines how the damaged was taken: DEATH_Killed_By_Collision - collision DEATH_Killed_By_Player - player ship attacking (anything else is assumed to be something else and essentially nothing to worry about) playerIndex = playerIndex of playerdoing damage Outputs : Return : returns TRUE if damage was fatal damage ----------------------------------------------------------------------------*/ bool ApplyDamageToTarget(SpaceObjRotImpTarg *target,real32 damagetaken,GunSoundType soundType,sdword damageHow,sdword playerIndex) { bool targetWasAlive = ((target->flags & SOF_Dead) == 0); real32 previousHealth = target->health; real32 maxHealth; if (nisIsRunning) { //do no damage during an NIS return FALSE; } if(target->flags & SOF_Slaveable) { //target is a ship and is slaveable if(((Ship *)target)->slaveinfo->flags & SF_SLAVE) { //object is a slave target = (SpaceObjRotImpTarg *) ((Ship *) target)->slaveinfo->Master; //potential reprocussions } } target->health -= damagetaken; switch (target->objtype) { case OBJ_AsteroidType: if (AsteroidTakesDamage((Asteroid *)target,(sdword)damagetaken,targetWasAlive)) { AddTargetToDeleteList(target,soundType); return TRUE; //used by effect } else { return FALSE; } break; case OBJ_DustType: switch (soundType) { case GS_LargeIonCannon: case GS_MediumIonCannon: case GS_SmallIonCannon: case GS_VeryLargeIonCannon: //charge, but don't take damage DustCloudChargesUp((DustCloud*)target, (sdword)damagetaken, targetWasAlive); return FALSE; } #if 0 if (DustCloudTakesDamage((DustCloud*)target, (sdword)damagetaken, targetWasAlive)) { AddTargetToDeleteList(target, soundType); return TRUE; } else #endif { return FALSE; } break; case OBJ_DerelictType: if (((Derelict *)target)->derelicttype == HyperspaceGate) { hsDerelictTakesDamage((Derelict *)target); } goto fallthrough; case OBJ_ShipType: switch(damageHow) { case DEATH_Killed_By_Player: case DEATH_Killed_By_Kamikaze: case DEATH_Killed_By_Player_Explosion: universe.gameStats.playerStats[playerIndex].pointsOfDamageDoneToWho[((Ship *)target)->playerowner->playerIndex] += damagetaken; break; } maxHealth = ((ShipStaticInfo *)target->staticinfo)->maxhealth; if (previousHealth / maxHealth >= BAT_InTheRedFactor) { //it wasn't in the red if (target->health / maxHealth < BAT_InTheRedFactor) { //if it's just now in the red bitSet(((Ship *)target)->chatterBits, BCB_DamageInTheRed);//flag as such } } // purposefully fall through fallthrough:; default: if ((target->health <= 0) && (targetWasAlive)) // target just died, so add to DeleteShipList { if(target->objtype == OBJ_ShipType) { ((Ship *)target)->howDidIDie = damageHow; ((Ship *)target)->whoKilledMe = playerIndex; //if was collision death, who cares bountyShipWasKilled((Ship *)target); } if(target->flags & SOF_Slaveable) { //target is a ship and is slaveable if(!(((Ship *)target)->slaveinfo->flags & SF_DEAD)) //if we haven't added then already { addSlavesToDeleteList((Ship *)target,soundType); } if(!(target->flags & SOF_Dead)) AddTargetToDeleteList(target,soundType); } else { AddTargetToDeleteList(target,soundType); } return TRUE; //used by effect } else { return FALSE; } break; } } /*----------------------------------------------------------------------------- Name : pointExplosionInSpace(vector *position,real32 blastRadius) Description : Make all impactable objects within blast radius get rocked Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void pointExplosionInSpace(vector *position,real32 blastRadius,real32 maxDamage,sdword playerIndex) { Node *objnode = universe.ImpactableList.head; SpaceObjRotImp *obj; vector distvector; real32 distsquared; real32 dist; //real32 blastradius = shipstatic->staticheader.staticCollInfo.collspheresize * shipstatic->blastRadiusShockWave; real32 blastradiussqr = blastRadius * blastRadius; real32 blaststrength; while (objnode != NULL) { obj = (SpaceObjRotImp *) listGetStructOfNode(objnode); if ((obj->flags & (SOF_Impactable|SOF_NotBumpable)) != (SOF_Impactable)) { goto nextnode; // object must be impactable, bumpable } vecSub(distvector,obj->collInfo.collPosition,*position); if (!isBetweenInclusive(distvector.x,-blastRadius,blastRadius)) { goto nextnode; } if (!isBetweenInclusive(distvector.y,-blastRadius,blastRadius)) { goto nextnode; } if (!isBetweenInclusive(distvector.z,-blastRadius,blastRadius)) { goto nextnode; } distsquared = vecMagnitudeSquared(distvector); if (distsquared > blastradiussqr) { goto nextnode; } if (obj->staticinfo->staticheader.immobile) { goto nextnode; } // obj is within blast radius, so it should get rocked if (distsquared < 1.0f) { distsquared = 1.0f; } dist = fsqrt(distsquared); blaststrength = 1.0f - (dist / blastRadius); vecNormalizeToLength(&distvector,BLAST_CONSTANT * blaststrength); ApplyExplosionRockToTarget(obj,blaststrength); vecScalarMultiply(distvector,distvector,TW_BURST_ATTACK_FORCE_MODIFIER); physApplyForceVectorToObj(obj,distvector); if (obj->flags & SOF_Targetable) { // calculate damage based on blast as a % of maxhealth ApplyDamageToTarget((SpaceObjRotImpTarg *)obj,maxDamage,0,DEATH_Killed_By_Player_Explosion,playerIndex); } nextnode: objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : univBulletCollidedWithTarget Description : Call when bullet collides with a target Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univBulletCollidedWithTarget(SpaceObjRotImpTarg *target,StaticHeader *targetstaticheader,Bullet *bullet,real32 collideLineDist,sdword collSide) { real32 damagetaken = bullet->damage; Ship *bulletowner = bullet->owner; real32 massovertime; vector recoil; etgeffectstatic *stat; sdword LOD; vector LOF; bool fatalHit; vector hitLocation; matrix hitCoordsys; real32 floatDamage; real32 damageMult = 1.0f; udword intDamage; real32 targetScale; // if target isn't immobile, apply impact force from bullet if (!targetstaticheader->immobile && !bitTest(target->flags, SOF_NotBumpable)) { if(target->objtype == OBJ_ShipType ) { if(((Ship *)target)->specialFlags2 & SPECIAL_2_ShipInGravwell) { goto nobulletmasstransfer; } } massovertime = bullet->bulletmass / universe.phystimeelapsed; recoil = bullet->posinfo.velocity; vecMultiplyByScalar(recoil,massovertime); ApplyRockToTarget(target,bullet->bulletmass); physApplyForceVectorToObj(target,recoil); } nobulletmasstransfer: if (target->objtype == OBJ_ShipType) { damageMult = ((Ship *)target)->magnitudeSquared; // apply different damage modifier based on collision side: if (collSide >= 0) { dbgAssert(collSide < NUM_TRANS_DEGOFFREEDOM); damagetaken *= ((Ship *)target)->staticinfo->collSideModifiers[collSide]; } if (bulletowner != NULL) { if ((bulletowner->shiptype == HeavyDefender) && (((ShipStaticInfo *)target->staticinfo)->shipclass == CLASS_Fighter)) { // armour piercing bullets vs fighters damagetaken *= armourPiercingModifier; } if (bulletowner->playerowner == ((Ship *)target)->playerowner) { //if it's friendly fire //!!! why only in non-debug??? #ifndef HW_Debug damagetaken *= friendlyFireModifier; #endif bitSet(((Ship *)target)->chatterBits, BCB_FriendlyFire); } else if (allianceIsShipAlly((Ship *)target, bulletowner->playerowner)) { bitSet(((Ship *)target)->chatterBits, BCB_AlliedFire); } else { speechEventUnderAttack((Ship *)target); } ((Ship *)target)->gettingrocked = bulletowner; ((Ship *)target)->recentAttacker = bulletowner; ((Ship *)target)->recentlyAttacked = RECENT_ATTACK_DURATION; // start counting down } else { speechEventUnderAttack((Ship *)target); } } if(bulletowner != NULL && bulletowner->playerowner != NULL) fatalHit = ApplyDamageToTarget(target,damagetaken,bullet->soundType,DEATH_Killed_By_Player,bulletowner->playerowner->playerIndex); else fatalHit = ApplyDamageToTarget(target,damagetaken,bullet->soundType,DEATH_Killed_By_Dead_Player,99); //delete the bullet effect #if ETG_DISABLEABLE if (bullet->effect != NULL && etgEffectsEnabled && bullet->bulletType != BULLET_Beam) #else if (bullet->effect != NULL && bullet->bulletType != BULLET_Beam) #endif { etgEffectDelete(bullet->effect); univRemoveObjFromRenderList((SpaceObj *)bullet->effect); listDeleteNode(&bullet->effect->objlink); bullet->effect = NULL; } if (bullet->bulletType != BULLET_Beam) { if (collideLineDist == 0.0f) { //if it's a beam weapon and there is no collision vector return; //don't play any hit effects } } //play the imact effect if (bullet->hitEffect != NULL) { LOD = target->currentLOD; if (LOD < bullet->hitEffect->nLevels) { stat = bullet->hitEffect->level[LOD]; } else { stat = NULL; } } else { stat = NULL; } if (univSpaceObjInRenderList((SpaceObj *)target)) { #if ETG_DISABLEABLE if (stat != NULL && etgHitEffectsEnabled && etgEffectsEnabled && (!etgFrequencyExceeded(stat))) #else if (stat != NULL && etgHitEffectsEnabled && (!etgFrequencyExceeded(stat))) #endif { if (collideLineDist == 0.0f) { hitLocation = bullet->posinfo.position; } else { dbgAssert(collideLineDist > 0.0f); vecScalarMultiply(hitLocation,bullet->bulletheading,collideLineDist); vecAdd(hitLocation,bullet->posinfo.position,hitLocation); } //N-Lip the impact position if (target->objtype == OBJ_ShipType) { targetScale = ((Ship *)target)->magnitudeSquared; vecSubFrom(hitLocation, target->posinfo.position); vecMultiplyByScalar(hitLocation, targetScale); vecAddTo(hitLocation, target->posinfo.position); } else { targetScale = 1.0f; } vecCopyAndNegate(LOF,bullet->bulletheading); matCreateCoordSysFromHeading(&hitCoordsys,&LOF); floatDamage = (real32)damagetaken; if (RGLtype == SWtype) { //smaller hit effects in software floatDamage *= etgSoftwareScalarHit; } floatDamage *= damageMult; intDamage = TreatAsUdword(floatDamage); etgEffectCreate(stat, NULL, &hitLocation, &target->posinfo.velocity, &hitCoordsys, targetScale, 0, 2, intDamage, fatalHit); } } } /*----------------------------------------------------------------------------- Name : univMissileCollidedWithTarget Description : Call when a missile collides with a target Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univMissileCollidedWithTarget(SpaceObjRotImpTarg *target,StaticHeader *targetstaticheader,Missile *missile,real32 collideLineDist,sdword collSide) { real32 damagetaken = missile->damage; Ship *missileowner = missile->owner; MissileStaticInfo *missilestaticinfo = (MissileStaticInfo *)missile->staticinfo; real32 massovertime; vector recoil; etgeffectstatic *stat; sdword LOD; bool fatalHit; real32 floatDamage; udword intDamage; vector missileheading; vector hitLocation; real32 targetScale; real32 damageMult = 1.0f; if(target->objtype == OBJ_ShipType) { if(((Ship *)target)->playerowner->playerIndex == universe.curPlayerIndex) { if(missile->FORCE_DROPPED) { //////////////////// //target hit by force drop mines..local players ship //speech event num: STAT_Grp_InMineField //or STAT_AssGrp_InMineField //use battle chatter if(selHotKeyGroupNumberTest((Ship *)target) == SEL_InvalidHotKey) { //not in a group! if (battleCanChatterAtThisTime(BCE_STAT_Grp_InMineField, ((Ship *)target))) { battleChatterAttempt(SOUND_EVENT_DEFAULT, BCE_STAT_Grp_InMineField, ((Ship *)target), SOUND_EVENT_DEFAULT); } } else { if (battleCanChatterAtThisTime(BCE_STAT_AssGrp_InMineField, ((Ship *)target))) { battleChatterAttempt(SOUND_EVENT_DEFAULT, BCE_STAT_AssGrp_InMineField, ((Ship *)target), selHotKeyGroupNumberTest((Ship *)target)); } } ////////////// } } } // if target isn't immobile, apply impact force from missile if (!targetstaticheader->immobile && !bitTest(target->flags, SOF_NotBumpable)) { massovertime = missilestaticinfo->staticheader.mass / universe.phystimeelapsed; recoil = missile->posinfo.velocity; vecMultiplyByScalar(recoil,massovertime); ApplyRockToTarget(target,missilestaticinfo->staticheader.mass); physApplyForceVectorToObj(target,recoil); } if (target->objtype == OBJ_ShipType) { damageMult = ((Ship *)target)->magnitudeSquared; // apply different damage modifier based on collision side: if (collSide >= 0) { dbgAssert(collSide < NUM_TRANS_DEGOFFREEDOM); damagetaken *= ((Ship *)target)->staticinfo->collSideModifiers[collSide]; } if (missileowner != NULL) { if (missileowner->playerowner == ((Ship *)target)->playerowner) { //if it's friendly fire //!!! why only in non-debug??? #ifndef HW_Debug damagetaken *= friendlyFireModifier; #endif bitSet(((Ship *)target)->chatterBits, BCB_FriendlyFire); } else if (allianceIsShipAlly((Ship *)target, missileowner->playerowner)) { bitSet(((Ship *)target)->chatterBits, BCB_AlliedFire); } else { speechEventUnderAttack((Ship *)target); } ((Ship *)target)->gettingrocked = missileowner; ((Ship *)target)->recentAttacker = missileowner; ((Ship *)target)->recentlyAttacked = RECENT_ATTACK_DURATION; // start counting down } else { speechEventUnderAttack((Ship *)target); } } if(missileowner != NULL && missileowner->playerowner != NULL) fatalHit = ApplyDamageToTarget(target,damagetaken,missile->soundType,DEATH_Killed_By_Player,missileowner->playerowner->playerIndex); else fatalHit = ApplyDamageToTarget(target,damagetaken,missile->soundType,DEATH_Killed_By_Dead_Player,99); if (missile->hitEffect != NULL) { LOD = target->currentLOD; if (LOD < missile->hitEffect->nLevels) { stat = missile->hitEffect->level[LOD]; } else { stat = NULL; } } else { stat = NULL; } if (univSpaceObjInRenderList((SpaceObj *)target)) { #if ETG_DISABLEABLE if (stat != NULL && etgHitEffectsEnabled && etgEffectsEnabled && (!etgFrequencyExceeded(stat))) #else if (stat != NULL && etgHitEffectsEnabled && (!etgFrequencyExceeded(stat))) #endif { if (collideLineDist == 0.0f) { hitLocation = missile->posinfo.position; } else { dbgAssert(collideLineDist > 0.0f); matGetVectFromMatrixCol3(missileheading,missile->rotinfo.coordsys); vecScalarMultiply(hitLocation,missileheading,collideLineDist); vecAdd(hitLocation,missile->posinfo.position,hitLocation); } //N-Lip the impact position if (target->objtype == OBJ_ShipType) { targetScale = ((Ship *)target)->magnitudeSquared; vecSubFrom(hitLocation, target->posinfo.position); vecMultiplyByScalar(hitLocation, targetScale); vecAddTo(hitLocation, target->posinfo.position); } else { targetScale = 1.0f; } floatDamage = (real32)damagetaken; if (RGLtype == SWtype) { //smaller hit effects in software floatDamage *= etgSoftwareScalarHit; } floatDamage *= damageMult; intDamage = TreatAsUdword(floatDamage); etgEffectCreate(stat, NULL, &hitLocation, &target->posinfo.velocity, &missile->rotinfo.coordsys, targetScale, 0, 2, intDamage, fatalHit); } } } /*----------------------------------------------------------------------------- Name : univRemoveAllTargetReferencesFromBullets Description : removes all ship references from any bullets Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveAllTargetReferencesFromBullets(SpaceObjRotImpTarg *target) { Node *objnodebullet = universe.BulletList.head; Bullet *bullet; // Update bullet->owner for all bullets whose owner is now dead while (objnodebullet != NULL) { bullet = (Bullet *)listGetStructOfNode(objnodebullet); dbgAssert(bullet->objtype == OBJ_BulletType); if (bullet->owner == (Ship *)target) { bullet->owner = NULL; // bullet has no owner now that parent ship died bullet->gunowner = NULL; if (bullet->bulletType == BULLET_Beam) { bullet->timelived += 10000.0f; // delete bullet if it is a beam weapon and target wielding it died } } if (bullet->target == target) { bullet->target = NULL; } objnodebullet = objnodebullet->next; } } /*----------------------------------------------------------------------------- Name : univRemoveAllTargetReferencesFromMissiles Description : removes all target references from any missiles Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveAllTargetReferencesFromMissiles(SpaceObjRotImpTarg *target) { Node *objnodemissile = universe.MissileList.head; Missile *missile; // Update missile->owner for all missiles whose owner is now dead while (objnodemissile != NULL) { missile = (Missile *)listGetStructOfNode(objnodemissile); dbgAssert(missile->objtype == OBJ_MissileType); if (missile->owner == (Ship *)target) { missile->owner = NULL; // missile has no owner now that parent ship died } if (missile->target == target) { missile->target = NULL; } objnodemissile = objnodemissile->next; } } /*----------------------------------------------------------------------------- Name : univRemoveAllDerelictReferencesFromSpecialShips Description : removes all derelict references from special ships Inputs : d - derelictto remove Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveAllDerelictReferencesFromSpecialShips(Derelict *d) { Node *node = universe.ShipList.head; Ship *ship; InsideShip *insideShip; while (node != NULL) { ship = (Ship *)listGetStructOfNode(node); dbgAssert(ship->objtype == OBJ_ShipType); if(ship->specialFlags & SPECIAL_IsASalvager) salCapRemoveDerelictReferences(ship,d); node = node->next; } // walk through ships in hyperspace too! node = singlePlayerGameInfo.ShipsInHyperspace.head; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); if(ship->specialFlags & SPECIAL_IsASalvager) salCapRemoveDerelictReferences(ship,d); node = node->next; } } void univRemoveAllShipReferencesFromSpecialShip(Ship *shiptoremove,Ship *ship) { if (ship->recentlyAttacked && ship->recentAttacker == shiptoremove) { // forget that this ship had recently attacked anyone ship->recentlyAttacked = 0; ship->recentAttacker = NULL; } if (ship->recentlyFiredUpon && ship->firingAtUs == shiptoremove) { ship->recentlyFiredUpon = 0; ship->firingAtUs = NULL; } if (ship->rowGetOutOfWay == shiptoremove) { rowGetOutOfWayShipDiedCB(ship); ship->rowGetOutOfWay = NULL; } if(!(gameIsEnding && ship->shiptype == ResearchShip)) { if(((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipRemoveShipReferences != NULL) { ((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipRemoveShipReferences(ship, shiptoremove); } } } /*----------------------------------------------------------------------------- Name : univRemoveAllShipReferencesFromSpecialShips Description : removes all ship references from special ships Inputs : shiptoremove Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveAllShipReferencesFromSpecialShips(Ship *shiptoremove) { Node *node = universe.ShipList.head; Ship *ship; InsideShip *insideShip; while (node != NULL) { ship = (Ship *)listGetStructOfNode(node); dbgAssert(ship->objtype == OBJ_ShipType); univRemoveAllShipReferencesFromSpecialShip(shiptoremove,ship); node = node->next; } // walk through ships in hyperspace too! node = singlePlayerGameInfo.ShipsInHyperspace.head; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); univRemoveAllShipReferencesFromSpecialShip(shiptoremove,ship); node = node->next; } } /*----------------------------------------------------------------------------- Name : DeleteShipsInsideMe Description : deletes ships that are inside me Inputs : ship which contains other ships Outputs : Return : ----------------------------------------------------------------------------*/ void DeleteShipsInsideMe(Ship *ship) { InsideShip *insideShip; Node *node = ship->shipsInsideMe->insideList.head; while (node != NULL) { insideShip = listGetStructOfNode(node); dbgAssert(insideShip->ship); insideShip->ship->howDidIDie = ship->howDidIDie; insideShip->ship->whoKilledMe = ship->whoKilledMe; univDeleteWipeInsideShipOutOfExistence(insideShip->ship); node = node->next; } listDeleteAll(&ship->shipsInsideMe->insideList); memFree(ship->shipsInsideMe); ship->shipsInsideMe = NULL; } /*----------------------------------------------------------------------------- Name : FreeShipsInsideMe Description : frees ships contents that are inside me Inputs : ship which contains other ships Outputs : Return : ----------------------------------------------------------------------------*/ void FreeShipsInsideMe(Ship *ship) { InsideShip *insideShip; Node *node = ship->shipsInsideMe->insideList.head; while (node != NULL) { insideShip = listGetStructOfNode(node); dbgAssert(insideShip->ship); insideShip->ship->howDidIDie = ship->howDidIDie; insideShip->ship->whoKilledMe = ship->whoKilledMe; univFreeShipContents(insideShip->ship); memFree(insideShip->ship); node = node->next; } listDeleteAll(&ship->shipsInsideMe->insideList); memFree(ship->shipsInsideMe); ship->shipsInsideMe = NULL; } /*----------------------------------------------------------------------------- Name : univWipeShipOutOfExistence Description : totally destroys and wipes this ship out of memory, and removes any remaining references. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univWipeShipOutOfExistence(Ship *ship) { dbgAssert(ship != NULL); if (ship->shiplink.belongto != NULL) { listRemoveNode(&ship->shiplink); } if (ship->impactablelink.belongto != NULL) { listRemoveNode(&ship->impactablelink); } nisObjectDied((SpaceObj *)ship); etgShipDied(ship); univRemoveObjFromRenderList((SpaceObj *)ship); univFreeShipContents(ship); listDeleteNode(&ship->objlink); } /*----------------------------------------------------------------------------- Name : univDeleteWipeInsideShipOutOfExistence Description : totally destroys and wipes a "ship that was inside another ship" out of memory. Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void univDeleteWipeInsideShipOutOfExistence(Ship *ship) { univFreeShipContents(ship); memFree(ship); //added Ala the request of Gary } /*----------------------------------------------------------------------------- Name : univRemoveShipFromOutside Description : removes a ship from the universe and from mission spheres so that it can be placed inside a ship Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveShipFromOutside(Ship *ship) { // if a ship goes inside another ship, missile can no longer target it univRemoveAllTargetReferencesFromMissiles((SpaceObjRotImpTarg *)ship); shipHasJustDisappeared(ship); // Update Camera focus stacks ccRemoveShip(&universe.mainCameraCommand,ship); // Let any mission spheres know ship is leaving it // msShipLeavingMissphere(ship,ship->myMissphere,ship->playerowner->homeMissphere); // Remove ships that are destroyed from any current selection clRemoveShipFromSelection((SelectAnyCommand *)&selSelected,ship); clRemoveShipFromSelection((SelectAnyCommand *)&selSelecting,ship); ioUpdateShipTotals(); // Remove ship from hotkey group if it belongs to one. //univRemoveShipFromHotkeyGroup(ship, FALSE); maintain hotkey groups //if sensors manager is running, delete it from any sm spheres if (smSensorsActive) { smObjectDied(ship); } bobObjectDied((SpaceObj *)ship,&universe.collBlobList); pingObjectDied((SpaceObj *)ship); ship->collMyBlob = NULL; pieShipDied(ship); mouseClickShipDied(ship); if (ship->shiplink.belongto != NULL) { listRemoveNode(&ship->shiplink); } if (ship->impactablelink.belongto != NULL) { listRemoveNode(&ship->impactablelink); } if (ship->objlink.belongto != NULL) { listRemoveNode(&ship->objlink); } univRemoveObjFromRenderList((SpaceObj *)ship); // Tell the sound layer that the ship is removed from the universe soundEventShipRemove(ship); ship->flags |= SOF_Hide; } /*----------------------------------------------------------------------------- Name : univRemoveShipFromHotkeyGroup Description : Removes ship from hotkey group Inputs : ship Outputs : if ship was removed or not. Return : bool8 ----------------------------------------------------------------------------*/ bool8 univRemoveShipFromHotkeyGroup(Ship *ship, bool8 PlayDefeat) { udword i; if (selAnyHotKeyTest(ship)) { //if ship in any hot key groups // Remove ships that are destroyed from any hot key groups for (i=0;iplayerowner->playerIndex); } } } } } ship->hotKeyGroup = 0; return(TRUE); } return(FALSE); } /*----------------------------------------------------------------------------- Name : univHideShipFromSpheres Description : Removes a ship from all mission spheres but not from the universe Inputs : Outputs : Return : void ----------------------------------------------------------------------------*/ void univHideShipFromSpheres(Ship *ship) { univRemoveObjFromRenderList((SpaceObj *)ship); ship->flags |= SOF_Hide; } /*----------------------------------------------------------------------------- Name : univHideJustAboutEverything Description : Sets the SOF_Hide bit on evertything that is not a resource and does not have the SOF_NISShip bit set. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univHideJustAboutEverything(void) { Node *node; SpaceObj *obj; for (node = universe.SpaceObjList.head; node != NULL; node = node->next) { obj = (SpaceObj *)listGetStructOfNode(node); if (!bitTest(obj->flags, SOF_NISShip)) { switch (obj->objtype) { case OBJ_ShipType: case OBJ_BulletType: case OBJ_DerelictType: case OBJ_EffectType: case OBJ_MissileType: if (bitTest(obj->flags, SOF_Hide)) { bitSet(obj->flags, SOF_HideSave2); } else { univRemoveObjFromRenderList(obj); bitSet(obj->flags, SOF_Hide); } break; case OBJ_AsteroidType: case OBJ_NebulaType: case OBJ_GasType: case OBJ_DustType: break; } } } } /*----------------------------------------------------------------------------- Name : univUnhideJustAboutEverything Description : Unhide everything that was hidden in a previous call to univHideJustAboutEverything. Inputs : void Outputs : Return : void ----------------------------------------------------------------------------*/ void univUnhideJustAboutEverything(void) { Node *node; SpaceObj *obj; for (node = universe.SpaceObjList.head; node != NULL; node = node->next) { obj = (SpaceObj *)listGetStructOfNode(node); if (bitTest(obj->flags, SOF_HideSave2)) { //if it was already hidden last univHideJustAboutEverything bitClear(obj->flags, SOF_HideSave2); } else { bitClear(obj->flags, SOF_Hide); } } } /*----------------------------------------------------------------------------- Name : univDeleteDeadShip Description : Removes all references to the ship, except for a few ones to let the ship hang around after the explosion. Inputs : ship deathby - mode of death, or -1 for no death effects Outputs : Return : ----------------------------------------------------------------------------*/ void univDeleteDeadShip(Ship *ship, sdword deathBy) { Effect *effect; etgeffectstatic *explosion; etglod *etgLOD; sdword LOD; real32 colSize; udword colSizeDword, velMagnitudeDword; real32 velMagnitude, maxVelocity; vecZeroVector(ship->posinfo.force); vecZeroVector(ship->rotinfo.torque); ship->flags |= SOF_Dead; bitClear(ship->flags,SOF_Selectable); if (((ShipStaticInfo *)(ship->staticinfo))->canBuildShips) { cmRemoveFactory(ship); } if (ship->rceffect != NULL) { TurnHarvestEffectOff(ship); } // Check if launch manager is up, if it is then fix it update it on a ships death. if (lmActive) { if ((ship->shiptype==Carrier) || (ship->shiptype==Mothership)) { lmLaunchCapableShipDied(ship); } } if(((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipDied != NULL) { ((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipDied(ship); } tacticsShipDied(ship); if (ship->shipsInsideMe) { DeleteShipsInsideMe(ship); } if (bitTest(ship->flags,SOF_Slaveable)) { //ship was involved with the slave trade...so cleanup dockDealWithDeadSlaveable(ship); } // Remove repair droids... /* if(ship->pRepairDroids) { memFree(ship->pRepairDroids); ship->pRepairDroids = 0; }*/ #if ETG_DISABLEABLE if (deathBy >= 0 && etgEffectsEnabled) #else if (deathBy >= 0) #endif { if (deathBy == EDT_AccumDamage) { battleShipDyingWithTimeToScream(ship); } // if (univSpaceObjInRenderList((SpaceObj *)ship)) // { etgLOD = etgDeathEventTable[((ShipStaticInfo *)ship->staticinfo)->shiprace] [((ShipStaticInfo *)ship->staticinfo)->shipclass][deathBy]; if (etgLOD != NULL) { LOD = ship->currentLOD; explosion = etgLOD->level[min(etgLOD->nLevels - 1, LOD)]; } else { explosion = NULL; } if (explosion) { colSize = ((ShipStaticInfo *)(ship->staticinfo))->staticheader.staticCollInfo.collspheresize; // if (univSpaceObjInRenderList((SpaceObj *)ship)) // { colSize *= ship->magnitudeSquared; // } colSizeDword = TreatAsUdword(colSize); maxVelocity = ship->staticinfo->staticheader.maxvelocity; if (maxVelocity != 0.0f) { velMagnitude = fsqrt(vecMagnitudeSquared(ship->posinfo.velocity)); velMagnitude /= maxVelocity; } else { velMagnitude = 0; } velMagnitudeDword = TreatAsUdword(velMagnitude); //!!!??? should there be some EAF_ here??? effect = etgEffectCreate(explosion, ship, NULL, NULL, NULL, 1.0f, 0, 2, colSizeDword, velMagnitudeDword); /* if (!univSpaceObjInRenderList((SpaceObj *)effect)) { univAddObjToRenderList((SpaceObj *)effect);//make sure it's in render list } */ bitSet(effect->flags, SOF_ForceVisible); } // } } //notify leader of a lost wingman if (ship->tacticstype == Evasive) { //if in evasive tactics if (ship->attackvars.myLeaderIs != NULL) { //if ship has a wingman bitSet(ship->attackvars.myLeaderIs->chatterBits, BCB_WingmanLost); } } univRemoveAllTargetReferencesFromBullets((SpaceObjRotImpTarg *)ship); univRemoveAllTargetReferencesFromMissiles((SpaceObjRotImpTarg *)ship); univRemoveAllShipReferencesFromSpecialShips(ship); //if sensors manager is running, delete it from any sm spheres if (smSensorsActive) { smObjectDied(ship); } bobObjectDied((SpaceObj *)ship,&universe.collBlobList); pingObjectDied((SpaceObj *)ship); ship->collMyBlob = NULL; // Update Ship Command Layer to reflect ship death clShipDied(&universe.mainCommandLayer,ship); // Update computer player structures to reflect ship death aiplayerShipDied(ship); // Update Camera focus stacks ccRemoveShip(&universe.mainCameraCommand,ship); // Let any mission spheres know ship is leaving it // msShipLeavingMissphere(ship,ship->myMissphere,ship->playerowner->homeMissphere); // Tell single player game module ship died singlePlayerShipDied(ship); if(tutorial) tutPlayerShipDied(ship); // Tell the sound layer that the ship died soundEventShipDied(ship); IDToPtrTableObjDied(&ShipIDToPtr,ship->shipID.shipNumber); growSelectRemoveShipBySettingNULL(&universe.HousekeepShipList,ship); // Remove ships that are destroyed from any current selection clRemoveShipFromSelection((SelectCommand *)&selSelected,ship); clRemoveShipFromSelection((SelectCommand *)&selSelecting,ship); ioUpdateShipTotals(); pieShipDied(ship); mouseClickShipDied(ship); // Remove ships that are destroyed from any hot key groups and play group death sound if all dead univRemoveShipFromHotkeyGroup(ship, TRUE); //if no effects, we should delete it properly /* #if ETG_DISABLEABLE if (!etgEffectsEnabled) { univWipeShipOutOfExistence(ship); } #endif */ } void DisableAllPlayersShips(sdword playerIndex) { Node *objnode = universe.ShipList.head; Ship *ship; Player *player = &universe.players[playerIndex]; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); if (ship->playerowner == player) { if(!(ship->flags & SOF_Disabled)) { ship->flags |= SOF_Crazy; ship->deathtime = universe.totaltimeelapsed + frandombetween(PLAYERLOSE_SHIPDEATHTIMEMIN,PLAYERLOSE_SHIPDEATHTIMEMAX); if ((!singlePlayerGame) && (ship->shiptype != Mothership)) ApplyCareenRotationDirectly(ship); } bitClear(ship->flags,SOF_Selectable); // Remove ships that are destroyed from any current selection clRemoveShipFromSelection((SelectCommand *)&selSelected,ship); // Remove ships that are destroyed from any hot key groups univRemoveShipFromHotkeyGroup(ship,FALSE); } objnode = objnode->next; } ioUpdateShipTotals(); if (piePointSpecMode != PSM_Idle) { //make sure the movement mechanism is off piePointModeOnOff(); } } void DisplayPlayerHasBeenDefeatedMessage(sdword playerIndex) { char defeatmsg[100]; strcpy(defeatmsg,playerNames[playerIndex]); strcat(defeatmsg," "); strcat(defeatmsg,strGetString(strPlayerHasDied)); clCommandMessage(defeatmsg); } void CheckPlayerWin(void) { udword numPlayersAlive = 0; udword i; if (gatherStats|showStatsFight|showStatsFancyFight) return; // just gathering stats, don't want to do the player win thing if (!singlePlayerGame) { // check how many players are still alive for (i=0;i1) { strcat(winmsg, ", "); } else if ((numPlayersAlive-count)==1) { strcat(winmsg, strGetString(strAnd)); strcat(winmsg, " "); } } else { if (i != universe.curPlayerIndex) { otherplayersdied++; if (universe.gameStats.playerStats[i].typeOfPlayerDeath == PLAYERKILLED_DROPPEDOUT) { otherplayersdroppedout++; } } } } if ((otherplayersdroppedout > 0) && (otherplayersdroppedout == otherplayersdied)) { bigmessageDisplay(strGetString(strAllOtherPlayersDroppedOut),1); } else { strcat(winmsg," "); if (numPlayersAlive > 1) strcat(winmsg,strGetString(strPlayersHaveWon)); else strcat(winmsg,strGetString(strPlayerHasWon)); bigmessageDisplay(winmsg,1); } } } /*----------------------------------------------------------------------------- Name : univFindBackupMothership Description : Looks through the universeshiplist to find out if the player who's mothership died has a backup mothership Inputs : player - the player who's mothership just died Outputs : Return : TRUE if a backup was found ----------------------------------------------------------------------------*/ bool univFindBackupMothership(struct Player *player) { Node *shipnode = universe.ShipList.head; Ship *ship; while (shipnode != NULL) { ship = (Ship *)listGetStructOfNode(shipnode); if (((ship->flags & SOF_Dead) == 0) && (ship->playerowner == player) && (ship != player->PlayerMothership)) { if ((ship->shiptype == Carrier) || (ship->staticinfo->shipclass == CLASS_Mothership)) { if (player->PlayerMothership != NULL) { if (gameIsRunning && (player->PlayerMothership->shiptype == Mothership)) { speechEventFleet(STAT_F_ReassignedToCarrier, 0, player->playerIndex); } } player->PlayerMothership = ship; return TRUE; } } shipnode = shipnode->next; } return FALSE; } void univKillOtherPlayersIfDead(Ship *ship) { bool aplayerdied=FALSE; sdword i; //for (i=0;iplayerowner->playerIndex; if (ship == universe.players[i].PlayerMothership) { if (singlePlayerGame) { if (i != 0) { // player 1 of single player game just lost mothership - but don't kill player universe.players[i].PlayerMothership = NULL; } else { aplayerdied = TRUE; univKillPlayer(i,PLAYERKILLED_STANDARDDEATH); } } else if (!univFindBackupMothership(&universe.players[i])) { if(tpGameCreated.flag & MG_LastMotherShip || universe.players[i].totalships == 0) { aplayerdied = TRUE; univKillPlayer(i,PLAYERKILLED_STANDARDDEATH); } else { universe.players[i].PlayerMothership = NULL; } } } else if(universe.players[i].totalships == 0) { if(!singlePlayerGame) { //all players ships are dead...he's dead aplayerdied = TRUE; univKillPlayer(i,PLAYERKILLED_STANDARDDEATH); } } if (aplayerdied) { CheckPlayerWin(); } //} } /*----------------------------------------------------------------------------- Name : univDeleteDeadShips Description : deletes all dead ships Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univDeleteDeadShips() { Node *objnode = universe.DeleteShipList.head; DeleteShip *deleteShip; Ship *ship; Node *nextnode; if (objnode == NULL) { return; } do { nextnode = objnode->next; deleteShip = (DeleteShip *)listGetStructOfNode(objnode); ship = deleteShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); univDeleteDeadShip(ship,deleteShip->deathBy); if (ship->impactablelink.belongto != NULL) { listRemoveNode(&ship->impactablelink); } if (ship->shiptype == ResourceCollector) { if (ship->resources > 0) { DustCloud *dustCloud = univAddDustCloud(DustCloud0,&ship->posinfo.position); dustCloud->resourcevalue = (sdword) (ship->resources * DUSTCLOUD_RCOLLECTOR_BLOWUP_SCALE); HandleDustCloudScaling(dustCloud); univAddObjToRenderListIf((SpaceObj *)dustCloud,(SpaceObj *)ship); // add to render list if parent is in render list } } univKillOtherPlayersIfDead(ship); // don't totally delete ship, until after explosion #if ETG_DISABLEABLE if (!etgEffectsEnabled) { univWipeShipOutOfExistence(ship); } #endif objnode = nextnode; } while (objnode != NULL); listDeleteAll(&universe.DeleteShipList); } /*----------------------------------------------------------------------------- Name : univReallyDeleteThisShipRightNow Description : Like you need to ask? It kills it. No effects, no melodrama, no waiting around to delete it later. Just BOOM! Inputs : ship - ship to delete Outputs : Return : void Note : do not call this while you are walking any ship-related linked lists. ----------------------------------------------------------------------------*/ void univReallyDeleteThisShipRightNow(Ship *ship) { univDeleteDeadShip(ship, -1); if (ship->impactablelink.belongto != NULL) { listRemoveNode(&ship->impactablelink); } univWipeShipOutOfExistence(ship); } /*----------------------------------------------------------------------------- Name : DeleteDeadResource Description : Deletes an individual resource, and removes all references to it. Also frees its contents. Inputs : resource Outputs : Return : ----------------------------------------------------------------------------*/ void DeleteDeadResource(Resource *resource) { //if sensors manager is running, delete it from any sm spheres if (smSensorsActive) { smObjectDied((SpaceObj *)resource); } ccRemoveShip(&universe.mainCameraCommand,(Ship *)resource); bobObjectDied((SpaceObj *)resource,&universe.collBlobList); pingObjectDied((SpaceObj *)resource); // Update Command Layer to reflect resource death univRemoveAllTargetReferencesFromBullets((SpaceObjRotImpTarg *)resource); univRemoveAllTargetReferencesFromMissiles((SpaceObjRotImpTarg *)resource); clResourceDied(&universe.mainCommandLayer,resource); aiplayerResourceDied(resource); IDToPtrTableObjDied(&ResourceIDToPtr,resource->resourceID.resourceNumber); univFreeResourceContents(resource); } /*----------------------------------------------------------------------------- Name : univReallyDeleteThisResourceRightNow Description : Like you need to ask? It kills it. No effects, no melodrama, no waiting around to delete it later. Just BOOM! Inputs : resource - resource to delete Outputs : Return : do not call this while you are walking any resource-related linked lists. ----------------------------------------------------------------------------*/ void univReallyDeleteThisResourceRightNow(Resource *resource) { DeleteDeadResource(resource); if ((resource->objtype == OBJ_AsteroidType) && (((Asteroid *)resource)->asteroidtype == Asteroid0)) { univRemoveObjFromMinorRenderList((SpaceObj *)resource); listDeleteNode(&resource->objlink); } else { listRemoveNode(&resource->resourcelink); listRemoveNode(&resource->impactablelink); univRemoveObjFromRenderList((SpaceObj *)resource); listDeleteNode(&resource->objlink); } } /*----------------------------------------------------------------------------- Name : univDeleteDeadResources Description : deletes all dead resources Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univDeleteDeadResources() { Node *objnode = universe.DeleteResourceList.head; DeleteResource *deleteResource; Resource *resource; if (objnode == NULL) { return; } do { deleteResource = (DeleteResource *)listGetStructOfNode(objnode); resource = deleteResource->resource; if (resource->resourcevalue == 0) { // resource was harvested away, so give opportunity for more to grow: if (resource->resourceVolume) { if (resource->resourceVolume->actualnumber > 0) { resource->resourceVolume->actualnumber--; } } } if (resource->objtype == OBJ_AsteroidType) { if (((Asteroid *)resource)->resourcevalue > 0) { BreakAsteroidUp((Asteroid *)resource); } } univReallyDeleteThisResourceRightNow(resource); objnode = objnode->next; } while (objnode != NULL); listDeleteAll(&universe.DeleteResourceList); } /*----------------------------------------------------------------------------- Name : DeleteDeadDerelict Description : Deletes an individual derelict, and removes all references to it. Also frees its contents. Inputs : derelict - derelict to delete deathBy - how it died. Used to create an effect. -1 means no effect. Outputs : Return : TRUE if an effect for deleting the derelict was played ----------------------------------------------------------------------------*/ bool DeleteDeadDerelict(Derelict *derelict, sdword deathBy) { Effect *effect; etgeffectstatic *explosion = NULL; etglod *etgLOD; sdword LOD; real32 colSize; udword colSizeDword, velMagnitudeDword; real32 velMagnitude; if (derelict->derelicttype == HyperspaceGate) { hsDerelictDied(derelict); } bobObjectDied((SpaceObj *)derelict,&universe.collBlobList); //if sensors manager is running, delete it from any sm spheres if (smSensorsActive) { smObjectDied((SpaceObj *)derelict); } /* shut off the derelicts sounds */ soundEventDerelictRemove(derelict); univRemoveAllDerelictReferencesFromSpecialShips(derelict); pingObjectDied((SpaceObj *)derelict); ccRemoveShip(&universe.mainCameraCommand,(Ship *)derelict); // Update Command Layer to reflect derelict death univRemoveAllTargetReferencesFromBullets((SpaceObjRotImpTarg *)derelict); univRemoveAllTargetReferencesFromMissiles((SpaceObjRotImpTarg *)derelict); clDerelictDied(&universe.mainCommandLayer,derelict); IDToPtrTableObjDied(&DerelictIDToPtr,derelict->derelictID.derelictNumber); #if ETG_DISABLEABLE if (deathBy >= 0 && etgEffectsEnabled) #else if (deathBy >= 0) #endif { //if we should create an effect etgLOD = etgDeathEventTableDerelict[derelict->derelicttype][deathBy]; if (etgLOD != NULL) { LOD = derelict->currentLOD; explosion = etgLOD->level[min(etgLOD->nLevels - 1, LOD)]; } else { explosion = NULL; } if (explosion) { colSize = ((ShipStaticInfo *)(derelict->staticinfo))->staticheader.staticCollInfo.collspheresize; //colSize *= derelict->magnitudeSquared; colSizeDword = TreatAsUdword(colSize); velMagnitude = fsqrt(vecMagnitudeSquared(derelict->posinfo.velocity)); velMagnitude /= derelict->staticinfo->staticheader.maxvelocity; velMagnitudeDword = TreatAsUdword(velMagnitude); //??? EAF_ flags ??? effect = etgEffectCreate(explosion, derelict, NULL, NULL, NULL, 1.0f, 0, 2, colSizeDword, velMagnitudeDword); bitSet(effect->flags, SOF_ForceVisible); } } univFreeDerelictContents(derelict); listRemoveNode(&derelict->derelictlink); listRemoveNode(&derelict->impactablelink); derelict->derelictlink.belongto = NULL; derelict->impactablelink.belongto = NULL; univRemoveFromWorldList(derelict); //if it's a planet, remove it from the planet list return(explosion != NULL); } /*----------------------------------------------------------------------------- Name : univReallyDeleteThisDerelictRightNow Description : Like you need to ask? It kills it. No effects, no melodrama, no waiting around to delete it later. Just BOOM! Inputs : derelict - derelict to delete Outputs : Return : do not call this while you are walking any derelict-related linked lists. ----------------------------------------------------------------------------*/ void univReallyDeleteThisDerelictRightNow(Derelict *derelict) { bitSet(derelict->flags, SOF_Dead); DeleteDeadDerelict(derelict, -1); // listRemoveNode(&derelict->derelictlink); // listRemoveNode(&derelict->impactablelink); univRemoveObjFromRenderList((SpaceObj *)derelict); // univRemoveFromWorldList(derelict); //if it's a planet, remove it from the planet list listDeleteNode(&derelict->objlink); } /*----------------------------------------------------------------------------- Name : univWipeDerelictOutOfExistance Description : This function deletes a derelict upon command of the death effect. Inputs : derelict - derelict to go bye-bye Outputs : Return : void ----------------------------------------------------------------------------*/ void univWipeDerelictOutOfExistance(Derelict *derelict) { bobObjectDied((SpaceObj *)derelict,&universe.collBlobList); nisObjectDied((SpaceObj *)derelict); if (derelict->derelictlink.belongto != NULL) { listRemoveNode(&derelict->derelictlink); } if (derelict->impactablelink.belongto != NULL) { listRemoveNode(&derelict->impactablelink); } univRemoveObjFromRenderList((SpaceObj *)derelict); etgShipDied((Ship *)derelict); univRemoveFromWorldList(derelict); //if it's a planet, remove it from the planet list listDeleteNode(&derelict->objlink); } /*----------------------------------------------------------------------------- Name : univDeleteDeadDerelicts Description : deletes all dead derelicts Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univDeleteDeadDerelicts() { Node *objnode = universe.DeleteDerelictList.head; DeleteDerelict *deleteDerelict; Derelict *derelict; if (objnode == NULL) { return; } do { deleteDerelict = (DeleteDerelict *)listGetStructOfNode(objnode); derelict = deleteDerelict->derelict; dbgAssert(derelict->objtype == OBJ_DerelictType); // don't totally delete ship, until after explosion #if ETG_DISABLEABLE if (!etgEffectsEnabled) { univReallyDeleteThisDerelictRightNow(derelict); } else #endif { if (!DeleteDeadDerelict(derelict, deleteDerelict->deathBy)) { //if there was no effect univRemoveObjFromRenderList((SpaceObj *)derelict); listDeleteNode(&derelict->objlink); //finish deleting it properly } } objnode = objnode->next; } while (objnode != NULL); listDeleteAll(&universe.DeleteDerelictList); } /*----------------------------------------------------------------------------- Name : DeleteDeadMissile Description : Deletes a dead missile, and removes all references to it. Also frees it contents. Inputs : deathBy - how the missile/mine died Outputs : Return : ----------------------------------------------------------------------------*/ void DeleteDeadMissile(Missile *missile, sdword deathBy) { real32 damagetaken = missile->damage; MissileStaticInfo *missilestaticinfo = (MissileStaticInfo *)missile->staticinfo; etgeffectstatic *stat; sdword LOD; bool fatalHit; real32 floatDamage; udword intDamage; vector hitLocation; if (deathBy >= 0) { //if it was destroyed by forced attack if (missile->hitEffect != NULL) { //use the regular hit effect LOD = missile->currentLOD; if (LOD < missile->hitEffect->nLevels) { stat = missile->hitEffect->level[LOD]; } else { stat = NULL; } } else { stat = NULL; } if (univSpaceObjInRenderList((SpaceObj *)missile)) { #if ETG_DISABLEABLE if (stat != NULL && etgHitEffectsEnabled && etgEffectsEnabled && (!etgFrequencyExceeded(stat))) #else if (stat != NULL && etgHitEffectsEnabled && (!etgFrequencyExceeded(stat))) #endif { hitLocation = missile->posinfo.position; floatDamage = missile->damage; if (RGLtype == SWtype) { //smaller hit effects in software floatDamage *= etgSoftwareScalarHit; } intDamage = TreatAsUdword(floatDamage); fatalHit = FALSE; etgEffectCreate(stat, NULL, &hitLocation, &missile->posinfo.velocity, &missile->rotinfo.coordsys, 1.0, 0, 2, intDamage, fatalHit); } } } bobObjectDied((SpaceObj *)missile,&universe.collBlobList); pingObjectDied((SpaceObj *)missile); univRemoveAllTargetReferencesFromBullets((SpaceObjRotImpTarg *)missile); univRemoveAllTargetReferencesFromMissiles((SpaceObjRotImpTarg *)missile); nisRemoveMissileReference(missile); clMissileDied(&universe.mainCommandLayer,missile); IDToPtrTableObjDied(&MissileIDToPtr,missile->missileID.missileNumber); univFreeMissileContents(missile); } /*----------------------------------------------------------------------------- Name : univDeleteDeadMissiles Description : deletes all dead missiles Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univDeleteDeadMissiles() { Node *objnode = universe.DeleteMissileList.head; DeleteMissile *deleteMissile; Missile *missile; if (objnode == NULL) { return; } do { deleteMissile = (DeleteMissile *)listGetStructOfNode(objnode); missile = deleteMissile->missile; DeleteDeadMissile(missile, deleteMissile->deathBy); listRemoveNode(&missile->missilelink); listRemoveNode(&missile->impactablelink); univRemoveObjFromRenderList((SpaceObj *)missile); listDeleteNode(&missile->objlink); objnode = objnode->next; } while (objnode != NULL); listDeleteAll(&universe.DeleteMissileList); } /*----------------------------------------------------------------------------- Name : univGetResourceStatistics Description : outputs the total resourceValue, numHarvestableResources, and numAsteroid0s in the universe Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univGetResourceStatistics(sdword *resourceValue,sdword *numHarvestableResources,sdword *numAsteroid0s) { Node *objnode = universe.ResourceList.head; Resource *resource; sdword total = 0; sdword numharvestable = 0; sdword numasteroid0s = 0; while (objnode != NULL) { resource = (Resource *)listGetStructOfNode(objnode); dbgAssert(resource->flags & SOF_Resource); if (nisIsRunning && (resource->health >= REALlyBig)) { goto nextnode; // don't pick NIS resources } #if 0 // Asteroid0's not in Resource list anymore so this isn't needed if ((resource->objtype == OBJ_AsteroidType) && (((Asteroid *)resource)->asteroidtype == Asteroid0)) { goto nextnode; // don't harvest pebbles } #endif total += resource->resourcevalue; numharvestable++; nextnode: objnode = objnode->next; } for (objnode = universe.MinorSpaceObjList.head;objnode != NULL;objnode = objnode->next) { resource = (Resource *)listGetStructOfNode(objnode); if ((resource->objtype == OBJ_AsteroidType) && (((Asteroid *)resource)->asteroidtype == Asteroid0)) { numasteroid0s++; } } *resourceValue = total; *numHarvestableResources = numharvestable; *numAsteroid0s = numasteroid0s; } bool univObjectOutsideWorld(SpaceObj *obj) { vector pos = obj->posinfo.position; vector oldpos = pos; vector zero = { 0.0f, 0.0f, 0.0f }; pieMovePointClipToLimits(smUniverseSizeX,smUniverseSizeY,smUniverseSizeZ,&zero,&pos); if (vecAreEqual(pos,oldpos)) { return FALSE; } return TRUE; } /*----------------------------------------------------------------------------- Name : univFindNearestResource Description : finds the nearest resource to ship in optional volume specified by volumePosition and volumeRadius. If VolumeRadius == 0, then volume is ignored Inputs : Outputs : Return : returns the nearest resource (or NULL if none found) ----------------------------------------------------------------------------*/ Resource *univFindNearestResource(Ship *ship,real32 volumeRadius,vector *volumePosition) { Node *objnode = universe.ResourceList.head; vector diff; real32 dist; real32 mindist = (real32)1e20; Resource *closestResource = NULL; Resource *resource; while (objnode != NULL) { resource = (Resource *)listGetStructOfNode(objnode); dbgAssert(resource->flags & SOF_Resource); if (nisIsRunning && (resource->health >= REALlyBig)) { goto nextnode; // don't pick NIS resources } #if 0 // Asteroid0's not in Resource list anymore so this isn't needed if ((resource->objtype == OBJ_AsteroidType) && (((Asteroid *)resource)->asteroidtype == Asteroid0)) { goto nextnode; // don't harvest pebbles } #endif if (ResourceMovingTooFast(resource)) { goto nextnode; } if (ResourceAlreadyBeingHarvested(&universe.mainCommandLayer,NULL,resource)) { goto nextnode; } if (resource->resourceNotAccessible) { goto nextnode; } if (volumeRadius > 0.0f) { vecSub(diff,resource->posinfo.position,*volumePosition); if (!isBetweenInclusive(diff.x,-volumeRadius,volumeRadius)) { goto nextnode; } if (!isBetweenInclusive(diff.y,-volumeRadius,volumeRadius)) { goto nextnode; } if (!isBetweenInclusive(diff.z,-volumeRadius,volumeRadius)) { goto nextnode; } } if (univObjectOutsideWorld((SpaceObj *)resource)) { goto nextnode; } vecSub(diff,resource->posinfo.position,ship->posinfo.position); dist = vecMagnitudeSquared(diff); if (dist < mindist) { mindist = dist; closestResource = resource; } nextnode: objnode = objnode->next; } return closestResource; } /*----------------------------------------------------------------------------- Name : univGetChecksum Description : returns a checksum representing the status of the universe Inputs : Outputs : numShipsInChecksum Return : checksum of mission sphere at this point in time ----------------------------------------------------------------------------*/ real32 univGetChecksum(sdword *numShipsInChecksum) { Node *objnode = universe.ShipList.head; Ship *ship; Derelict *derelict; Resource *resource; #if BINNETLOG Bullet *bullet; #endif sdword countShips = 0; real32 x=0.0f; real32 y=0.0f; real32 z=0.0f; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); if ((ship->flags & SOF_Dead) == 0) { x += ship->posinfo.position.x; y += ship->posinfo.position.y; z += ship->posinfo.position.z; if ((netlogfile) && (logEnable == LOG_VERBOSE)) { #if BINNETLOG binnetlogShipInfo bns; bns.header = makenetcheckHeader('S','S','S','S'); bns.shipID = ship->shipID.shipNumber; bns.playerIndex = ship->playerowner->playerIndex; bns.shiprace = ship->shiprace; bns.shiptype = ship->shiptype; if (ship->command) { bns.shiporder = ship->command->ordertype.order; bns.shipattributes = ship->command->ordertype.attributes; } else { bns.shiporder = -1; bns.shipattributes = -1; } bns.tacticstype = ship->tacticstype; bns.isDodging = ship->isDodging; bns.DodgeDir = ship->DodgeDir; bns.health = ship->health; bns.x = ship->posinfo.position.x; bns.y = ship->posinfo.position.y; bns.z = ship->posinfo.position.z; bns.vx = ship->posinfo.velocity.x; bns.vy = ship->posinfo.velocity.y; bns.vz = ship->posinfo.velocity.z; bns.fuel = ship->fuel; fwrite(&bns, sizeof(bns), 1 ,netlogfile); #else fprintf(netlogfile,"S:%d %d %d %d (%f %f %f) %f %d %d %d %d (%d %d)\n",ship->playerowner->playerIndex,ship->shiprace,ship->shiptype, ship->shipID.shipNumber,ship->posinfo.position.x,ship->posinfo.position.y,ship->posinfo.position.z,ship->fuel,ship->tacticstype,ship->isDodging,ship->DodgeDir,ship->tactics_ordertype, (ship->command == NULL) ? -1 : ship->command->ordertype.order, (ship->command == NULL) ? -1 : ship->command->ordertype.attributes); #endif if ((ship->command) && (ship->command->ordertype.order == COMMAND_COLLECTRESOURCE)) { CollectResourceCommand *collect = &ship->command->collect; #if BINNETLOG binnetlogShipResourceInfo bnr; bnr.header = makenetcheckHeader('S','R','S','R'); bnr.resourceID = (collect->resource != NULL) ? collect->resource->resourceID.resourceNumber : -1; bnr.volume = collect->resourceVolumeSize; bnr.x = collect->resourceVolumePosition.x; bnr.y = collect->resourceVolumePosition.y; bnr.z = collect->resourceVolumePosition.z; fwrite(&bnr, sizeof(bnr), 1 ,netlogfile); #else fprintf(netlogfile,"R:%d ",(collect->resource != NULL) ? collect->resource->resourceID.resourceNumber : -1,collect->resourceVolumeSize, collect->resourceVolumePosition.x,collect->resourceVolumePosition.y,collect->resourceVolumePosition.z); #endif } if (ship->dockInfo != NULL) { DockInfo *dockInfo = ship->dockInfo; sdword i; #if BINNETLOG binnetlogShipDockInfo bnd; udword orflag = 1; bnd.header = makenetcheckHeader('S','D','S','D'); bnd.busyness = dockInfo->busyness; bnd.numDockPoints = dockInfo->numDockPoints; bnd.thisDockBusy = 0; for (i=0;inumDockPoints;i++) { if (dockInfo->dockpoints[i].thisDockBusy) { bnd.thisDockBusy |= orflag; } orflag <<= 1; } fwrite(&bnd, sizeof(bnd), 1 ,netlogfile); #else fprintf(netlogfile,"B:%d P:",dockInfo->busyness); for (i=0;inumDockPoints;i++) { fprintf(netlogfile," %d",dockInfo->dockpoints[i].thisDockBusy ? 1 : 0); } fprintf(netlogfile,"\n"); #endif } if (ship->madBindings != NULL) { #if BINNETLOG binnetlogShipMadInfo bnm; bnm.header = makenetcheckHeader('S','M','S','M'); bnm.info[0] = ship->madAnimationFlags; bnm.info[1] = ship->madGunStatus; bnm.info[2] = ship->madWingStatus; bnm.info[3] = ship->madDoorStatus; bnm.info[4] = ship->madSpecialStatus; bnm.info[5] = ship->cuedAnimationIndex; bnm.info[6] = ship->cuedAnimationType; bnm.info[7] = 0; bnm.info[8] = 0; fwrite(&bnm, sizeof(bnm), 1 ,netlogfile); #else // flags,guns,wings,dock,special,index,type fprintf(netlogfile,"M: %df %dg %dw %dd %ds %di %dt\n", ship->madAnimationFlags,ship->madGunStatus,ship->madWingStatus, ship->madDoorStatus, ship->madSpecialStatus,ship->cuedAnimationIndex,ship->cuedAnimationType); #endif } } countShips++; } objnode = objnode->next; } #ifndef HW_Release #if BINNETLOG if ((netlogfile) && (logEnable == LOG_VERBOSE)) { objnode = universe.BulletList.head; while (objnode != NULL) { bullet = (Bullet *)listGetStructOfNode(objnode); if ((!bitTest(bullet->flags, SOF_Dead))) { #if BINNETLOG binnetlogBulletInfo bi; bi.header = makenetcheckHeader('B','B','B','B'); bi.bullettype = bullet->bulletType; bi.bulletplayerowner = (bullet->playerowner!= NULL) ? bullet->playerowner->playerIndex : -1; bi.bulletowner = (bullet->owner != NULL) ? bullet->owner->shipID.shipNumber : -1; bi.x = bullet->posinfo.position.x; bi.y = bullet->posinfo.position.y; bi.z = bullet->posinfo.position.z; bi.vx = bullet->posinfo.velocity.x; bi.vy = bullet->posinfo.velocity.y; bi.vz = bullet->posinfo.velocity.z; bi.timelived = bullet->timelived; bi.totallifetime = bullet->totallifetime; bi.traveldist = bullet->traveldist; bi.damage = bullet->damage; bi.damageFull = bullet->damageFull; bi.DFGFieldEntryTime = bullet->DFGFieldEntryTime; bi.BulletSpeed = 0.0f; bi.collBlobSortDist = (bullet->collMyBlob) ? bullet->collMyBlob->sortDistance : 0.0f; fwrite(&bi,sizeof(bi),1,netlogfile); #else if (bullet->playerowner!= NULL) fprintf(netlogfile,"B: %d %d ", universe.univUpdateCounter, bullet->playerowner->playerIndex); else fprintf(netlogfile,"B: %d N ", universe.univUpdateCounter); fprintf(netlogfile,"(%f %f %f) [%f %f] %f <%f %f> %f %f ", bullet->posinfo.position.x, bullet->posinfo.position.y, bullet->posinfo.position.z, bullet->timelived, bullet->totallifetime, bullet->traveldist, bullet->damage, bullet->damageFull, bullet->DFGFieldEntryTime, bullet->BulletSpeed); if (bullet->collMyBlob) fprintf(netlogfile, "%f\n", bullet->collMyBlob->sortDistance); else fprintf(netlogfile, "N\n"); #endif } objnode = objnode->next; } } #endif #if BINNETLOG if ((netlogfile) && (logEnable == LOG_VERBOSE)) { clChecksum(); } #endif objnode = universe.DerelictList.head; while (objnode != NULL) { derelict = (Derelict *)listGetStructOfNode(objnode); if ((derelict->flags & SOF_Dead) == 0) { x += derelict->posinfo.position.x; y += derelict->posinfo.position.y; z += derelict->posinfo.position.z; if ((netlogfile) && (logEnable == LOG_VERBOSE)) { #if BINNETLOG binnetDerelictInfo di; di.header = makenetcheckHeader('D','D','D','D'); di.derelictid = derelict->derelictID.derelictNumber; di.derelicttype = derelict->derelicttype; di.health = derelict->health; di.x = derelict->posinfo.position.x; di.y = derelict->posinfo.position.y; di.z = derelict->posinfo.position.z; di.vx = derelict->posinfo.velocity.x; di.vy = derelict->posinfo.velocity.y; di.vz = derelict->posinfo.velocity.z; fwrite(&di,sizeof(di),1,netlogfile); #else fprintf(netlogfile," Derelict:%d %s (%f %f %f)\n",derelict->derelictID.derelictNumber,DerelictTypeToStr(derelict->derelicttype), derelict->posinfo.position.x,derelict->posinfo.position.y,derelict->posinfo.position.z); #endif } countShips++; } objnode = objnode->next; } objnode = universe.ResourceList.head; while (objnode != NULL) { resource = (Resource *)listGetStructOfNode(objnode); x += resource->posinfo.position.x; y += resource->posinfo.position.y; z += resource->posinfo.position.z; #if BINNETLOG if ((netlogfile) && (logEnable == LOG_VERBOSE)) { binnetResourceInfo ri; ri.header = makenetcheckHeader('R','R','R','R'); ri.resourceid = resource->resourceID.resourceNumber; ri.resourcetype = resource->objtype; ri.resourceValue = resource->resourcevalue; ri.health = resource->health; ri.x = resource->posinfo.position.x; ri.y = resource->posinfo.position.y; ri.z = resource->posinfo.position.z; ri.vx = resource->posinfo.velocity.x; ri.vy = resource->posinfo.velocity.y; ri.vz = resource->posinfo.velocity.z; fwrite(&ri,sizeof(ri),1,netlogfile); } #endif countShips++; objnode = objnode->next; } #endif *numShipsInChecksum = countShips; return (x+y+z); } /*----------------------------------------------------------------------------- Name : univCalcShipChecksum Description : returns a checksum representing the status of the ships in the universe Inputs : Outputs : numShipsInChecksum Return : checksum of mission sphere at this point in time ----------------------------------------------------------------------------*/ udword univCalcShipChecksum(void) { udword shipcheck=0; Node *search; Bullet *bullet; /* Ship *ship; udword i; search = universe.ShipList.head; while (search!=NULL) { ship = listGetStructOfNode(search); if(ship->flags & SOF_Dead) { //don't consider dead ships...they're bad news //they still get written to the syncdump.txt, but //they won't generate a sync error. search = search->next; continue; } shipcheck += TreatAsUdword(ship->collOptimizeDist); shipcheck += TreatAsUdword(ship->posinfo.position.x); shipcheck += TreatAsUdword(ship->posinfo.position.y); shipcheck += TreatAsUdword(ship->posinfo.position.z); shipcheck += TreatAsUdword(ship->posinfo.velocity.x); shipcheck += TreatAsUdword(ship->posinfo.velocity.y); shipcheck += TreatAsUdword(ship->posinfo.velocity.z); shipcheck += TreatAsUdword(ship->posinfo.force.x); shipcheck += TreatAsUdword(ship->posinfo.force.y); shipcheck += TreatAsUdword(ship->posinfo.force.z); shipcheck += (udword)ship->posinfo.isMoving; shipcheck += (udword)ship->posinfo.haventCalculatedDist; shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m11); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m21); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m31); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m12); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m22); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m32); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m13); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m23); shipcheck += TreatAsUdword(ship->rotinfo.coordsys.m33); shipcheck += TreatAsUdword(ship->rotinfo.rotspeed.x); shipcheck += TreatAsUdword(ship->rotinfo.rotspeed.y); shipcheck += TreatAsUdword(ship->rotinfo.rotspeed.z); shipcheck += TreatAsUdword(ship->rotinfo.torque.x); shipcheck += TreatAsUdword(ship->rotinfo.torque.y); shipcheck += TreatAsUdword(ship->rotinfo.torque.z); shipcheck += TreatAsUdword(ship->collInfo.collOffset.x); shipcheck += TreatAsUdword(ship->collInfo.collOffset.y); shipcheck += TreatAsUdword(ship->collInfo.collOffset.z); shipcheck += TreatAsUdword(ship->collInfo.collPosition.x); shipcheck += TreatAsUdword(ship->collInfo.collPosition.y); shipcheck += TreatAsUdword(ship->collInfo.collPosition.z); for (i=0;i<8;i++) { shipcheck += TreatAsUdword(ship->collInfo.rectpos[i].x); shipcheck += TreatAsUdword(ship->collInfo.rectpos[i].y); shipcheck += TreatAsUdword(ship->collInfo.rectpos[i].z); } shipcheck += TreatAsUdword(ship->health); shipcheck += TreatAsUdword(ship->lasttimecollided); for (i=0;inonstatvars.thruststrength[i]); for (i=0;inonstatvars.rotstrength[i]); for (i=0;inonstatvars.turnspeed[i]); shipcheck += (udword)ship->aistate; shipcheck += ship->specialFlags&(~(SPECIAL_Resourcing|SPECIAL_Finished_Resource)); shipcheck += ship->specialFlags2; for (i=0;isalvageNumTagged[i]; shipcheck += TreatAsUdword(ship->moveTo.x); shipcheck += TreatAsUdword(ship->moveTo.y); shipcheck += TreatAsUdword(ship->moveTo.z); shipcheck += TreatAsUdword(ship->moveFrom.x); shipcheck += TreatAsUdword(ship->moveFrom.y); shipcheck += TreatAsUdword(ship->moveFrom.z); shipcheck += TreatAsUdword(ship->formationOffset.x); shipcheck += TreatAsUdword(ship->formationOffset.y); shipcheck += TreatAsUdword(ship->formationOffset.z); shipcheck += TreatAsUdword(ship->formationHeading.x); shipcheck += TreatAsUdword(ship->formationHeading.y); shipcheck += TreatAsUdword(ship->formationHeading.z); shipcheck += TreatAsUdword(ship->engineOffset.x); shipcheck += TreatAsUdword(ship->engineOffset.y); shipcheck += TreatAsUdword(ship->engineOffset.z); shipcheck += TreatAsUdword(ship->enginePosition.x); shipcheck += TreatAsUdword(ship->enginePosition.y); shipcheck += TreatAsUdword(ship->enginePosition.z); shipcheck += TreatAsUdword(ship->rowOriginalPoint.x); shipcheck += TreatAsUdword(ship->rowOriginalPoint.y); shipcheck += TreatAsUdword(ship->rowOriginalPoint.z); shipcheck += TreatAsUdword(ship->rowOutOfWayPoint.x); shipcheck += TreatAsUdword(ship->rowOutOfWayPoint.y); shipcheck += TreatAsUdword(ship->rowOutOfWayPoint.z); shipcheck += (udword)ship->shiptype; shipcheck += (udword)ship->shiprace; shipcheck += (udword)ship->tacticstype; shipcheck += (udword)ship->isDodging; shipcheck += TreatAsUdword(ship->DodgeTime); shipcheck += (udword)ship->DodgeDir; shipcheck += (udword)ship->tacticsFormationVar1; shipcheck += ship->DodgeFlag; shipcheck += (udword)ship->tactics_ordertype; shipcheck += ship->tacticsTalk; shipcheck += (udword)ship->tacticsNeedToFlightMan; shipcheck += TreatAsUdword(ship->timeCreated); shipcheck += (udword)ship->kamikazeState; shipcheck += TreatAsUdword(ship->kamikazeVector.x); shipcheck += TreatAsUdword(ship->kamikazeVector.y); shipcheck += TreatAsUdword(ship->kamikazeVector.z); shipcheck += TreatAsUdword(ship->speedBurstTime); shipcheck += TreatAsUdword(ship->singlePlayerSpeedLimiter); shipcheck += TreatAsUdword(ship->deathtime); shipcheck += ship->madAnimationFlags; shipcheck += ship->madGunStatus; shipcheck += ship->madWingStatus; shipcheck += ship->madDoorStatus; shipcheck += ship->madSpecialStatus; shipcheck += ship->cuedAnimationIndex; shipcheck += ship->cuedAnimationType; shipcheck += ship->nextAnim; shipcheck += (udword)ship->forceCancelDock; shipcheck += (udword)ship->resources; shipcheck += TreatAsUdword(ship->fuel); shipcheck += TreatAsUdword(ship->lastTimeRepaired); shipcheck += (udword)ship->shipidle; shipcheck += (udword)ship->autostabilizeship; shipcheck += (udword)ship->autostabilizestate; shipcheck += (udword)ship->autostabilizewait; shipcheck += (udword)ship->aistateattack; shipcheck += (udword)ship->flightmanState1; shipcheck += (udword)ship->flightmanState2; shipcheck += (udword)ship->rcstate1; shipcheck += (udword)ship->rcstate2; shipcheck += (udword)ship->aistatecommand; shipcheck += (udword)ship->dontrotateever; shipcheck += (udword)ship->dontapplyforceever; shipcheck += (udword)ship->shipisattacking; shipcheck += (udword)ship->colorScheme; shipcheck += (udword)ship->recentlyAttacked; shipcheck += (udword)ship->recentlyFiredUpon; shipcheck += (udword)ship->rowState; shipcheck += TreatAsUdword(ship->aidescend); shipcheck += ship->flightman; shipcheck += ship->attackvars.flightmansLeft; shipcheck += ship->attackvars.attacksituation; shipcheck += ship->attackvars.attackevents; shipcheck += TreatAsUdword(ship->dockvars.destination.x); shipcheck += TreatAsUdword(ship->dockvars.destination.y); shipcheck += TreatAsUdword(ship->dockvars.destination.z); shipcheck += (udword)ship->dockvars.dockstate; shipcheck += (udword)ship->dockvars.dockstate2; shipcheck += (udword)ship->dockvars.dockstate3; shipcheck += (udword)ship->dockvars.reserveddocking; shipcheck += (udword)ship->visibleToWho; shipcheck += (udword)ship->visibleToWhoPreviousFrame; shipcheck += (udword)ship->howDidIDie; shipcheck += (udword)ship->whoKilledMe; shipcheck += (udword)ship->nDamageLights; shipcheck += TreatAsUdword(ship->holdingPatternPos.x); shipcheck += TreatAsUdword(ship->holdingPatternPos.y); shipcheck += TreatAsUdword(ship->holdingPatternPos.z); shipcheck += TreatAsUdword(ship->damageModifier); shipcheck += ship->tractorbeaminfo; shipcheck += ship->tractorvector; shipcheck += (udword)ship->tractorstate; for (i=0;i<4;i++) shipcheck += (udword)ship->tractorPosinfo[i]; shipcheck += (udword)ship->needNewLeader; shipcheck += (udword)ship->newDockIndexTransfer; shipcheck += TreatAsUdword(ship->shipDeCloakTime); shipcheck += (udword)ship->shipID.shipNumber; search = search->next; } */ search = universe.BulletList.head; while (search!=NULL) { bullet = (Bullet *)listGetStructOfNode(search); if(bullet->flags & SOF_Dead) { search = search->next; continue; } //shipcheck += TreatAsUdword(bullet->posinfo.position.x); //shipcheck += TreatAsUdword(bullet->posinfo.position.y); //shipcheck += TreatAsUdword(bullet->posinfo.position.z); //shipcheck += TreatAsUdword(bullet->posinfo.velocity.x); //shipcheck += TreatAsUdword(bullet->posinfo.velocity.y); //shipcheck += TreatAsUdword(bullet->posinfo.velocity.z); //shipcheck += (udword)bullet->posinfo.isMoving; //shipcheck += (udword)bullet->posinfo.haventCalculatedDist; //shipcheck += TreatAsUdword(bullet->lengthmag); //shipcheck += TreatAsUdword(bullet->traveldist); //shipcheck += TreatAsUdword(bullet->timelived); //shipcheck += TreatAsUdword(bullet->totallifetime); //shipcheck += TreatAsUdword(bullet->damage); //shipcheck += TreatAsUdword(bullet->damageFull); //shipcheck += (udword)bullet->SpecialEffectFlag; //shipcheck += TreatAsUdword(bullet->DFGFieldEntryTime); //shipcheck += TreatAsUdword(bullet->BulletSpeed); if(bullet->owner != NULL) { shipcheck += 128; } //if(bullet->gunowner != NULL) //{ // shipcheck += 32; //} search = search->next; } return (shipcheck); } /*----------------------------------------------------------------------------- Name : univMinorSetupShipForControl Description : A version of univSetupShipForControl specific to ships with the SOF_DontApplyPhysics bit set (such as NIS ships). Inputs : Ship - ship to "set up" Outputs : Return : ----------------------------------------------------------------------------*/ void univMinorSetupShipForControl(Ship *ship) { sdword i; ship->gettingrocked = NULL; // always make sure this gets set to NULL - don't want any bad references dmgShipThink(ship); //update engine trails for (i = 0; i < MAX_NUM_TRAILS; i++) { if (ship->trail[i] != NULL) { trailUpdate(ship->trail[i], &ship->enginePosition); } } } void univSetupShipForControl(Ship *ship) { ShipStaticInfo *shipstatic; sdword i,t; ship->gettingrocked = NULL; // always make sure this gets set to NULL - don't want any bad references if (ship->flags & SOF_Dead) { return; } shipstatic = (ShipStaticInfo *)ship->staticinfo; //vecZeroVector(ship->posinfo.force); //vecZeroVector(ship->rotinfo.torque); if((universe.univUpdateCounter & TW_ProximitySensorSearchRate) == TW_ProximitySensorSearchFrame) { //this univupdate, proximity sensors will search again for //ships nearby, lets make all ships 'invisible' and then let //the proximity sensor make those ships visible that should be //do a check like so here: Check if ship has just become //invisible to other ships, if so, remove it from being targetted //by those people. if(ship->flags & SOF_Cloaked) { for(t = 0; t <= universe.numPlayers; t++) { //looking for state such that the player COULD see the ship in the //previous frame(2 ago), but now cannot (1 ago) if(bitTest(ship->visibleToWhoPreviousFrame,0x01<visibleToWho,0x01<visibleToWhoPreviousFrame = ship->visibleToWho; ship->visibleToWho = 0; } ship->shipidle = TRUE; ship->autostabilizeship = TRUE; //clear this flag incase it is a target of a salcapcorvette bitClear(ship->specialFlags2,SPECIAL_2_SalvageTargetAlreadyDriven); // memory of attackers fades away... if (ship->recentlyAttacked) { if (!ship->recentAttacker) { ship->recentlyAttacked = 0; } else { --ship->recentlyAttacked; if (!ship->recentlyAttacked) ship->recentAttacker = NULL; } } if (ship->recentlyFiredUpon) { if (ship->firingAtUs == NULL) { ship->recentlyFiredUpon = 0; } else { ship->recentlyFiredUpon--; if (ship->recentlyFiredUpon == 0) { ship->firingAtUs = NULL; } } } bitClear(ship->flags, SOF_DontDrawTrails); // always clear this bit, SOF_Disabled ships never have trails drawn dmgShipThink(ship); //update engine trails for (i = 0; i < MAX_NUM_TRAILS; i++) { if (ship->trail[i] != NULL) { trailUpdate(ship->trail[i], &ship->enginePosition); } } if ((ship->flags & SOF_Disabled) == 0) { if (shipstatic->repairDamage != 0) { if (ship->health < shipstatic->maxhealth) { real32 repairTime; real32 repairDamage; if (ship->recentlyAttacked | ship->shipisattacking | bitTest(ship->specialFlags, SPECIAL_ForcedAttackStatus)) { repairTime = shipstatic->repairCombatTime; repairDamage = shipstatic->repairCombatDamage; } else { repairTime = shipstatic->repairTime; repairDamage = shipstatic->repairDamage; } if ((universe.totaltimeelapsed - ship->lastTimeRepaired) >= repairTime) { ship->lastTimeRepaired = universe.totaltimeelapsed; ship->health += repairDamage; if (ship->health > shipstatic->maxhealth) { ship->health = shipstatic->maxhealth; } } } else { ship->lastTimeRepaired = universe.totaltimeelapsed; } } } if (shipstatic->custshipheader.CustShipHousekeep != NULL) // put in a list, and execute all housekeeps later { // to make sure all of the ships have been properly setup growSelectAddShip(&universe.HousekeepShipList,ship); } } /*----------------------------------------------------------------------------- Name : univSetupShipsForControl Description : initializes the shipidle and gettingrocked flags for this pass Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univSetupShipsForControl() { Node *objnode = universe.ShipList.head; Ship *ship; SelectCommand *housekeepSelection; sdword i; sdword numShips; growSelectReset(&universe.HousekeepShipList); while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); univSetupShipForControl(ship); objnode = objnode->next; } housekeepSelection = universe.HousekeepShipList.selection; numShips = housekeepSelection->numShips; for (i=0;iShipPtr[i]; if (ship) { ship->staticinfo->custshipheader.CustShipHousekeep(ship); } } growSelectReset(&universe.HousekeepShipList); // don't need to keep track of them anymore } //same as above but for derelicts void univSetupDerelictsForControl() { } SelectCommand *getEnemiesWithinProximity(Ship *thisship,real32 retaliateZone) { Player *playerowner = thisship->playerowner; blob *thisblob = thisship->collMyBlob; sdword objindex = 0; SelectCommand *blobships = thisblob->blobShips; udword num = blobships->numShips; SelectCommand *select; udword shipIndex = 0; Ship *ship; vector diff; real32 timesincecloak; real32 negRetaliateZone = -retaliateZone; if ((thisship->shiptype == FloatingCity) || (thisship->shiptype == TargetDrone) || (bitTest(thisship->specialFlags, SPECIAL_FriendlyStatus))) { return NULL; // traders get special status, not considered enemy to anyone } select = memAlloc(sizeofSelectCommand(num),"se(selectenemies)",Pyrophoric); // worst case scenario while (objindex < num) { ship = blobships->ShipPtr[objindex]; if (allianceIsShipAlly(ship,playerowner)) { goto nextnode; // must be enemy ship } if ((ship->shiptype == FloatingCity) || (ship->shiptype == TargetDrone) || (bitTest(ship->specialFlags, SPECIAL_FriendlyStatus|SPECIAL_Hyperspacing))) { goto nextnode; // traders get special status, not considered enemy to anyone } if (bitTest(ship->flags,SOF_Cloaked)) { if(!proximityCanPlayerSeeShip(thisship->playerowner,ship)) { goto nextnode; // if ship is cloaked, and can't be seen by that ships player.. } } timesincecloak = universe.totaltimeelapsed - ship->shipDeCloakTime; if(timesincecloak <= TW_SECOND_SEE_TIME) { if(timesincecloak < TW_NOONE_SEE_TIME) { //ship being considered has decloaked so recently that 'thisship' couldn't //possibly see it yet goto nextnode; } else if(timesincecloak < TW_FIRST_SEE_TIME) { //ship has decloaked so long ago that only a small percentage could see it now if(randombetween(0,100) > TW_FIRST_SEE_PERCENTAGE) { goto nextnode; } } else if(timesincecloak < TW_SECOND_SEE_TIME) { //ship has decloaked a fair ammount of time ago, and some more ships are likely to see it now if(randombetween(0,100) > TW_SECOND_SEE_PERCENTAGE) { goto nextnode; } } } if(bitTest(ship->flags,SOF_Disabled|SOF_Hide)) { //don't attack disabled ships. goto nextnode; } vecSub(diff,ship->posinfo.position,thisship->posinfo.position); if (isBetweenExclusive(diff.x,negRetaliateZone,retaliateZone) && isBetweenExclusive(diff.y,negRetaliateZone,retaliateZone) && isBetweenExclusive(diff.z,negRetaliateZone,retaliateZone) ) { select->ShipPtr[shipIndex++] = ship; } nextnode: objindex++; } if (shipIndex == 0) { memFree(select); return NULL; } select->numShips = shipIndex; return select; } void univCheckShipState(Ship *ship) { SelectCommand selectone; AttackCommand *attack; CommandToDo *command; if (ship->deathtime != 0.0f) { if ((ship->flags & SOF_Dead) == 0) { if (universe.totaltimeelapsed >= ship->deathtime) { // Self-destruct ship if(!(ship->flags & SOF_Disabled)) { //if ship is disabled..it didn't receive the //self destruct order ship->health = 0; AddShipToDeleteShipList(ship,-1); } else if(ship->shiptype == GravWellGenerator) { if( ((GravWellGeneratorSpec *)ship->ShipSpecifics)->GravDerelict) { //if ship is disabled..it didn't receive the //self destruct order ship->health = 0; AddShipToDeleteShipList(ship,-1); } } } } } if (ship->flags & SOF_Dead) { return; } if (ship->specialFlags & SPECIAL_rowGettingOutOfWay) { rowFlyShipOutOfWay(ship); //ship->autostabilizeship = FALSE; } if (ship->autostabilizeship && !bitTest(ship->flags, SOF_Disabled|SOF_Hide|SOF_Hyperspace|SOF_NISShip|SOF_Crazy) && !bitTest(ship->attributes,ATTRIBUTES_DontApplyFriction) && (ship->shiptype != Drone)) { bool isShipSteady; if (ship->shiptype == MiningBase) isShipSteady = aitrackIsShipSteadyAnywhere(ship); // don't care about orientation up else isShipSteady = aitrackIsShipSteady(ship); if (isShipSteady) { aitrackForceSteadyShip(ship); ship->autostabilizestate = 0; } else { switch (ship->autostabilizestate) { case 0: // we just detected ship isn't stable ship->autostabilizewait = AUTOSTABILIZE_FRAME_KICK_IN; ship->autostabilizestate = 1; case 1: if (ship->autostabilizewait > 0) { ship->autostabilizewait--; break; } else { ship->autostabilizestate = 2; } case 2: if (ship->shiptype == MiningBase) aitrackSteadyShipAnywhere(ship); else aitrackSteadyShip(ship); break; default: dbgAssert(FALSE); break; } } } #ifndef HW_Release if (!shipsRetaliate) { return; } #endif if (nisIsRunning) { return; // don't autoretaliate if NIS running } if(!bitTest(ship->flags, SOF_Disabled)) { tacticsUpdate(ship); //perform tactical processes for ship if ((universe.univUpdateCounter & CHECK_SHIP_OUTOFWORLD_RATE) == (ship->shipID.shipNumber & CHECK_SHIP_OUTOFWORLD_RATE)) { if (!(singlePlayerGame && (ship->playerowner->playerIndex == 1))) // don't apply to KAS ships in single player game { if (univObjectOutsideWorld((SpaceObj *)ship)) { vector newdest; SelectCommand *selectmove = getShipAndItsFormation(&universe.mainCommandLayer,ship); CommandToDo *commandofship = getShipAndItsCommand(&universe.mainCommandLayer,ship); dbgAssert(selectmove); if(commandofship != NULL) { if(commandofship->ordertype.order == COMMAND_MP_HYPERSPACEING) { //if ship is hyperspaceing, don't check if its in the world...once it is done, then we can move it! goto DONT_MOVE_ME_YET; } if(commandofship->ordertype.order == COMMAND_ATTACK) { // let's see if we can find a target that is inside the world and attack that: SpaceObjRotImpTarg *currenttarget = ship->attackvars.attacktarget; SpaceObjRotImpTarg *testtarget; AttackCommand *attack; sdword i; if (currenttarget) { if (!univObjectOutsideWorld((SpaceObj *)currenttarget)) { // our target is still in world, so we don't have to move into world goto DONT_MOVE_ME_YET; } } attack = commandofship->attack; for (i=0;inumTargets;i++) { testtarget = attack->TargetPtr[i]; if (testtarget == currenttarget) { continue; } if (!univObjectOutsideWorld((SpaceObj *)testtarget)) { ship->attackvars.attacktarget = testtarget; goto DONT_MOVE_ME_YET; } } } } newdest = ship->posinfo.position; vecMultiplyByScalar(newdest,FLY_INTO_WORLD_PERCENT_DIST); clMove(&universe.mainCommandLayer,selectmove,ship->posinfo.position,newdest); memFree(selectmove); return; } } } } DONT_MOVE_ME_YET: if (ship->shipidle) { if(!bitTest(ship->flags, SOF_Disabled)) { if (ship->shiptype == ResourceCollector) { if ((ship->gettingrocked != NULL) && (ship->tacticstype != Aggressive)) { // tell ResourceCollector to run away selectone.numShips = 1; selectone.ShipPtr[0] = ship; clDock(&universe.mainCommandLayer,&selectone,DOCK_TO_DEPOSIT_RESOURCES | DOCK_FOR_REFUELREPAIR,NULL); } } else { if ((ship->gettingrocked != NULL) && (!allianceIsShipAlly(ship,ship->gettingrocked->playerowner))) { // attack ship->gettingrocked and its formation attack = (AttackCommand *)getShipAndItsFormation(&universe.mainCommandLayer,ship->gettingrocked); dbgAssert(attack->numTargets > 0); command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if (command != NULL) { if (AreAnyShipsPassiveAttackCapable(command->selection)) { #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsDelegateAttackCommand(ship,command,attack,TRUE,FALSE); } #ifdef DEBUG_TACTICS else { if (canChangeOrderToPassiveAttack(command,attack)) { ChangeOrderToPassiveAttack(command,attack); } } #endif } } else { if (isShipPassiveAttackCapable(ship)) { selectone.numShips = 1; selectone.ShipPtr[0] = ship; #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsDelegateSingleAttack(ship,&universe.mainCommandLayer,&selectone,attack,TRUE); } #ifdef DEBUG_TACTICS else { clPassiveAttack(&universe.mainCommandLayer,&selectone,attack); } #endif } } memFree(attack); } else if (((universe.univUpdateCounter & CHECK_PASSIVE_ATTACK_RATE) == (ship->shipID.shipNumber & CHECK_PASSIVE_ATTACK_RATE)) && (ship->staticinfo->passiveRetaliateZone != 0.0f)) { if(bitTest(ship->flags,SOF_Cloaked) || bitTest(ship->flags,SOF_Cloaking)) { //if a ship is cloaked or it is cloaking, don't passive attack because we //want to act stealthy :) return; } command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if (command != NULL) { if ((canChangeOrderToPassiveAttack(command,NULL)) && (AreAnyShipsPassiveAttackCapable(command->selection))) { attack = (AttackCommand *)getEnemiesWithinProximity(ship,ship->staticinfo->passiveRetaliateZone); if (attack != NULL) { #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsDelegateAttackCommand(ship,command,attack,FALSE,TRUE); } #ifdef DEBUG_TACTICS else { ChangeOrderToPassiveAttack(command,attack); } #endif memFree(attack); } } } else { if (isShipPassiveAttackCapable(ship)) { selectone.numShips = 1; selectone.ShipPtr[0] = ship; attack = (AttackCommand *)getEnemiesWithinProximity(ship,ship->staticinfo->passiveRetaliateZone); if (attack != NULL) { //fix later by removing this and all other tacticsOn checks #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsDelegateSingleAttack(ship,&universe.mainCommandLayer,&selectone,attack,FALSE); } #ifdef DEBUG_TACTICS else { clPassiveAttack(&universe.mainCommandLayer,&selectone,attack); } #endif memFree(attack); } } } } } } } else { //ship isn't idle //so lets do a check every so often to see if //ship should attempt passive attacking if(!bitTest(ship->flags, SOF_Disabled)) { if (((universe.univUpdateCounter & CHECK_PASSIVE_ATTACK_WHILEMOVING_RATE) == (ship->shipID.shipNumber & CHECK_PASSIVE_ATTACK_WHILEMOVING_RATE)) && (ship->staticinfo->passiveRetaliateZone != 0.0f) && (isCapitalShip(ship))) { command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if (command != NULL) { if(command->ordertype.order == COMMAND_MOVE ||command->ordertype.order == COMMAND_DOCK) { if(bitTest(ship->flags,SOF_Cloaked) || bitTest(ship->flags,SOF_Cloaking)) { //if a ship is cloaked or it is cloaking, don't passive attack because we //want to act stealthy :) return; } if ((canChangeOrderToPassiveAttack(command,NULL)) && (AreAnyShipsPassiveAttackCapable(command->selection))) { attack = (AttackCommand *)getEnemiesWithinProximity(ship,ship->staticinfo->passiveRetaliateZone); if (attack != NULL) { #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsDelegateAttackCommand(ship,command,attack,FALSE,TRUE); } #ifdef DEBUG_TACTICS else { ChangeOrderToPassiveAttack(command,attack); } #endif memFree(attack); } } } } } } } } #if 0 // no longer needed - univCheckShipState called in univUpdateAllObjPosVel /*----------------------------------------------------------------------------- Name : univCheckShipStates Description : checks if shipidle and gettingrocked, and if so, tells ship to retaliate Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univCheckShipStates() { Node *objnode = universe.ShipList.head; Ship *ship; // bool attackpassive; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); univCheckShipState(ship); nextnode: objnode = objnode->next; } } #endif /*----------------------------------------------------------------------------- Name : univupdateReset Description : resets univupdate Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univupdateReset() { universe.phystimeelapsed = UNIVERSE_UPDATE_PERIOD; universe.totaltimeelapsed = 0.0f; universe.radius = 20000.0f; universe.univUpdateCounter = 0; universe.resourceNumber = 0; universe.derelictNumber = 0; universe.shipNumber = 0; universe.missileNumber = 0; univResetFastNetworkIDLookups(); universe.quittime = 0.0f; universe.wintime = 0.0f; universe.aiplayerProcessing = FALSE; universe.CapitalShipCaptured = 0; universe.PlayerWhoWon = 0; universe.lasttimeadded = 0; universe.DerelictTech=FALSE; universe.crateTimer = 0.0f; universe.numCratesInWorld = 0; universe.bounties = FALSE; universe.collUpdateAllBlobs = FALSE; listInit(&universe.RenderList); listInit(&universe.MinorRenderList); universe.dontUpdateRenderList = FALSE; listInit(&universe.SpaceObjList); listInit(&universe.MinorSpaceObjList); listInit(&universe.effectList); listInit(&universe.ShipList); listInit(&universe.BulletList); listInit(&universe.ResourceList); listInit(&universe.DerelictList); listInit(&universe.ImpactableList); listInit(&universe.MissileList); listInit(&universe.MineFormationList); listInit(&universe.DeleteMissileList); listInit(&universe.DeleteResourceList); listInit(&universe.DeleteDerelictList); listInit(&universe.DeleteShipList); listInit(&universe.collBlobList); // tactics stuff listInit(&universe.RetreatList); listInit(&universe.AttackMemory); // regrow resources stuff listInit(&universe.ResourceVolumeList); growSelectClose(&universe.HousekeepShipList); growSelectInit(&universe.HousekeepShipList); growSelectClose(&ClampedShipList); growSelectInit(&ClampedShipList); } /*----------------------------------------------------------------------------- Name : univupdateInit Description : intializes univupdate Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univupdateInit() { univInitFastNetworkIDLookups(); growSelectInit(&universe.HousekeepShipList); growSelectInit(&ClampedShipList); universe.bountySize = 0; univupdateReset(); universe.star3dinfo = star3dInit(NUM_STARS, universe.radius - 1.0f, universe.radius); scriptSet(NULL,"Galaxy.script",GalaxyTweaks); universe.backgroundColor = backgroundColor; } /*----------------------------------------------------------------------------- Name : univFreeEffectContents Description : frees the effect (for exiting the program) Inputs : effect Outputs : Return : ----------------------------------------------------------------------------*/ void univFreeEffectContents(Effect *effect) { etgEffectDelete(effect); } /*----------------------------------------------------------------------------- Name : univFreeDerelictContents Description : Frees the contents of a derelict without freeing the derelict itself. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univFreeDerelictContents(Derelict *derelict) { if (derelict->navLightInfo) { memFree(derelict->navLightInfo); derelict->navLightInfo = NULL; } if(derelict->salvageInfo != NULL) { memFree(derelict->salvageInfo); derelict->salvageInfo = NULL; } if(derelict->clampInfo != NULL) { memFree(derelict->clampInfo); derelict->clampInfo = NULL; } } /*----------------------------------------------------------------------------- Name : univFreeAsteroidContents Description : frees the asteroid contents Inputs : asteroid Outputs : Return : ----------------------------------------------------------------------------*/ void univFreeAsteroidContents(Asteroid *asteroid) { if (asteroid->flags & SOF_StaticInfoIsDynamic) { memFree(asteroid->staticinfo); } } void univFreeNebulaContents(Nebula *nebula) { if (nebula->stub != NULL) { nebDeleteChunk((nebChunk*)nebula->stub); nebula->stub = NULL; } if (nebula->flags & SOF_StaticInfoIsDynamic) { memFree(nebula->staticinfo); } } void univFreeGasCloudContents(GasCloud *gascloud) { if (gascloud->flags & SOF_StaticInfoIsDynamic) { memFree(gascloud->staticinfo); } } void univFreeDustCloudContents(DustCloud *dustcloud) { if (dustcloud->stub != NULL) { cloudDeleteSystem((cloudSystem*)dustcloud->stub); dustcloud->stub = NULL; } if (dustcloud->flags & SOF_StaticInfoIsDynamic) { memFree(dustcloud->staticinfo); } } void univFreeResourceContents(Resource *resource) { switch (resource->objtype) { case OBJ_AsteroidType: univFreeAsteroidContents((Asteroid *)resource); break; case OBJ_NebulaType: univFreeNebulaContents((Nebula *)resource); break; case OBJ_GasType: univFreeGasCloudContents((GasCloud *)resource); break; case OBJ_DustType: univFreeDustCloudContents((DustCloud *)resource); break; } } /*----------------------------------------------------------------------------- Name : univFreeBulletContents Description : frees the bullet (for exiting program) Inputs : bullet Outputs : Return : ----------------------------------------------------------------------------*/ void univFreeBulletContents(Bullet *bullet) { ; } /*----------------------------------------------------------------------------- Name : univFreeMissileContents Description : frees the missile (for exiting program) Inputs : missile Outputs : Return : ----------------------------------------------------------------------------*/ void univFreeMissileContents(Missile *missile) { if(missile->formationinfo != NULL) { listRemoveNode(&missile->formationLink); //take out of formation info list } if(missile->trail != NULL) { mistrailDelete(missile->trail); missile->trail = NULL; } if(missile->clampInfo != NULL) { memFree(missile->clampInfo); missile->clampInfo = NULL; } } /*----------------------------------------------------------------------------- Name : univRemoveShipReferences Description : Calls functions to remove references to ships. This will always be called no matter how a ship dies. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveShipReferences(Ship *ship) { ship->flags |= SOF_Dead; bitClear(ship->flags,SOF_Selectable); //hopefully, at this point, the ships howDidIDied variable is set and valid! gameStatsShipDied(ship); if (((ShipStaticInfo *)(ship->staticinfo))->canBuildShips) { cmRemoveFactory(ship); } if (ship->rceffect != NULL) { TurnHarvestEffectOff(ship); } if(!(gameIsEnding && ship->shiptype == ResearchShip)) { if(((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipDied != NULL) { ((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipDied(ship); } } tacticsShipDied(ship); if (ship->shipsInsideMe) { DeleteShipsInsideMe(ship); } if (bitTest(ship->flags,SOF_Slaveable)) { dockDealWithDeadSlaveable(ship); //this probably won't be called ever } univRemoveAllTargetReferencesFromBullets((SpaceObjRotImpTarg *)ship); univRemoveAllTargetReferencesFromMissiles((SpaceObjRotImpTarg *)ship); univRemoveAllShipReferencesFromSpecialShips(ship); //if sensors manager is running, delete it from any sm spheres if (smSensorsActive) { smObjectDied(ship); } bobObjectDied((SpaceObj *)ship,&universe.collBlobList); pingObjectDied((SpaceObj *)ship); // Update Ship Command Layer to reflect ship death clShipDied(&universe.mainCommandLayer,ship); // Update computer player structures to reflect ship death aiplayerShipDied(ship); // Tell single player game module ship died singlePlayerShipDied(ship); if(tutorial) tutPlayerShipDied(ship); // Tell the sound layer that the ship died // soundEventShipDied(ship); // 06/23/99 SA - I changed the above line because I think it'll only be called when // a ship is retired, this was causing it to say "ship lost" when it was retired. // the soundEventShipRemove() doesn't say the ship died speech event soundEventShipRemove(ship); IDToPtrTableObjDied(&ShipIDToPtr,ship->shipID.shipNumber); growSelectRemoveShipBySettingNULL(&universe.HousekeepShipList,ship); // Remove ships that are destroyed from any hot key groups univRemoveShipFromHotkeyGroup(ship,FALSE); mrShipDied(ship); } /*----------------------------------------------------------------------------- Name : univFreeShipContents Description : frees the ship (for exiting program) Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void univFreeShipContents(Ship *ship) { udword i; univRemoveShipReferences(ship); if (ship->attackvars.multipleAttackTargets != NULL) { memFree(ship->attackvars.multipleAttackTargets); ship->attackvars.multipleAttackTargets = NULL; } if (ship->flightman != FLIGHTMAN_NULL) { flightmanClose(ship); } if (ship->shipSinglePlayerGameInfo) { memFree(ship->shipSinglePlayerGameInfo); ship->shipSinglePlayerGameInfo = NULL; } // Remove repair droids... /* if(ship->pRepairDroids) { memFree(ship->pRepairDroids); ship->pRepairDroids = NULL; }*/ if (ship->navLightInfo) { memFree(ship->navLightInfo); ship->navLightInfo = NULL; } if(ship->salvageInfo != NULL) { memFree(ship->salvageInfo); ship->salvageInfo = NULL; } if(ship->clampInfo != NULL) { memFree(ship->clampInfo); ship->clampInfo = NULL; } //remove lightnings for (i = 0; i < 2; i++) { if (ship->lightning[i] != NULL) { cloudKillLightning((lightning*)ship->lightning[i]); ship->lightning[i] = NULL; } } dmgStopEffect(ship, DMG_All); dmgClearLights(ship); for (i = 0; i < MAX_NUM_TRAILS; i++) { if (ship->trail[i] != NULL) { trailDelete(ship->trail[i]); ship->trail[i] = NULL; } } if (ship->gunInfo != NULL) { memFree(ship->gunInfo); ship->gunInfo = NULL; } if (ship->dockInfo != NULL) { memFree(ship->dockInfo); ship->dockInfo = NULL; } if(ship->slaveinfo != NULL) { dockDealWithDeadSlaveable(ship); } if (ship->shipsInsideMe) { FreeShipsInsideMe(ship); } if (((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipClose) { ((ShipStaticInfo *)ship->staticinfo)->custshipheader.CustShipClose(ship); } univMeshBindingsDelete(ship); if (ship->flags & SOF_StaticInfoIsDynamic) { memFree(ship->staticinfo); } } /*----------------------------------------------------------------------------- Name : univupdateCloseAllObjectsAndMissionSpheres Description : closes all objects in the universe and all mission spheres in the universe Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univupdateCloseAllObjectsAndMissionSpheres() { Node *objnode = universe.SpaceObjList.head; SpaceObj *obj; sdword i; univCloseFastNetworkIDLookups(); growSelectClose(&universe.HousekeepShipList); growSelectClose(&ClampedShipList); gameIsEnding = TRUE; //set global flag to indicate to other functions that the game is ending singlePlayerGameCleanup(); while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); switch (obj->objtype) { case OBJ_ShipType: ((Ship *)obj)->howDidIDie = DEATH_BY_GAME_END; univFreeShipContents((Ship *)obj); break; case OBJ_BulletType: univFreeBulletContents((Bullet *)obj); break; case OBJ_AsteroidType: univFreeAsteroidContents((Asteroid *)obj); break; case OBJ_NebulaType: univFreeNebulaContents((Nebula *)obj); break; case OBJ_GasType: univFreeGasCloudContents((GasCloud *)obj); break; case OBJ_DustType: univFreeDustCloudContents((DustCloud *)obj); break; case OBJ_DerelictType: univFreeDerelictContents((Derelict *)obj); break; case OBJ_EffectType: univFreeEffectContents((Effect *)obj); break; case OBJ_MissileType: univFreeMissileContents((Missile *)obj); break; default: dbgAssert(FALSE); break; } univRemoveObjFromRenderList(obj); objnode = objnode->next; } objnode = universe.MinorSpaceObjList.head; while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); switch (obj->objtype) { case OBJ_AsteroidType: univFreeAsteroidContents((Asteroid *)obj); break; default: dbgAssert(FALSE); break; } univRemoveObjFromMinorRenderList(obj); objnode = objnode->next; } listDeleteAll(&universe.SpaceObjList); listDeleteAll(&universe.MinorSpaceObjList); listInit(&universe.effectList); listInit(&universe.ShipList); listInit(&universe.BulletList); listInit(&universe.ResourceList); listInit(&universe.DerelictList); listInit(&universe.ImpactableList); listInit(&universe.MissileList); bobListDelete(&universe.collBlobList); listDeleteAll(&universe.MineFormationList); listDeleteAll(&universe.ResourceVolumeList); // deselect everything as well selSelected.numShips = 0; selSelecting.numTargets = 0; if (!singlePlayerGameLoadNewLevelFlag) { for (i=0;istaticinfo; sdword sizeofSalvageInfo; switch(object->objtype) { case OBJ_ShipType: if(((Ship *)object)->staticinfo->salvageStaticInfo == NULL) { object->salvageInfo = NULL; return; } break; case OBJ_DerelictType: if(((Derelict *)object)->staticinfo->salvageStaticInfo == NULL) { object->salvageInfo = NULL; return; } break; default: return; } { SalvagePoint *cursalvagepoint; SalvageStaticPoint *cursalvagestaticpoint; sdword i; sizeofSalvageInfo = sizeofSalvageInfo(statinfo->salvageStaticInfo->numSalvagePoints-1); object->salvageInfo = memAlloc(sizeofSalvageInfo,"SalvageInfo",NonVolatile); object->salvageInfo->numSalvagePoints = statinfo->salvageStaticInfo->numSalvagePoints; //list free ones...account for heading and up ones! object->salvageInfo->numSalvagePointsFree = object->salvageInfo->numSalvagePoints; for(i=0,cursalvagepoint=&object->salvageInfo->salvagePoints[0],cursalvagestaticpoint=&statinfo->salvageStaticInfo->salvageStaticPoints[0];isalvageStaticInfo->numSalvagePoints;i++,cursalvagepoint++,cursalvagestaticpoint++) { cursalvagepoint->salvageStaticPoint = cursalvagestaticpoint; cursalvagepoint->busy = 0; } object->salvageInfo->numNeededForSalvage = statinfo->salvageStaticInfo->numNeededForSalvage; } } /*----------------------------------------------------------------------------- Name : univRegrowAsteroids Description : regrows an asteroid Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univRegrowAsteroids() { ; // complete later, regrowing asteroids properly } #if 0 /*----------------------------------------------------------------------------- Name : ObjDistCompareCB Description : callback function to compare camera distances of obj1,obj2 Inputs : obj1,obj2 Outputs : Return : returns TRUE if obj1 camera distance > obj2 camera distance ----------------------------------------------------------------------------*/ bool ObjDistCompareCB(SpaceObj *obj1,SpaceObj *obj2) { return (obj1->collOptimizeDist > obj2->collOptimizeDist); } #endif /*----------------------------------------------------------------------------- Name : univSpaceObjInRenderList Description : returns TRUE if the SpaceObj is in the render list Inputs : Outputs : Return : returns TRUE if the SpaceObj is in the render list ----------------------------------------------------------------------------*/ bool univSpaceObjInRenderList(SpaceObj *spaceobj) { if (spaceobj->renderlink.belongto != NULL) { return TRUE; } else { return FALSE; } } /*----------------------------------------------------------------------------- Name : univRemoveObjFromRenderList Description : removes the spaceobj from the render list (if it is there) Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univRemoveObjFromRenderList(SpaceObj *spaceobj) { listVerify(&universe.RenderList); if (univSpaceObjInRenderList(spaceobj)) { listRemoveNode(&spaceobj->renderlink); } listVerify(&universe.RenderList); } /*----------------------------------------------------------------------------- Name : univAddObjToRenderListIf Description : adds newobj to renderlist if ifobj is in render list Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univAddObjToRenderListIf(SpaceObj *newobj,SpaceObj *ifobj) { listVerify(&universe.RenderList); if (univSpaceObjInRenderList(ifobj)) { if ((newobj->flags & SOF_Hide) == 0) // don't add hidden objects { if (bitTest(newobj->flags, SOF_AlwaysSortFront)) { univAddObjToRenderList(newobj); return; } newobj->cameraDistanceVector = ifobj->cameraDistanceVector; newobj->cameraDistanceSquared = ifobj->cameraDistanceSquared; listAddNodeAfter(&ifobj->renderlink,&newobj->renderlink,newobj); } } } /*----------------------------------------------------------------------------- Name : univAddObjToRenderList Description : adds newobj to renderlist Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univAddObjToRenderList(SpaceObj *newobj) { listVerify(&universe.RenderList); if ((newobj->flags & SOF_Hide) == 0) { vector eyeposition = universe.mainCameraCommand.actualcamera.eyeposition; Node *objnode; SpaceObj *obj; vecSub(newobj->cameraDistanceVector,eyeposition,newobj->posinfo.position); newobj->cameraDistanceSquared = vecMagnitudeSquared(newobj->cameraDistanceVector); // now find where to put object in list to maintain sort order // render list is sorted from back to front, so find first node which has cameraDistanceSquared less than ours if (bitTest(newobj->flags, SOF_AlwaysSortFront)) { //if this object has to go in front objnode = universe.RenderList.tail; while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); if (obj->cameraDistanceSquared > newobj->cameraDistanceSquared) { listAddNodeAfter(objnode, &newobj->renderlink,newobj); listVerify(&universe.RenderList); goto doneForward; } objnode = objnode->prev; } listAddNode(&universe.RenderList,&newobj->renderlink,newobj); listVerify(&universe.RenderList); doneForward:; } else { objnode = universe.RenderList.head; while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); if (obj->cameraDistanceSquared < newobj->cameraDistanceSquared) { listAddNodeBefore(objnode,&newobj->renderlink,newobj); goto doneRegular; } objnode = objnode->next; } listAddNode(&universe.RenderList,&newobj->renderlink,newobj); doneRegular: ; } } } void univUpdateMinorRenderList() { Node *objnode = universe.MinorSpaceObjList.head; SpaceObj *obj; vector distvec; vector lookatposition; vector eyeposition; real32 distsqr; if (mrCamera == NULL) { return; // don't update render list if no mrCamera } lookatposition = mrCamera->lookatpoint; eyeposition = mrCamera->eyeposition; listVerify(&universe.RenderList); listRemoveAll(&universe.MinorRenderList); while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); #ifdef gshaw dbgAssert((obj->objtype == OBJ_AsteroidType) && (((Asteroid *)obj)->asteroidtype == Asteroid0)); #endif vecSub(distvec,lookatposition,obj->posinfo.position); distsqr = vecMagnitudeSquared(distvec); if (distsqr < RENDER_VIEWABLE_DISTANCE_SQR) { if ((obj->flags & SOF_Hide) == 0) // don't add hidden objects { vecSub(obj->cameraDistanceVector,eyeposition,obj->posinfo.position); obj->cameraDistanceSquared = vecMagnitudeSquared(obj->cameraDistanceVector); listAddNode(&universe.MinorRenderList,&obj->renderlink,obj); } } objnode = objnode->next; } listVerify(&universe.RenderList); } /*----------------------------------------------------------------------------- Name : univUpdateRenderList Description : updates the render list Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univUpdateRenderList() { Node *objnode = universe.SpaceObjList.head; Node *nextnode; SpaceObj *obj; vector distvec; vector lookatposition; vector eyeposition; real32 distsqr; static bool limited = FALSE; static real32 limits[NUM_CLASSES]; LinkedList sortFrontList; listVerify(&universe.RenderList); listInit(&sortFrontList); if (mrCamera == NULL) { return; // don't update render list if no mrCamera } if (!limited) { limits[CLASS_Mothership] = RENDER_LIMIT_MOTHERSHIP; limits[CLASS_HeavyCruiser] = RENDER_LIMIT_HEAVYCRUISER; limits[CLASS_Carrier] = RENDER_LIMIT_CARRIER; limits[CLASS_Destroyer] = RENDER_LIMIT_DESTROYER; limits[CLASS_Frigate] = RENDER_LIMIT_FRIGATE; limits[CLASS_Corvette] = RENDER_LIMIT_CORVETTE; limits[CLASS_Fighter] = RENDER_LIMIT_FIGHTER; limits[CLASS_Resource] = RENDER_LIMIT_RESOURCE; limits[CLASS_NonCombat] = RENDER_LIMIT_NONCOMBAT; limited = TRUE; } lookatposition = mrCamera->lookatpoint; eyeposition = mrCamera->eyeposition; listRemoveAll(&universe.RenderList); while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); #ifdef gshaw #ifndef HW_Release if ((obj->objtype == OBJ_AsteroidType) && (((Asteroid *)obj)->asteroidtype == Asteroid0)) { dbgAssert(FALSE); } #endif #endif vecSub(distvec,lookatposition,obj->posinfo.position); distsqr = vecMagnitudeSquared(distvec); if (obj->flags & SOF_ForceVisible) { //if object is to always be in render list vecSub(obj->cameraDistanceVector, eyeposition, obj->posinfo.position); obj->cameraDistanceSquared = vecMagnitudeSquared(obj->cameraDistanceVector); listAddNode(&universe.RenderList, &obj->renderlink, obj); } else { //else check if object should be in render list if (obj->objtype == OBJ_ShipType) { ShipClass shipclass; real32 limit; shipclass = ((ShipStaticInfo*)obj->staticinfo)->shipclass; dbgAssert((udword)shipclass < NUM_CLASSES); if (((ShipStaticInfo*)obj->staticinfo)->renderlistLimitSqr != 0.0f) { limit = ((ShipStaticInfo*)obj->staticinfo)->renderlistLimitSqr; } else { limit = limits[(sdword)shipclass]; } if ((distsqr < limit) && !(obj->flags & SOF_Hide)) { vecSub(obj->cameraDistanceVector, eyeposition, obj->posinfo.position); obj->cameraDistanceSquared = vecMagnitudeSquared(obj->cameraDistanceVector); listAddNode(&universe.RenderList, &obj->renderlink, obj); } } else if (obj->objtype == OBJ_DerelictType) { real32 limit; if (((DerelictStaticInfo*)obj->staticinfo)->renderlistLimitSqr != 0.0f) { limit = ((DerelictStaticInfo*)obj->staticinfo)->renderlistLimitSqr; } else { limit = (obj->flags & SOF_BigObject) ? RENDER_MAXVIEWABLE_DISTANCE_SQR : RENDER_VIEWABLE_DISTANCE_SQR; } if (distsqr < limit) { if ((obj->flags & SOF_Hide) == 0) { vecSub(obj->cameraDistanceVector, eyeposition, obj->posinfo.position); obj->cameraDistanceSquared = vecMagnitudeSquared(obj->cameraDistanceVector); if (bitTest(obj->flags, SOF_AlwaysSortFront)) { listAddNode(&sortFrontList, &obj->renderlink, obj); } else { listAddNode(&universe.RenderList, &obj->renderlink, obj); } } } } else { if (distsqr < RENDER_MAXVIEWABLE_DISTANCE_SQR) { if ((distsqr < RENDER_VIEWABLE_DISTANCE_SQR) || (obj->flags & SOF_BigObject)) { if ((obj->flags & SOF_Hide) == 0) // don't add hidden objects { vecSub(obj->cameraDistanceVector,eyeposition,obj->posinfo.position); obj->cameraDistanceSquared = vecMagnitudeSquared(obj->cameraDistanceVector); if (bitTest(obj->flags, SOF_AlwaysSortFront)) { listAddNode(&sortFrontList, &obj->renderlink, obj); } else { listAddNode(&universe.RenderList,&obj->renderlink,obj); } } } } } } objnode = objnode->next; } listVerify(&universe.RenderList); listMergeSort2(&universe.RenderList); listVerify(&universe.RenderList); if (sortFrontList.num > 0) { //if there are force-front-sort objects listMergeSort2(&sortFrontList); //sort them like regular rederlist objects objnode = sortFrontList.head; while (objnode != NULL) { obj = (SpaceObj *)listGetStructOfNode(objnode); nextnode = objnode->next; dbgAssert(&obj->renderlink == objnode); listAddNode(&universe.RenderList, &obj->renderlink, obj); objnode = nextnode; } } listVerify(&universe.RenderList); } /*----------------------------------------------------------------------------- Name : univCheckRegrowResources Description : checks if any resources should be regrown Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void univCheckRegrowResources(void) { Node *node = universe.ResourceVolumeList.head; ResourceVolume *resvol; while (node != NULL) { resvol = (ResourceVolume *)listGetStructOfNode(node); if (resvol->actualnumber < resvol->number) { // we are short some resources, so there is a chance we might regrow them real32 shortperc = 1.0f - ( (real32) resvol->actualnumber / (real32) resvol->number ); sdword regrowrate = resvol->attributesParam; if ((regrowrate < 0) || (regrowrate > REGROWRATE_Max)) { regrowrate = 0; } shortperc *= REGROW_RATE_MODIFIER[regrowrate]; if (frandom(1) < shortperc) { sdword tr; // let's regrow the resource ... or at least try 3 times (make sure it doesn't overlap ship) switch (resvol->resourceVolumeType) { case ResourceSphereType: for (tr=0;tr<3;tr++) { if (AddResourceToSphere(resvol,TRUE)) { #ifdef HW_Debug dbgMessage("\nRegrowing resource!"); #endif resvol->actualnumber++; break; } } break; case ResourceCylinderType: for (tr=0;tr<3;tr++) { if (AddResourceToCylinder(resvol,TRUE)) { #ifdef HW_Debug dbgMessage("\nRegrowing resource!"); #endif resvol->actualnumber++; break; } } break; case ResourceRectangleType: for (tr=0;tr<3;tr++) { if (AddResourceToRectangle(resvol,TRUE)) { #ifdef HW_Debug dbgMessage("\nRegrowing resource!"); #endif resvol->actualnumber++; break; } } break; default: dbgAssert(FALSE); break; } } } node = node->next; } } void wkSmoothShips(void); /*----------------------------------------------------------------------------- Name : univUpdate Description : Updates the mission sphere ships and objects Inputs : phystimeelapsed, time since last time it was updated Outputs : Return : TRUE if done (game over) ----------------------------------------------------------------------------*/ bool univUpdate(real32 phystimeelapsed) { if ((autoSaveDebug) && ((universe.univUpdateCounter & 31) == 0)) // every 2s { char savegamename[200]; static sdword savenumber = 0; if (multiPlayerGame) { sprintf(savegamename,"SavedGames\\AutoSave%f",universe.totaltimeelapsed); SaveGame(savegamename); clCommandMessage(strGetString(strSavedGame)); } else { if ((universe.univUpdateCounter & 255) == 0) // only save every 16s { if (((singlePlayerGame) && (singlePlayerGameInfo.hyperspaceState)) || (nisIsRunning)) { ; // don't save game } else { sprintf(savegamename,"SavedGames\\AutoSaveDebug%d",savenumber); savenumber = (savenumber+1) & 7; SaveGame(savegamename); clCommandMessage(strGetString(strSavedGame)); } } } } #ifdef GOD_LIKE_SYNC_CHECKING if(multiPlayerGame) { if(syncDumpOn) { industrialStrengthSyncDebugging(universe.univUpdateCounter); netcheckIndustrialChecksum(); } } #endif universe.univUpdateCounter++; #define firstTime (universe.univUpdateCounter == 1) universe.phystimeelapsed = phystimeelapsed; universe.totaltimeelapsed += phystimeelapsed; if (universe.quittime != 0.0f) { real32 usequittime = universe.quittime; if (universe.wintime != 0.0f) { if (universe.totaltimeelapsed >= universe.wintime) { DeclarePlayerWin(); universe.wintime = 0.0f; } } if ((multiPlayerGame) && (IAmCaptain)) { usequittime += 5.0f; // give a extra 5s so we transmit some more sync pkts other people so they quit too... } if (universe.totaltimeelapsed >= usequittime) { // quit the game here spMainScreen(); #if (defined(Downloadable) || defined(OEM)) if ((singlePlayerGame) && (!(utyCreditsSequence|utyPlugScreens))) #else if ((singlePlayerGame) && (!(utyCreditsSequence))) #endif { gameIsRunning = FALSE; mrReset(); feScreenStart(ghMainRegion, "SP_Game_Over"); return TRUE; } else { utyGameQuitToMain(NULL,NULL); return TRUE; } } } else if (multiPlayerGame) { if (queueNumberOverruns(ProcessSyncPktQ) > 0) { bigmessageDisplay(strGetString(strSyncPktQOverrun),1); universe.quittime = universe.totaltimeelapsed + WAIT_TIME_TO_QUIT; } } PTSLAB(0,"univupdate"); #ifdef OPTIMIZE_VERBOSE vecNormalizeCounter=0; #endif if ((!singlePlayerGame) && ((universe.univUpdateCounter & regenerateRUrate) == 0)) { sdword i; Player *player; for (i=0;iplayerState != PLAYER_DEAD) && (player->resourceUnits < regenerateRUsIfUnder)) { sdword before = player->resourceUnits; player->resourceUnits += regenerateRUs; if (player->resourceUnits > regenerateRUsIfUnder) { player->resourceUnits = regenerateRUsIfUnder; } universe.gameStats.playerStats[player->playerIndex].totalRegeneratedResourceUnits += (player->resourceUnits - before); } } } if(tutorialdone) { // quit the game here spMainScreen(); utyGameQuitToMain(NULL,NULL); return TRUE; } PTSLAB(1,"collblobs"); if (firstTime || ((universe.univUpdateCounter & REFRESH_COLLBLOB_RATE) == REFRESH_COLLBLOB_FRAME)) { collUpdateCollBlobs(); } else { if ((universe.univUpdateCounter & REFRESH_COLLBLOB_RATE) == REFRESH_COLLBLOB_BATTLEPING_FRAME) { pingBattlePingsCreate(&universe.collBlobList); } collUpdateObjsInCollBlobs(); } PTEND(1); cloudSetFog(); //dust/gas cloud think nebSetFog(); //nebula think //construction manager deterministic think cmDeterministicBuildProcess(); // crappy fucking construction manager, no longer a task cmBuildTaskFunction(); if ((universe.univUpdateCounter & REFRESH_RESEARCH_RATE) == REFRESH_RESEARCH_FRAME) { rmUpdateResearch(); } if (!singlePlayerGame) { if ( (bitTest(tpGameCreated.flag,MG_ResourceInjections)) && ((universe.univUpdateCounter-universe.lasttimeadded) == tpGameCreated.resourceInjectionInterval) ) { universe.lasttimeadded = universe.univUpdateCounter; resourceInjectionTask(); //speech event to notify of this: if (universe.players[universe.curPlayerIndex].playerState != PLAYER_DEAD) speechEventFleet(STAT_F_ResourceInjectionRecieved,0,universe.curPlayerIndex); } if ( (bitTest(tpGameCreated.flag,MG_ResourceLumpSum)) && (universe.univUpdateCounter == tpGameCreated.resourceLumpSumTime)) { resourceLumpSum(); if (universe.players[universe.curPlayerIndex].playerState != PLAYER_DEAD) speechEventFleet(STAT_F_ResourceInjectionRecieved,0,universe.curPlayerIndex); } if (universe.univUpdateCounter == universe.players[sigsPlayerIndex].AllianceProposalsTimeout) { universe.players[sigsPlayerIndex].AllianceProposals = 0; } } #ifdef COLLISION_CHECK_STATS shipsavoidingstuff = 0; shipsavoidedwalks = 0; shipsavoidedchecks = 0; #endif //univZeroForcesOnResources(); // no longer needed - force, torque cleared after physUpdateObjPosVel acts on it //univZeroForcesOnDerelicts(); //univZeroForcesOnMissiles(); if (firstTime) { univSetupShipsForControl(); // only do it first time, since its done at end of univUpdateAllPosVel gameStatsCalcShipCostTotals(); // calculate the total ship cost at game start for the game stats } //univSetupDerelictsForControl(); if (wkTradeStuffActive) { wkTradeUpdate(); } if (singlePlayerGame) singlePlayerGameUpdate(); else if (bitTest(tpGameCreated.flag,MG_Hyperspace)) UpdateMidLevelHyperspacingShips(); PTSLAB(3,"collbulmis"); collCheckAllBulletMissileCollisions(); PTEND(3); PTSLAB(4,"compplayer"); cratesUpdate(); aiplayerUpdateAll(); PTEND(4); PTSLAB(5,"cmdlayer"); PTSLABLITTLE(2,"aiship"); if (!nisUniversePause) { clProcess(&universe.mainCommandLayer); clPostProcess(&universe.mainCommandLayer); } PTENDLITTLE(2); //do global tactics updates tacticsGlobalUpdate(); #if 0 // no longer needed - univCheckShipState called in univUpdateAllPosVel now if (!nisUniversePause) { univCheckShipStates(); } #endif univUpdateMineWallFormations(); PTEND(5); PTSLAB(7,"collbump"); collCheckAllBumpCollisions(); PTEND(7); PTSLAB(6,"updateobjpos"); //old school function replaced with new functions below //univUpdateAllPosVel(); // also does a univCheckShipStates univUpdateAllPosVelMinorObjs(); univUpdateAllPosVelBullets(); univUpdateAllPosVelMissiles(); if (!nisUniversePause) { univUpdateAllPosVelDerelicts(); univUpdateAllPosVelResources(); univUpdateAllPosVelShips(); // also does a univCheckShipStates } else { univPausedUpdateAllPosVelShips(); //certain operations only for when universe is paused } univUpdateAllPosVelEffects(); // MUST do effects sometime after ships+bullets and possibly other things that they may be attached to PTEND(6); univDeleteDeadShips(); univDeleteDeadResources(); univDeleteDeadDerelicts(); univDeleteDeadMissiles(); if ((universe.univUpdateCounter & REGROW_RESOURCES_CHECK_RATE) == REGROW_RESOURCES_CHECK_FRAME) { univCheckRegrowResources(); } PTEND(0); if (singlePlayerGameLoadNewLevelFlag) { singlePlayerLoadNewLevel(); if (hrAbortLoadingGame) { utyGameQuitToMain(NULL,NULL); hrAbortLoadingGame = FALSE; return TRUE; } } if(!universe.bounties || (universe.univUpdateCounter & CHECK_BOUNTIES_RATE) == CHECK_BOUNTIES_FRAME) { universe.bounties = TRUE; calculatePlayerBounties(); } return FALSE; } void univKillPlayer(sdword i,sdword playerdeathtype) { char filename[50]; if (gatherStats|showStatsFight|showStatsFancyFight) return; // just gathering stats, don't want to do the player win thing if (universe.players[i].playerState == PLAYER_DEAD) { return; // player already dead } if (i == MAX_MULTIPLAYER_PLAYERS) { return; // special player for autoguns, just exit } universe.players[i].PlayerMothership = NULL; universe.players[i].playerState = PLAYER_DEAD; universe.players[i].sensorLevel = 2; universe.gameStats.playerStats[i].timeOfDeath = universe.totaltimeelapsed; universe.gameStats.playerStats[i].typeOfPlayerDeath = playerdeathtype; aiplayerPlayerDied(&universe.players[i]); if (i == universe.curPlayerIndex) { smGhostMode = TRUE; } /* This will only be played if its a Relic player that died */ speechEventFleet(STAT_F_RelicPlayerDies, i, SOUND_EVENT_DEFAULT); // player i has lost the game if (!singlePlayerGame) DisplayPlayerHasBeenDefeatedMessage(i); DisableAllPlayersShips(i); if (universe.curPlayerIndex == i) { if (singlePlayerGame) { bigmessageDisplay(strGetString(strQuestIsLost),0); } else { if (playerdeathtype == PLAYERKILLED_DROPPEDOUT) { bigmessageDisplay(strGetString(strHaveBeenDroppedOut),0); } else { bigmessageDisplay(strGetString(strHaveBeenDefeated),0); } } } strcpy(filename,playerNames[i]); strcat(filename,strGetString(strStatsDiedStats)); writeGameStatsToFile(filename); }