/*============================================================================= Name : CommandLayer.c Purpose : This is the ship command layer of homeworld. All commands to ship go through this layer, whether the commands originate from across a network, a computer player, a human player, etc. Created 7/5/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include #include #include #include "types.h" #include "fastmath.h" #include "debug.h" #include "memory.h" #include "formation.h" #include "commandlayer.h" #include "aiship.h" #include "aitrack.h" #include "statscript.h" #include "universe.h" #include "univupdate.h" #include "soundevent.h" #include "select.h" #include "collision.h" #include "flightman.h" #include "attack.h" #include "aiplayer.h" #include "GenericInterceptor.h" #include "DDDFrigate.h" #include "SalCapCorvette.h" #include "MinelayerCorvette.h" #include "GravWellGenerator.h" #include "CloakGenerator.h" #include "ResearchShip.h" #include "researchAPI.h" #include "tactics.h" #include "singleplayer.h" #include "repaircorvette.h" #include "genericdefender.h" #include "alliance.h" #include "probe.h" #include "heavycorvette.h" #include "madLinkIn.h" #include "proximitysensor.h" #include "consmgr.h" #include "rescollect.h" #include "tutor.h" #include "randy.h" #include "battle.h" #include "ping.h" #include "netcheck.h" #include "commandnetwork.h" #ifdef gshaw #ifndef HW_Release //#define DEBUG_FORMATIONS //#define DEBUG_LAUNCHSHIP //#define DEBUG_COLLECTRESOURCES //#define DEBUG_COMMANDLAYER #endif #endif /*============================================================================= Private Functions: =============================================================================*/ static void FormationCleanup(struct CommandToDo *formationtodo); static void MoveCleanup(struct CommandToDo *movetodo); static void MpHyperspaceCleanup(struct CommandToDo *movetodo); static void RemoveShipFromMoving(Ship *ship); //static void RemoveShipFromAttacking(Ship *ship); //static void RemoveShipReferencesFromExtraAttackInfo(Ship *shiptoremove,CommandToDo *todo); //static void RemoveAttackTargetFromExtraAttackInfo(SpaceObjRotImpTarg *targettoremove,CommandToDo *todo); static SpaceObjRotImpTarg *AttackClosestOpponent(Ship *ship,AttackCommand *attack); static void AttackCleanup(struct CommandToDo *attacktodo); static AttackTargets *allocateMultipleAttackTargets(Ship *ship); static void pickMultipleAttackTargets(Ship *ship,AttackCommand *attack); void ChangeOrderToHalt(CommandToDo *alreadycommand); bool processMoveToDo(CommandToDo *movetodo,bool passiveAttacked); bool processMpHyperspaceingToDo(CommandToDo *movetodo); bool processMoveLeaderToDo(CommandToDo *movetodo,bool passiveAttacked); void clMoveThese(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to); void clMpHyperspaceThese(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to); bool isSelectionExclusivlyStrikeCraft(SelectCommand *selection); bool isSelectionExclusivlyStrikeCraftorNoMoveAndAttackShips(SelectCommand *selection); bool selectionHasSwarmers(SelectCommand *selection); void removeShipFromMpHyperspaceing(Ship *ship); /*============================================================================= Tweakables: =============================================================================*/ #define DISTMINRATIO 1.0f real32 DISTMAXRATIO = 5.0f; real32 SHRINKMIN = 0.1f; real32 SHRINKMAX = 1.0f; real32 MAX_DIST_JOIN_GROUP_SQR = (2000.0f*2000.0f); real32 PROTECT_GET_THIS_CLOSE_CAP = 2000.0f; real32 PROTECT_GET_THIS_CLOSE_STRIKE = 2000.0f; real32 PROTECT_STILL_VELOCITY = 20.0f; real32 WINGMAN_JOIN_MAX_DIST = (200.0f*200.0f); real32 FIGHTER_MAX_THREAT_DIST[NUM_TACTICS_TYPES] = { (2000.0f*2000.0f),(2000.0f*2000.0f),(2000.0f*2000.0f) }; udword FIGHTER_PAIRUP_PROB = 255; udword FIGHTER_PAIR_CHECK_RATE[NUM_TACTICS_TYPES] = { 7,7,7 }; udword FIGHTER_SINGLE_CHECK_RATE[NUM_TACTICS_TYPES] = { 31,31,31 }; udword MULTIPLETARGETS_RETARGET_RATE = 31; bool8 HYPERSPACE = FALSE; real32 GLOBAL_SHIP_HEALTH_MODIFIER = 1.0f; real32 RESCONTROLLER_FOLLOW_BEHIND = 1.5f; extern sdword FIGHTER_BREAK_ANGLE_MIN; extern sdword FIGHTER_BREAK_ANGLE_MAX; extern sdword FIGHTER_BREAK_VERTICAL_ANGLE_MIN; extern sdword FIGHTER_BREAK_VERTICAL_ANGLE_MAX; extern bool BombersUseBombingRun; extern real32 FLY_INTO_WORLD_PERCENT_DIST; scriptEntry CommandLayerTweaks[] = { makeEntry(MAX_DIST_JOIN_GROUP_SQR,scriptSetReal32SqrCB), makeEntry(PROTECT_GET_THIS_CLOSE_CAP,scriptSetReal32CB), makeEntry(PROTECT_GET_THIS_CLOSE_STRIKE,scriptSetReal32CB), makeEntry(PROTECT_STILL_VELOCITY,scriptSetReal32CB), makeEntry(HYPERSPACE,scriptSetBool8), makeEntry(WINGMAN_JOIN_MAX_DIST,scriptSetReal32SqrCB), makeEntry(FIGHTER_PAIRUP_PROB,scriptSetUdwordCB), makeEntry(FIGHTER_PAIR_CHECK_RATE[Evasive],scriptSetUdwordCB), makeEntry(FIGHTER_PAIR_CHECK_RATE[Neutral],scriptSetUdwordCB), makeEntry(FIGHTER_PAIR_CHECK_RATE[Aggressive],scriptSetUdwordCB), makeEntry(FIGHTER_SINGLE_CHECK_RATE[Evasive],scriptSetUdwordCB), makeEntry(FIGHTER_SINGLE_CHECK_RATE[Neutral],scriptSetUdwordCB), makeEntry(FIGHTER_SINGLE_CHECK_RATE[Aggressive],scriptSetUdwordCB), makeEntry(FIGHTER_MAX_THREAT_DIST[Evasive],scriptSetReal32SqrCB), makeEntry(FIGHTER_MAX_THREAT_DIST[Neutral],scriptSetReal32SqrCB), makeEntry(FIGHTER_MAX_THREAT_DIST[Aggressive],scriptSetReal32SqrCB), makeEntry(MULTIPLETARGETS_RETARGET_RATE,scriptSetUdwordCB), makeEntry(GLOBAL_SHIP_HEALTH_MODIFIER,scriptSetReal32CB), makeEntry(FIGHTER_BREAK_ANGLE_MIN,scriptSetSdwordCB), makeEntry(FIGHTER_BREAK_ANGLE_MAX,scriptSetSdwordCB), makeEntry(FIGHTER_BREAK_VERTICAL_ANGLE_MIN,scriptSetSdwordCB), makeEntry(FIGHTER_BREAK_VERTICAL_ANGLE_MAX,scriptSetSdwordCB), makeEntry(BombersUseBombingRun,scriptSetBool), makeEntry(DISTMAXRATIO,scriptSetReal32CB), makeEntry(SHRINKMIN,scriptSetReal32CB), makeEntry(SHRINKMAX,scriptSetReal32CB), makeEntry(FLY_INTO_WORLD_PERCENT_DIST,scriptSetReal32CB), makeEntry(RESCONTROLLER_FOLLOW_BEHIND,scriptSetReal32CB), endEntry }; /*----------------------------------------------------------------------------- Name : RemoveShipsFromCommand Description : removes ships from command Inputs : command Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipsFromCommand(CommandToDo *command) { sdword i; SelectCommand *selection = command->selection; for (i=0;inumShips;i++) { selection->ShipPtr[i]->command = NULL; } } /*----------------------------------------------------------------------------- Name : RemoveShipFromCommand Description : removes a ship from command Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipFromCommand(Ship *ship) { ship->command = NULL; } /*----------------------------------------------------------------------------- Name : PrepareShipsForCommand Description : prepares ships for executing a new command Inputs : command, rowClear - flag indicating if rightOfWay should be removed Outputs : Return : ----------------------------------------------------------------------------*/ void PrepareShipsForCommand(CommandToDo *command,bool rowClear) { sdword i; SelectCommand *selection = command->selection; Ship *ship; for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; ship->command = command; if ((rowClear) && (ship->specialFlags & SPECIAL_rowGettingOutOfWay)) { rowRemoveShipFromGettingOutOfWay(ship); } } tacticsMakeShipsNotLookForOtherShips(command->selection); } /*----------------------------------------------------------------------------- Name : PrepareOneShipForCommand Description : prepares ship for executing a new command Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void PrepareOneShipForCommand(Ship *ship,CommandToDo *command,bool rowClear) { ship->command = command; if ((rowClear) && (ship->specialFlags & SPECIAL_rowGettingOutOfWay)) { rowRemoveShipFromGettingOutOfWay(ship); } } /*----------------------------------------------------------------------------- Name : InitShipsForAI Description : Initializes selection ships AI state variables Inputs : fresh (if we're starting a new command, this should be TRUE otherwise, it should be false Outputs : Return : ----------------------------------------------------------------------------*/ void InitShipsForAI(SelectCommand *selection,bool fresh) { sdword i; Ship *ship; for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; ship->aistate = 0; ship->aistateattack = 0; if(fresh) ship->aistatecommand = 0; } } void InitShipsForAIDontResetAttack(SelectCommand *selection,bool fresh) { sdword i; Ship *ship; for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; ship->aistate = 0; if(fresh) ship->aistatecommand = 0; } } /*----------------------------------------------------------------------------- Name : getRangeToClosestTarget Description : REturns the square of the range from command selection to its closest target Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ real32 getRangeToClosestTarget(CommandToDo *command) { vector commandPosition; real32 tempSqr,minDistanceSqr = REALlyBig; vector distvec; sdword i; dbgAssert(command->selection->numShips > 0); dbgAssert(command->ordertype.order == COMMAND_ATTACK); commandPosition = command->selection->ShipPtr[0]->posinfo.position; for(i=0;iattack->numTargets;i++) { vecSub(distvec,commandPosition,command->attack->TargetPtr[0]->posinfo.position); tempSqr = vecMagnitudeSquared(distvec); if(tempSqr < minDistanceSqr) { minDistanceSqr = tempSqr; } } return minDistanceSqr; } /*----------------------------------------------------------------------------- Name : InitShipAI Description : Initializes ship's AI state variables Inputs : if fresh is TRUE, aicommand will be set (should only be true if this is called from a new command generation function Outputs : Return : ----------------------------------------------------------------------------*/ void InitShipAI(Ship *ship,bool fresh) { ship->aistate = 0; ship->aistateattack = 0; if(fresh) ship->aistatecommand = 0; } /*----------------------------------------------------------------------------- Name : protectShip Description : ship "protects" or flys nearby protectThisShip Inputs : ship, protectThisShip Outputs : Return : ----------------------------------------------------------------------------*/ #define COLLECTOR_STEADY_DIST 3000.0f void protectShip(Ship *ship,Ship *protectThisShip,bool passiveAttacked) { vector diff; real32 dist; real32 desiredrange; real32 shipSize = ((ShipStaticInfo *)(ship->staticinfo))->staticheader.staticCollInfo.collspheresize; real32 protectThisShipSize = ((ShipStaticInfo *)(protectThisShip->staticinfo))->staticheader.staticCollInfo.collspheresize; bool dontrotate; vector protectPoint = protectThisShip->posinfo.position; vector heading; if (protectThisShipSize < shipSize) { protectThisShipSize = shipSize; // if guarding a smaller ship, give more room } if(isCapitalShip(ship) || ship->shiptype == GravWellGenerator || ship->shiptype == CloakGenerator) { desiredrange = PROTECT_GET_THIS_CLOSE_CAP + shipSize + protectThisShipSize; } else { desiredrange = PROTECT_GET_THIS_CLOSE_STRIKE + shipSize + protectThisShipSize; } // Let's have some ships like the ResourceController follow behind the ship so we don't accidentally block them. if (ship->shiptype == ResourceController) { real32 followbehind = desiredrange * RESCONTROLLER_FOLLOW_BEHIND; // follow behind 1.5 radii matGetVectFromMatrixCol3(heading,protectThisShip->rotinfo.coordsys); vecSubFromScalarMultiply(protectPoint,heading,followbehind); } vecSub(diff,ship->posinfo.position,protectPoint); dist = getVectDistSloppy(diff); // ship->shipidle = FALSE; dontrotate = ((passiveAttacked) & (bool)((ShipStaticInfo *)ship->staticinfo)->rotateToRetaliate); if (ship->shiptype == DDDFrigate) { DDDFrigateMakeSureItCanGuard(ship); } if (protectThisShip->dockvars.dockship == ship) // if docking with me { if(MoveReachedDestinationVariable(ship,&protectThisShip->posinfo.position,COLLECTOR_STEADY_DIST)) { //within range of steadyness goto juststeady; } } if (dist < desiredrange) { if ((ship->specialFlags2 & SPECIAL_2_CircularGuard) || (getVectDistSloppy(protectThisShip->posinfo.velocity) < PROTECT_STILL_VELOCITY) ) { juststeady: if (dontrotate) { aitrackSteadyShipDriftOnly(ship); } else { aitrackSteadyShip(ship); } } else { // just maintain velocity if (dontrotate) { if (aishipFlyToPointAvoidingObjsWithVel(ship,NULL,AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectThisShip->posinfo.velocity) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShipDriftOnly(ship); } } else { if (aishipFlyToPointAvoidingObjsWithVel(ship,NULL,AISHIP_PointInDirectionFlying|AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectThisShip->posinfo.velocity) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShip(ship); } } } } else { // fly to protectThisShip if (dontrotate) { if (aishipFlyToPointAvoidingObjsWithVel(ship,&protectPoint,AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectThisShip->posinfo.velocity) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShipDriftOnly(ship); } } else { if (aishipFlyToPointAvoidingObjsWithVel(ship,&protectPoint,AISHIP_PointInDirectionFlying|AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectThisShip->posinfo.velocity) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShip(ship); } } } } /*----------------------------------------------------------------------------- Name : protectShipsAvg Description : ship "protects" or flys nearby median position of protectships Inputs : ship, protectThisShip Outputs : Return : ----------------------------------------------------------------------------*/ void protectShipsAvg(Ship *ship,ProtectCommand *protectShips,bool passiveAttacked) { vector diff; real32 dist; real32 desiredrange; real32 shipSize = ((ShipStaticInfo *)(ship->staticinfo))->staticheader.staticCollInfo.collspheresize; bool dontrotate; sdword numShips = protectShips->numShips; vector temp,guardposition; sdword i; vector protectShipsAvgVel; real32 realtemp; real32 minx,maxx,miny,maxy,minz,maxz; bool anyshipdockingwithme; dbgAssert(numShips > 1); temp = protectShips->ShipPtr[0]->posinfo.position; minx = maxx = temp.x; miny = maxy = temp.y; minz = maxz = temp.z; protectShipsAvgVel = protectShips->ShipPtr[0]->posinfo.velocity; for (i=1;iShipPtr[i]->posinfo.position; vecAddTo(protectShipsAvgVel,protectShips->ShipPtr[i]->posinfo.velocity); if (temp.x < minx) minx = temp.x; else if (temp.x > maxx) maxx = temp.x; if (temp.y < miny) miny = temp.y; else if (temp.y > maxy) maxy = temp.y; if (temp.z < minz) minz = temp.z; else if (temp.z > maxz) maxz = temp.z; } guardposition.x = (minx + maxx) * 0.5f; guardposition.y = (miny + maxy) * 0.5f; guardposition.z = (minz + maxz) * 0.5f; vecDivideByScalar(protectShipsAvgVel,(real32)numShips,realtemp); // we now have the optimum guardposition and protectShipsAvgVel vecSub(diff,ship->posinfo.position,guardposition); dist = getVectDistSloppy(diff); if(isCapitalShip(ship) || ship->shiptype == GravWellGenerator || ship->shiptype == CloakGenerator) { desiredrange = PROTECT_GET_THIS_CLOSE_CAP + shipSize; } else { desiredrange = PROTECT_GET_THIS_CLOSE_STRIKE + shipSize; } // ship->shipidle = FALSE; dontrotate = ((passiveAttacked) & (bool)((ShipStaticInfo *)ship->staticinfo)->rotateToRetaliate); if (ship->shiptype == DDDFrigate) { DDDFrigateMakeSureItCanGuard(ship); } anyshipdockingwithme = FALSE; if (ship->staticinfo->canReceiveSomething) { for (i=0;iShipPtr[i]->dockvars.dockship == ship) { anyshipdockingwithme = TRUE; break; } } } if (anyshipdockingwithme) // if docking with me { goto juststeady; } if (dist < desiredrange) { if ((ship->specialFlags2 & SPECIAL_2_CircularGuard) || (getVectDistSloppy(protectShipsAvgVel) < PROTECT_STILL_VELOCITY)) { juststeady: if (dontrotate) { aitrackSteadyShipDriftOnly(ship); } else { aitrackSteadyShip(ship); } } else { // just maintain velocity if (dontrotate) { if (aishipFlyToPointAvoidingObjsWithVel(ship,NULL,AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectShipsAvgVel) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShipDriftOnly(ship); } } else { if (aishipFlyToPointAvoidingObjsWithVel(ship,NULL,AISHIP_PointInDirectionFlying|AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectShipsAvgVel) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShip(ship); } } } } else { // fly to protectThisShip if (dontrotate) { if (aishipFlyToPointAvoidingObjsWithVel(ship,&guardposition,AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectShipsAvgVel) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShipDriftOnly(ship); } } else { if (aishipFlyToPointAvoidingObjsWithVel(ship,&guardposition,AISHIP_PointInDirectionFlying|AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured, 0.0f,&protectShipsAvgVel) & AISHIP_FLY_OBJECT_IN_WAY) { aitrackSteadyShip(ship); } } } } /*----------------------------------------------------------------------------- Name : processLaunchShipToDo Description : processes a launch ship command Inputs : Outputs : Return : returns TRUE if ship has finished launching ----------------------------------------------------------------------------*/ bool processLaunchShipToDo(CommandToDo *launchtodo) { Ship *ship = launchtodo->selection->ShipPtr[0]; Ship *receiverShip = launchtodo->launchship.receiverShip; ShipStaticInfo *receiverShipStatic = (ShipStaticInfo *)receiverShip->staticinfo; dbgAssert(launchtodo->selection->numShips == 1); dbgAssert(receiverShip); /* if(launchtodo->launchship.receiverShip->shipSinglePlayerGameInfo->shipHyperspaceState != SHIPHYPERSPACE_NONE) { if(ship->collMyBlob == NULL) { //carrier/mothership is going into hyperspace... //so halt launching ships! //unless its already outside..then finish it up but quick! return FALSE; } }*/ if(!singlePlayerGame) { CommandToDo *shipcommand=getShipAndItsCommand(&universe.mainCommandLayer,launchtodo->launchship.receiverShip); if(shipcommand != NULL) { if(shipcommand->ordertype.order == COMMAND_MP_HYPERSPACEING) { //launching ship is hyperspaceing... if(ship->collMyBlob == NULL) { //wait return FALSE; } } } } else if(singlePlayerGame && singlePlayerGameInfo.hyperspaceState != NO_HYPERSPACE) { if(ship->collMyBlob == NULL) { //carrier/mothership is going into hyperspace... //so halt launching ships! //unless its already outside..then finish it up but quick! return FALSE; } } if (receiverShipStatic->LaunchShipXFromShipY(ship,receiverShip)) { RemoveShipFromLaunching(ship); return TRUE; } else { return FALSE; } } bool isThisShipThreateningMe(SpaceObjRotImpTarg *target,Ship *me) { SpecificAttackVars *targetattackvars; Ship *targetsLeader; Ship *targetsWingman; if (target->objtype != OBJ_ShipType) { return FALSE; // non-ship objects are not considered threatening } targetattackvars = &((Ship *)target)->attackvars; // check if target is attacking me if (targetattackvars->attacktarget == (SpaceObjRotImpTarg *)me) { if (targetattackvars->attacksituation == 0) return TRUE; else return FALSE; } // check if target's leader or wingman is attacking me if ((targetsLeader = targetattackvars->myLeaderIs) != NULL) { dbgAssert(targetattackvars->myWingmanIs == NULL); if (targetsLeader->attackvars.attacktarget == (SpaceObjRotImpTarg *)me) { if (targetattackvars->attacksituation == 0) return TRUE; else return FALSE; } } else if ((targetsWingman = targetattackvars->myWingmanIs) != NULL) { dbgAssert(targetattackvars->myLeaderIs == NULL); if (targetsWingman->attackvars.attacktarget == (SpaceObjRotImpTarg *)me) { if (targetattackvars->attacksituation == 0) return TRUE; else return FALSE; } } return FALSE; } SpaceObjRotImpTarg *FindNewThreateningTarget(Ship *ship,AttackCommand *attack,SpaceObjRotImpTarg *oldtarget) { sdword numShipsToAttack = attack->numTargets; SpaceObjRotImpTarg *newtarget; vector dist; real32 distsqr; real32 mindist = REALlyBig; sdword mindistj = -1; sdword j; real32 fighterMaxThreatDist = FIGHTER_MAX_THREAT_DIST[ship->tacticstype]; dbgAssert(ship->tacticstype < NUM_TACTICS_TYPES); // for all ships/targets we are attacking for (j=0;jTargetPtr[j]; if (newtarget == oldtarget) { continue; } if (isThisShipThreateningMe(newtarget,ship)) { vecSub(dist,newtarget->posinfo.position,ship->posinfo.position); distsqr = vecMagnitudeSquared(dist); if (distsqr < fighterMaxThreatDist) { if (distsqr < mindist) { mindist = distsqr; mindistj = j; } } } } if (mindistj != -1) { return attack->TargetPtr[mindistj]; } else { return NULL; } } /*----------------------------------------------------------------------------- Name : EnemyIsBehindMe Description : returns TRUE if enemy is behind me Inputs : enemy, ship Outputs : Return : returns TRUE if enemy is behind me ----------------------------------------------------------------------------*/ bool EnemyIsBehindMe(SpaceObjRotImpTarg *enemy,Ship *me) { vector myheading; vector enemytome; matGetVectFromMatrixCol3(myheading,me->rotinfo.coordsys); vecSub(enemytome,me->posinfo.position,enemy->posinfo.position); if (vecDotProduct(myheading,enemytome) > 0.0f) { // enemy is behind us return TRUE; } else { return FALSE; } } /*----------------------------------------------------------------------------- Name : processSpecialToDo Description : processes a special Inputs : Outputs : Return : returns TRUE if special has been completed ----------------------------------------------------------------------------*/ bool processSpecialToDo(CommandToDo *todo) { SelectCommand *selection = todo->selection; sdword numShips = selection->numShips; Ship *ship; ShipStaticInfo *shipstaticinfo; bool alldone = TRUE; bool shipdone; sdword i; dbgAssert(numShips > 0); if (todo->specialtargets) { // do the special target command for (i=0;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); shipdone = TRUE; if (shipstaticinfo->custshipheader.CustShipSpecialTarget) { shipdone = shipstaticinfo->custshipheader.CustShipSpecialTarget(ship,todo->specialtargets); } alldone &= shipdone; } return alldone; } else { // do a continuous special activate command for (i=0;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); shipdone = TRUE; dbgAssert(shipstaticinfo->specialActivateIsContinuous); if (shipstaticinfo->custshipheader.CustShipSpecialActivate) { shipdone = shipstaticinfo->custshipheader.CustShipSpecialActivate(ship); } alldone &= shipdone; } return alldone; } } /*----------------------------------------------------------------------------- Name : processHaltToDo Description : returns TRUE if halt command is finished Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ bool processHaltToDo(CommandToDo *command,bool passiveAttacked) { SelectCommand *selection = command->selection; sdword numShips = selection->numShips; bool done = TRUE; sdword i; Ship *ship; for (i=0;iShipPtr[i]; if ((passiveAttacked) & (bool)((ShipStaticInfo *)ship->staticinfo)->rotateToRetaliate) { done &= aitrackSteadyShipDriftOnly(ship); } else { done &= aitrackSteadyShip(ship); } } return done; } /*----------------------------------------------------------------------------- Name : ShipsOutOfPassiveAttackRange Description : returns TRUE if ships being attacked are out of selection's passive attack range Inputs : Outputs : Return : returns TRUE if ships being attacked are out of selection's passive attack range ----------------------------------------------------------------------------*/ bool ShipsInPassiveAttackRange(SelectCommand *selection,AttackCommand *attack) { sdword numShips = selection->numShips; sdword numShipsToAttack = attack->numTargets; sdword i; vector avgposition = { 0.0f, 0.0f, 0.0f }; Ship *ship; vector diff; real32 retaliateZone; real32 negRetaliateZone; bool aShipInRange = FALSE; real32 temp; dbgAssert(numShips > 0); dbgAssert(numShipsToAttack > 0); for (i=0;iTargetPtr[i]->posinfo.position); } vecDivideByScalar(avgposition,(real32)numShipsToAttack,temp); for (i=0;iShipPtr[i]; retaliateZone = ship->staticinfo->passiveRetaliateZone; negRetaliateZone = -retaliateZone; vecSub(diff,ship->posinfo.position,avgposition); if (isBetweenExclusive(diff.x,negRetaliateZone,retaliateZone) && isBetweenExclusive(diff.y,negRetaliateZone,retaliateZone) && isBetweenExclusive(diff.z,negRetaliateZone,retaliateZone) ) { aShipInRange = TRUE; } } return aShipInRange; } /*----------------------------------------------------------------------------- Name : processPassiveAttackToDo Description : processes a passive attack command (attack without moving) Inputs : Outputs : Return : returns TRUE if passive attack has been completed ----------------------------------------------------------------------------*/ bool processPassiveAttackToDo(CommandToDo *attacktodo) { SelectCommand *selection = attacktodo->selection; AttackCommand *attack = attacktodo->attack; sdword numShips = selection->numShips; sdword numShipsToAttack = attack->numTargets; Ship *ship; ShipStaticInfo *shipstaticinfo; SpecificAttackVars *attackvars; SpaceObjRotImpTarg *target; sdword i; bool rotate; dbgAssert(numShips > 0); if ((numShipsToAttack == 0) || (!ShipsInPassiveAttackRange(selection,attack)) || ((singlePlayerGame) && (singlePlayerGameInfo.hyperspaceState != NO_HYPERSPACE ) )) { #ifndef HW_Release #ifdef gshaw if (numShipsToAttack != 0) { dbgMessagef("\nDropping out of passive attack"); } #endif #endif for (i=0;iShipPtr[i],NULL); } return TRUE; } //fix later and optimize by passing in a rotate flag if(attacktodo->ordertype.order == COMMAND_MOVE || attacktodo->ordertype.order == COMMAND_DOCK) rotate = FALSE; else rotate = TRUE; for (i=0;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if (target == NULL) { soundEventBurstStop(ship,NULL); attackvars->attacktarget = target = AttackClosestOpponent(ship,attack); InitShipAI(ship,FALSE); } if (attackvars->multipleAttackTargets) { dbgAssert(shipstaticinfo->canTargetMultipleTargets); if ((universe.univUpdateCounter & MULTIPLETARGETS_RETARGET_RATE) == (ship->shipID.shipNumber & MULTIPLETARGETS_RETARGET_RATE)) { pickMultipleAttackTargets(ship,attack); } } if(ship->tacticstype == Evasive) { //ships in evasive don't passive retaliate if(rotate == TRUE) { vector dir; vecSub(dir,target->posinfo.position,ship->posinfo.position); vecNormalize(&dir); aitrackHeading(ship,&dir,0.999f); } continue; } if (shipstaticinfo->custshipheader.CustShipAttackPassive) { //dbgAssert(target->objtype == OBJ_ShipType); // only target SHIPs should be passive attacked shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,rotate); } attackvars->attackevents = 0; // clear any events we received } return FALSE; } /*----------------------------------------------------------------------------- Name : processAttackToDo Description : processes an attack command Inputs : Outputs : Return : returns TRUE if attack has been completed ----------------------------------------------------------------------------*/ bool processAttackToDo(CommandToDo *attacktodo) { sdword i; SelectCommand *selection = attacktodo->selection; AttackCommand *attack = attacktodo->attack; sdword numShips = selection->numShips; sdword numShipsToAttack = attack->numTargets; Ship *ship; ShipStaticInfo *shipstaticinfo; SpecificAttackVars *attackvars; bool alldone; SpaceObjRotImpTarg *target; udword checkbogeyrate; SpaceObjRotImpTarg *newtarget; bool fighterpair; udword flightman; Ship *myLeader; sdword attackAndMoving; dbgAssert(numShips > 0); if (numShipsToAttack == 0) { // don't do holding pattern for swarmers (don't want them to go into delta formation) if(isSelectionExclusivlyStrikeCraft(attacktodo->selection) && (!selectionHasSwarmers(attacktodo->selection))) { attacktodo->holdingStartTime = universe.totaltimeelapsed; //use leaders heading for holding pattern attacktodo->turnAroundTimes = 0; //use leaders heading for holding pattern matGetVectFromMatrixCol3(attacktodo->holdingPatternVec,selection->ShipPtr[0]->rotinfo.coordsys); vecScalarMultiply(attacktodo->holdingPatternVec,attacktodo->holdingPatternVec,TW_HOLDING_PATTERN_PATH_DISTANCE); for (i=0;inumShips;i++) { //save individual positions attacktodo->selection->ShipPtr[i]->holdingPatternPos = selection->ShipPtr[i]->posinfo.position; soundEventBurstStop(selection->ShipPtr[i],NULL); } //////////////////// //Speech Event for end of attack //event num: STAT_Strike_AttackComplete //use battlechatter if(attacktodo->selection->ShipPtr[0]->playerowner->playerIndex == universe.curPlayerIndex) { if (battleCanChatterAtThisTime(BCE_STAT_Strike_AttackComplete, attacktodo->selection->ShipPtr[0])) { battleChatterAttempt(SOUND_EVENT_DEFAULT, BCE_STAT_Strike_AttackComplete, attacktodo->selection->ShipPtr[0], SOUND_EVENT_DEFAULT); } } /////////////// attacktodo->ordertype.attributes |= COMMAND_IS_HOLDINGPATTERN; return TRUE; //return to do the holding pattern } alldone = TRUE; // we've killed all the ships, so let's stop movement and wait for next command for (i=0;inumShips;i++) { //save individual positions attacktodo->selection->ShipPtr[i]->holdingPatternPos = selection->ShipPtr[i]->posinfo.position; soundEventBurstStop(selection->ShipPtr[i],NULL); } if (attacktodo->ordertype.attributes) { // something else to do afterwards, so return TRUE without bothering to stabilize ships return TRUE; } for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; if (!aitrackSteadyShip(ship)) { alldone = FALSE; } } return alldone; } //check attack conditions as to whether or not we need to //go back to guarding a ship... if((tacticsInfo.tacticsGuardConditionCheckRate & universe.univUpdateCounter) == tacticsInfo.tacticsGuardConditionCheckFrame) { if(attacktodo->ordertype.attributes & COMMAND_IS_PROTECTING) if(tacticsCheckGuardConditionsDuringAttack(attacktodo)) { for (i=0;iShipPtr[i],NULL); } return TRUE; } } attackAndMoving = FALSE; if(attacktodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { //specialized movement + combat attackAndMoving = TRUE; //set flag for future movement reference processMoveToDo(attacktodo,TRUE); //move the group to the new attack location } for (i=0;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if(!isShipStaticInterceptor(shipstaticinfo) && ship->shiptype != DefenseFighter) { if (target == NULL) { soundEventBurstStop(ship,NULL); attackvars->attacktarget = target = AttackClosestOpponent(ship,attack); InitShipAI(ship,FALSE); } if (attackvars->multipleAttackTargets) { dbgAssert(shipstaticinfo->canTargetMultipleTargets); if ((universe.univUpdateCounter & MULTIPLETARGETS_RETARGET_RATE) == (ship->shipID.shipNumber & MULTIPLETARGETS_RETARGET_RATE)) { pickMultipleAttackTargets(ship,attack); } } goto doattack; } else { // if we are an interceptor then things get more complicated... // execute any flight maneuvers if (attackvars->flightmansLeft) { if (flightmanExecute(ship)) { attackvars->flightmansLeft--; if (attackvars->flightmansLeft >= 1) { // select another evading maneuver flightmanInit(ship,flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,target),FLIGHTMAN_EVASIVE_PURE)); } else { attackvars->attacksituation = 0; } } goto skipattack; } else { dbgAssert(attackvars->attacksituation == 0); } // if we have a leader if(ship->tacticstype != Aggressive) //only do wingman stuff for evasive if ((myLeader = attackvars->myLeaderIs) != NULL) { SpaceObjRotImpTarg *myLeaderTarget; bool shouldrotate = TRUE; dbgAssert(attackvars->myWingmanIs == NULL); if (attackvars->attacktarget != NULL) { goto doattack; } if(ship->shiptype != DefenseFighter) { if ((myLeaderTarget = myLeader->attackvars.attacktarget) != NULL) { if (shipstaticinfo->custshipheader.CustShipAttackPassive) { vector leadhead; vector othervec; matGetVectFromMatrixCol3(leadhead,myLeader->rotinfo.coordsys); vecSub(othervec,ship->posinfo.position,myLeaderTarget->posinfo.position); if(vecDotProduct(othervec,leadhead) < 0) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)myLeaderTarget,TRUE); shouldrotate = FALSE; } } } else { soundEventBurstStop(ship,NULL); } } // track leader position formationWingmanTrackLeader(ship,myLeader,shouldrotate); goto skipattack; } if (((attackvars->myLeaderIs) || (attackvars->myWingmanIs)) && ship->tacticstype != Aggressive) { fighterpair = TRUE; checkbogeyrate = FIGHTER_PAIR_CHECK_RATE[ship->tacticstype]; } else { fighterpair = FALSE; checkbogeyrate = FIGHTER_SINGLE_CHECK_RATE[ship->tacticstype]; } if (target != NULL) { if ((universe.univUpdateCounter & checkbogeyrate) == (CHECKBOGEY_FRAME & checkbogeyrate)) { if (!isThisShipThreateningMe(target,ship)) { // we should switch targets to a target that is threatening me, if there is one newtarget = FindNewThreateningTarget(ship,attack,target); if (newtarget != NULL) { soundEventBurstStop(ship,NULL); if ((fighterpair) && ((gamerand() & 255) < FLIGHTMAN_SPLIT_PROB[ship->tacticstype])) { Ship *wingman = attackvars->myWingmanIs; dbgAssert(wingman); wingman->attackvars.flightmansLeft = 1; wingman->attackvars.attacksituation = ATTACKSITUATION_SPLITTING; if (gamerand() & 1) { flightmanInitWithFlags(wingman,FLIGHTMAN_IMMELMAN,IMMELMAN_INVERT); } else { flightmanInit(wingman,FLIGHTMAN_HARDBANK); } attackvars->flightmansLeft = 1; attackvars->attacksituation = ATTACKSITUATION_SPLITTING; flightmanInitWithFlags(ship,FLIGHTMAN_IMMELMAN,0); } else { if ((gamerand() & 255) < FLIGHTMAN_EVASIVEPURE_PROB[ship->tacticstype]) { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,newtarget),FLIGHTMAN_EVASIVE_PURE); attackvars->flightmansLeft = FLIGHTMAN_NUM_EVASIVEPURE_MANEUVERS[ship->tacticstype]; attackvars->attacksituation = ATTACKSITUATION_JUSTEVADING; } else { // sensed threatening enemy attack if (EnemyIsBehindMe(newtarget,ship)) { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,newtarget),FLIGHTMAN_EVASIVE_BEHIND); } else { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,newtarget),FLIGHTMAN_EVASIVE_FRONT); } attackvars->flightmansLeft = 1; attackvars->attacksituation = ATTACKSITUATION_EVADINGREPOS; } flightmanInit(ship,flightman); } attackvars->attacktarget = target = newtarget; InitShipAI(ship,FALSE); goto skipattack; } else { // no new threatening target, so keep attacking old one. goto doattack; } } else { // ship is threatening me, perhaps a token pure evasive maneuver if we are in Evasive tactics? if ((ship->tacticstype == Evasive) && ((universe.univUpdateCounter & FLIGHTMAN_TOKEN_EVASIVEPURE_CHECKRATE) == FLIGHTMAN_TOKEN_EVASIVEPURE_CHECKFRAME)) { if ((gamerand() & 255) < FLIGHTMAN_TOKEN_EVASIVEPURE_PROB) { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,target),FLIGHTMAN_EVASIVE_PURE); attackvars->flightmansLeft = FLIGHTMAN_NUM_EVASIVEPURE_MANEUVERS[ship->tacticstype]; attackvars->attacksituation = ATTACKSITUATION_JUSTEVADING; flightmanInit(ship,flightman); goto skipattack; } } goto doattack; } } else { // just attack our target goto doattack; } } else { // we don't currently have a target. Let's pick one. soundEventBurstStop(ship,NULL); newtarget = FindNewThreateningTarget(ship,attack,target); if (newtarget != NULL) { if ((gamerand() & 255) < FLIGHTMAN_EVASIVEPURE_PROB[ship->tacticstype]) { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,newtarget),FLIGHTMAN_EVASIVE_PURE); attackvars->flightmansLeft = FLIGHTMAN_NUM_EVASIVEPURE_MANEUVERS[ship->tacticstype]; attackvars->attacksituation = ATTACKSITUATION_JUSTEVADING; } else { // sensed threatening enemy attack if (EnemyIsBehindMe(newtarget,ship)) { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,newtarget),FLIGHTMAN_EVASIVE_BEHIND); } else { flightman = flightmanGetRandom(GenericInterceptorGetFlightManProb(ship,newtarget),FLIGHTMAN_EVASIVE_FRONT); } attackvars->flightmansLeft = 1; attackvars->attacksituation = ATTACKSITUATION_EVADINGREPOS; } flightmanInit(ship,flightman); attackvars->attacktarget = target = newtarget; InitShipAI(ship,FALSE); goto skipattack; } else { // find a new target to pick on attackvars->attacktarget = target = AttackClosestOpponent(ship,attack); InitShipAI(ship,FALSE); if ((fighterpair) && (target->objtype == OBJ_ShipType) && ((((Ship *)target)->attackvars.myWingmanIs == NULL) && (((Ship *)target)->attackvars.myLeaderIs == NULL)) && ((gamerand() & 255) < FLIGHTMAN_SANDWICH_PROB[ship->tacticstype]) ) { Ship *wingman = attackvars->myWingmanIs; dbgAssert(wingman); wingman->attackvars.attacktarget = target; InitShipAI(wingman,FALSE); wingman->flightmanState2 = SANDWICH_RIGHT; flightmanInitWithFlags(wingman,FLIGHTMAN_SANDWICH,(udword)target); wingman->attackvars.flightmansLeft = 1; wingman->attackvars.attacksituation = ATTACKSITUATION_SANDWICH; ship->flightmanState2 = SANDWICH_LEFT; flightmanInitWithFlags(ship,FLIGHTMAN_SANDWICH,(udword)target); attackvars->flightmansLeft = 1; attackvars->attacksituation = ATTACKSITUATION_SANDWICH; goto skipattack; } else // we didn't find a threatening enemy attack. Perhaps a celebratory barrel roll? if (attackvars->attackevents & ATTACKEVENT_ENEMYTARGET_DIED) { flightmanInit(ship,FLIGHTMAN_BARREL_ROLL); attackvars->flightmansLeft = 1; // speechEvent(ship, BattleEnemyKilled, 0); goto skipattack; } goto doattack; } } // end if target != NULL } // end if ship is Interceptor doattack: if(attackAndMoving) { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttackPassive) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } } else { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttack) { shipstaticinfo->custshipheader.CustShipAttack(ship,target,ship->staticinfo->bulletRange[ship->tacticstype]); } } skipattack: ship->shipidle = FALSE; attackvars->attackevents = 0; // clear any events we received } // end for return FALSE; } /*----------------------------------------------------------------------------- Name : fixWallFlipTurnSelection Description : Fixes selection when ships in WALL only formation attempt a flipturn Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void fixWallFlipTurnSelection(SelectCommand *selection) { sdword i; for(i=1;inumShips;i++) { selection->ShipPtr[i]->formationOffset.y = -selection->ShipPtr[i]->formationOffset.y; } } /*----------------------------------------------------------------------------- Name : fixFlipTurnSelection Description : Fixes selection when ships in formation attempt a flipturn Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void fixFlipTurnSelection(SelectCommand *selection,SelectCommand *global) { sdword i;//,j,switcher; Ship *tempship; //vector tempvec,tempvec2; //temporary formation vector //j=0; for(i=1;inumShips;i++) { //save temporary ship tempship = selection->ShipPtr[i]; /* tempvec = selection->ShipPtr[i]->formationOffset; i++; if(i >= selection->numShips) { */ tempship->formationOffset.y = -tempship->formationOffset.y; /* break; //done } while(tempship != global->ShipPtr[j]) j++; //while loop exits when j points to same ship as temp ship switcher = j; while(global->ShipPtr[j] != selection->ShipPtr[i]) j++; //while loop exits when j points to other ship to switch //swap ships and continue; selection->ShipPtr[i-1] = selection->ShipPtr[i]; tempvec2 = selection->ShipPtr[i]->formationOffset; selection->ShipPtr[i-1]->formationOffset = tempvec; selection->ShipPtr[i] = tempship; selection->ShipPtr[i]->formationOffset = tempvec2; global->ShipPtr[switcher] = selection->ShipPtr[i-1]; global->ShipPtr[j] = tempship; //global->ShipPtr[switcher]->formationOffset = selection->ShipPtr[i-1]->formationOffset; //global->ShipPtr[j]->formationOffset = tempvec; */ } } void formGroups(SelectCommand *selection,SelectCommand *selectionDefenders,SelectCommand *selectionCorvettes,SelectCommand *selectionCapitalShips,SelectCommand *selectionInterceptors) { sdword a,i,j,k,l; selectionDefenders->numShips = 0; //i selectionCorvettes->numShips = 0; //j selectionCapitalShips->numShips = 0; //k selectionInterceptors->numShips = 0; //l i = j = k = l = 0; for(a=0;anumShips;a++) { if((selection->ShipPtr[a]->staticinfo->shipclass == CLASS_Fighter) || (selection->ShipPtr[a]->staticinfo->shiptype == TargetDrone)) { //check if defender, else fighter if(selection->ShipPtr[a]->staticinfo->shiptype == LightDefender || selection->ShipPtr[a]->staticinfo->shiptype == HeavyDefender) { selectionDefenders->ShipPtr[i] = selection->ShipPtr[a]; i++; selectionDefenders->timeLastStatus = selection->timeLastStatus; selectionDefenders->numShips++; } else { selectionInterceptors->ShipPtr[l] = selection->ShipPtr[a]; l++; selectionInterceptors->timeLastStatus = selection->timeLastStatus; selectionInterceptors->numShips++; } } else if(selection->ShipPtr[a]->staticinfo->shipclass == CLASS_Corvette) { selectionCorvettes->ShipPtr[j] = selection->ShipPtr[a]; j++; selectionCorvettes->timeLastStatus = selection->timeLastStatus; selectionCorvettes->numShips++; } else // capital ship and miscellaneous ships { //captital ship selectionCapitalShips->ShipPtr[k] = selection->ShipPtr[a]; k++; selectionCapitalShips->timeLastStatus = selection->timeLastStatus; selectionCapitalShips->numShips++; } } } //group types #define DEFENDERS 10 #define INTERCEPTORS 11 #define CAPITALS 12 #define CORVETTES 13 bool delegateCommand(CommandToDo *attacktodo,sdword group,sdword doform, SelectCommand *globalselection) { sdword i; SelectCommand *selection = attacktodo->selection; AttackCommand *attack = attacktodo->attack; sdword numShips = selection->numShips; sdword allNotAttacking; sdword formationoverride = FALSE; sdword numShipsToAttack = attack->numTargets; Ship *ship; sdword pee; Ship *tempship; ShipStaticInfo *shipstaticinfo; SpecificAttackVars *attackvars; SpaceObjRotImpTarg *target; Ship *formationLeader; vector leadhead,othervec; real32 maxdist; bool attackAndMoving; formationLeader = selection->ShipPtr[0]; //set leader ship attackAndMoving = FALSE; if(attacktodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { //specialized movement + combat attackAndMoving = TRUE; //set flag for future movement reference processMoveLeaderToDo(attacktodo,TRUE); //move the group to the new attack location } //perform fancy pants reordering of selection if we leader does a flip turn if(group == INTERCEPTORS) { i=0; ship = selection->ShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if(doform && !attackAndMoving) { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttack) { shipstaticinfo->custshipheader.CustShipAttack(ship,target,ship->staticinfo->bulletRange[ship->tacticstype]); } } else { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if(shipstaticinfo->custshipheader.CustShipAttackPassive) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } } skipattack: if(ship->tacticsTalk != 0) { if(ship->formationcommand->formation.flipselectionfixed != TRUE) { //lock the leaders formation! //fix later: this is bad because this 'formation' is //only a pseudo formation...it will fuck up other sub formations //FUCK! if(!bitTest(ship->tacticsTalk,TACTALK_BARRELROLL)) { //if(ship->flightman == FLIGHTMAN_IMMELMAN) //{ //for immelman, lets not fix the formation, it should look good maybe // ship->formationcommand->formation.needFix = FALSE; // lockFormation(ship->formationcommand,0); //} //else //{ ship->formationcommand->formation.needFix = TRUE; //do special heading reversal in formation lock so //as to begin forming the new formation rightaway lockFormation(ship->formationcommand,1); //} } else { ship->formationcommand->formation.needFix = FALSE; lockFormation(ship->formationcommand,0); } ship->formationcommand->formation.flipselectionfixed = TRUE; for(i=1;iShipPtr[i],ship->flightman,0); #ifdef bpasechn #ifdef DEBUG_TACTICS if(ship->flightman == FLIGHTMAN_NULL) _asm int 3 #endif #endif selection->ShipPtr[i]->tacticsNeedToFlightMan=TRUE; } } } else { if(ship->formationcommand->formation.flipselectionfixed == TRUE) { //only if did flipturn; unlockFormation(ship->formationcommand); if(ship->formationcommand->formation.needFix == TRUE) { if(ship->formationcommand->formation.formationtype != WALL_FORMATION) { fixFlipTurnSelection(attacktodo->selection,globalselection); } else { fixWallFlipTurnSelection(attacktodo->selection); } ship->formationcommand->formation.needFix = FALSE; //negate this bool value so the formation status is uptodate. ship->formationcommand->formation.flipState = !ship->formationcommand->formation.flipState; } } ship->formationcommand->formation.flipselectionfixed = FALSE; } allNotAttacking=0; for (i=1;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; attackvars->attackevents = 0; // clear any events we received //perform ship flight maneuvers here if(ship->tacticsNeedToFlightMan) { #ifdef bpasechn #ifdef DEBUG_TACTICS if(ship->flightman == FLIGHTMAN_NULL) _asm int 3 #endif if(ship->flightman != FLIGHTMAN_NULL) #endif { if(flightmanExecute(ship)) { ship->tacticsNeedToFlightMan = FALSE; } else { formationoverride = TRUE; } continue; } } if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttackPassive) { //only do the attack if in proper attack state... //quick fix if(attacktodo->formation.formationtype == SPHERE_FORMATION) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); bitSet(ship->dontrotateever,4); } else { matGetVectFromMatrixCol3(leadhead,formationLeader->rotinfo.coordsys); vecSub(othervec,ship->posinfo.position,target->posinfo.position); if(vecDotProduct(othervec,leadhead) < 0) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); bitSet(ship->dontrotateever,4); } else { bitClear(ship->dontrotateever,4); } } } /* if (shipstaticinfo->custshipheader.CustShipAttackPassive) { //only do the attack if in proper attack state... if(!doform) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); continue; } if(ship->tacticsFormationVar1 == TRUE) if(selection->ShipPtr[0]->aistateattack != 4) { //set flag so we can try to attack because leader has finished attack pass continue; } else { ship->tacticsFormationVar1 = FALSE; } if(InterceptorInRange(ship,target)) { //set a state flag here so when it tries to passive attack again later, it won't //unless the leader is again attacking... ship->tacticsFormationVar1 = FALSE; shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } else { //ship is too close ship->tacticsFormationVar1 = TRUE; allNotAttacking++; } } */ } if(doform) { if(!formationoverride) { processFormationToDo(attacktodo,FALSE,FALSE); } else { //do formation, but loosish... processFormationToDo(attacktodo,FALSE,TRUE); } } for(i=0;inumShips;i++) { bitClear(selection->ShipPtr[i]->dontrotateever,4); } } else if(group == DEFENDERS) { i=0; ship = selection->ShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if (target == NULL) { soundEventBurstStop(ship,NULL); attackvars->attacktarget = target = AttackClosestOpponent(ship,attack); InitShipAI(ship,FALSE); } if (attackvars->multipleAttackTargets) { dbgAssert(shipstaticinfo->canTargetMultipleTargets); if ((universe.univUpdateCounter & MULTIPLETARGETS_RETARGET_RATE) == (ship->shipID.shipNumber & MULTIPLETARGETS_RETARGET_RATE)) { pickMultipleAttackTargets(ship,attack); } } maxdist = tacticsMaxDistToTarget(selection,target); if(doform && !attackAndMoving) { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttack) { shipstaticinfo->custshipheader.CustShipAttack(ship,target,maxdist); } } else { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttackPassive) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } } allNotAttacking=0; for (i=1;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttackPassive) { //only do the attack if in proper attack state... shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } attackvars->attackevents = 0; // clear any events we received } //these need to be moved up... if(doform) { processFormationToDo(attacktodo,FALSE,TRUE); } } else if(group == CORVETTES) { if(selection->ShipPtr[0]->shiptype == MinelayerCorvette) { for(pee=1;peenumShips;pee++) { if(selection->ShipPtr[pee]->shiptype != MinelayerCorvette) { tempship = selection->ShipPtr[0]; selection->ShipPtr[0] = selection->ShipPtr[pee]; selection->ShipPtr[pee] = tempship; } } } i=0; ship = selection->ShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if(doform && !attackAndMoving) { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttack) { shipstaticinfo->custshipheader.CustShipAttack(ship,target,ship->staticinfo->bulletRange[ship->tacticstype]); } } else { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttackPassive) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } } allNotAttacking=0; for (i=1;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttackPassive) { //only do the attack if in proper attack state... shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } else if(shipstaticinfo->shiptype == MinelayerCorvette) { bitSet(ship->specialFlags,SPECIAL_BrokenFormation); shipstaticinfo->custshipheader.CustShipAttack(ship,target,ship->staticinfo->bulletRange[ship->tacticstype]); } attackvars->attackevents = 0; // clear any events we received } if(doform) { if(selection->ShipPtr[0]->aistateattack == 3 || selection->ShipPtr[0]->aistateattack == 4) { //leader of formation is turning around or reaproaching, so lets do a real formation... processFormationToDo(attacktodo,FALSE,FALSE); } else { processFormationToDo(attacktodo,FALSE,TRUE); } } } else if(group == CAPITALS) { i=0; ship = selection->ShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if (target == NULL) { soundEventBurstStop(ship,NULL); attackvars->attacktarget = target = AttackClosestOpponent(ship,attack); InitShipAI(ship,FALSE); } if (attackvars->multipleAttackTargets) { dbgAssert(shipstaticinfo->canTargetMultipleTargets); if ((universe.univUpdateCounter & MULTIPLETARGETS_RETARGET_RATE) == (ship->shipID.shipNumber & MULTIPLETARGETS_RETARGET_RATE)) { pickMultipleAttackTargets(ship,attack); } } maxdist = tacticsMaxDistToTarget(selection,target); if(doform && !attackAndMoving) { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if (shipstaticinfo->custshipheader.CustShipAttack) { shipstaticinfo->custshipheader.CustShipAttack(ship,target,maxdist); } } else { if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else if(shipstaticinfo->custshipheader.CustShipAttackPassive) { shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } } allNotAttacking=0; for (i=1;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; target = attackvars->attacktarget; if(bitTest(ship->specialFlags,SPECIAL_Kamikaze)) { doKamikazeAttack(ship,target); } else { if (shipstaticinfo->custshipheader.CustShipAttackPassive) { //only do the attack if in proper attack state... shipstaticinfo->custshipheader.CustShipAttackPassive(ship,(Ship *)target,TRUE); } } ship->shipidle = FALSE; attackvars->attackevents = 0; // clear any events we received } if(doform) { processFormationToDo(attacktodo,FALSE,TRUE); } } return FALSE; } /*----------------------------------------------------------------------------- Name : targetIsntNearShip Description : determines if ships target is in or out of gun range Inputs : Outputs : Return : returns TRUE if ships target is out of gun range ----------------------------------------------------------------------------*/ bool targetIsntNearShip(Ship *ship,SpaceObjRotImpTarg *target) { vector distvect; real32 dist; vecSub(distvect,target->posinfo.position,ship->posinfo.position); dist = vecMagnitudeSquared(distvect); dist = fsqrt(dist); return(dist > ship->staticinfo->bulletRange[ship->tacticstype]); } /*----------------------------------------------------------------------------- Name : processAttackToDoInFormation Description : processes an attack command for a group of ships in formation...maintains formation Inputs : Outputs : Return : returns TRUE if attack has been completed ----------------------------------------------------------------------------*/ bool processAttackToDoInFormation(CommandToDo *attacktodo) { sdword donness = 0; CommandToDo fakeCommand; MaxSelection selectionDefenders, selectionCorvettes,selectionCaptialShips,selectionInterceptors; AttackCommand *attack = attacktodo->attack; sdword numShipsToAttack = attack->numTargets; SelectCommand *selection = attacktodo->selection; bool alldone; ShipStaticInfo *shipstaticinfo; Ship *ship; SpaceObjRotImpTarg *target; SpaceObjRotImpTarg *leadertarget; sdword i; sdword should = TRUE; bool focusfire = FALSE; SpecificAttackVars *attackvars; dbgAssert(selection->numShips > 0); if (selection->ShipPtr[0]->tacticstype == Aggressive) { focusfire = TRUE; } if (numShipsToAttack == 0) { // don't do holding pattern for swarmers (don't want them to go into delta formation) if(isSelectionExclusivlyStrikeCraft(attacktodo->selection) && (!selectionHasSwarmers(attacktodo->selection))) { attacktodo->holdingStartTime = universe.totaltimeelapsed; //use leaders heading for holding pattern attacktodo->turnAroundTimes = 0; //use leaders heading for holding pattern matGetVectFromMatrixCol3(attacktodo->holdingPatternVec,selection->ShipPtr[0]->rotinfo.coordsys); vecScalarMultiply(attacktodo->holdingPatternVec,attacktodo->holdingPatternVec,TW_HOLDING_PATTERN_PATH_DISTANCE); for (i=0;inumShips;i++) { //save individual positions attacktodo->selection->ShipPtr[i]->holdingPatternPos = selection->ShipPtr[i]->posinfo.position; soundEventBurstStop(selection->ShipPtr[i],NULL); } //////////////////// //Speech Event for end of attack //event num: STAT_Strike_AttackComplete //use battlechatter if(attacktodo->selection->ShipPtr[0]->playerowner->playerIndex == universe.curPlayerIndex) { if (battleCanChatterAtThisTime(BCE_STAT_Strike_AttackComplete, attacktodo->selection->ShipPtr[0])) { battleChatterAttempt(SOUND_EVENT_DEFAULT, BCE_STAT_Strike_AttackComplete, attacktodo->selection->ShipPtr[0], SOUND_EVENT_DEFAULT); } } /////////////// attacktodo->ordertype.attributes |= COMMAND_IS_HOLDINGPATTERN; return TRUE; //return to do the holding pattern } alldone = TRUE; // we've killed all the ships, so let's stop movement and wait for next command for (i=0;inumShips;i++) { //save individual positions attacktodo->selection->ShipPtr[i]->holdingPatternPos = selection->ShipPtr[i]->posinfo.position; soundEventBurstStop(selection->ShipPtr[i],NULL); } if (attacktodo->ordertype.attributes) { // something else to do afterwards, so return TRUE without bothering to stabilize ships return TRUE; } for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; if (!aitrackSteadyShip(ship)) { alldone = FALSE; } } return alldone; } //check attack conditions as to whether or not we need to //go back to guarding a ship... if((tacticsInfo.tacticsGuardConditionCheckRate & universe.univUpdateCounter) == tacticsInfo.tacticsGuardConditionCheckFrame) { if(attacktodo->ordertype.attributes & COMMAND_IS_PROTECTING) if(tacticsCheckGuardConditionsDuringAttack(attacktodo)) { for (i=0;inumShips;i++) { soundEventBurstStop(selection->ShipPtr[i],NULL); } return TRUE; } } for (i=0;inumShips;i++) { ship = selection->ShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); attackvars = &ship->attackvars; ship->shipidle = FALSE; target = attackvars->attacktarget; if(i==0) leadertarget = target; if (target == NULL) { if(i == 0) { soundEventBurstStop(ship,NULL); attackvars->attacktarget = target = AttackClosestOpponent(ship,attack); InitShipAI(ship,FALSE); leadertarget = target; } else { soundEventBurstStop(ship,NULL); if ( leadertarget && ( ( ((leadertarget->objtype != OBJ_ShipType) || (((Ship *)leadertarget)->formationcommand == NULL)) ) || (focusfire) ) ) { attackvars->attacktarget = leadertarget; } else { attackvars->attacktarget = AttackClosestOpponent(ship,attack); } InitShipAI(ship,FALSE); } } if(i >= 1) { if ( leadertarget && ((leadertarget->objtype != OBJ_ShipType) || (((Ship *)leadertarget)->formationcommand == NULL)) ) { attackvars->attacktarget = leadertarget; // override with leadertarget if we are attacking a ship that isn't in formation } if(attacktodo->formation.formationtype == SPHERE_FORMATION) { //do this check infrequently if ((universe.univUpdateCounter & SPHERE_CHECK_TARGET_MASK) == SPHERE_CHECK_TARGET_FRAME) { if(targetIsntNearShip(ship,ship->attackvars.attacktarget)) { soundEventBurstStop(ship,NULL); attackvars->attacktarget = AttackClosestOpponent(ship,attack);//leadertarget; InitShipAI(ship,FALSE); } } } } if (attackvars->multipleAttackTargets) { dbgAssert(shipstaticinfo->canTargetMultipleTargets); if ((universe.univUpdateCounter & MULTIPLETARGETS_RETARGET_RATE) == (ship->shipID.shipNumber & MULTIPLETARGETS_RETARGET_RATE)) { pickMultipleAttackTargets(ship,attack); } } } //ships should each be guaranteed to have their proper targets now! //GET TARGET HERE! //then, if in sphere formation, and target doesn't = center ship, // make it the center ship, otherwise, center ship is target, so keep // pumpleing it fakeCommand = *attacktodo; formGroups(selection,(SelectCommand *)&selectionDefenders,(SelectCommand *)&selectionCorvettes,(SelectCommand *)&selectionCaptialShips,(SelectCommand *)&selectionInterceptors); if(attacktodo->formation.formationtype == SPHERE_FORMATION) { //if(attacktodo->formation.enders != TRUE || attacktodo->formation.endership != (Ship *)leadertarget) if(attacktodo->formation.enders != TRUE) { //FormationSphereInfo *sphereInfo = (FormationSphereInfo *)attacktodo->formation.formationSpecificInfo; //SphereVar *sphereVar; attacktodo->formation.enders = TRUE; //attacktodo->formation.endership = (Ship *)leadertarget; //sphere formation...lets do enders game.. // //first we need to save old formation type, so we can change it back //attacktodo->selection->ShipPtr[attacktodo->selection->numShips] = attacktodo->selection->ShipPtr[0]; //attacktodo->selection->numShips++; //attacktodo->selection->ShipPtr[0] = (Ship *)target; formationContentHasChanged(attacktodo); should = FALSE; } else { should = FALSE; } processFormationToDo(attacktodo,FALSE,TRUE); } else { should = TRUE; } if(selectionInterceptors.numShips > 0) { fakeCommand.selection = (SelectCommand *)&selectionInterceptors; donness |= delegateCommand(&fakeCommand,INTERCEPTORS,should,attacktodo->selection); } if(selectionDefenders.numShips > 0) { fakeCommand.selection = (SelectCommand *)&selectionDefenders; donness |= delegateCommand(&fakeCommand,DEFENDERS,should,attacktodo->selection); } if(selectionCorvettes.numShips > 0) { fakeCommand.selection = (SelectCommand *)&selectionCorvettes; donness |= delegateCommand(&fakeCommand,CORVETTES,should,attacktodo->selection); } if(selectionCaptialShips.numShips > 0) { fakeCommand.selection = (SelectCommand *)&selectionCaptialShips; donness |= delegateCommand(&fakeCommand,CAPITALS,should,attacktodo->selection); //processAttackToDo(&fakeCommand); } return(donness); } /*----------------------------------------------------------------------------- Name : hyperspaceOutPingTimeOut Description : timeout cb function that determines if the ping should time out Inputs : hellaPing - the ping we're evaluating userID - cast to ship * for pointer to proximity sensor. userData - ignore bRemoveReferneces - if TRUE, remove references to pingDyingObject Outputs : Return : TRUE if the ping times out. ----------------------------------------------------------------------------*/ bool hyperspaceOutPingTimeOut(struct ping *hellaPing, udword userID, ubyte *userData, bool bRemoveReferences) { if (bRemoveReferences) { if ((SpaceObj *)userID == pingDyingObject) { return(TRUE); } return(FALSE); } if(((Ship *)userID)->flags & SOF_Hyperspace) { return(TRUE); } return(FALSE); } /*----------------------------------------------------------------------------- Name : hyperspaceInPingTimeOut Description : timeout cb function that determines if the ping should time out Inputs : hellaPing - the ping we're evaluating userID - cast to ship * for pointer to proximity sensor. userData - ignore bRemoveReferneces - if TRUE, remove references to pingDyingObject Outputs : Return : TRUE if the ping times out. ----------------------------------------------------------------------------*/ bool hyperspaceInPingTimeOut(struct ping *hellaPing, udword userID, ubyte *userData, bool bRemoveReferences) { if (bRemoveReferences) { if ((SpaceObj *)userID == pingDyingObject) { return(TRUE); } return(FALSE); } if(((Ship *)userID)->shipSinglePlayerGameInfo->shipHyperspaceState == SHIPHYPERSPACE_NONE) { return(TRUE); } return(FALSE); } bool needToWaitForThisShip(Ship *ship) { Node *node; CommandToDo *command; SelectCommand *selection; sdword i; if(ship->shiptype == AdvanceSupportFrigate) { //ship is an ASF //lets do a todo list walk! node = universe.mainCommandLayer.todolist.head; while(node != NULL) { command = (CommandToDo *)listGetStructOfNode(node); selection = command->selection; if(command->ordertype.order == COMMAND_DOCK) { for(i=0;inumShips;i++) { if(selection->ShipPtr[i]->dockvars.dockship == ship) { if(selection->ShipPtr[i]->dockvars.dockstate2 == ASF_WAITLATCHPOINT || selection->ShipPtr[i]->dockvars.dockstate2 == ASF_BACKUP || selection->ShipPtr[i]->dockvars.dockstate2 == ASF_WAIT || selection->ShipPtr[i]->dockvars.dockstate2 == ASF_REFUEL) { continue; } return TRUE; } } } else if(command->ordertype.order == COMMAND_LAUNCHSHIP) { if(command->launchship.receiverShip == ship) { if(ship->collMyBlob != NULL) { //ship hsing is launchiong a ship and that ship is outside...so finish it off. return TRUE; //need to wait for this ship! } } } node = node->next; } } else if(ship->shiptype == Mothership) { //ship is an ASF //lets do a todo list walk! node = universe.mainCommandLayer.todolist.head; while(node != NULL) { command = (CommandToDo *)listGetStructOfNode(node); selection = command->selection; if(command->ordertype.order == COMMAND_LAUNCHSHIP) { if(command->launchship.receiverShip == ship) { if(command->selection->ShipPtr[0]->collMyBlob != NULL) { //ship hsing is launchiong a ship and that ship is outside...so finish it off. return TRUE; //need to wait for this ship! } } } node = node->next; } } //don't need to wait for anything else return FALSE; } /*----------------------------------------------------------------------------- Name : processMpHyperspaceingToDo Description : processes the hyperspaceing command Inputs : movetodo Outputs : Return : ----------------------------------------------------------------------------*/ #define HW_ETHER_WAIT TW_MULTIPLAYER_HYPERSPACE_WAIT_IN_ETHER_TIME bool processMpHyperspaceingToDo(CommandToDo *movetodo) { sdword i,j,donehsflag; vector shipheading,othervector; real32 dotproduct; real32 timefraction; sdword shipslice; bool dontgoyet=FALSE; for(i=0;iselection->numShips;i++) { movetodo->selection->ShipPtr[i]->posinfo.isMoving = TRUE; } switch(movetodo->hyperspaceState) { case COM_HYPERSPACE_START: //check if any ASF's in selection //if yes, we won't hs out until all slots on asf are full for(i=0;iselection->numShips;i++) { if(aitrackZeroRotationAnywhere(movetodo->selection->ShipPtr[i]) && aitrackZeroVelocity(movetodo->selection->ShipPtr[i])) { movetodo->selection->ShipPtr[i]->rotinfo.rotspeed.x=0.0f; movetodo->selection->ShipPtr[i]->rotinfo.rotspeed.y=0.0f; movetodo->selection->ShipPtr[i]->rotinfo.rotspeed.z=0.0f; movetodo->selection->ShipPtr[i]->posinfo.velocity.x=0.0f; movetodo->selection->ShipPtr[i]->posinfo.velocity.y=0.0f; movetodo->selection->ShipPtr[i]->posinfo.velocity.z=0.0f; continue; } dontgoyet=TRUE; } for(i=0;iselection->numShips;i++) { if(needToWaitForThisShip(movetodo->selection->ShipPtr[i])) { //ship needs us to wait! return FALSE; } } if(dontgoyet) { return FALSE; } //set to hyperspace out! spHyperspaceSelectionOut(movetodo->selection); //setup timer to count down... movetodo->hyperSpaceingTime = universe.totaltimeelapsed; movetodo->hyperspaceState=COM_HYPERSPACE_OUT; //reset ping flag status for(j=0;jselection->numShips;j++) { movetodo->selection->ShipPtr[j]->hyperspacePing = FALSE; } return FALSE; case COM_HYPERSPACE_OUT: //check if all hyperspaced out... timefraction = universe.totaltimeelapsed - movetodo->hyperSpaceingTime; shipslice = (sdword) (timefraction/TW_PING_MAX_DISPERSAL)*movetodo->selection->numShips; if(shipslice-movetodo->selection->numShips == 1 || shipslice > movetodo->selection->numShips ) { shipslice = movetodo->selection->numShips; } else if(shipslice < 1) { shipslice = 1; } for(i=0;iselection->numShips;i++) { for(j=0;jselection->ShipPtr[j]->hyperspacePing) { ping *newPing; movetodo->selection->ShipPtr[j]->hyperspacePing=TRUE; //create ping HERE! newPing = pingCreate(NULL, (SpaceObj *)movetodo->selection->ShipPtr[j], hyperspaceOutPingTimeOut, NULL, 0, (udword)movetodo->selection->ShipPtr[j]); newPing->c = TW_HW_PING_COLOUR_OUT; newPing->size = TW_HW_PING_MAX_SIZE_OUT; newPing->minScreenSize = primScreenToGLScaleX(2); newPing->minSize = TW_HW_PING_MIN_SIZE_OUT; newPing->pingDuration = 1.0f / TW_HW_PING_RATE_OUT; newPing->interPingPause = 0.25; newPing->TOMask = PTOM_Hyperspace; /////////////////////////////// //PUT SPEECH EVENTS HERE! /////////////////////////////// } } if(!(movetodo->selection->ShipPtr[0]->flags & SOF_Hyperspace)) { return(FALSE); } } //tell code that all ships have no pings...so we can create them for hyping in.. for(i=0;iselection->numShips;i++) { movetodo->selection->ShipPtr[i]->hyperspacePing=FALSE; } movetodo->hyperSpaceingTime = universe.totaltimeelapsed+HW_ETHER_WAIT; movetodo->hyperspaceState=COM_HYPERSPACE_WAIT; break; case COM_HYPERSPACE_WAIT: //check if done in the ether if(movetodo->hyperSpaceingTime<= universe.totaltimeelapsed) { //hyperspaced out! hvector movetoplace; movetoplace.x = movetodo->move.destination.x; movetoplace.y = movetodo->move.destination.y; movetoplace.z = movetodo->move.destination.z; matGetVectFromMatrixCol3(shipheading,movetodo->selection->ShipPtr[0]->rotinfo.coordsys); vecSet(othervector,1.0f,0.0f,0.0f); dotproduct = vecDotProduct(shipheading,othervector); if(dotproduct >= 1.0) dotproduct = 1.0f; else if(dotproduct <=-1.0f) dotproduct = -1.0f; movetoplace.w = RAD_TO_DEG(acos(dotproduct)); vecSet(othervector,0.0f,1.0f,0.0f); dotproduct = vecDotProduct(shipheading,othervector); if(dotproduct < 0) { movetoplace.w = 360-movetoplace.w; } spHyperspaceSelectionIn(movetodo->selection,&movetoplace); movetodo->hyperspaceState=COM_HYPERSPACE_IN; movetodo->hyperSpaceingTime = universe.totaltimeelapsed; } break; case COM_HYPERSPACE_IN: //check if all hyperspaced in... donehsflag = TRUE; timefraction = universe.totaltimeelapsed - movetodo->hyperSpaceingTime; shipslice = (sdword) (timefraction/TW_PING_MAX_DISPERSAL)*movetodo->selection->numShips; if(shipslice-movetodo->selection->numShips == 1 || shipslice > movetodo->selection->numShips ) { shipslice = movetodo->selection->numShips; } else if(shipslice < 1) { shipslice = 1; } for(i=0;iselection->numShips;i++) { if(movetodo->selection->ShipPtr[i]->playerowner != universe.curPlayerPtr) { //ship isn't a local players ship... if(battleCanChatterAtThisTime(BCE_COMM_F_HyperspaceDetected, movetodo->selection->ShipPtr[i])) { battleChatterFleetAttempt(SOUND_EVENT_DEFAULT, BCE_COMM_F_HyperspaceDetected, SOUND_EVENT_DEFAULT, &movetodo->selection->ShipPtr[i]->posinfo.position); } } //do hs ping for ALL ships for(j=0;jselection->ShipPtr[j]->hyperspacePing) { ping *newPing; movetodo->selection->ShipPtr[j]->hyperspacePing=TRUE; //create ping HERE! newPing = pingCreate(NULL, (SpaceObj *)movetodo->selection->ShipPtr[j], hyperspaceInPingTimeOut, NULL, 0, (udword)movetodo->selection->ShipPtr[j]); newPing->c = TW_HW_PING_COLOUR_IN; newPing->size = TW_HW_PING_MAX_SIZE_IN; newPing->minScreenSize = primScreenToGLScaleX(2); newPing->minSize = TW_HW_PING_MIN_SIZE_IN; newPing->pingDuration = 1.0f / TW_HW_PING_RATE_IN; newPing->interPingPause = 0.25; newPing->TOMask = PTOM_Hyperspace; /////////////////////////////// //PUT SPEECH EVENTS HERE! /////////////////////////////// } } if((movetodo->selection->ShipPtr[i]->shipSinglePlayerGameInfo->shipHyperspaceState != SHIPHYPERSPACE_NONE)) { donehsflag = FALSE; } else { //individual ship is out of hyperspace...make it selectable! bitSet(movetodo->selection->ShipPtr[i]->flags,SOF_Selectable); } } if(donehsflag == FALSE) { return FALSE; } for(j=0;jselection->numShips;j++) { movetodo->selection->ShipPtr[j]->hyperspacePing=FALSE; } //all out.. for(i=0;iselection->numShips;i++) { //make selectable again! bitSet(movetodo->selection->ShipPtr[i]->flags,SOF_Selectable); } return(TRUE); break; } return(FALSE); } real32 hyperspaceCost(real32 distance,SelectCommand *selection) { sdword i; Ship *ship; real32 tempcost; real32 cost=0.0f; for(i=0;inumShips;i++) { ship = selection->ShipPtr[i]; if(distance < TW_HyperSpaceCostStruct[ship->shiptype].minDistance) { tempcost = TW_HyperSpaceCostStruct[ship->shiptype].min; } else { distance-= TW_HyperSpaceCostStruct[ship->shiptype].minDistance; tempcost = TW_HyperSpaceCostStruct[ship->shiptype].min + TW_HyperSpaceCostStruct[ship->shiptype].distanceSlope*distance; } if(tempcost > TW_HyperSpaceCostStruct[ship->shiptype].max) { tempcost = TW_HyperSpaceCostStruct[ship->shiptype].max; } cost+= tempcost; } return(cost); } /*----------------------------------------------------------------------------- Name : processMoveToDo Description : processes the move command Inputs : movetodo Outputs : Return : ----------------------------------------------------------------------------*/ bool processMoveToDo(CommandToDo *movetodo,bool passiveAttacked) { sdword i; SelectCommand *selection = movetodo->selection; sdword numShips = selection->numShips; Ship *ship; bool thisShipDone; bool allDone = TRUE; ShipStaticInfo *shipstatic; udword aiflags; sdword j; bool dontrotate; real32 MOVE_ARRIVE_TOLERANCE; dbgAssert(numShips > 0); for (i=0;iShipPtr[i]; shipstatic = (ShipStaticInfo *)ship->staticinfo; if(ship->shiptype == ResearchShip) { if(ship->flags & SOF_Slaveable) { dontrotate = TRUE; } else { dontrotate = (passiveAttacked & (bool)shipstatic->rotateToRetaliate); } } else { if(movetodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { dontrotate = TRUE; } else { dontrotate = (passiveAttacked & (bool)shipstatic->rotateToRetaliate); } } thisShipDone = FALSE; switch (ship->aistatecommand) { case 0: if ((ship->isDodging) || (universe.totaltimeelapsed < ship->DodgeTime)) { //ship is dodging...be a little more forgiving MOVE_ARRIVE_TOLERANCE = 250.0f; } else { MOVE_ARRIVE_TOLERANCE = 50.0f; } if (MoveReachedDestinationVariable(ship,&ship->moveTo,MOVE_ARRIVE_TOLERANCE)) { ship->aistatecommand = 1; vecZeroVector(ship->moveTo); vecZeroVector(ship->moveFrom); if(movetodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { for(j=1;jselection->numShips;j++) { vecZeroVector(movetodo->selection->ShipPtr[j]->moveTo); vecZeroVector(movetodo->selection->ShipPtr[j]->moveFrom); } } goto state1; } else { aiflags = (dontrotate) ? (AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured) : (AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured|AISHIP_FirstPointInDirectionFlying|AISHIP_PointInDirectionFlying|AISHIP_CarTurn); if (aishipFlyToPointAvoidingObjs(ship,&ship->moveTo,aiflags,(ship->shiptype == HeadShotAsteroid) ? (-tacticsGetShipsMaxVelocity(ship)) : -20.0f) & AISHIP_FLY_OBJECT_IN_WAY) { ship->aistatecommand = 1; vecZeroVector(ship->moveTo); vecZeroVector(ship->moveFrom); if(movetodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { for(j=1;jselection->numShips;j++) { vecZeroVector(movetodo->selection->ShipPtr[j]->moveTo); vecZeroVector(movetodo->selection->ShipPtr[j]->moveFrom); } } goto state1; } } break; state1: case 1: if ((dontrotate) || ((shipstatic->pitchdescend != 0.0f) && (ship->aidescend < 0.0f))) // -ve aidescend indicates ship is primarily moving down { thisShipDone = aitrackSteadyShipDriftOnly(ship); } else { vector trackthisheading; vecSet(trackthisheading,movetodo->move.heading.x,movetodo->move.heading.y,0.0f); vecNormalize(&trackthisheading); thisShipDone = (aitrackSteadyShipDriftOnly(ship) & aitrackHeadingWithBank(ship,&trackthisheading,FLYSHIP_HEADINGACCURACY,shipstatic->sinbank)); } break; } allDone &= thisShipDone; } return allDone; } /*----------------------------------------------------------------------------- Name : processMoveLeaderToDo Description : processes the move command, but only for the leader (this routine is only used for ships in formation moving, and the other ships maintain formation around the leader) Inputs : movetodo Outputs : Return : ----------------------------------------------------------------------------*/ bool processMoveLeaderToDo(CommandToDo *movetodo,bool passiveAttacked) { Ship *ship; SelectCommand *selection = movetodo->selection; ShipStaticInfo *shipstatic; bool dontrotate; udword aiflags; sdword j; udword moveresult; real32 MOVE_ARRIVE_TOLERANCE; dbgAssert(selection->numShips >= 1); ship = selection->ShipPtr[0]; shipstatic = (ShipStaticInfo *)ship->staticinfo; if(ship->shiptype == ResearchShip) { if(ship->flags & SOF_Slaveable) { dontrotate = TRUE; } else { dontrotate = (passiveAttacked & (bool)shipstatic->rotateToRetaliate); } } else { dontrotate = (passiveAttacked & (bool)shipstatic->rotateToRetaliate); } switch (ship->aistatecommand) { case 0: if ((ship->isDodging) || (universe.totaltimeelapsed < ship->DodgeTime)) { //ship is dodging...be a little more forgiving MOVE_ARRIVE_TOLERANCE = 250.0f; } else { MOVE_ARRIVE_TOLERANCE = 50.0f; } if (MoveReachedDestinationVariable(ship,&movetodo->move.destination,MOVE_ARRIVE_TOLERANCE)) { ship->aistatecommand = 1; vecZeroVector(ship->moveTo); vecZeroVector(ship->moveFrom); if(movetodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { for(j=1;jselection->numShips;j++) { vecZeroVector(movetodo->selection->ShipPtr[j]->moveTo); vecZeroVector(movetodo->selection->ShipPtr[j]->moveFrom); } } goto state1; } else { aiflags = (dontrotate) ? (AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured) : (AISHIP_DontFlyToObscuredPoints|AISHIP_ReturnImmedIfPointObscured|AISHIP_FirstPointInDirectionFlying|AISHIP_PointInDirectionFlying|AISHIP_CarTurn); moveresult = aishipFlyToPointAvoidingObjs(ship,&movetodo->move.destination,aiflags,movetodo->formation.travelvel); if (moveresult & AISHIP_FLY_OBJECT_IN_WAY) { vecZeroVector(ship->moveTo); vecZeroVector(ship->moveFrom); ship->aistatecommand = 1; if(movetodo->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { for(j=1;jselection->numShips;j++) { vecZeroVector(movetodo->selection->ShipPtr[j]->moveTo); vecZeroVector(movetodo->selection->ShipPtr[j]->moveFrom); } } goto state1; } } break; state1: case 1: if ((dontrotate) || ((shipstatic->pitchdescend != 0.0f) && (ship->aidescend < 0.0f))) // -ve aidescend indicates ship is primarily moving down { return aitrackSteadyShipDriftOnly(ship); } else { vector trackthisheading; vecSet(trackthisheading,movetodo->move.heading.x,movetodo->move.heading.y,0.0f); vecNormalize(&trackthisheading); return (aitrackSteadyShipDriftOnly(ship) & aitrackHeadingWithBank(ship,&trackthisheading,FLYSHIP_HEADINGACCURACY,shipstatic->sinbank)); } } return FALSE; } void SalCapCancelOrder(Ship *ship); void RemoveShipFromSpecial(Ship *shiptoremove) { if (shiptoremove->specialFlags & SPECIAL_IsASalvager) { SalCapCancelOrder(shiptoremove); SalCapOrderChangedCleanUp(shiptoremove); } else if (shiptoremove->shiptype == MinelayerCorvette) { MinelayerCorvetteOrderChangedCleanUp(shiptoremove); } else if(shiptoremove->shiptype == RepairCorvette) { RepairCorvetteOrderChanged(shiptoremove); } else if(shiptoremove->shiptype == HeavyCorvette) { heavyCorvetteOrderChanged(shiptoremove); } else if(shiptoremove->shiptype == AdvanceSupportFrigate) { if(shiptoremove->rceffect != NULL) { //need to turn off effect stopRepairEffect(shiptoremove); } } } void SpecialCleanup(CommandToDo *command) { sdword i; SelectCommand *selection = command->selection; sdword numShips = selection->numShips; //for all special ships using the repair effect for (i=0;iShipPtr[i]->rceffect != NULL) { if(selection->ShipPtr[i]->staticinfo->repairBeamCapable) { //ship must be using the repair effect stopRepairEffect(selection->ShipPtr[i]); } } RemoveShipFromSpecial(selection->ShipPtr[i]); } if (command->specialtargets) { memFree(command->specialtargets); command->specialtargets = NULL; } } void RemoveShipFromResourceCollecting(Ship *shiptoremove) { if (shiptoremove->rceffect != NULL) { TurnHarvestEffectOff(shiptoremove); } shiptoremove->rcstate1 = 0; shiptoremove->rcstate2 = 0; } void CollectResourceCleanup(CommandToDo *command) { sdword i; SelectCommand *selection = command->selection; sdword numShips = selection->numShips; for (i=0;iShipPtr[i]); } } void FreeCommandToDoContents(CommandToDo *command) { if (command->ordertype.attributes & COMMAND_IS_PROTECTING) { memFree(command->protect); bitClear(command->ordertype.attributes,COMMAND_IS_PROTECTING); } if (command->ordertype.attributes & COMMAND_IS_FORMATION) { FormationCleanup(command); bitClear(command->ordertype.attributes,COMMAND_IS_FORMATION); } if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { dbgAssert(command->ordertype.order != COMMAND_ATTACK); AttackCleanup(command); dbgAssert(command->attack); memFree(command->attack); bitClear(command->ordertype.attributes,COMMAND_IS_PASSIVEATTACKING); } //report dead command to tactics layer tacticsAttackCommandVoided(command); RemoveShipsFromCommand(command); switch (command->ordertype.order) { case COMMAND_ATTACK: AttackCleanup(command); dbgAssert(command->attack); memFree(command->attack); break; case COMMAND_MOVE: MoveCleanup(command); break; case COMMAND_MP_HYPERSPACEING: MpHyperspaceCleanup(command); break; case COMMAND_NULL: case COMMAND_HALT: case COMMAND_BUILDINGSHIP: break; case COMMAND_COLLECTRESOURCE: CollectResourceCleanup(command); break; case COMMAND_MILITARYPARADE: dbgAssert(command->militaryParade); FreeMilitaryParadeContents(command->militaryParade); memFree(command->militaryParade); break; case COMMAND_LAUNCHSHIP: LaunchCleanup(command); break; case COMMAND_DOCK: DockCleanup(command); break; case COMMAND_SPECIAL: SpecialCleanup(command); break; default: dbgAssert(FALSE); // should never get here break; } memFree(command->selection); } void FreeLastOrder(CommandToDo *command) { sdword dontClearHoldingPattern = FALSE; //report dead command to tactics layer tacticsAttackCommandVoided(command); // deliberately do not call // RemoveShipsFromCommand(command) // because last order is being freed in order to be changed, the actual command is not freed. switch (command->ordertype.order) { case COMMAND_ATTACK: AttackCleanup(command); dbgAssert(command->attack); memFree(command->attack); command->attack = NULL; dontClearHoldingPattern = TRUE; break; case COMMAND_MOVE: MoveCleanup(command); break; case COMMAND_MP_HYPERSPACEING: MpHyperspaceCleanup(command); break; case COMMAND_NULL: case COMMAND_HALT: case COMMAND_BUILDINGSHIP: break; case COMMAND_COLLECTRESOURCE: CollectResourceCleanup(command); break; case COMMAND_MILITARYPARADE: dbgAssert(command->militaryParade); FreeMilitaryParadeContents(command->militaryParade); memFree(command->militaryParade); command->militaryParade = NULL; break; case COMMAND_LAUNCHSHIP: LaunchCleanup(command); break; case COMMAND_DOCK: DockCleanup(command); break; case COMMAND_SPECIAL: SpecialCleanup(command); break; default: dbgAssert(FALSE); break; } if(!dontClearHoldingPattern) { bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } } void ClearPassiveAttacking(CommandToDo *command) { bitClear(command->ordertype.attributes,COMMAND_IS_PASSIVEATTACKING); AttackCleanup(command); dbgAssert(command->attack); memFree(command->attack); command->attack = NULL; /* Locked formation cleanup not needed because a check is done in AttackCleanup to see if a formation is locked and unlocks it if need be... */ /* if(command->ordertype.order == COMMAND_NULL) { //command is a NULL command if(command->ordertype.attributes & COMMAND_IS_FORMATION) { //command is a NULL command and in FORMATION //There for the formation had to be locked dbgAssert(command->formation.formationLocked); //unlock the formation unlockFormation(command); } } */ } void ClearProtecting(CommandToDo *command) { bitClear(command->ordertype.attributes,COMMAND_IS_PROTECTING); dbgAssert(command->protect); memFree(command->protect); command->protect = NULL; if (command->ordertype.attributes & COMMAND_IS_FORMATION) { // command->formation.formAroundProtectedShip = FALSE; if ((command->formation.formationtype == SPHERE_FORMATION) && (command->selection->numShips > 0)) { // sphere formation was surrounding protected ship, so let's undo this formationContentHasChanged(command); } } } void RemoveShipsFromDoingStuff(CommandLayer *comlayer,SelectCommand *selectcom) { sdword i; ShipPtr shiptoremove; Node *todonode; CommandToDo *todo; SelectCommand *todoselection; for (i=0;inumShips;i++) { shiptoremove = selectcom->ShipPtr[i]; if (shiptoremove->specialFlags & SPECIAL_rowGettingOutOfWay) { rowRemoveShipFromGettingOutOfWay(shiptoremove); } } todonode = comlayer->todolist.head; while (todonode != NULL) { todo = (CommandToDo *)listGetStructOfNode(todonode); todoselection = todo->selection; for (i=0;inumShips;i++) { shiptoremove = selectcom->ShipPtr[i]; if (todo->ordertype.attributes & COMMAND_IS_FORMATION) { sdword removedShipFromFormation; if ((removedShipFromFormation = formationRemoveShipFromSelection(todo,shiptoremove)) >= 0) { RemoveShipFromCommand(shiptoremove); RemoveShipFromFormation(shiptoremove); if ((todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) || (todo->ordertype.order == COMMAND_ATTACK)) { RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); } if (todo->ordertype.order == COMMAND_DOCK) { RemoveShipFromDocking(shiptoremove); } if (todo->ordertype.order == COMMAND_MOVE) { RemoveShipFromMoving(shiptoremove); } if(todo->ordertype.order == COMMAND_SPECIAL) { RemoveShipFromSpecial(shiptoremove); } if(todo->ordertype.order == COMMAND_MP_HYPERSPACEING) { removeShipFromMpHyperspaceing(shiptoremove); } if (todoselection->numShips < ABSOLUTE_MIN_SHIPS_IN_FORMATION) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } else { setFormationTravelVelocity(todo); //formationArrangeOptimum(todo); if (todo->formation.formationtype == WALL_FORMATION) // leave holes in WALL and SPHERE formations { if ((removedShipFromFormation == 0) && (shiptoremove->shiptype == TargetDrone)) // always preserve wall formation order if consists of Target Drones { todo->formation.formationtype = CUSTOM_FORMATION; formationContentHasChanged(todo); // leave holes in wall by making it custom } else { ; // deliberately do not call formationContentHasChanged for sphere formation } } else if (todo->formation.formationtype == SPHERE_FORMATION) { ; // deliberately do not call formationContentHasChanged for sphere formation } else if (todo->formation.formationtype == CUSTOM_FORMATION) { if (removedShipFromFormation == 0) { formationContentHasChanged(todo); // only update custom formation if leader died } } else { formationContentHasChanged(todo); } } } } else { switch (todo->ordertype.order) { case COMMAND_ATTACK: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_COLLECTRESOURCE: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); RemoveShipFromResourceCollecting(shiptoremove); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_MOVE: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); } RemoveShipFromMoving(shiptoremove); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_NULL: case COMMAND_HALT: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); } if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_SPECIAL: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { dbgAssert((todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) == 0); RemoveShipFromCommand(shiptoremove); RemoveShipFromSpecial(shiptoremove); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_MILITARYPARADE: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); } RemoveShipFromMilitaryParade(shiptoremove,todo->militaryParade); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_LAUNCHSHIP: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); } RemoveShipFromLaunching(shiptoremove); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_DOCK: if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { RemoveShipFromAttacking(shiptoremove); RemoveShipReferencesFromExtraAttackInfo(shiptoremove,todo); } RemoveShipFromDocking(shiptoremove); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; case COMMAND_BUILDINGSHIP: break; case COMMAND_MP_HYPERSPACEING: #ifdef HW_DEBUG dbgMessage("\nHyperspaceing ship removed from doing stuff!"); #endif if (clRemoveShipFromSelection(todoselection,shiptoremove)) { RemoveShipFromCommand(shiptoremove); removeShipFromMpHyperspaceing(shiptoremove); if (todoselection->numShips == 0) { Node *nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } break; default: dbgAssert(FALSE); break; } } } todonode = todonode->next; readyfornextnode: ; } } /*----------------------------------------------------------------------------- Name : clDerelictDied Description : Removes derelict from command layer, such that it is not the target of any ship. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clDerelictDied(CommandLayer *comlayer,DerelictPtr derelict) { Node *todonode; CommandToDo *todo; todonode = comlayer->todolist.head; while (todonode != NULL) { todo = (CommandToDo *)listGetStructOfNode(todonode); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { dbgAssert(todo->ordertype.order != COMMAND_ATTACK); if (clRemoveTargetFromSelection(todo->attack,(SpaceObjRotImpTarg *)derelict)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)derelict,todo); } } switch (todo->ordertype.order) { case COMMAND_NULL: case COMMAND_MOVE: case COMMAND_MP_HYPERSPACEING: case COMMAND_DOCK: case COMMAND_LAUNCHSHIP: break; case COMMAND_SPECIAL: if (todo->specialtargets) { clRemoveShipFromSelection(todo->specialtargets,(SpaceObjRotImpTarg *)derelict); } break; case COMMAND_HALT: case COMMAND_BUILDINGSHIP: case COMMAND_COLLECTRESOURCE: case COMMAND_MILITARYPARADE: break; case COMMAND_ATTACK: if (clRemoveTargetFromSelection(todo->attack,(SpaceObjRotImpTarg *)derelict)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)derelict,todo); } break; default: dbgAssert(FALSE); break; } todonode = todonode->next; } } /*----------------------------------------------------------------------------- Name : clResourceDied Description : Removes resource from command layer, such that it is not the target of any ship. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clResourceDied(CommandLayer *comlayer,ResourcePtr resource) { Node *todonode; CommandToDo *todo; todonode = comlayer->todolist.head; while (todonode != NULL) { todo = (CommandToDo *)listGetStructOfNode(todonode); if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { dbgAssert(todo->ordertype.order != COMMAND_ATTACK); if (clRemoveTargetFromSelection(todo->attack,(SpaceObjRotImpTarg *)resource)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)resource,todo); } } switch (todo->ordertype.order) { case COMMAND_NULL: case COMMAND_MOVE: case COMMAND_MP_HYPERSPACEING: case COMMAND_DOCK: case COMMAND_LAUNCHSHIP: case COMMAND_SPECIAL: case COMMAND_HALT: case COMMAND_BUILDINGSHIP: case COMMAND_MILITARYPARADE: break; case COMMAND_ATTACK: if (clRemoveTargetFromSelection(todo->attack,(SpaceObjRotImpTarg *)resource)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)resource,todo); } break; case COMMAND_COLLECTRESOURCE: if (resource == todo->collect.resource) { TurnOffAnyResourcingEffects(todo); todo->collect.resource = NULL; } break; default: dbgAssert(FALSE); break; } todonode = todonode->next; } } /*----------------------------------------------------------------------------- Name : clMissileDied Description : Removes missile from command layer, such that it is not the target of any ship. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clMissileDied(CommandLayer *comlayer,MissilePtr missile) { Node *todonode; CommandToDo *todo; todonode = comlayer->todolist.head; while (todonode != NULL) { todo = (CommandToDo *)listGetStructOfNode(todonode); switch (todo->ordertype.order) { case COMMAND_NULL: case COMMAND_MOVE: case COMMAND_MP_HYPERSPACEING: case COMMAND_DOCK: case COMMAND_LAUNCHSHIP: case COMMAND_SPECIAL: case COMMAND_COLLECTRESOURCE: case COMMAND_HALT: case COMMAND_BUILDINGSHIP: case COMMAND_MILITARYPARADE: break; case COMMAND_ATTACK: if (clRemoveTargetFromSelection(todo->attack,(SpaceObjRotImpTarg *)missile)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)missile,todo); } break; default: dbgAssert(FALSE); break; } todonode = todonode->next; } } /*----------------------------------------------------------------------------- Name : shipHasJustCloaked Description : cleans up te games such that the ship that just cloaked isn';t referenced by ships n' such that it shouldn't be. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void shipHasJustCloaked(Ship *ship) { RemoveShipFromBeingTargeted(&universe.mainCommandLayer,ship,REMOVE_CLOAKED); if(ship->playerowner != universe.curPlayerPtr && !proximityCanPlayerSeeShip(universe.curPlayerPtr,ship)) { //if ship isn't players...remove it from camera stack ccRemoveShip(&universe.mainCameraCommand,ship); } } void shipHasJustDisappeared(Ship *ship) { RemoveShipFromBeingTargeted(&universe.mainCommandLayer,ship,REMOVE_DISAPPEARED); } /*----------------------------------------------------------------------------- Name : shipHasJustBeenDisabled Description : cleans up the game such that the ship that has just been disabled isn't being attacked by ships, etc. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void shipHasJustBeenDisabled(Ship *ship) { RemoveShipFromBeingTargeted(&universe.mainCommandLayer,ship,REMOVE_DISABLED); ccRemoveShip(&universe.mainCameraCommand,ship); switch (ship->shiptype) { case DDDFrigate: DDDFrigateJustDisabled(ship); break; case CloakGenerator: CloakGeneratorJustDisabled(ship); break; case GravWellGenerator: GravWellGeneratorJustDisabled(ship); break; } //if ship doesn't have the animation it won't matter madLinkInCloseGunsShip(ship); if(ship->shiptype != SensorArray) { madLinkInPreDockingShip(ship); } if(ship->shiptype != SensorArray) { //we don't want to do this for the sensor array madLinkInCloseSpecialShip(ship); } } // removeShipsFromDockingWithThisShip // // If there are ships docking with this ship, they will be cancelled void removeShipsFromDockingWithThisShip(Ship *shiptoremove) { Node *todonode; CommandToDo *todo; if(!shiptoremove->staticinfo->canReceiveShips) { //ship can't receive ships therefore don't do check return; } todonode = universe.mainCommandLayer.todolist.head; while (todonode != NULL) { todo = (CommandToDo *)listGetStructOfNode(todonode); switch (todo->ordertype.order) { case COMMAND_DOCK: { SelectCommand *selection = todo->selection; sdword numShips = selection->numShips; Ship *ship; sdword i; for (i=0;iShipPtr[i]; if ((ship->dockvars.dockship == shiptoremove) || (ship->dockvars.busyingThisShip == shiptoremove)) { RemoveShipFromDocking(ship); } } } break; } todonode = todonode->next; } } /*----------------------------------------------------------------------------- Name : RemoveShipFromBeingTargeted Description : removes shiptoremove from command layer, such that it is not the target of any other ship Inputs : shiptoremove, removeFlag which can be: REMOVE_PROTECT - should remove ship from being protected REMOVE_CLOAKED - ship was just cloaked. If this flag is true, when the ship is removed, it is only removed if the reference that is being considered CAN'T see the ship REMOVE_DISABLED - ship was SOF_Disable'd (e.g. Salvaged) REMOVE_DISAPPEARED - ship was hidden (SOF_Hide'd) Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipFromBeingTargeted(CommandLayer *comlayer,ShipPtr shiptoremove,udword removeFlag) { Node *todonode; Node *nextnode; CommandToDo *todo; udword REMOVE_PROTECT_FLAG = removeFlag & REMOVE_PROTECT; udword CLOAKED_REMOVAL_FLAG = removeFlag & REMOVE_CLOAKED; udword SHIP_JUST_DISABLED_FLAG = removeFlag & REMOVE_DISABLED; udword SHIP_DISAPPEARED_FLAG = removeFlag & REMOVE_DISAPPEARED; udword SHIP_HYPERSPACEING_FLAG = removeFlag & REMOVE_HYPERSPACING; todonode = comlayer->todolist.head; while (todonode != NULL) { todo = (CommandToDo *)listGetStructOfNode(todonode); //if ship is hyperspaceing out...via mp hp move if(SHIP_HYPERSPACEING_FLAG) { //prevent ships from docking with it! if(todo->ordertype.order == COMMAND_DOCK) { SelectCommand *selection = todo->selection; sdword numShips = selection->numShips; bool removefromdocking; Ship *ship; sdword i; for (i=0;iShipPtr[i]; removefromdocking = FALSE; if (ship->dockvars.dockship == shiptoremove) { removefromdocking = TRUE; if(ship->dockvars.dockship->shiptype == AdvanceSupportFrigate) { if(ship->dockvars.dockstate2 != ASF_WAITLATCHPOINT) { removefromdocking = FALSE; } } } else if(ship->dockvars.busyingThisShip == shiptoremove) { removefromdocking = TRUE; if(ship->dockvars.busyingThisShip->shiptype == AdvanceSupportFrigate) { if(ship->dockvars.dockstate2 != ASF_WAITLATCHPOINT) { removefromdocking = FALSE; } } } if (removefromdocking) RemoveShipFromDocking(ship); } } else if(todo->ordertype.order == COMMAND_SPECIAL) { sdword i; for(i=0;iselection->numShips;i++) { if(todo->selection->ShipPtr[i]->shiptype == SalCapCorvette) { if((((SalCapCorvetteSpec *)(todo->selection->ShipPtr[i])->ShipSpecifics))->target != NULL && ((Ship *)(((SalCapCorvetteSpec *)(todo->selection->ShipPtr[i])->ShipSpecifics))->target) == shiptoremove) { if(((ShipStaticInfo *)todo->selection->ShipPtr[i]->staticinfo)->custshipheader.CustShipRemoveShipReferences != NULL) { ((ShipStaticInfo *)todo->selection->ShipPtr[i]->staticinfo)->custshipheader.CustShipRemoveShipReferences(todo->selection->ShipPtr[i], shiptoremove); } } } } if ( SHIP_DISAPPEARED_FLAG || (!(CLOAKED_REMOVAL_FLAG && proximityCanPlayerSeeShip(todo->selection->ShipPtr[0]->playerowner,shiptoremove))) ) { if (!SHIP_JUST_DISABLED_FLAG) { if (todo->specialtargets) { clRemoveShipFromSelection(todo->specialtargets,shiptoremove); } } } } todonode = todonode->next; goto readyfornextnode; } if(REMOVE_PROTECT_FLAG) { if (todo->ordertype.attributes & COMMAND_IS_PROTECTING) { if ( SHIP_DISAPPEARED_FLAG || (!(CLOAKED_REMOVAL_FLAG && proximityCanPlayerSeeShip(todo->selection->ShipPtr[0]->playerowner,shiptoremove))) ) { clRemoveShipFromSelection((SelectCommand *)todo->protect,shiptoremove); if (todo->protect->numShips == 0) { ClearProtecting(todo); if (todo->ordertype.attributes == 0) { if (todo->ordertype.order == COMMAND_NULL) { nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } } } } } } if (todo->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { dbgAssert(todo->ordertype.order != COMMAND_ATTACK); if ( SHIP_DISAPPEARED_FLAG || (!(CLOAKED_REMOVAL_FLAG && proximityCanPlayerSeeShip(todo->selection->ShipPtr[0]->playerowner,shiptoremove))) ) { if (clRemoveShipFromSelection((SelectCommand *)todo->attack,shiptoremove)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)shiptoremove,todo); } } } switch (todo->ordertype.order) { case COMMAND_ATTACK: if ( SHIP_DISAPPEARED_FLAG || (!(CLOAKED_REMOVAL_FLAG && proximityCanPlayerSeeShip(todo->selection->ShipPtr[0]->playerowner,shiptoremove))) ) { if (clRemoveShipFromSelection((SelectCommand *)todo->attack,shiptoremove)) { RemoveAttackTargetFromExtraAttackInfo((SpaceObjRotImpTarg *)shiptoremove,todo); } } break; case COMMAND_SPECIAL: if ( SHIP_DISAPPEARED_FLAG || (!(CLOAKED_REMOVAL_FLAG && proximityCanPlayerSeeShip(todo->selection->ShipPtr[0]->playerowner,shiptoremove))) ) { if (!SHIP_JUST_DISABLED_FLAG) { if (todo->specialtargets) { sdword h; clRemoveShipFromSelection(todo->specialtargets,shiptoremove); for(h=0;hselection->numShips;h++) { if(todo->selection->ShipPtr[h]->shiptype == SalCapCorvette || todo->selection->ShipPtr[h]->shiptype == JunkYardDawg) { salCapCleanUpCloakingTarget(todo->selection->ShipPtr[h],shiptoremove); } } } } } break; case COMMAND_DOCK: if (!SHIP_DISAPPEARED_FLAG) { SelectCommand *selection = todo->selection; sdword numShips = selection->numShips; Ship *ship; sdword i; for (i=0;iShipPtr[i]; if ((ship->dockvars.dockship == shiptoremove) || (ship->dockvars.busyingThisShip == shiptoremove)) { RemoveShipFromDocking(ship); } } } break; case COMMAND_LAUNCHSHIP: if (!SHIP_DISAPPEARED_FLAG) if (shiptoremove == todo->launchship.receiverShip) { Ship *ship = todo->selection->ShipPtr[0]; dbgAssert(todo->selection->numShips == 1); RemoveShipFromLaunching(ship); nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } break; case COMMAND_BUILDINGSHIP: if (!SHIP_DISAPPEARED_FLAG) if (shiptoremove == todo->buildingship.creator) { nextnode = todonode->next; FreeCommandToDoContents(todo); listDeleteNode(todonode); todonode = nextnode; goto readyfornextnode; } break; case COMMAND_MILITARYPARADE: if (!SHIP_DISAPPEARED_FLAG) if (shiptoremove == todo->militaryParade->aroundShip) { ChangeOrderToHalt(todo); } break; case COMMAND_NULL: case COMMAND_MOVE: case COMMAND_MP_HYPERSPACEING: case COMMAND_COLLECTRESOURCE: case COMMAND_HALT: break; default: dbgAssert(FALSE); break; } todonode = todonode->next; readyfornextnode: ; } } /*============================================================================= Public Functions: =============================================================================*/ /*----------------------------------------------------------------------------- Name : clInit Description : Initializes the command layer Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clInit(CommandLayer *comlayer) { listInit(&comlayer->todolist); } /*----------------------------------------------------------------------------- Name : clClose Description : Closes the command layer Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clClose(CommandLayer *comlayer) { Node *nextnode; Node *curnode = comlayer->todolist.head; CommandToDo *command; // free contents of each node of list while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (singlePlayerGameLoadNewLevelFlag) { // preserve parade formation throughout hyperspace if (command->ordertype.order == COMMAND_MILITARYPARADE) { Ship *aroundship = command->militaryParade->aroundShip; if ((aroundship) && (aroundship->playerowner == universe.curPlayerPtr)) { goto dontfreeit; } } } FreeCommandToDoContents(command); listDeleteNode(curnode); dontfreeit: curnode = nextnode; } } /*----------------------------------------------------------------------------- Name : clCancelAllLaunchOrdersFromPlayer Description : Cancels all launch orders so ships in process of launching will be available to dock and to fix a fatal bug with the single player game Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clCancelAllLaunchOrdersFromPlayer(struct Player *player) { Node *nextnode; Node *curnode = universe.mainCommandLayer.todolist.head; CommandToDo *command; sdword i; dbgAssert(singlePlayerGame); while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (command->ordertype.order == COMMAND_LAUNCHSHIP) { for(i=0;iselection->numShips;i++) { if(command->selection->ShipPtr[i]->playerowner == player) { if(!ShipCanHyperspace(command->selection->ShipPtr[i]) ) { RemoveShipFromLaunching(command->selection->ShipPtr[i]); if(clRemoveShipFromSelection(command->selection,command->selection->ShipPtr[i])) { if(command->selection->numShips == 0) { FreeCommandToDoContents(command); listDeleteNode(curnode); break; } } } } } } curnode = nextnode; } } /*----------------------------------------------------------------------------- Name : clReset Description : Resets the command layer Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clReset(CommandLayer *comlayer) { clClose(comlayer); } /*----------------------------------------------------------------------------- Name : IsSelectionAlreadyDoingSomething Description : sees if the selection specified is alread in the command layer Inputs : Outputs : Return : CommandToDo pointer if the selection specified is already present, NULL otherwise ----------------------------------------------------------------------------*/ CommandToDo *IsSelectionAlreadyDoingSomething(CommandLayer *comlayer,SelectCommand *selectcom) { Node *curnode = comlayer->todolist.head; CommandToDo *command; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); if (SelectionsAreEquivalent(selectcom,command->selection)) { return command; } curnode = curnode->next; } return NULL; } //returns TRUE if a strike craft is in the selection bool selectionHasStrikeCraft(SelectCommand *selection) { sdword i; for(i=0;inumShips;i++) { if(selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Fighter || selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Corvette) { return TRUE; } } return FALSE; } bool isSelectionExclusivlyStrikeCraft(SelectCommand *selection) { sdword i; for(i=0;inumShips;i++) { if(!(selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Fighter || selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Corvette)) { return FALSE; } } return TRUE; } bool isSelectionExclusivlyStrikeCraftorNoMoveAndAttackShips(SelectCommand *selection) { sdword i; for(i=0;inumShips;i++) { if(!(selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Fighter || selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Corvette || selection->ShipPtr[i]->staticinfo->cantMoveAndAttack)) { return FALSE; } } return TRUE; } bool selectionHasSwarmers(SelectCommand *selection) { sdword i; Ship *ship; for(i=0;inumShips;i++) { ship = selection->ShipPtr[i]; if ((ship->shiptype == P2Swarmer) || (ship->shiptype == P2AdvanceSwarmer)) { return TRUE; } } return FALSE; } /* ;============================================================================= ; Solution for ships moving to single point nastiness: ;============================================================================= ; If it looks like a bunch of ships in different parts of the world are moving to a "central location" then ; their relative positions will be scaled near to a single point using the factor SHRINKMIN. ; If it looks like a bunch of ships are moving to a relatively far location, they will preserve their ; relative positions (SHRINKMAX, which is typically 1.0) ; Here is the technical description: ; consider a distance d, which is the average position the ships travel. ; consider a radius r, within which all ships ordered to move are within ; Take the ratio d/r. ; if d/r is less than DISTMINRATIO, the relative scaling factor will be SHRINKMIN ; if d/r is greater than DISTMAXRATIO, the relative scaling factor will be SHRINKMAX ; in between, ; scales from a d/r ratio of DISTMINRATIO to a scale factor of SHRINKMIN ; DISTMAXRATIO SHRINKMAX */ typedef struct { vector fromtoship; vector totoship; } ShipMoveCalcs; #define NUM_STACK_MOVECALCS 100 /*----------------------------------------------------------------------------- Name : CalculateMoveToPoints Description : Inputs : selection, from, to Outputs : moveTo vectors are filled in Return : ----------------------------------------------------------------------------*/ void CalculateMoveToPoints(SelectCommand *selection,vector from,vector to) { sdword numShips = selection->numShips; ShipMoveCalcs stackMoveCalcs[NUM_STACK_MOVECALCS]; bool moveCalcsAllocated = FALSE; ShipMoveCalcs *movecalcs; sdword i; vector d,r; real32 dsqr,rsqr,dmag; real32 maxrsqr,maxrmag; real32 scale,doverr; dbgAssert(numShips > 0); if (numShips == 1) { selection->ShipPtr[0]->moveTo = to; return; } vecSub(d,to,from); dsqr = vecMagnitudeSquared(d); dmag = fsqrt(dsqr); if (numShips > NUM_STACK_MOVECALCS) { moveCalcsAllocated = TRUE; movecalcs = memAlloc(sizeof(ShipMoveCalcs)*numShips,"movecalcs",0); } else { movecalcs = stackMoveCalcs; } maxrsqr = -1.0f; for (i=0;iShipPtr[i]->posinfo.position,from); movecalcs[i].fromtoship = r; movecalcs[i].totoship = r; rsqr = vecMagnitudeSquared(r); if (rsqr > maxrsqr) { maxrsqr = rsqr; } } dbgAssert(maxrsqr >= 0.0f); maxrmag = fsqrt(maxrsqr); // scales from a d/r ratio of DISTMINRATIO to SHRINKMIN // DISTMAXRATIO SHRINKMAX if (dmag < maxrmag) // assumes DISTMINRATIO = 1.0 { // shrink to a point: scale = SHRINKMIN; } else { // preserve relative movement doverr = dmag / maxrmag; if (doverr >= DISTMAXRATIO) { // skip scaling scale = SHRINKMAX; } else { // somewhere between moving to a point and relative movement scale = ((doverr - DISTMINRATIO) * (SHRINKMAX - SHRINKMIN) / (DISTMAXRATIO - DISTMINRATIO)) + SHRINKMIN; } } if (scale != 1.0f) for (i=0;iShipPtr[i]->moveTo,to,movecalcs[i].totoship); } if (moveCalcsAllocated) { memFree(movecalcs); } } bool selectionHasOtherForceNoAttackAndMoveShips(SelectCommand *selection) { sdword i; for(i=0;inumShips;i++) { if(selection->ShipPtr[i]->staticinfo->cantMoveAndAttack) { return TRUE; } } return FALSE; } void ChangeOrderToMove(CommandToDo *alreadycommand,vector from,vector to) { SelectCommand *selectcom = alreadycommand->selection; sdword i,j; if (alreadycommand->ordertype.attributes & COMMAND_IS_PROTECTING) { ClearProtecting(alreadycommand); } if (alreadycommand->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { ClearPassiveAttacking(alreadycommand); } if(alreadycommand->ordertype.order == COMMAND_ATTACK) { //selection is actually attacking...so this should be a move attack if(selectionHasStrikeCraft(alreadycommand->selection) || selectionHasOtherForceNoAttackAndMoveShips(alreadycommand->selection)) { //filter strike craft...give strike craft a move order, //and give give capital ships a majik command if(!isSelectionExclusivlyStrikeCraftorNoMoveAndAttackShips(alreadycommand->selection)) { //some capitals some strike craft MaxSelection tempSelection; tempSelection.numShips = 0; j = 0; dbgAssert(alreadycommand->selection->numShips < COMMAND_MAX_SHIPS); for(i=0;iselection->numShips;i++) { if(alreadycommand->selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Fighter || alreadycommand->selection->ShipPtr[i]->staticinfo->shipclass == CLASS_Corvette || alreadycommand->selection->ShipPtr[i]->staticinfo->cantMoveAndAttack) { tempSelection.numShips++; tempSelection.ShipPtr[j] = alreadycommand->selection->ShipPtr[i]; j++; } } RemoveShipsFromDoingStuff(&universe.mainCommandLayer,(SelectCommand *)&tempSelection); clMoveThese(&universe.mainCommandLayer,(SelectCommand *)&tempSelection,from,to); if(alreadycommand->ordertype.attributes & COMMAND_IS_FORMATION) { clFormation(&universe.mainCommandLayer,(SelectCommand *)&tempSelection,alreadycommand->formation.formationtype); } goto StillDoAttackMoveWithCapitals; } } else { StillDoAttackMoveWithCapitals: //just capital ships in selection if(!bitTest(alreadycommand->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL)) { alreadycommand->ordertype.attributes |= COMMAND_IS_ATTACKINGANDMOVING; alreadycommand->move.destination = to; vecSub(alreadycommand->move.heading,to,from); vecNormalize(&alreadycommand->move.heading); InitShipsForAI(selectcom,TRUE); CalculateMoveToPoints(selectcom,from,to); for (i=0;inumShips;i++) { //selectcom->ShipPtr[i]->moveTo = to; selectcom->ShipPtr[i]->moveFrom = selectcom->ShipPtr[i]->posinfo.position; //soundEventStartEngine(selectcom->ShipPtr[i]); } PrepareShipsForCommand(alreadycommand,TRUE); return; } bitClear(alreadycommand->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); } } if (alreadycommand->ordertype.order == COMMAND_MOVE) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nChanging movement direction"); #endif } else { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nChanging command to move"); #endif FreeLastOrder(alreadycommand); alreadycommand->ordertype.order = COMMAND_MOVE; } alreadycommand->move.destination = to; vecSub(alreadycommand->move.heading,to,from); vecNormalize(&alreadycommand->move.heading); if (alreadycommand->ordertype.attributes & COMMAND_IS_FORMATION) { InitShipAI(selectcom->ShipPtr[0],TRUE); } else { InitShipsForAI(selectcom,TRUE); } CalculateMoveToPoints(selectcom,from,to); for (i=0;inumShips;i++) { //selectcom->ShipPtr[i]->moveTo = to; selectcom->ShipPtr[i]->moveFrom = selectcom->ShipPtr[i]->posinfo.position; soundEventStartEngine(selectcom->ShipPtr[i]); } PrepareShipsForCommand(alreadycommand,TRUE); if(selectcom->numShips == 1) { if(selectcom->ShipPtr[0]->shiptype == Probe) { ((ProbeSpec *) selectcom->ShipPtr[0]->ShipSpecifics)->HaveMoved = TRUE; ((ProbeSpec *) selectcom->ShipPtr[0]->ShipSpecifics)->moveTime = universe.totaltimeelapsed; } } return; } /*----------------------------------------------------------------------------- Name : clMoveThese Description : Command to move ships Inputs : selectcom, from, to Outputs : Return : ----------------------------------------------------------------------------*/ void clMoveThese(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to) { CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; udword sizeofselection; sdword numShips = selectcom->numShips; sdword i; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships To Move"); #endif return; } if(numShips == 1) { if(selectcom->ShipPtr[0]->shiptype == Probe) { //need probe 'solution' again //so set specific probe variables ((ProbeSpec *) selectcom->ShipPtr[0]->ShipSpecifics)->HaveMoved = TRUE; ((ProbeSpec *) selectcom->ShipPtr[0]->ShipSpecifics)->moveTime = universe.totaltimeelapsed; } } if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selectcom)) != NULL) { ChangeOrderToMove(alreadycommand,from,to); return; } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to move"); #endif { sdword sizeofcopycom; SelectCommand *copycom; CommandToDo *currentShipCommand; sizeofcopycom = sizeofSelectCommand(selectcom->numShips); copycom = memAlloc(sizeofcopycom,"cc1(copycom)", Pyrophoric); memcpy(copycom,selectcom,sizeofcopycom); for(i=0;inumShips;i++) { currentShipCommand = getShipAndItsCommand(&universe.mainCommandLayer,copycom->ShipPtr[i]); if(currentShipCommand != NULL) { //ship is doing something if(currentShipCommand->ordertype.order == COMMAND_ATTACK) { //ship is attacking MaxSelection tempSelection; MaxAnySelection targetSelection; TypeOfFormation formation; bool needFormation; sdword j; tempSelection.numShips = 0; for(j=0;jselection->numShips;j++) { if(ShipInSelection(copycom,currentShipCommand->selection->ShipPtr[j])) { tempSelection.ShipPtr[tempSelection.numShips] = currentShipCommand->selection->ShipPtr[j]; tempSelection.numShips++; clRemoveShipFromSelection(copycom,currentShipCommand->selection->ShipPtr[j]); } } dbgAssert(tempSelection.numShips > 0); for(j=0;jattack->numTargets;j++) { targetSelection.TargetPtr[j] = currentShipCommand->attack->TargetPtr[j]; } targetSelection.numTargets = currentShipCommand->attack->numTargets; if(currentShipCommand->ordertype.attributes & COMMAND_IS_FORMATION) { formation = currentShipCommand->formation.formationtype; needFormation = TRUE; } else { needFormation = FALSE; } RemoveShipsFromDoingStuff(&universe.mainCommandLayer,(SelectCommand *)&tempSelection); if(needFormation) clFormation(&universe.mainCommandLayer,(SelectCommand *)&tempSelection,formation); //need to reissue attack because the formation command cancels the attack! clAttackThese(&universe.mainCommandLayer,(SelectCommand *)&tempSelection,(SelectAnyCommand *)&targetSelection); clMoveThese(&universe.mainCommandLayer,(SelectCommand *)&tempSelection,from,to); } } } sizeofcopycom = sizeofSelectCommand(copycom->numShips); memcpy(selectcom,copycom,sizeofcopycom); memFree(copycom); numShips = selectcom->numShips; if(numShips == 0) return; } RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); sizeofselection = sizeofSelectCommand(numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); newcommand->selection = selection; newcommand->move.destination = to; vecSub(newcommand->move.heading,to,from); vecNormalize(&newcommand->move.heading); newcommand->ordertype.order = COMMAND_MOVE; newcommand->ordertype.attributes = 0; InitShipsForAI(selection,TRUE); CalculateMoveToPoints(selection,from,to); for (i=0;iShipPtr[i]->moveTo = to; selection->ShipPtr[i]->moveFrom = selection->ShipPtr[i]->posinfo.position; soundEventStartEngine(selectcom->ShipPtr[i]); } PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); } /*----------------------------------------------------------------------------- Name : clMove Description : Command to move ships Inputs : selectcom, from, to Outputs : Return : ----------------------------------------------------------------------------*/ void clMove(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to) { SelectCommand *copycom; udword sizeofcopycom; Node *curnode; Node *endnode; Node *nextnode; CommandToDo *command; MakeShipsMobile(selectcom); salCapExtraSpecialOrderCleanUp(selectcom,COMMAND_MOVE,NULL,NULL); if (selectcom->numShips < 1) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo ships to move"); #endif return; } #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsReportMove(comlayer,selectcom); } sizeofcopycom = sizeofSelectCommand(selectcom->numShips); copycom = memAlloc(sizeofcopycom,"cc2(copycom)", Pyrophoric); memcpy(copycom,selectcom,sizeofcopycom); // Now see if the selected ships are already in the command layer, and if so, individually order these groups // to move (to preserve formations) curnode = comlayer->todolist.head; endnode = comlayer->todolist.tail; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (TheseShipsAreInSelection(command->selection,copycom)) { MakeShipsNotIncludeTheseShips(copycom,command->selection); ChangeOrderToMove(command,from,to); } if (copycom->numShips == 0) { break; } if (curnode == endnode) { break; } curnode = nextnode; } // tell rest of ships left to move too. if (copycom->numShips != 0) { clMoveThese(comlayer,copycom,from,to); } memFree(copycom); } void TurnAllGravwellsOff(SelectCommand *selectcom) { sdword i; for(i=0;inumShips;i++) { if(selectcom->ShipPtr[i]->shiptype == GravWellGenerator) { if( ((GravWellGeneratorSpec *)(selectcom->ShipPtr[i]->ShipSpecifics))->GravFieldOn) { turnoffGravwell(selectcom->ShipPtr[i]); } } } } void ChangeOrderToHyperSpace(CommandToDo *alreadycommand,vector from,vector to) { SelectCommand *selectcom = alreadycommand->selection; if (alreadycommand->ordertype.attributes & COMMAND_IS_PROTECTING) { ClearProtecting(alreadycommand); } if (alreadycommand->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { ClearPassiveAttacking(alreadycommand); } if (alreadycommand->ordertype.order == COMMAND_MP_HYPERSPACEING) { dbgMessagef("\nA little wierd: Ships that were hyperspaceing have been given an order to hyperspace again. Ignoreing..."); return; dbgFatalf(DBG_Loc,"Shouldn't Be able to Order these guys...they're hyperspaceing!"); } else { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nChanging command to Hyperspace"); #endif FreeLastOrder(alreadycommand); alreadycommand->ordertype.order = COMMAND_MP_HYPERSPACEING; } alreadycommand->move.destination = to; vecSub(alreadycommand->move.heading,to,from); vecNormalize(&alreadycommand->move.heading); if (alreadycommand->ordertype.attributes & COMMAND_IS_FORMATION) { InitShipAI(selectcom->ShipPtr[0],TRUE); } else { InitShipsForAI(selectcom,TRUE); } //CalculateMoveToPoints(selectcom,from,to); //for (i=0;inumShips;i++) //{ // //selectcom->ShipPtr[i]->moveTo = to; // selectcom->ShipPtr[i]->moveFrom = selectcom->ShipPtr[i]->posinfo.position; // //soundEventStartEngine(selectcom->ShipPtr[i]); //} alreadycommand->hyperSpaceingTime = 0.0f; alreadycommand->hyperspaceState = COM_HYPERSPACE_START; PrepareShipsForCommand(alreadycommand,TRUE); return; } /*----------------------------------------------------------------------------- Name : clMpHyperspaceThese Description : Command to mp hyperspace ships Inputs : selectcom, from, to Outputs : Return : ----------------------------------------------------------------------------*/ void clMpHyperspaceThese(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to) { CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; udword sizeofselection; sdword numShips = selectcom->numShips; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships To hyperspace"); #endif return; } if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selectcom)) != NULL) { ChangeOrderToHyperSpace(alreadycommand,from,to); return; } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to hyperspace"); #endif TurnAllGravwellsOff(selectcom); RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); sizeofselection = sizeofSelectCommand(numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); newcommand->selection = selection; newcommand->move.destination = to; vecSub(newcommand->move.heading,to,from); vecNormalize(&newcommand->move.heading); newcommand->ordertype.order = COMMAND_MP_HYPERSPACEING; newcommand->ordertype.attributes = 0; newcommand->hyperSpaceingTime = 0.0f; newcommand->hyperspaceState = COM_HYPERSPACE_START; InitShipsForAI(selection,TRUE); //CalculateMoveToPoints(selection,from,to); /* for (i=0;iShipPtr[i]->moveTo = to; selection->ShipPtr[i]->moveFrom = selection->ShipPtr[i]->posinfo.position; soundEventStartEngine(selectcom->ShipPtr[i]); } */ PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); } /*----------------------------------------------------------------------------- Name : getNewHyperspaceingDestination Description : calculates the correct destination point for a group of ships hyperspacing Inputs : selection, from, to,newTo Outputs : Return : ----------------------------------------------------------------------------*/ void getNewHyperspaceingDestination(vector *newTo,vector *from,vector *to,SelectCommand *selection) { //sdword i; // vector groupPos; //real32 temp; //groupPos.x=0.0f; //groupPos.y=0.0f; //groupPos.z=0.0f; //for(i=0;inumShips;i++) //{ // vecAddTo(groupPos,selection->ShipPtr[i]->posinfo.position); //} //vecDivideByScalar(groupPos,((float)selection->numShips),temp); //grouppos is the average posiiton of the group vecSub(*newTo,selection->ShipPtr[0]->posinfo.position,*from); //add the offset to the desired desitination vecAddTo(*newTo,*to); } /*----------------------------------------------------------------------------- Name : clMpHyperspace Description : Command to hyperspace ships in multiplayer game ships Inputs : selectcom, from, to Outputs : Return : ----------------------------------------------------------------------------*/ void clMpHyperspace(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to) { SelectCommand *copycom; udword sizeofcopycom; Node *curnode; Node *endnode; Node *nextnode; CommandToDo *command; sdword cost; vector distsqr; real32 tempdist,distance; sdword i; vector newTo; sdword playerindex; makeSelectionHyperspaceCapable(selectcom); //should probably leave in this check...you never know if (selectcom->numShips < 1) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo ships to hyperspace"); #endif return; } vecSub(distsqr,from,to); tempdist = vecMagnitudeSquared(distsqr); distance = fsqrt(tempdist); cost = (sdword)hyperspaceCost(distance,selectcom); playerindex = selectcom->ShipPtr[0]->playerowner->playerIndex; if ( (playerindex >= tpGameCreated.numPlayers) || // computer player RU's arene't deterministic, always let it spend RUs (computer RUs can go negative) (universe.players[playerindex].resourceUnits >= cost) ) { universe.players[playerindex].resourceUnits -= cost; universe.gameStats.playerStats[playerindex].totalResourceUnitsSpent += cost; } else { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nCan't afford HS - cancelling"); #endif return; } #ifdef DEBUG_TACTICS if(tacticsOn) #endif { tacticsReportMove(comlayer,selectcom); } //remove ships from being targeted... for(i=0;inumShips;i++) { RemoveShipFromBeingTargeted(comlayer,selectcom->ShipPtr[i],REMOVE_PROTECT|REMOVE_HYPERSPACING); } sizeofcopycom = sizeofSelectCommand(selectcom->numShips); copycom = memAlloc(sizeofcopycom,"cc3(copycom)", Pyrophoric); memcpy(copycom,selectcom,sizeofcopycom); // Now see if the selected ships are already in the command layer, and if so, individually order these groups // to hyperspace (to preserve formations) curnode = comlayer->todolist.head; endnode = comlayer->todolist.tail; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (TheseShipsAreInSelection(command->selection,copycom)) { MakeShipsNotIncludeTheseShips(copycom,command->selection); getNewHyperspaceingDestination(&newTo,&from,&to,command->selection); ChangeOrderToHyperSpace(command,from,newTo); } if (copycom->numShips == 0) { break; } if (curnode == endnode) { break; } curnode = nextnode; } // tell rest of ships left to move too. if (copycom->numShips != 0) { getNewHyperspaceingDestination(&newTo,&from,&to,copycom); clMpHyperspaceThese(comlayer,copycom,from,newTo); } memFree(copycom); } /*----------------------------------------------------------------------------- Name : AttackClosestOpponent Description : finds and returns nearest opponent to ship to attack Inputs : ship, todo Outputs : Return : ----------------------------------------------------------------------------*/ SpaceObjRotImpTarg *AttackClosestOpponent(Ship *ship,AttackCommand *attack) { sdword numTargetsToAttack = attack->numTargets; sdword i; vector dist; real32 distsqr; real32 mindist; sdword attacki; SpaceObjRotImpTarg *attackme; if (numTargetsToAttack <= 0) { return NULL; } mindist = REALlyBig; attacki = -1; for (i=0;iTargetPtr[i]; vecSub(dist,ship->posinfo.position,attackme->posinfo.position); distsqr = vecMagnitudeSquared(dist); if (distsqr < mindist) { mindist = distsqr; attacki = i; } } dbgAssert(attacki != -1); return attack->TargetPtr[attacki]; } /*----------------------------------------------------------------------------- Name : RemoveShipFromAttacking Description : Removes ship from attacking Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipFromAttacking(Ship *ship) { if(ship->shiptype == ResourceCollector) { removeResourcerFromAttacking(ship); } if (ship->attackvars.multipleAttackTargets != NULL) { memFree(ship->attackvars.multipleAttackTargets); ship->attackvars.multipleAttackTargets = NULL; } ship->attackvars.attacktarget = NULL; ship->attackvars.myLeaderIs = NULL; ship->attackvars.myWingmanIs = NULL; ship->shipisattacking = FALSE; bitClear(ship->specialFlags,SPECIAL_Kamikaze|SPECIAL_BrokenFormation|SPECIAL_AttackFromAbove); if (ship->flightman != FLIGHTMAN_NULL) { flightmanClose(ship); } if(ship->formationcommand != NULL) { if(ship->formationcommand->formation.formationLocked) unlockFormation(ship->formationcommand); ship->formationcommand->formation.flipselectionfixed = FALSE; ship->formationcommand->formation.needFix = FALSE; } //dbgMessagef("\nShip %i is no longer attacking", ship->shipID.shipNumber); } /*----------------------------------------------------------------------------- Name : AttackCleanup Description : cleans up anything from a attack command Inputs : attacktodo Outputs : Return : ----------------------------------------------------------------------------*/ void AttackCleanup(struct CommandToDo *attacktodo) { sdword i; SelectCommand *selection = attacktodo->selection; sdword numShips = selection->numShips; bool clearmove = TRUE; if (bitTest(attacktodo->ordertype.attributes,COMMAND_IS_ATTACKINGANDMOVING)) { bitClear(attacktodo->ordertype.attributes,COMMAND_IS_ATTACKINGANDMOVING); } if(attacktodo->ordertype.order == COMMAND_MOVE) { clearmove = FALSE; } if(attacktodo->ordertype.attributes & COMMAND_IS_FORMATION) { #ifdef DEBUG_TACTICS if(tacticsOn) #endif { if ((attacktodo->formation.formationtype == SPHERE_FORMATION) && (attacktodo->selection->numShips > 0)) { attacktodo->formation.enders = FALSE; //attacktodo->formation.endership = NULL; formationContentHasChanged(attacktodo); } } } for (i=0;iShipPtr[i]); // don't clear unless clearing a move otherwise some moves will get erroneously cancelled if(clearmove) { vecZeroVector(selection->ShipPtr[i]->moveTo); vecZeroVector(selection->ShipPtr[i]->moveFrom); } bitClear(selection->ShipPtr[i]->specialFlags,SPECIAL_BrokenFormation); } //attack has been cancelled, lets cue up a gun closing animation //need a delay item here... madLinkInCloseGuns(attacktodo,1); } /*----------------------------------------------------------------------------- Name : RemoveShipFromFormation Description : cleans up ship stuff that was in a formation Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipFromFormation(Ship *ship) { ship->formationcommand = NULL; } /*----------------------------------------------------------------------------- Name : FormationCleanup Description : cleans up anything from a formation Inputs : formationtodo Outputs : Return : ----------------------------------------------------------------------------*/ void FormationCleanup(struct CommandToDo *formationtodo) { sdword i; SelectCommand *selection = formationtodo->selection; sdword numShips = selection->numShips; for (i=0;iShipPtr[i]); } if (formationtodo->formation.formationSpecificInfo) { memFree(formationtodo->formation.formationSpecificInfo); formationtodo->formation.formationSpecificInfo = NULL; } } /*----------------------------------------------------------------------------- Name : RemoveShipFromMoving Description : removes ship from moving Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipFromMoving(Ship *ship) { vecZeroVector(ship->moveTo); vecZeroVector(ship->moveFrom); } /*----------------------------------------------------------------------------- Name : MoveCleanup Description : cleans up anything from a move command Inputs : movetodo Outputs : Return : ----------------------------------------------------------------------------*/ void MoveCleanup(struct CommandToDo *movetodo) { sdword i; SelectCommand *selection = movetodo->selection; sdword numShips = selection->numShips; for (i=0;iShipPtr[i]); } } void removeShipFromMpHyperspaceing(Ship *ship) { vecZeroVector(ship->moveTo); vecZeroVector(ship->moveFrom); bitSet(ship->flags,SOF_Selectable); } /*----------------------------------------------------------------------------- Name : MpHyperspaceCleanup Description : cleans up anything from a hyperspaceing move command Inputs : movetodo Outputs : Return : ----------------------------------------------------------------------------*/ void MpHyperspaceCleanup(struct CommandToDo *movetodo) { sdword i; SelectCommand *selection = movetodo->selection; sdword numShips = selection->numShips; for (i=0;iShipPtr[i]); } } /*----------------------------------------------------------------------------- Name : RemoveAttackTargetFromExtraAttackInfo Description : removes shiptoremove from being targeted for attack. Whatever ship was attacking it will choose a new target. Inputs : shiptoremove, extraattackinfo Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveAttackTargetFromExtraAttackInfo(SpaceObjRotImpTarg *targettoremove,CommandToDo *todo) { sdword i; SelectCommand *selection = todo->selection; sdword numShips = selection->numShips; AttackCommand *attack = todo->attack; sdword numShipsToAttack = attack->numTargets; Ship *ship; AttackTargets *multipleAttackTargets; dbgAssert(numShips > 0); for (i=0;iShipPtr[i]; if (ship->attackvars.attacktarget == targettoremove) { ship->attackvars.attacktarget = NULL; ship->attackvars.attackevents |= ATTACKEVENT_ENEMYTARGET_DIED; } if ((multipleAttackTargets = ship->attackvars.multipleAttackTargets) != NULL) { sdword j; sdword numAttackTargets = multipleAttackTargets->numAttackTargets; for (j=0;jTargetPtr[j] == targettoremove) { multipleAttackTargets->TargetPtr[j] = NULL; } } } } } /*----------------------------------------------------------------------------- Name : RemoveShipReferencesFromExtraAttackInfo Description : removes shiptoremove from being reference by extraattackinfo. For example, if a wingman ship was removed, the reference to the wingman would be removed. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void RemoveShipReferencesFromExtraAttackInfo(Ship *shiptoremove,CommandToDo *todo) { SelectCommand *selection = todo->selection; sdword numShips = selection->numShips; SpecificAttackVars *attackvars; sdword i; Ship *ship; dbgAssert(shiptoremove); for (i=0;iShipPtr[i]; attackvars = &ship->attackvars; if (ship == shiptoremove) { continue; // don't process one that is going to be removed } if (attackvars->myLeaderIs == shiptoremove) { // this ship's leader has fallen, so we should not be his wingman anymore. attackvars->myLeaderIs = NULL; dbgAssert(attackvars->myWingmanIs == NULL); attackvars->attackevents |= ATTACKEVENT_LEADER_DIED; } if (attackvars->myWingmanIs == shiptoremove) { // lost wingman dbgAssert(attackvars->myLeaderIs == NULL); attackvars->myWingmanIs = NULL; // we lost our wingman attackvars->attackevents |= ATTACKEVENT_WINGMAN_DIED; } } } /*----------------------------------------------------------------------------- Name : allocateMultipleAttackTargets Description : allocates a AttackTargets structure based on the number of guns on ship Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ AttackTargets *allocateMultipleAttackTargets(Ship *ship) { sdword numGuns = ship->gunInfo->numGuns; AttackTargets *attacktargets; sdword sizeofAttackTargets; dbgAssert(numGuns > 0); sizeofAttackTargets = sizeofAttackTargets(numGuns); attacktargets = memAlloc(sizeofAttackTargets,"AttackTargets",0); memset(attacktargets,0,sizeofAttackTargets); attacktargets->numAttackTargets = numGuns; return attacktargets; } typedef struct { vector targetInShipCoordSys; real32 pickedMe; } PickMultipleTargetsInfo; /*----------------------------------------------------------------------------- Name : pickMultipleAttackTargets Description : picks best attack target for each gun on a ship Inputs : ship, attack Outputs : Return : ----------------------------------------------------------------------------*/ void pickMultipleAttackTargets(Ship *ship,AttackCommand *attack) { sdword numShipsToAttack = attack->numTargets; AttackTargets *attacktargets = ship->attackvars.multipleAttackTargets; sdword numAttackTargets = attacktargets->numAttackTargets; PickMultipleTargetsInfo *multipleTargetsInfo; PickMultipleTargetsInfo *thisTargetInfo; SpaceObjRotImpTarg *target; SpaceObjRotImpTarg *target1 = NULL; SpaceObjRotImpTarg *target2 = NULL; sdword i; sdword j; GunInfo *gunInfo = ship->gunInfo; Gun *gun; GunStatic *gunstatic; sdword bestIndex; real32 bestDist; vector targetInWorldCoordSys; vector targetInShipCoordSys; real32 dist; real32 range; real32 temp; real32 dotprod,bonus,gunRange; dbgAssert(numShipsToAttack > 0); dbgAssert(numAttackTargets > 0); dbgAssert(numAttackTargets == gunInfo->numGuns); multipleTargetsInfo = memAlloc(sizeof(PickMultipleTargetsInfo)*numShipsToAttack,"mTI(multTargInfo)",Pyrophoric); for (i=0,thisTargetInfo=multipleTargetsInfo;iTargetPtr[i]; aishipGetTrajectory(ship,target,&targetInWorldCoordSys); matMultiplyVecByMat(&thisTargetInfo->targetInShipCoordSys,&targetInWorldCoordSys,&ship->rotinfo.coordsys); thisTargetInfo->pickedMe = 1.0f; } if(ship->staticinfo->shipclass == CLASS_Fighter) { bonus = tacticsInfo.BulletRangeBonus[Tactics_Fighter][ship->tacticstype]; } else if(ship->staticinfo->shipclass == CLASS_Corvette) { bonus = tacticsInfo.BulletRangeBonus[Tactics_Corvette][ship->tacticstype]; } else { bonus = 1.0f; } if (ship->staticinfo->shipclass == CLASS_Frigate) { target = ship->attackvars.attacktarget; if (target->objtype == OBJ_ShipType) { if (((Ship *)target)->staticinfo->shipclass == CLASS_Corvette) { target1 = target; for (i=0;iTargetPtr[i] != target1) { target2 = attack->TargetPtr[i]; break; } } } } } for (i=0;iguns[i]; gunstatic = gun->gunstatic; gunRange = gunstatic->bulletrange*bonus; bestDist = REALlyBig; bestIndex = -1; for (j=0,thisTargetInfo=multipleTargetsInfo;jTargetPtr[j]; if (target1) { // concentrate firepower: skip this loop for other ships than target1, target2 if ((target != target1) && (target != target2)) { continue; } } vecSub(targetInShipCoordSys,thisTargetInfo->targetInShipCoordSys,gunstatic->position); dist = fsqrt(vecMagnitudeSquared(targetInShipCoordSys)); range = RangeToTargetGivenDist(ship,target,dist); if (range < gunRange) { vecDivideByScalar(targetInShipCoordSys,dist,temp); dotprod = vecDotProduct(targetInShipCoordSys,gunstatic->gunnormal); dist *= (2.0f - dotprod); dist *= thisTargetInfo->pickedMe; // treat distance as if it were bigger for targets already picked if (dist < bestDist) { bestDist = dist; bestIndex = j; } } } if (bestIndex != -1) { attacktargets->TargetPtr[i] = attack->TargetPtr[bestIndex]; multipleTargetsInfo[bestIndex].pickedMe *= 1.2f; } else { attacktargets->TargetPtr[i] = NULL; } } memFree(multipleTargetsInfo); } #define WINGMANSHIP_USED 1 #define WINGMANSHIP_IGNORE 2 typedef struct { sdword numShips; sdword flags[1]; } FindWingmenInfo; #define sizeofFindWingmenInfo(n) (sizeof(FindWingmenInfo) + (((n)-1)*sizeof(sdword))) /*----------------------------------------------------------------------------- Name : BreakupIntoLeaderWingman Description : breaks up the selection of ships into leader/wingman pairs Inputs : selection Outputs : Return : ----------------------------------------------------------------------------*/ void BreakupIntoLeaderWingman(SelectCommand *selection) { sdword numShips = selection->numShips; udword sizeofFindwingmen; FindWingmenInfo *findwingmen; sdword *flags; vector dist; real32 distsqr; Ship *compareto; Ship *wingman; sdword wingmanj; sdword i,j; Ship *leader; real32 mindist; Ship *ship; sizeofFindwingmen = sizeofFindWingmenInfo(numShips); findwingmen = memAlloc(sizeofFindwingmen,"findwingmen",0); findwingmen->numShips = numShips; for (i=0,flags=&findwingmen->flags[0];iShipPtr[i]; if ((isShipInterceptor(ship) || ship->shiptype == DefenseFighter) && (ship->shiprace != P1) && (ship->shiprace != P2) && ((gamerand() & 255) <= FIGHTER_PAIRUP_PROB) ) { *flags = 0; } else { *flags = WINGMANSHIP_IGNORE; } } for (i=0;iflags[i]) { continue; } leader = selection->ShipPtr[i]; if(leader->shiptype == DefenseFighter) { sdword cont=FALSE; for(j=i+1;jflags[j]) { continue; } if(selection->ShipPtr[j]->shiptype != DefenseFighter) { Ship *temp = selection->ShipPtr[j]; selection->ShipPtr[j] = leader; selection->ShipPtr[i] = temp; cont = TRUE; break; } } if(cont) { i--; } continue; } mindist = REALlyBig; wingmanj = 0; for (j=i+1;jflags[j]) { continue; } compareto = selection->ShipPtr[j]; if(compareto->shiptype == DefenseFighter) { //autoselect defenseFighter wingmanj = j; break; } vecSub(dist,leader->posinfo.position,compareto->posinfo.position); distsqr = vecMagnitudeSquared(dist); if (distsqr >= WINGMAN_JOIN_MAX_DIST) { continue; } if (distsqr < mindist) { mindist = distsqr; wingmanj = j; } } if (wingmanj != 0) { // found a wingman wingman = selection->ShipPtr[wingmanj]; findwingmen->flags[wingmanj] |= WINGMANSHIP_USED; leader->attackvars.myLeaderIs = NULL; leader->attackvars.myWingmanIs = wingman; wingman->attackvars.myLeaderIs = leader; wingman->attackvars.myWingmanIs = NULL; } } memFree(findwingmen); } typedef struct { sdword numFlaggedShips; sdword flags[1]; } AssignTargetsInfo; #define sizeofAssignTargetsInfo(n) (sizeof(AssignTargetsInfo) + (((n)-1)*sizeof(sdword))) /*----------------------------------------------------------------------------- Name : InitExtraAttackInfo Description : intializes the extra attack information in the ships based on attackers (selection) and ships getting attacked (attack) Inputs : selection, attack Outputs : Return : ----------------------------------------------------------------------------*/ void InitExtraAttackInfo(SelectCommand *selection,AttackCommand *attack,bool findwingmen) { sdword numShips = selection->numShips; sdword numTargetsToAttack = attack->numTargets; sdword i,j,k; SpecificAttackVars *attackvars; sdword sizeofAssigntargets; AssignTargetsInfo *assigntargets; sdword closestshipj; vector dist; real32 distsqr; real32 mindist; SpaceObjRotImpTarg *target; Ship *ship; sdword *flags; vector avgposSelection = { 0.0f, 0.0f, 0.0f }; vector avgposTargets = { 0.0f, 0.0f, 0.0f }; vector diff; real32 temp; Ship *leader = selection->ShipPtr[0]; bool focusfire = FALSE; dbgAssert(numShips > 0); dbgAssert(numTargetsToAttack > 0); if (leader->tacticstype == Aggressive) { focusfire = TRUE; } for (i=0;iShipPtr[i]; //dbgAssert(ship->attackvars.multipleAttackTargets == NULL); ship->attackvars.multipleAttackTargets = NULL; ship->attackvars.attacktarget = NULL; ship->attackvars.myLeaderIs = NULL; ship->attackvars.myWingmanIs = NULL; ship->attackvars.flightmansLeft = 0; ship->attackvars.attacksituation = 0; ship->attackvars.attackevents = 0; bitClear(ship->specialFlags,SPECIAL_Kamikaze|SPECIAL_BrokenFormation|SPECIAL_AttackFromAbove); ship->tacticsNeedToFlightMan = FALSE; ship->tacticsTalk = 0; vecAddTo(avgposSelection,ship->posinfo.position); } for (i=0;iTargetPtr[i]; vecAddTo(avgposTargets,target->posinfo.position); } vecDivideByScalar(avgposSelection,((real32)numShips),temp); vecDivideByScalar(avgposTargets,((real32)numTargetsToAttack),temp); vecSub(diff,avgposSelection,avgposTargets); diff.z = abs(diff.z); if ((diff.z >= ATTACKING_FROM_ABOVE_MIN_DIST) && (diff.z >= (fsqrt(diff.x*diff.x + diff.y*diff.y)*ATTACKING_FROM_ABOVE_MIN_RATIO))) { for (i=0;iShipPtr[i]; bitSet(ship->specialFlags,SPECIAL_AttackFromAbove); } } // break fighters up into leader/wingman. if (findwingmen) { BreakupIntoLeaderWingman(selection); } if (numTargetsToAttack == 1) { for (i=0;iShipPtr[i]->attackvars; if (!IAmAWingman(attackvars)) { attackvars->attacktarget = attack->TargetPtr[0]; } } } else { sizeofAssigntargets = sizeofAssignTargetsInfo(numTargetsToAttack); assigntargets = memAlloc(sizeofAssigntargets,"assigntargets",Pyrophoric); memset(assigntargets,0,sizeofAssigntargets); for (i=0;iShipPtr[i]; attackvars = &ship->attackvars; if (IAmAWingman(attackvars)) { continue; } closestshipj = -1; mindist = REALlyBig; for (j=0,flags=&assigntargets->flags[0];jTargetPtr[j]; vecSub(dist,ship->posinfo.position,target->posinfo.position); distsqr = vecMagnitudeSquared(dist); if (distsqr < mindist) { mindist = distsqr; closestshipj = j; } } dbgAssert(closestshipj != -1); dbgAssert(assigntargets->flags[closestshipj] == 0); assigntargets->flags[closestshipj] = 1; assigntargets->numFlaggedShips++; dbgAssert(assigntargets->numFlaggedShips <= numTargetsToAttack); if (assigntargets->numFlaggedShips == numTargetsToAttack) { for (k=0,flags=&assigntargets->flags[0];knumFlaggedShips = 0; } attackvars->attacktarget = attack->TargetPtr[closestshipj]; if (focusfire && (i > 0) && leader->attackvars.attacktarget) { attackvars->attacktarget = leader->attackvars.attacktarget; // override for focus fire } if (((ShipStaticInfo *)(ship->staticinfo))->canTargetMultipleTargets) { dbgAssert(attackvars->multipleAttackTargets == NULL); attackvars->multipleAttackTargets = allocateMultipleAttackTargets(ship); pickMultipleAttackTargets(ship,attack); } } memFree(assigntargets); } } /*----------------------------------------------------------------------------- Name : ChangeOrderToAttack Description : changes order to attack Inputs : alreadycommand, attackcom Outputs : Return : ----------------------------------------------------------------------------*/ void ChangeOrderToAttack(CommandToDo *alreadycommand,AttackCommand *attackcom) { AttackCommand *attack; udword sizeofattack; SelectCommand *selection = alreadycommand->selection; bool dontresetattack = FALSE; if (alreadycommand->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { ClearPassiveAttacking(alreadycommand); } if (alreadycommand->ordertype.order == COMMAND_MP_HYPERSPACEING) { return; // don't want to attack under any circumstances if hyperspacing! } if (alreadycommand->ordertype.order == COMMAND_ATTACK) { if (SelectionsAreTotallyEquivalent((SelectCommand *)alreadycommand->attack,(SelectCommand *)attackcom)) { if(alreadycommand->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { bitClear(alreadycommand->ordertype.attributes,COMMAND_IS_ATTACKINGANDMOVING); } return; } dontresetattack = TRUE; #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to change target of attack"); #endif } else { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nChanging command to attack"); #endif } dbgAssert(AreAllShipsAttackCapable(alreadycommand->selection)); FreeLastOrder(alreadycommand); sizeofattack = sizeofAttackCommand(attackcom->numTargets); attack = memAlloc(sizeofattack,"ToDoAttack",0); memcpy(attack,attackcom,sizeofattack); InitExtraAttackInfo(selection,attack,TRUE); alreadycommand->ordertype.order = COMMAND_ATTACK; alreadycommand->attack = attack; if (alreadycommand->ordertype.attributes & COMMAND_IS_FORMATION) { alreadycommand->formation.doneInitialAttack = FALSE; } PrepareShipsForCommand(alreadycommand,TRUE); if (dontresetattack) { InitShipsForAIDontResetAttack(alreadycommand->selection,TRUE); } else { InitShipsForAI(alreadycommand->selection,TRUE); } } /*----------------------------------------------------------------------------- Name : clAttackThese Description : Command to attack Inputs : selectcom, attackcom Outputs : Return : ----------------------------------------------------------------------------*/ CommandToDo *clAttackThese(CommandLayer *comlayer,SelectCommand *selectcom,AttackCommand *attackcom) { CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; AttackCommand *attack; udword sizeofselection; udword sizeofattack; // for safety if (!AreAllShipsAttackCapable(selectcom)) { MakeShipsAttackCapable(selectcom,selectcom); dbgMessage("\nWARNING: ships not capable of attacking told to attack"); } if (selectcom->numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships As Attackers"); #endif return NULL; } if (attackcom->numTargets <= 0) { dbgMessage("\nWARNING: NO SHIPS TO ATTACK."); return NULL; } if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selectcom)) != NULL) { if (alreadycommand->ordertype.attributes & COMMAND_IS_PROTECTING) { ClearProtecting(alreadycommand); } ChangeOrderToAttack(alreadycommand,attackcom); //link in animation to open guns here: //madLinkInOpenGuns(alreadycommand,1); return alreadycommand; } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to attack"); #endif RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); sizeofselection = sizeofSelectCommand(selectcom->numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); sizeofattack = sizeofAttackCommand(attackcom->numTargets); attack = memAlloc(sizeofattack,"ToDoAttack",0); memcpy(attack,attackcom,sizeofattack); InitExtraAttackInfo(selection,attack,TRUE); newcommand->selection = selection; newcommand->ordertype.order = COMMAND_ATTACK; newcommand->ordertype.attributes = 0; newcommand->attack = attack; InitShipsForAI(selection,TRUE); PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); //link in animation to open guns here: //madLinkInOpenGuns(newcommand,1); return newcommand; } /*----------------------------------------------------------------------------- Name : clAttack Description : Command to attack Inputs : selectcom, attackcom Outputs : Return : ----------------------------------------------------------------------------*/ void clAttack(CommandLayer *comlayer,SelectCommand *selectcom,AttackCommand *attackcom) { SelectCommand *copycom; udword sizeofcopycom; Node *curnode; Node *endnode; Node *nextnode; CommandToDo *command; // for safety if (!AreAllShipsAttackCapable(selectcom)) { MakeShipsAttackCapable(selectcom,selectcom); dbgMessage("\nWARNING: ships not capable of attacking told to attack"); } if (selectcom->numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships As Attackers"); #endif return; } if (attackcom->numTargets <= 0) { dbgMessage("\nWARNING: NO SHIPS TO ATTACK."); return; } salCapExtraSpecialOrderCleanUp(selectcom,COMMAND_ATTACK,NULL,NULL); sizeofcopycom = sizeofSelectCommand(selectcom->numShips); copycom = memAlloc(sizeofcopycom,"cc2(copycom)", Pyrophoric); memcpy(copycom,selectcom,sizeofcopycom); // Now see if the selected ships are already in the command layer, and if so, individually order these groups // to move (to preserve formations) curnode = comlayer->todolist.head; endnode = comlayer->todolist.tail; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (TheseShipsAreInSelection(command->selection,copycom)) { MakeShipsNotIncludeTheseShips(copycom,command->selection); if (command->ordertype.attributes & COMMAND_IS_PROTECTING) { ClearProtecting(command); } ChangeOrderToAttack(command,attackcom); } if (copycom->numShips == 0) { break; } if (curnode == endnode) { break; } curnode = nextnode; } // tell rest of ships left to move too. if (copycom->numShips != 0) { clAttackThese(comlayer,copycom,attackcom); } memFree(copycom); } /*----------------------------------------------------------------------------- Name : canChangeOrderToPassiveAttack Description : returns TRUE if alreadycommand can passive attack Inputs : Outputs : Return : returns TRUE if alreadycommand can passive attack ----------------------------------------------------------------------------*/ bool canChangeOrderToPassiveAttack(CommandToDo *alreadycommand,AttackCommand *attack) { if (alreadycommand->ordertype.attributes & COMMAND_IS_HOLDINGPATTERN) { return FALSE; } if (alreadycommand->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if ((alreadycommand->attack == NULL) || (attack == NULL)) { return FALSE; } if (!SelectionsAreEquivalent((SelectCommand *)alreadycommand->attack,(SelectCommand *)attack)) { return TRUE; } } switch (alreadycommand->ordertype.order) { case COMMAND_NULL: case COMMAND_HALT: case COMMAND_MILITARYPARADE: return TRUE; case COMMAND_MOVE: case COMMAND_DOCK: #ifdef DEBUG_TACTICS if(tacticsOn) #endif { if (!tacticsAreStrikeCraftInSelection(alreadycommand->selection)) return TRUE; //this is a new exceptions else return FALSE; } case COMMAND_ATTACK: case COMMAND_MP_HYPERSPACEING: return FALSE; case COMMAND_LAUNCHSHIP: case COMMAND_COLLECTRESOURCE: case COMMAND_SPECIAL: case COMMAND_BUILDINGSHIP: return FALSE; } return TRUE; } /*----------------------------------------------------------------------------- Name : ChangeOrderToPassiveAttack Description : Activates passive attack for command Inputs : alreadycommand, attackcom Outputs : Return : ----------------------------------------------------------------------------*/ void ChangeOrderToPassiveAttack(CommandToDo *alreadycommand,AttackCommand *attackcom) { AttackCommand *attack; udword sizeofattack; SelectCommand *selection = alreadycommand->selection; sdword count,i; // should check canChangeOrderToPassiveAttack before calling this routine dbgAssert(canChangeOrderToPassiveAttack(alreadycommand,attackcom)); count=0; for(i=0;iselection->numShips;i++) { if(alreadycommand->selection->ShipPtr[i]->passiveAttackCancelTimer > universe.totaltimeelapsed) { //count a bad ship count++; } } if( (((real32) count)/((real32) alreadycommand->selection->numShips)) > 0.5f) { //more than 50% in group bad, so don't passive attack return; } #ifdef gshaw dbgMessage("\nChanging Order to passive attack"); #endif sizeofattack = sizeofAttackCommand(attackcom->numTargets); attack = memAlloc(sizeofattack,"ToDoAttack",0); memcpy(attack,attackcom,sizeofattack); InitExtraAttackInfo(selection,attack,FALSE); alreadycommand->ordertype.attributes |= COMMAND_IS_PASSIVEATTACKING; bitClear(alreadycommand->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); // don't do holding pattern if passive attacking alreadycommand->attack = attack; InitShipsForAI(alreadycommand->selection,FALSE); PrepareShipsForCommand(alreadycommand,FALSE); //setup formation properly here if(alreadycommand->ordertype.order == COMMAND_NULL) { if(alreadycommand->ordertype.attributes & COMMAND_IS_FORMATION) { //NULL command and in formation lockFormation(alreadycommand,0); } } } /*----------------------------------------------------------------------------- Name : clPassiveAttack Description : Command to attack Inputs : selectcom, attackcom Outputs : Return : ----------------------------------------------------------------------------*/ void clPassiveAttack(CommandLayer *comlayer,SelectCommand *selectcom,AttackCommand *attackcom) { CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; AttackCommand *attack; udword sizeofselection; udword sizeofattack; sdword count,i; if (selectcom->numShips == 0) { #ifdef gshaw dbgMessage("\nNo Ships For Passive attack"); #endif return; } if (attackcom->numTargets <= 0) { dbgMessage("\nWARNING: NO SHIPS TO PASSIVE ATTACK."); return; } if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selectcom)) != NULL) { if (canChangeOrderToPassiveAttack(alreadycommand,attackcom)) { ChangeOrderToPassiveAttack(alreadycommand,attackcom); } else { dbgMessage("\nWARNING: Couldn't change order to passive attack"); } return; } #ifdef gshaw dbgMessage("\nReceived Order to passive attack"); #endif count=0; for(i=0;inumShips;i++) { if(selectcom->ShipPtr[i]->passiveAttackCancelTimer > universe.totaltimeelapsed) { //count a bad ship count++; } } if( (((real32) count)/((real32) selectcom->numShips)) > 0.5f) { //more than 50% in group bad, so don't passive attack return; } RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); sizeofselection = sizeofSelectCommand(selectcom->numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); sizeofattack = sizeofAttackCommand(attackcom->numTargets); attack = memAlloc(sizeofattack,"ToDoAttack",0); memcpy(attack,attackcom,sizeofattack); InitExtraAttackInfo(selection,attack,FALSE); newcommand->selection = selection; newcommand->ordertype.order = COMMAND_NULL; newcommand->ordertype.attributes = COMMAND_IS_PASSIVEATTACKING; newcommand->attack = attack; InitShipsForAI(selection,FALSE); PrepareShipsForCommand(newcommand,FALSE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); } /*----------------------------------------------------------------------------- Name : ChangeOrderToSpecial Description : changes order to special Inputs : alreadycommand, targets Outputs : Return : ----------------------------------------------------------------------------*/ void ChangeOrderToSpecial(CommandToDo *alreadycommand,SpecialCommand *targets) { SelectCommand *selection = alreadycommand->selection; SpecialCommand *specialtargets; udword sizeofspecialtargets; if (alreadycommand->ordertype.attributes & COMMAND_IS_PROTECTING) { ClearProtecting(alreadycommand); } if (alreadycommand->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { ClearPassiveAttacking(alreadycommand); } FreeLastOrder(alreadycommand); if (targets != NULL) { sizeofspecialtargets = sizeofSelectCommand(targets->numTargets); specialtargets = memAlloc(sizeofspecialtargets,"ToDoSpecial",0); memcpy(specialtargets,targets,sizeofspecialtargets); alreadycommand->specialtargets = specialtargets; } else { alreadycommand->specialtargets = NULL; } alreadycommand->ordertype.order = COMMAND_SPECIAL; PrepareShipsForCommand(alreadycommand,TRUE); } bool ShipsAreSalCapCorvettes(Ship *ship) { if (ship->shiptype == SalCapCorvette) { return TRUE; } return FALSE; } void clPlaySpeechEventsForSupportCraft(SelectCommand *selection) { sdword i; if(selection->ShipPtr[0]->playerowner->playerIndex == universe.curPlayerIndex) { //local machines ships... for(i=0;inumShips;i++) { switch(selection->ShipPtr[i]->shiptype) { case Carrier: case ResourceController: case ResourceCollector: case AdvanceSupportFrigate: case RepairCorvette: //I don't think we need battle chatter limiting... speechEvent(selection->ShipPtr[0],COMM_Support,0); return; //return after first one...all we need! } } } } /*----------------------------------------------------------------------------- Name : clSpecial Description : Command to do special Inputs : selectcom, targets Outputs : Return : ----------------------------------------------------------------------------*/ void clSpecial(CommandLayer *comlayer,SelectCommand *selectcom,SpecialCommand *targets) { CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; SpecialCommand *specialtargets; udword sizeofselection; udword sizeofspecialtargets; if (selectcom->numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships for special"); #endif return; } if(selectcom->numShips == 1) { if(selectcom->ShipPtr[0]->shiptype == SalCapCorvette) { CommandToDo *oldcommand=getShipAndItsCommand(&universe.mainCommandLayer,selectcom->ShipPtr[0]); if(oldcommand != NULL) { if(oldcommand->ordertype.order == COMMAND_SPECIAL) { if((((SalCapCorvetteSpec *)selectcom->ShipPtr[0]->ShipSpecifics)->target) != NULL) { if(ShipInSelection((SelectCommand *)targets,(Ship *)(((SalCapCorvetteSpec *)selectcom->ShipPtr[0]->ShipSpecifics)->target))) { //ship is targetting a ship in the list already...lets ignore the command //if we get bugs..we could only do this if the target list is the same exactly return; } } } } } } clPlaySpeechEventsForSupportCraft(selectcom); salCapExtraSpecialOrderCleanUp(selectcom,COMMAND_SPECIAL,NULL,(SelectCommand *)targets); sizeofselection = sizeofSelectCommand(selectcom->numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); if (targets == NULL) { // this is just a special activate command, so activate it for each ship and return without modifying // the command layer as long as the specialActivateIsContinuous flag isn't set. sdword numShips = selectcom->numShips; sdword i; Ship *ship; ShipStaticInfo *shipstaticinfo; for (i=0;iShipPtr[i]; shipstaticinfo = (ShipStaticInfo *)(ship->staticinfo); if (!shipstaticinfo->specialActivateIsContinuous) { clRemoveShipFromSelection(selection,ship); if (shipstaticinfo->custshipheader.CustShipSpecialActivate) { shipstaticinfo->custshipheader.CustShipSpecialActivate(ship); } } } if (selection->numShips == 0) { // all ships have been activated, so return memFree(selection); return; } } if (!DoAnyShipsFollowConstraints(selection,ShipsAreSalCapCorvettes)) // Bryce later make salcap corvettes be able to salvage in formation fix later { if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selection)) != NULL) { memFree(selection); ChangeOrderToSpecial(alreadycommand,targets); return; } } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to do special"); #endif RemoveShipsFromDoingStuff(comlayer,selection); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); if (targets != NULL) { sizeofspecialtargets = sizeofSelectCommand(targets->numTargets); specialtargets = memAlloc(sizeofspecialtargets,"ToDoSpecial",0); memcpy(specialtargets,targets,sizeofspecialtargets); newcommand->specialtargets = specialtargets; } else { newcommand->specialtargets = NULL; } newcommand->selection = selection; newcommand->ordertype.order = COMMAND_SPECIAL; newcommand->ordertype.attributes = 0; PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); } void tacticsChangedForShipCB(Ship *ship,TacticsType oldtactics,TacticsType newtactics) { #if 0 // take this out due to micromanagement abuse if(newtactics == Evasive) { tacticsSetShipToDoDodge(ship); } #endif } /*----------------------------------------------------------------------------- Name : clSetTactics Description : Sets tactics for a group of ships Inputs : selectcom, tacticstype Outputs : Return : ----------------------------------------------------------------------------*/ void clSetTactics(CommandLayer *comlayer,SelectCommand *selectcom,TacticsType tacticstype) { sdword numShips = selectcom->numShips; sdword i; Ship *ship; TacticsType oldtactics; #ifdef DEBUG_TACTICS if(!tacticsOn) return; //don't change tactics settings if not enabled #endif dbgAssert(tacticstype >= 0); dbgAssert(tacticstype < NUM_TACTICS_TYPES); for (i=0;iShipPtr[i]; //adjust ships maximum bullet range (don't forget to adjust //individual guns ranges on the fly /* if(ship->staticinfo->shipclass == CLASS_Fighter) { ship->bulletrage*=tacticsInfo.BulletRangeBonus[CLASS_Fighter][ship->tacticstype]; } else if (ship->staticinfo->shipclass == CLASS_Corvette) { ship->bulletrage*=tacticsInfo.BulletRangeBonus[Tactics_Corvette][ship->tacticstype]; } */ oldtactics = ship->tacticstype; //if ship wasn't in evassive, make sure its attack target is set to //NULL if switching to evassive so that the wingman leader AI will work properly... if(tacticstype == Evasive) if(oldtactics != Evasive) if (ship->attackvars.myLeaderIs) { ship->attackvars.attacktarget = NULL; //set target to } ship->tacticstype = tacticstype; tacticsChangedForShipCB(ship,oldtactics,tacticstype); } } /*----------------------------------------------------------------------------- Name : clRUTransfer Description : Transfers RU's from selectcom.numShips to selectcom.timeLastStatus Inputs : selectcom,resourceUntis Outputs : Return : ----------------------------------------------------------------------------*/ void clRUTransfer(CommandLayer *comlayer, sdword toIndex, sdword fromIndex, sdword resourceUnits,ubyte flags) { //Place sound event here bitch (thats right...I'm talking to you Shane) sdword from,to; from = fromIndex; to = toIndex; if (resourceUnits <= 0) // prevents cheating client side even if they hack it return; switch(flags) { case RUTRANS_GENERATEDRUS: case RUTRANS_BUILTSHIP: case RUTRANS_BUILDCANCEL: // obsolete now break; default: if (universe.players[to].playerState == PLAYER_DEAD) return; if (universe.players[from].playerState == PLAYER_DEAD) return; universe.players[from].resourceUnits -= resourceUnits; universe.players[to].resourceUnits += resourceUnits; universe.gameStats.playerStats[from].totalResourceUnits -= resourceUnits; universe.gameStats.playerStats[from].totalResourceUnitsGiven += resourceUnits; universe.gameStats.playerStats[to].totalResourceUnits += resourceUnits; universe.gameStats.playerStats[to].totalResourceUnitsRecieved += resourceUnits; if (from == universe.curPlayerIndex) { //if we've given some away speechEventFleet(COMM_F_ResourcesTransferred, 0, from); } else if (to == universe.curPlayerIndex) { //if we just got some soundEvent(NULL, UI_ReceivingRUs); speechEventFleet(COMM_F_ResourcesReceived, 0, to); } break; } } /*----------------------------------------------------------------------------- Name : clSetKamikaze Description : Sets the kamikaze bit for all ships that are kamikaze capable provided they are doing an attack...ideally, we do that check before we transmitt Inputs : selectcom, Outputs : Return : ----------------------------------------------------------------------------*/ void clSetKamikaze(CommandLayer *comlayer,SelectCommand *selectcom) { sdword numShips = selectcom->numShips; sdword i; Ship *ship; CommandToDo *command; for (i=0;iShipPtr[i]; command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if(command == NULL) { } else if(command->ordertype.order != COMMAND_ATTACK) { if(command->ordertype.order == COMMAND_SPECIAL) { if(ship->shiptype == SalCapCorvette) { bitSet(ship->specialFlags,SPECIAL_Kamikaze|SPECIAL_BrokenFormation); continue; } } if(ship->playerowner->playerIndex == universe.curPlayerIndex) { //shouldn't set the kamikazee stuff... ///////////////////// //no target kamikazee speech event //num: COMM_Kamikaze_NoTargets //battle chatter ////////////// } } else { bitSet(ship->specialFlags,SPECIAL_Kamikaze|SPECIAL_BrokenFormation); } } } /*----------------------------------------------------------------------------- Name : ChangeOrderToHalt Description : changes order to Halt Inputs : alreadycommand Outputs : Return : ----------------------------------------------------------------------------*/ void ChangeOrderToHalt(CommandToDo *alreadycommand) { if (alreadycommand->ordertype.attributes & COMMAND_IS_PROTECTING) { ClearProtecting(alreadycommand); } if (alreadycommand->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { ClearPassiveAttacking(alreadycommand); } if (alreadycommand->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { bitClear(alreadycommand->ordertype.attributes,COMMAND_IS_ATTACKINGANDMOVING); } FreeLastOrder(alreadycommand); alreadycommand->ordertype.order = COMMAND_HALT; PrepareShipsForCommand(alreadycommand,TRUE); } /*----------------------------------------------------------------------------- Name : clHaltThese Description : command for ships to halt Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clHaltThese(CommandLayer *comlayer,SelectCommand *selectcom) { CommandToDo *command; CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; udword sizeofselection; sdword numShips = selectcom->numShips; sdword i; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships to halt!"); #endif return; } for (i=0;inumShips;) { command = getShipAndItsCommand(&universe.mainCommandLayer,selectcom->ShipPtr[i]); if(command != NULL) { if(command->ordertype.order == COMMAND_DOCK || command->ordertype.order == COMMAND_LAUNCHSHIP) { //if docking ship is NULL, we can assume some stuff... if (selectcom->ShipPtr[i]->dockingship != NULL) { if(selectcom->ShipPtr[i]->shiptype != ResearchShip) { //signal docking ship to cancel selectcom->ShipPtr[i]->forceCancelDock=TRUE; selectcom->numShips--; selectcom->ShipPtr[i] = selectcom->ShipPtr[selectcom->numShips]; continue; // we must check same index again because we put last array entry here } } } } i++; } salCapExtraSpecialOrderCleanUp(selectcom,COMMAND_HALT,NULL,NULL); if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selectcom)) != NULL) { if (alreadycommand->ordertype.order == COMMAND_MOVE) { speechEvent(selectcom->ShipPtr[0], COMM_MoveCancelled, 0); } else if (alreadycommand->ordertype.order == COMMAND_ATTACK) { speechEvent(selectcom->ShipPtr[0], COMM_AttackCancelled, 0); } #ifdef HW_DEBUG dbgAssert(alreadycommand->ordertype.order != COMMAND_MP_HYPERSPACEING); #endif ChangeOrderToHalt(alreadycommand); return; } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to halt"); #endif RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); sizeofselection = sizeofSelectCommand(numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); newcommand->selection = selection; newcommand->ordertype.order = COMMAND_HALT; newcommand->ordertype.attributes = 0; PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); } /*----------------------------------------------------------------------------- Name : clHalt Description : command for ships to halt Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clHalt(CommandLayer *comlayer,SelectCommand *selectcom) { SelectCommand *copycom; udword sizeofcopycom; Node *curnode; Node *endnode; Node *nextnode; CommandToDo *command; sdword numShips = selectcom->numShips; sdword i; Ship *ship; bool stoppassiveattacking; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships to halt!"); #endif return; } for(i=0;inumShips;i++) { ship = selectcom->ShipPtr[i]; stoppassiveattacking = FALSE; if (ship->command == NULL) stoppassiveattacking = TRUE; else { switch (ship->command->ordertype.order) { case COMMAND_NULL: case COMMAND_HALT: case COMMAND_MILITARYPARADE: stoppassiveattacking = TRUE; } } if (stoppassiveattacking) ship->passiveAttackCancelTimer=universe.totaltimeelapsed+TW_GLOBAL_PASSIVE_ATTACK_CANCEL_MODIFIER; else ship->passiveAttackCancelTimer=0.0f; } sizeofcopycom = sizeofSelectCommand(selectcom->numShips); copycom = memAlloc(sizeofcopycom,"cc2(copycom)", Pyrophoric); memcpy(copycom,selectcom,sizeofcopycom); // Now see if the selected ships are already in the command layer, and if so, individually order these groups // to move (to preserve formations) curnode = comlayer->todolist.head; endnode = comlayer->todolist.tail; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (TheseShipsAreInSelection(command->selection,copycom)) { MakeShipsNotIncludeTheseShips(copycom,command->selection); ChangeOrderToHalt(command); } if (copycom->numShips == 0) { break; } if (curnode == endnode) { break; } curnode = nextnode; } // tell rest of ships left to move too. if (copycom->numShips != 0) { clHaltThese(comlayer,copycom); } memFree(copycom); } /*----------------------------------------------------------------------------- Name : clScuttle Description : command for ship to scuttle itself Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clScuttle(CommandLayer *comlayer,SelectCommand *selectcom) { sdword numShips = selectcom->numShips; sdword i; if((tutorial==TUTORIAL_ONLY) && !tutEnable.bScuttle) return; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships to scuttle!"); #endif return; } for (i=0;iShipPtr[i]->deathtime = universe.totaltimeelapsed + frandombetween(SCUTTLE_SHIPDEATHTIMEMIN,SCUTTLE_SHIPDEATHTIMEMAX); } return; } /*----------------------------------------------------------------------------- Name : clLaunchShip Description : command for a ship to launch from a ship Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clLaunchShip(CommandLayer *comlayer,SelectCommand *selectcom,ShipPtr receiverShip) { CommandToDo *newcommand; SelectCommand *selection; Ship *ship = selectcom->ShipPtr[0]; ShipStaticInfo *receiverShipStaticInfo = (ShipStaticInfo *)receiverShip->staticinfo; dbgAssert(selectcom->numShips == 1); dbgAssert(receiverShip != NULL); dbgAssert(receiverShipStaticInfo->canReceiveShips|receiverShipStaticInfo->canReceiveShipsPermanently); if (ship->specialFlags & SPECIAL_Launching) { return; // already launching } if (ship->shiplink.belongto) { return; // already outside } RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); selection = memAlloc(sizeof(SelectCommand),"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeof(SelectCommand)); newcommand->selection = selection; newcommand->ordertype.order = COMMAND_LAUNCHSHIP; newcommand->ordertype.attributes = 0; newcommand->launchship.receiverShip = receiverShip; dockPrepareSingleShipForLaunch(ship,receiverShip); dockInitShipForLaunch(ship); InitShipsForAI(selection,TRUE); PrepareShipsForCommand(newcommand,TRUE); //listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); //insert at begining to take priority over other commands that will be occurring //i.e. retiring listAddNodeBeginning(&comlayer->todolist,&newcommand->todonode,newcommand); } /*----------------------------------------------------------------------------- Name : clLaunchMultipleShips Description : command for multiple ships to launch from a ship Inputs : selectcom, receiverShip Outputs : Return : ----------------------------------------------------------------------------*/ void clLaunchMultipleShips(CommandLayer *comlayer,SelectCommand *selectcom,ShipPtr launchFrom) { sdword numShips = selectcom->numShips; sdword i; SelectCommand selectone; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ships to launch"); #endif return; } if (launchFrom == NULL) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo Ship to launch from"); #endif return; } selectone.numShips = 1; for (i=0;iShipPtr[i]->attributes & ATTRIBUTES_Defector)) // don't let the defector launch { selectone.ShipPtr[0] = selectcom->ShipPtr[i]; clLaunchShip(comlayer,&selectone,launchFrom); } } } /*----------------------------------------------------------------------------- Name : LaunchAllInternalShipsOfPlayerThatMustBeLaunched Description : launches all internal ships of given player that must be launched Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void LaunchAllInternalShipsOfPlayerThatMustBeLaunched(struct Player *player) { Node *objnode = universe.ShipList.head; Ship *ship; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { if ((ship->shipsInsideMe) && (ship->shiptype != DDDFrigate)) { Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship; SelectCommand selectone; while (insidenode != NULL) { insideship = (InsideShip *)listGetStructOfNode(insidenode); dbgAssert(insideship->ship->objtype == OBJ_ShipType); // make defector launch if ((insideship->ship->attributes & ATTRIBUTES_Defector) || (ShipHasToLaunch(ship,insideship->ship))) { selectone.numShips = 1; selectone.ShipPtr[0] = insideship->ship; clLaunchShip(&universe.mainCommandLayer,&selectone,ship); } insidenode = insidenode->next; } } } objnode = objnode->next; } } void FillInCarrierMothershipInfo(struct Player *player,Ship **mothership,Ship *carrierX[]) { sdword i,j; *mothership = NULL; for (i=0;i<4;i++) { carrierX[i] = NULL; } *mothership = player->PlayerMothership; if ((*mothership) && ((*mothership)->shiptype == Carrier)) { carrierX[0] = *mothership; *mothership = NULL; } for (i = 0; i < cmNumCarriers; i++) { if (cmCarriers[i].owner == player) { for (j=0;j<4;j++) // find first free slot and put it in { if (carrierX[j] == NULL) { carrierX[j] = cmCarriers[i].ship; break; } } } } } /*----------------------------------------------------------------------------- Name : LaunchAllInternalShipsOfPlayer Description : launches all internal ships of given player Inputs : carriermask: 0=momship, 1=carrier1, 2=carrier2 Outputs : Return : the number of ships launched ----------------------------------------------------------------------------*/ sdword LaunchAllInternalShipsOfPlayer(struct Player *player, udword carriermask) { Node *objnode = universe.ShipList.head; Ship *ship; Ship *mothership; Ship *carriers[4]; sdword numShips = 0; FillInCarrierMothershipInfo(player,&mothership,carriers); while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); dbgAssert(ship->objtype == OBJ_ShipType); if (ship->playerowner == player) { if ((ship->shipsInsideMe) && (ship->shiptype != DDDFrigate)) { if ( ((carriermask & BIT0) && (ship == mothership)) || ((carriermask & BIT1) && (ship == carriers[0])) || ((carriermask & BIT2) && (ship == carriers[1])) || ((carriermask & BIT3) && (ship == carriers[2])) || ((carriermask & BIT4) && (ship == carriers[3])) ) { Node *insidenode = ship->shipsInsideMe->insideList.head; InsideShip *insideship; SelectCommand selectone; while (insidenode != NULL) { insideship = (InsideShip *)listGetStructOfNode(insidenode); dbgAssert(insideship->ship->objtype == OBJ_ShipType); if (!(insideship->ship->attributes & ATTRIBUTES_Defector)) // don't let the defector launch { selectone.numShips = 1; selectone.ShipPtr[0] = insideship->ship; clLaunchShip(&universe.mainCommandLayer,&selectone,ship); numShips++; } insidenode = insidenode->next; } } } } objnode = objnode->next; } return (numShips); } /*----------------------------------------------------------------------------- Name : clAutoLaunch Description : command to turn the autoLaunch flag on or off for a given player Inputs : OnOff - bit0=Mothership, bit1=Carrier 1, bit2=Carrier2, bit3=Carrier3, bit4=Carrier4 Outputs : Return : ----------------------------------------------------------------------------*/ void clAutoLaunch(udword OnOff,udword playerIndex) { Player *player = &universe.players[playerIndex]; bool speechevent = FALSE; sdword i; udword bit = BIT0; for (i=0;i<(4+1);i++,bit<<=1) // 4 Carriers + 1 Mothership BIT0..BIT4 are valid { if ((player->autoLaunch & bit) != (OnOff & bit)) { if (OnOff & bit) { // changing autoLaunch from Off to On player->autoLaunch |= bit; // tell all internal ships to launch: if (LaunchAllInternalShipsOfPlayer(player, bit) > 0) { speechevent = TRUE; } } else { // changing autoLaunch from On to Off player->autoLaunch &= ~bit; } } } if (speechevent && (singlePlayerGameInfo.currentMission != 16)) { //fleet command is out of commission in Mission 16, so don't play this speechEventFleet(COMM_F_AutolaunchOn, 0, playerIndex); } } /*----------------------------------------------------------------------------- Name : clSetAlliance Description : sets an alliance based on a player bitmask Inputs : player bitmask Outputs : Return : void ----------------------------------------------------------------------------*/ void clSetAlliance(udword AllianceType, uword curalliance, uword newalliance) { allianceSetAlliance(AllianceType, curalliance, newalliance); } void clSetResearch(udword type, udword playernum, udword labnum, udword tech) { if (type == RESEARCH_SUBCOMMAND_START) { rmAssignPlayersLabToResearch(&universe.players[playernum], labnum, tech); } else if (type == RESEARCH_SUBCOMMAND_STOP) { rmClearResearchlab(&universe.players[playernum], labnum); } } bool ShipHasToLaunch(Ship *InsideShip, Ship *ship) { if (singlePlayerGame) { if (singlePlayerGameInfo.hyperspaceState != NO_HYPERSPACE) { return FALSE; } } if (ship->specialFlags & SPECIAL_STAY_TILL_EXPLICITLAUNCH) { return FALSE; } if ( (ship->staticinfo->shipclass==CLASS_Fighter) && (InsideShip->shipsInsideMe->FightersInsideme <= InsideShip->staticinfo->maxDockableFighters) ) return (FALSE); if ( (ship->staticinfo->shipclass==CLASS_Corvette) && (InsideShip->shipsInsideMe->CorvettesInsideme <= InsideShip->staticinfo->maxDockableCorvettes) ) return (FALSE); return (TRUE); } /*----------------------------------------------------------------------------- Name : clCreateShip Description : creates a new ship Inputs : shipType, shipRace, playerIndex, creator Outputs : Return : ----------------------------------------------------------------------------*/ Ship *clCreateShip(CommandLayer *comlayer,ShipType shipType,ShipRace shipRace,uword playerIndex,ShipPtr creator) { Ship *ship; SelectCommand select; Player *player = &universe.players[playerIndex]; Ship *mothership, *carrier1=NULL, *carrier2=NULL; sword i; udword launchmask; mothership = player->PlayerMothership; if ((mothership) && (mothership->shiptype == Carrier)) { carrier1 = mothership; mothership = NULL; } for (i = 0; i < cmNumCarriers; i++) { if (cmCarriers[i].owner == player) { if (carrier1) { carrier2 = cmCarriers[i].ship; break; } else { carrier1 = cmCarriers[i].ship; } } } if (creator == mothership) { launchmask = BIT0; } else if (creator == carrier1) { launchmask = BIT1; } else if (creator == carrier2) { launchmask = BIT2; } if (creator == NULL) { return NULL; // if creator died, too bad for ships inside } if (universe.players[playerIndex].playerState != PLAYER_ALIVE) { return NULL; // if player is not alive, don't allow ship creation } dbgAssert(playerIndex < universe.numPlayers); // create ship ship = univCreateShip(shipType,shipRace,&creator->posinfo.position,&universe.players[playerIndex],1); gameStatsAddShip(ship,playerIndex); // update unit cap variables unitCapCreateShip(ship,&universe.players[playerIndex]); // update the special internet ghost totals, because of lag. if ((multiPlayerGame) && (playerIndex==sigsPlayerIndex)) { shiplagtotals[shipType]--; } // if ship is a research ship and player is curplayer or aiplayer // ****** IMPORTANT we have changed the research so that it is deterministic on all machines. if (ship->staticinfo->shiptype==ResearchShip) { /* if ( (&universe.players[playerIndex]==universe.curPlayerPtr) || (universe.players[playerIndex].aiPlayer!=NULL) ) {*/ rmActivateFreeLab(&universe.players[playerIndex]); // } } // put ship inside creator dockPutShipInside(ship,creator); if ( (universe.players[playerIndex].autoLaunch & launchmask) || (ShipHasToLaunch(creator, ship)) ) { select.numShips = 1; select.ShipPtr[0] = ship; clLaunchShip(comlayer,&select,(ShipPtr)creator); } return(ship); } /*----------------------------------------------------------------------------- Name : processBuildingShipToDo Description : processes ship building command Inputs : Outputs : Return : TRUE if command is done ----------------------------------------------------------------------------*/ bool processBuildingShipToDo(CommandToDo *command) { Player *player; Ship *creator; if (universe.univUpdateCounter > command->buildingship.frameAtWhichToCreate) { creator = command->buildingship.creator; dbgAssert(creator != NULL); player = creator->playerowner; clCreateShip(&universe.mainCommandLayer,command->buildingship.shipType,command->buildingship.shipRace, player->playerIndex,creator); return TRUE; } return FALSE; } udword NumberOfOtherShipsOfSameTypeBeingBuilt(ShipType shipType,ShipPtr creator,udword playerIndex) { Node *curnode = universe.mainCommandLayer.todolist.head; CommandToDo *command; udword numOtherBeingBuilt = 0; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); if ((command->ordertype.order == COMMAND_BUILDINGSHIP) && (command->buildingship.playerIndex == playerIndex)) { if (command->buildingship.shipType == shipType) { // if they don't have a creator or both have the same creator if ( ((creator == NULL) || (command->buildingship.creator == NULL)) || (creator == command->buildingship.creator)) { numOtherBeingBuilt++; } } } curnode = curnode->next; } return numOtherBeingBuilt; } void clDeterministicBuild(udword command, CommandLayer* comLayer, sdword numShips, ShipType shipType, ShipRace shipRace, uword playerIndex, ShipPtr creator) { Player* player = &universe.players[playerIndex]; dbgAssert(playerIndex < universe.numPlayers); if (creator == NULL) { //creator died, ships die too return; } if (player->playerState != PLAYER_ALIVE) { //!alive players can't build ships return; } cmDeterministicBuild(command, numShips, shipType, shipRace, playerIndex, creator); } /*----------------------------------------------------------------------------- Name : clBuildShip Description : builds a new ship (including waiting time for it to be built) Inputs : shipType, shipRace, playerIndex, creator Outputs : Return : ----------------------------------------------------------------------------*/ void clBuildShip(CommandLayer *comlayer,ShipType shipType,ShipRace shipRace,uword playerIndex,ShipPtr creator) { Player *player = &universe.players[playerIndex]; ShipStaticInfo *shipstaticinfo = GetShipStaticInfo(shipType,shipRace); CommandToDo *newcommand; dbgAssert(playerIndex < universe.numPlayers); if (creator == NULL) { return; // if creator died, too bad for ships inside } if (player->playerState != PLAYER_ALIVE) { return; // if player is not alive, don't allow ship creation } //if (shipstaticinfo->buildCost <= player->resourceUnits) { player->resourceUnits -= shipstaticinfo->buildCost; // subtract cost right away universe.gameStats.playerStats[player->playerIndex].totalResourceUnitsSpent += shipstaticinfo->buildCost; // make new command indicating delayed build: newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); newcommand->selection = memAlloc(sizeofSelectCommand(1),"NoSelection",0); newcommand->selection->numShips = 0; newcommand->ordertype.order = COMMAND_BUILDINGSHIP; newcommand->ordertype.attributes = 0; newcommand->buildingship.shipType = shipType; newcommand->buildingship.shipRace = shipRace; newcommand->buildingship.creator = creator; newcommand->buildingship.playerIndex = (udword)playerIndex; newcommand->buildingship.frameAtWhichToCreate = universe.univUpdateCounter + ((NumberOfOtherShipsOfSameTypeBeingBuilt(shipType,creator,playerIndex)+1) * shipstaticinfo->buildTime); PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); } } /*----------------------------------------------------------------------------- Name : FindShipsWereGuarding Description : finds ships we're guarding, either directly or indirectly Inputs : Outputs : Return : returns TRUE if terminating condition was finding that shipsWereGuarding are directly or indirectly guarding us - returns FALSE if terminating condition is we can't find any more ships we're guarding ----------------------------------------------------------------------------*/ bool FindShipsWereGuarding(CommandToDo *originalus,SelectCommand *us,GrowSelection *shipsWereGuarding,sdword iteration) { sdword i,j; sdword usnumShips = us->numShips; Ship *ship; CommandToDo *shipcommand; GrowSelection newshipsWereGuarding; bool result; if (iteration > 10) { return FALSE; // safety for recursive function } for (i=0;iShipPtr[i]; shipcommand = ship->command; if (shipcommand && (shipcommand->ordertype.attributes & COMMAND_IS_PROTECTING)) { for (j=0;jprotect->numShips;j++) { growSelectAddShipNoDuplication(shipsWereGuarding,shipcommand->protect->ShipPtr[j]); } } } if (shipsWereGuarding->selection->numShips == 0) { return FALSE; } if (AnyOfTheseShipsAreInSelection(shipsWereGuarding->selection,originalus->selection)) { return TRUE; } growSelectInit(&newshipsWereGuarding); result = FindShipsWereGuarding(originalus,shipsWereGuarding->selection,&newshipsWereGuarding,iteration+1); growSelectClose(&newshipsWereGuarding); return result; } /*----------------------------------------------------------------------------- Name : CheckCircularGuard Description : Checks for circular guard, and if it is sets the bit SPECIAL_2_CircularGuard for each ship otherwise clears it Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void CheckCircularGuard(CommandToDo *command) { sdword i; GrowSelection shipsWereGuarding; growSelectInit(&shipsWereGuarding); if (FindShipsWereGuarding(command,command->selection,&shipsWereGuarding,0)) { // ships we're guarding are directly or indirectly guarding us: SelectCommand *selection = command->selection; for (i=0;inumShips;i++) { bitSet(selection->ShipPtr[i]->specialFlags2,SPECIAL_2_CircularGuard); } } else { // ships we're guarding are NOT directly or indirectly guarding us: SelectCommand *selection = command->selection; for (i=0;inumShips;i++) { bitClear(selection->ShipPtr[i]->specialFlags2,SPECIAL_2_CircularGuard); } } growSelectClose(&shipsWereGuarding); } void ChangeOrderToProtect(CommandToDo *alreadycommand,ProtectCommand *protectcom) { ProtectCommand *protect; udword sizeofprotect; alreadycommand->protectFlags = 0; if (alreadycommand->ordertype.attributes & COMMAND_IS_PROTECTING) { if (SelectionsAreTotallyEquivalent(alreadycommand->protect,protectcom)) { return; } ClearProtecting(alreadycommand); } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nChanged Order to protect"); #endif FreeLastOrder(alreadycommand); alreadycommand->ordertype.order = COMMAND_NULL; alreadycommand->ordertype.attributes |= COMMAND_IS_PROTECTING; sizeofprotect = sizeofProtectCommand(protectcom->numShips); protect = memAlloc(sizeofprotect,"ToDoProtect",0); memcpy(protect,protectcom,sizeofprotect); alreadycommand->protect = protect; PrepareShipsForCommand(alreadycommand,TRUE); CheckCircularGuard(alreadycommand); if (alreadycommand->ordertype.attributes & COMMAND_IS_FORMATION) { if (alreadycommand->formation.formationtype == SPHERE_FORMATION) { // we are now protecting in sphere formation, and we want to surround // a ship that we protect when go in sphere formation. This is different // than the regular sphere formation, so call to tell formationContentHasChanged formationContentHasChanged(alreadycommand); } } } /*----------------------------------------------------------------------------- Name : clProtect Description : command to protect a ship Inputs : selectcom, protectThisShip Outputs : Return : ----------------------------------------------------------------------------*/ CommandToDo *clProtect(CommandLayer *comlayer,SelectCommand *selectcom,ProtectCommand *protectcom) { CommandToDo *newcommand; CommandToDo *alreadycommand; SelectCommand *selection; ProtectCommand *protect; udword sizeofselection; udword sizeofprotect; sdword numShips = selectcom->numShips; sdword numShipsToProtect = protectcom->numShips; if (numShips == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo ships selected as protectors"); #endif return NULL; } //cleanup like halt command salCapExtraSpecialOrderCleanUp(selectcom,COMMAND_HALT,NULL,NULL); if (numShipsToProtect == 0) { #ifdef DEBUG_COMMANDLAYER dbgMessage("\nNo ships to protect"); #endif return NULL; } if ((alreadycommand = IsSelectionAlreadyDoingSomething(comlayer,selectcom)) != NULL) { ChangeOrderToProtect(alreadycommand,protectcom); return alreadycommand; } #ifdef DEBUG_COMMANDLAYER dbgMessage("\nReceived Order to protect"); #endif RemoveShipsFromDoingStuff(comlayer,selectcom); newcommand = memAlloc(sizeof(CommandToDo),"ToDo",NonVolatile); InitCommandToDo(newcommand); sizeofselection = sizeofSelectCommand(numShips); selection = memAlloc(sizeofselection,"ToDoSelection",NonVolatile); memcpy(selection,selectcom,sizeofselection); sizeofprotect = sizeofProtectCommand(numShipsToProtect); protect = memAlloc(sizeofprotect,"ToDoProtect",0); memcpy(protect,protectcom,sizeofprotect); newcommand->selection = selection; newcommand->ordertype.order = COMMAND_NULL; newcommand->ordertype.attributes = COMMAND_IS_PROTECTING; newcommand->protect = protect; newcommand->protectFlags = 0; PrepareShipsForCommand(newcommand,TRUE); listAddNode(&comlayer->todolist,&newcommand->todonode,newcommand); CheckCircularGuard(newcommand); return newcommand; } /*----------------------------------------------------------------------------- Name : AddShipToGroup Description : Adds ship to CommandToDo group Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void AddShipToGroup(ShipPtr ship,CommandToDo *group) { SelectCommand *newselection; sdword sizeofnewselection; sdword sizeofselection; sdword numShips; numShips = group->selection->numShips; dbgAssert(numShips >= 1); sizeofnewselection = sizeofSelectCommand(numShips+1); sizeofselection = sizeofSelectCommand(numShips); newselection = memAlloc(sizeofnewselection,"ToDoSelection",NonVolatile); memcpy(newselection,group->selection,sizeofselection); memFree(group->selection); group->selection = newselection; newselection->ShipPtr[numShips] = ship; newselection->numShips = numShips + 1; PrepareOneShipForCommand(ship,group,TRUE); } /*----------------------------------------------------------------------------- Name : AddShipToFormationGroup Description : adds a ship to group Inputs : ship, group Outputs : Return : ----------------------------------------------------------------------------*/ void AddShipToFormationGroup(ShipPtr ship,CommandToDo *group) { #ifdef DEBUG_LAUNCHSHIP dbgMessage("\nAdding ship to group"); #endif AddShipToGroup(ship,group); FillInShipFormationStuff(ship,group); dbgAssert(group->ordertype.attributes & COMMAND_IS_FORMATION); formationContentHasChanged(group); InitShipAI(ship,TRUE); } /*----------------------------------------------------------------------------- Name : thereareothercompatibleresearchships(ship) Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ bool thereareothercompatibleresearchships(Ship *ship) { Node *resnode; Ship *resship; dbgAssert(ship->shiptype == ResearchShip); resnode = universe.ShipList.head; if(((ResearchShipSpec *)ship->ShipSpecifics)->master) return FALSE; while(resnode != NULL) { resship = (Ship *) listGetStructOfNode(resnode); if(resship->shiptype == ResearchShip && resship->playerowner == ship->playerowner) { //there exists a ship to dock with... if(resship != ship) { return TRUE; } } resnode = resnode->next; } return FALSE; } /*----------------------------------------------------------------------------- Name : GroupShipIntoMilitaryParade Description : groups ship into military parade around aroundShip Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void GroupShipIntoMilitaryParade(CommandLayer *comlayer,ShipPtr ship,ShipPtr aroundShip) { CommandToDo *militaryGroup = GetMilitaryGroupAroundShip(comlayer,aroundShip); if (militaryGroup != NULL) { AddShipToMilitaryGroup(ship,militaryGroup); } else { CreateMilitaryGroupAroundShip(comlayer,ship,aroundShip); } } /*----------------------------------------------------------------------------- Name : clSetMilitaryParade Description : sets ships into a military parade Inputs : selectcom Outputs : Return : ----------------------------------------------------------------------------*/ void clSetMilitaryParade(CommandLayer *comlayer,SelectCommand *selectcom) { sdword index; sdword i; Ship *aroundShip; Ship *ship; CommandToDo *command; index = MothershipOrCarrierIndexInSelection(selectcom); if (index < 0) { dbgMessagef("\nWarning: No ship to form parade around"); return; } dbgAssert(index < selectcom->numShips); aroundShip = selectcom->ShipPtr[index]; for (i=0;inumShips;i++) { if (i != index) { ship = selectcom->ShipPtr[i]; command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if ((command != NULL) && (command->ordertype.order == COMMAND_MILITARYPARADE) && (command->militaryParade->aroundShip == aroundShip)) { // already in military Parade around aroundShip // do nothing } else { SelectCommand selectone; selectone.ShipPtr[0] = ship; selectone.numShips = 1; RemoveShipsFromDoingStuff(comlayer,&selectone); GroupShipIntoMilitaryParade(comlayer,selectcom->ShipPtr[i],aroundShip); } } } } /*----------------------------------------------------------------------------- Name : GroupShip Description : puts a ship into an appropriate group around the aroundShip Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void GroupShip(CommandLayer *comlayer,ShipPtr ship,ShipPtr aroundShip) { // ShipStaticInfo *shipstaticinfo = (ShipStaticInfo *)ship->staticinfo; CommandToDo *shipGroup; // sdword groupSize; // vector destination; SelectCommand selection; // groupSize = shipstaticinfo->groupSize; // dbgAssert(groupSize > 0); if((ship->shiptype == ResearchShip) && (thereareothercompatibleresearchships(ship))) { selection.numShips = 1; selection.ShipPtr[0] = ship; bitClear(ship->flags,SOF_Selectable); clDock(comlayer, &selection, DOCK_FOR_RESEARCH, NULL); return; } shipGroup = getShipAndItsFormationCommand(comlayer,aroundShip); if (shipGroup != NULL) { // the aroundShip is in formation, so let's just add this ship to aroundShip's formation AddShipToFormationGroup(ship,shipGroup); return; } GroupShipIntoMilitaryParade(comlayer,ship,aroundShip); #if 0 if (groupSize == 1) { // just position at random location selection.numShips = 1; selection.ShipPtr[0] = ship; GetRandomLocationAroundShip(&destination,aroundShip); clMove(comlayer,&selection,ship->posinfo.position,destination); } else { shipGroup = FindGroupForShip(comlayer,ship,groupSize); if (shipGroup == NULL) { // make new group, and position ship at random location selection.numShips = 1; selection.ShipPtr[0] = ship; GetRandomLocationAroundShip(&destination,aroundShip); clFormation(comlayer,&selection,DELTA_FORMATION); clMove(comlayer,&selection,ship->posinfo.position,destination); } else { // put ship in that group AddShipToFormationGroup(ship,shipGroup); } } #endif } /*----------------------------------------------------------------------------- Name : clShipDied Description : Call this function to update ship command layer when ship dies Inputs : deadship Outputs : Return : ----------------------------------------------------------------------------*/ void clShipDied(CommandLayer *comlayer,ShipPtr deadship) { SelectCommand selection; selection.numShips = 1; selection.ShipPtr[0] = deadship; RemoveShipsFromDoingStuff(comlayer,&selection); RemoveShipFromBeingTargeted(comlayer,deadship,REMOVE_PROTECT); // Update player unit caps info unitCapDeleteShip(deadship,deadship->playerowner); // if ship is a research ship and player is curplayer or aiplayer // ****** IMPORTANT we have changed the research so that it is deterministic on all machines. if (deadship->staticinfo->shiptype==ResearchShip) { /* if ( (deadship->playerowner==universe.curPlayerPtr) || (deadship->playerowner->aiPlayer!=NULL) ) {*/ rmDeactivateLab(deadship->playerowner); // } } } /*----------------------------------------------------------------------------- Name : TargetWithinAttackRange Description : returns TRUE if ship within attack range of target Inputs : ship,target Outputs : Return : returns TRUE if ship within attack range of target ----------------------------------------------------------------------------*/ bool TargetWithinAttackRange(Ship *ship,SpaceObjRotImpTarg *target) { vector trajectory; real32 range; aishipGetTrajectory(ship,target,&trajectory); range = RangeToTarget(ship,target,&trajectory); if (range < 10000.0f) { return TRUE; // consider within attack range sooner } #if 0 if(((ShipStaticInfo *)(ship->staticinfo))->bulletRange[ship->tacticstype] == 0.0f) { if(ship->shiptype == CloakGenerator || ship->shiptype == DFGFrigate || ship->shiptype == GravWellGenerator) { if(range < 7000.0f) { return TRUE; } } } #endif if (range < ((ShipStaticInfo *)(ship->staticinfo))->bulletRange[ship->tacticstype]) { return TRUE; } else { return FALSE; } } void putFightersInSelection(MaxSelection *dst,MaxSelection *src) { sdword i,k; k=0; for(i=0;inumShips;i++) { if(src->ShipPtr[i]->staticinfo->shipclass == CLASS_Fighter) { dst->ShipPtr[k] = src->ShipPtr[i]; k++; } } dst->numShips = k; } void putCorvettesInSelection(MaxSelection *dst,MaxSelection *src) { sdword i,k; k=0; for(i=0;inumShips;i++) { if(src->ShipPtr[i]->staticinfo->shipclass == CLASS_Corvette) { dst->ShipPtr[k] = src->ShipPtr[i]; k++; } } dst->numShips = k; } void putCapitalsInSelection(MaxSelection *dst,MaxSelection *src) { sdword i,k; k=0; for(i=0;inumShips;i++) { if(src->ShipPtr[i]->staticinfo->shipclass != CLASS_Fighter && src->ShipPtr[i]->staticinfo->shipclass != CLASS_Corvette) { dst->ShipPtr[k] = src->ShipPtr[i]; k++; } } dst->numShips = k; } /*----------------------------------------------------------------------------- Name : clHoldingPattern Description : Puts command into a holding pattern by breaking into small formations n' such. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ #define MAX_UNORDERED_GROUP_SIZE 11 //maximum ships in a group after //attacking {ships ungrouped previously) void clHoldingPattern(CommandLayer *comlayer,CommandToDo *command) { bool needToPassiveAttack = FALSE; bool needToProtect = FALSE; MaxSelection passiveAttackSelection; MaxSelection protectSelection; MaxSelection fighters,corvettes,capitals; MaxSelection working; MaxSelection *curselection; sdword i,j,k; if (command->ordertype.attributes & COMMAND_IS_FORMATION) { return; // already in formation, don't need to split them up into deltas } if(command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if(command->attack->numTargets > 0) { //if group was passive attacking, set a flag so we remember that that was the case needToPassiveAttack = TRUE; //store the target list acordingly selSelectionCopy((MaxAnySelection *)&passiveAttackSelection,(MaxAnySelection *)command->attack); } } if(command->ordertype.attributes & COMMAND_IS_PROTECTING) { if(command->protect->numShips > 0) { //group was protecting a ship, set the protection list and //set a flag accordingly selSelectionCopy((MaxAnySelection *)&protectSelection,(MaxAnySelection *)command->protect); needToProtect = TRUE; } } putFightersInSelection(&fighters,(MaxSelection *)command->selection); putCorvettesInSelection(&corvettes,(MaxSelection *)command->selection); putCapitalsInSelection(&capitals,(MaxSelection *)command->selection); // want to make sure all ships go into either fighters, corvettes, or frigates dbgAssert(fighters.numShips + corvettes.numShips + capitals.numShips == command->selection->numShips); //loop through all types of ships {capitals,fighters and corvetters} for(j = 0; j < 3; j++) { working.numShips = 0; if(j == 0) curselection = &fighters; else if (j==1) curselection = &corvettes; else curselection = &capitals; k =0; //loop through the current selection and divide type into groups //of MAX_UNORDERED_GROUP_SIZE for(i=0;inumShips;i++) { working.ShipPtr[k] = curselection->ShipPtr[i]; k++; if(k == MAX_UNORDERED_GROUP_SIZE) { //maximum # reached for this group //set their formation,targets,and protection flags accordingly working.numShips = k; clFormation(comlayer,(SelectCommand *)&working,DELTA_FORMATION); //if group was previously passiveattacking or //protecting set them to doing the same thing again with the //same target list if(needToPassiveAttack) clPassiveAttack(comlayer,(SelectCommand *)&working,(SelectAnyCommand *)&passiveAttackSelection); if(needToProtect) clProtect(comlayer,(SelectCommand *)&working,(SelectCommand *)&protectSelection); working.numShips = 0; k = 0; } } //done looping through groups working.numShips = k; if(k > 0) { //set remaining ships to formation + other tasks clFormation(comlayer,(SelectCommand *)&working,DELTA_FORMATION); //if group was previously passiveattacking or //protecting set them to doing the same thing again with the //same target list if(needToPassiveAttack) clPassiveAttack(comlayer,(SelectCommand *)&working,(SelectAnyCommand *)&passiveAttackSelection); if(needToProtect) clProtect(comlayer,(SelectCommand *)&working,(SelectCommand *)&protectSelection); working.numShips = 0; k = 0; } } } /*----------------------------------------------------------------------------- Name : salCapCorvettesInSelectionCantDoFormation Description : returns true if salcaps in selection can't dealwith a formation at the present time Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ bool salCapCorvettesInSelectionCantDoFormation(SelectCommand *selection) { sdword i; for(i=0;inumShips;i++) { if(selection->ShipPtr[i]->shiptype == SalCapCorvette) { if(selection->ShipPtr[i]->specialFlags & SPECIAL_BrokenFormation) { return TRUE; } } } return FALSE; } /*----------------------------------------------------------------------------- Name : processHoldingPattern Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ bool processHoldingPattern(CommandToDo *command,SelectCommand *selection) { sdword i,count; vector destination,heading,right; if(selection->numShips == 0) return TRUE; //flight forward for 2 secs...then turn if((universe.totaltimeelapsed - command->holdingStartTime) > TW_HOLDING_PATTERN_PATHTIME) { //turn right command->holdingStartTime = universe.totaltimeelapsed; matGetVectFromMatrixCol2(right,selection->ShipPtr[0]->rotinfo.coordsys); matGetVectFromMatrixCol3(heading,selection->ShipPtr[0]->rotinfo.coordsys); vecScalarMultiply(right,right,TW_HOLDING_PATTERN_RIGHT_COEFFICIENT); vecScalarMultiply(heading,heading,TW_HOLDING_PATTERN_HEADING_COEFFICIENT); vecAdd(command->holdingPatternVec,right,heading); vecScalarMultiply(command->holdingPatternVec,command->holdingPatternVec,TW_HOLDING_PATTERN_PATH_DISTANCE); command->turnAroundTimes++; if(command->turnAroundTimes > TW_HOLDING_PATTERN_NUM_TURNS) return TRUE; } count = selection->numShips; if(command->ordertype.attributes & COMMAND_IS_FORMATION) count = 1; //only process leader if command is in formation! for(i=0;iholdingPatternVec,selection->ShipPtr[i]->holdingPatternPos); aishipFlyToPointAvoidingObjs(selection->ShipPtr[i],&destination,AISHIP_PointInDirectionFlying|AISHIP_FastAsPossible,TW_HOLDING_PATTERN_PATH_MAX_SPEED); } return FALSE; } /*----------------------------------------------------------------------------- Name : clProcess Description : processes the command layer (executes all commands) Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clProcess(CommandLayer *comlayer) { Node *curnode = comlayer->todolist.head; CommandToDo *command; Node *nextnode; bool passiveAttacked; bool done; Ship *ship; Ship *target; SpaceObjRotImpTarg *attacktarget; sdword i; SelectCommand *commandselection; #ifndef HW_Release flightmanTest(); #endif while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); #ifdef DEBUG_COMMANDLAYER // dbgMessagef("\nclProcessing %d %d",command->ordertype.order,command->ordertype.attributes); #endif commandselection = command->selection; if(singlePlayerGame) { for (i=0;inumShips;i++) { ship = commandselection->ShipPtr[i]; if (ship->flags & (SOF_Hyperspace|SOF_Crazy)) { goto nextcommand; // don't process command if any ships in hyperspace ether or Crazy } ship->autostabilizeship = FALSE; // don't autostabilize ship if ship has a command to do } } else { //multiplayer version...allowing hyperspace! for (i=0;inumShips;i++) { ship = commandselection->ShipPtr[i]; if (ship->flags & (SOF_Crazy)) { goto nextcommand; // don't process command if any ships Crazy } ship->autostabilizeship = FALSE; // don't autostabilize ship if ship has a command to do } } if (command->ordertype.attributes & COMMAND_IS_FORMATION) { switch (command->ordertype.order) { case COMMAND_ATTACK: ship = command->selection->ShipPtr[0]; attacktarget = command->attack->TargetPtr[0]; #ifdef DEBUG_TACTICS if(tacticsOn) #endif { if(!tacticsInfo.holdFormationDuringBattle[ship->tacticstype] || command->selection->numShips == 1) { //evasive tactics, so attack as per olden times if ((command->formation.doneInitialAttack) || (command->attack->numTargets == 0) || (TargetWithinAttackRange(ship,attacktarget))) { command->formation.doneInitialAttack = TRUE; if (processAttackToDo(command)) { if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING && command->selection->ShipPtr[0]->aistatecommand == 0) { bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); } else { // free COMMAND_ATTACK stuff FreeLastOrder(command); // attack has finished so convert it back to a formation command->ordertype.order = COMMAND_NULL; } } else { if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { if((universe.univUpdateCounter & CHECK_ATTACKMOVE_TO_MOVE_RATE)==CHECK_ATTACKMOVE_TO_MOVE_FRAME) if(getRangeToClosestTarget(command) >= tacticsInfo.movingAttackTurnsIntoMoveCommandDistanceSqr) { if(command->selection->ShipPtr[0]->aistatecommand == 0) { //it is at this distance that the group of ships turns and hitails it out of there... bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); break; } else { ChangeOrderToHalt(command); } } } } } else { aishipFlyToShipAvoidingObjs(ship,attacktarget,AISHIP_PointInDirectionFlying + AISHIP_CarTurn, command->formation.travelvel); ship->shipidle = FALSE; processFormationToDo(command,FALSE,FALSE); } } else { // neutral or aggressive leader tactics, //formation type is held during combat, be it loose or tight //command->formation.doneInitialAttack = TRUE; // we attack in formation anyway, so let's just attack if ((command->formation.doneInitialAttack) || (command->attack->numTargets == 0) || (TargetWithinAttackRange(ship,attacktarget))) { command->formation.doneInitialAttack = TRUE; if (processAttackToDoInFormation(command)) { // free COMMAND_ATTACK stuff if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING && command->selection->ShipPtr[0]->aistatecommand == 0) { //attacking and moving and HAVEN'T reached destination! bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); } else { // free COMMAND_ATTACK stuff FreeLastOrder(command); // attack has finished so convert it back to a formation command->ordertype.order = COMMAND_NULL; } //command->ordertype.attributes |= COMMAND_IS_HOLDINGPATTERN; } else { if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { if((universe.univUpdateCounter & CHECK_ATTACKMOVE_TO_MOVE_RATE)==CHECK_ATTACKMOVE_TO_MOVE_FRAME) if(getRangeToClosestTarget(command) >= tacticsInfo.movingAttackTurnsIntoMoveCommandDistanceSqr) { if(command->selection->ShipPtr[0]->aistatecommand == 0) { //it is at this distance that the group of ships turns and hitails it out of there... bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); break; } else { ChangeOrderToHalt(command); } } } } } else { aishipFlyToShipAvoidingObjs(ship,attacktarget,AISHIP_PointInDirectionFlying + AISHIP_CarTurn, command->formation.travelvel); ship->shipidle = FALSE; processFormationToDo(command,FALSE,FALSE); } } } #ifdef DEBUG_TACTICS else { if ((command->formation.doneInitialAttack) || (command->attack->numTargets == 0) || (TargetWithinAttackRange(ship,attacktarget))) { command->formation.doneInitialAttack = TRUE; if (processAttackToDo(command)) { // free COMMAND_ATTACK stuff FreeLastOrder(command); // attack has finished so convert it back to a formation command->ordertype.order = COMMAND_NULL; } } else { aishipFlyToShipAvoidingObjs(ship,attacktarget,AISHIP_PointInDirectionFlying + AISHIP_CarTurn, command->formation.travelvel); ship->shipidle = FALSE; processFormationToDo(command,FALSE,FALSE); } } #endif break; case COMMAND_DOCK: if (command->dock.dockType & DOCK_INSTANTANEOUSLY) { goto processdock; } if ((command->dock.allDockingWithSameShip) && (!command->dock.allNearShip)) { ship = command->selection->ShipPtr[0]; target = ship->dockvars.dockship; if ((target == NULL) || ShipWithinDockRange(ship,target)) { command->dock.allNearShip = TRUE; goto processdock; } if (aishipFlyToShipAvoidingObjs(ship,target,AISHIP_PointInDirectionFlying + AISHIP_CarTurn + AISHIP_DontFlyToObscuredPoints + AISHIP_ReturnImmedIfPointObscured, command->formation.travelvel) & AISHIP_FLY_OBJECT_IN_WAY) { command->dock.allNearShip = TRUE; goto processdock; } ship->shipidle = FALSE; processFormationToDo(command,FALSE,FALSE); } else { processdock: if (processDockToDo(command)) { if (command->selection->numShips == 0) { // All ships have been removed from docking. Therefore totally wipe out command nextnode = curnode->next; // dock command has finished FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } else { FreeLastOrder(command); // free COMMAND_DOCK command->ordertype.order = COMMAND_NULL; } } } break; case COMMAND_HALT: passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } passiveAttacked = TRUE; bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } ship = command->selection->ShipPtr[0]; if ((passiveAttacked) & (bool)((ShipStaticInfo *)ship->staticinfo)->rotateToRetaliate) { done = aitrackSteadyShipDriftOnly(ship); } else { done = aitrackSteadyShip(ship); } processFormationToDo(command,TRUE,passiveAttacked); if (done) { FreeLastOrder(command); command->ordertype.order = COMMAND_NULL; } break; case COMMAND_NULL: passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PROTECTING) { if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } passiveAttacked = TRUE; bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } // if (command->formation.formAroundProtectedShip) if ((command->formation.formationtype == SPHERE_FORMATION) && (command->selection->numShips > 1)) // for Sphere formation, formation will { // surround protected ship. processFormationToDo(command,FALSE,passiveAttacked); } else { if (command->protect->numShips > 1) { protectShipsAvg(command->selection->ShipPtr[0],command->protect,passiveAttacked); processFormationToDo(command,FALSE,passiveAttacked); } else { protectShip(command->selection->ShipPtr[0],command->protect->ShipPtr[0],passiveAttacked); processFormationToDo(command,!command->protect->ShipPtr[0]->posinfo.isMoving,passiveAttacked); } } } else { if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } passiveAttacked = TRUE; bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } if(command->ordertype.attributes & COMMAND_IS_HOLDINGPATTERN) { if(processHoldingPattern(command,command->selection)) { bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } } else { ship = command->selection->ShipPtr[0]; if ((passiveAttacked) & (bool)((ShipStaticInfo *)ship->staticinfo)->rotateToRetaliate) { //if(ship->tacticstype != Evasive) aitrackSteadyShipDriftOnly(ship); } else { aitrackSteadyShip(ship); } } if(passiveAttacked)// && command->selection->ShipPtr[0]->tacticstype == Evasive) processFormationToDo(command,FALSE,passiveAttacked); else processFormationToDo(command,bitTest(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN) ? FALSE : TRUE,passiveAttacked); } break; case COMMAND_MP_HYPERSPACEING: if(processMpHyperspaceingToDo(command)) { FreeLastOrder(command); // free COMMAND_MP_HYPERSPACEING stuff command->ordertype.order = COMMAND_NULL; } break; case COMMAND_MOVE: passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); //don't set to true as it controls whether or not a ship //faces in movement direction //passiveAttacked = TRUE; } if (processMoveLeaderToDo(command,passiveAttacked)) { // reached destination, so change COMMAND_MOVE | COMMAND_IS_FORMATION to COMMAND_NULL | COMMAND_IS_FORMATION #ifdef DEBUG_FORMATIONS dbgMessage("\nReached Destination in Formation."); #endif if (universe.curPlayerPtr->PlayerMothership != NULL) { // Mothership can be carrier, so make sure it is really a mothership if ((universe.curPlayerPtr->PlayerMothership->shiptype == Mothership) && (selShipInSelection(command->selection->ShipPtr, command->selection->numShips, universe.curPlayerPtr->PlayerMothership))) { speechEventFleetSpec(universe.curPlayerPtr->PlayerMothership, COMM_F_MoShip_Arrived, 0, universe.curPlayerIndex); } } FreeLastOrder(command); // free COMMAND_MOVE stuff command->ordertype.order = COMMAND_NULL; } processFormationToDo(command,FALSE,passiveAttacked); break; case COMMAND_SPECIAL: if (processSpecialToDo(command)) { FreeLastOrder(command); // free COMMAND_SPECIAL command->ordertype.order = COMMAND_NULL; } if(salCapCorvettesInSelectionCantDoFormation(command->selection)) { processFormationToDo(command,FALSE,FALSE); } break; default: dbgAssert(FALSE); break; } } else { switch (command->ordertype.order) { case COMMAND_ATTACK: //open guns if in range or if we need to if (processAttackToDo(command)) { if (command->ordertype.attributes & COMMAND_IS_PROTECTING) { if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); } else { // free COMMAND_ATTACK stuff FreeLastOrder(command); // attack has finished so convert it back to a formation command->ordertype.order = COMMAND_NULL; } } else { /* nextnode = curnode->next; // attack has finished FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; */ if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); } else { // free COMMAND_ATTACK stuff FreeLastOrder(command); // attack has finished so convert it back to a formation command->ordertype.order = COMMAND_NULL; } //command->ordertype.attributes |= COMMAND_IS_HOLDINGPATTERN; } } else { if(command->ordertype.attributes & COMMAND_IS_ATTACKINGANDMOVING) { if((universe.univUpdateCounter & CHECK_ATTACKMOVE_TO_MOVE_RATE)==CHECK_ATTACKMOVE_TO_MOVE_FRAME) if(getRangeToClosestTarget(command) >= tacticsInfo.movingAttackTurnsIntoMoveCommandDistanceSqr) { //it is at this distance that the group of ships turns and hitails it out of there... bitSet(command->selection->ShipPtr[0]->specialFlags,SPECIAL_ATTACKMOVECANCEL); ChangeOrderToMove(command,command->selection->ShipPtr[0]->moveFrom,command->selection->ShipPtr[0]->moveTo); break; } } } break; case COMMAND_MP_HYPERSPACEING: if(processMpHyperspaceingToDo(command)) { FreeLastOrder(command); // free COMMAND_MOVE stuff command->ordertype.order = COMMAND_NULL; } break; case COMMAND_MOVE: passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); //don't set to true so we face direction we're //moving //passiveAttacked = TRUE; } if (processMoveToDo(command,passiveAttacked)) { if (universe.curPlayerPtr->PlayerMothership != NULL) { // Mothership can be carrier, so make sure it is really a mothership if ((universe.curPlayerPtr->PlayerMothership->shiptype == Mothership) && (selShipInSelection(command->selection->ShipPtr, command->selection->numShips, universe.curPlayerPtr->PlayerMothership))) { speechEventFleetSpec(universe.curPlayerPtr->PlayerMothership, COMM_F_MoShip_Arrived, 0, universe.curPlayerIndex); } } // has the probe arrived? for (i = 0; i < command->selection->numShips; i++) { if (command->selection->ShipPtr[i]->shiptype == Probe) { if (command->selection->ShipPtr[i]->playerowner == universe.curPlayerPtr) { speechEventFleetSpec(command->selection->ShipPtr[i], STAT_F_Probe_Arrived, 0, universe.curPlayerIndex); break; } } } if (command->ordertype.attributes & (COMMAND_IS_PROTECTING|COMMAND_IS_PASSIVEATTACKING)) { FreeLastOrder(command); // free COMMAND_MOVE stuff command->ordertype.order = COMMAND_NULL; } else { nextnode = curnode->next; FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } } break; case COMMAND_DOCK: if (processDockToDo(command)) { if (command->dock.wasHarvesting) { Ship *resourceShip = command->selection->ShipPtr[0]; dbgAssert(command->selection->numShips == 1); // changing COMMAND_DOCK to COMMAND_COLLECTRESOURCE FreeLastOrder(command); ChangeSingleShipToCollectResource(command); } else { nextnode = curnode->next; // dock command has finished FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } } break; case COMMAND_SPECIAL: if (processSpecialToDo(command)) { nextnode = curnode->next; // special command has finished FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } break; case COMMAND_COLLECTRESOURCE: if (processCollectResource(command)) { Ship *ship = command->selection->ShipPtr[0]; dbgAssert(command->selection->numShips >= 1); nextnode = curnode->next; // collect resource command has finished FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; if ((ship->playerowner) && (ship->playerowner->PlayerMothership)) { GroupShipIntoMilitaryParade(&universe.mainCommandLayer,ship,ship->playerowner->PlayerMothership); } goto readyfornextnode; } break; case COMMAND_LAUNCHSHIP: if (processLaunchShipToDo(command)) { Ship *ship; Ship *receiverShip; dbgAssert(command->selection->numShips == 1); ship = command->selection->ShipPtr[0]; receiverShip = command->launchship.receiverShip; nextnode = curnode->next; FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; GroupShip(comlayer,ship,receiverShip); aiplayerShipLaunchedCallback(ship); goto readyfornextnode; } break; case COMMAND_HALT: passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } passiveAttacked = TRUE; bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } if (processHaltToDo(command,passiveAttacked)) { if (command->ordertype.attributes) { FreeLastOrder(command); command->ordertype.order = COMMAND_NULL; } else { nextnode = curnode->next; FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } } break; case COMMAND_MILITARYPARADE: passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } passiveAttacked = TRUE; bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } processMilitaryParadeToDo(command,passiveAttacked); break; case COMMAND_BUILDINGSHIP: dbgAssert(command->ordertype.attributes == 0); // don't allow COMMAND_IS_PASSIVEATTACKING, COMMAND_IS_PROTECTING, COMMAND_IS_FORMATION if (processBuildingShipToDo(command)) { nextnode = curnode->next; FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } break; case COMMAND_NULL: if (command->ordertype.attributes == 0) { // NULL command, and no attributes, so let's remove it from the command layer nextnode = curnode->next; FreeCommandToDoContents(command); listDeleteNode(curnode); curnode = nextnode; goto readyfornextnode; } if(command->ordertype.attributes & COMMAND_IS_HOLDINGPATTERN) { //put ships in holding pattern...maintain all relevant info though (protecting and passiveattacking!) nextnode = curnode->next; //this function will result in command being turned into a formation flying a holding pattern clHoldingPattern(comlayer,command); curnode = nextnode; goto readyfornextnode; } if (command->ordertype.attributes & COMMAND_IS_PROTECTING) { ProtectCommand *commandprotect = command->protect; SelectCommand *commandselect = command->selection; sdword numShipsToProtect = commandprotect->numShips; sdword numShips = commandselect->numShips; Ship *protectShipPtr; sdword protectThisShip; sdword i; passiveAttacked = FALSE; if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } passiveAttacked = TRUE; bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); } dbgAssert(numShipsToProtect > 0); if (numShipsToProtect == 1) { protectShipPtr = commandprotect->ShipPtr[0]; for (i=0;iShipPtr[i],protectShipPtr,passiveAttacked); } } else { if (numShips == 1) { // we only have 1 ship, and > 1 ships to protect, so try to stay in middle protectShipsAvg(commandselect->ShipPtr[0],commandprotect,passiveAttacked); } else { protectThisShip = 0; for (i=0;iShipPtr[i],commandprotect->ShipPtr[protectThisShip],passiveAttacked); protectThisShip++; if (protectThisShip >= numShipsToProtect) { protectThisShip = 0; } } } } } else { if (command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING) { if (processPassiveAttackToDo(command)) { ClearPassiveAttacking(command); } bitClear(command->ordertype.attributes,COMMAND_IS_HOLDINGPATTERN); ship = command->selection->ShipPtr[0]; if ((bool)((ShipStaticInfo *)ship->staticinfo)->rotateToRetaliate) { if(ship->tacticstype != Evasive) aitrackSteadyShipDriftOnly(ship); } else { aitrackSteadyShip(ship); } } } break; default: dbgAssert(FALSE); break; } } nextcommand: curnode = curnode->next; readyfornextnode: ; } } /*----------------------------------------------------------------------------- Name : clPostProcess Description : processes all of the command layer (2nd pass), for actions such as protecting Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clPostProcess(CommandLayer *comlayer) { Node *curnode = comlayer->todolist.head; Node *endnode = comlayer->todolist.tail; CommandToDo *command; Node *nextnode; AttackCommand *attack; ShipPtr protectMe; sdword i; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); nextnode = curnode->next; if (command->ordertype.attributes & COMMAND_IS_PROTECTING) { dbgAssert(command->protect->numShips > 0); if (bitTest(command->protectFlags,PROTECTFLAGS_JUST_FOLLOW)) { goto dontretaliate; } for (i=0;iprotect->numShips;i++) { protectMe = command->protect->ShipPtr[i]; if ((protectMe->gettingrocked != NULL) && (!allianceIsShipAlly(command->selection->ShipPtr[0],protectMe->gettingrocked->playerowner))) { if ((command->ordertype.order == COMMAND_NULL) || (command->ordertype.order == COMMAND_MOVE) || (command->ordertype.order == COMMAND_ATTACK)) { if ((command->ordertype.attributes & COMMAND_IS_FORMATION) && (command->formation.formationtype == SPHERE_FORMATION)) { if (i == 0) // 0th ship is always protected by a surrounded sphere of ships, and these ships { // should stay nearby (not attack, just passive attack). goto dontretaliate; } } /* if (AreAllShipsAttackCapable(command->selection)) { attack = (AttackCommand *)getShipAndItsFormation(&universe.mainCommandLayer,protectMe->gettingrocked); //filter gravwell and other related ships for attack during guarding makeShipsNotHaveNonCombatShipsForGuardAttack(command->selection); //ChangeOrderToAttack(command,attack); clAttack(&universe.mainCommandLayer,command->selection,attack); memFree(attack); } else */ { // if (AreAnyShipsAttackCapable(command->selection)) MaxSelection attackingships; if (MakeShipsAttackCapable((SelectCommand *)&attackingships, command->selection)) { // just tell the ships that are attack capable to attack CommandToDo *newcommand; // SelectCommand *attackingships = selectDupSelection(command->selection); attack = (AttackCommand *)getShipAndItsFormation(&universe.mainCommandLayer,protectMe->gettingrocked); // MakeShipsAttackCapable(attackingships); dbgAssert(attackingships.numShips > 0); makeShipsNotHaveNonCombatShipsForGuardAttack((SelectCommand *)&attackingships); if(attackingships.numShips > 0) { newcommand = clProtect(comlayer,(SelectCommand *)&attackingships,command->protect); //filter gravwell and other related ships for attack during guarding ChangeOrderToAttack(newcommand,attack); } memFree(attack); // memFree(attackingships); } } goto dontretaliate; } } } } dontretaliate: if (curnode == endnode) { break; } curnode = nextnode; } } /*----------------------------------------------------------------------------- Name : getShipAndItsCommand Description : Returns allocated command containing the ship Inputs : Outputs : Return : Returns allocated command containing the ship ----------------------------------------------------------------------------*/ CommandToDo *getShipAndItsCommand(CommandLayer *comlayer,ShipPtr ship) { #if 0 Node *curnode = comlayer->todolist.head; CommandToDo *command; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); if (selShipInSelection(command->selection->ShipPtr,command->selection->numShips,ship)) { dbgAssert(ship->command == command); return command; } curnode = curnode->next; } dbgAssert(ship->command == NULL); return NULL; #else return ship->command; #endif } /*----------------------------------------------------------------------------- Name : getShipAndItsCommandSelection Description : Returns selection for allocated command containing the ship. If there's no command for the ship, it'll return the formation of the ship. Inputs : Outputs : Return : Returns allocated command containing the ship ----------------------------------------------------------------------------*/ SelectCommand *getShipAndItsCommandSelection(CommandLayer *comlayer,ShipPtr ship,bool *parade) { CommandToDo *command = getShipAndItsCommand(comlayer,ship); SelectCommand *returnselect; udword sizeofselect; if (command != NULL) { if (command->ordertype.order == COMMAND_MILITARYPARADE) { *parade = TRUE; } else { *parade = FALSE; } sizeofselect = sizeofSelectCommand(command->selection->numShips); returnselect = memAlloc(sizeofselect,"commandselection",Pyrophoric); memcpy(returnselect,command->selection,sizeofselect); } else { //no command found containing this ship; return just the single ship returnselect = memAlloc(sizeofSelectCommand(1),"sf(shipandform)", Pyrophoric); returnselect->numShips = 1; returnselect->ShipPtr[0] = ship; *parade = FALSE; } return returnselect; } /*----------------------------------------------------------------------------- Name : GetMilitaryGroupAroundShip Description : gets the Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ CommandToDo *GetMilitaryGroupAroundShip(CommandLayer *comlayer,Ship *aroundShip) { Node *curnode = comlayer->todolist.head; CommandToDo *command; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); if (command->ordertype.order == COMMAND_MILITARYPARADE) { if (command->militaryParade->aroundShip == aroundShip) { return command; } } curnode = curnode->next; } return NULL; } /*----------------------------------------------------------------------------- Name : clPresetShipsToPosition Description : sets ships to correct position according to command layer Inputs : comlayer Outputs : Return : ----------------------------------------------------------------------------*/ void clPresetShipsToPosition(CommandLayer *comlayer) { Node *curnode = comlayer->todolist.head; CommandToDo *command; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); if (command->ordertype.order == COMMAND_MILITARYPARADE) { setMilitaryParade(command); } else if (command->ordertype.attributes & COMMAND_IS_FORMATION) { setFormationToDo(command); } curnode = curnode->next; } } /*----------------------------------------------------------------------------- Name : getShipAndItsFormationCommand Description : Returns allocated command containing the ship and its formation Inputs : Outputs : Return : Returns allocated command containing the ship and its formation ----------------------------------------------------------------------------*/ CommandToDo *getShipAndItsFormationCommand(CommandLayer *comlayer,ShipPtr ship) { return ship->formationcommand; } /*----------------------------------------------------------------------------- Name : getShipAndItsFormation Description : Returns allocated selectcommand containing the ship and its formation Inputs : Outputs : Return : Returns allocated selectcommand containing the ship and its formation ----------------------------------------------------------------------------*/ SelectCommand *getShipAndItsFormation(CommandLayer *comlayer,ShipPtr ship) { CommandToDo *command = getShipAndItsFormationCommand(comlayer,ship); SelectCommand *returnselect; udword sizeofselect; if (command != NULL) { sizeofselect = sizeofSelectCommand(command->selection->numShips); returnselect = memAlloc(sizeofselect,"sf(shipandform)",Pyrophoric); memcpy(returnselect,command->selection,sizeofselect); } else { returnselect = memAlloc(sizeofSelectCommand(1),"sf(shipandform)", Pyrophoric); returnselect->numShips = 1; returnselect->ShipPtr[0] = ship; } return returnselect; } /*----------------------------------------------------------------------------- Name : CommandInCommandLayer Description : returns TRUE if searchfor is in comlayer Inputs : comlayer, searchfor Outputs : Return : returns TRUE if searchfor is in comlayer ----------------------------------------------------------------------------*/ bool CommandInCommandLayer(CommandLayer *comlayer,CommandToDo *searchfor) { Node *curnode = comlayer->todolist.head; CommandToDo *command; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); if (command == searchfor) { return TRUE; } curnode = curnode->next; } return FALSE; } #if BINNETLOG void binwriteselection(udword header,SelectCommand *selection) { binnetselectionMax sel; sdword i; sel.header = header; if (selection == NULL) { sel.numShips = 0; } else { sel.numShips = selection->numShips; for (i=0;iShipPtr[i]->shipID.shipNumber; } } fwrite(&sel,sizeofbinnetselection(sel.numShips),1,netlogfile); } void binwriteship(Ship *ship) { uword shipid = (uword)-1; if (ship != NULL) { shipid = ship->shipID.shipNumber; } fwrite(&shipid,sizeof(uword),1,netlogfile); } void binwriteanyselection(udword header,SelectAnyCommand *selection) { binnetanyselectionMax sel; SpaceObjRotImpTarg *target; uword objtype; uword id; sdword i; sel.header = header; if (selection == NULL) { sel.numTargets = 0; } else { sel.numTargets = selection->numTargets; for (i=0;iTargetPtr[i]; objtype = target->objtype; id = 0; switch (objtype) { case OBJ_ShipType: id = ((Ship *)target)->shipID.shipNumber; break; case OBJ_MissileType: id = ((Missile *)target)->missileID.missileNumber; break; case OBJ_AsteroidType: case OBJ_NebulaType: case OBJ_GasType: case OBJ_DustType: id = ((Resource *)target)->resourceID.resourceNumber; break; case OBJ_DerelictType: id = ((Derelict *)target)->derelictID.derelictNumber; break; } sel.TargetID[i] = objtype | (id << 16); } } fwrite(&sel,sizeofbinnetanyselection(sel.numTargets),1,netlogfile); } void clChecksum(void) { Node *curnode = universe.mainCommandLayer.todolist.head; CommandToDo *command; SelectCommand *selection; binnetCmdLayerInfoMax cmd; udword header; sdword i; while (curnode != NULL) { command = (CommandToDo *)listGetStructOfNode(curnode); selection = command->selection; cmd.header = makenetcheckHeader('C','C','C','C'); cmd.order = command->ordertype.order; cmd.attributes = command->ordertype.attributes; cmd.numShips = selection->numShips; for (i=0;iShipPtr[i]->shipID.shipNumber; } fwrite(&cmd,sizeofbinnetCmdLayerInfo(cmd.numShips),1,netlogfile); if (cmd.attributes & COMMAND_IS_FORMATION) { header = makenetcheckHeader('F','O','R','M'); fwrite(&header,sizeof(header),1,netlogfile); fwrite(&command->formation,sizeof(TypeOfFormation)+sizeof(bool),1,netlogfile); fwrite(&command->formation.tacticalState,44,1,netlogfile); } if (cmd.attributes & COMMAND_IS_PROTECTING) { binwriteselection(makenetcheckHeader('S','E','P','T'),command->protect); } if (cmd.attributes & COMMAND_IS_PASSIVEATTACKING) { binwriteanyselection(makenetcheckHeader('S','E','P','A'),command->attack); } #if 0 if (cmd.attributes & COMMAND_IS_ATTACKINGANDMOVING) { header = makenetcheckHeader('A','T','M','V'); fwrite(&header,sizeof(header),1,netlogfile); fwrite(&command->move,sizeof(MoveCommand),1,netlogfile); } #endif switch (cmd.order) { case COMMAND_MOVE: header = makenetcheckHeader('M','O','V','E'); fwrite(&header,sizeof(header),1,netlogfile); fwrite(&command->move,sizeof(MoveCommand),1,netlogfile); break; case COMMAND_ATTACK: binwriteanyselection(makenetcheckHeader('S','E','A','T'),command->attack); break; case COMMAND_DOCK: header = makenetcheckHeader('D','O','C','K'); fwrite(&header,sizeof(header),1,netlogfile); fwrite(&command->dock,sizeof(DockCommand),1,netlogfile); break; case COMMAND_LAUNCHSHIP: header = makenetcheckHeader('L','A','U','N'); fwrite(&header,sizeof(header),1,netlogfile); binwriteship(command->launchship.receiverShip); break; case COMMAND_COLLECTRESOURCE: break; // don't bother - ship already has this info case COMMAND_BUILDINGSHIP: header = makenetcheckHeader('B','U','L','D'); fwrite(&header,sizeof(header),1,netlogfile); fwrite(&command->buildingship,sizeof(ShipType)+sizeof(ShipRace),1,netlogfile); break; case COMMAND_SPECIAL: binwriteanyselection(makenetcheckHeader('S','P','E','C'),command->specialtargets); break; case COMMAND_MILITARYPARADE: header = makenetcheckHeader('M','I','L','P'); fwrite(&header,sizeof(header),1,netlogfile); binwriteship(command->militaryParade->aroundShip); fwrite(&command->militaryParade->paradeType,sizeof(sdword),1,netlogfile); break; case COMMAND_MP_HYPERSPACEING: header = makenetcheckHeader('M','P','H','P'); fwrite(&header,sizeof(header),1,netlogfile); fwrite(&command->hyperspaceState,sizeof(sdword),1,netlogfile); break; } curnode = curnode->next; } } #endif