/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * mission.c * * all the stuff relevant to a mission */ #include #include "mission.h" #include "lib/gamelib/gtime.h" #include "game.h" #include "projectile.h" #include "power.h" #include "structure.h" #include "message.h" #include "research.h" #include "hci.h" #include "player.h" #include "order.h" #include "action.h" #include "display3d.h" #include "lib/ivis_common/piestate.h" #include "effects.h" #include "radar.h" #include "resource.h" // for mousecursors #include "transporter.h" #include "group.h" #include "text.h" #include "frontend.h" // for displaytextoption. #include "csnap.h" // cursor snapping #include "intdisplay.h" #include "display.h" #include "loadsave.h" #include "lib/script/script.h" #include "scripttabs.h" #include "lib/sound/audio.h" #include "audio_id.h" #include "cmddroid.h" #include "warcam.h" #include "wrappers.h" #include "lib/widget/widget.h" #include "lib/widget/widgint.h" #include "console.h" #include "droid.h" #include "data.h" #include "multiplay.h" #include "lib/ivis_common/rendmode.h" // for downloadbuffer #include "lib/ivis_common/piefunc.h" #include "lib/ivis_common/pieblitfunc.h" #include "environ.h" #include "loop.h" #include "levels.h" #include "visibility.h" #include "mapgrid.h" #include "cluster.h" #include "gateway.h" #include "selection.h" #include "scores.h" #include "keymap.h" #include "cdspan.h" #include "lib/sound/cdaudio.h" //#include "texture.h" // ffs #include "texture.h" extern CURSORSNAP InterfaceSnap; //DEFINES************** //#define IDTIMER_FORM 11000 // has to be in the header..boohoo //#define IDTIMER_DISPLAY 11001 //#define IDMISSIONRES_FORM 11002 // has to be in the header..boohoo #define IDMISSIONRES_TXT 11004 #define IDMISSIONRES_LOAD 11005 //#define IDMISSIONRES_SAVE 11006 //#define IDMISSIONRES_QUIT 11007 //has to be in the header. bummer. #define IDMISSIONRES_CONTINUE 11008 //#define IDTRANSTIMER_FORM 11009 //form for transporter timer //#define IDTRANTIMER_DISPLAY 11010 //timer display for transporter timer //#define IDTRANTIMER_BUTTON 11012 //transporter button on timer display #define IDMISSIONRES_BACKFORM 11013 #define IDMISSIONRES_TITLE 11014 /*mission timer position*/ #define TIMER_X (568 + E_W) //#define TIMER_Y 22 //#define TIMER_WIDTH 38 //#define TIMER_HEIGHT 20 #define TIMER_LABELX 15 #define TIMER_LABELY 0 /*Transporter Timer form position */ #define TRAN_FORM_X STAT_X #define TRAN_FORM_Y TIMER_Y #define TRAN_FORM_WIDTH 75 #define TRAN_FORM_HEIGHT 25 /*Transporter Timer position */ #define TRAN_TIMER_X 4 #define TRAN_TIMER_Y TIMER_LABELY #define TRAN_TIMER_WIDTH 25 //#define TRAN_TIMER_HEIGHT TRAN_FORM_HEIGHT /*Transporter Timer button position */ //#define TRAN_BUTTON_X (TRAN_TIMER_X + TRAN_TIMER_WIDTH + TRAN_TIMER_X) //#define TRAN_BUTTON_Y TRAN_TIMER_Y //#define TRAN_BUTTON_WIDTH 20 //#define TRAN_BUTTON_HEIGHT 20 #define MISSION_1_X 5 // pos of text options in box. #define MISSION_1_Y 15 #define MISSION_2_X 5 #define MISSION_2_Y 35 #define MISSION_3_X 5 #define MISSION_3_Y 55 #define MISSION_TEXT_W MISSIONRES_W-10 #define MISSION_TEXT_H 16 //these are used for mission countdown #define TEN_MINUTES (10 * 60 * GAME_TICKS_PER_SEC) #define FIVE_MINUTES (5 * 60 * GAME_TICKS_PER_SEC) #define FOUR_MINUTES (4 * 60 * GAME_TICKS_PER_SEC) #define THREE_MINUTES (3 * 60 * GAME_TICKS_PER_SEC) #define TWO_MINUTES (2 * 60 * GAME_TICKS_PER_SEC) #define ONE_MINUTE (1 * 60 * GAME_TICKS_PER_SEC) #define NOT_PLAYED_ONE 0x01 #define NOT_PLAYED_TWO 0x02 #define NOT_PLAYED_THREE 0x04 #define NOT_PLAYED_FIVE 0x08 #define NOT_PLAYED_TEN 0x10 #define NOT_PLAYED_ACTIVATED 0x20 //EXTERNALS************* extern char SaveGamePath[]; MISSION mission; BOOL offWorldKeepLists; // Set by scrFlyInTransporter. True if were currenly tracking the transporter. BOOL bTrackingTransporter = FALSE; /*lists of droids that are held seperate over several missions. There should only be selectedPlayer's droids but have possibility for MAX_PLAYERS - also saves writing out list functions to cater for just one player*/ DROID *apsLimboDroids[MAX_PLAYERS]; /**********TEST************/ //static UDWORD addCount = 0; //STATICS*************** //Where the Transporter lands for player 0 (sLandingZone[0]), and the rest are //a list of areas that cannot be built on, used for landing the enemy transporters static LANDING_ZONE sLandingZone[MAX_NOGO_AREAS]; //flag to indicate when the droids in a Transporter are flown to safety and not the next mission static BOOL bDroidsToSafety; /* mission result holder */ static BOOL g_bMissionResult; // return positions for vtols POINT asVTOLReturnPos[MAX_PLAYERS]; static UBYTE missionCountDown; //flag to indicate whether the coded mission countdown is played static UBYTE bPlayCountDown; //FUNCTIONS************** static void addLandingLights( UDWORD x, UDWORD y); static BOOL startMissionOffClear(STRING *pGame); static BOOL startMissionOffKeep(STRING *pGame); static BOOL startMissionCampaignStart(STRING *pGame); static BOOL startMissionCampaignChange(STRING *pGame); static BOOL startMissionCampaignExpand(STRING *pGame); static BOOL startMissionCampaignExpandLimbo(STRING *pGame); static BOOL startMissionBetween(void); static void endMissionCamChange(void); static void endMissionOffClear(void); static void endMissionOffKeep(void); static void endMissionOffKeepLimbo(void); static void endMissionExpandLimbo(void); static void saveMissionData(void); static void restoreMissionData(void); static void saveCampaignData(void); static void aiUpdateMissionStructure(STRUCTURE *psStructure); static void missionResetDroids(); static void saveMissionLimboData(void); static void restoreMissionLimboData(void); static void processMissionLimbo(void); static void intUpdateMissionTimer(struct _widget *psWidget, struct _w_context *psContext); static BOOL intAddMissionTimer(void); static void intUpdateTransporterTimer(struct _widget *psWidget, struct _w_context *psContext); static void adjustMissionPower(void); static void saveMissionPower(void); static UDWORD getHomeLandingX(void); static UDWORD getHomeLandingY(void); void swapMissionPointers(void); static void fillTimeDisplay(STRING *psText, UDWORD time, BOOL bHours); static void processPreviousCampDroids(void); static BOOL intAddTransporterTimer(void); static void clearCampaignUnits(void); static void emptyTransporters(BOOL bOffWorld); //result screen functions BOOL intAddMissionResult(BOOL result, BOOL bPlaySucess); //void intRemoveMissionResult (void); //void intRemoveMissionResultNoAnim (void); //void intRunMissionResult (void); //void intProcessMissionResult (UDWORD id); BOOL MissionResUp = FALSE; BOOL ClosingMissionRes = FALSE; static SDWORD g_iReinforceTime = 0; //static DROID_GROUP *g_CurrentScriptGroup = NULL; /* Which campaign are we dealing with? */ static UDWORD camNumber = 1; //static iSprite *pMissionBackDrop; //pointer to backdrop piccy //returns TRUE if on an off world mission BOOL missionIsOffworld(void) { return ((mission.type == LDS_MKEEP) || (mission.type == LDS_MCLEAR) || (mission.type == LDS_MKEEP_LIMBO) ); } //returns TRUE if the correct type of mission for reinforcements BOOL missionForReInforcements(void) { if ( (mission.type == LDS_CAMSTART) OR missionIsOffworld() OR (mission.type == LDS_CAMCHANGE) ) { return TRUE; } else { return FALSE; } } //returns TRUE if the correct type of mission and a reinforcement time has been set BOOL missionCanReEnforce(void) { // return missionIsOffworld() && (mission.ETA >= 0); if (mission.ETA >= 0) { //added CAMSTART for when load up a save game on Cam2A - AB 17/12/98 //if (missionIsOffworld() OR (mission.type == LDS_CAMCHANGE) OR // (mission.type == LDS_CAMSTART)) if (missionForReInforcements()) { return TRUE; } } return FALSE; } //returns TRUE if the mission is a Limbo Expand mission BOOL missionLimboExpand(void) { if (mission.type == LDS_EXPAND_LIMBO) { return TRUE; } else { return FALSE; } } //mission initialisation game code void initMission(void) { UDWORD inc; debug( LOG_NEVER, "***Init Mission ***\n" ); //mission.type = MISSION_NONE; mission.type = LDS_NONE; for (inc = 0; inc < MAX_PLAYERS; inc++) { mission.apsStructLists[inc] = NULL; mission.apsDroidLists[inc] = NULL; mission.apsFeatureLists[inc] = NULL; //mission.apsProxDisp[inc] = NULL; mission.apsFlagPosLists[inc] = NULL; apsLimboDroids[inc] = NULL; } offWorldKeepLists = FALSE; mission.time = -1; // ffs ab setMissionCountDown(); mission.ETA = -1; mission.startTime = 0; mission.psGateways = NULL; mission.apRLEZones = NULL; mission.gwNumZones = 0; mission.mapHeight = 0; mission.mapWidth = 0; mission.aNumEquiv = NULL; mission.apEquivZones = NULL; mission.aZoneReachable = NULL; //init all the landing zones for (inc = 0; inc < MAX_NOGO_AREAS; inc++) { sLandingZone[inc].x1 = sLandingZone[inc].y1 = sLandingZone[inc].x2 = sLandingZone[inc].y2 = 0; } // init the vtol return pos memset(asVTOLReturnPos, 0, sizeof(POINT)*MAX_PLAYERS); bDroidsToSafety = FALSE; setPlayCountDown(TRUE); //start as not cheating! mission.cheatTime = 0; } // reset the vtol landing pos void resetVTOLLandingPos(void) { memset(asVTOLReturnPos, 0, sizeof(POINT)*MAX_PLAYERS); } //this is called everytime the game is quit void releaseMission(void) { /*mission.apsDroidLists may contain some droids that have been transferred from one campaign to the next*/ freeAllMissionDroids(); /*apsLimboDroids may contain some droids that have been saved at the end of one mission and not yet used*/ freeAllLimboDroids(); } //called to shut down when mid-mission on an offWorld map BOOL missionShutDown(void) { UDWORD inc; //if (mission.type == MISSION_OFFKEEP OR mission.type == MISSION_OFFCLEAR) if (missionIsOffworld()) //mission.type == LDS_MKEEP OR mission.type == LDS_MCLEAR) { //clear out the audio audio_StopAll(); freeAllDroids(); freeAllStructs(); freeAllFeatures(); releaseAllProxDisp(); gwShutDown(); //flag positions go with structs // FREE(psMapTiles); // mapFreeTilesAndStrips(); //FREE(aMapLinePoints); for (inc = 0; inc < MAX_PLAYERS; inc++) { apsDroidLists[inc] = mission.apsDroidLists[inc]; mission.apsDroidLists[inc] = NULL; apsStructLists[inc] = mission.apsStructLists[inc]; mission.apsStructLists[inc] = NULL; apsFeatureLists[inc] = mission.apsFeatureLists[inc]; mission.apsFeatureLists[inc] = NULL; //apsProxDisp[inc] = mission.apsProxDisp[inc]; apsFlagPosLists[inc] = mission.apsFlagPosLists[inc]; mission.apsFlagPosLists[inc] = NULL; } psMapTiles = mission.psMapTiles; aMapLinePoints = mission.aMapLinePoints; mapWidth = mission.mapWidth; mapHeight = mission.mapHeight; psGateways = mission.psGateways; apRLEZones = mission.apRLEZones; gwNumZones = mission.gwNumZones; aNumEquiv = mission.aNumEquiv; apEquivZones = mission.apEquivZones; aZoneReachable = mission.aZoneReachable; } // sorry if this breaks something - but it looks like it's what should happen - John mission.type = LDS_NONE; return TRUE; } /*on the PC - sets the countdown played flag*/ void setMissionCountDown(void) { SDWORD timeRemaining; timeRemaining = mission.time - (gameTime - mission.startTime); if (timeRemaining < 0) { timeRemaining = 0; } //need to init the countdown played each time the mission time is changed missionCountDown = NOT_PLAYED_ONE | NOT_PLAYED_TWO | NOT_PLAYED_THREE | NOT_PLAYED_FIVE | NOT_PLAYED_TEN | NOT_PLAYED_ACTIVATED; if (timeRemaining < TEN_MINUTES - 1) { missionCountDown &= ~NOT_PLAYED_TEN; } if (timeRemaining < FIVE_MINUTES - 1) { missionCountDown &= ~NOT_PLAYED_FIVE; } if (timeRemaining < THREE_MINUTES - 1) { missionCountDown &= ~NOT_PLAYED_THREE; } if (timeRemaining < TWO_MINUTES - 1) { missionCountDown &= ~NOT_PLAYED_TWO; } if (timeRemaining < ONE_MINUTE - 1) { missionCountDown &= ~NOT_PLAYED_ONE; } } //BOOL startMission(MISSION_TYPE missionType, STRING *pGame) BOOL startMission(LEVEL_TYPE missionType, STRING *pGame) { BOOL loaded = TRUE; /* Player has (obviously) not failed at the start */ setPlayerHasLost(FALSE); setPlayerHasWon(FALSE); /*and win/lose video is obvioulsy not playing*/ setScriptWinLoseVideo(PLAY_NONE); //HACKY HACK HACK //this inits the flag so that 'reinforcements have arrived' message isn't played for the first transporter load initFirstTransporterFlag(); // if (missionType == LDS_CAMSTART) // { //only load up one campaign start // if (!DemoStart) // { // DBERROR(("Unable to load mission")); // return FALSE; // } // DemoStart = FALSE; // } // else if (missionType == LDS_MKEEP) // { //only load up one other mission // if (!DemoExpand) // { // DBERROR(("Unable to load mission")); // return FALSE; // } // DemoExpand = FALSE; // } // else if(missionType == LDS_BETWEEN) // { // // do nothing. // } // else // { // //don't want to load up any other type of mission // DBERROR(("Unable to load mission")); // return FALSE; // } //if (mission.type != MISSION_NONE) if (mission.type != LDS_NONE) { /*mission type gets set to none when you have returned from a mission so don't want to go another mission when already on one! - so ignore*/ debug( LOG_NEVER, "Already on a mission" ); return TRUE; } // reset the cluster stuff clustInitialise(); initEffectsSystem(); //load the game file for all types of mission except a Between Mission //if (missionType != MISSION_BETWEEN) if (missionType != LDS_BETWEEN) { loadGameInit(pGame); } //all proximity messages are removed between missions now releaseAllProxDisp(); switch (missionType) { case LDS_CAMSTART: { if (!startMissionCampaignStart(pGame)) { loaded = FALSE; } break; } case LDS_MKEEP: case LDS_MKEEP_LIMBO: { if (!startMissionOffKeep(pGame)) { loaded = FALSE; } break; } case LDS_BETWEEN: { //do anything? if (!startMissionBetween()) { loaded = FALSE; } break; } case LDS_CAMCHANGE: { /*if (getCampaignNumber() == 1) { //play the cam 2 video seq_ClearSeqList(); #ifndef PSX seq_AddSeqToList("cam2/c002.rpl",NULL,"cam2/c002.txa",FALSE); #else seq_AddSeqToList("cam2/c002.str","1656f"); #endif seq_StartNextFullScreenVideo(); } else { //play the cam 3 video seq_ClearSeqList(); #ifndef PSX seq_AddSeqToList("cam2/cam2out.rpl",NULL,NULL,FALSE); seq_AddSeqToList("cam3/c003.rpl",NULL,"cam3/c003.txa",FALSE); #else seq_AddSeqToList("cam3/c003.str","1656f"); #endif seq_StartNextFullScreenVideo(); }*/ if (!startMissionCampaignChange(pGame)) { loaded = FALSE; } break; } case LDS_EXPAND: { if (!startMissionCampaignExpand(pGame)) { loaded = FALSE; } break; } case LDS_EXPAND_LIMBO: { if (!startMissionCampaignExpandLimbo(pGame)) { loaded = FALSE; } break; } case LDS_MCLEAR: { if (!startMissionOffClear(pGame)) { loaded = FALSE; } break; } default: { //error! debug( LOG_ERROR, "Unknown Mission Type" ); abort(); loaded = FALSE; } } if (!loaded) { debug( LOG_ERROR, "Unable to load mission file" ); abort(); return FALSE; } mission.type = missionType; if (missionIsOffworld()) { //add what power have got from the home base adjustMissionPower(); } if (missionCanReEnforce()) { //add mission timer - if necessary addMissionTimerInterface(); //add Transporter Timer addTransporterTimerInterface(); } scoreInitSystem(); //add proximity messages for all untapped VISIBLE oil resources addOilResourceProximities(); return TRUE; } // initialise the mission stuff for a save game BOOL startMissionSave(SDWORD missionType) { mission.type = missionType; return TRUE; } /*checks the time has been set and then adds the timer if not already on the display*/ void addMissionTimerInterface(void) { //don't add if the timer hasn't been set if (mission.time < 0) { return; } //check timer is not already on the screen if (!widgGetFromID(psWScreen, IDTIMER_FORM)) { intAddMissionTimer(); } } /*checks that the timer has been set and that a Transporter exists before adding the timer button*/ void addTransporterTimerInterface(void) { DROID *psDroid, *psTransporter; BOOL bAddInterface = FALSE; W_CLICKFORM *psForm; //check if reinforcements are allowed if (mission.ETA >= 0) { //check the player has at least one Transporter back at base psDroid = psTransporter = NULL; for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType == DROID_TRANSPORTER) { psTransporter = psDroid; break; } } if (psDroid) { //don't bother checking for reinforcements - always add it if you've got a Transporter //check the player has some reinforcements back at home /*psDroid = NULL; for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType != DROID_TRANSPORTER) { break; } } //if reinforcements available if (psDroid)*/ { bAddInterface = TRUE; //check timer is not already on the screen if (!widgGetFromID(psWScreen, IDTRANTIMER_BUTTON)) { intAddTransporterTimer(); } //set the data for the transporter timer widgSetUserData(psWScreen, IDTRANTIMER_DISPLAY, (void*)psTransporter); //lock the button if necessary if (transporterFlying(psTransporter)) { //disable the form so can't add any more droids into the transporter psForm = (W_CLICKFORM*)widgGetFromID(psWScreen,IDTRANTIMER_BUTTON); if (psForm) { formSetClickState(psForm, WBUT_LOCK); } } } } } //if criteria not met if (!bAddInterface) { //make sure its not there! intRemoveTransporterTimer(); } } #define OFFSCREEN_RADIUS 600 #define OFFSCREEN_HEIGHT 600 #define EDGE_SIZE 1 /* pick nearest map edge to point */ void missionGetNearestCorner( UWORD iX, UWORD iY, UWORD *piOffX, UWORD *piOffY ) { UDWORD iMidX = (scrollMinX + scrollMaxX)/2, iMidY = (scrollMinY + scrollMaxY)/2; if ( ((UDWORD)(iX>>TILE_SHIFT)) < iMidX ) { *piOffX = (UWORD) ((scrollMinX << TILE_SHIFT) + (EDGE_SIZE*TILE_UNITS)); } else { *piOffX = (UWORD) ((scrollMaxX << TILE_SHIFT) - (EDGE_SIZE*TILE_UNITS)); } if ( ((UDWORD)(iY>>TILE_SHIFT)) < iMidY ) { *piOffY = (UWORD) ((scrollMinY << TILE_SHIFT) + (EDGE_SIZE*TILE_UNITS)); } else { *piOffY = (UWORD) ((scrollMaxY << TILE_SHIFT) - (EDGE_SIZE*TILE_UNITS)); } } /* fly in transporters at start of level */ void missionFlyTransportersIn( SDWORD iPlayer, BOOL bTrackTransporter ) { DROID *psTransporter, *psNext; UWORD iX, iY, iZ; SDWORD iLandX, iLandY, iDx, iDy; FRACT_D fR; bTrackingTransporter = bTrackTransporter; iLandX = getLandingX(iPlayer); iLandY = getLandingY(iPlayer); missionGetTransporterEntry( iPlayer, &iX, &iY ); iZ = (UWORD) (map_Height( iX, iY ) + OFFSCREEN_HEIGHT); psNext = NULL; //get the droids for the mission for (psTransporter = mission.apsDroidLists[iPlayer]; psTransporter != NULL; psTransporter = psNext) { psNext = psTransporter->psNext; if (psTransporter->droidType == DROID_TRANSPORTER) { //check that this transporter actually contains some droids if (psTransporter->psGroup AND psTransporter->psGroup->refCount > 1) { //remove out of stored list and add to current Droid list if (droidRemove(psTransporter, mission.apsDroidLists)) { //don't want to add it unless managed to remove it from the previous list addDroid(psTransporter, apsDroidLists); } /* set start position */ psTransporter->x = iX; psTransporter->y = iY; psTransporter->z = iZ; /* set start direction */ iDx = iLandX - iX; iDy = iLandY - iY; fR = (FRACT_D) atan2(iDx, iDy); if ( fR < 0.0 ) { fR += (FRACT_D) (2*PI); } psTransporter->direction = (UWORD)( RAD_TO_DEG(fR) ); // Camera track requested and it's the selected player. if ( ( bTrackTransporter == TRUE ) && (iPlayer == (SDWORD)selectedPlayer) ) { /* deselect all droids */ selDroidDeselect( selectedPlayer ); if ( getWarCamStatus() ) { camToggleStatus(); } /* select transporter */ psTransporter->selected = TRUE; camToggleStatus(); } //little hack to ensure all Transporters are fully repaired by time enter world psTransporter->body = psTransporter->originalBody; /* set fly-in order */ orderDroidLoc( psTransporter, DORDER_TRANSPORTIN, iLandX, iLandY ); audio_PlayObjDynamicTrack( psTransporter, ID_SOUND_BLIMP_FLIGHT, moveCheckDroidMovingAndVisible ); //only want to fly one transporter in at a time now - AB 14/01/99 break; } } } } /*Saves the necessary data when moving from a home base Mission to an OffWorld mission*/ void saveMissionData(void) { UDWORD inc; DROID *psDroid; STRUCTURE *psStruct, *psStructBeingBuilt; BOOL bRepairExists; //clear out the audio audio_StopAll(); //save the mission data mission.psMapTiles = psMapTiles; mission.aMapLinePoints = aMapLinePoints; mission.mapWidth = mapWidth; mission.mapHeight = mapHeight; mission.scrollMinX = scrollMinX; mission.scrollMinY = scrollMinY; mission.scrollMaxX = scrollMaxX; mission.scrollMaxY = scrollMaxY; mission.psGateways = psGateways; psGateways = NULL; mission.apRLEZones = apRLEZones; apRLEZones = NULL; mission.gwNumZones = gwNumZones; gwNumZones = 0; mission.aNumEquiv = aNumEquiv; aNumEquiv = NULL; mission.apEquivZones = apEquivZones; apEquivZones = NULL; mission.aZoneReachable = aZoneReachable; aZoneReachable = NULL; //save the selectedPlayer's LZ mission.homeLZ_X = getLandingX(selectedPlayer); mission.homeLZ_Y = getLandingY(selectedPlayer); bRepairExists = FALSE; //set any structures currently being built to completed for the selected player for (psStruct = apsStructLists[selectedPlayer]; psStruct != NULL; psStruct = psStruct->psNext) { if (psStruct->status == SS_BEING_BUILT) { //find a droid working on it for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (orderStateObj(psDroid, DORDER_BUILD, (BASE_OBJECT **)&psStructBeingBuilt)) { if (psStructBeingBuilt == psStruct) { //check there is enough power to complete //inc = psStruct->pStructureType->powerToBuild - psStruct->currentPowerAccrued; inc = structPowerToBuild(psStruct) - psStruct->currentPowerAccrued; if (inc > 0) { //not accrued enough power, so check if there is enough available if (checkPower(selectedPlayer, inc, FALSE)) { //enough - so use it and set to complete usePower(selectedPlayer, inc); buildingComplete(psStruct); } } else { //enough power or more than enough! - either way, set to complete buildingComplete(psStruct); } //don't bother looking for any other droids working on it break; } } } } //check if have a completed repair facility on home world if (psStruct->pStructureType->type == REF_REPAIR_FACILITY AND psStruct->status == SS_BUILT) { bRepairExists = TRUE; } } //repair all droids back at home base if have a repair facility if (bRepairExists) { for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (droidIsDamaged(psDroid)) { psDroid->body = psDroid->originalBody; } } } //clear droid orders for all droids except constructors still building for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (orderStateObj(psDroid, DORDER_BUILD, (BASE_OBJECT **)&psStructBeingBuilt)) { if (psStructBeingBuilt->status == SS_BUILT) { orderDroid(psDroid, DORDER_STOP); } } else { orderDroid(psDroid, DORDER_STOP); } } //THIS HAPPENS AT THE END OF THE CAMCHANGE MISSION NOW - AB 22/12/98 //before copy the pointers over check selectedPlayer's mission.droids since //there might be some from the previous camapign //processPreviousCampDroids(); for (inc = 0; inc < MAX_PLAYERS; inc++) { mission.apsStructLists[inc] = apsStructLists[inc]; mission.apsDroidLists[inc] = apsDroidLists[inc]; mission.apsFeatureLists[inc] = apsFeatureLists[inc]; //mission.apsProxDisp[inc] = apsProxDisp[inc]; mission.apsFlagPosLists[inc] = apsFlagPosLists[inc]; } mission.playerX = player.p.x; mission.playerY = player.p.z; //save the power settings saveMissionPower(); //reset before loading in the new game //resetFactoryNumFlag(); //init before loading in the new game initFactoryNumFlag(); //clear all the effects from the map initEffectsSystem(); resetRadarRedraw(); } /* This routine frees the memory for the offworld mission map (in the call to mapShutdown) - so when this routine is called we must still be set to the offworld map data i.e. We shoudn't have called SwapMissionPointers() */ void restoreMissionData(void) { UDWORD inc; BASE_OBJECT *psObj; //clear out the audio audio_StopAll(); //clear all the lists //clearPlayerPower(); proj_FreeAllProjectiles(); freeAllDroids(); freeAllStructs(); freeAllFeatures(); gwShutDown(); mapShutdown(); // FREE(psMapTiles); // mapFreeTilesAndStrips(); //FREE(aMapLinePoints); //releaseAllProxDisp(); //flag positions go with structs //restore the game pointers for (inc = 0; inc < MAX_PLAYERS; inc++) { apsDroidLists[inc] = mission.apsDroidLists[inc]; mission.apsDroidLists[inc] = NULL; for(psObj = (BASE_OBJECT *)apsDroidLists[inc]; psObj; psObj=psObj->psNext) { gridAddObject(psObj); //make sure the died flag is not set psObj->died = FALSE; } apsStructLists[inc] = mission.apsStructLists[inc]; mission.apsStructLists[inc] = NULL; for(psObj = (BASE_OBJECT *)apsStructLists[inc]; psObj; psObj=psObj->psNext) { gridAddObject(psObj); } apsFeatureLists[inc] = mission.apsFeatureLists[inc]; mission.apsFeatureLists[inc] = NULL; for(psObj = (BASE_OBJECT *)apsFeatureLists[inc]; psObj; psObj=psObj->psNext) { gridAddObject(psObj); } apsFlagPosLists[inc] = mission.apsFlagPosLists[inc]; mission.apsFlagPosLists[inc] = NULL; //apsProxDisp[inc] = mission.apsProxDisp[inc]; //mission.apsProxDisp[inc] = NULL; //asPower[inc]->usedPower = mission.usedPower[inc]; //init the next structure to be powered asPower[inc]->psLastPowered = NULL; } //swap mission data over psMapTiles = mission.psMapTiles; aMapLinePoints = mission.aMapLinePoints; mapWidth = mission.mapWidth; mapHeight = mission.mapHeight; scrollMinX = mission.scrollMinX; scrollMinY = mission.scrollMinY; scrollMaxX = mission.scrollMaxX; scrollMaxY = mission.scrollMaxY; psGateways = mission.psGateways; apRLEZones = mission.apRLEZones; gwNumZones = mission.gwNumZones; aNumEquiv = mission.aNumEquiv; apEquivZones = mission.apEquivZones; aZoneReachable = mission.aZoneReachable; //and clear the mission pointers mission.psMapTiles = NULL; mission.aMapLinePoints = NULL; mission.mapWidth = 0; mission.mapHeight = 0; mission.scrollMinX = 0; mission.scrollMinY = 0; mission.scrollMaxX = 0; mission.scrollMaxY = 0; mission.psGateways = NULL; mission.apRLEZones = NULL; mission.gwNumZones = 0; mission.aNumEquiv = NULL; mission.apEquivZones = NULL; mission.aZoneReachable = NULL; //reset the current structure lists setCurrentStructQuantity(FALSE); //initPlayerPower(); initFactoryNumFlag(); resetFactoryNumFlag(); //terrain types? - hopefully not! otherwise we have to load in the terrain texture pages. //reset the game time //gameTimeReset(mission.startTime); offWorldKeepLists = FALSE; resetRadarRedraw(); //reset the environ map back to the homebase settings environReset(); //intSetMapPos(mission.playerX, mission.playerY); } /*Saves the necessary data when moving from one mission to a limbo expand Mission*/ void saveMissionLimboData(void) { DROID *psDroid, *psNext; //UDWORD droidX, droidY; //PICKTILE pickRes; STRUCTURE *psStruct; //clear out the audio audio_StopAll(); //before copy the pointers over check selectedPlayer's mission.droids since //there might be some from the previous camapign processPreviousCampDroids(); //only need to keep the selectedPlayer's droid's separate //mission.apsDroidLists[selectedPlayer] = apsDroidLists[selectedPlayer]; //apsDroidLists[selectedPlayer] = NULL; //move droids properly - does all the clean up code for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; if (droidRemove(psDroid, apsDroidLists)) { addDroid(psDroid, mission.apsDroidLists); } } apsDroidLists[selectedPlayer] = NULL; //this is happening in a separate function now so can be called once the mission has started /*apsDroidLists[selectedPlayer] = apsLimboDroids[selectedPlayer]; apsLimboDroids[selectedPlayer] = NULL; //set up location for each of the droids for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { droidX = getLandingX(LIMBO_LANDING) >> TILE_SHIFT; droidY = getLandingY(LIMBO_LANDING) >> TILE_SHIFT; pickRes = pickHalfATile(&droidX, &droidY,LOOK_FOR_EMPTY_TILE); if (pickRes == NO_FREE_TILE ) { ASSERT( FALSE, "saveMissionLimboData: Unable to find a free location" ); } psDroid->x = (UWORD)(droidX << TILE_SHIFT); psDroid->y = (UWORD)(droidY << TILE_SHIFT); if (pickRes == HALF_FREE_TILE ) { psDroid->x += TILE_UNITS; psDroid->y += TILE_UNITS; } psDroid->z = map_Height(psDroid->x, psDroid->y); updateDroidOrientation(psDroid); //psDroid->lastTile = mapTile(psDroid->x >> TILE_SHIFT, // psDroid->y >> TILE_SHIFT); psDroid->selected = FALSE; //this is mainly for VTOLs psDroid->psBaseStruct = NULL; psDroid->cluster = 0; //initialise the movement data initDroidMovement(psDroid); //make sure the died flag is not set psDroid->died = FALSE; }*/ //any selectedPlayer's factories/research need to be put on holdProduction/holdresearch for (psStruct = apsStructLists[selectedPlayer]; psStruct != NULL; psStruct = psStruct->psNext) { if (StructIsFactory(psStruct)) { holdProduction(psStruct); } else if (psStruct->pStructureType->type == REF_RESEARCH) { holdResearch(psStruct); } } } //this is called via a script function to place the Limbo droids once the mission has started void placeLimboDroids(void) { DROID *psDroid, *psNext; UDWORD droidX, droidY; PICKTILE pickRes; //copy the droids across for the selected Player for (psDroid = apsLimboDroids[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; if (droidRemove(psDroid, apsLimboDroids)) { addDroid(psDroid, apsDroidLists); //KILL OFF TRANSPORTER - should never be one but.... if (psDroid->droidType == DROID_TRANSPORTER) { vanishDroid(psDroid); continue; } //set up location for each of the droids droidX = getLandingX(LIMBO_LANDING) >> TILE_SHIFT; droidY = getLandingY(LIMBO_LANDING) >> TILE_SHIFT; pickRes = pickHalfATile(&droidX, &droidY,LOOK_FOR_EMPTY_TILE); if (pickRes == NO_FREE_TILE ) { ASSERT( FALSE, "placeLimboUnits: Unable to find a free location" ); } psDroid->x = (UWORD)(droidX << TILE_SHIFT); psDroid->y = (UWORD)(droidY << TILE_SHIFT); if (pickRes == HALF_FREE_TILE ) { psDroid->x += TILE_UNITS; psDroid->y += TILE_UNITS; } psDroid->z = map_Height(psDroid->x, psDroid->y); updateDroidOrientation(psDroid); //psDroid->lastTile = mapTile(psDroid->x >> TILE_SHIFT, // psDroid->y >> TILE_SHIFT); psDroid->selected = FALSE; //this is mainly for VTOLs psDroid->psBaseStruct = NULL; psDroid->cluster = 0; //initialise the movement data initDroidMovement(psDroid); //make sure the died flag is not set psDroid->died = FALSE; } else { ASSERT( FALSE, "placeLimboUnits: Unable to remove unit from Limbo list" ); } } } /*restores the necessary data on completion of a Limbo Expand mission*/ void restoreMissionLimboData(void) { DROID *psDroid, *psNext; /*the droids stored in the mission droid list need to be added back into the current droid list*/ for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; //remove out of stored list and add to current Droid list if (droidRemove(psDroid, mission.apsDroidLists)) { addDroid(psDroid, apsDroidLists); psDroid->cluster = 0; gridAddObject((BASE_OBJECT *)psDroid); //reset droid orders orderDroid(psDroid, DORDER_STOP); //the location of the droid should be valid! } } ASSERT( mission.apsDroidLists[selectedPlayer] == NULL, "restoreMissionLimboData: list should be empty" ); } /*Saves the necessary data when moving from one campaign to the start of the next - saves out the list of droids for the selected player*/ void saveCampaignData(void) { UBYTE inc; DROID *psDroid, *psNext, *psSafeDroid, *psNextSafe, *psCurr, *psCurrNext; //if the droids have been moved to safety then get any Transporters that exist if (getDroidsToSafetyFlag()) { //move any Transporters into the mission list psDroid = apsDroidLists[selectedPlayer]; while(psDroid != NULL) { psNext = psDroid->psNext; if (psDroid->droidType == DROID_TRANSPORTER) { /*Now - we want to empty the transporter and make it turn up with the first ten out of the previous mission*/ //we want to make sure they are full /*if (calcRemainingCapacity(psDroid)) //before we move the droid into the mission list - check to see if it's empty //if (psDroid->psGroup AND psDroid->psGroup->refCount == 1) { //fill it with droids from the mission list for (psSafeDroid = mission.apsDroidLists[selectedPlayer]; psSafeDroid != NULL; psSafeDroid = psNextSafe) { psNextSafe = psSafeDroid->psNext; //add to the Transporter, checking for when full if (checkTransporterSpace(psDroid, psSafeDroid)) { if (droidRemove(psSafeDroid, mission.apsDroidLists)) { grpJoin(psDroid->psGroup, psSafeDroid); } } else { //setting this will cause the loop to end psNextSafe = NULL; } } }*/ //empty the transporter into the mission list ASSERT( psDroid->psGroup != NULL, "saveCampaignData: Transporter does not have a group" ); for (psCurr = psDroid->psGroup->psList; psCurr != NULL AND psCurr != psDroid; psCurr = psCurrNext) { psCurrNext = psCurr->psGrpNext; //remove it from the transporter group grpLeave( psDroid->psGroup, psCurr); //cam change add droid psCurr->x = INVALID_XY; psCurr->y = INVALID_XY; //add it back into current droid lists addDroid(psCurr, mission.apsDroidLists); } //remove the transporter from the current list if (droidRemove(psDroid, apsDroidLists)) { //cam change add droid psDroid->x = INVALID_XY; psDroid->y = INVALID_XY; addDroid(psDroid, mission.apsDroidLists); } } psDroid = psNext; } } else { //reserve the droids for selected player for start of next campaign mission.apsDroidLists[selectedPlayer] = apsDroidLists[selectedPlayer]; apsDroidLists[selectedPlayer] = NULL; psDroid = mission.apsDroidLists[selectedPlayer]; while(psDroid != NULL) { //cam change add droid psDroid->x = INVALID_XY; psDroid->y = INVALID_XY; psDroid = psDroid->psNext; } } //if the droids have been moved to safety then get any Transporters that exist if (getDroidsToSafetyFlag()) { /*now that every unit for the selected player has been moved into the mission list - reverse it and fill the transporter with the first ten units*/ reverseObjectList((BASE_OBJECT**)&mission.apsDroidLists[selectedPlayer]); //find the *first* transporter for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType == DROID_TRANSPORTER) { //fill it with droids from the mission list for (psSafeDroid = mission.apsDroidLists[selectedPlayer]; psSafeDroid != NULL; psSafeDroid = psNextSafe) { psNextSafe = psSafeDroid->psNext; if (psSafeDroid != psDroid) { //add to the Transporter, checking for when full if (checkTransporterSpace(psDroid, psSafeDroid)) { if (droidRemove(psSafeDroid, mission.apsDroidLists)) { grpJoin(psDroid->psGroup, psSafeDroid); } } else { //setting this will cause the loop to end psNextSafe = NULL; } } } //only want to fill one transporter break; } } } //clear all other droids for (inc = 0; inc < MAX_PLAYERS; inc++) { psDroid = apsDroidLists[inc]; while(psDroid != NULL) { psNext = psDroid->psNext; vanishDroid(psDroid); psDroid = psNext; } } //clear out the audio audio_StopAll(); //clear all other memory freeAllStructs(); freeAllFeatures(); } //start an off world mission - clearing the object lists BOOL startMissionOffClear(STRING *pGame) { saveMissionData(); //load in the new game clearing the lists if (!loadGame(pGame, !KEEPOBJECTS, !FREEMEM,FALSE)) { return FALSE; } //call after everything has been loaded up - done on stageThreeInit //gridReset(); offWorldKeepLists = FALSE; intResetPreviousObj(); //this gets set when the timer is added in scriptFuncs //mission.startTime = gameTime; // ffs ab //the message should have been played at the between stage missionCountDown &= ~NOT_PLAYED_ACTIVATED; return TRUE; } //start an off world mission - keeping the object lists BOOL startMissionOffKeep(STRING *pGame) { saveMissionData(); //load in the new game clearing the lists if (!loadGame(pGame, !KEEPOBJECTS, !FREEMEM,FALSE)) { return FALSE; } //call after everything has been loaded up - done on stageThreeInit //gridReset(); offWorldKeepLists = TRUE; intResetPreviousObj(); //this gets set when the timer is added in scriptFuncs //mission.startTime = gameTime; // ffs ab //the message should have been played at the between stage missionCountDown &= ~NOT_PLAYED_ACTIVATED; return TRUE; } BOOL startMissionCampaignStart(STRING *pGame) { //clear out all intelligence screen messages freeMessages(); //check no units left with any settings that are invalid clearCampaignUnits(); //load in the new game details if (!loadGame(pGame, !KEEPOBJECTS, FREEMEM, FALSE)) { return FALSE; } //call after everything has been loaded up - done on stageThreeInit //gridReset(); offWorldKeepLists = FALSE; return TRUE; } BOOL startMissionCampaignChange(STRING *pGame) { //clear out all intelligence screen messages freeMessages(); //check no units left with any settings that are invalid clearCampaignUnits(); //clear out the production run between campaigns changeProductionPlayer((UBYTE)selectedPlayer); saveCampaignData(); //load in the new game details if (!loadGame(pGame, !KEEPOBJECTS, !FREEMEM, FALSE)) { return FALSE; } offWorldKeepLists = FALSE; intResetPreviousObj(); return TRUE; } BOOL startMissionCampaignExpand(STRING *pGame) { //load in the new game details if (!loadGame(pGame, KEEPOBJECTS, !FREEMEM, FALSE)) { return FALSE; } //call after everything has been loaded up - done on stageThreeInit //gridReset(); offWorldKeepLists = FALSE; return TRUE; } BOOL startMissionCampaignExpandLimbo(STRING *pGame) { saveMissionLimboData(); //load in the new game details if (!loadGame(pGame, KEEPOBJECTS, !FREEMEM, FALSE)) { return FALSE; } //call after everything has been loaded up - done on stageThreeInit //gridReset(); offWorldKeepLists = FALSE; return TRUE; } BOOL startMissionBetween(void) { offWorldKeepLists = FALSE; return TRUE; } //check no units left with any settings that are invalid void clearCampaignUnits(void) { DROID *psDroid; for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { orderDroid(psDroid, DORDER_STOP); psDroid->psBaseStruct = NULL; } } /*This deals with droids at the end of an offworld mission*/ void processMission() { DROID *psNext; DROID *psDroid; UDWORD droidX, droidY; PICKTILE pickRes; //and the rest on the mission map - for now? for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; //reset order - do this to all the droids that are returning from offWorld orderDroid(psDroid, DORDER_STOP); //remove out of stored list and add to current Droid list if (droidRemove(psDroid, apsDroidLists)) { addDroid(psDroid, mission.apsDroidLists); // just remove the droid from the grid cos // there is only one grid which gets reset when we go back to the // campaign map //gridRemoveObject((BASE_OBJECT *)psDroid); - happens in droidRemove() //set the x/y for now //psDroid->x = getHomeLandingX() + 256; //psDroid->y = getHomeLandingY() + 256; //droidX = getLandingX(psDroid->player) >> TILE_SHIFT; //getHomeLandingX() >> TILE_SHIFT; //droidY = getLandingY(psDroid->player) >> TILE_SHIFT; //getHomeLandingY() >> TILE_SHIFT; droidX = getHomeLandingX(); droidY = getHomeLandingY(); //swap the droid and map pointers swapMissionPointers(); pickRes = pickHalfATile(&droidX, &droidY,LOOK_FOR_EMPTY_TILE); if (pickRes == NO_FREE_TILE ) { ASSERT( FALSE, "processMission: Unable to find a free location" ); } psDroid->x = (UWORD)(droidX << TILE_SHIFT); psDroid->y = (UWORD)(droidY << TILE_SHIFT); if (pickRes == HALF_FREE_TILE ) { psDroid->x += TILE_UNITS; psDroid->y += TILE_UNITS; } psDroid->z = map_Height(psDroid->x, psDroid->y); updateDroidOrientation(psDroid); /* psDroid->lastTile = mapTile(psDroid->x >> TILE_SHIFT, psDroid->y >> TILE_SHIFT); */ //swap the droid and map pointers back again swapMissionPointers(); psDroid->selected = FALSE; //this is mainly for VTOLs psDroid->psBaseStruct = NULL; psDroid->cluster = 0; //initialise the movement data initDroidMovement(psDroid); //orderSelectedLoc(psDroid->player, psDroid->x + 3 * TILE_UNITS, // psDroid->y + 3 * TILE_UNITS); } } } #define MAXLIMBODROIDS (999) /*This deals with droids at the end of an offworld Limbo mission*/ void processMissionLimbo(void) { DROID *psNext, *psDroid; UDWORD numDroidsAddedToLimboList=0; //all droids (for selectedPlayer only) are placed into the limbo list for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; //KILL OFF TRANSPORTER - should never be one but.... if (psDroid->droidType == DROID_TRANSPORTER) { vanishDroid(psDroid); } else { if (numDroidsAddedToLimboList >= MAXLIMBODROIDS) // any room in limbo list { vanishDroid(psDroid); } else { //remove out of stored list and add to current Droid list if (droidRemove(psDroid, apsDroidLists)) { //limbo list invalidate XY psDroid->x = INVALID_XY; psDroid->y = INVALID_XY; addDroid(psDroid, apsLimboDroids); //this is mainly for VTOLs psDroid->psBaseStruct = NULL; psDroid->cluster = 0; orderDroid(psDroid, DORDER_STOP); numDroidsAddedToLimboList++; } } } } } /*switch the pointers for the map and droid lists so that droid placement and orientation can occur on the map they will appear on*/ void swapMissionPointers(void) { void *pVoid; void **ppVoid; UDWORD udwTemp, inc; //swap psMapTiles pVoid = (void*)psMapTiles; psMapTiles = mission.psMapTiles; mission.psMapTiles = (MAPTILE *)pVoid; //swap map sizes udwTemp = mapWidth; mapWidth = mission.mapWidth; mission.mapWidth = udwTemp; udwTemp = mapHeight; mapHeight = mission.mapHeight; mission.mapHeight = udwTemp; //swap gateway zones pVoid = (void*)psGateways; psGateways = mission.psGateways; mission.psGateways = (struct _gateway *)pVoid; ppVoid = (void**)apRLEZones; apRLEZones = mission.apRLEZones; mission.apRLEZones = (UBYTE **)ppVoid; udwTemp = (UDWORD)gwNumZones; gwNumZones = mission.gwNumZones; mission.gwNumZones = (SDWORD)udwTemp; pVoid = (void*)aNumEquiv; aNumEquiv = mission.aNumEquiv; mission.aNumEquiv = (UBYTE *)pVoid; ppVoid = (void**)apEquivZones; apEquivZones = mission.apEquivZones; mission.apEquivZones = (UBYTE **)ppVoid; pVoid = (void*)aZoneReachable; aZoneReachable = mission.aZoneReachable; mission.aZoneReachable = (UBYTE *)pVoid; //swap scroll limits udwTemp = scrollMinX; scrollMinX = mission.scrollMinX; mission.scrollMinX = udwTemp; udwTemp = scrollMinY; scrollMinY = mission.scrollMinY; mission.scrollMinY = udwTemp; udwTemp = scrollMaxX; scrollMaxX = mission.scrollMaxX; mission.scrollMaxX = udwTemp; udwTemp = scrollMaxY; scrollMaxY = mission.scrollMaxY; mission.scrollMaxY = udwTemp; for (inc = 0; inc < MAX_PLAYERS; inc++) { pVoid = (void*)apsDroidLists[inc]; apsDroidLists[inc] = mission.apsDroidLists[inc]; mission.apsDroidLists[inc] = (DROID *)pVoid; pVoid = (void*)apsStructLists[inc]; apsStructLists[inc] = mission.apsStructLists[inc]; mission.apsStructLists[inc] = (STRUCTURE *)pVoid; pVoid = (void*)apsFeatureLists[inc]; apsFeatureLists[inc] = mission.apsFeatureLists[inc]; mission.apsFeatureLists[inc] = (FEATURE *)pVoid; pVoid = (void*)apsFlagPosLists[inc]; apsFlagPosLists[inc] = mission.apsFlagPosLists[inc]; mission.apsFlagPosLists[inc] = (FLAG_POSITION *)pVoid; } /* for (inc = 0; inc < MAX_PLAYERS; inc++) { udwTemp = iTranspEntryTileX[inc]; iTranspEntryTileX[inc] = mission.iTranspEntryTileX[inc]; mission.iTranspEntryTileX[inc] = udwTemp; udwTemp = iTranspEntryTileY[inc]; iTranspEntryTileY[inc] = mission.iTranspEntryTileY[inc]; mission.iTranspEntryTileY[inc] = udwTemp; udwTemp = iTranspExitTileX[inc]; iTranspExitTileX[inc] = mission.iTranspExitTileX[inc]; mission.iTranspExitTileX[inc] = udwTemp; udwTemp = iTranspExitTileY[inc]; iTranspExitTileY[inc] = mission.iTranspExitTileY[inc]; mission.iTranspExitTileY[inc] = udwTemp; } */ // NOTE: none of the gateway pointers are swapped at the moment // which isn't a problem for the current usage - might need to be // added later /* stuff to add UDWORD type; //defines which start and end functions to use - see levels_type in levels.h //struct _proximity_display *apsProxDisp[MAX_PLAYERS]; FLAG_POSITION *apsFlagPosLists[MAX_PLAYERS]; PLAYER_POWER asPower[MAX_PLAYERS]; //stuff for save game UDWORD startTime; //time the mission started SDWORD time; //how long the mission can last // < 0 = no limit SDWORD ETA; //time taken for reinforcements to arrive // < 0 = none allowed UWORD homeLZ_X; //selectedPlayer's LZ x and y UWORD homeLZ_Y; SDWORD playerX; //original view position SDWORD playerY; */ } void endMission(void) { if (mission.type == LDS_NONE) { //can't go back any further!! debug( LOG_NEVER, "Already returned from mission" ); return; } switch (mission.type) { case LDS_CAMSTART: { //any transporters that are flying in need to be emptied emptyTransporters(FALSE); //when loading in a save game mid cam2a or cam3a it is loaded as a camstart endMissionCamChange(); /* This is called at the end of every campaign mission */ break; } case LDS_MKEEP: { //any transporters that are flying in need to be emptied emptyTransporters(TRUE); endMissionOffKeep(); break; } case LDS_EXPAND: case LDS_BETWEEN: { /* This is called at the end of every campaign mission */ break; } case LDS_CAMCHANGE: { //any transporters that are flying in need to be emptied emptyTransporters(FALSE); endMissionCamChange(); break; } /* left in so can skip the mission for testing...*/ case LDS_EXPAND_LIMBO: { //shouldn't be any transporters on this mission but...who knows? endMissionExpandLimbo(); break; } case LDS_MCLEAR: { //any transporters that are flying in need to be emptied emptyTransporters(TRUE); endMissionOffClear(); break; } case LDS_MKEEP_LIMBO: { //any transporters that are flying in need to be emptied emptyTransporters(TRUE); endMissionOffKeepLimbo(); break; } default: { //error! debug( LOG_ERROR, "Unknown Mission Type" ); abort(); } } if (missionCanReEnforce()) //mission.type == LDS_MCLEAR OR mission.type == LDS_MKEEP) { intRemoveMissionTimer(); intRemoveTransporterTimer(); } //at end of mission always do this intRemoveTransporterLaunch(); //and this... //make sure the cheat time is not set for the next mission mission.cheatTime = 0; //reset the bSetPlayCountDown flag setPlayCountDown(TRUE); //mission.type = MISSION_NONE; mission.type = LDS_NONE; // reset the transporters initTransporters(); } void endMissionCamChange(void) { //get any droids remaining from the previous campaign processPreviousCampDroids(); } void endMissionOffClear(void) { processMission(); //called in setUpMission now - AB 6/4/98 //intAddMissionResult(result);// ajl. fire up interface. restoreMissionData(); //reset variables in Droids missionResetDroids(); } void endMissionOffKeep(void) { processMission(); //called in setUpMission now - AB 6/4/98 //intAddMissionResult(result);// ajl. fire up interface. restoreMissionData(); //reset variables in Droids missionResetDroids(); } /*In this case any droids remaining (for selectedPlayer) go into a limbo list for use in a future mission (expand type) */ void endMissionOffKeepLimbo(void) { //save any droids left 'alive' processMissionLimbo(); //set the lists back to the home base lists restoreMissionData(); //reset variables in Droids missionResetDroids(); } //This happens MID_MISSION now! but is left here in case the scripts fail but somehow get here...? /*The selectedPlayer's droids which were separated at the start of the mission need to merged back into the list*/ void endMissionExpandLimbo(void) { restoreMissionLimboData(); } //this is called mid Limbo mission via the script void resetLimboMission(void) { //add the units that were moved into the mission list at the start of the mission restoreMissionLimboData(); //set the mission type to plain old expand... mission.type = LDS_EXPAND; } /* The AI update routine for all Structures left back at base during a Mission*/ void aiUpdateMissionStructure(STRUCTURE *psStructure) { BASE_STATS *pSubject = NULL; UDWORD pointsToAdd; PLAYER_RESEARCH *pPlayerRes = asPlayerResList[psStructure->player]; UDWORD structureMode = 0; DROID *psNewDroid; UBYTE Quantity; FACTORY *psFactory; RESEARCH_FACILITY *psResFacility; #ifdef INCLUDE_FACTORYLISTS DROID_TEMPLATE *psNextTemplate; #endif ASSERT( PTRVALID(psStructure, sizeof(STRUCTURE)), "aiUpdateMissionStructure: invalid Structure pointer" ); ASSERT( (psStructure->pStructureType->type == REF_FACTORY OR psStructure->pStructureType->type == REF_CYBORG_FACTORY OR psStructure->pStructureType->type == REF_VTOL_FACTORY OR psStructure->pStructureType->type == REF_RESEARCH), "aiUpdateMissionStructure: Structure is not a Factory or Research Facility" ); //only interested if the Structure "does" something! if (psStructure->pFunctionality == NULL) { return; } //check if any power available if ((asPower[psStructure->player]->currentPower > POWER_PER_CYCLE) OR (!powerCalculated)) { //check if this structure is due some power if (getLastPowered((BASE_OBJECT *)psStructure)) { //get some power if necessary if (accruePower((BASE_OBJECT *)psStructure)) { updateLastPowered((BASE_OBJECT *)psStructure, psStructure->player); } } } //determine the Subject switch (psStructure->pStructureType->type) { case REF_RESEARCH: { pSubject = ((RESEARCH_FACILITY*)psStructure->pFunctionality)->psSubject; structureMode = REF_RESEARCH; break; } case REF_FACTORY: case REF_CYBORG_FACTORY: case REF_VTOL_FACTORY: { pSubject = ((FACTORY*)psStructure->pFunctionality)->psSubject; structureMode = REF_FACTORY; //check here to see if the factory's commander has died if (((FACTORY*)psStructure->pFunctionality)->psCommander AND ((FACTORY*)psStructure->pFunctionality)->psCommander->died) { //remove the commander from the factory assignFactoryCommandDroid(psStructure, NULL); } break; } } if (pSubject != NULL) { //if subject is research... if (structureMode == REF_RESEARCH) { psResFacility = (RESEARCH_FACILITY*)psStructure->pFunctionality; //if on hold don't do anything if (psResFacility->timeStartHold) { return; } pPlayerRes += (pSubject->ref - REF_RESEARCH_START); //check research has not already been completed by another structure if (IsResearchCompleted(pPlayerRes)==0) { //check to see if enough power to research has accrued if (psResFacility->powerAccrued < ((RESEARCH *)pSubject)->researchPower) { //wait until enough power return; } if (psResFacility->timeStarted == ACTION_START_TIME) { //set the time started psResFacility->timeStarted = gameTime; } pointsToAdd = (psResFacility->researchPoints * (gameTime - psResFacility->timeStarted)) / GAME_TICKS_PER_SEC; //check if Research is complete //if ((pointsToAdd + pPlayerRes->currentPoints) > psResFacility-> // timeToResearch) if ((pointsToAdd + pPlayerRes->currentPoints) > ( (RESEARCH *)pSubject)->researchPoints) { //store the last topic researched - if its the best if (psResFacility->psBestTopic == NULL) { psResFacility->psBestTopic = psResFacility->psSubject; } else { if (((RESEARCH *)psResFacility->psSubject)->researchPoints > ((RESEARCH *)psResFacility->psBestTopic)->researchPoints) { psResFacility->psSubject = psResFacility->psSubject; } } psResFacility->psSubject = NULL; intResearchFinished(psStructure); researchResult(pSubject->ref - REF_RESEARCH_START, psStructure->player, TRUE); //check if this result has enabled another topic intCheckResearchButton(); } } else { //cancel this Structure's research since now complete psResFacility->psSubject = NULL; intResearchFinished(psStructure); } } //check for manufacture else if (structureMode == REF_FACTORY) { psFactory = (FACTORY *)psStructure->pFunctionality; Quantity = psFactory->quantity; //if on hold don't do anything if (psFactory->timeStartHold) { return; } // if (psFactory->timeStarted == ACTION_START_TIME) // { // // also need to check if a command droid's group is full // if ( ( psFactory->psCommander != NULL ) && // ( grpNumMembers( psFactory->psCommander->psGroup ) >= // cmdDroidMaxGroup( psFactory->psCommander ) ) ) // { // return; // } // } if(CheckHaltOnMaxUnitsReached(psStructure) == TRUE) { return; } //check enough power has accrued to build the droid if (psFactory->powerAccrued < ((DROID_TEMPLATE *)pSubject)-> powerPoints) { //wait until enough power return; } /*must be enough power so subtract that required to build*/ if (psFactory->timeStarted == ACTION_START_TIME) { //set the time started psFactory->timeStarted = gameTime; } pointsToAdd = (gameTime - psFactory->timeStarted) / GAME_TICKS_PER_SEC; //check for manufacture to be complete if (pointsToAdd > psFactory->timeToBuild) { //build droid - store in mission list psNewDroid = buildMissionDroid((DROID_TEMPLATE *)pSubject, psStructure->x, psStructure->y, psStructure->player); if (!psNewDroid) { //if couldn't build then cancel the production Quantity = 0; psFactory->psSubject = NULL; intManufactureFinished(psStructure); } else { if (psStructure->player == selectedPlayer) { intRefreshScreen(); // update the interface. } //store the factory as the droid's baseStructure instead //add it to the factory group /*if (!psFactory->psGroup) { //create the factory group if (!grpCreate(&psFactory->psGroup)) { DBPRINTF(("missionUpdateStructure: unable to create group\n")); } else { grpJoin(psFactory->psGroup, psNewDroid); } } else { grpJoin(psFactory->psGroup, psNewDroid); }*/ psNewDroid->psBaseStruct = psStructure; //reset the start time psFactory->timeStarted = ACTION_START_TIME; psFactory->powerAccrued = 0; #ifdef INCLUDE_FACTORYLISTS //next bit for productionPlayer only if (productionPlayer == psStructure->player) { psNextTemplate = factoryProdUpdate(psStructure, (DROID_TEMPLATE *)pSubject); if (psNextTemplate) { structSetManufacture(psStructure, psNextTemplate,Quantity); return; } else { //nothing more to manufacture - reset the Subject and Tab on HCI Form psFactory->psSubject = NULL; intManufactureFinished(psStructure); } } else #endif { //decrement the quantity to manufacture if not set to infinity if (Quantity != NON_STOP_PRODUCTION) { psFactory->quantity--; Quantity--; } // If quantity not 0 then kick of another manufacture. if(Quantity) { // Manufacture another. structSetManufacture(psStructure, (DROID_TEMPLATE*)pSubject,Quantity); //playerNewDroid(psNewDroid); return; } else { //when quantity = 0, reset the Subject and Tab on HCI Form psFactory->psSubject = NULL; intManufactureFinished(psStructure); } } } } } } } /* The update routine for all Structures left back at base during a Mission*/ void missionStructureUpdate(STRUCTURE *psBuilding) { ASSERT( PTRVALID(psBuilding, sizeof(STRUCTURE)), "structureUpdate: Invalid Structure pointer" ); //update the manufacture/research of the building // if (psBuilding->pStructureType->type == REF_FACTORY OR // psBuilding->pStructureType->type == REF_CYBORG_FACTORY OR // psBuilding->pStructureType->type == REF_VTOL_FACTORY OR // psBuilding->pStructureType->type == REF_RESEARCH) if(StructIsFactory(psBuilding) OR psBuilding->pStructureType->type == REF_RESEARCH) { if (psBuilding->status == SS_BUILT) { aiUpdateMissionStructure(psBuilding); } } } /* The update routine for all droids left back at home base Only interested in Transporters at present*/ void missionDroidUpdate(DROID *psDroid) { ASSERT( PTRVALID(psDroid, sizeof(DROID)), "unitUpdate: Invalid unit pointer" ); /*This is required for Transporters that are moved offWorld so the saveGame doesn't try to set their position in the map - especially important for endCam2 where there isn't a valid map!*/ if (psDroid->droidType == DROID_TRANSPORTER) { psDroid->x = INVALID_XY; psDroid->y = INVALID_XY; } //ignore all droids except Transporters if ( (psDroid->droidType != DROID_TRANSPORTER) || !(orderState(psDroid, DORDER_TRANSPORTOUT) || orderState(psDroid, DORDER_TRANSPORTIN) || orderState(psDroid, DORDER_TRANSPORTRETURN)) ) { return; } // NO ai update droid // update the droids order orderUpdateDroid(psDroid); // update the action of the droid actionUpdateDroid(psDroid); //NO move update } //reset variables in Droids such as order and position void missionResetDroids() { UDWORD player; DROID *psDroid, *psNext; STRUCTURE *psStruct; FACTORY *psFactory = NULL; // UDWORD mapX, mapY; BOOL placed; UDWORD x, y; PICKTILE pickRes; for (player = 0; player < MAX_PLAYERS; player++) { for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; //reset order - unless constructor droid that is mid-build //if (psDroid->droidType == DROID_CONSTRUCT AND orderStateObj(psDroid, if ((psDroid->droidType == DROID_CONSTRUCT OR psDroid->droidType == DROID_CYBORG_CONSTRUCT) AND orderStateObj(psDroid, DORDER_BUILD, (BASE_OBJECT **)&psStruct)) { //need to set the action time to ignore the previous mission time psDroid->actionStarted = gameTime; } else { orderDroid(psDroid, DORDER_STOP); } //KILL OFF TRANSPORTER if (psDroid->droidType == DROID_TRANSPORTER) { vanishDroid(psDroid); } } } //don't need to implement this hack now since using pickATile - oh what a wonderful routine... //only need to look through the selected players list /*need to put all the droids that were built whilst off on the mission into a temp list so can add them back into the world one by one so that the path blocking routines do not fail*/ /*for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; //for all droids that have never left home base if (psDroid->x == INVALID_XY AND psDroid->y == INVALID_XY) { if (droidRemove(psDroid, apsDroidLists)) { addDroid(psDroid, mission.apsDroidLists); } } }*/ for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) //for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; // psDroid = psNext) { psNext = psDroid->psNext; //for all droids that have never left home base if (psDroid->x == INVALID_XY AND psDroid->y == INVALID_XY) { //this is now stored as the baseStruct when the droid is built... //find which factory produced the droid /*psFactory = NULL; for (psStruct = apsStructLists[psDroid->player]; psStruct != NULL; psStruct = psStruct->psNext) { if(StructIsFactory(psStruct)) { if (((FACTORY *)psStruct->pFunctionality)->psGroup == psDroid->psGroup) { //found the right factory psFactory = (FACTORY *)psStruct->pFunctionality; break; } } }*/ psStruct = psDroid->psBaseStruct; if (psStruct AND StructIsFactory(psStruct)) { psFactory = (FACTORY *)psStruct->pFunctionality; } placed = FALSE; //find a location next to the factory if (psStruct) { /*placed = placeDroid(psStruct, &x, &y); if (placed) { psDroid->x = (UWORD)(x << TILE_SHIFT); psDroid->y = (UWORD)(y << TILE_SHIFT); } else { psStruct = NULL; }*/ //use pickATile now - yipeee! //use factory DP if one if (psFactory->psAssemblyPoint) { x = (UWORD)(psFactory->psAssemblyPoint->coords.x >> TILE_SHIFT); y = (UWORD)(psFactory->psAssemblyPoint->coords.y >> TILE_SHIFT); } else { x = (UWORD)(psStruct->x >> TILE_SHIFT); y = (UWORD)(psStruct->y >> TILE_SHIFT); } pickRes = pickHalfATile(&x, &y,LOOK_FOR_EMPTY_TILE); if (pickRes == NO_FREE_TILE ) { ASSERT( FALSE, "missionResetUnits: Unable to find a free location" ); psStruct = NULL; } else { psDroid->x = (UWORD)(x << TILE_SHIFT); psDroid->y = (UWORD)(y << TILE_SHIFT); if (pickRes == HALF_FREE_TILE ) { psDroid->x += TILE_UNITS; psDroid->y += TILE_UNITS; } placed = TRUE; } } //if couldn't find the factory - hmmm if (!psStruct) { //just stick them near the HQ for (psStruct = apsStructLists[psDroid->player]; psStruct != NULL; psStruct = psStruct->psNext) { if (psStruct->pStructureType->type == REF_HQ) { /*psDroid->x = (UWORD)(psStruct->x + 128); psDroid->y = (UWORD)(psStruct->y + 128); placed = TRUE; break;*/ //use pickATile again... x = (UWORD)(psStruct->x >> TILE_SHIFT); y = (UWORD)(psStruct->y >> TILE_SHIFT); pickRes = pickHalfATile(&x, &y,LOOK_FOR_EMPTY_TILE); if (pickRes == NO_FREE_TILE ) { ASSERT( FALSE, "missionResetUnits: Unable to find a free location" ); psStruct = NULL; } else { psDroid->x = (UWORD)(x << TILE_SHIFT); psDroid->y = (UWORD)(y << TILE_SHIFT); if (pickRes == HALF_FREE_TILE ) { psDroid->x += TILE_UNITS; psDroid->y += TILE_UNITS; } placed = TRUE; } break; } } } if (placed) { //don't need to do this since using pickATile now... //add them to the current list /*if (droidRemove(psDroid, mission.apsDroidLists)) { addDroid(psDroid, apsDroidLists); if (psFactory) { //order the droid to the factory DP orderSelectedLoc(psDroid->player, psFactory->psAssemblyPoint->coords.x, psFactory->psAssemblyPoint->coords.y); } else { //order them to move to an arbitary new location! orderSelectedLoc(psDroid->player, psDroid->x + 3 * TILE_UNITS, psDroid->y + 3 * TILE_UNITS); }*/ //do all the things in build droid that never did when it was built! // check the droid is a reasonable distance from the edge of the map if (psDroid->x <= TILE_UNITS || psDroid->x >= (mapWidth * TILE_UNITS) - TILE_UNITS || psDroid->y <= TILE_UNITS || psDroid->y >= (mapHeight * TILE_UNITS) - TILE_UNITS) { debug( LOG_NEVER, "missionResetUnits: unit too close to edge of map - removing" ); vanishDroid(psDroid); continue; } // Check there is nothing on the map /*if ( TILE_OCCUPIED(mapTile(x >> TILE_SHIFT, y >> TILE_SHIFT)) ) { DBPRINTF(("missionResetDroids: tile occupied\n"); ` removeDroid(psDroid, apsDroidLists); droidRelease(psDroid); HEAP_FREE(psDroidHeap, psDroid); }*/ /* mapX = psDroid->x >> TILE_SHIFT; mapY = psDroid->y >> TILE_SHIFT; psDroid->lastTile = mapTile(mapX,mapY); */ //set droid height psDroid->z = map_Height(psDroid->x, psDroid->y); // People always stand upright //if(psDroid->droidType != DROID_PERSON AND psDroid->type != DROID_CYBORG) if(psDroid->droidType != DROID_PERSON AND !cyborgDroid(psDroid)) { updateDroidOrientation(psDroid); } visTilesUpdate((BASE_OBJECT *)psDroid,FALSE); //reset the selected flag psDroid->selected = FALSE; //} } else { //can't put it down so get rid of this droid!! ASSERT( FALSE,"missionResetUnits: can't place unit - cancel to continue" ); vanishDroid(psDroid); } } } } /*unloads the Transporter passed into the mission at the specified x/y goingHome = TRUE when returning from an off World mission*/ void unloadTransporter(DROID *psTransporter, UDWORD x, UDWORD y, BOOL goingHome) { DROID *psDroid, *psNext; DROID **ppCurrentList, **ppStoredList; UDWORD droidX, droidY; UWORD iX, iY; DROID_GROUP *psGroup; if (goingHome) { ppCurrentList = mission.apsDroidLists; ppStoredList = apsDroidLists; } else { ppCurrentList = apsDroidLists; ppStoredList = mission.apsDroidLists; } //look through the stored list of droids to see if there any transporters /*for (psTransporter = ppStoredList[selectedPlayer]; psTransporter != NULL; psTransporter = psTransporter->psNext) { if (psTransporter->droidType == DROID_TRANSPORTER) { //remove out of stored list and add to current Droid list removeDroid(psTransporter, ppStoredList); addDroid(psTransporter, ppCurrentList); //need to put the Transporter down at a specified location psTransporter->x = x; psTransporter->y = y; } }*/ //unload all the droids from within the current Transporter if (psTransporter->droidType == DROID_TRANSPORTER) { // If the scripts asked for transporter tracking then clear the "tracking a transporter" flag // since the transporters landed and unloaded now. if(psTransporter->player == selectedPlayer) { bTrackingTransporter = FALSE; } // reset the transporter cluster psTransporter->cluster = 0; for (psDroid = psTransporter->psGroup->psList; psDroid != NULL AND psDroid != psTransporter; psDroid = psNext) { psNext = psDroid->psGrpNext; //add it back into current droid lists addDroid(psDroid, ppCurrentList); //hack in the starting point! /*psDroid->x = x; psDroid->y = y; if (!goingHome) { //send the droids off to a starting location orderSelectedLoc(psDroid->player, x + 3*TILE_UNITS, y + 3*TILE_UNITS); }*/ //starting point...based around the value passed in droidX = x >> TILE_SHIFT; droidY = y >> TILE_SHIFT; if (goingHome) { //swap the droid and map pointers swapMissionPointers(); } if (!pickATileGen(&droidX, &droidY,LOOK_FOR_EMPTY_TILE,zonedPAT)) { ASSERT( FALSE, "unloadTransporter: Unable to find a valid location" ); } psDroid->x = (UWORD)(droidX << TILE_SHIFT); psDroid->y = (UWORD)(droidY << TILE_SHIFT); psDroid->z = map_Height(psDroid->x, psDroid->y); updateDroidOrientation(psDroid); // a commander needs to get it's group back if (psDroid->droidType == DROID_COMMAND) { if (grpCreate(&psGroup)) { grpJoin(psGroup, psDroid); } clearCommandDroidFactory(psDroid); } //initialise the movement data initDroidMovement(psDroid); //reset droid orders orderDroid(psDroid, DORDER_STOP); gridAddObject((BASE_OBJECT *)psDroid); psDroid->selected = FALSE; //this is mainly for VTOLs psDroid->psBaseStruct = NULL; psDroid->cluster = 0; if (goingHome) { //swap the droid and map pointers swapMissionPointers(); } //inform all other players if (bMultiPlayer) { sendDroidDisEmbark(psDroid); } } /* trigger script callback detailing group about to disembark */ transporterSetScriptCurrent( psTransporter ); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_TRANSPORTER_LANDED); transporterSetScriptCurrent( NULL ); /* remove droids from transporter group if not already transferred to script group */ for (psDroid = psTransporter->psGroup->psList; psDroid != NULL AND psDroid != psTransporter; psDroid = psNext) { psNext = psDroid->psGrpNext; grpLeave( psTransporter->psGroup, psDroid ); } } //don't do this in multiPlayer if (!bMultiPlayer) { //send all transporter Droids back to home base if off world if (!goingHome) { /* stop the camera following the transporter */ psTransporter->selected = FALSE; /* send transporter offworld */ missionGetTransporterExit( psTransporter->player, &iX, &iY ); orderDroidLoc(psTransporter, DORDER_TRANSPORTRETURN, iX, iY ); //set the launch time so the transporter doesn't just disappear for CAMSTART/CAMCHANGE transporterSetLaunchTime(gameTime); } } } void missionMoveTransporterOffWorld( DROID *psTransporter ) { W_CLICKFORM *psForm; DROID *psDroid; if (psTransporter->droidType == DROID_TRANSPORTER) { /* trigger script callback */ transporterSetScriptCurrent( psTransporter ); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_TRANSPORTER_OFFMAP); transporterSetScriptCurrent( NULL ); //gridRemoveObject( (BASE_OBJECT *) psTransporter ); - these happen in droidRemove() //clustRemoveObject( (BASE_OBJECT *) psTransporter ); if (droidRemove(psTransporter, apsDroidLists)) { addDroid(psTransporter, mission.apsDroidLists); } //stop the droid moving - the moveUpdate happens AFTER the orderUpdate and can cause problems if the droid moves from one tile to another moveReallyStopDroid(psTransporter); //if offworld mission, then add the timer //if (mission.type == LDS_MKEEP OR mission.type == LDS_MCLEAR) if (missionCanReEnforce() AND psTransporter->player == selectedPlayer) { addTransporterTimerInterface(); //set the data for the transporter timer label widgSetUserData(psWScreen, IDTRANTIMER_DISPLAY, (void*)psTransporter); //make sure the button is enabled psForm = (W_CLICKFORM*)widgGetFromID(psWScreen,IDTRANTIMER_BUTTON); if (psForm) { formSetClickState(psForm, WCLICK_NORMAL); } } //need a callback for when all the selectedPlayers' reinforcements have been delivered if (psTransporter->player == selectedPlayer) { psDroid = NULL; for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType != DROID_TRANSPORTER) { break; } } if (psDroid == NULL) { eventFireCallbackTrigger((TRIGGER_TYPE)CALL_NO_REINFORCEMENTS_LEFT); } } } else { debug( LOG_NEVER, "missionMoveTransporterOffWorld: droid type not transporter!\n" ); } } //add the Mission timer into the top right hand corner of the screen BOOL intAddMissionTimer(void) { W_FORMINIT sFormInit; W_LABINIT sLabInit; //check to see if it exists already if (widgGetFromID(psWScreen,IDTIMER_FORM) != NULL) { return TRUE; } // Add the background memset(&sFormInit, 0, sizeof(W_FORMINIT)); sFormInit.formID = 0; sFormInit.id = IDTIMER_FORM; sFormInit.style = WFORM_PLAIN; sFormInit.width = iV_GetImageWidth(IntImages,IMAGE_MISSION_CLOCK);//TIMER_WIDTH; sFormInit.height = iV_GetImageHeight(IntImages,IMAGE_MISSION_CLOCK);//TIMER_HEIGHT; sFormInit.x = (SWORD)(RADTLX + RADWIDTH - sFormInit.width); sFormInit.y = (SWORD)TIMER_Y; sFormInit.pUserData = (void*)PACKDWORD_TRI(0,IMAGE_MISSION_CLOCK,IMAGE_MISSION_CLOCK_UP);; sFormInit.pDisplay = intDisplayMissionClock; if (!widgAddForm(psWScreen, &sFormInit)) { return FALSE; } //add labels for the time display memset(&sLabInit,0,sizeof(W_LABINIT)); sLabInit.formID = IDTIMER_FORM; sLabInit.id = IDTIMER_DISPLAY; sLabInit.style = WLAB_PLAIN | WIDG_HIDDEN; sLabInit.x = TIMER_LABELX; sLabInit.y = TIMER_LABELY; sLabInit.width = sFormInit.width;//TIMER_WIDTH; sLabInit.height = sFormInit.height;//TIMER_HEIGHT; sLabInit.pText = "00:00:00"; sLabInit.FontID = WFont; sLabInit.pCallback = intUpdateMissionTimer; if (!widgAddLabel(psWScreen, &sLabInit)) { return FALSE; } return TRUE; } //add the Transporter timer into the top left hand corner of the screen BOOL intAddTransporterTimer(void) { W_FORMINIT sFormInit; W_LABINIT sLabInit; //make sure that Transporter Launch button isn't up as well intRemoveTransporterLaunch(); //check to see if it exists already if (widgGetFromID(psWScreen, IDTRANTIMER_BUTTON) != NULL) { return TRUE; } // Add the button form - clickable memset(&sFormInit, 0, sizeof(W_FORMINIT)); sFormInit.formID = 0; sFormInit.id = IDTRANTIMER_BUTTON; sFormInit.style = WFORM_CLICKABLE | WFORM_NOCLICKMOVE; sFormInit.x = TRAN_FORM_X; sFormInit.y = TRAN_FORM_Y; sFormInit.width = iV_GetImageWidth(IntImages,IMAGE_TRANSETA_UP); sFormInit.height = iV_GetImageHeight(IntImages,IMAGE_TRANSETA_UP); sFormInit.pTip = strresGetString(psStringRes, STR_INT_TRANSPORTER); sFormInit.pDisplay = intDisplayImageHilight; sFormInit.pUserData = (void*)PACKDWORD_TRI(0,IMAGE_TRANSETA_DOWN, IMAGE_TRANSETA_UP); if (!widgAddForm(psWScreen, &sFormInit)) { return FALSE; } //add labels for the time display memset(&sLabInit,0,sizeof(W_LABINIT)); sLabInit.formID = IDTRANTIMER_BUTTON; sLabInit.id = IDTRANTIMER_DISPLAY; sLabInit.style = WLAB_PLAIN | WIDG_HIDDEN; sLabInit.x = TRAN_TIMER_X; sLabInit.y = TRAN_TIMER_Y; sLabInit.width = TRAN_TIMER_WIDTH; sLabInit.height = sFormInit.height; sLabInit.FontID = WFont; sLabInit.pCallback = intUpdateTransporterTimer; if (!widgAddLabel(psWScreen, &sLabInit)) { return FALSE; } //add the capacity label memset(&sLabInit,0,sizeof(W_LABINIT)); sLabInit.formID = IDTRANTIMER_BUTTON; sLabInit.id = IDTRANS_CAPACITY; sLabInit.style = WLAB_PLAIN; sLabInit.x = 65; sLabInit.y = 1; sLabInit.width = 16; sLabInit.height = 16; sLabInit.pText = "00/10"; sLabInit.FontID = WFont; sLabInit.pCallback = intUpdateTransCapacity; if (!widgAddLabel(psWScreen, &sLabInit)) { return FALSE; } return TRUE; } /* //add the Transporter timer into the top left hand corner of the screen BOOL intAddTransporterTimer(void) { #ifndef PSX W_FORMINIT sFormInit; W_LABINIT sLabInit; W_BUTINIT sButInit; // Add the background - invisible since the button image caters for this memset(&sFormInit, 0, sizeof(W_FORMINIT)); //#ifdef PSX // WidgSetOTIndex(OT2D_FARBACK); //#endif sFormInit.formID = 0; sFormInit.id = IDTRANSTIMER_FORM; sFormInit.style = WFORM_PLAIN | WFORM_INVISIBLE; sFormInit.x = TRAN_FORM_X; sFormInit.y = TRAN_FORM_Y; sFormInit.width = iV_GetImageWidth(IntImages,IMAGE_TRANSETA_UP);//TRAN_FORM_WIDTH; sFormInit.height = iV_GetImageHeight(IntImages,IMAGE_TRANSETA_UP);//TRAN_FORM_HEIGHT; //sFormInit.pDisplay = intDisplayPlainForm; if (!widgAddForm(psWScreen, &sFormInit)) { return FALSE; } //#ifdef PSX // WidgSetOTIndex(OT2D_BACK); //#endif //add labels for the time display memset(&sLabInit,0,sizeof(W_LABINIT)); sLabInit.formID = IDTRANSTIMER_FORM; sLabInit.id = IDTRANTIMER_DISPLAY; sLabInit.style = WLAB_PLAIN | WIDG_HIDDEN; sLabInit.x = TRAN_TIMER_X; sLabInit.y = TRAN_TIMER_Y; sLabInit.width = TRAN_TIMER_WIDTH; sLabInit.height = sFormInit.height;//TRAN_TIMER_HEIGHT; //sLabInit.pText = "00.00"; //if (mission.ETA < 0) //{ // sLabInit.pText = "00:00"; //} //else //{ // fillTimeDisplay(sLabInit.pText, mission.ETA); } sLabInit.FontID = WFont; sLabInit.pCallback = intUpdateTransporterTimer; if (!widgAddLabel(psWScreen, &sLabInit)) { return FALSE; } //#ifdef PSX // WidgSetOTIndex(OT2D_FARBACK); //#endif //set up button data memset(&sButInit, 0, sizeof(W_BUTINIT)); sButInit.formID = IDTRANSTIMER_FORM; sButInit.id = IDTRANTIMER_BUTTON; sButInit.x = 0;//TRAN_FORM_X; sButInit.y = 0;//TRAN_FORM_Y; sButInit.width = sFormInit.width; sButInit.height = sFormInit.height; sButInit.FontID = WFont; sButInit.style = WBUT_PLAIN; //sButInit.pText = "T"; sButInit.pTip = strresGetString(psStringRes, STR_INT_TRANSPORTER); sButInit.pDisplay = intDisplayImageHilight; sButInit.pUserData = (void*)PACKDWORD_TRI(0,IMAGE_TRANSETA_DOWN, IMAGE_TRANSETA_UP); //#ifdef PSX // AddCursorSnap(&InterfaceSnap, // sFormInit.x+sButInit.x+sButInit.width/2, // sFormInit.y+sButInit.y+sButInit.height/2,sButInit.formID); //#endif if (!widgAddButton(psWScreen, &sButInit)) { return FALSE; } #endif return TRUE; } */ void missionSetReinforcementTime( UDWORD iTime ) { g_iReinforceTime = iTime; } UDWORD missionGetReinforcementTime(void) { return g_iReinforceTime; } //fills in a hours(if bHours = TRUE), minutes and seconds display for a given time in 1000th sec void fillTimeDisplay(STRING *psText, UDWORD time, BOOL bHours) { UDWORD calcTime, inc; inc = 0; //this is only for the transporter timer - never have hours! if (time == LZ_COMPROMISED_TIME) { psText[inc++] = (UBYTE)('-'); psText[inc++] = (UBYTE)('-'); //seperator psText[inc++] = (UBYTE)(':'); psText[inc++] = (UBYTE)('-'); psText[inc++] = (UBYTE)('-'); //terminate the timer text psText[inc] = '\0'; } else { if (bHours) { //hours calcTime = time / (60*60*GAME_TICKS_PER_SEC); psText[inc++] = (UBYTE)('0'+ calcTime / 10); psText[inc++] = (UBYTE)('0'+ calcTime % 10); time -= (calcTime * (60*60*GAME_TICKS_PER_SEC)); //seperator psText[inc++] = (UBYTE)(':'); } //minutes calcTime = time / (60*GAME_TICKS_PER_SEC); psText[inc++] = (UBYTE)('0'+ calcTime / 10); psText[inc++] = (UBYTE)('0'+ calcTime % 10); time -= (calcTime * (60*GAME_TICKS_PER_SEC)); //seperator psText[inc++] = (UBYTE)(':'); //seconds calcTime = time / GAME_TICKS_PER_SEC; psText[inc++] = (UBYTE)('0'+ calcTime / 10); psText[inc++] = (UBYTE)('0'+ calcTime % 10); //terminate the timer text psText[inc] = '\0'; } } //update function for the mission timer void intUpdateMissionTimer(struct _widget *psWidget, struct _w_context *psContext) { W_LABEL *Label = (W_LABEL*)psWidget; UDWORD timeElapsed;//, calcTime; SDWORD timeRemaining; //take into account cheating with the mission timer //timeElapsed = gameTime - mission.startTime; //if the cheatTime has been set, then don't want the timer to countdown until stop cheating if (mission.cheatTime) { timeElapsed = mission.cheatTime - mission.startTime; } else { timeElapsed = gameTime - mission.startTime; } //check not gone over more than one hour - the mission should have been aborted? //if (timeElapsed > 60*60*GAME_TICKS_PER_SEC) //check not gone over more than 99 mins - the mission should have been aborted? //check not gone over more than 5 hours - arbitary number of hours if (timeElapsed > 5*60*60*GAME_TICKS_PER_SEC) { ASSERT( FALSE,"You've taken too long for this mission!" ); return; } timeRemaining = mission.time - timeElapsed; if (timeRemaining < 0) { timeRemaining = 0; } fillTimeDisplay(Label->aText, timeRemaining, TRUE); //make sure its visible Label->style &= ~WIDG_HIDDEN; //make timer flash if time remaining < 5 minutes if (timeRemaining < FIVE_MINUTES) { flashMissionButton(IDTIMER_FORM); } //stop timer from flashing when gets to < 4 minutes if (timeRemaining < FOUR_MINUTES) { stopMissionButtonFlash(IDTIMER_FORM); } //play audio the first time the timed mission is started if (timeRemaining AND (missionCountDown & NOT_PLAYED_ACTIVATED)) { audio_QueueTrack(ID_SOUND_MISSION_TIMER_ACTIVATED); missionCountDown &= ~NOT_PLAYED_ACTIVATED; } //play some audio for mission countdown - start at 10 minutes remaining if (getPlayCountDown() AND timeRemaining < TEN_MINUTES) { if (timeRemaining < TEN_MINUTES AND (missionCountDown & NOT_PLAYED_TEN)) { audio_QueueTrack(ID_SOUND_10_MINUTES_REMAINING); missionCountDown &= ~NOT_PLAYED_TEN; } else if (timeRemaining < FIVE_MINUTES AND (missionCountDown & NOT_PLAYED_FIVE)) { audio_QueueTrack(ID_SOUND_5_MINUTES_REMAINING); missionCountDown &= ~NOT_PLAYED_FIVE; } else if (timeRemaining < THREE_MINUTES AND (missionCountDown & NOT_PLAYED_THREE)) { audio_QueueTrack(ID_SOUND_3_MINUTES_REMAINING); missionCountDown &= ~NOT_PLAYED_THREE; } else if (timeRemaining < TWO_MINUTES AND (missionCountDown & NOT_PLAYED_TWO)) { audio_QueueTrack(ID_SOUND_2_MINUTES_REMAINING); missionCountDown &= ~NOT_PLAYED_TWO; } else if (timeRemaining < ONE_MINUTE AND (missionCountDown & NOT_PLAYED_ONE)) { audio_QueueTrack(ID_SOUND_1_MINUTE_REMAINING); missionCountDown &= ~NOT_PLAYED_ONE; } } } #define TRANSPORTER_REINFORCE_LEADIN 10*GAME_TICKS_PER_SEC //update function for the transporter timer void intUpdateTransporterTimer(struct _widget *psWidget, struct _w_context *psContext) { W_LABEL *Label = (W_LABEL*)psWidget; DROID *psTransporter; SDWORD timeRemaining; SDWORD ETA; ETA = mission.ETA; if(ETA < 0) { ETA = 0; } // Get the object associated with this widget. psTransporter = (DROID *)Label->pUserData; if (psTransporter != NULL) { ASSERT( PTRVALID(psTransporter, sizeof(DROID)), "intUpdateTransporterTimer: invalid Droid pointer" ); if (psTransporter->action == DACTION_TRANSPORTIN || psTransporter->action == DACTION_TRANSPORTWAITTOFLYIN ) { if (mission.ETA == LZ_COMPROMISED_TIME) { timeRemaining = LZ_COMPROMISED_TIME; } else { timeRemaining = mission.ETA - (gameTime - g_iReinforceTime); if (timeRemaining < 0) { timeRemaining = 0; } if (timeRemaining < TRANSPORTER_REINFORCE_LEADIN ) { // arrived: tell the transporter to move to the new onworld // location if not already doing so if ( psTransporter->action == DACTION_TRANSPORTWAITTOFLYIN ) { missionFlyTransportersIn( selectedPlayer, FALSE ); eventFireCallbackTrigger( (TRIGGER_TYPE)CALL_TRANSPORTER_REINFORCE ); } } } fillTimeDisplay(Label->aText, timeRemaining, FALSE); } else { fillTimeDisplay(Label->aText, ETA, FALSE); } } else { if(missionCanReEnforce()) { // ((mission.type == LDS_MKEEP) || (mission.type == LDS_MCLEAR)) & (mission.ETA >= 0) ) { fillTimeDisplay(Label->aText, ETA, FALSE); } else { fillTimeDisplay(Label->aText, 0, FALSE); } } //minutes /*calcTime = timeRemaining / (60*GAME_TICKS_PER_SEC); Label->aText[0] = (UBYTE)('0'+ calcTime / 10); Label->aText[1] = (UBYTE)('0'+ calcTime % 10); timeElapsed -= calcTime * (60*GAME_TICKS_PER_SEC); //seperator Label->aText[3] = (UBYTE)(':'); //seconds calcTime = timeRemaining / GAME_TICKS_PER_SEC; Label->aText[3] = (UBYTE)('0'+ calcTime / 10); Label->aText[4] = (UBYTE)('0'+ calcTime % 10);*/ Label->style &= ~WIDG_HIDDEN; } /* Remove the Mission Timer widgets from the screen*/ void intRemoveMissionTimer(void) { // Check it's up. if(widgGetFromID(psWScreen,IDTIMER_FORM) != NULL) { //and remove it. widgDelete(psWScreen, IDTIMER_FORM); } } /* Remove the Transporter Timer widgets from the screen*/ void intRemoveTransporterTimer(void) { //remove main screen if(widgGetFromID(psWScreen,IDTRANTIMER_BUTTON) != NULL) { widgDelete(psWScreen, IDTRANTIMER_BUTTON); } } // //////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////// // mission result functions for the interface. void intDisplayMissionBackDrop(struct _widget *psWidget, UDWORD xOffset, UDWORD yOffset, UDWORD *pColours) { // iV_DownloadDisplayBuffer(pMissionBackDrop->bmp); scoreDataToScreen(); } void missionResetInGameState( void ) { //stop the game if in single player mode setMissionPauseState(); // reset the input state resetInput(); // Add the background // get rid of reticule etc.. intResetScreen(FALSE); //intHidePowerBar(); forceHidePowerBar(); intRemoveReticule(); intRemoveMissionTimer(); } static BOOL _intAddMissionResult(BOOL result, BOOL bPlaySuccess) { W_FORMINIT sFormInit; W_LABINIT sLabInit; W_BUTINIT sButInit; // UDWORD fileSize=0; missionResetInGameState(); memset(&sFormInit, 0, sizeof(W_FORMINIT)); // add some funky beats cdAudio_PlayTrack(2); // 2= frontend music. pie_LoadBackDrop(SCREEN_MISSIONEND,FALSE); sFormInit.formID = 0; sFormInit.id = IDMISSIONRES_BACKFORM; sFormInit.style = WFORM_PLAIN; sFormInit.x = (SWORD)(0 + D_W); sFormInit.y = (SWORD)(0 + D_H); sFormInit.width = 640; sFormInit.height = 480; sFormInit.pDisplay = intDisplayMissionBackDrop; if (!widgAddForm(psWScreen, &sFormInit)) { return FALSE; } //#endif // TITLE sFormInit.formID = IDMISSIONRES_BACKFORM; sFormInit.id = IDMISSIONRES_TITLE; sFormInit.style = WFORM_PLAIN; sFormInit.x = MISSIONRES_TITLE_X; sFormInit.y = MISSIONRES_TITLE_Y; sFormInit.width = MISSIONRES_TITLE_W; sFormInit.height = MISSIONRES_TITLE_H; sFormInit.disableChildren = TRUE; sFormInit.pDisplay = intOpenPlainForm; //intDisplayPlainForm; // removed for more space //#ifdef PSX if (!widgAddForm(psWScreen, &sFormInit)) { return FALSE; } //#endif // add form sFormInit.formID = IDMISSIONRES_BACKFORM; sFormInit.id = IDMISSIONRES_FORM; sFormInit.style = WFORM_PLAIN; sFormInit.x = MISSIONRES_X; sFormInit.y = MISSIONRES_Y; sFormInit.width = MISSIONRES_W; sFormInit.height = MISSIONRES_H; sFormInit.disableChildren = TRUE; sFormInit.pDisplay = intOpenPlainForm; //intDisplayPlainForm; if (!widgAddForm(psWScreen, &sFormInit)) { return FALSE; } // description of success/fail memset(&sLabInit,0,sizeof(W_LABINIT)); sLabInit.formID = IDMISSIONRES_TITLE; sLabInit.id = IDMISSIONRES_TXT; sLabInit.style = WLAB_PLAIN | WLAB_ALIGNCENTRE; sLabInit.x = 0; sLabInit.y = 12; sLabInit.width = MISSIONRES_TITLE_W; sLabInit.height = 16; if(result) { //don't bother adding the text if haven't played the audio if (bPlaySuccess) { sLabInit.pText = strresGetString(psStringRes,STR_MR_OBJECTIVE_ACHIEVED);//"Objective Achieved"; } } else { sLabInit.pText = strresGetString(psStringRes,STR_MR_OBJECTIVE_FAILED);//"Objective Failed; } sLabInit.FontID = WFont; //removed for more space //#ifdef PSX if (!widgAddLabel(psWScreen, &sLabInit)) { return FALSE; } //#endif // options. memset(&sButInit,0,sizeof(W_BUTINIT)); sButInit.formID = IDMISSIONRES_FORM; sButInit.style = WBUT_PLAIN | WBUT_TXTCENTRE; sButInit.width = MISSION_TEXT_W; sButInit.height = MISSION_TEXT_H; sButInit.FontID = WFont; sButInit.pTip = NULL; sButInit.pDisplay = displayTextOption; //if won or in debug mode if(result OR getDebugMappingStatus()) { //continue sButInit.x = MISSION_2_X; // Won the game, so display "Quit to main menu" if(testPlayerHasWon()) { sButInit.id = IDMISSIONRES_QUIT; sButInit.y = MISSION_2_Y-8; sButInit.pText = strresGetString(psStringRes,STR_MR_QUIT_TO_MAIN); widgAddButton(psWScreen, &sButInit); intSetCurrentCursorPosition(&InterfaceSnap,sButInit.id); } else { // Finished the mission, so display "Continue Game" sButInit.y = MISSION_2_Y; sButInit.id = IDMISSIONRES_CONTINUE; sButInit.pText = strresGetString(psStringRes,STR_MR_CONTINUE);//"Continue Game"; widgAddButton(psWScreen, &sButInit); intSetCurrentCursorPosition(&InterfaceSnap,sButInit.id); } /* Only add save option if in the game for real, ie, not fastplay. And the player hasn't just completed the whole game Don't add save option if just lost and in debug mode*/ if (!testPlayerHasWon() AND !(testPlayerHasLost() AND getDebugMappingStatus())) { //save sButInit.id = IDMISSIONRES_SAVE; sButInit.x = MISSION_1_X; sButInit.y = MISSION_1_Y; sButInit.pText = strresGetString(psStringRes,STR_MR_SAVE_GAME);//"Save Game"; widgAddButton(psWScreen, &sButInit); intSetCurrentCursorPosition(&InterfaceSnap,sButInit.id); } } else { //load sButInit.id = IDMISSIONRES_LOAD; sButInit.x = MISSION_1_X; sButInit.y = MISSION_1_Y; sButInit.pText = strresGetString(psStringRes,STR_MR_LOAD_GAME);//"Load Saved Game"; widgAddButton(psWScreen, &sButInit); intSetCurrentCursorPosition(&InterfaceSnap,sButInit.id); //quit sButInit.id = IDMISSIONRES_QUIT; sButInit.x = MISSION_2_X; sButInit.y = MISSION_2_Y; sButInit.pText = strresGetString(psStringRes,STR_MR_QUIT_TO_MAIN);//"Quit to Main Menu"; widgAddButton(psWScreen, &sButInit); } intMode = INT_MISSIONRES; MissionResUp = TRUE; /* play result audio */ if ( result == TRUE AND bPlaySuccess) { audio_QueueTrack( ID_SOUND_OBJECTIVE_ACCOMPLISHED ); } return TRUE; } BOOL intAddMissionResult(BOOL result, BOOL bPlaySuccess) { /* save result */ g_bMissionResult = result; return _intAddMissionResult(result, bPlaySuccess); } void intRemoveMissionResultNoAnim(void) { widgDelete(psWScreen, IDMISSIONRES_TITLE); widgDelete(psWScreen, IDMISSIONRES_FORM); widgDelete(psWScreen, IDMISSIONRES_BACKFORM); cdAudio_Stop(); MissionResUp = FALSE; ClosingMissionRes = FALSE; intMode = INT_NORMAL; //reset the pauses resetMissionPauseState(); // add back the reticule and power bar. intAddReticule(); intShowPowerBar(); // EnableMouseDraw(TRUE); // MouseMovement(TRUE); } void intRunMissionResult() { processFrontendSnap(FALSE); frameSetCursorFromRes(IDC_DEFAULT); if(bLoadSaveUp) { if(runLoadSave(FALSE))// check for file name. { if(strlen(sRequestResult)) { debug( LOG_NEVER, "Returned %s", sRequestResult ); if(bRequestLoad) { // loadGame( ); } else { saveGame(sRequestResult,GTYPE_SAVE_START); addConsoleMessage(strresGetString(psStringRes, STR_GAME_SAVED), LEFT_JUSTIFY); } } } } } void missionContineButtonPressed( void ) { //SHOULDN'T BE ABLE TO BE ANY OTHER TYPE AT PRESENT! // start the next mission if necessary // otherwise wait for the Launch button to be pressed //if (nextMissionType == MISSION_CAMPSTART OR nextMissionType == // MISSION_CAMPEXPAND OR nextMissionType == MISSION_BETWEEN) if (nextMissionType == LDS_CAMSTART OR nextMissionType == LDS_BETWEEN OR nextMissionType == LDS_EXPAND OR nextMissionType == LDS_EXPAND_LIMBO ) { //if we're moving from cam2-cam3? launchMission(); } widgDelete(psWScreen,IDMISSIONRES_FORM); //close option box. //if (nextMissionType == MISSION_OFFKEEP OR nextMissionType == MISSION_OFFCLEAR) /*if (nextMissionType == MISSION_BETWEEN) { intRemoveMissionResultNoAnim(); }*/ // intRemoveMissionResultNoAnim(); //just being paranoid here - definately don't want this in the final build #ifdef DEBUG //in the final build we won't be able to get here beciase the CONTINUE button will not be available //if we're in debug mode and we've lost it is preferred if we just return to the game if (getDebugMappingStatus() AND testPlayerHasLost()) { intRemoveMissionResultNoAnim(); } #endif } void intProcessMissionResult(UDWORD id) { W_BUTINIT sButInit; switch(id) { case IDMISSIONRES_LOAD: // throw up some filerequester addLoadSave(LOAD_MISSIONEND,SaveGamePath,"gam",strresGetString(psStringRes,STR_MR_LOAD_GAME)/*"Load Game"*/); break; case IDMISSIONRES_SAVE: addLoadSave(SAVE_MISSIONEND,SaveGamePath,"gam",strresGetString(psStringRes,STR_MR_SAVE_GAME)/*"Save Game"*/); if (widgGetFromID(psWScreen, IDMISSIONRES_QUIT) == NULL) { //Add Quit Button now save has been pressed memset(&sButInit,0,sizeof(W_BUTINIT)); sButInit.formID = IDMISSIONRES_FORM; sButInit.style = WBUT_PLAIN | WBUT_TXTCENTRE; sButInit.width = MISSION_TEXT_W; sButInit.height = MISSION_TEXT_H; sButInit.FontID = WFont; sButInit.pTip = NULL; sButInit.pDisplay = displayTextOption; sButInit.id = IDMISSIONRES_QUIT; sButInit.x = MISSION_3_X; sButInit.y = MISSION_3_Y; sButInit.pText = strresGetString(psStringRes,STR_MR_QUIT_TO_MAIN); widgAddButton(psWScreen, &sButInit); } break; case IDMISSIONRES_QUIT: // catered for by hci.c. break; case IDMISSIONRES_CONTINUE: if(bLoadSaveUp) { closeLoadSave(); // close save interface if it's up. } missionContineButtonPressed(); break; default: break; } } // end of interface stuff. // //////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////// /*builds a droid back at the home base whilst on a mission - stored in a list made available to the transporter interface*/ DROID * buildMissionDroid(DROID_TEMPLATE *psTempl, UDWORD x, UDWORD y, UDWORD player) { DROID *psNewDroid; psNewDroid = buildDroid(psTempl, x << TILE_SHIFT, y << TILE_SHIFT, player, TRUE); if (!psNewDroid) { return NULL; } //addDroid(psNewDroid, mission.apsBuiltDroids); addDroid(psNewDroid, mission.apsDroidLists); //set its x/y to impossible values so can detect when return from mission psNewDroid->x = INVALID_XY; psNewDroid->y = INVALID_XY; //set all the droids to selected from when return psNewDroid->selected = TRUE; return psNewDroid; } //this causes the new mission data to be loaded up - only if startMission has been called void launchMission(void) { //if (mission.type == MISSION_NONE) if (mission.type == LDS_NONE) { // tell the loop that a new level has to be loaded up loopMissionState = LMS_NEWLEVEL; } else { debug( LOG_NEVER, "Start Mission has not been called" ); } } void intCDOK( void ) { resetMissionPauseState(); intAddReticule(); intShowPowerBar(); launchMission(); } void intCDCancel( void ) { /* do nothing - dealt with in HCI */ } //sets up the game to start a new mission //BOOL setUpMission(MISSION_TYPE type) BOOL setUpMission(UDWORD type) { /*#ifndef PSX CD_INDEX CDrequired; #endif*/ //MISSION_TYPE oldMission; //close the interface intResetScreen(TRUE); //oldMission = mission.type; /*the last mission must have been successful otherwise endgame would have been called*/ endMission(); //release the level data for the previous mission if (!levReleaseMissionData()) { return FALSE; } //if (type == MISSION_OFFCLEAR OR type == MISSION_OFFKEEP) if ( type == LDS_CAMSTART ) { //this cannot be called here since we need to be able to save the game at the end of cam1 and cam2 /*CDrequired = getCDForCampaign( getCampaignNumber() ); if ( cdspan_CheckCDPresent(CDrequired) )*/ { //another one of those lovely hacks!! BOOL bPlaySuccess = TRUE; //we don't want the 'mission accomplished' audio/text message at end of cam1 if (getCampaignNumber() == 2) { bPlaySuccess = FALSE; } //give the option of save/continue if (!intAddMissionResult(TRUE, bPlaySuccess)) { return FALSE; } loopMissionState = LMS_SAVECONTINUE; //intCDOK(); - do this later - in missionContineButtonPressed() to be exact } /*else { if(!getWidgetsStatus()) { setWidgetsStatus(TRUE); intResetScreen(FALSE); } missionResetInGameState(); addCDChangeInterface( CDrequired, intCDOK, intCDCancel ); loopMissionState = LMS_SAVECONTINUE; }*/ } else if (type == LDS_MKEEP OR type == LDS_MCLEAR OR type == LDS_MKEEP_LIMBO ) { launchMission(); } else { if(!getWidgetsStatus()) { setWidgetsStatus(TRUE); intResetScreen(FALSE); } //give the option of save/continue if (!intAddMissionResult(TRUE, TRUE)) { return FALSE; } loopMissionState = LMS_SAVECONTINUE; } //if current mission is 'between' then don't give option to save/continue again /*if (oldMission != MISSION_BETWEEN) { //give the option of save/continue if (!intAddMissionResult(TRUE)) { return FALSE; } }*/ return TRUE; } //save the power settings before loading in the new map data void saveMissionPower(void) { UDWORD inc; for (inc = 0; inc < MAX_PLAYERS; inc++) { //mission.asPower[inc].initialPower = asPower[inc]->initialPower; mission.asPower[inc].extractedPower = asPower[inc]->extractedPower; mission.asPower[inc].currentPower = asPower[inc]->currentPower; } } //add the power from the home base to the current power levels for the mission map void adjustMissionPower(void) { UDWORD inc; for (inc = 0; inc < MAX_PLAYERS; inc++) { //asPower[inc]->initialPower += mission.asPower[inc].initialPower; asPower[inc]->extractedPower += mission.asPower[inc].extractedPower; asPower[inc]->currentPower += mission.asPower[inc].currentPower; } } /*sets the appropriate pause states for when the interface is up but the game needs to be paused*/ void setMissionPauseState(void) { if (!bMultiPlayer) { gameTimeStop(); setGameUpdatePause(TRUE); setAudioPause(TRUE); setScriptPause(TRUE); setConsolePause(TRUE); } } /*resets the pause states */ void resetMissionPauseState(void) { if (!bMultiPlayer) { setGameUpdatePause(FALSE); setAudioPause(FALSE); setScriptPause(FALSE); setConsolePause(FALSE); gameTimeStart(); } } //gets the coords for a no go area LANDING_ZONE* getLandingZone(SDWORD i) { ASSERT( (i >= 0) && (i < MAX_NOGO_AREAS), "getLandingZone out of range." ); return &sLandingZone[i]; } /*Initialises all the nogo areas to 0 - DOESN'T INIT THE LIMBO AREA because we have to set this up in the mission BEFORE*/ void initNoGoAreas(void) { UBYTE i; for (i = 0; i < MAX_NOGO_AREAS; i++) { if (i != LIMBO_LANDING) { sLandingZone[i].x1 = sLandingZone[i].y1 = sLandingZone[i].x2 = sLandingZone[i].y2 = 0; } } } //sets the coords for the Transporter to land (for player 0 - selectedPlayer) void setLandingZone(UBYTE x1, UBYTE y1, UBYTE x2, UBYTE y2) { //quick check that x2 > x1 and y2 > y1 if (x2 < x1) { sLandingZone[0].x1 = x2; sLandingZone[0].x2 = x1; } else { sLandingZone[0].x1 = x1; sLandingZone[0].x2 = x2; } if (y2 < y1) { sLandingZone[0].y1 = y2; sLandingZone[0].y2 = y1; } else { sLandingZone[0].y1 = y1; sLandingZone[0].y2 = y2; } addLandingLights(getLandingX(0)+64,getLandingY(0)+64); } //sets the coords for a no go area void setNoGoArea(UBYTE x1, UBYTE y1, UBYTE x2, UBYTE y2, UBYTE area) { //quick check that x2 > x1 and y2 > y1 if (x2 < x1) { sLandingZone[area].x1 = x2; sLandingZone[area].x2 = x1; } else { sLandingZone[area].x1 = x1; sLandingZone[area].x2 = x2; } if (y2 < y1) { sLandingZone[area].y1 = y2; sLandingZone[area].y2 = y1; } else { sLandingZone[area].y1 = y1; sLandingZone[area].y2 = y2; } if (area == 0) { addLandingLights(getLandingX(area) + 64, getLandingY(area) + 64); } } void addLandingLights( UDWORD x, UDWORD y) { iVector pos; pos.x = x; pos.z = y; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_MIDDLE); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,TRUE); //middle pos.x=x+128; pos.z=y+128; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_OUTER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); //outer pos.x=x+128; pos.z=y-128; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_OUTER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); pos.x=x-128; pos.z=y+128; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_OUTER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); pos.x=x-128; pos.z=y-128; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_OUTER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); pos.x=x+64; pos.z=y+64; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_INNER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); // inner pos.x=x+64; pos.z=y-64; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_INNER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); pos.x=x-64; pos.z=y+64; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_INNER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); pos.x=x-64; pos.z=y-64; pos.y = map_Height(pos.x,pos.z) +16; effectSetLandLightSpec(LL_INNER); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LAND_LIGHT,FALSE,NULL,FALSE); } /* checks the x,y passed in are not within the boundary of any Landing Zone x and y in tile coords*/ BOOL withinLandingZone(UDWORD x, UDWORD y) { UDWORD inc; ASSERT( x < mapWidth, "withinLandingZone: x coord bigger than mapWidth" ); ASSERT( y < mapHeight, "withinLandingZone: y coord bigger than mapHeight" ); for (inc = 0; inc < MAX_NOGO_AREAS; inc++) { if ((x >= (UDWORD)sLandingZone[inc].x1 AND x <= (UDWORD)sLandingZone[inc].x2) AND (y >= (UDWORD)sLandingZone[inc].y1 AND y <= (UDWORD)sLandingZone[inc].y2)) { return TRUE; } } return FALSE; } //returns the x coord for where the Transporter can land (for player 0) UWORD getLandingX( SDWORD iPlayer ) { ASSERT( iPlayer> TILE_SHIFT); } //returns the y coord for where the Transporter can land back at home base UDWORD getHomeLandingY(void) { //return ((mission.homeLZ.y1 + (mission.homeLZ.y2 - // mission.homeLZ.y1)/2) << TILE_SHIFT); return (mission.homeLZ_Y >> TILE_SHIFT); } void missionSetTransporterEntry( SDWORD iPlayer, SDWORD iEntryTileX, SDWORD iEntryTileY ) { ASSERT( iPlayer scrollMinX) && (iEntryTileX < scrollMaxX) ) { mission.iTranspEntryTileX[iPlayer] = (UWORD) iEntryTileX; } else { debug( LOG_NEVER, "missionSetTransporterEntry: entry point x %i outside scroll limits %i->%i\n", iEntryTileX, scrollMinX, scrollMaxX ); mission.iTranspEntryTileX[iPlayer] = (UWORD) (scrollMinX + EDGE_SIZE); } if( (iEntryTileY > scrollMinY) && (iEntryTileY < scrollMaxY) ) { mission.iTranspEntryTileY[iPlayer] = (UWORD) iEntryTileY; } else { debug( LOG_NEVER, "missionSetTransporterEntry: entry point y %i outside scroll limits %i->%i\n", iEntryTileY, scrollMinY, scrollMaxY ); mission.iTranspEntryTileY[iPlayer] = (UWORD) (scrollMinY + EDGE_SIZE); } } void missionSetTransporterExit( SDWORD iPlayer, SDWORD iExitTileX, SDWORD iExitTileY ) { ASSERT( iPlayer scrollMinX) && (iExitTileX < scrollMaxX) ) { mission.iTranspExitTileX[iPlayer] = (UWORD) iExitTileX; } else { debug( LOG_NEVER, "missionSetTransporterExit: entry point x %i outside scroll limits %i->%i\n", iExitTileX, scrollMinX, scrollMaxX ); mission.iTranspExitTileX[iPlayer] = (UWORD) (scrollMinX + EDGE_SIZE); } if( (iExitTileY > scrollMinY) && (iExitTileY < scrollMaxY) ) { mission.iTranspExitTileY[iPlayer] = (UWORD) iExitTileY; } else { debug( LOG_NEVER, "missionSetTransporterExit: entry point y %i outside scroll limits %i->%i\n", iExitTileY, scrollMinY, scrollMaxY ); mission.iTranspExitTileY[iPlayer] = (UWORD) (scrollMinY + EDGE_SIZE); } } void missionGetTransporterEntry( SDWORD iPlayer, UWORD *iX, UWORD *iY ) { ASSERT( iPlayer= 0 ) //AND ( //mission.type == LDS_MKEEP OR mission.type == LDS_MKEEP_LIMBO OR //mission.type == LDS_MCLEAR OR mission.type == LDS_BETWEEN)) { //check if time is up if ((SDWORD)(gameTime - mission.startTime) > mission.time) { //the script can call the end game cos have failed! eventFireCallbackTrigger((TRIGGER_TYPE)CALL_MISSION_TIME); } } } } // Remove any objects left ie walls,structures and droids that are not the selected player. // void missionDestroyObjects(void) { DROID *psDroid; STRUCTURE *psStruct; UBYTE Player; debug( LOG_NEVER, "missionDestroyObjects\n" ); for(Player = 0; Player < MAX_PLAYERS; Player++) { if(Player != selectedPlayer) { psDroid = apsDroidLists[Player]; while(psDroid != NULL) { DROID *psNext = psDroid->psNext; removeDroidBase(psDroid); // droidRemove(psDroid, apsDroidLists); // droidRelease(psDroid); // HEAP_FREE(psDroidHeap, psDroid); psDroid = psNext; } //clear out the mission lists as well to make sure no Tranporters exist apsDroidLists[Player] = mission.apsDroidLists[Player]; psDroid = apsDroidLists[Player]; while(psDroid != NULL) { DROID *psNext = psDroid->psNext; //make sure its died flag is not set since we've swapped the apsDroidList pointers over psDroid->died = FALSE; removeDroidBase(psDroid); // droidRemove(psDroid, apsDroidLists); // droidRelease(psDroid); // HEAP_FREE(psDroidHeap, psDroid); psDroid = psNext; } mission.apsDroidLists[Player] = NULL; psStruct = apsStructLists[Player]; while(psStruct != NULL) { STRUCTURE *psNext = psStruct->psNext; removeStruct(psStruct, TRUE); psStruct = psNext; } } } gameTime++; // Wonderfull hack to ensure objects destroyed above get free'ed up by objmemUpdate. objmemUpdate(); // Not sure why but we need to call this after freeing up the droids. List house keeping? } void processPreviousCampDroids(void) { DROID *psDroid, *psNext; //UDWORD droidX, droidY; //BOOL bPlaced; //see if any are left if (mission.apsDroidLists[selectedPlayer]) { for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psNext) { psNext = psDroid->psNext; //We want to kill off all droids now! - AB 27/01/99 //KILL OFF TRANSPORTER //if (psDroid->droidType == DROID_TRANSPORTER) { if (droidRemove(psDroid, mission.apsDroidLists)) { addDroid(psDroid, apsDroidLists); vanishDroid(psDroid); } } /*else { //remove out of stored list and add to current Droid list if (droidRemove(psDroid, mission.apsDroidLists)) { addDroid(psDroid, apsDroidLists); //set the x/y droidX = getLandingX(psDroid->player) >> TILE_SHIFT; droidY = getLandingY(psDroid->player) >> TILE_SHIFT; bPlaced = pickATileGen(&droidX, &droidY,LOOK_FOR_EMPTY_TILE,normalPAT); if (!bPlaced) { ASSERT( FALSE, "processPreviousCampDroids: Unable to find a free location \ cancel to continue" ); vanishDroid(psDroid); } else { psDroid->x = (UWORD)(droidX << TILE_SHIFT); psDroid->y = (UWORD)(droidY << TILE_SHIFT); psDroid->z = map_Height(psDroid->x, psDroid->y); updateDroidOrientation(psDroid); //psDroid->lastTile = mapTile(psDroid->x >> TILE_SHIFT, // psDroid->y >> TILE_SHIFT); psDroid->selected = FALSE; psDroid->cluster = 0; gridAddObject((BASE_OBJECT *)psDroid); //initialise the movement data initDroidMovement(psDroid); } } }*/ } } } //access functions for droidsToSafety flag - so we don't have to end the mission when a Transporter fly's off world void setDroidsToSafetyFlag(BOOL set) { bDroidsToSafety = set; } BOOL getDroidsToSafetyFlag(void) { return bDroidsToSafety; } //access functions for bPlayCountDown flag - TRUE = play coded mission count down void setPlayCountDown(UBYTE set) { bPlayCountDown = set; } BOOL getPlayCountDown(void) { return bPlayCountDown; } //checks to see if the player has any droids (except Transporters left) BOOL missionDroidsRemaining(UDWORD player) { DROID *psDroid; BOOL bDroidsRemaining = FALSE; for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType != DROID_TRANSPORTER) { bDroidsRemaining = TRUE; //don't bother looking for more break; } } return bDroidsRemaining; } /*called when a Transporter gets to the edge of the world and the droids are being flown to safety. The droids inside the Transporter are placed into the mission list for later use*/ void moveDroidsToSafety(DROID *psTransporter) { DROID *psDroid, *psNext; ASSERT( psTransporter->droidType == DROID_TRANSPORTER, "moveUnitsToSafety: unit not a Transporter" ); //move droids out of Transporter into mission list for (psDroid = psTransporter->psGroup->psList; psDroid != NULL AND psDroid != psTransporter; psDroid = psNext) { psNext = psDroid->psGrpNext; grpLeave(psTransporter->psGroup, psDroid); //cam change add droid psDroid->x = INVALID_XY; psDroid->y = INVALID_XY; addDroid(psDroid, mission.apsDroidLists); } //move the transporter into the mission list also if (droidRemove(psTransporter, apsDroidLists)) { //cam change add droid - done in missionDroidUpdate() //psDroid->x = INVALID_XY; //psDroid->y = INVALID_XY; addDroid(psTransporter, mission.apsDroidLists); } } void clearMissionWidgets(void) { //remove any widgets that are up due to the missions if (mission.time > 0) { intRemoveMissionTimer(); } if (missionCanReEnforce()) { intRemoveTransporterTimer(); } intRemoveTransporterLaunch(); } void resetMissionWidgets(void) { DROID *psDroid; //add back any widgets that should be up due to the missions if (mission.time > 0) { intAddMissionTimer(); //make sure its not flashing when added stopMissionButtonFlash(IDTIMER_FORM); } if (missionCanReEnforce()) { addTransporterTimerInterface(); } //if not a reinforceable mission and a transporter exists, then add the launch button //if (!missionCanReEnforce()) //check not a typical reinforcement mission else if (!missionForReInforcements()) { for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType == DROID_TRANSPORTER) { intAddTransporterLaunch(psDroid); break; } } /*if we got to the end without adding a transporter - there might be one sitting in the mission list which is waiting to come back in*/ if (!psDroid) { for (psDroid = mission.apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->droidType == DROID_TRANSPORTER AND psDroid->action == DACTION_TRANSPORTWAITTOFLYIN) { intAddTransporterLaunch(psDroid); break; } } } } } void setCampaignNumber( UDWORD number ) { ASSERT( number<4,"Campaign Number too high!" ); camNumber = number; } UDWORD getCampaignNumber( void ) { return(camNumber); } /*deals with any selectedPlayer's transporters that are flying in when the mission ends. bOffWorld is TRUE if the Mission is currenly offWorld*/ void emptyTransporters(BOOL bOffWorld) { DROID *psTransporter, *psDroid, *psNext, *psNextTrans; //see if there are any Transporters in the world for (psTransporter = apsDroidLists[selectedPlayer]; psTransporter != NULL; psTransporter = psNextTrans) { psNextTrans = psTransporter->psNext; if (psTransporter->droidType == DROID_TRANSPORTER) { //if flying in, empty the contents if (orderState(psTransporter, DORDER_TRANSPORTIN)) { /* if we're offWorld, all we need to do is put the droids into the apsDroidList and processMission() will assign them a location etc */ if (bOffWorld) { for (psDroid = psTransporter->psGroup->psList; psDroid != NULL AND psDroid != psTransporter; psDroid = psNext) { psNext = psDroid->psGrpNext; //take it out of the Transporter group grpLeave(psTransporter->psGroup, psDroid); //add it back into current droid lists addDroid(psDroid, apsDroidLists); } } /* we're not offWorld so add to mission.apsDroidList to be processed by the endMission function */ else { for (psDroid = psTransporter->psGroup->psList; psDroid != NULL AND psDroid != psTransporter; psDroid = psNext) { psNext = psDroid->psGrpNext; //take it out of the Transporter group grpLeave(psTransporter->psGroup, psDroid); //add it back into current droid lists addDroid(psDroid, mission.apsDroidLists); } } //now kill off the Transporter vanishDroid(psDroid); } } } //deal with any transporters that are waiting to come over for (psTransporter = mission.apsDroidLists[selectedPlayer]; psTransporter != NULL; psTransporter = psTransporter->psNext) { if (psTransporter->droidType == DROID_TRANSPORTER) { //for each droid within the transporter... for (psDroid = psTransporter->psGroup->psList; psDroid != NULL AND psDroid != psTransporter; psDroid = psNext) { psNext = psDroid->psGrpNext; //take it out of the Transporter group grpLeave(psTransporter->psGroup, psDroid); //add it back into mission droid lists addDroid(psDroid, mission.apsDroidLists); } } //don't need to destory the transporter here - it is dealt with by the endMission process } } /*bCheating = TRUE == start of cheat bCheating = FALSE == end of cheat */ void setMissionCheatTime(BOOL bCheating) { if (bCheating) { mission.cheatTime = gameTime; } else { //adjust the mission start time for the duration of the cheat! mission.startTime += gameTime - mission.cheatTime; mission.cheatTime = 0; } }