#include #include #include #include #include #include "bink.h" #include "types.h" #include "vector.h" #include "linkedlist.h" #include "objtypes.h" #include "spaceobj.h" #include "statscript.h" #include "font.h" #include "feflow.h" #include "universe.h" #include "univupdate.h" #include "physics.h" #include "singleplayer.h" #include "DDDFrigate.h" #include "taskbar.h" #include "utility.h" #include "mainrgn.h" #include "aitrack.h" #include "aiship.h" #include "levelload.h" #include "sensors.h" #include "nis.h" #include "mouse.h" #include "select.h" #include "soundevent.h" #include "collision.h" #include "ResearchShip.h" #include "InfoOverlay.h" #include "AIPlayer.h" #include "KAS.h" #include "hs.h" #include "AIVar.h" #include "Objectives.h" #include "ResearchGUI.h" #include "launchMgr.h" #include "trademgr.h" #include "file.h" #include "etg.h" #include "horserace.h" #include "attributes.h" #include "SaveGame.h" #include "damage.h" #include "TradeMgr.h" #include "ping.h" #include "Tutor.h" #include "randy.h" #include "..\\Generated\\mission01.h" #include "..\\Generated\\mission02.h" #include "..\\Generated\\mission03.h" #include "..\\Generated\\mission04.h" #ifdef OEM #include "..\\Generated\\mission05_OEM.h" #else #include "..\\Generated\\mission05.h" #include "..\\Generated\\mission06.h" #include "..\\Generated\\mission07.h" #include "..\\Generated\\mission08.h" #include "..\\Generated\\mission09.h" #include "..\\Generated\\mission10.h" #include "..\\Generated\\mission11.h" #include "..\\Generated\\mission12.h" #include "..\\Generated\\mission13.h" #include "..\\Generated\\mission14.h" #include "..\\Generated\\mission15.h" #include "..\\Generated\\mission16.h" #endif #include "..\\Generated\\tutorial1.h" #include "strings.h" #include "animatic.h" #include "gravwellgenerator.h" #include "commandwrap.h" #include "mothership.h" #include "alliance.h" #define NUMBER_SINGLEPLAYER_MISSIONS 19 #define ShipCanMakeHyperspace(ship) (ShipCanHyperspace(ship) && ((ship)->specialFlags & SPECIAL_SinglePlayerWithinHyperSpaceRange)) #define WARP_FILENAME "Warp.script" //stores the current mission filename char CurrentLevelName[256] = ""; bool singlePlayerHyperspacingInto = FALSE; bool spHoldHyperspaceWindow = FALSE; bool hyperspaceOverride = FALSE; real32 spFleetModifier = 0.0f; real32 spFleetStr = 0.0f; real32 BaseFleetStrengthForLevel[NUMBER_SINGLEPLAYER_MISSIONS+1]; real32 AdvancedFleetStrengthForLevel[NUMBER_SINGLEPLAYER_MISSIONS+1]; bool triggerNIS = FALSE; sdword warpLevel = 0; bool spBinkPlay; extern fonthandle selGroupFont0; // state of kas debug overlays #if SP_DEBUGLEVEL2 extern sdword mrKASDebugDrawStates; extern sdword mrKASDebugDrawTimers; extern sdword mrKASDebugDrawVars; extern sdword mrKASDebugDrawVolumes; extern sdword mrKASSkipFactor; #endif bool singlePlayerGameLoadNewLevelFlag; SinglePlayerGameInfo singlePlayerGameInfo; real32 HYPERSPACE_SLICE_RATE = 0.075f; bool HYPERSPACE_MOTHERSHIP_ALWAYS_ARRIVES_FIRST = TRUE; real32 HYPERSPACE_WAIT_FOR_MOTHERSHIPFOCUS_TIME = 5.0f; real32 HYPERSPACE_BASE_WAITTIME = 3.0f; real32 HYPERSPACE_BASE_ARRIVETIME = 2.0f; real32 HYPERSPACE_BASE_MOTHERSHIPARRIVETIME = 1.0f; real32 HYPERSPACE_RANDOM_WAITTIME = 0.2f; real32 HYPERSPACE_WAITTIME_PER_DISTANCE = 0.0005f; real32 HYPERSPACE_MINWAITTIME_DISTANCE = -1.0f; real32 HYPERSPACE_MAXWAITTIME_DISTANCE = 1.0f; real32 HYPERSPACE_ACCELERATE_TIME = 3.0f; real32 HYPERSPACE_ACCELERATION = 1000.0f; real32 HYPERSPACE_WHIP_VELOCITY = 50000.0f; real32 HYPERSPACE_HEADINGACCURACY = 0.98f; real32 HYPERSPACE_FARCLIPPLANE = 100000.0f; real32 HYPERSPACE_WHITEOUTTIME = 0.2f; real32 HYPERSPACE_FUNNEL_WIDTH = 5000.0f; real32 HYPERSPACE_FUNNEL_WIDTH_TO_BACK_FACTOR = 0.5f; real32 HYPERSPACE_FARCLIPPLANESQR; real32 HYPERSPACE_DECELERATE_TIME = 3.0f; real32 HYPERSPACE_DECELERATION = 10000.0f; real32 HYPERSPACE_DECELERATE_DIST; real32 HYPERSPACE_DECELERATE_INITIAL_VEL; real32 HYPERSPACE_WHIPTOWARDSDIST; real32 HYPERSPACE_TRAVELDIST_PER_RU_SPENT = 10000.0f; sdword SINGLEPLAYER_STARTINGRUS = 200; sdword SINGLEPLAYER_DEBUGLEVEL = 0; real32 HS_CLIPT_NEG_THRESH = -0.4f; real32 HS_CLIPT_NEG_SCALAR = 0.33f; real32 HS_CLIPT_NEG_FINISH = 0.9f; real32 HS_CLIPT_POS_THRESH = 0.5f; real32 HS_CLIPT_POS_SCALAR = 0.5f; real32 HS_CLIPT_POS_START = 1.0f; real32 HYPERSPACEGATE_HEALTH = 20000.0f; real32 HYPERSPACEGATE_WIDTH = 1000.0f; real32 HYPERSPACEGATE_HEIGHT = 1000.0f; real32 HYPERSPACEGATE_WAYPOINTDIST = 800.0f; real32 HYPERSPACE_RANGE = (3000.0f*3000.0f); real32 spHyperspaceDelay = 0.0f; real32 SINGLEPLAYER_MISSION14_SPHERE_OVERRIDE = 50000.0f; real32 SUBMESSAGE_SAFETY_TIMEOUT = 15.0f; real32 SINGLEPLAYER_BOBBIGGESTRADIUS_LEVEL6 = 40000.0f; void tmTechInit(void); static void SetBaseFleetStrCB(char *directory,char *field,void *dataToFillIn); scriptEntry SinglePlayerTweaks[] = { makeEntry(HS_CLIPT_NEG_THRESH, scriptSetReal32CB), makeEntry(HS_CLIPT_NEG_SCALAR, scriptSetReal32CB), makeEntry(HS_CLIPT_NEG_FINISH, scriptSetReal32CB), makeEntry(HS_CLIPT_POS_THRESH, scriptSetReal32CB), makeEntry(HS_CLIPT_POS_SCALAR, scriptSetReal32CB), makeEntry(HS_CLIPT_POS_START, scriptSetReal32CB), makeEntry(HYPERSPACEGATE_HEALTH, scriptSetReal32CB), makeEntry(HYPERSPACEGATE_WIDTH , scriptSetReal32CB), makeEntry(HYPERSPACEGATE_HEIGHT, scriptSetReal32CB), makeEntry(HYPERSPACEGATE_WAYPOINTDIST, scriptSetReal32CB), makeEntry(HYPERSPACE_SLICE_RATE, scriptSetReal32CB), makeEntry(HYPERSPACE_WAIT_FOR_MOTHERSHIPFOCUS_TIME,scriptSetReal32CB), makeEntry(HYPERSPACE_MOTHERSHIP_ALWAYS_ARRIVES_FIRST,scriptSetBool), makeEntry(HYPERSPACE_BASE_WAITTIME,scriptSetReal32CB), makeEntry(HYPERSPACE_BASE_ARRIVETIME,scriptSetReal32CB), makeEntry(HYPERSPACE_BASE_MOTHERSHIPARRIVETIME,scriptSetReal32CB), makeEntry(HYPERSPACE_RANDOM_WAITTIME,scriptSetReal32CB), makeEntry(HYPERSPACE_WAITTIME_PER_DISTANCE,scriptSetReal32CB), makeEntry(HYPERSPACE_MINWAITTIME_DISTANCE,scriptSetReal32CB), makeEntry(HYPERSPACE_MAXWAITTIME_DISTANCE,scriptSetReal32CB), makeEntry(HYPERSPACE_ACCELERATE_TIME,scriptSetReal32CB), makeEntry(HYPERSPACE_ACCELERATION,scriptSetReal32CB), makeEntry(HYPERSPACE_WHIP_VELOCITY,scriptSetReal32CB), makeEntry(HYPERSPACE_FARCLIPPLANE,scriptSetReal32CB), makeEntry(HYPERSPACE_HEADINGACCURACY,scriptSetCosAngCB), makeEntry(HYPERSPACE_WHITEOUTTIME,scriptSetReal32CB), makeEntry(HYPERSPACE_DECELERATE_TIME,scriptSetReal32CB), makeEntry(HYPERSPACE_DECELERATION,scriptSetReal32CB), makeEntry(HYPERSPACE_TRAVELDIST_PER_RU_SPENT,scriptSetReal32CB), makeEntry(SINGLEPLAYER_STARTINGRUS,scriptSetSdwordCB), makeEntry(SINGLEPLAYER_DEBUGLEVEL,scriptSetSdwordCB), makeEntry(HYPERSPACE_RANGE,scriptSetReal32SqrCB), makeEntry(SINGLEPLAYER_MISSION14_SPHERE_OVERRIDE,scriptSetReal32CB), makeEntry(SUBMESSAGE_SAFETY_TIMEOUT,scriptSetReal32CB), makeEntry(SINGLEPLAYER_BOBBIGGESTRADIUS_LEVEL6,scriptSetReal32CB), { "BaseFleetStrengthForLevel",SetBaseFleetStrCB,NULL }, endEntry }; static void SetWarpFleetCB(char *directory,char *field,void *dataToFillIn); static void WarpFleetStrengthCB(char *directory,char *field,void *dataToFillIn); static void WarpFleetRUStrengthCB(char *directory,char *field,void *dataToFillIn); static void WarpPreLoadCB(char *directory,char *field,void *dataToFillIn); scriptEntry WarpScriptTable[] = { { "WarpStartRU", scriptSetSdwordCB, &universe.players[0].resourceUnits}, { "WarpFleet", SetWarpFleetCB, NULL }, endEntry }; scriptEntry WarpCalcFleetStrengthTable[] = { { "WarpStartRU", WarpFleetRUStrengthCB, NULL}, { "WarpFleet", WarpFleetStrengthCB, NULL }, endEntry }; scriptEntry WarpPreLoadTable[] = { { "WarpFleet", WarpPreLoadCB, NULL }, endEntry }; static sdword WarpToLevelEnabled(); static void SetMissionStartEnemyRUs(char *directory,char *field,void *dataToFillIn); static void SetOnMissionCompleteInfo(char *directory,char *field,void *dataToFillIn); static void singlePlayerKasMissionStart(sdword missionnum); static void singlePlayerStartNis(char *nis, char *script, bool centre, hvector *positionAndAngle); void singlePlayerNISNamesGet(char **nisname, char **scriptname, bool *centreMothership, sdword nisNumber); //static bool spSavedCameraYet; //static Camera spSavedCamera; scriptEntry SinglePlayerScriptTable[] = { { "onMissionComplete", SetOnMissionCompleteInfo, &singlePlayerGameInfo }, { "onMissionLoad", scriptSetSdwordCB, &singlePlayerGameInfo.onLoadPlayNIS}, { "MissionStartEnemyRUs", scriptSetSdwordCB, &universe.players[1].resourceUnits }, { "MissionStartEnemyRace", scriptSetShipRaceCB, &universe.players[1].race }, { "Asteroid0sCanMove", scriptSetBool, &singlePlayerGameInfo.asteroid0sCanMove }, endEntry }; fibfileheader *spHyperspaceRollCallHandle = NULL; regionhandle spHyperspaceRollCallScreen = NULL; //taskbutton *hyperspacetbbutton = NULL; void spAbortHyperspaceCB(char *string, featom *atom); void spGoNowHyperspaceCB(char *string, featom *atom); void spShipsRemainingDrawCB(featom *atom, regionhandle region); void spShipsDockedDrawCB(featom *atom, regionhandle region); void singlePlayerLoadNewLevel(void); fecallback spHyperspaceCallback[] = { {spAbortHyperspaceCB, "AbortHyperspace"}, {spGoNowHyperspaceCB, "GoNowHyperspace"}, {NULL, NULL} }; fedrawcallback spHyperspaceDrawCallback[] = { {spShipsRemainingDrawCB, "ShipsRemainingDraw"}, {spShipsDockedDrawCB, "ShipsDockedDraw"}, {NULL, NULL} }; char spMissionsDir[32]; char spMissionsFile[32]; void GetMissionsDirAndFile(sdword mission) { #ifdef OEM if (mission == 5) { sprintf(spMissionsDir, "SinglePlayer\\Mission05_OEM\\"); sprintf(spMissionsFile, "Mission05_OEM.mission"); return; } #endif sprintf(spMissionsDir, "SinglePlayer\\Mission%02d\\", mission); sprintf(spMissionsFile,"Mission%02d.mission", mission); } bool GetPointOfName(hvector *point,char *name) { hvector *vectptr = kasVectorPtr(name); if (vectptr != NULL) { *point = *vectptr; return TRUE; } else { point->x = 0.0f; point->y = 0.0f; point->z = 0.0f; point->w = 0.0f; return FALSE; } } bool GetStartPointPlayer(hvector *startpoint) { return GetPointOfName(startpoint,"StartPointPlayer"); } void SetBaseFleetStrCB(char *directory,char *field,void *dataToFillIn) { sdword level; real32 basestrength; real32 advancedstrength; RemoveCommasFromString(field); sscanf(field,"%d %f %f",&level,&basestrength,&advancedstrength); dbgAssert(level <= NUMBER_SINGLEPLAYER_MISSIONS); BaseFleetStrengthForLevel[level] = basestrength; AdvancedFleetStrengthForLevel[level] = advancedstrength; } real32 GetStrengthOfShip(Ship *ship) { // for now, just return RU cost of ship: if (ship->shiptype == Mothership) { return 0.0f; } return (real32)ship->staticinfo->buildCost; } real32 GetStrengthOfShipStatic(ShipStaticInfo *shipstatic) { // for now, just return RU cost of ship: if (shipstatic->shiptype == Mothership) { return 0.0f; } return (real32)shipstatic->buildCost; } real32 GetStrengthOfRUs(sdword RUs) { return (real32)RUs; } typedef void (*GetStrengthOfFleet)(Player *player); /*----------------------------------------------------------------------------- Name : GetFleetStrengthHyperspace Description : Returns fleet strength of ships in hyperspace of player Inputs : Outputs : Return : Returns fleet strength of ships in hyperspace of player ----------------------------------------------------------------------------*/ void GetFleetStrengthHyperspace(Player *player) { // find total strength of fleet in hyperspace Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; InsideShip *insideShip; Ship *ship; spFleetStr = 0.0f; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { spFleetStr += GetStrengthOfShip(ship); // also check ships inside it: if ((ship->shipsInsideMe != NULL) && (ship->shiptype != DDDFrigate)) { // check ships inside too Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship2; while (insidenode != NULL) { insideship2 = (InsideShip *)listGetStructOfNode(insidenode); dbgAssert(insideship2->ship->objtype == OBJ_ShipType); spFleetStr += GetStrengthOfShip(insideship2->ship); insidenode = insidenode->next; } } } node = node->next; } spFleetStr += GetStrengthOfRUs(player->resourceUnits); } /*----------------------------------------------------------------------------- Name : GetFleetStrengthInUniverseOfPlayer Description : Returns fleet strength of ships of player in universe Inputs : Outputs : Return : Returns fleet strength of ships of player in universe ----------------------------------------------------------------------------*/ void GetFleetStrengthInUniverseOfPlayer(Player *player) { Node *objnode = universe.ShipList.head; Ship *ship; spFleetStr = 0.0f; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { spFleetStr += GetStrengthOfShip(ship); // also check ships inside it: if ((ship->shipsInsideMe != NULL) && (ship->shiptype != DDDFrigate)) { // check ships inside too Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship2; while (insidenode != NULL) { insideship2 = (InsideShip *)listGetStructOfNode(insidenode); dbgAssert(insideship2->ship->objtype == OBJ_ShipType); spFleetStr += GetStrengthOfShip(insideship2->ship); insidenode = insidenode->next; } } } objnode = objnode->next; } spFleetStr += GetStrengthOfRUs(player->resourceUnits); } void GetFleetStrengthWarpScript(Player *player) { spFleetStr = 0.0f; scriptSet(NULL,WARP_FILENAME,WarpCalcFleetStrengthTable); } void singlePlayerPreLoadCheck(void) { if (warpLevel) { scriptSet(NULL,WARP_FILENAME,WarpPreLoadTable); } } /*----------------------------------------------------------------------------- Name : Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void SetFleetModifier(sdword level,GetStrengthOfFleet getStrengthOfFleetFunc) { real32 comparestrength,comparestrength2; comparestrength = BaseFleetStrengthForLevel[level]; comparestrength2 = AdvancedFleetStrengthForLevel[level]; if ((comparestrength == 0.0f) || (comparestrength2 <= comparestrength)) { spFleetModifier = 0.0f; return; } getStrengthOfFleetFunc(&universe.players[0]); // set spFleetModifier to 0 to 1 based on spFleetStr commpared to comparestrength and comparestrength2 if (spFleetStr <= comparestrength) { spFleetModifier = 0.0f; return; } else if (spFleetStr >= comparestrength2) { spFleetModifier = 1.0f; return; } else { spFleetModifier = (spFleetStr - comparestrength) / (comparestrength2 - comparestrength); if (spFleetModifier > 1.0f) { spFleetModifier = 1.0f; } else if (spFleetModifier < 0.0f) { spFleetModifier = 0.0f; } } } // this function doesn't need to be a function, its only called in one place // and it changes the state variable of the case statement its called from, very confusing // and its only 4 lines - 07/02/99 SA //void spHyperspaceCommence() //{ //FocusCommand focusone; // singlePlayerGameInfo.hyperspaceState = HYPERSPACE_SHIPSLEAVING; // singlePlayerGameInfo.hyperspaceSubState = FOCUSING_ON_MOTHERSHIP; // dbgAssert(universe.curPlayerPtr->PlayerMothership != NULL); // focusone.numShips = 1; // focusone.ShipPtr[0] = universe.curPlayerPtr->PlayerMothership; // ccFocus(&universe.mainCameraCommand,&focusone); // singlePlayerGameInfo.hyperspaceTimeStamp = universe.totaltimeelapsed; // + HYPERSPACE_WAIT_FOR_MOTHERSHIPFOCUS_TIME; // speechEventFleet(STAT_F_Hyper_TakingOff, 0, universe.curPlayerIndex); //} void TellShipsToAbortDockForHyperspace() { SelectCommand *selectAll; selectAll = selectAllCurrentPlayersNonHyperspacingShips(); clHalt(&universe.mainCommandLayer,selectAll); memFree(selectAll); } void spAbortHyperspaceCB(char *string, featom *atom) { if (singlePlayerGameInfo.hyperspaceState == HYPERSPACE_WAITINGROLLCALL) { // Let's abort hyperspace speechEventFleet(COMM_F_Hyper_Abort, 0, universe.curPlayerIndex); // give player refund on RU's // universe.curPlayerPtr->resourceUnits += singlePlayerGameInfo.RUNeededForNextHyperSpaceJump; clAutoLaunch(singlePlayerGameInfo.oldAutoLaunch,universe.curPlayerIndex); singlePlayerGameInfo.hyperspaceState = NO_HYPERSPACE; TellShipsToAbortDockForHyperspace(); feScreenDelete(spHyperspaceRollCallScreen); spHyperspaceRollCallScreen = NULL; } } void spGoNowHyperspaceCB(char *string, featom *atom) { if (singlePlayerGameInfo.hyperspaceState == HYPERSPACE_WAITINGROLLCALL) { SelectCommand *selectAll; CommandToDo *militaryGroup; sdword i; Ship *ship; Ship *mothership = universe.curPlayerPtr->PlayerMothership; dbgAssert(mothership); selectAll = selectAllCurrentPlayersNonHyperspacingShips(); clDock(&universe.mainCommandLayer,selectAll,DOCK_PERMANENTLY|DOCK_INSTANTANEOUSLY,NULL); memFree(selectAll); selectAll = selectAllCurrentPlayersHyperspacingShips(); if(universe.curPlayerPtr->PlayerMothership->shiprace == R1) { mothershipCleanDoorForHSInstant(universe.curPlayerPtr->PlayerMothership); } for (i=0;inumShips;i++) { ship = selectAll->ShipPtr[i]; if (ship->shiptype != Mothership) { SelectCommand selectone; // if (!(ship->specialFlags & SPECIAL_SinglePlayerWithinHyperSpaceRange)) // { putinparade: selectone.ShipPtr[0] = ship; selectone.numShips = 1; RemoveShipsFromDoingStuff(&universe.mainCommandLayer,&selectone); aitrackForceSteadyShip(ship); GroupShipIntoMilitaryParade(&universe.mainCommandLayer,ship,mothership); bitSet(ship->specialFlags,SPECIAL_SinglePlayerWithinHyperSpaceRange|SPECIAL_SinglePlayerInParade); /* } else { if (!(ship->specialFlags & SPECIAL_SinglePlayerInParade)) { goto putinparade; #if 0 // steady ship and make sure it's pointing the right way. aitrackForceSteadyShip(ship); // face same direction as mothership ship->rotinfo.coordsys = mothership->rotinfo.coordsys; univUpdateObjRotInfo((SpaceObjRot *)ship); #endif } } */ } if (ship->shiptype == DDDFrigate) { DDDFrigateDockAllDronesInstantly(ship); } } memFree(selectAll); militaryGroup = GetMilitaryGroupAroundShip(&universe.mainCommandLayer,universe.curPlayerPtr->PlayerMothership); if (militaryGroup) setMilitaryParade(militaryGroup); feScreenDelete(spHyperspaceRollCallScreen); spHyperspaceRollCallScreen = NULL; } } void spShipsRemainingDrawCB(featom *atom, regionhandle region) { fonthandle fhSave; char numstr[10]; sprintf(numstr,"%d",singlePlayerGameInfo.rollcallShipsRemaining); fhSave = fontCurrentGet(); //save the current font fontMakeCurrent(selGroupFont2); //select the appropriate font fontPrintCentreCentreRectangle(®ion->rect,colWhite,numstr); fontMakeCurrent(fhSave); } void spShipsDockedDrawCB(featom *atom, regionhandle region) { fonthandle fhSave; char numstr[10]; sprintf(numstr,"%d",singlePlayerGameInfo.rollcallShipsDocked); fhSave = fontCurrentGet(); //save the current font fontMakeCurrent(selGroupFont2); //select the appropriate font fontPrintCentreCentreRectangle(®ion->rect,colWhite,numstr); fontMakeCurrent(fhSave); } void CalculateRollCall() { Node *objnode = universe.ShipList.head; Ship *ship; singlePlayerGameInfo.rollcallShipsDocked = 0; singlePlayerGameInfo.rollcallShipsRemaining = 0; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && (ship->shiptype != CryoTray) && (ship->shiptype != Probe) && !(ship->flags & SOF_Disabled)) { if (ship->shipsInsideMe) { if (ship->shiptype != DDDFrigate) { singlePlayerGameInfo.rollcallShipsDocked += ship->shipsInsideMe->insideList.num; } } if (!ShipCanHyperspace(ship)) { singlePlayerGameInfo.rollcallShipsRemaining++; } else if (ship->shiptype != Mothership) // non-mothership hyperspacecapable ships must get in range { // is ship in range? if (ship->specialFlags & SPECIAL_SinglePlayerWithinHyperSpaceRange) { singlePlayerGameInfo.rollcallShipsDocked++; } else { singlePlayerGameInfo.rollcallShipsRemaining++; } } } objnode = objnode->next; } } void TellShipsToDockForHyperspace() { Node *objnode; Ship *ship; SelectCommand *selectAll; selectAll = selectAllCurrentPlayersNonHyperspacingShips(); //cancel all the launch orders so we can then tell all ships to dock permanently. clCancelAllLaunchOrdersFromPlayer(universe.curPlayerPtr); if (clDock(&universe.mainCommandLayer,selectAll,DOCK_PERMANENTLY,NULL) > 0) { // moved this here cause I don't want it said if no ships are docking speechEventFleet(STAT_F_Hyper_Autodock, 0, universe.curPlayerIndex); } memFree(selectAll); objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && !(ship->flags & SOF_Disabled)) { if (ship->shiptype == DDDFrigate) { DDDFrigateMakeReadyForHyperspace(ship); } if(ship->shiptype == ResearchShip) { ResearchShipMakeReadyForHyperspace(ship); } } objnode = objnode->next; } } bool AreNotInParadeConstraint(Ship *ship) { if (ship->specialFlags & SPECIAL_SinglePlayerInParade) { return FALSE; } else { return TRUE; } } void MakeShipsNotIncludeLaunchingShips(SelectCommand *selection) { sdword i; CommandToDo *shipcommand; for(i=0;inumShips;) { if(ShipCanHyperspace(selection->ShipPtr[i])) { //ship can hyperspace shipcommand = getShipAndItsCommand(&universe.mainCommandLayer,selection->ShipPtr[i]); if(shipcommand != NULL) { if(shipcommand->ordertype.order == COMMAND_LAUNCHSHIP) { //launching ship can hyperspace...lets not cancel its //orders selection->numShips--; selection->ShipPtr[i] = selection->ShipPtr[selection->numShips]; continue; } } } i++; } } void TellHyperspacingShipsToReady() { SelectCommand *selectAll; sdword i; Ship *ship; selectAll = selectAllCurrentPlayersHyperspacingShips(); for (i=0;inumShips;i++) { ship = selectAll->ShipPtr[i]; if (ship->shiptype == Mothership) { bitSet(ship->specialFlags,SPECIAL_SinglePlayerWithinHyperSpaceRange); } else { if (shipInMilitaryParade(ship)) { bitSet(ship->specialFlags,SPECIAL_SinglePlayerWithinHyperSpaceRange|SPECIAL_SinglePlayerInParade); } else { bitClear(ship->specialFlags,SPECIAL_SinglePlayerWithinHyperSpaceRange); } } } //MakeShipsOnlyFollowConstraints(selectAll,AreNotInParadeConstraint); MakeShipsNotIncludeLaunchingShips(selectAll); RemoveShipsFromDoingStuff(&universe.mainCommandLayer,selectAll); clSetMilitaryParade(&universe.mainCommandLayer,selectAll); memFree(selectAll); } sdword HyperspaceRollCallBegin(regionhandle region, sdword ID, udword event, udword data) { if (!spHyperspaceRollCallHandle) { feCallbackAddMultiple(spHyperspaceCallback); //add in the callbacks feDrawCallbackAddMultiple(spHyperspaceDrawCallback); spHyperspaceRollCallHandle = feScreensLoad("FEMan\\Hyperspace_Roll_Call.fib"); //load in the screen } CalculateRollCall(); spHyperspaceRollCallScreen = feScreenStart(region, "HyperspaceRollCall"); return 0; } ShipSinglePlayerGameInfo *spNewShipSinglePlayerGameInfo() { ShipSinglePlayerGameInfo *newone = memAlloc(sizeof(ShipSinglePlayerGameInfo),"shipsingleGame",0); memset(newone,0,sizeof(*newone)); newone->shipHyperspaceState = SHIPHYPERSPACE_NONE; newone->midlevelHyperspaceIn = FALSE; newone->midlevelHyperspaceOut = FALSE; newone->staticHyperspaceGate = FALSE; return newone; } void CalculateHyperspaceTimeForShip(Ship *ship,Ship *mothership) { ShipSinglePlayerGameInfo *shipSinglePlayerGameInfo = ship->shipSinglePlayerGameInfo; vector mothershipheading; vector mothershipright; vector mothershipup; vector mothershipposition; vector mothershipToShip; if (mothership != NULL) { matGetVectFromMatrixCol3(mothershipheading,mothership->rotinfo.coordsys); matGetVectFromMatrixCol2(mothershipright,mothership->rotinfo.coordsys); matGetVectFromMatrixCol1(mothershipup,mothership->rotinfo.coordsys); mothershipposition = mothership->posinfo.position; } if ((mothership != NULL) && (ship != mothership)) { vecSub(mothershipToShip,ship->posinfo.position,mothershipposition); shipSinglePlayerGameInfo->forbehind = vecDotProduct(mothershipheading,mothershipToShip); shipSinglePlayerGameInfo->rightleft = vecDotProduct(mothershipright,mothershipToShip); shipSinglePlayerGameInfo->updown = vecDotProduct(mothershipup,mothershipToShip); } else { shipSinglePlayerGameInfo->forbehind = 0.0f; shipSinglePlayerGameInfo->rightleft = 0.0f; shipSinglePlayerGameInfo->updown = 0.0f; } shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_WAITING; shipSinglePlayerGameInfo->hsState = HS_INACTIVE; shipSinglePlayerGameInfo->hsFlags = 0; if ((mothership == NULL) || (ship == mothership)) { shipSinglePlayerGameInfo->timeToDelayHyperspace = 0.0f; } else { shipSinglePlayerGameInfo->timeToDelayHyperspace = HYPERSPACE_BASE_WAITTIME + frandom(HYPERSPACE_RANDOM_WAITTIME); } shipSinglePlayerGameInfo->timeToHyperspace = universe.totaltimeelapsed + shipSinglePlayerGameInfo->timeToDelayHyperspace; bitSet(ship->specialFlags,SPECIAL_Hyperspacing); shipHasJustDisappeared(ship); } void CalculateHyperspaceTimesForShips(void) { Node *objnode; Ship *ship; Ship *mothership = universe.curPlayerPtr->PlayerMothership; dbgAssert(mothership != NULL); objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && !(ship->flags & SOF_Disabled)) { if (ShipCanMakeHyperspace(ship)) { CalculateHyperspaceTimeForShip(ship,mothership); } } objnode = objnode->next; } } void AddShipToHyperspace(Ship *ship) { InsideShip *insideShip; insideShip = memAlloc(sizeof(InsideShip),"InsideHyper",0); insideShip->ship = ship; listAddNode(&singlePlayerGameInfo.ShipsInHyperspace,&insideShip->node,insideShip); bitSet(ship->flags,SOF_Hyperspace); if(singlePlayerGame) { // ships in hyperspace are instantly healed/refueled: //ONLY in single player game! ship->health = ship->staticinfo->maxhealth; ship->fuel = ship->staticinfo->maxfuel; } // and any ships inside as well: if (ship->shipsInsideMe != NULL) { // check ships inside too Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship2; Ship *iship; while (insidenode != NULL) { insideship2 = (InsideShip *)listGetStructOfNode(insidenode); iship = insideship2->ship; dbgAssert(iship->objtype == OBJ_ShipType); iship->health = iship->staticinfo->maxhealth; iship->fuel = iship->staticinfo->maxfuel; insidenode = insidenode->next; } } } bool NoneDoing(udword state) { Node* objnode; Ship* ship; objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship*)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && ShipCanMakeHyperspace(ship) && !(ship->flags & SOF_Disabled)) { if (ship->shipSinglePlayerGameInfo->hsState == state) { return FALSE; } } objnode = objnode->next; } return TRUE; } bool AllDoingExceptMe(udword state, Ship* me) { Node* objnode; Ship* ship; objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship*)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && ShipCanMakeHyperspace(ship) && !(ship->flags & SOF_Disabled)) { if (ship != me) { if (ship->shipSinglePlayerGameInfo->hsState != state) { return FALSE; } } } objnode = objnode->next; } return TRUE; } bool NoneDoingHS(udword state) { InsideShip* insideship; Node* objnode; Ship* ship; objnode = singlePlayerGameInfo.ShipsInHyperspace.head; while (objnode != NULL) { insideship = (InsideShip*)listGetStructOfNode(objnode); ship = insideship->ship; dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && ShipCanMakeHyperspace(ship)) { if (ship->shipSinglePlayerGameInfo->hsState == state) { return FALSE; } } objnode = objnode->next; } return TRUE; } bool AllDoingExceptMeHS(udword state, Ship* me) { InsideShip* insideship; Node* objnode; Ship* ship; objnode = singlePlayerGameInfo.ShipsInHyperspace.head; while (objnode != NULL) { insideship = (InsideShip*)listGetStructOfNode(objnode); ship = insideship->ship; dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && ShipCanMakeHyperspace(ship)) { if (ship != me) { if (ship->shipSinglePlayerGameInfo->hsState != state) { return FALSE; } } } objnode = objnode->next; } return TRUE; } void InstantlyHyperspaceOut(Ship *ship,Ship *mothership) { CalculateHyperspaceTimeForShip(ship,mothership); //disappear ... ship->shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_GONE; if ((ship->flags & SOF_Hyperspace) == 0) // extra safety for hyperspacing ships already hyperspacing { univRemoveShipFromOutside(ship); // ... into the hyperspace ether AddShipToHyperspace(ship); } } bool UpdateHyperspacingShip(Ship *ship,bool midLevel) { bool hyperspacedout = FALSE; ShipSinglePlayerGameInfo *shipSinglePlayerGameInfo = ship->shipSinglePlayerGameInfo; static sdword hsSFXHandle = -1; ship->autostabilizeship = FALSE; // ship is doing something, don't want to autostabilize ship->shipidle = FALSE; shipSinglePlayerGameInfo->midlevelHyperspaceOut = midLevel; dbgAssert(shipSinglePlayerGameInfo); if ((singlePlayerGameInfo.hyperspaceFails) && (!midLevel)) { // do effect state machine for hyperspace fizzling here switch (shipSinglePlayerGameInfo->hsState) { case HS_INACTIVE: if (universe.totaltimeelapsed >= shipSinglePlayerGameInfo->timeToHyperspace) { shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_ACCELERATINGAWAY; shipSinglePlayerGameInfo->timeToHyperspace = universe.totaltimeelapsed + HYPERSPACE_ACCELERATE_TIME; clRemoveShipFromSelection((SelectAnyCommand *)&selSelected,ship); clRemoveShipFromSelection((SelectAnyCommand *)&selSelecting,ship); // hyperspacedout = TRUE; // shipSinglePlayerGameInfo->hsState = HS_FINISHED; hsStart(ship, HYPERSPACE_SLICE_RATE, TRUE, hsShouldDisplayEffect(ship)); if (ship == universe.curPlayerPtr->PlayerMothership) { hsSFXHandle = soundEvent(ship, Ship_SinglePlayerHyperspace); } } break; case HS_SLICING_INTO: if ( midLevel || (NoneDoing(HS_POPUP_INTO) && NoneDoing(HS_INACTIVE)) ) { shipSinglePlayerGameInfo->hsState = HS_COLLAPSE_INTO; } break; case HS_COLLAPSE_INTO: if (hsSFXHandle != SOUND_NOTINITED) { if (ship == universe.curPlayerPtr->PlayerMothership) { soundEventStopSound(hsSFXHandle, 1); hsSFXHandle = SOUND_NOTINITED; } } hsUpdate(ship); break; case HS_FINISHED: hyperspacedout = TRUE; break; default: hsUpdate(ship); break; } } else switch (shipSinglePlayerGameInfo->hsState) { case HS_INACTIVE: if (universe.totaltimeelapsed >= shipSinglePlayerGameInfo->timeToHyperspace) { shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_ACCELERATINGAWAY; shipSinglePlayerGameInfo->timeToHyperspace = universe.totaltimeelapsed + HYPERSPACE_ACCELERATE_TIME; clRemoveShipFromSelection((SelectAnyCommand *)&selSelected,ship); clRemoveShipFromSelection((SelectAnyCommand *)&selSelecting,ship); hsStart(ship, HYPERSPACE_SLICE_RATE, TRUE, hsShouldDisplayEffect(ship)); if (!singlePlayerGame || (singlePlayerGame && (ship->playerowner != universe.curPlayerPtr))) { soundEvent(ship, Ship_Hyperdrive); } } break; case HS_SLICING_INTO: if ( midLevel || (NoneDoing(HS_POPUP_INTO) && NoneDoing(HS_INACTIVE)) ) { hsUpdate(ship); } break; case HS_COLLAPSE_INTO: if (ship->soundevent.hyperspaceHandle != SOUND_NOTINITED) { soundEventStopSound(ship->soundevent.hyperspaceHandle, 1); ship->soundevent.hyperspaceHandle = SOUND_NOTINITED; } if ( (!midLevel) && (ship == universe.curPlayerPtr->PlayerMothership)) { if (mrWhiteOut || AllDoingExceptMe(HS_FINISHED, ship)) { mrWhiteOut = TRUE; mrWhiteOutT = 1.0f - shipSinglePlayerGameInfo->clipt; hsUpdate(ship); } } else { hsUpdate(ship); } break; case HS_FINISHED: //disappear ... shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_GONE; if ((ship->flags & SOF_Hyperspace) == 0) // extra safety for hyperspacing ships already hyperspacing { univRemoveShipFromOutside(ship); if ((!midLevel) && (ship->playerowner != universe.curPlayerPtr)) { ship->howDidIDie = DEATH_Left_Behind_At_Hyperspace; univDeleteWipeInsideShipOutOfExistence(ship); // kill non player ships that went along for the ride } else { // ... into the hyperspace ether AddShipToHyperspace(ship); } } hyperspacedout = TRUE; break; default: hsUpdate(ship); } return hyperspacedout; } bool UpdateHyperspacingShips(void) { Node *objnode; Ship *ship; bool shipsAllHyperspacedAway = TRUE; bool result; objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); objnode = objnode->next; dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && !(ship->flags & SOF_Disabled)) { if (ShipCanMakeHyperspace(ship)) { result = UpdateHyperspacingShip(ship,FALSE); if (singlePlayerGameInfo.hyperspaceFails) { if (!result) { shipsAllHyperspacedAway = FALSE; } } else { shipsAllHyperspacedAway = FALSE; } } } } return shipsAllHyperspacedAway; } bool OrientHyperspacingShips(void) { Node *objnode; Ship *ship; vector mothershipheading; bool shipsoriented = TRUE; Ship *mothership = universe.curPlayerPtr->PlayerMothership; udword shipsNotInHyperspaceRange = 0; dbgAssert(mothership != NULL); matGetVectFromMatrixCol3(mothershipheading,mothership->rotinfo.coordsys); objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && !(ship->flags & SOF_Disabled)) { if (ShipCanHyperspace(ship)) { ship->autostabilizeship = FALSE; // ship is doing something, don't want to autostabilize ship->shipidle = FALSE; if (ship->shiptype != Mothership) { if (ship->specialFlags & SPECIAL_SinglePlayerWithinHyperSpaceRange) { //if (!(ship->specialFlags & SPECIAL_SinglePlayerInParade)) { sdword shipready=FALSE; shipready = aitrackHeadingWithBank(ship,&mothershipheading,HYPERSPACE_HEADINGACCURACY,ship->staticinfo->sinbank); if(!shipready) { singlePlayerGameInfo.rollcallShipsRemaining++; //add to count because we really aren't ready shipsNotInHyperspaceRange++; } shipsoriented &= shipready; //don't track zero velocity //if in parade...which is everyone...lets check though //aitrackSteadyShipDriftOnly(ship); } } else { shipsNotInHyperspaceRange++; if (aiuFindDistanceSquared(ship->posinfo.position,mothership->posinfo.position) < HYPERSPACE_RANGE) { CommandToDo *shipcommand = getShipAndItsCommand(&universe.mainCommandLayer,ship); if(shipcommand !=NULL) { if(shipcommand->ordertype.order == COMMAND_LAUNCHSHIP) { goto notreadyhscrud; } } bitSet(ship->specialFlags,SPECIAL_SinglePlayerWithinHyperSpaceRange); notreadyhscrud:; } else { aishipFlyToShipAvoidingObjs(ship,mothership,AISHIP_PointInDirectionFlying|AISHIP_FirstPointInDirectionFlying| AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured,0.0f); } } } } } objnode = objnode->next; } return shipsoriented; } void PutShipOutsideAt(Ship *ship,vector *createat,vector *createvelocity,real32 angle) { sdword i; collAddSpaceObjToCollBlobs((SpaceObj *)ship); listAddNode(&universe.SpaceObjList,&(ship->objlink),ship); listAddNode(&universe.ShipList,&(ship->shiplink),ship); listAddNode(&universe.ImpactableList,&(ship->impactablelink),ship); bitClear(ship->flags, SOF_Hide); ship->posinfo.position = *createat; ship->posinfo.velocity = *createvelocity; ship->rotinfo.coordsys = defaultshipmatrix; univAddObjToRenderList((SpaceObj *)ship); univRotateObjYaw((SpaceObjRot *)ship,angle); for (i = 0; i < MAX_NUM_TRAILS; i++) { if (ship->trail[i]) { trailZeroLength(ship->trail[i]); } } } void CalculateArrivingTimesForShips() { InsideShip *insideShip; Ship *ship; Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; struct ShipSinglePlayerGameInfo *shipSinglePlayerGameInfo; dbgAssert(node != NULL); while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; shipSinglePlayerGameInfo = ship->shipSinglePlayerGameInfo; dbgAssert(ship->objtype == OBJ_ShipType); if ((ship->shiptype == Mothership) && (HYPERSPACE_MOTHERSHIP_ALWAYS_ARRIVES_FIRST)) { shipSinglePlayerGameInfo->timeToDelayHyperspace = universe.totaltimeelapsed + HYPERSPACE_BASE_MOTHERSHIPARRIVETIME; } else { shipSinglePlayerGameInfo->timeToDelayHyperspace = universe.totaltimeelapsed + HYPERSPACE_BASE_ARRIVETIME + shipSinglePlayerGameInfo->timeToDelayHyperspace; } node = node->next; } } bool UpdateArrivingShip(Ship *ship,hvector *topoint,bool midLevel) { ShipSinglePlayerGameInfo *shipSinglePlayerGameInfo = ship->shipSinglePlayerGameInfo; bool thisShipArrived = FALSE; dbgAssert(shipSinglePlayerGameInfo); ship->autostabilizeship = FALSE; // ship is doing something, don't want to autostabilize ship->shipidle = FALSE; switch (shipSinglePlayerGameInfo->shipHyperspaceState) { case SHIPHYPERSPACE_GONE: { real32 forbehind; real32 rightleft; real32 updown; vector tempvec; vector createposition; vector createvelocity; real32 angle; matrix rotzmat; // calculate position based on forbehind, rightleft forbehind = shipSinglePlayerGameInfo->forbehind; rightleft = shipSinglePlayerGameInfo->rightleft; updown = shipSinglePlayerGameInfo->updown; vecSet(tempvec,forbehind,-rightleft,updown); vecSet(createvelocity,0.0f,0.0f,0.0f); angle = DEG_TO_RAD(topoint->w); matMakeRotAboutZ(&rotzmat,(real32)cos(angle),(real32)sin(angle)); matMultiplyMatByVec(&createposition,&rotzmat,&tempvec); vecAddTo(createposition,*topoint); PutShipOutsideAt(ship,&createposition,&createvelocity,angle); bitClear(ship->flags,SOF_Hyperspace); shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_DECELERATE; { if (midLevel) { shipSinglePlayerGameInfo->timeToHyperspace = 0.0f; } else { real32 waittime; if (ship == universe.curPlayerPtr->PlayerMothership) { waittime = 2.0f * HYPERSPACE_BASE_WAITTIME; } else { waittime = 3.0f * HYPERSPACE_BASE_WAITTIME + frandom(HYPERSPACE_RANDOM_WAITTIME); } shipSinglePlayerGameInfo->timeToHyperspace = universe.totaltimeelapsed + waittime; } } if ((ship == universe.curPlayerPtr->PlayerMothership) && (!midLevel)) { //FocusCommand focusone; //TransitionType trans; //focusone.numShips = 1; //focusone.ShipPtr[0] = ship; //trans = universe.mainCameraCommand.transition; //universe.mainCameraCommand.transition = CAMERA_SET; //dbgAssert(spSavedCameraYet); //ccFocus(&universe.mainCameraCommand, &focusone); //universe.mainCameraCommand.currentCameraStack->remembercam = spSavedCamera; //universe.mainCameraCommand.transition = trans; ccFocusOnPlayersMothership(&universe.mainCameraCommand,0); } } if (!midLevel) { break; } // if midLevel, deliberately fall through case SHIPHYPERSPACE_DECELERATE: if (universe.totaltimeelapsed > shipSinglePlayerGameInfo->timeToHyperspace) { vecZeroVector(ship->posinfo.velocity); shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_DONE; shipSinglePlayerGameInfo->timeToHyperspace = universe.totaltimeelapsed + HYPERSPACE_DECELERATE_TIME; hsStart(ship, HYPERSPACE_SLICE_RATE, FALSE, hsShouldDisplayEffect(ship)); if (!singlePlayerGame || (singlePlayerGame && (ship->playerowner != universe.curPlayerPtr))) { soundEvent(ship, Ship_HyperdriveOff); } } break; case SHIPHYPERSPACE_DONE: mrWhiteOutT -= 3.0f * HYPERSPACE_SLICE_RATE; if (mrWhiteOutT < 0.0f) { mrWhiteOut = FALSE; } switch (shipSinglePlayerGameInfo->hsState) { case HS_SLICING_OUTOF: if ( midLevel || (NoneDoingHS(HS_POPUP_OUTOF) && NoneDoingHS(HS_FINISHED)) ) { if(!singlePlayerGame) { CommandToDo *command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if(command != NULL) { if(command->ordertype.order == COMMAND_MP_HYPERSPACEING) { //if its a capital ship (docked ships won't have this command) if(universe.totaltimeelapsed - command->hyperSpaceingTime < TW_HYPERSPACE_TELEGRAPH_WAIT_TIME) { //ship hasn't waited long enough for incomming hs... //so don't goto noupdateonincomminghs; } } } } hsUpdate(ship); noupdateonincomminghs:; } break; case HS_COLLAPSE_OUTOF: if (ship->soundevent.hyperspaceHandle != SOUND_NOTINITED) { soundEventStopSound(ship->soundevent.hyperspaceHandle, 1); ship->soundevent.hyperspaceHandle = SOUND_NOTINITED; } if ((!midLevel) && (ship == universe.curPlayerPtr->PlayerMothership)) { if (AllDoingExceptMe(HS_INACTIVE, ship)) { hsUpdate(ship); } } else { if(!singlePlayerGame) { if(gravwellIsShipStuckForHyperspaceing(ship)) { //ship is stuck here because of a gravwell gen somewhere goto noupdateHSMP; } } hsUpdate(ship); noupdateHSMP:; } break; case HS_INACTIVE: if (universe.totaltimeelapsed > shipSinglePlayerGameInfo->timeToHyperspace) { thisShipArrived = TRUE; } break; default: hsUpdate(ship); } break; } if (thisShipArrived) { bitClear(ship->specialFlags,SPECIAL_Hyperspacing); } return thisShipArrived; } bool UpdateArrivingShips() { InsideShip *insideShip; Ship *ship; Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; bool allShipsArrived = TRUE; hvector startpoint; GetStartPointPlayer(&startpoint); dbgAssert(node != NULL); while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr)) { allShipsArrived &= UpdateArrivingShip(ship,&startpoint,FALSE); } node = node->next; } return allShipsArrived; } void GetShipsClampedToInHS(Ship *clampedto,GrowSelection *clampedships) { InsideShip *insideShip; Ship *ship; Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; // dbgAssert(node != NULL); while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); if (ship->clampInfo) { if ((ship->clampInfo->host == (SpaceObjRotImpTarg *)clampedto) && ship->shipSinglePlayerGameInfo->midlevelHyperspaceIn != TRUE) { //add ship to the ships to hs in growSelectAddShip(clampedships,ship); } } node = node->next; } } void spHyperspaceSelectionIn(SelectCommand *selection,hvector *destination) { ShipSinglePlayerGameInfo *ssinfo; sdword i; sdword numShips = selection->numShips; GrowSelection clampedShips; if (numShips < 1) { return; } for (i=0;iShipPtr[i],&clampedShips); if (clampedShips.selection->numShips > 0) { spHyperspaceSelectionIn(clampedShips.selection,destination); } growSelectClose(&clampedShips); ssinfo = selection->ShipPtr[i]->shipSinglePlayerGameInfo; ssinfo->midlevelHyperspaceIn = TRUE; ssinfo->staticHyperspaceGate = FALSE; ssinfo->midlevelHyperspaceTo = *destination; ssinfo->timeToHyperspace += spHyperspaceDelay; } } void spHyperspaceSelectionInStatic(SelectCommand *selection,hvector *destination) { ShipSinglePlayerGameInfo *ssinfo; sdword i; sdword numShips = selection->numShips; if (numShips < 1) { return; } for (i=0;iShipPtr[i]->shipSinglePlayerGameInfo; ssinfo->midlevelHyperspaceIn = TRUE; ssinfo->staticHyperspaceGate = TRUE; ssinfo->midlevelHyperspaceTo = *destination; } } void GetShipsClampedTo(Ship *clampedto,GrowSelection *clampedships) { Node *objnode = universe.ShipList.head; Ship *ship; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->clampInfo) { if ((ship->clampInfo->host == (SpaceObjRotImpTarg *)clampedto) && (!ShipInSelection(singlePlayerGameInfo.ShipsToHyperspace.selection,ship))) { growSelectAddShip(clampedships,ship); } } objnode = objnode->next; } } bool ShipAlreadyHyperspaceOut(Ship *ship) { if ((ship->flags & SOF_Hyperspace) || ShipInSelection(singlePlayerGameInfo.ShipsToHyperspace.selection,ship)) { return TRUE; } return FALSE; } void spHyperspaceSelectionOut(SelectCommand *selection) { Ship *ship; Ship *defactoMothership; sdword i; sdword numShips = selection->numShips; GrowSelection clampedShips; if (numShips < 1) { return; } defactoMothership = selection->ShipPtr[0]; for (i=0;iShipPtr[i]; if (!ShipAlreadyHyperspaceOut(ship)) { if (ship->attributes & ATTRIBUTES_StartInHS) { InstantlyHyperspaceOut(ship,defactoMothership); bitClear(ship->attributes, ATTRIBUTES_StartInHS); } else { CalculateHyperspaceTimeForShip(ship,defactoMothership); ship->shipSinglePlayerGameInfo->timeToHyperspace += spHyperspaceDelay; if(ship->clampInfo != NULL) { //ship is clamped to another ship... if(ship->clampInfo->host->objtype == OBJ_ShipType) { if( ((Ship *)ship->clampInfo->host)->specialFlags & SPECIAL_Hyperspacing) { //host has been told to hyperspace //so lets set the clamped ships hyperspace time, to the same time as its hose ship->shipSinglePlayerGameInfo->timeToHyperspace = ((Ship *)ship->clampInfo->host)->shipSinglePlayerGameInfo->timeToHyperspace; } } } growSelectAddShip(&singlePlayerGameInfo.ShipsToHyperspace,ship); } growSelectInit(&clampedShips); GetShipsClampedTo(ship,&clampedShips); if (clampedShips.selection->numShips > 0) { spHyperspaceSelectionOut(clampedShips.selection); } growSelectClose(&clampedShips); } #ifndef HW_Release else { char warningmsg[200]; sprintf(warningmsg,"Hyperspaced %s tried to hyperspace",ShipTypeToStr(ship->shiptype)); clCommandMessage(warningmsg); } #endif } } void spHyperspaceSelectionOutStatic(SelectCommand *selection) { Ship *ship; Ship *defactoMothership; sdword i; sdword numShips = selection->numShips; GrowSelection clampedShips; if (numShips < 1) { return; } defactoMothership = selection->ShipPtr[0]; for (i=0;iShipPtr[i]; if (!ShipAlreadyHyperspaceOut(ship)) { growSelectInit(&clampedShips); GetShipsClampedTo(ship,&clampedShips); if (clampedShips.selection->numShips > 0) { spHyperspaceSelectionOutStatic(clampedShips.selection); } growSelectClose(&clampedShips); if (ship->attributes & ATTRIBUTES_StartInHS) { InstantlyHyperspaceOut(ship,defactoMothership); } else { ShipSinglePlayerGameInfo* ssinfo = ship->shipSinglePlayerGameInfo; if (ssinfo != NULL) { ssinfo->staticHyperspaceGate = TRUE; } CalculateHyperspaceTimeForShip(ship,defactoMothership); growSelectAddShip(&singlePlayerGameInfo.ShipsToHyperspace,ship); } } #ifndef HW_Release else { char warningmsg[200]; sprintf(warningmsg,"Hyperspaced %s tried to hyperspace",ShipTypeToStr(ship->shiptype)); clCommandMessage(warningmsg); } #endif } } void singlePlayerInit(void) { if (singlePlayerGame) { warpLevel = WarpToLevelEnabled(); if (warpLevel) { singlePlayerGameInfo.currentMission = warpLevel; } else { singlePlayerGameInfo.currentMission = 1; } } singlePlayerGameInfo.hyperspaceState = NO_HYPERSPACE; singlePlayerGameInfo.resourceUnitsCollected = 0; singlePlayerGameInfo.giveComputerFleetControl = FALSE; singlePlayerGameInfo.asteroid0sCanMove = FALSE; singlePlayerGameInfo.hyperspaceFails = FALSE; singlePlayerGameLoadNewLevelFlag = FALSE; singlePlayerGameInfo.playerCanHyperspace = FALSE; smUpdateHyperspaceStatus(FALSE); listInit(&singlePlayerGameInfo.ShipsInHyperspace); growSelectInit(&singlePlayerGameInfo.ShipsToHyperspace); if (singlePlayerGame) { objectiveStartup(); kasInit(); } HYPERSPACE_FARCLIPPLANESQR = HYPERSPACE_FARCLIPPLANE * HYPERSPACE_FARCLIPPLANE; HYPERSPACE_DECELERATE_DIST = 0.5f * HYPERSPACE_DECELERATION * HYPERSPACE_DECELERATE_TIME*HYPERSPACE_DECELERATE_TIME; HYPERSPACE_DECELERATE_INITIAL_VEL = HYPERSPACE_DECELERATION * HYPERSPACE_DECELERATE_TIME; dbgAssert(HYPERSPACE_DECELERATE_DIST < HYPERSPACE_FARCLIPPLANE); HYPERSPACE_WHIPTOWARDSDIST = HYPERSPACE_FARCLIPPLANE - HYPERSPACE_DECELERATE_DIST; dbgAssert(HYPERSPACE_DECELERATE_INITIAL_VEL < HYPERSPACE_WHIP_VELOCITY); } static udword spLockoutFlags = 0; void spMainScreen() { if (smSensorsActive) { smSensorsCloseForGood(); } cmCloseIfOpen(); rmCloseIfOpen(); lmCloseIfOpen(); feAllMenusDelete(); //make sure no menus stay up during NIS tbForceTaskbar(FALSE); //make sure taskbar is down during NIS } void spLockout(udword flags) { spLockoutFlags = flags; //comment out the rest of this function to use NIS debugging keys #if NIS_PRINT_INFO if (nisNoLockout) { return; } #endif if (flags & SPLOCKOUT_DESELECT) { selSelectNone(); } smSensorsDisable = TRUE; tbDisable = TRUE; if (flags & SPLOCKOUT_MR) { mrDisable(); } if (flags & SPLOCKOUT_MOUSE) { mouseDisable(); } } void spMainScreenAndLockout(udword flags) { spMainScreen(); spLockout(flags); } void spUnlockout() { //comment out the rest of this function to use NIS debugging keys smSensorsDisable = FALSE; tbDisable = FALSE; if (spLockoutFlags & SPLOCKOUT_DESELECT) { bitClear(spLockoutFlags,SPLOCKOUT_DESELECT); } if (spLockoutFlags & SPLOCKOUT_MR) { mrEnable(); bitClear(spLockoutFlags,SPLOCKOUT_MR); } if (spLockoutFlags & SPLOCKOUT_MOUSE) { mouseEnable(); bitClear(spLockoutFlags,SPLOCKOUT_MOUSE); } } void spHyperspaceButtonPushed(void) { dbgAssert(singlePlayerGame); if (!singlePlayerGameInfo.playerCanHyperspace && !hyperspaceOverride) { return; } hyperspaceOverride = FALSE; singlePlayerGameInfo.playerCanHyperspace = FALSE; smUpdateHyperspaceStatus(FALSE); if (singlePlayerGameInfo.hyperspaceState != NO_HYPERSPACE) { return; } if (universe.curPlayerPtr->PlayerMothership == NULL) { return; } if (universePause) { universePause = FALSE; // unpause game } ccFocusOnMyMothership(&universe.mainCameraCommand); singlePlayerGameInfo.hyperspaceState = HYPERSPACE_WAITINGROLLCALL; singlePlayerGameInfo.oldAutoLaunch = universe.curPlayerPtr->autoLaunch; // restore autolaunch flag after hyperspace clAutoLaunch(FALSE,universe.curPlayerIndex); TellShipsToDockForHyperspace(); TellHyperspacingShipsToReady(); spMainScreenAndLockout(SPLOCKOUT_MR); HyperspaceRollCallBegin(ghMainRegion,0,0,0); } void spTaskBarHyperspaceCB(struct taskbutton *button, ubyte *userData) { spHyperspaceButtonPushed(); } void singlePlayerPostInit(bool loadingSaveGame) { if (!loadingSaveGame) { if (singlePlayerGameInfo.currentMission == 1) { animBinkPlay(0, 1); } } //hyperspacetbbutton = tbButtonCreate("Hyperspace",spTaskBarHyperspaceCB,(ubyte *)0x00,0); //tbButtonListRefresh(); } void singlePlayerClose(void) { //dbgAssert(hyperspacetbbutton); //tbButtonDelete(hyperspacetbbutton); tbButtonListRefresh(); kasClose(); if (spHyperspaceRollCallHandle) { feScreensDelete(spHyperspaceRollCallHandle); spHyperspaceRollCallHandle = NULL; } smSensorWeirdness = FALSE; // turns on Fleet command again, if it's been turned off soundEventSetActorFlag(ACTOR_FLEETCOMMAND_FLAG, TRUE); #if SP_DEBUGLEVEL2 mrKASDebugDrawStates = FALSE; mrKASDebugDrawTimers = FALSE; mrKASDebugDrawVars = FALSE; mrKASDebugDrawVolumes = FALSE; mrKASSkipFactor = 0; #endif } void singlePlayerSetMissionAttributes(char *directory,char *filename) { singlePlayerGameInfo.onCompleteHyperspace = TRUE; // default to TRUE singlePlayerGameInfo.onLoadPlayNIS = 0; singlePlayerGameInfo.onCompletePlayNIS = 0; singlePlayerGameInfo.resourceUnitsCollected = 0; if (universe.players[0].race == R1) { universe.players[1].race = R2; // default to enemy race } else { universe.players[1].race = R1; // default to enemy race } scriptSet(directory,filename,SinglePlayerScriptTable); //reset nebulae nebReset(); } void singlePlayerLevelLoaded(void) { if (singlePlayerGameInfo.currentMission == 10) { Node *objnode; Ship *ship; objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); objnode = objnode->next; dbgAssert(ship->objtype == OBJ_ShipType); if ((ship->playerowner->playerIndex == 1) && (ship->shiptype == Carrier)) { bitSet(ship->specialFlags2,SPECIAL_2_DontPlowThroughEnemyShips); } } } } void CleanupDamageEffects(void) { InsideShip* insideShip; Ship* ship; Node* node = singlePlayerGameInfo.ShipsInHyperspace.head; while (node != NULL) { insideShip = (InsideShip*)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); node = node->next; dmgForgetEffects(ship); } } void CleanupHyperspacingShip(Ship *ship) { dbgAssert(ship->shipSinglePlayerGameInfo); ship->shipSinglePlayerGameInfo->shipHyperspaceState = SHIPHYPERSPACE_NONE; } void CleanupHyperspacingShipsThatDidntHyperspace() { Node *objnode; Ship *ship; objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); objnode = objnode->next; dbgAssert(ship->objtype == OBJ_ShipType); if (allianceIsShipAlly(ship,universe.curPlayerPtr) && !(ship->flags & SOF_Disabled)) { if (ShipCanMakeHyperspace(ship)) { CleanupHyperspacingShip(ship); } } } } void CleanupHyperspacingShips(bool FreeShips,bool cleanupAllPlayersShips) { InsideShip *insideShip; Ship *ship; Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; Node *deletenode; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); deletenode = node; node = node->next; if (cleanupAllPlayersShips || allianceIsShipAlly(ship,universe.curPlayerPtr)) { listDeleteNode(deletenode); // put before so univFreeShipContents doesn't try to delete this twice if ((FreeShips) && (ship->shipSinglePlayerGameInfo->shipHyperspaceState == SHIPHYPERSPACE_GONE)) { univFreeShipContents(ship); memFree(ship); } else { CleanupHyperspacingShip(ship); } } } if (singlePlayerGameInfo.hyperspaceFails) { CleanupHyperspacingShipsThatDidntHyperspace(); } } void singlePlayerShipDied(Ship *ship) { growSelectRemoveShip(&singlePlayerGameInfo.ShipsToHyperspace,ship); if (singlePlayerGameInfo.ShipsInHyperspace.num) { InsideShip *insideShip; Ship *checkship; Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); checkship = insideShip->ship; dbgAssert(checkship->objtype == OBJ_ShipType); if (checkship == ship) { listDeleteNode(node); break; } node = node->next; } } } void singlePlayerGameCleanup(void) { if (!singlePlayerGame) { CleanupHyperspacingShips(TRUE,TRUE); return; } // delete all objectives objectiveShutdown(); // remove ship lists, paths, volumes, etc. kasLabelledEntitiesDestroy(); pingAllPingsDelete(); if (singlePlayerGameLoadNewLevelFlag) { return; } spUnlockout(); CleanupHyperspacingShips(TRUE,TRUE); if (singlePlayerGameInfo.hyperspaceState != NO_HYPERSPACE) { if (spHyperspaceRollCallScreen != NULL) { feScreenDelete(spHyperspaceRollCallScreen); spHyperspaceRollCallScreen = NULL; } ccClearModeFlag(&universe.mainCameraCommand, CCMODE_LOCK_OUT_USER_INPUT | CCMODE_FOCUS_CHANGES_LOOKATPOINT_ONLY | CCMODE_FOCUS_CHANGES_NOTHING); mrWhiteOut = FALSE; mrWhiteOutT = 0.0f; } } void UpdateMidLevelHyperspacingShips() { bool addedShipToHyperspace = FALSE; sdword i; // update any mid-level hyperspacing away ships for (i=0;inumShips; ) { if (UpdateHyperspacingShip(singlePlayerGameInfo.ShipsToHyperspace.selection->ShipPtr[i],TRUE)) { addedShipToHyperspace = TRUE; growSelectRemoveShipIndex(&singlePlayerGameInfo.ShipsToHyperspace,i); // deliberate don't increment i } else { i++; } } // update any mid-level hyperspacing in ships { Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; Node *nextnode; InsideShip *insideShip; Ship *ship; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); nextnode = node->next; if ((ship->attributes & ATTRIBUTES_DeleteAfterHSOut) && (ship->flags & SOF_Hyperspace)) { ship->howDidIDie = DEATH_Left_Behind_At_Hyperspace; univDeleteWipeInsideShipOutOfExistence(ship); // will wipe out of ShipsInHyperspace list } else if (ship->shipSinglePlayerGameInfo->midlevelHyperspaceIn) { if (UpdateArrivingShip(ship,&ship->shipSinglePlayerGameInfo->midlevelHyperspaceTo,TRUE)) { ship->shipSinglePlayerGameInfo->midlevelHyperspaceIn = FALSE; CleanupHyperspacingShip(ship); listDeleteNode(node); } } node = nextnode; } } } void singlePlayerGameUpdate() { AIVar *tgui; AIVar *nisVariable = aivarFindAnyFSM("PlayNis"); AIVar *nisletVariable = aivarFindAnyFSM("PlayNislet"); sdword value; static real32 hyperspaceStartTime; #ifdef gshaw if (triggerNIS) { hvector startpoint; char *nis, *script; bool centre; GetStartPointPlayer(&startpoint); singlePlayerNISNamesGet(&nis, &script, ¢re, 2); singlePlayerStartNis(nis, script, centre, &startpoint); triggerNIS = FALSE; } #endif //play an nis when the time is right if (nisVariable) { value = aivarValueGet(nisVariable); if (value != 0) { hvector startpoint; char *nis, *script; bool centre; GetStartPointPlayer(&startpoint); singlePlayerNISNamesGet(&nis, &script, ¢re, value); singlePlayerStartNis(nis, script, centre, &startpoint); } aivarDestroy(nisVariable); } //play an nislet when the time is right if (nisletVariable) { value = aivarValueGet(nisletVariable); if (value != 0) { hvector startpoint; char *nis, *script; GetStartPointPlayer(&startpoint); singlePlayerNISletNamesGet(&nis, &script, value); singlePlayerStartNis(nis, script, FALSE, &startpoint); } aivarDestroy(nisletVariable); } //pause or unpause the NIS task. This has to be called from within a task, //not the loading code, in case the player alt-tabs during loading. //Can you think of a better spot for it? nisTaskPauseOrResume(); UpdateMidLevelHyperspacingShips(); if (universe.curPlayerPtr->PlayerMothership == NULL) { if (singlePlayerGameInfo.hyperspaceState == HYPERSPACE_WAITINGROLLCALL) { spAbortHyperspaceCB(NULL,NULL); } return; } tgui = aivarFind("Traders.PopUpGUI"); if (tgui) { if (tgui->value) { tgui->value = 0; tmTradeBegin(ghMainRegion, 0, 0, 0); } } switch (singlePlayerGameInfo.hyperspaceState) { case HYPERSPACE_WAITINGROLLCALL: CalculateRollCall(); OrientHyperspacingShips(); if (singlePlayerGameInfo.rollcallShipsRemaining == 0) { if (spHyperspaceRollCallScreen != NULL) { feScreenDelete(spHyperspaceRollCallScreen); spHyperspaceRollCallScreen = NULL; } //spHyperspaceCommence(); // moved code from this function to here // singlePlayerGameInfo.hyperspaceState = HYPERSPACE_SHIPSLEAVING; singlePlayerGameInfo.hyperspaceState = HYPERSPACE_WAITINGFORFLEETCOMMAND; singlePlayerGameInfo.hyperspaceSubState = FOCUSING_ON_MOTHERSHIP; singlePlayerGameInfo.hyperspaceTimeStamp = universe.totaltimeelapsed; // + HYPERSPACE_WAIT_FOR_MOTHERSHIPFOCUS_TIME; speechEventFleet(STAT_F_Hyper_TakingOff, 0, universe.curPlayerIndex); // this isn't the nicest way to do this, but it works. I'd rather write a callback for this hyperspaceStartTime = universe.totaltimeelapsed + SPEECH_HYPERSPACE_DELAY; // soundEventStopTrack(SongNumber, SPEECH_HYPERSPACE_DELAY + 1.5f); } break; case HYPERSPACE_WAITINGFORFLEETCOMMAND: // this isn't the nicest way to do this, but it works. I'd rather write a callback for this if (universe.totaltimeelapsed >= hyperspaceStartTime) { singlePlayerGameInfo.hyperspaceState = HYPERSPACE_SHIPSLEAVING; } break; case HYPERSPACE_SHIPSLEAVING: if (singlePlayerGameInfo.hyperspaceSubState == FOCUSING_ON_MOTHERSHIP) { bool shipsoriented = OrientHyperspacingShips(); if ((universe.totaltimeelapsed >= singlePlayerGameInfo.hyperspaceTimeStamp) && (shipsoriented)) { CalculateHyperspaceTimesForShips(); singlePlayerGameInfo.hyperspaceSubState = LOCKED_FOCUS_FOR_HOP; tutGameMessage("Game_BeginHyperspace"); // ccSetModeFlag(&universe.mainCameraCommand, CCMODE_FOCUS_CHANGES_NOTHING); // spSavedCameraYet = FALSE; if (!singlePlayerGameInfo.hyperspaceFails) { soundEvent(universe.curPlayerPtr->PlayerMothership, Ship_SinglePlayerHyperspace); soundEventStopTrack(musicEventCurrentTrack(), 1.5f); } } } else if (singlePlayerGameInfo.hyperspaceSubState == LOCKED_FOCUS_FOR_HOP) { if (UpdateHyperspacingShips()) { if (singlePlayerGameInfo.hyperspaceFails) { singlePlayerGameInfo.hyperspaceState = HYPERSPACE_FAILED; goto hyperspacefailed; } // all ships have hyperspaced out. spBinkPlay = TRUE; singlePlayerGameInfo.hyperspaceState = HYPERSPACE_WHITEOUT; singlePlayerGameInfo.hyperspaceTimeStamp = universe.totaltimeelapsed + HYPERSPACE_WHITEOUTTIME; speechEventStopAll(0.5f); // screen should fade out mrWhiteOut = TRUE; mrWhiteOutT = 1.0f; // save camera //if (!spSavedCameraYet) //{ // Ship* mo = universe.curPlayerPtr->PlayerMothership; // spSavedCamera = universe.mainCameraCommand.currentCameraStack->remembercam; // vecSubFrom(spSavedCamera.eyeposition, mo->posinfo.position); // vecSubFrom(spSavedCamera.lookatpoint, mo->posinfo.position); //spSavedCameraYet = TRUE; //} } } break; case HYPERSPACE_WHITEOUT: if (universe.totaltimeelapsed >= singlePlayerGameInfo.hyperspaceTimeStamp) { if (spBinkPlay) { #if defined(OEM) //no animatic between OEM Missions 4 and 5 and thereafter if (singlePlayerGameInfo.currentMission < 4) #endif //playback level transition animatic animBinkPlay(singlePlayerGameInfo.currentMission, singlePlayerGameInfo.currentMission + 1); #if defined(CGW) if (singlePlayerGameInfo.currentMission == 2) { universe.quittime = universe.totaltimeelapsed; utyPlugScreens = TRUE; } #elif defined(OEM) if (singlePlayerGameInfo.currentMission == 5) { universe.quittime = universe.totaltimeelapsed; utyPlugScreens = TRUE; } #else if (singlePlayerGameInfo.currentMission == 16) { universe.quittime = universe.totaltimeelapsed; utyCreditsSequence = TRUE; } #endif spBinkPlay = FALSE; mrWhiteOut = FALSE; } else { if (!binkDonePlaying) { break; } // clear damage effects from ships CleanupDamageEffects(); // rebuild level now singlePlayerGameLoadNewLevelFlag = TRUE; singlePlayerHyperspacingInto = TRUE; // singlePlayerGameInfo.hyperspaceState = HYPERSPACE_SHIPSARRIVING; singlePlayerGameInfo.hyperspaceState = HYPERSPACE_LOADDONE; } } break; case HYPERSPACE_LOADDONE: soundEvent(universe.curPlayerPtr->PlayerMothership, Ship_SinglePlayerHyperspace); // speechEventFleet(STAT_F_HyperspaceInterrupted, 0, universe.curPlayerIndex); singlePlayerGameInfo.hyperspaceState = HYPERSPACE_SHIPSARRIVING; break; case HYPERSPACE_SHIPSARRIVING: if (UpdateArrivingShips()) { // all ships have arrived and are in starting position singlePlayerHyperspacingInto = FALSE; singlePlayerGameInfo.hyperspaceState = NO_HYPERSPACE; CleanupHyperspacingShips(FALSE,FALSE); //ccClearModeFlag(&universe.mainCameraCommand,CCMODE_LOCK_OUT_USER_INPUT); // ccFocusOnFleetSmooth(&universe.mainCameraCommand); spUnlockout(); //launch all necessary ships first LaunchAllInternalShipsOfPlayerThatMustBeLaunched(universe.curPlayerPtr); clAutoLaunch(singlePlayerGameInfo.oldAutoLaunch,universe.curPlayerIndex); /* if (!singlePlayerGameInfo.oldAutoLaunch) { // auto launch not set to true, therefore manually launch all ships that must be: LaunchAllInternalShipsOfPlayerThatMustBeLaunched(universe.curPlayerPtr); } */ if (singlePlayerGameInfo.onLoadPlayNIS <= 0) { soundEventPlayMusic(SongNumber); } singlePlayerKasMissionStart(singlePlayerGameInfo.currentMission); aiplayerGameStart(universe.players[1].aiPlayer); if (singlePlayerGameInfo.onLoadPlayNIS > 0) { hvector startpoint; char *nis, *script; bool centre; GetStartPointPlayer(&startpoint); singlePlayerNISNamesGet(&nis, &script, ¢re, singlePlayerGameInfo.onLoadPlayNIS); singlePlayerStartNis(nis, script, centre, &startpoint); } } break; hyperspacefailed: case HYPERSPACE_FAILED: { // all ships have arrived and are in starting position singlePlayerGameInfo.hyperspaceState = NO_HYPERSPACE; CleanupHyperspacingShips(FALSE,FALSE); spUnlockout(); //launch all necessary ships first LaunchAllInternalShipsOfPlayerThatMustBeLaunched(universe.curPlayerPtr); clAutoLaunch(singlePlayerGameInfo.oldAutoLaunch,universe.curPlayerIndex); tutGameMessage("Game_HyperspaceFailed"); singlePlayerGameInfo.hyperspaceFails = FALSE; } break; } } static void SetOnMissionCompleteInfo(char *directory,char *field,void *dataToFillIn) { char str[30]; SinglePlayerGameInfo *info = (SinglePlayerGameInfo *)dataToFillIn; RemoveCommasFromString(field); sscanf(field,"%d %s",&info->onCompletePlayNIS,str); if (strcmp(str,"NOHYPERSPACE") == 0) { info->onCompleteHyperspace = FALSE; } else { info->onCompleteHyperspace = TRUE; } } void DeleteAllRemainingShips() { Ship *ship; Node *node = universe.ShipList.head; while (node != NULL) { ship = listGetStructOfNode(node); ship->howDidIDie = DEATH_Left_Behind_At_Hyperspace; univReallyDeleteThisShipRightNow(ship); node = universe.ShipList.head; // walk the list like this because univReallyDeleteThisShipRightNow is always // modifying the list of ships as it deletes stuff } } void DeleteAnyMidLevelHyperspacingShips() { Node *node = singlePlayerGameInfo.ShipsInHyperspace.head; Node *nextnode; InsideShip *insideShip; Ship *ship; while (node != NULL) { insideShip = (InsideShip *)listGetStructOfNode(node); ship = insideShip->ship; dbgAssert(ship->objtype == OBJ_ShipType); nextnode = node->next; if (ship->shipSinglePlayerGameInfo->midlevelHyperspaceOut) { ship->howDidIDie = DEATH_Left_Behind_At_Hyperspace; univDeleteWipeInsideShipOutOfExistence(ship); // will wipe out of ShipsInHyperspace list } node = nextnode; } } void singlePlayerLoadNewLevel(void) { real32 savephystime = universe.phystimeelapsed; real32 savetottime= universe.totaltimeelapsed; Ship *mothership = universe.players[0].PlayerMothership; DeleteAllRemainingShips(); DeleteAnyMidLevelHyperspacingShips(); rmResetStaticInfo(); universeReset(); univupdateReset(); universe.phystimeelapsed = savephystime; universe.totaltimeelapsed = savetottime; universe.players[0].PlayerMothership = mothership; // restart computer AI player dbgAssert(universe.players[1].aiPlayer == NULL); universe.players[1].aiPlayer = aiCurrentAIPlayer = aiplayerInit(&universe.players[1],AI_ADV); aiCurrentAIPlayer->primaryEnemyPlayer = &universe.players[0]; // remove sensor weirdness if it's on kasfSensorsWeirdness(FALSE); #ifndef HW_Release if (SINGLEPLAYER_DEBUGLEVEL) { singlePlayerGameInfo.currentMission = SINGLEPLAYER_DEBUGLEVEL; SINGLEPLAYER_DEBUGLEVEL = 0; } else #endif singlePlayerGameInfo.currentMission++; // go to next level singlePlayerGameInfo.giveComputerFleetControl = FALSE; // reset any computer control singlePlayerGameInfo.asteroid0sCanMove = FALSE; GetMissionsDirAndFile(singlePlayerGameInfo.currentMission); //what mission file to load? //... start of crazy starting code from utility.c //reset the enemy player's race to the 'opposite' (R1/R2 race) universe.players[1].race = (universe.players[0].race == R1) ? R2 : R1; //!!! should we reset the other things like RU's and OnMissionLoad stuff? horseRaceInit(); levelPreInit(spMissionsDir,spMissionsFile); //see what ships need to be loaded and what ships we can toss // pause sound engine soundEventPause(TRUE); //load in all the ships, register all the textures universeStaticInit(); if (hrAbortLoadingGame) { goto abortload; } trailsRecolorize(); mistrailsRecolorize(); SetFleetModifier(singlePlayerGameInfo.currentMission, GetFleetStrengthHyperspace); levelStartNext(spMissionsDir,spMissionsFile); clPresetShipsToPosition(&universe.mainCommandLayer); singlePlayerLevelLoaded(); etgReloadReset(); trRegistryRefresh(); //refresh registry - load in all textures //if (hrAbortLoadingGame) goto abortload; abortload: //start the sound engine playing again soundEventPause(FALSE); horseRaceShutdown(); //... end of crazy starting code from utility.c if (!hrAbortLoadingGame) { //ccSetModeFlag(&universe.mainCameraCommand,CCMODE_LOCK_OUT_USER_INPUT); univUpdateRenderList(); CalculateArrivingTimesForShips(); } bobInitProperties(); singlePlayerGameLoadNewLevelFlag = FALSE; } Ship *FindPlayersMothership(Player *player) { Node *objnode; Ship *ship; objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { if (ship->shiptype == Mothership) { return ship; } } objnode = objnode->next; } return NULL; } SelectCommand *GetAllPlayersShipsExceptMothership(Player *player) { Node *objnode; Ship *ship; SelectCommand *selection; sdword numShips = 0; selection = memAlloc(sizeofSelectCommand(universe.ShipList.num),"ToDoSelection",0); objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { if (ship->shiptype != Mothership) { selection->ShipPtr[numShips++] = ship; } } objnode = objnode->next; } selection->numShips = numShips; return selection; } #define NUMBER_SINGLEPLAYER_NIS 12 //Note: single player order has been changed. The Previous mission 3 // where the player meets the traders and fights the Turanic raiders // has been moved to mission 4. Previous mission 4 (capture the // enemy ship, see the destruction of your homeworld) now happens // in mission 3 with a new background (you fly back to your homeworld). // This explains the weird ordering of the NISs //Note2:In addition, the previous mission 15 (mining base) has been moved // to Mission 14, with the Headshot asteroid level (old mission 14) // is now Mission 15. The only change this effects is the nislet names char *nisR1Names[NUMBER_SINGLEPLAYER_NIS][2] = { { "nis\\n01r1.nis", "nis\\n01r1.script" }, #if defined (Downloadable) { "nis\\n02.nis", "nis\\n02-demo.script" }, #else { "nis\\n02.nis", "nis\\n02.script" }, #endif { "nis\\n04.nis", "nis\\n04.script" }, { "nis\\n03r1.nis", "nis\\n03r1.script" }, { "nis\\n05r1.nis", "nis\\n05r1.script" }, { "nis\\n06.nis", "nis\\n06.script" }, { "nis\\n07.nis", "nis\\n07.script" }, { "nis\\n08r1a.nis", "nis\\n08r1a.script" }, { "nis\\n09.nis", "nis\\n09.script" }, { "nis\\n10r1.nis", "nis\\n10r1.script" }, { "nis\\n11r1.nis", "nis\\n11r1.script" }, { "nis\\n12r1.nis", "nis\\n12r1.script" }, }; char *singlePlayerNISName = NULL; //See note above regarding the weird NIS ordering char *nisR2Names[NUMBER_SINGLEPLAYER_NIS][2] = { { "nis\\n01r2.nis", "nis\\n01r2.script" }, { "nis\\n02.nis", "nis\\n02.script" }, { "nis\\n04.nis", "nis\\n04.script" }, { "nis\\n03r2.nis", "nis\\n03r2.script" }, { "nis\\n05r2.nis", "nis\\n05r2.script" }, { "nis\\n06.nis", "nis\\n06.script" }, { "nis\\n07.nis", "nis\\n07.script" }, { "nis\\n08r2a.nis", "nis\\n08r2a.script" }, { "nis\\n09.nis", "nis\\n09.script" }, { "nis\\n10r2.nis", "nis\\n10r2.script" }, { "nis\\n11r2.nis", "nis\\n11r2.script" }, { "nis\\n12r2.nis", "nis\\n12r2.script" }, }; #define NISLETS_PER_LEVEL 3 typedef struct { char *name[NISLETS_PER_LEVEL]; bool8 raceSpecific[NISLETS_PER_LEVEL]; } nisletnames; //see note above nisR1Names regarding nis\m03 being in mission 4 //in addition, Missions 14 and 15 were swapped nisletnames nisletNames[NUMBER_SINGLEPLAYER_MISSIONS] = { {{NULL, NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m02a", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{NULL, NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m03", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{NULL, NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m06a", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m07a", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m08a", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m09a", "nis\\m09b", "nis\\m06a"},{FALSE,FALSE,FALSE}}, {{"nis\\m10a", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m11a", NULL, NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m12a", "nis\\m12b", NULL}, {FALSE,TRUE, FALSE}}, {{"nis\\m13a", NULL, "nis\\m13c"}, {FALSE,FALSE,TRUE }}, {{"nis\\m15a", "nis\\m15b", NULL}, {FALSE,FALSE,FALSE}}, {{"nis\\m14a", NULL, NULL}, {TRUE, FALSE,FALSE}}, {{"nis\\m16a", "nis\\m16a", "nis\\m16a"},{FALSE,FALSE,TRUE }}, {{NULL, NULL, NULL}, {FALSE,FALSE,FALSE}}, }; bool8 nisPlayRelativeToMothership[NUMBER_SINGLEPLAYER_NIS] = { FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }; void *WatchFunctionAddress(sdword i) { switch (i) { case 0: return &Watch_Mission01; case 1: return &Watch_Mission02; case 2: return &Watch_Mission03; case 3: return &Watch_Mission04; #ifdef OEM case 4: return &Watch_Mission05_OEM; #else case 4: return &Watch_Mission05; case 5: return &Watch_Mission06; case 6: return &Watch_Mission07; case 7: return &Watch_Mission08; case 8: return &Watch_Mission09; case 9: return &Watch_Mission10; case 10: return &Watch_Mission11; case 11: return &Watch_Mission12; case 12: return &Watch_Mission13; case 13: return &Watch_Mission14; case 14: return &Watch_Mission15; case 15: return &Watch_Mission16; #endif case 16: return &Watch_Tutorial1; } return NULL; } sdword WatchFunctionToIndex(KASWatchFunction watchFunction) { sdword i; for (i=0;i= 0); dbgAssert(index < NUMBER_SINGLEPLAYER_MISSIONS); return WatchFunctionAddress(index); } //nisplaying *singlePlayerNISPlaying = NULL; sdword singlePlayerNisNumber = -1; sdword singlePlayerNisletNumber = -1; void singlePlayerKasMissionStart(sdword missionnum) { switch (missionnum) { case 1: kasMissionStart("mission01", Init_Mission01, Watch_Mission01); break; case 2: kasMissionStart("mission02", Init_Mission02, Watch_Mission02); break; case 3: kasMissionStart("mission03", Init_Mission03, Watch_Mission03); break; case 4: kasMissionStart("mission04", Init_Mission04, Watch_Mission04); break; #ifdef OEM case 5: kasMissionStart("mission05_OEM", Init_Mission05_OEM, Watch_Mission05_OEM); break; #else case 5: kasMissionStart("mission05", Init_Mission05, Watch_Mission05); break; case 6: kasMissionStart("mission06", Init_Mission06, Watch_Mission06); break; case 7: kasMissionStart("mission07", Init_Mission07, Watch_Mission07); break; case 8: kasMissionStart("mission08", Init_Mission08, Watch_Mission08); break; case 9: kasMissionStart("mission09", Init_Mission09, Watch_Mission09); break; case 10: kasMissionStart("mission10", Init_Mission10, Watch_Mission10); break; case 11: kasMissionStart("mission11", Init_Mission11, Watch_Mission11); break; case 12: kasMissionStart("mission12", Init_Mission12, Watch_Mission12); break; case 13: kasMissionStart("mission13", Init_Mission13, Watch_Mission13); break; case 14: kasMissionStart("mission14", Init_Mission14, Watch_Mission14); break; case 15: kasMissionStart("mission15", Init_Mission15, Watch_Mission15); break; case 16: kasMissionStart("mission16", Init_Mission16, Watch_Mission16); break; #endif default: dbgFatalf(DBG_Loc,"\nUnsupported mission #%d",missionnum); break; } } bool hyperspaceAtEndOfSinglePlayerNIS = FALSE; void RotatePlayersFleetAbout(Player *player,vector about,real32 deg) { Node *objnode; Ship *ship; vector position; vector rotatedposition; matrix rotzmat; real32 rot = DEG_TO_RAD(deg); objnode = universe.ShipList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { vecSub(position,ship->posinfo.position,about); matMakeRotAboutZ(&rotzmat,(real32)cos(rot),(real32)sin(rot)); matMultiplyMatByVec(&rotatedposition,&rotzmat,&position); vecAdd(ship->posinfo.position,about,rotatedposition); univRotateObjYaw((SpaceObjRot *)ship,rot); } objnode = objnode->next; } } /*----------------------------------------------------------------------------- Name : FindShipsOfShipTypeOfPlayer Description : finds ships of shiptype and owned by player, puts them in growselect Inputs : growselect, shiptype, player Outputs : Return : ----------------------------------------------------------------------------*/ void FindShipsOfShipTypeOfPlayer(GrowSelection *growselect,ShipType shiptype,Player *player) { Node *node; Ship *ship; for (node = universe.ShipList.head; node != NULL; node = node->next) { ship = (Ship *)listGetStructOfNode(node); dbgAssert(ship->objtype == OBJ_ShipType); if ((ship->playerowner == player) && (ship->shiptype == shiptype)) { growSelectAddShip(growselect,ship); } } } void singlePlayerNisStoppedCB(void) { bool savefirstlevel = FALSE; AIVar *var; char bufferstr[30]; switch (singlePlayerNisNumber) { case 1: { GrowSelection growselect; universe.curPlayerPtr->PlayerMothership = FindPlayersMothership(universe.curPlayerPtr); dbgAssert(universe.curPlayerPtr->PlayerMothership != NULL); growSelectInit(&growselect); FindShipsOfShipTypeOfPlayer(&growselect,LightInterceptor,universe.curPlayerPtr); if (growselect.selection->numShips > 0) { clFormation(&universe.mainCommandLayer,growselect.selection,CUSTOM_FORMATION); } growSelectClose(&growselect); savefirstlevel = TRUE; } break; } soundEventPlayMusic(SongNumber); //set the KAS global variable when an NIS has stopped if (singlePlayerNisletNumber != -1) { //if it's an nislet sprintf(bufferstr,"Nislet%dComplete",singlePlayerNisletNumber); var = aivarCreate(bufferstr); aivarValueSet(var, TRUE); } else { dbgAssert(toupper(singlePlayerNISName[0]) == 'N'); sprintf(bufferstr,"Nis%dComplete",singlePlayerNisNumber); var = aivarCreate(bufferstr); aivarValueSet(var, TRUE); } spUnlockout(); if (hyperspaceAtEndOfSinglePlayerNIS) { hyperspaceAtEndOfSinglePlayerNIS = FALSE; spHyperspaceButtonPushed(); } if (savefirstlevel) // this should be at end of function { kasfSaveLevel(1, strGetString(strSaveSinglePlayerLevel1)); } singlePlayerNISName = NULL; singlePlayerNisletNumber = -1; singlePlayerNisNumber = -1; } /*----------------------------------------------------------------------------- Name : singlePlayerNISNamesGet Description : Figure out the name of the nis and script file for a given nis number. Inputs : nisNumber - number of NIS to run Outputs : nisname - name of nis file scriptname - name of nis script file centreMothership - TRUE if the NIS is to be centred about player's mothership Return : void ----------------------------------------------------------------------------*/ void singlePlayerNISNamesGet(char **nisname, char **scriptname, bool *centreMothership, sdword nisNumber) { singlePlayerNisNumber = nisNumber; nisNumber--; dbgAssert(nisNumber >= 0); dbgAssert(nisNumber < NUMBER_SINGLEPLAYER_NIS); if (universe.players[0].race == R1) { *nisname = nisR1Names[nisNumber][0]; *scriptname = nisR1Names[nisNumber][1]; } else { *nisname = nisR2Names[nisNumber][0]; *scriptname = nisR2Names[nisNumber][1]; } singlePlayerNISName = *nisname; *centreMothership = (bool)nisPlayRelativeToMothership[nisNumber]; } /*----------------------------------------------------------------------------- Name : singlePlayerNISletNamesGet Description : Figure out the name of the nis and script file for a given nis number. Inputs : nisletNumber - number of NISlet to run Outputs : nisname - name of nis file scriptname - name of nis script file Return : TRUE if the NIS can be played properly ----------------------------------------------------------------------------*/ bool singlePlayerNISletNamesGet(char **nisname, char **scriptname, sdword nisletNumber) { sdword missionNumber, insertNumber; static char nisFileName[16]; static char nisScriptName[16]; singlePlayerNisletNumber = nisletNumber; missionNumber = nisletNumber / 10; insertNumber = nisletNumber % 10; missionNumber--; dbgAssert(missionNumber >= 0); dbgAssert(missionNumber < NUMBER_SINGLEPLAYER_MISSIONS); dbgAssert(insertNumber >= 0); dbgAssert(insertNumber < NISLETS_PER_LEVEL); #if NIS_PRINT_INFO if (nisletNames[missionNumber].name[insertNumber] == NULL) { dbgMessagef("\nMission %d insert (%c) is not defined", missionNumber + 1, insertNumber + 'A'); return(FALSE); } #endif strcpy(nisFileName, nisletNames[missionNumber].name[insertNumber]); if (nisletNames[missionNumber].raceSpecific[insertNumber]) { if (universe.curPlayerPtr->race == R1) { strcat(nisFileName, "r1"); } else { strcat(nisFileName, "r2"); } } strcpy(nisScriptName, nisFileName); strcat(nisFileName, ".nis"); strcat(nisScriptName, ".script"); #if NIS_PRINT_INFO if (!fileExists(nisFileName, 0) || !fileExists(nisScriptName, 0)) { dbgMessagef("\n'%s' and/or '%s' do not exist.", nisFileName, nisScriptName); return(0); } #endif *nisname = nisFileName; *scriptname = nisScriptName; singlePlayerNISName = *nisname; return(TRUE); } /*----------------------------------------------------------------------------- Name : singlePlayerStartNis Description : Start a single player NIS or NISlet Inputs : nisname - name of NIS file scriptname - name of NIS script file centreMothership - TRUE if this NIS plays relative to the player's mothership (does not apply to NISlets). positionAndHeading - ? Outputs : Return : void ----------------------------------------------------------------------------*/ void singlePlayerStartNis(char *nisname, char *scriptname, bool centreMothership, hvector *positionAndAngle) { matrix thisIdentityMatrix; vector position; real32 angle; matrix rotz; matrix nisMatrix; vector motherVector; matrix motherMatrix; vector tempvec; Ship *nisCentreShip = NULL; bool centreShipFound = FALSE; GrowSelection *nisSelection; ShipRace raceOfCentreShip = (ShipRace)-1; ShipType typeOfCentreShip; sdword instanceOfCentreShip = -1; AIVar *nisVar; Node *node; Derelict *derelict; vecGrabVecFromHVec(position,*positionAndAngle); angle = DEG_TO_RAD(positionAndAngle->w); spMainScreenAndLockout(SPLOCKOUT_EVERYTHING); thisNisDoneCB = singlePlayerNisStoppedCB; thisNisHeader = nisLoad(nisname, scriptname); //see if we should centre NIS around a ship nisSelection = kasGetGrowSelectionPtrIfExists("NisCentreShip"); if ((nisSelection) && (nisSelection->selection->numShips > 0)) { nisCentreShip = nisSelection->selection->ShipPtr[0]; kasGrowSelectionClear(nisSelection); typeOfCentreShip = nisCentreShip->shiptype; } //see if we should centre NIS around a derelict nisVar = aivarFind("NisCentreDerelict"); if (nisVar != NULL) { raceOfCentreShip = NSR_Derelict; typeOfCentreShip = aivarValueGet(nisVar); nisCentreShip = NULL; //find a derelict of the specified type for (node = universe.DerelictList.head; node != NULL; node = node->next) { derelict = (Derelict *)listGetStructOfNode(node); if (derelict->derelicttype == typeOfCentreShip) { //if this is the derelict we're looking for nisCentreShip = (Ship *)derelict; break; } } } //see if a ship instance was specified nisVar = aivarFind("NisCentreInstance"); if (nisVar != NULL) { instanceOfCentreShip = aivarValueGet(nisVar); } if (nisCentreShip) { vector heading; vector normal = {1.0, 0.0, 0.0}; real32 cross; centreShipFound = nisShipStartPosition(&motherVector, &motherMatrix, thisNisHeader, raceOfCentreShip, typeOfCentreShip, instanceOfCentreShip); position = nisCentreShip->posinfo.position; //to get the ships rotation abouts Z, get it's heading on the XY plane, //cross it with a 'normal' (zero degree) vector and check sign of Y to determine what to do with cross product matGetVectFromMatrixCol3(heading, nisCentreShip->rotinfo.coordsys); heading.z = 0; vecNormalize(&heading); cross = vecDotProduct(heading, normal); if (heading.y < 0.0f) { angle = TWOPI - (real32)acos((double)cross); } else { angle = (real32)acos((double)cross); } } else { if (centreMothership) { centreShipFound = nisShipStartPosition(&motherVector, &motherMatrix, thisNisHeader, (ShipRace)-1, Mothership, -1); dbgAssert(universe.curPlayerPtr->PlayerMothership != NULL); nisCentreShip = universe.curPlayerPtr->PlayerMothership; } } if (centreShipFound) { // we found the mothership or other centre ship, so let's play the NIS relative to it: // matMultiplyMatByMat(&thisIdentityMatrix,&motherMatrix,&IdentityMatrix); // matTranspose(&thisIdentityMatrix); matCopyAndTranspose(&motherMatrix, &thisIdentityMatrix); matMultiplyMatByMat(&nisMatrix,&nisCentreShip->rotinfo.coordsys,&thisIdentityMatrix); matMultiplyMatByVec(&tempvec,&nisMatrix,&motherVector); vecSubFrom(position,tempvec); } else { //play about 0,0,0 thisIdentityMatrix = IdentityMatrix; matMakeRotAboutZ(&rotz,(real32)cos(angle),(real32)sin(angle)); matMultiplyMatByMat(&nisMatrix,&thisIdentityMatrix,&rotz); } thisNisPlaying = nisStart(thisNisHeader, &position, &nisMatrix); nisTaskResume(); } #define MAXLINENEED 100 sdword WarpToLevelEnabled() { filehandle fh; char line[MAXLINENEED]; char tempbuf[30]; sdword status; sdword warpLevel; if (fileExists(WARP_FILENAME,0)) { fh = fileOpen(WARP_FILENAME,FF_TextMode); status = fileLineRead(fh,line,MAXLINENEED); fileClose(fh); if (status == FR_EndOfFile) { return 0; } if (sscanf(line,"%s %d",tempbuf,&warpLevel) == 2) { if (_stricmp("WarpTo",tempbuf) == 0) { if (warpLevel > 1) { return warpLevel; } } else { return 0; } } } return 0; } void WarpFleetStrengthCB(char *directory,char *field,void *dataToFillIn) { ShipType shiptype; ShipRace shiprace; char shiptypestr[50]; sdword num = 0; ShipStaticInfo *shipstatic; RemoveCommasFromString(field); sscanf(field,"%s %d",shiptypestr,&num); shiptype = StrToShipType(shiptypestr); if (!(shiptype < TOTAL_NUM_SHIPS)) { dbgFatalf(DBG_Loc,"Bad shiptype in %s",field); } if (num <= 0) { return; } if (shiptype > STD_LAST_SHIP) { shiprace = GetValidRaceForShipType(shiptype); } else { shiptype = GetAppropriateShipTypeForRace(shiptype,universe.players[0].race); dbgAssert(shiptype != -1); shiprace = universe.players[0].race; } shipstatic = GetShipStaticInfo(shiptype,shiprace); dbgAssert(bitTest(shipstatic->staticheader.infoFlags, IF_InfoLoaded)); spFleetStr += (GetStrengthOfShipStatic(shipstatic)*num); } void WarpPreLoadCB(char *directory,char *field,void *dataToFillIn) { ShipType shiptype; ShipRace shiprace; char shiptypestr[50]; sdword num = 0; RemoveCommasFromString(field); sscanf(field,"%s %d",shiptypestr,&num); shiptype = StrToShipType(shiptypestr); if (!(shiptype < TOTAL_NUM_SHIPS)) { dbgFatalf(DBG_Loc,"Bad shiptype in %s",field); } if (num <= 0) { return; } if (shiptype > STD_LAST_SHIP) { shiprace = GetValidRaceForShipType(shiptype); } else { shiptype = GetAppropriateShipTypeForRace(shiptype,universe.players[0].race); dbgAssert(shiptype != -1); shiprace = universe.players[0].race; } SetInfoNeededForShipAndRelatedStaticInfo(shiptype,shiprace,TRUE); } void WarpFleetRUStrengthCB(char *directory,char *field,void *dataToFillIn) { sdword num = 0; sscanf(field,"%d",&num); spFleetStr += GetStrengthOfRUs(num); } void SetWarpFleetCB(char *directory,char *field,void *dataToFillIn) { Ship *ship; ShipType shiptype; ShipRace shiprace; char shiptypestr[50]; sdword num = 0; sdword i; vector zeroVector = { 0.0f, 0.0f, 0.0f }; Player *player = &universe.players[0]; RemoveCommasFromString(field); sscanf(field,"%s %d",shiptypestr,&num); shiptype = StrToShipType(shiptypestr); if (!(shiptype < TOTAL_NUM_SHIPS)) { dbgFatalf(DBG_Loc,"Bad shiptype in %s",field); } if (num <= 0) { return; } if (shiptype > STD_LAST_SHIP) { shiprace = GetValidRaceForShipType(shiptype); } else { shiptype = GetAppropriateShipTypeForRace(shiptype,universe.players[0].race); dbgAssert(shiptype != -1); shiprace = universe.players[0].race; } dbgAssert(universe.players[0].PlayerMothership); for (i=0;iaiPlayer!=NULL) ) { rmActivateFreeLab(player); } } GroupShipIntoMilitaryParade(&universe.mainCommandLayer,ship,universe.players[0].PlayerMothership); } } void WarpInFleet(hvector *startPoint) { // Create Mothership Ship *ship; Player *player = &universe.players[0]; ship = univAddShip(Mothership,universe.players[0].race,(vector *)startPoint,&universe.players[0],0); gameStatsAddShip(ship,0); univRotateObjYaw((SpaceObjRot *)ship,DEG_TO_RAD(startPoint->w)); // Update ship totals for player unitCapCreateShip(ship,player); universe.players[0].PlayerMothership = ship; // Now add rest of fleet in parade formation scriptSet(NULL,WARP_FILENAME,WarpScriptTable); clPresetShipsToPosition(&universe.mainCommandLayer); } /*----------------------------------------------------------------------------- Name : singleHackResearchWarp Description : A function that hard codes the research a player is has already had based on the settings daly told me they would be in each level ugly stuff... replace with an elegant solution later Inputs : Outputs : Allows the player to research certain topics Return : void ----------------------------------------------------------------------------*/ void singleHackResearchWarp(void) { switch (warpLevel) { case 16: case 15: PlayerAcquiredTechnology("HeavyGuns"); case 14: AllowPlayerToResearch("HeavyGuns"); PlayerAcquiredTechnology("CloakDefenseFighter"); case 13: PlayerAcquiredTechnology("MissileWeapons"); AllowPlayerToResearch("CloakDefenseFighter"); case 12: PlayerAcquiredTechnology("GravityWellGeneratorTech"); case 11: AllowPlayerToResearch("GravityWellGeneratorTech"); PlayerAcquiredTechnology("ProximityDetector"); PlayerAcquiredTechnology("RepairTech"); case 10: AllowPlayerToResearch("RepairTech"); AllowPlayerToResearch("MissileWeapons"); case 9: PlayerAcquiredTechnology("DDDFDFGFTech"); PlayerAcquiredTechnology("TargetingSystems"); case 8: AllowPlayerToResearch("TargetingSystems"); case 7: AllowPlayerToResearch("DDDFDFGFTech"); PlayerAcquiredTechnology("MassDrive1Mt"); PlayerAcquiredTechnology("FireControl"); case 6: // Mission 06: GIVE: Plasma Weapons PlayerAcquiredTechnology("PlasmaWeapons"); AllowPlayerToResearch("MassDrive1Mt"); AllowPlayerToResearch("FireControl"); case 5: // Mission 05: GIVE: Capital Ship Chassis, Ion Weapons, ALLOW: Plasma Weapons AllowPlayerToResearch("PlasmaWeapons"); PlayerAcquiredTechnology("Chassis3"); PlayerAcquiredTechnology("IonWeapons"); case 4: // Mission 04: GIVE: Capital Ship Drive, ALLOW: Capital Ship Chassis PlayerAcquiredTechnology("MassDrive100Kt"); AllowPlayerToResearch("Chassis3"); case 3: // Mission 03: GIVE: Corvette Chassis, Heavy Corvette PlayerAcquiredTechnology("Chassis2"); PlayerAcquiredTechnology("MediumGuns"); case 2: // Mission 02: GIVE: Fighter Chassis, Corvette Drive PlayerAcquiredTechnology("Chassis1"); PlayerAcquiredTechnology("MassDrive10Kt"); case 1: // Mission 01, GIVE: Fighter Drive, ALLOW: Fighter Chassis PlayerAcquiredTechnology("MassDrive1Kt"); AllowPlayerToResearch("Chassis1"); } } void singlePlayerStartGame(void) { hvector startPoint; char *nis, *script; bool centre; universe.players[1].resourceUnits = 0; if (warpLevel) { dbgAssert(singlePlayerGameInfo.currentMission == warpLevel); universeSwitchToPlayer(0); GetMissionsDirAndFile(singlePlayerGameInfo.currentMission); SetFleetModifier(singlePlayerGameInfo.currentMission, GetFleetStrengthWarpScript); levelStartNext(spMissionsDir,spMissionsFile); GetStartPointPlayer(&startPoint); WarpInFleet(&startPoint); singlePlayerLevelLoaded(); singleHackResearchWarp(); ccFocusOnPlayersMothership(&universe.mainCameraCommand,0); soundEventPlayMusic(SongNumber); singlePlayerKasMissionStart(singlePlayerGameInfo.currentMission); if (singlePlayerGameInfo.onLoadPlayNIS > 0) { hvector startpoint; GetStartPointPlayer(&startpoint); singlePlayerNISNamesGet(&nis, &script, ¢re, singlePlayerGameInfo.onLoadPlayNIS); singlePlayerStartNis(nis, script, centre, &startpoint); } return; } tmTechInit(); spFleetModifier = 0.0f; GetMissionsDirAndFile(1); levelInit(spMissionsDir,spMissionsFile); singlePlayerLevelLoaded(); singlePlayerKasMissionStart(1); GetStartPointPlayer(&startPoint); singlePlayerNISNamesGet(&nis, &script, ¢re, 1); singlePlayerStartNis(nis, script, centre, &startPoint); } void singlePlayerNextLevel(void) { hvector startnisAt = { 0.0f, 0.0f, 0.0f, 0.0f }; char *nis, *script; bool centre; if (singlePlayerGameInfo.currentMission == 16) { singlePlayerMissionCompleteCB(); return; } if (singlePlayerGameInfo.onCompletePlayNIS > 0) { singlePlayerNISNamesGet(&nis, &script, ¢re, singlePlayerGameInfo.onCompletePlayNIS); singlePlayerStartNis(nis, script, centre, &startnisAt); if (singlePlayerGameInfo.onCompleteHyperspace) { hyperspaceAtEndOfSinglePlayerNIS = TRUE; } else { dbgFatal(DBG_Loc,"\nNot hyperspacing at end of level not supported yet"); } return; } if (singlePlayerGameInfo.onCompleteHyperspace) { if (hyperspaceOverride) { spHyperspaceButtonPushed(); } } } void singlePlayerMissionCompleteCB(void) { selSelected.numShips = 0; ioUpdateShipTotals(); mrRenderMainScreen = FALSE; universe.quittime = universe.totaltimeelapsed; // this will prevent the shiplist from being displayed! utyCreditsSequence = TRUE; } void singlePlayerMissionFailedCB(void) { univKillPlayer(0,PLAYERKILLED_LOSTMISSION); CheckPlayerWin(); } #if SP_DEBUGKEYS void singlePlayerCheckDebugKeys(sdword ID) { #if SP_DEBUGLEVEL2 sdword state; #endif switch (ID) // general stuff { case RKEY: { sdword total; sdword numHarvestables; sdword numAsteroid0s; char totalstr[50]; univGetResourceStatistics(&total,&numHarvestables,&numAsteroid0s); sprintf(totalstr,"RV=%d #R=%d #A0=%d",total,numHarvestables,numAsteroid0s); clCommandMessage(totalstr); } return; } if (singlePlayerGame) // singlePlayerGame specific stuff switch (ID) { case CKEY: hyperspaceOverride = TRUE; singlePlayerNextLevel(); break; case FKEY: singlePlayerMissionFailedCB(); break; case EKEY: { char totalstr[50]; GetFleetStrengthInUniverseOfPlayer(&universe.players[0]); sprintf(totalstr,"Fleet Str = %f",spFleetStr); clCommandMessage(totalstr); } break; case DKEY: kasTakeADump(); break; #if SP_DEBUGLEVEL2 // debug overlays case SKEY: // states mrKASDebugDrawStates ^= TRUE; break; case TKEY: // timers mrKASDebugDrawTimers ^= TRUE; break; case VKEY: // vars mrKASDebugDrawVars ^= TRUE; break; case ZKEY: // volumes mrKASDebugDrawVolumes ^= TRUE; break; case AKEY: // all state = !(mrKASDebugDrawStates && mrKASDebugDrawTimers && mrKASDebugDrawVars && mrKASDebugDrawVolumes); mrKASDebugDrawStates = state; mrKASDebugDrawTimers = state; mrKASDebugDrawVars = state; mrKASDebugDrawVolumes = state; break; case PAGEDOWNKEY: mrKASSkipFactor++; break; #endif //SP_DEBUGLEVEL2 } } #endif //SP_DEBUGKEYS /*----------------------------------------------------------------------------- Name : spNISletTestAttempt Description : Attempt to test an nislet for the current level Inputs : index - number of NISlet for the current level (0..2) Outputs : Return : void ----------------------------------------------------------------------------*/ #if SP_NISLET_TEST void spNISletTestAttempt(sdword index) { hvector startpoint; char *nis, *script; if (index < 0 || index >= NUMBER_SINGLEPLAYER_MISSIONS) { dbgMessage("\nindex < 0 || index >= NUMBER_SINGLEPLAYER_MISSIONS"); return; } if (nisletNames[singlePlayerGameInfo.currentMission - 1].name[index] == NULL) { dbgMessagef("\nnisletNames[%d].name[%d] == NULL", singlePlayerGameInfo.currentMission - 1, index); return; } GetStartPointPlayer(&startpoint); singlePlayerNISletNamesGet(&nis, &script, singlePlayerGameInfo.currentMission * 10 + index); if (!fileExists(nis, 0)) { dbgMessagef("\n'%s' not found.", nis); return; } if (!fileExists(script, 0)) { dbgMessagef("\n'%s' not found.", script); return; } singlePlayerStartNis(nis, script, FALSE, &startpoint); } #endif /*----------------------------------------------------------------------------- Name : spFindCameraAttitude Description : Find the level's default camera poistion, if any. Inputs : Outputs : position - position for the camera's eye point Return : TRUE if the camera position could be computed, FALSE otherwise ----------------------------------------------------------------------------*/ bool spFindCameraAttitude(vector *position) { hvector *hPosition = kasVectorPtrIfExists(SP_CAMERA_POINT_NAME); if (hPosition == NULL) { return(FALSE); } position->x = hPosition->x; position->y = hPosition->y; position->z = hPosition->z; return(TRUE); } /*============================================================================= Save Game Stuff =============================================================================*/ void SaveSinglePlayerGame(void) { dbgAssert(singlePlayerGameLoadNewLevelFlag == FALSE); SaveStructureOfSize(&singlePlayerGameInfo,sizeof(SinglePlayerGameInfo)); SaveLinkedListOfInsideShips(&singlePlayerGameInfo.ShipsInHyperspace); SaveGrowSelection(&singlePlayerGameInfo.ShipsToHyperspace); } void LoadSinglePlayerGame(void) { singlePlayerGameLoadNewLevelFlag = FALSE; LoadStructureOfSizeToAddress(&singlePlayerGameInfo,sizeof(SinglePlayerGameInfo)); LoadLinkedListOfInsideShips(&singlePlayerGameInfo.ShipsInHyperspace); FixLinkedListOfInsideShips(&singlePlayerGameInfo.ShipsInHyperspace); LoadGrowSelectionAndFix(&singlePlayerGameInfo.ShipsToHyperspace); }