#include "univupdate.h" sdword aimProcessGuardCooperatingTeam(AITeam *team) { AITeamMove *thisMove = team->curMove; SelectCommand *selection = team->shipList.selection; SelectCommand *guardselection = NULL; dbgAssert(!thisMove->processing); if (team->shipList.selection->numShips == 0) { return FALSE; } if (team->cooperatingTeam != NULL) { guardselection = team->cooperatingTeam->shipList.selection; } if ((selection->numShips > 0) && (guardselection != NULL) && (guardselection->numShips > 0)) { aiuWrapProtect(selection,guardselection); } else { aiplayerLog((aiIndex,"Warning: no ships to guard")); } thisMove->processing = TRUE; return TRUE; } AITeamMove *aimCreateGuardCooperatingTeamNoAdd(AITeam *team, bool8 wait, bool8 remove) { TypeOfFormation formation = SAME_FORMATION; AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "guardcoopmove", 0); InitNewMove(newMove,MOVE_GUARDCOOPTEAM,wait,remove,formation,Neutral,aimProcessGuardCooperatingTeam,NULL,NULL); // aiplayerLog((aiIndex,"Created GuardCoop Move")); return newMove; } AITeamMove *aimCreateGuardCooperatingTeam(AITeam *team, bool8 wait, bool8 remove) { AITeamMove *newMove = aimCreateGuardCooperatingTeamNoAdd(team, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_GuardCoopTeam(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessGuardCooperatingTeam,NULL,NULL); } sdword aimProcessLaunch(AITeam *team) { AITeamMove *thisMove = team->curMove; SelectCommand *selection = team->shipList.selection; Ship *ship; Ship *shipIAmInside; sdword i; if (selection->numShips == 0) { return FALSE; } ship = selection->ShipPtr[0]; shipIAmInside = univFindShipIAmInside(ship); if (shipIAmInside != NULL) { SelectCommand *selectcopy = memAlloc(sizeofSelectCommand(selection->numShips),"selectcopy",0); selectcopy->numShips = 0; dbgAssert(!thisMove->processing); for (i=0;inumShips;i++) { if (univAmIInsideThisShip(selection->ShipPtr[i],shipIAmInside)) { selectcopy->ShipPtr[selectcopy->numShips++] = selection->ShipPtr[i]; } } dbgAssert(selectcopy->numShips > 0); // Falko, don't replace this with aiuWrap because many ships in selectcopy are hidden, but this is okay in this case clWrapLaunchMultipleShips(&universe.mainCommandLayer,selectcopy,shipIAmInside); memFree(selectcopy); thisMove->processing = TRUE; return TRUE; } else if ((ship->specialFlags & SPECIAL_STAY_TILL_EXPLICITLAUNCH) && (ShipIsWaitingForSoftLaunch(ship))) { // we're on the ASF, so let's do a "soft" launch for (i=0;inumShips;i++) { bitClear(selection->ShipPtr[i]->specialFlags,SPECIAL_STAY_TILL_EXPLICITLAUNCH); } thisMove->processing = TRUE; return TRUE; } return FALSE; } AITeamMove *aimCreateLaunchNoAdd(AITeam *team, bool8 wait, bool8 remove) { AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "launchmove", 0); InitNewMove(newMove,MOVE_LAUNCH,wait,remove,SAME_FORMATION,Neutral,aimProcessLaunch,NULL,NULL); // aiplayerLog((aiIndex,"Created Launch Move")); return newMove; } AITeamMove *aimCreateLaunch(AITeam *team, bool8 wait, bool8 remove) { AITeamMove *newMove; newMove = aimCreateLaunchNoAdd(team, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_Launch(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessLaunch,NULL,NULL); } sdword aimProcessFormation(AITeam *team) { AITeamMove *thisMove = team->curMove; SelectCommand *selection = team->shipList.selection; dbgAssert(!thisMove->processing); if (team->shipList.selection->numShips == 0) { return FALSE; } if (selection->numShips > 0) { if (!aiuWrapFormation(selection,thisMove->params.formation.formationtype)) { return FALSE; } } else { aiplayerLog((aiIndex,"Warning: no ships to put in formation")); } thisMove->processing = TRUE; // // future enhancement: // it might be nice to pay attention to the thisMove->wait flag here, // and not return TRUE until the ships were actually in their formation // return TRUE; } AITeamMove *aimCreateFormationNoAdd(AITeam *team, TypeOfFormation formationtype, bool8 wait, bool8 remove) { TypeOfFormation formation = SAME_FORMATION; AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "formationmove", 0); InitNewMove(newMove,MOVE_FORMATION,wait,remove,formation,Neutral,aimProcessFormation,NULL,NULL); newMove->params.formation.formationtype = formationtype; // aiplayerLog((aiIndex,"Created Formation Move")); return newMove; } AITeamMove *aimCreateFormation(AITeam *team, TypeOfFormation formationtype, bool8 wait, bool8 remove) { AITeamMove *newMove; newMove = aimCreateFormationNoAdd(team, formationtype, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_Formation(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessFormation,NULL,NULL); } sdword aimProcessMoveDone(AITeam *team) { return FALSE; } AITeamMove *aimCreateMoveDone(AITeam *team, bool8 wait, bool8 remove) { TypeOfFormation formation = SAME_FORMATION; AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "movedone", 0); InitNewMove(newMove,MOVE_DONE,wait,remove,formation,Neutral,aimProcessMoveDone,NULL,NULL); // aiplayerLog((aiIndex,"Created Move Done Move")); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_MoveDone(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessMoveDone,NULL,NULL); } //generic bandbox attack move sdword aimProcessAttack(AITeam *team) { AITeamMove *thisMove = team->curMove; SelectCommand *selection = team->shipList.selection/*, *targets*/; if (team->shipList.selection->numShips == 0) { aiplayerLog((aiIndex,"Attack Move, Zero Sized Team")); return TRUE; } if (!thisMove->processing) { if ((selection->numShips > 0) && (thisMove->params.attack.ships->numShips > 0)) { aiuWrapAttack(team->shipList.selection, thisMove->params.attack.ships); // targets = aiuWrapAttack(team, thisMove->params.attack.ships); // aiumemFree(targets); thisMove->processing = TRUE; } else { aiplayerLog((aiIndex,"Warning: no ships to attack")); thisMove->processing = TRUE; } return !thisMove->wait; } else { if ((selection->numShips == 0) || (thisMove->params.attack.ships->numShips == 0) || (aiuShipsNoLongerAttacking(selection))) { // we're done memFree(thisMove->params.attack.ships); thisMove->params.attack.ships = NULL; return TRUE; } else { return FALSE; } } } void aimShipDiedAttack(AITeam *team,AITeamMove *move,Ship *ship) { SelectCommand *ships = move->params.attack.ships; if (ships != NULL) { if (clRemoveShipFromSelection(ships,ship)) { // note! one of the ships we were attacking died! ; } } } void aimCloseAttack(AITeam *team,AITeamMove *move) { if (move->params.attack.ships != NULL) { memFree(move->params.attack.ships); move->params.attack.ships = NULL; } } AITeamMove *aimCreateAttackNoAdd(AITeam *team, SelectCommand *targets,TypeOfFormation formation, bool8 wait, bool8 remove) { AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "attackmove", 0); InitNewMove(newMove,MOVE_ATTACK,wait,remove,formation,Neutral,aimProcessAttack,aimShipDiedAttack,aimCloseAttack); newMove->params.attack.ships = targets; // aiplayerLog((aiIndex,"Created Attack Move")); return newMove; } AITeamMove *aimCreateAttack(AITeam *team, SelectCommand *targets,TypeOfFormation formation, bool8 wait, bool8 remove) { AITeamMove *newMove; newMove = aimCreateAttackNoAdd(team, targets, formation, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_Attack(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessAttack,aimShipDiedAttack,aimCloseAttack); } void aimSave_Attack(AITeamMove *move) { if (move->params.attack.ships) SaveSelection((SpaceObjSelection *)move->params.attack.ships); } void aimLoad_Attack(AITeamMove *move) { if (move->params.attack.ships) move->params.attack.ships = (SelectCommand *)LoadSelectionAndFix(); } //special move sdword aimProcessSpecial(AITeam *team) { AITeamMove *thisMove = team->curMove; SelectCommand *selection = team->shipList.selection; if (team->shipList.selection->numShips == 0) { aiplayerLog((aiIndex,"Special Move, Zero Sized Team")); return TRUE; } if (!thisMove->processing) { if ((selection->numShips > 0) && ((thisMove->params.attack.ships == NULL) || (thisMove->params.attack.ships->numShips > 0))) { aiuWrapSpecial(team->shipList.selection, thisMove->params.attack.ships); thisMove->processing = TRUE; } else { aiplayerLog((aiIndex,"Warning: no ships to special stuff with")); thisMove->processing = TRUE; } return FALSE; } else { if ((selection->numShips == 0) || (!aitTeamIsDoingSpecialOp(team))) { // we're done aiumemFree(thisMove->params.attack.ships); return TRUE; } else { return (!thisMove->wait); } } } void aimSpecialShipDied(AITeam *team,AITeamMove *move,Ship *ship) { SelectCommand *ships = move->params.attack.ships; if (ships != NULL) { if (clRemoveShipFromSelection(ships,ship)) { // note! one of the ships we were attacking died! ; } } } void aimSpecialClose(AITeam *team,AITeamMove *move) { aiumemFree(move->params.attack.ships); } AITeamMove *aimCreateSpecialNoAdd(AITeam *team, SelectCommand *targets,TypeOfFormation formation, TacticsType tactics, bool8 wait, bool8 remove) { AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "special", 0); InitNewMove(newMove,MOVE_SPECIAL,wait,remove,formation,tactics,aimProcessSpecial,aimSpecialShipDied,aimSpecialClose); newMove->params.attack.ships = targets; aiplayerLog((aiIndex,"Created Special Move")); return newMove; } AITeamMove *aimCreateSpecial(AITeam *team, SelectCommand *targets,TypeOfFormation formation, TacticsType tactics, bool8 wait, bool8 remove) { AITeamMove *newMove; newMove = aimCreateSpecialNoAdd(team, targets, formation, tactics, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_Special(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessSpecial,aimSpecialShipDied,aimSpecialClose); } void aimSave_Special(AITeamMove *move) { if (move->params.attack.ships) SaveSelection((SpaceObjSelection *)move->params.attack.ships); } void aimLoad_Special(AITeamMove *move) { if (move->params.attack.ships) move->params.attack.ships = (SelectCommand *)LoadSelectionAndFix(); } sdword aimProcessKamikaze(AITeam *team) { AITeamMove *thisMove = team->curMove; SelectCommand *selection = team->shipList.selection; if (selection->numShips == 0) { aiplayerLog((aiIndex,"Kamikaze Move, Zero Sized Team")); return TRUE; } if (!thisMove->processing) { if ((selection->numShips > 0) && (thisMove->params.kamikaze.ships->numShips > 0)) { if (aiuWrapSetTactics(selection,Aggressive)) { if (aiuWrapAttack(selection, thisMove->params.kamikaze.ships)) { aiuWrapSetKamikaze(selection); thisMove->processing = TRUE; } } } else { aiplayerLog((aiIndex,"Warning: no ships to kamikaze into")); thisMove->processing = TRUE; } return FALSE; } else { if ((selection->numShips == 0) || (thisMove->params.kamikaze.ships->numShips == 0) || (aiuShipsNoLongerAttacking(selection))) { // we're done memFree(thisMove->params.kamikaze.ships); thisMove->params.kamikaze.ships = NULL; return TRUE; } else { return FALSE; } } } void aimShipDiedKamikaze(AITeam *team,AITeamMove *move,Ship *ship) { SelectCommand *ships = move->params.kamikaze.ships; if (ships != NULL) { if (clRemoveShipFromSelection(ships,ship)) { // note! one of the ships we were kamikazeing died! ; } } } void aimCloseKamikaze(AITeam *team,AITeamMove *move) { if (move->params.kamikaze.ships != NULL) { memFree(move->params.kamikaze.ships); move->params.kamikaze.ships = NULL; } } AITeamMove *aimCreateKamikazeNoAdd(AITeam *team, SelectCommand *targets,TypeOfFormation formation, bool8 wait, bool8 remove) { AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "kamikazemove", 0); InitNewMove(newMove,MOVE_KAMIKAZE,wait,remove,formation,Aggressive,aimProcessKamikaze,aimShipDiedKamikaze,aimCloseKamikaze); newMove->params.kamikaze.ships = targets; // aiplayerLog((aiIndex,"Created Kamikaze Move")); return newMove; } AITeamMove *aimCreateKamikaze(AITeam *team, SelectCommand *targets,TypeOfFormation formation, bool8 wait, bool8 remove) { AITeamMove *newMove; newMove = aimCreateKamikazeNoAdd(team, targets, formation, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } void aimFix_Kamikaze(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessKamikaze,aimShipDiedKamikaze,aimCloseKamikaze); } void aimSave_Kamikaze(AITeamMove *move) { if (move->params.kamikaze.ships) SaveSelection((SpaceObjSelection *)move->params.kamikaze.ships); } void aimLoad_Kamikaze(AITeamMove *move) { if (move->params.kamikaze.ships) move->params.kamikaze.ships = (SelectCommand *)LoadSelectionAndFix(); } static void GetFromReservesShipsOfShipType(AITeam *team,ShipType shiptype,sdword equivvalue,sdword *numPointsLeftToGet) { SelectCommand *selection; sdword i; Ship *ship; while (*numPointsLeftToGet > 0) { selection = aiCurrentAIPlayer->newships.selection; for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; if (ship->staticinfo->shiptype == shiptype) { growSelectRemoveShipIndex(&aiCurrentAIPlayer->newships,i); aitAddShip(team,ship); *numPointsLeftToGet -= equivvalue; goto foundship; } } // no ships found, so return return; foundship:; } } void aimCloseFancyGetShips(AITeam *team,AITeamMove *move) { if (move->params.fancyGetShips.doneVar != NULL) { aivarDestroy(move->params.fancyGetShips.doneVar); move->params.fancyGetShips.doneVar = NULL; } } sdword aimProcessFancyGetShips(AITeam *team) { AITeamMove *thisMove = team->curMove; AIVar *doneVar; char label[AIVAR_LABEL_MAX_LENGTH+1]; sdword numShipsToBuild, numShipsToGet; if (!thisMove->processing) { ShipType shiptype = thisMove->params.fancyGetShips.shipType; sdword numPointsLeftToGet = thisMove->params.fancyGetShips.numShips * ALTERNATIVE_SHIP_BASE; AlternativeShips *alternativeShips = &thisMove->params.fancyGetShips.alternatives; sdword numAlternatives = alternativeShips->numNextPicks; sdword i; doneVar = aivarCreate(aivarLabelGenerate(label)); aivarValueSet(doneVar, 0); thisMove->params.fancyGetShips.doneVar = doneVar; // try to get as many of the requested ships as possible: // if any types are unavailable due to research, add check here later GetFromReservesShipsOfShipType(team,shiptype,ALTERNATIVE_SHIP_BASE,&numPointsLeftToGet); // now try alternatives for (i=0;ishipTypeNextPicks[i],alternativeShips->shipNumEquivNextPicks[i],&numPointsLeftToGet); } if (numPointsLeftToGet > 0) { // we couldn't get enough from reserves, so we have to build the rest: if (alternativeShips->alternativeFlags & ALTERNATIVE_RANDOM) { // randomly pick the rest sdword j; udword randomnum = ranRandom(RAN_AIPlayer) % (numAlternatives+1); if (randomnum == 0) { goto buildbaseshiporfirst; } randomnum--; dbgAssert(randomnum < numAlternatives); for (i=randomnum,j=0;;) { if (aiuCanBuildShipType(alternativeShips->shipTypeNextPicks[i],(team->teamType == ScriptTeam))) { // found the ship we can build. Let's build them. numShipsToGet = numPointsLeftToGet / alternativeShips->shipNumEquivNextPicks[i]; if (numShipsToGet == 0) { numShipsToGet++; } //determine the number of ships that unit caps will let us build numShipsToBuild = aiuUnitCapCanBuildShip(aiCurrentAIPlayer, alternativeShips->shipTypeNextPicks[i], numShipsToGet); //if we can build all the ships if (numShipsToBuild == numShipsToGet) { aifTeamRequestsShipsCB(alternativeShips->shipTypeNextPicks[i],numShipsToGet,team,aivarLabelGet(doneVar), thisMove->params.fancyGetShips.priority); break; } //if we can only build a few else if (numShipsToBuild > numShipsToGet*0.25) { numPointsLeftToGet = (numShipsToGet - numShipsToBuild) * alternativeShips->shipNumEquivNextPicks[i]; dbgAssert(numPointsLeftToGet > 0); aifTeamRequestsShipsCB(alternativeShips->shipTypeNextPicks[i],numShipsToBuild,team,aivarLabelGet(doneVar), thisMove->params.fancyGetShips.priority); } } j++; if (j >= numAlternatives) { // aiplayerLog((aiIndex,"Warning - couldn't build any secondary ships")); goto buildbaseshiporfirst; } i++; if (i >= numAlternatives) { i = 0; } } } else { buildbaseshiporfirst: if (aiuCanBuildShipType(shiptype,(team->teamType == ScriptTeam))) { numShipsToGet = numPointsLeftToGet / ALTERNATIVE_SHIP_BASE; if (numShipsToGet == 0) { numShipsToGet++; } //determine the number of ships that unit caps will let us build numShipsToBuild = aiuUnitCapCanBuildShip(aiCurrentAIPlayer, shiptype, numShipsToGet); //if we can build all the ships if (numShipsToBuild == numShipsToGet) { aifTeamRequestsShipsCB(shiptype,numShipsToGet,team,aivarLabelGet(doneVar),thisMove->params.fancyGetShips.priority); } //if we can only build some else if (numShipsToBuild > numShipsToGet*0.75) { numPointsLeftToGet = (numShipsToBuild - numShipsToGet) * ALTERNATIVE_SHIP_BASE; aifTeamRequestsShipsCB(shiptype,numShipsToBuild,team,aivarLabelGet(doneVar),thisMove->params.fancyGetShips.priority); } else { aivarDestroy(doneVar); thisMove->params.fancyGetShips.doneVar = NULL; //postfinal: found that the fancygetships move gets stuck here, // even though the team already has all the ships, // added if and while loop // if the current team *already* has the ships we need, // the move is done if (team->shipList.selection->numShips) { if ((team->shipList.selection->ShipPtr[0]->shiptype == thisMove->params.fancyGetShips.shipType) && (team->shipList.selection->numShips >= thisMove->params.fancyGetShips.numShips)) { return TRUE; } // same as above, but goes through all the alternatives for (i=0;ishipList.selection->ShipPtr[0]->shiptype == alternativeShips->shipTypeNextPicks[i]) && (team->shipList.selection->numShips >= (thisMove->params.fancyGetShips.numShips * 10)/alternativeShips->shipNumEquivNextPicks[i])) { return TRUE; } } } return FALSE; } } else { for (i=0;ishipTypeNextPicks[i],(team->teamType == ScriptTeam))) { numShipsToGet = numPointsLeftToGet / alternativeShips->shipNumEquivNextPicks[i]; if (numShipsToGet == 0) { numShipsToGet++; } //determine the number of ships that unit caps will let us build numShipsToBuild = aiuUnitCapCanBuildShip(aiCurrentAIPlayer, alternativeShips->shipTypeNextPicks[i], numShipsToGet); //if we can build all the ships if (numShipsToBuild == numShipsToGet) { aifTeamRequestsShipsCB(alternativeShips->shipTypeNextPicks[i],numShipsToGet,team,aivarLabelGet(doneVar), thisMove->params.fancyGetShips.priority); goto havebuiltbaseshiporfirst; } //if we can only build some else if (numShipsToBuild > numShipsToGet*0.75) { numPointsLeftToGet = (numShipsToBuild - numShipsToGet) * ALTERNATIVE_SHIP_BASE; aifTeamRequestsShipsCB(alternativeShips->shipTypeNextPicks[i],numShipsToBuild,team,aivarLabelGet(doneVar), thisMove->params.fancyGetShips.priority); } } } // we couldn't build base ship, or any alternatives, // destroy the team and try again. aiplayerLog((aiIndex,"Warning - couldn't build base or secondary ship %i, deleting team", shiptype)); aivarDestroy(doneVar); thisMove->params.fancyGetShips.doneVar = NULL; bitSet(team->teamFlags, AIT_DestroyTeam); return TRUE; } havebuiltbaseshiporfirst:; } } else { aivarValueSet(doneVar, TRUE); } thisMove->processing = TRUE; } // normally in a move processing function, we'd check the // thisMove->wait flag, but... // // always wait for the ships to be built (we could loosen this a bit later, maybe) // but consider what happens when the rest of the ships are actually built if we // leave early // // Falko's note later: leaving early is too much trouble - formations become incomplete, // the ships built after the team executes a move sit around for // an undefined period of time and the current Numbers Low // event handler gets confused return aivarValueGet(thisMove->params.getShips.doneVar); } AITeamMove *aimCreateFancyGetShipsNoAdd(AITeam *team, ShipType shiptype, sbyte num_ships, AlternativeShips *alternatives, sdword priority, bool8 wait, bool8 remove) { TypeOfFormation formation = SAME_FORMATION; AITeamMove *newMove = (AITeamMove *)memAlloc(sizeof(AITeamMove), "getshipsmove", 0); dbgAssert(num_ships > 0); #ifndef HW_Release { sdword i; for (i=0;inumNextPicks;i++) { dbgAssert(alternatives->shipNumEquivNextPicks[i] > 0); } } #endif InitNewMove(newMove,MOVE_FANCYGETSHIPS,wait,remove,formation,Neutral,aimProcessFancyGetShips,NULL,aimCloseFancyGetShips); newMove->params.fancyGetShips.shipType = shiptype; newMove->params.fancyGetShips.numShips = num_ships; newMove->params.fancyGetShips.priority = priority; newMove->params.fancyGetShips.alternatives = *alternatives; newMove->params.fancyGetShips.doneVar = NULL; // aiplayerLog((aiIndex,"Created Fancy Getships Move")); return newMove; } AITeamMove *aimCreateFancyGetShips(AITeam *team, ShipType shiptype, sbyte num_ships, AlternativeShips *alternatives, sdword priority, bool8 wait, bool8 remove) { AITeamMove *newMove; dbgAssert(num_ships > 0); newMove = aimCreateFancyGetShipsNoAdd(team, shiptype, num_ships, alternatives, priority, wait, remove); listAddNode(&(team->moves), &(newMove->listNode), newMove); return newMove; } #pragma warning( 4 : 4047) // turns off "different levels of indirection warning" void aimFix_FancyGetShips(AITeamMove *move) { FixMoveFuncPtrs(move,aimProcessFancyGetShips,NULL,aimCloseFancyGetShips); move->params.fancyGetShips.doneVar = NumberToAIVar((sdword)move->params.fancyGetShips.doneVar); } void aimPreFix_FancyGetShips(AITeamMove *move) { move->params.fancyGetShips.doneVar = AIVarToNumber(move->params.fancyGetShips.doneVar); } #pragma warning( 2 : 4047) // turn back on "different levels of indirection warning"