/*============================================================================= Name : Mothership.c Purpose : Specifics for the Mothership Created 6/30/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include "types.h" #include "debug.h" #include "spaceobj.h" #include "Mothership.h" #include "statscript.h" #include "gun.h" #include "attack.h" #include "DefaultShip.h" #include "madLinkin.h" #include "savegame.h" #include "universe.h" #include "aitrack.h" #include "salcapcorvette.h" #include "madlinkindefs.h" #include "commandlayer.h" extern sdword R1MOTHERSHIP_Big; extern sdword R2MOTHERSHIP_Big; MothershipStatics MothershipStatic; MothershipStatics MothershipStaticRace1; MothershipStatics MothershipStaticRace2; //I don't envision any more ships attaching themselves to the mothership door...should they then hooray! scriptStructEntry MothershipStaticTable[] = { { "specialDoorOffset[R1][AdvanceSupportFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][AdvanceSupportFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][Carrier]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][Carrier]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][CloakGenerator]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][CloakGenerator]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][DDDFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][DDDFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][DFGFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][DFGFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][GravWellGenerator]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][GravWellGenerator]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][HeavyCruiser]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][HeavyCruiser]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][IonCannonFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][IonCannonFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][MissileDestroyer]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][MissileDestroyer]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][ResourceController]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][ResourceController]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][StandardDestroyer]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][StandardDestroyer]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][StandardFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][StandardFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][P1IonArrayFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][P1IonArrayFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][P2MultiBeamFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][P2MultiBeamFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][P2FuelPod]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][P2FuelPod]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R1][CryoTray]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R1][CryoTray]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][AdvanceSupportFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][AdvanceSupportFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][Carrier]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][Carrier]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][CloakGenerator]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][CloakGenerator]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][DDDFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][DDDFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][DFGFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][DFGFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][GravWellGenerator]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][GravWellGenerator]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][HeavyCruiser]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][HeavyCruiser]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][IonCannonFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][IonCannonFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][MissileDestroyer]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][MissileDestroyer]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][ResourceController]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][ResourceController]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][StandardDestroyer]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][StandardDestroyer]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][StandardFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][StandardFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][P1IonArrayFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][P1IonArrayFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][P2MultiBeamFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][P2MultiBeamFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][P2FuelPod]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][P2FuelPod]), (udword) &(MothershipStatic) }, { "specialDoorOffset[R2][CryoTray]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[R2][CryoTray]), (udword) &(MothershipStatic) }, { "specialDoorOffset[P1][P1IonArrayFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[P1][P1IonArrayFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[P2][P2MultiBeamFrigate]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[P2][P2MultiBeamFrigate]), (udword) &(MothershipStatic) }, { "specialDoorOffset[P2][P2FuelPod]", scriptSetLWToHWMonkeyVectorCB, (udword) &(MothershipStatic.specialDoorOffset[P2][P2FuelPod]), (udword) &(MothershipStatic) }, { "specialDoorOffset", scriptSetSpecialDoorOffsetCB,(udword) &(MothershipStatic), (udword) &(MothershipStatic)}, { "specialDoorInterpolationPerSecond", scriptSetReal32CB, (udword) &(MothershipStatic.specialDoorInterpolationPerSecond), (udword) &(MothershipStatic) }, { NULL,NULL,0,0 } }; real32 DOCKPOINT_BUSY_TOO_LONG = UNIVERSE_UPDATE_RATE * 60; void MothershipStaticInit(char *directory,char *filename,struct ShipStaticInfo *statinfo) { MothershipStatics *mothershipstat = (statinfo->shiprace == R1) ? &MothershipStaticRace1 : &MothershipStaticRace2; sdword i,j; //init static info for(i=0;ispecialDoorOffset[i][j].x = 0.0f; mothershipstat->specialDoorOffset[i][j].y = 0.0f; mothershipstat->specialDoorOffset[i][j].z = 0.0f; mothershipstat->specialDoorOrientationHeading[i][j]=-1; mothershipstat->specialDoorOrientationUp[i][j]=-1; } } scriptSetStruct(directory,filename,MothershipStaticTable,(ubyte *)mothershipstat); statinfo->custstatinfo = mothershipstat; } void MothershipInit(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; sdword i; for(i=0;idropstate[i] = 0; spec->droptarget[i] = NULL; spec->dockindex[i] = 0; } spec->doorCargo = NULL; spec->CAP_NumInBuildCue=0; } void MothershipAttack(Ship *ship,SpaceObjRotImpTarg *target,real32 maxdist) { attackPassive(ship,(Ship *)target); // Typecast to prevent warning, attackPassive will handle SpaceObjRotImpTarg } void MothershipAttackPassive(Ship *ship,Ship *target,bool rotate) { attackPassive(ship,target); } //single player game...called when hs button is pushed for quick //docking + progression to HS..this will ensure it partly works when //launching big ships... void mothershipCleanDoorForHSInstant(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; if(spec->doorCargo != NULL) { MothershipDettachObjectFromDoor(ship); spec->doorCargo = NULL; } if( ship->madDoorStatus != MAD_STATUS_DOOR_CLOSED) { //if door isn't closed if(ship->madBindings->bPaused) { //animation is paused...well,unpause it //so as to fix a bug in lukes code! madAnimationPause(ship,!ship->madBindings->bPaused); } madAnimationStop(ship); ship->cuedAnimationIndex = ship->staticinfo->madStatic->DoorOpenIndexes[0]; ship->cuedAnimationType = MAD_ANIMATION_NOTHING; ship->madDoorStatus = MAD_STATUS_DOOR_CLOSED; ship->madAnimationFlags=0; ship->nextAnim = 0; } } void MothershipDoorUpKeep(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; MothershipStatics *mothershipstatics = (MothershipStatics *)((ship->staticinfo))->custstatinfo; vector positionWS,doorHeading,doorRight,doorUp; vector heading,up,right; matrix coordsysWS; if(spec->doorCargo != NULL) { //get info mothershipGetCargoPosition(ship,spec->doorCargo,&positionWS,&coordsysWS,&heading,&up,&right); matGetVectFromMatrixCol1(doorUp,coordsysWS); matGetVectFromMatrixCol2(doorRight,coordsysWS); matGetVectFromMatrixCol3(doorHeading,coordsysWS); //vecScalarMultiply(doorLeft,doorRight,-1.0f); matPutVectIntoMatrixCol1(up,spec->doorCargo->rotinfo.coordsys); //vecScalarMultiply(doorHeading,doorHeading,-1.0f); matPutVectIntoMatrixCol2(right,spec->doorCargo->rotinfo.coordsys); matPutVectIntoMatrixCol3(heading,spec->doorCargo->rotinfo.coordsys); if(spec->doorCargo->putOnDoor==TRUE || MoveReachedDestinationVariable((Ship *)spec->doorCargo,&positionWS,1.0f)) { spec->doorCargo->putOnDoor=TRUE; spec->doorCargo->posinfo.position = positionWS; } else { vector adder; vecSub(adder,positionWS,spec->doorCargo->posinfo.position); vecScalarMultiply(adder,adder,0.3f); vecAddTo(spec->doorCargo->posinfo.position,adder); } } } /*----------------------------------------------------------------------------- Name : GetDirectionVectorFromMat Description : gets a direction vector from a matrix (0=up,1=right,2=forward,4=down,5=left,6=back) Inputs : direction, ship Outputs : result Return : ----------------------------------------------------------------------------*/ void GetDirectionVectorFromMat(vector *result,udword direction,matrix *coordsys) { switch (direction & 3) { case 0: matGetVectFromMatrixCol1(*result,*coordsys); break; case 1: matGetVectFromMatrixCol2(*result,*coordsys); break; case 2: case 3: matGetVectFromMatrixCol3(*result,*coordsys); break; } if (direction & 4) { vecNegate(*result); } } void mothershipGetCargoPosition(Ship *ship,SpaceObjRotImpTargGuidanceShipDerelict *cargo, vector *position, matrix *coordsys, vector *heading, vector *up, vector *right) { MothershipStatics *mothershipstatics = (MothershipStatics *)((ship->staticinfo))->custstatinfo; vector worldRelative;//doorUp,doorRight,doorHeading; sdword headingdir,updir; if(madLinkInGetDoorInfo(ship,coordsys,position)) { position->x += TW_R1_MOTHERSHIP_DOOR_OFFSET_MODIFIER; headingdir = mothershipstatics->specialDoorOrientationHeading[((Ship *)cargo)->shiprace][((Ship *)cargo)->shiptype]; if(headingdir == -1) headingdir = 0; updir = mothershipstatics->specialDoorOrientationUp[((Ship *)cargo)->shiprace][((Ship *)cargo)->shiptype]; if(updir == -1) updir = 1; if(cargo->objtype == OBJ_ShipType) { matMultiplyMatByVec(&worldRelative,coordsys,&mothershipstatics->specialDoorOffset[((Ship *)cargo)->shiprace][((Ship *)cargo)->shiptype]); GetDirectionVectorFromMat(heading,headingdir,coordsys); GetDirectionVectorFromMat(up,updir,coordsys); vecCrossProduct(*right,*heading,*up); } else { //probably never going to get here normally...derelicts will not be salvaged or docked. //use default values matMultiplyMatByVec(&worldRelative,coordsys,&mothershipstatics->specialDoorOffset[R1][HeavyCruiser]); GetDirectionVectorFromMat(heading,0,&ship->rotinfo.coordsys); GetDirectionVectorFromMat(up,1,&ship->rotinfo.coordsys); vecCrossProduct(*right,*heading,*up); } vecAddTo(*position,worldRelative); } else { //no postion attainable use default values *position = ship->posinfo.position; GetDirectionVectorFromMat(heading,0,&ship->rotinfo.coordsys); GetDirectionVectorFromMat(up,1,&ship->rotinfo.coordsys); vecCrossProduct(*right,*heading,*up); } } void MothershipRemoveShipReferences(Ship *ship,Ship *shiptoremove) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; sdword i; if(shiptoremove == (Ship *) spec->doorCargo) { spec->doorCargo = NULL; } for(i=0;idroptarget[i]) { if(ship->dockInfo != NULL) { if(ship->dockInfo->dockpoints[spec->dockindex[i]].thisDockBusy) ship->dockInfo->dockpoints[spec->dockindex[i]].thisDockBusy--; } if(ship->shiprace == R1) { if(ship->shiptype == Mothership) { if(salCapNeedBig(ship,spec->droptarget[i])) { //we will have told the door too open...so lets check if(ship->madDoorStatus == MAD_STATUS_DOOR_OPEN || ship->madDoorStatus == MAD_STATUS_DOOR_OPENING) { madLinkInCloseDoor(ship); //close door if open! } } } } spec->droptarget[i] = NULL; } } } void MothershipAttachObjectToDoor(Ship *ship,SpaceObjRotImpTargGuidanceShipDerelict *object) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; #ifndef HW_Release dbgAssert(ship->shiptype == Mothership); dbgAssert(ship->shiprace == R1); #endif spec->doorCargo = object; //in future, need to calculate offset from offset from offset! } void MothershipDettachObjectFromDoor(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; #ifndef HW_Release dbgAssert(ship->shiptype == Mothership); dbgAssert(ship->shiprace == R1); #endif spec->doorCargo = NULL; } #pragma warning( 4 : 4047) // turns off "different levels of indirection warning" void Mothership_PreFix(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; sdword i; for(i=0;idroptarget[i] = SpaceObjRegistryGetID((SpaceObj *)spec->droptarget[i]); } spec->doorCargo = SpaceObjRegistryGetID((SpaceObj *)spec->doorCargo); } void Mothership_Fix(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; sdword i; for(i=0;idroptarget[i] = (SpaceObjRotImpTargGuidanceShipDerelict *)SpaceObjRegistryGetObj((sdword)spec->droptarget[i]); } spec->doorCargo = (SpaceObjRotImpTargGuidanceShipDerelict *)SpaceObjRegistryGetObj((sdword)spec->doorCargo); } #pragma warning( 2 : 4047) // turn back on "different levels of indirection warning" udword ReadDockTimer(ubyte *docktimer) { return (docktimer[0] | (docktimer[1]<<8) | (docktimer[2]<<16)); } void WriteDockTimer(ubyte *docktimer,udword newvalue) { docktimer[0] = newvalue & 255; docktimer[1] = (newvalue>>8) & 255; docktimer[2] = (newvalue>>16) & 255; } bool NoShipUsingThisDockPoint(Ship *ship,DockStaticPoint *dockpoint) { // walk the command layer to make sure no one is using this dockpoint Node *curnode = universe.mainCommandLayer.todolist.head; CommandToDo *command; SelectCommand *selection; Ship *dship; sdword i; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); switch (command->ordertype.order) { case COMMAND_DOCK: case COMMAND_LAUNCHSHIP: selection = command->selection; for (i=0;inumShips;i++) { dship = selection->ShipPtr[i]; if (dship->dockvars.dockship == ship) { if (dship->dockvars.dockstaticpoint == dockpoint) { // we're docking/launching with this ship, return FALSE; } } } break; } curnode = curnode->next; } return TRUE; } void MothershipHouseKeep(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; sdword i; bool droppingTargets = FALSE; for(i=0;idroptarget[i] != NULL) { droppingTargets = TRUE; if(DropTargetInShip(ship,&spec->dropstate[i],spec->droptarget[i],&spec->dockindex[i])) { spec->dropstate[i] = 0; spec->droptarget[i] = NULL; } } } for(i=0;iCAP_NumInBuildCue;) { spec->CAPTimeToBuildShip[i]-= universe.phystimeelapsed; if(spec->CAPTimeToBuildShip[i] < 0.0f) { //time for a captured ship to pop out Ship *newship; if (GetShipStaticInfoSafe(spec->CAPshiptype[i],spec->CAPshiprace[i])) { if ((multiPlayerGame) && (ship->playerowner->playerIndex==sigsPlayerIndex)) { shiplagtotals[spec->CAPshiptype[i]]++; } newship = clCreateShip(&universe.mainCommandLayer, spec->CAPshiptype[i], spec->CAPshiprace[i], ship->playerowner->playerIndex, ship); newship->colorScheme = spec->CAPcolorScheme[i]; } spec->CAP_NumInBuildCue--; spec->CAPTimeToBuildShip[i] = spec->CAPTimeToBuildShip[spec->CAP_NumInBuildCue]; spec->CAPshiptype[i] = spec->CAPshiptype[spec->CAP_NumInBuildCue]; spec->CAPshiprace[i] = spec->CAPshiprace[spec->CAP_NumInBuildCue]; spec->CAPcolorScheme[i] = spec->CAPcolorScheme[spec->CAP_NumInBuildCue]; continue; } i++; } // check dock point and unbusy if it's busy for really long if (!droppingTargets) { DockPoint *dockpoint = NULL; if (ship->shiprace == R1) { dbgAssert((R1MOTHERSHIP_Big >= 0) && (R1MOTHERSHIP_Big < ship->dockInfo->numDockPoints)); dockpoint = &ship->dockInfo->dockpoints[R1MOTHERSHIP_Big]; } else if (ship->shiprace == R2) { dbgAssert((R2MOTHERSHIP_Big >= 0) && (R2MOTHERSHIP_Big < ship->dockInfo->numDockPoints)); dockpoint = &ship->dockInfo->dockpoints[R2MOTHERSHIP_Big]; } if (dockpoint) { udword docktimer = ReadDockTimer(spec->freedocktimer); if (dockpoint->thisDockBusy) { if (docktimer >= DOCKPOINT_BUSY_TOO_LONG) { if (NoShipUsingThisDockPoint(ship,dockpoint->dockstaticpoint)) { dockpoint->thisDockBusy = 0; } docktimer = 0; } else { docktimer++; } } else { docktimer = 0; } WriteDockTimer(spec->freedocktimer,docktimer); } } } void MothershipDied(Ship *ship) { MothershipSpec *spec = (MothershipSpec *)ship->ShipSpecifics; sdword i; for(i=0;idroptarget[i] != NULL) { if(spec->droptarget[i]->objtype == OBJ_ShipType) { //undisabled and make selectable if(!bitTest(((Ship *)spec->droptarget[i])->specialFlags2,SPECIAL_2_DisabledForever)) { bitClear(spec->droptarget[i]->flags,SOF_Disabled); bitClear(spec->droptarget[i]->specialFlags,SPECIAL_SalvagedTargetGoingIntoDockWithShip); bitSet(spec->droptarget[i]->flags,SOF_Selectable); } } } } } CustShipHeader MothershipHeader = { Mothership, sizeof(MothershipSpec), MothershipStaticInit, NULL, MothershipInit, NULL, MothershipAttack, DefaultShipFire, MothershipAttackPassive, NULL, NULL, MothershipHouseKeep, MothershipRemoveShipReferences, MothershipDied, Mothership_PreFix, NULL, NULL, Mothership_Fix };