/*============================================================================= Name : AIResourceMan.c Purpose : Code for the Resource Manager of the computer player Created 5/27/1998 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include "types.h" #include "spaceobj.h" #include "universe.h" #include "univupdate.h" #include "rescollect.h" #include "CommandWrap.h" #include "AIResourceMan.h" #include "AIPlayer.h" #include "AIFleetMan.h" #include "AIOrders.h" #include "AITeam.h" #include "AIUtilities.h" #include "AIDefenseMan.h" #include "select.h" #include "blobs.h" #include "consMgr.h" #include "MultiplayerGame.h" #include "randy.h" udword UPDATE_RU_COUNT_RATE = 3; sdword BUILD_RC_CASH_TABLE_BASE = 400; sdword BUILD_RC_CASH_TABLE_INC = 200; sdword MAX_RCOLLECTORS_PER_DOCK_POINT = 3; sdword MAX_RCOLLECTORS_TO_BUILD = 5; sdword MIN_WORTHWHILE_RUs = 400; sdword RUs_PER_RCOLLECTOR = 1000; sdword MIN_WORTHWHILE_RUs_FOR_CONTROLLER = 3000; sdword MIN_RCOLLECTORS_FOR_CONTROLLER = 4; sdword BUILD_RCONTROLLER_CASH_BASE = 1500; sdword BUILD_ASF_CASH_BASE = 2000; real32 ASF_POSITION_TO_ENEMY_MOTHERSHIP = 0.4f; real32 RCONTROLLER_POS_FACTOR_FRIENDLIES_PRESENT = 1.1f; real32 RCONTROLLER_POS_FACTOR_ENEMIESPRESENT_BUT_OUTNUMBERED = 0.6f; real32 RCONTROLLER_POS_FACTOR_ENEMIESPRESENT = 0.3f; real32 RCONTROLLER_POS_FACTOR_INBETWEEN = 1.0f; real32 RCONTROLLER_POS_FACTOR_NOTINBETWEEN = 0.1f; scriptEntry AIResourceManTweaks[] = { makeEntry(UPDATE_RU_COUNT_RATE,scriptSetUdwordCB), makeEntry(BUILD_RC_CASH_TABLE_BASE,scriptSetSdwordCB), makeEntry(BUILD_RC_CASH_TABLE_INC,scriptSetSdwordCB), makeEntry(MAX_RCOLLECTORS_PER_DOCK_POINT,scriptSetSdwordCB), makeEntry(MAX_RCOLLECTORS_TO_BUILD,scriptSetSdwordCB), makeEntry(MIN_WORTHWHILE_RUs,scriptSetSdwordCB), makeEntry(RUs_PER_RCOLLECTOR,scriptSetSdwordCB), makeEntry(MIN_WORTHWHILE_RUs_FOR_CONTROLLER,scriptSetSdwordCB), makeEntry(MIN_RCOLLECTORS_FOR_CONTROLLER,scriptSetSdwordCB), makeEntry(BUILD_RCONTROLLER_CASH_BASE,scriptSetSdwordCB), makeEntry(ASF_POSITION_TO_ENEMY_MOTHERSHIP,scriptSetReal32CB), makeEntry(BUILD_ASF_CASH_BASE,scriptSetSdwordCB), makeEntry(RCONTROLLER_POS_FACTOR_FRIENDLIES_PRESENT,scriptSetReal32CB), makeEntry(RCONTROLLER_POS_FACTOR_ENEMIESPRESENT_BUT_OUTNUMBERED,scriptSetReal32CB), makeEntry(RCONTROLLER_POS_FACTOR_ENEMIESPRESENT,scriptSetReal32CB), makeEntry(RCONTROLLER_POS_FACTOR_INBETWEEN,scriptSetReal32CB), makeEntry(RCONTROLLER_POS_FACTOR_NOTINBETWEEN,scriptSetReal32CB), endEntry }; /*----------------------------------------------------------------------------- Name : NumberOfEasilyAccesibleRUs Description : returns total number of easily accesible RU's to player Inputs : Outputs : Return : returns total number of easily accesible RU's to player ----------------------------------------------------------------------------*/ sdword NumberOfEasilyAccesibleRUs(Player *player) { // for now, just return the total number of RU's. Node *objnode = universe.ResourceList.head; Resource *resource; sdword numRUs = 0; while (objnode != NULL) { resource = (Resource *)listGetStructOfNode(objnode); dbgAssert(resource->flags & SOF_Resource); numRUs += resource->resourcevalue; nextnode: objnode = objnode->next; } return numRUs; } /*----------------------------------------------------------------------------- Name : NumRUDockPointsOnThisShip Description : returns number of RU dock points on ship Inputs : Outputs : Return : returns number of RU dock points on ship ----------------------------------------------------------------------------*/ sdword NumRUDockPointsOnThisShip(Ship *ship) { if ((ship->shiptype == Carrier) || (ship->shiptype == Mothership)) { return 2; } else { return 1; } } /*----------------------------------------------------------------------------- Name : airInit Description : initialize resource manager stuff in aiplayer Inputs : aiplayer Outputs : Return : ----------------------------------------------------------------------------*/ void airInit(struct AIPlayer *aiplayer) { udword i; growSelectInit(&aiplayer->airResourceReserves); growSelectInit(&aiplayer->airResourceCollectors); aiplayer->airEasilyAccesibleRUsInWorld = -1; aiplayer->airNumRCollectors = 0; aiplayer->airNumRControllers = 0; aiplayer->airNumASF = 0; aiplayer->airNumResearchShips = 0; aiplayer->numSupportTeams = 0; aiplayer->ResourcersToBuild = 0; for (i=0;isupportTeam[i] = NULL; } switch (aiplayer->aiplayerDifficultyLevel) { case AI_ADV: aiuEnableResourceFeature(AIR_ACTIVE_MOTHERSHIP); aiuEnableResourceFeature(AIR_ACTIVE_RESOURCE_COLLECTION); aiuEnableResourceFeature(AIR_ACTIVE_SUPPORT_FRIGATE); aiuEnableResourceFeature(AIR_RESOURCE_DISTRESS_SIGNALS); aiuEnableResourceFeature(AIR_SMART_COLLECTOR_REQUESTS); aiuEnableResourceFeature(AIR_SMART_RESEARCH_SHIP_REQUESTS); case AI_INT: aiuEnableResourceFeature(AIR_AGGRESSIVE_RESOURCING); aiuEnableResourceFeature(AIR_RESOURCE_CONTROLLER_REQUESTS); aiuEnableResourceFeature(AIR_SUPPORT_FRIGATE_REQUESTS); case AI_BEG: aiuEnableResourceFeature(AIR_ACTIVE_RESOURCE_CONTROLLER); break; default: dbgAssert(FALSE); } } /*----------------------------------------------------------------------------- Name : airClose Description : Closes resource manager stuff in aiplayer Inputs : aiplayer Outputs : Return : ----------------------------------------------------------------------------*/ void airClose(struct AIPlayer *aiplayer) { udword i; growSelectClose(&aiplayer->airResourceReserves); growSelectClose(&aiplayer->airResourceCollectors); for (i=0;isupportTeam[i]) { aitDestroy(aiplayer, aiplayer->supportTeam[i], FALSE); aiplayer->supportTeam[i] = NULL; } } } /*----------------------------------------------------------------------------- Name : airShipDied Description : Removes a dead ship from the Combat Manager's internal structures Inputs : aiplayer, ship - the ship that died Outputs : Return : TRUE if the ship was found and removed ----------------------------------------------------------------------------*/ bool airShipDied(struct AIPlayer *aiplayer,ShipPtr ship) { // if ((ship->playerowner == aiplayer->player) || (ship->attributes & ATTRIBUTES_Defector)) // do all checks for defector // { if (growSelectRemoveShip(&aiplayer->airResourceReserves, ship)) { if (ship->shiptype == ResourceCollector) { growSelectRemoveShip(&aiplayer->airResourceCollectors, ship); aiplayer->airNumRCollectors--; dbgAssert(aiplayer->airNumRCollectors >= 0); } else if (ship->shiptype == ResourceController) { aiplayer->airNumRControllers--; dbgAssert(aiplayer->airNumRControllers >= 0); } else if (ship->shiptype == AdvanceSupportFrigate) { aiplayer->airNumASF--; dbgAssert(aiplayer->airNumASF >= 0); } else if (ship->shiptype == ResearchShip) { aiplayer->airNumResearchShips--; dbgAssert(aiplayer->airNumResearchShips >= 0); aiCurrentAIPlayer->TechnologyDeficit = 0; //recalculate technology deficit } return TRUE; } // } return FALSE; } /*----------------------------------------------------------------------------- Name : airTeamDied Description : Removes a team from the resource stuff and all that jazz Inputs : aiplayer, team Outputs : does some things Return : void ----------------------------------------------------------------------------*/ void airTeamDied(struct AIPlayer *aiplayer, struct AITeam *team) { udword i; for (i=0;inumSupportTeams;i++) { if (aiplayer->supportTeam[i] == team) { aiplayer->numSupportTeams--; aiplayer->supportTeam[i] = aiplayer->supportTeam[aiplayer->numSupportTeams]; aiplayer->supportTeam[aiplayer->numSupportTeams] = NULL; } } } /*----------------------------------------------------------------------------- Name : airAddedResourceCollector Description : called when a resource collector is added Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void airAddedResourceCollector(Ship *ship) { Resource *resource; SelectCommand selectone; udword new_team_num = aiCurrentAIPlayer->numSupportTeams; AITeam *team; growSelectAddShip(&aiCurrentAIPlayer->airResourceCollectors, ship); aiCurrentAIPlayer->airNumRCollectors++; if (aiCurrentAIPlayer->NumRCollectorsBeingBuilt > 0) aiCurrentAIPlayer->NumRCollectorsBeingBuilt--; if (aiuResourceFeatureEnabled(AIR_ACTIVE_RESOURCE_COLLECTION)) { //add the resource collector to an existing team, or make a new team team = aitFindNextTeamWithFlag(NULL, RESOURCECOLLECT_TEAM); while (team) { if (team->shipList.selection->numShips < 3) { aitAddShip(team, ship); break; } team = aitFindNextTeamWithFlag(team, RESOURCECOLLECT_TEAM); } if (!team) { team = aiCurrentAIPlayer->supportTeam[new_team_num] = aitCreate(ResourceTeam); bitSet(aiCurrentAIPlayer->supportTeam[new_team_num]->teamFlags, RESOURCECOLLECT_TEAM); growSelectAddShip(&aiCurrentAIPlayer->supportTeam[new_team_num]->shipList, ship); aioCreateResourcer(aiCurrentAIPlayer->supportTeam[new_team_num]); aiCurrentAIPlayer->numSupportTeams++; } if (aiuResourceFeatureEnabled(AIR_AGGRESSIVE_RESOURCING)) { aiuWrapSetTactics(team->shipList.selection, Aggressive); } } else { resource = univFindNearestResource(ship, 0, NULL); selectone.numShips = 1; selectone.ShipPtr[0] = ship; if (aiuResourceFeatureEnabled(AIR_AGGRESSIVE_RESOURCING)) { aiuWrapSetTactics(&selectone, Aggressive); } aiuWrapCollectResource(&selectone,resource); } } bool FindBestPlaceForAdvanceSupportFrigate(vector *destination) { Ship *myMothership = aiCurrentAIPlayer->player->PlayerMothership; Ship *enemyMothership = aiuFindEnemyMothership(aiCurrentAIPlayer->player); vector diff; if (myMothership == NULL) { return FALSE; } if (enemyMothership == NULL) { return FALSE; } vecSub(diff,enemyMothership->posinfo.position,myMothership->posinfo.position); vecMultiplyByScalar(diff,ASF_POSITION_TO_ENEMY_MOTHERSHIP); vecAdd(*destination,myMothership->posinfo.position,diff); return TRUE; } /*----------------------------------------------------------------------------- Name : airAddedResourceController Description : called when a resource controller is added Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void airAddedResourceController(Ship *ship) { SelectCommand selectone; vector destination; udword new_team_num = aiCurrentAIPlayer->numSupportTeams; if (aiuResourceFeatureEnabled(AIR_ACTIVE_RESOURCE_CONTROLLER)) { aiCurrentAIPlayer->supportTeam[new_team_num] = aitCreate(ResourceTeam); growSelectAddShip(&aiCurrentAIPlayer->supportTeam[new_team_num]->shipList, ship); aioCreateActiveSupport(aiCurrentAIPlayer->supportTeam[new_team_num], NULL, SUPPORT_RESOURCE); aiCurrentAIPlayer->numSupportTeams++; } else { if (aiuFindBestResourceBlob(&destination)) { selectone.numShips = 1; selectone.ShipPtr[0] = ship; aiuWrapMove(&selectone,destination); } } aiCurrentAIPlayer->airNumRControllers++; if (aiCurrentAIPlayer->NumRControllersBeingBuilt > 0) aiCurrentAIPlayer->NumRControllersBeingBuilt--; // tell new resource controller to do something here } /*----------------------------------------------------------------------------- Name : airAddedAdvanceSupportFrigate Description : called when a Advance Support Frigate is added Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void airAddedAdvanceSupportFrigate(Ship *ship) { vector destination; SelectCommand selectone; udword new_team_num = aiCurrentAIPlayer->numSupportTeams; if (aiuResourceFeatureEnabled(AIR_ACTIVE_SUPPORT_FRIGATE)) { aiCurrentAIPlayer->supportTeam[new_team_num] = aitCreate(ResourceTeam); growSelectAddShip(&aiCurrentAIPlayer->supportTeam[new_team_num]->shipList, ship); aioCreateActiveSupport(aiCurrentAIPlayer->supportTeam[new_team_num], NULL, SUPPORT_STRIKECRAFT); aiCurrentAIPlayer->numSupportTeams++; } else { if (FindBestPlaceForAdvanceSupportFrigate(&destination)) { selectone.numShips = 1; selectone.ShipPtr[0] = ship; aiuWrapMove(&selectone,destination); } } aiCurrentAIPlayer->airNumASF++; if (aiCurrentAIPlayer->NumASFBeingBuilt > 0) aiCurrentAIPlayer->NumASFBeingBuilt--; } /*----------------------------------------------------------------------------- Name : airAddedResearchShip Description : Inputs : Outputs : Return : void ----------------------------------------------------------------------------*/ void airAddedResearchShip(Ship *ship) { aiCurrentAIPlayer->airNumResearchShips++; if (aiCurrentAIPlayer->NumResearchShipsBeingBuilt > 0) aiCurrentAIPlayer->NumResearchShipsBeingBuilt--; // tell new research ship to do something here } /*----------------------------------------------------------------------------- Name : airAddedProximitySensor Description : Does that proximity sensor adding stuff that we all read about in Newsweek. Proximity Sensor positioning and stuff is taken care of by the defense manager Inputs : ship - the new ship Outputs : Return : void ----------------------------------------------------------------------------*/ void airAddedProximitySensor(ShipPtr ship) { if (!aiCurrentAIPlayer->aidProximitySensors) { aiuNewSelection(aiCurrentAIPlayer->aidProximitySensors, 10, "dproxsens"); } selSelectionAddSingleShip((MaxSelection *)aiCurrentAIPlayer->aidProximitySensors, ship); } /*----------------------------------------------------------------------------- Name : airAddedMothership Description : Adds a mothership team to the support teams Inputs : ship - the new mothership Outputs : Creates a new team Return : void ----------------------------------------------------------------------------*/ void airAddedMothership(ShipPtr ship) { udword new_team_num = aiCurrentAIPlayer->numSupportTeams; if (aiuResourceFeatureEnabled(AIR_ACTIVE_MOTHERSHIP)) { aiCurrentAIPlayer->supportTeam[new_team_num] = aitCreate(ResourceTeam); growSelectAddShip(&aiCurrentAIPlayer->supportTeam[new_team_num]->shipList, ship); aioCreateActiveSupport(aiCurrentAIPlayer->supportTeam[new_team_num], NULL, SUPPORT_MOTHERSHIP); aiCurrentAIPlayer->numSupportTeams++; } } /*----------------------------------------------------------------------------- Name : airAddNewShip Description : Adds newly assigned ships to the shipreserve structure Inputs : newsel - selection of the new ships Outputs : Return : void ----------------------------------------------------------------------------*/ void airAddNewShip(Ship *ship) { growSelectAddShip(&aiCurrentAIPlayer->airResourceReserves, ship); if (ship->shiptype == ResourceCollector) { airAddedResourceCollector(ship); } else if (ship->shiptype == ResourceController) { airAddedResourceController(ship); } else if (ship->shiptype == AdvanceSupportFrigate) { airAddedAdvanceSupportFrigate(ship); } else if (ship->shiptype == ResearchShip) { airAddedResearchShip(ship); } else if (ship->shiptype == ProximitySensor) { airAddedProximitySensor(ship); } else if ((ship->shiptype == Mothership) || (ship->shiptype == Carrier)) { airAddedMothership(ship); } } /*----------------------------------------------------------------------------- Name : aiuCanBuildShipType Description : Returns TRUE if current AI player can build ships of shiptype Inputs : shiptype Outputs : Return : Returns TRUE if current AI player can build ships of shiptype ----------------------------------------------------------------------------*/ bool aiuCanBuildShipType(ShipType shiptype,bool scriptteam) { Ship *playerMothership = aiCurrentAIPlayer->player->PlayerMothership; ShipStaticInfo *teststatic; ShipRace race; if (playerMothership == NULL) { return FALSE; } race = playerMothership->shiprace; if ((RacesAllowedForGivenShip[shiptype] & RaceToRaceBits(race)) == 0) { // Can't build this shiptype for this race return FALSE; } teststatic = GetShipStaticInfo(shiptype,race); if (!bitTest(teststatic->staticheader.infoFlags, IF_InfoLoaded)) { return FALSE; // this ship static info not loaded, so can't build } if ((shiptype == ResourceCollector) && (!singlePlayerGame) && (!bitTest(tpGameCreated.flag,MG_HarvestinEnabled))) { return FALSE; } if (scriptteam) { return TRUE; } else { return cmCanBuildShipType(playerMothership,shiptype,FALSE); // later set to TRUE to take into account research } } /*----------------------------------------------------------------------------- Name : airCheckForDistressSignals Description : Checks all resource ships to make sure they aren't being attacked. If they are, a distress signal is sent to the defense manager Inputs : None Outputs : Creates a selection of distressed ships, if there are any Return : void ----------------------------------------------------------------------------*/ void airCheckForDistressSignals(void) { udword i, numShips = aiCurrentAIPlayer->airResourceReserves.selection->numShips; ShipPtr ship; MaxSelection distressed_ships; distressed_ships.numShips = 0; for (i = 0; i < numShips; i++) { ship = aiCurrentAIPlayer->airResourceReserves.selection->ShipPtr[i]; if (ship->gettingrocked) { selSelectionAddSingleShip(&distressed_ships, ship); } } if (distressed_ships.numShips) { aidSendDistressSignal((SelectCommand *)&distressed_ships); } } /*----------------------------------------------------------------------------- Name : airDivideSupportTasks Description : Divides the support tasks among the support teams Looks through the team array to find any ship with Inputs : none Outputs : Fiddles with the support team structures Return : void ----------------------------------------------------------------------------*/ void airDivideSupportTasks(void) { udword num_teams, num_support_teams, num_teams_per_support, num_teams_leftover,i; udword num_ASF_teams = 0; AITeam *teams[AIPLAYER_NUM_SUPPORTTEAMS]; AITeam *team; num_teams = aitFindNumTeamsWithFlag(TEAM_NEEDS_SUPPORT); num_support_teams = aiCurrentAIPlayer->numSupportTeams; if (!num_teams) { return; } //only get non-resourcecontroller support teams //later fix this to support more than one team needing support //also make AdvanceSupportFrigate support roving guarding teams for (i=0; isupportTeam[i]; if ((team->shipList.selection->numShips) && (aitTeamShipTypeIs(AdvanceSupportFrigate, team))) { teams[num_ASF_teams++] = team; } } if (!num_ASF_teams) { return; } num_teams_per_support = num_teams / num_support_teams; num_teams_leftover = num_teams % num_support_teams; team = aitFindNextTeamWithFlag(NULL, TEAM_NEEDS_SUPPORT); i = 0; while (team && (i < num_ASF_teams)) { aitMakeTeamSupportShips(teams[i++], team->shipList.selection); team = aitFindNextTeamWithFlag(team, TEAM_NEEDS_SUPPORT); } /* if (!num_teams_leftover) { for (i=0;iairNumResearchShips) { if (aiuCanBuildShipType(ResearchShip,FALSE)) { aifResourceManRequestsShipsCB(ResearchShip, 1, REQUESTSHIPS_HIPRI); } } } /*----------------------------------------------------------------------------- Name : airDumbResourcerRequests Description : Uses the ResourcersToBuild variable to determine if a new resourcer needs to be built Inputs : Outputs : Return : Whether or not a new resourcer has been requested at high priority ----------------------------------------------------------------------------*/ bool airDumbResourcerRequests(void) { if (aiCurrentAIPlayer->ResourcersToBuild) { sdword totalRCollectors = aiCurrentAIPlayer->airNumRCollectors + aiCurrentAIPlayer->NumRCollectorsBeingBuilt; if (aiCurrentAIPlayer->ResourcersToBuild > totalRCollectors) { if (aiuCanBuildShipType(ResourceCollector,FALSE)) { aifResourceManRequestsShipsCB(ResourceCollector, aiCurrentAIPlayer->ResourcersToBuild - totalRCollectors, REQUESTSHIPS_HIPRI); return TRUE; } } } else { aiCurrentAIPlayer->ResourcersToBuild = randyrandombetween(RAN_AIPlayer, (2 + (udword)aiCurrentAIPlayer->aiplayerDifficultyLevel), (4 + (udword)aiCurrentAIPlayer->aiplayerDifficultyLevel)); aiplayerLog((aiIndex, "Using Dumb Resourcing - %i Resource Ships", aiCurrentAIPlayer->ResourcersToBuild)); } return FALSE; } /*----------------------------------------------------------------------------- Name : airDumbResearchRequests Description : Uses the Researchers to build variable to determine of a new research ship needs to be built Inputs : Outputs : Return : Whether or not a new research ship has been requested at high priority ----------------------------------------------------------------------------*/ bool airDumbResearchRequests(bool *requestedResearch) { if (aiCurrentAIPlayer->ResearchersToBuild) { sdword totalResearch = aiCurrentAIPlayer->airNumResearchShips + aiCurrentAIPlayer->NumResearchShipsBeingBuilt; if (totalResearch < aiCurrentAIPlayer->ResearchersToBuild) { if (aiuCanBuildShipType(ResearchShip,FALSE)) { if (!totalResearch) { aifResourceManRequestsShipsCB(ResearchShip, 1, REQUESTSHIPS_HIPRI); *requestedResearch = TRUE; return TRUE; } else { aifResourceManRequestsShipsCB(ResearchShip, 1, 0); *requestedResearch = TRUE; } } } } else { //later maybe add a bonehead move variable as well to build up to 6 aiCurrentAIPlayer->ResearchersToBuild = randyrandombetween(RAN_AIPlayer, 1, 3); aiplayerLog((aiIndex, "Using Dumb Research - %i Research Ships", aiCurrentAIPlayer->ResearchersToBuild)); } return FALSE; } /*----------------------------------------------------------------------------- Name : airSmartResearchRequests Description : Uses the technology deficit variable to determine if a new research ship needs to be built Inputs : Outputs : Return : Whether or not a new research ship has been requested at high priority ----------------------------------------------------------------------------*/ bool airSmartResearchRequests(bool *requestedResearch) { if (aiCurrentAIPlayer->TechnologyDeficit > 0) { sdword totalResearch = aiCurrentAIPlayer->airNumResearchShips + aiCurrentAIPlayer->NumResearchShipsBeingBuilt; if ((real32)(totalResearch) < (((real32)(aiCurrentAIPlayer->TechnologyDeficit))/2.0)) { if (aiuCanBuildShipType(ResearchShip,FALSE)) { if (!totalResearch) { aifResourceManRequestsShipsCB(ResearchShip, 1, REQUESTSHIPS_HIPRI); *requestedResearch = TRUE; return TRUE; } else { aifResourceManRequestsShipsCB(ResearchShip, 1, 0); *requestedResearch = TRUE; return FALSE; } } } else { //recalculate technology deficit every time now. aiCurrentAIPlayer->TechnologyDeficit = 0; } } *requestedResearch = FALSE; return FALSE; } /*----------------------------------------------------------------------------- Name : airResourceManager Description : Manages all the resources and non-combat ships for the computer player Inputs : orders - orders from the fleet manager Outputs : sets resource collectors to collect resources and optimizes resource controller and other non-combat ships Return : requests for new ships ----------------------------------------------------------------------------*/ void airResourceManager(void) { sdword cashGenerated; bool requestedSomething = FALSE, requestedResearch = FALSE; // Update continuously updated variables if (((aiCurrentAIPlayer->aiplayerFrameCount & UPDATE_RU_COUNT_RATE) == 0)|| (aiCurrentAIPlayer->airEasilyAccesibleRUsInWorld == -1)) { aiCurrentAIPlayer->airEasilyAccesibleRUsInWorld = NumberOfEasilyAccesibleRUs(aiCurrentAIPlayer->player); } cashGenerated = aiCurrentAIPlayer->player->resourceUnits + aiCurrentAIPlayer->NumRUsSpentOnAttman + aiCurrentAIPlayer->NumRUsSpentOnDefman + aiCurrentAIPlayer->NumRUsSpentOnScriptman; //determine if we should build any research ships if (singlePlayerGame || (!singlePlayerGame && bitTest(tpGameCreated.flag,MG_ResearchEnabled))) { if (aiuResourceFeatureEnabled(AIR_SMART_RESEARCH_SHIP_REQUESTS)) { if (airSmartResearchRequests(&requestedResearch)) { //a high priority request for a research ship has been entered return; } } else { if (airDumbResearchRequests(&requestedResearch)) { //a high priority request for a research ship has been entered return; } } } //determine if we should build any resource collectors if ((singlePlayerGame || (!singlePlayerGame && bitTest(tpGameCreated.flag,MG_HarvestinEnabled))) && (aiCurrentAIPlayer->airEasilyAccesibleRUsInWorld > MIN_WORTHWHILE_RUs)) { sdword totalRCollectors = aiCurrentAIPlayer->airNumRCollectors + aiCurrentAIPlayer->NumRCollectorsBeingBuilt; if (aiuResourceFeatureEnabled(AIR_SMART_COLLECTOR_REQUESTS)) { if (totalRCollectors < MAX_RCOLLECTORS_TO_BUILD) { if (totalRCollectors < aiCurrentAIPlayer->NumRUDockPoints*MAX_RCOLLECTORS_PER_DOCK_POINT) { if ((totalRCollectors == 0) || (aiCurrentAIPlayer->airEasilyAccesibleRUsInWorld > (totalRCollectors+1)*RUs_PER_RCOLLECTOR)) { // see if we have enough cash to make it worthwhile. (The more resource collectors we // already have, the more cash we want before building a new one because the Resource // Manager should always be providing an increasing amount of RU's). // Note that when we say cash, we mean cashGenerated if ((totalRCollectors == 0) || // if this is our first RCollector, we always want to build one (cashGenerated > BUILD_RC_CASH_TABLE_BASE + BUILD_RC_CASH_TABLE_INC * (totalRCollectors-1))) { if (totalRCollectors == 0) { if (aiuCanBuildShipType(ResourceCollector,FALSE)) { aifResourceManRequestsShipsCB(ResourceCollector, 1, REQUESTSHIPS_HIPRI); return; } } else { if (aiuCanBuildShipType(ResourceCollector,FALSE)) { aifResourceManRequestsShipsCB(ResourceCollector, 1, 0); requestedSomething = TRUE; } } } } } } } else { if (airDumbResourcerRequests()) { //a resource collector has been requested at high priority return; } } //determine if we should build any resource controllers if (aiuResourceFeatureEnabled(AIR_RESOURCE_CONTROLLER_REQUESTS) && (totalRCollectors >= MIN_RCOLLECTORS_FOR_CONTROLLER)) { sdword totalRControllers = aiCurrentAIPlayer->airNumRControllers + aiCurrentAIPlayer->NumRControllersBeingBuilt; if ((totalRControllers == 0) && (!requestedResearch)) { if (aiCurrentAIPlayer->airEasilyAccesibleRUsInWorld > (MIN_WORTHWHILE_RUs_FOR_CONTROLLER * universe.numPlayers)) { if (cashGenerated > BUILD_RCONTROLLER_CASH_BASE) { if (aiuCanBuildShipType(ResourceController,FALSE)) { aifResourceManRequestsShipsCB(ResourceController, 1, 0); return; } } } } } } if (!requestedSomething) { //determine if we should build any AdvanceSupportFrigates if (aiuResourceFeatureEnabled(AIR_SUPPORT_FRIGATE_REQUESTS) && ((!singlePlayerGame) && bitTest(tpGameCreated.flag, MG_FuelBurnEnabled)) ) { if (cashGenerated > BUILD_ASF_CASH_BASE) { sdword totalASF = aiCurrentAIPlayer->airNumASF + aiCurrentAIPlayer->NumASFBeingBuilt; if ((totalASF == 0) && aitNeedStrikeSupport(randyrandombetween(RAN_AIPlayer, 10, 15))) { if (aiuCanBuildShipType(AdvanceSupportFrigate,FALSE) && aiuUnitCapCanBuildShip(aiCurrentAIPlayer, AdvanceSupportFrigate, 1)) { aifResourceManRequestsShipsCB(AdvanceSupportFrigate, 1, 0); } } } } } if (aiCurrentAIPlayer->numSupportTeams) { airDivideSupportTasks(); } //Distress signals only work with active guard, //so that feature has to be activated as well if (aiuResourceFeatureEnabled(AIR_RESOURCE_DISTRESS_SIGNALS)) { airCheckForDistressSignals(); } }