/** ** Check what must be built / trained. */ static void AiCheckingWork() { // Supply has the highest priority if (AiPlayer->NeedSupply) { if (AiPlayer->UnitTypeBuilt.empty() || AiPlayer->UnitTypeBuilt[0].Type->Stats[AiPlayer->Player->Index].Variables[SUPPLY_INDEX].Value == 0) { AiPlayer->NeedSupply = false; AiRequestSupply(); } } // Look to the build requests, what can be done. const int sz = AiPlayer->UnitTypeBuilt.size(); for (int i = 0; i < sz; ++i) { AiBuildQueue &queue = AiPlayer->UnitTypeBuilt[AiPlayer->UnitTypeBuilt.size() - sz + i]; CUnitType &type = *queue.Type; bool new_supply = false; // FIXME: must check if requirements are fulfilled. // Buildings can be destroyed. // Check if we have enough food. if (type.Stats[AiPlayer->Player->Index].Variables[DEMAND_INDEX].Value && !AiCheckSupply(*AiPlayer, type)) { AiPlayer->NeedSupply = true; new_supply = true; } // Check limits, AI should be broken if reached. if (queue.Want > queue.Made && AiPlayer->Player->CheckLimits(type) < 0) { continue; } // Check if resources available. const int c = AiCheckUnitTypeCosts(type); if (c) { AiPlayer->NeededMask |= c; // NOTE: we can continue and build things with lesser // resource or other resource need! continue; } else if (queue.Want > queue.Made && queue.Wait <= GameCycle) { if (AiMakeUnit(type, queue.Pos)) { // AiRequestSupply can change UnitTypeBuilt so recalculate queue AiBuildQueue &queue2 = AiPlayer->UnitTypeBuilt[AiPlayer->UnitTypeBuilt.size() - sz + i]; ++queue2.Made; queue2.Wait = 0; } else if (queue.Type->Building) { // Finding a building place is costly, don't try again for a while if (queue.Wait == 0) { queue.Wait = GameCycle + 150; } else { queue.Wait = GameCycle + 450; } } } if (new_supply) { // trigger this last, because it may re-arrange the queue and invalidate our queue item AiRequestSupply(); } } }
/** ** Check if the upgrade-to can be done. ** ** @param type FIXME: docu */ void AiAddUpgradeToRequest(CUnitType &type) { // Check if resources are available. const int resourceNeeded = AiCheckUnitTypeCosts(type); if (resourceNeeded) { AiPlayer->NeededMask |= resourceNeeded; return; } if (AiPlayer->Player->CheckLimits(type) < 0) { return; } // // Check if we have a place for the upgrade to. // const int n = AiHelpers.Upgrade.size(); std::vector<std::vector<CUnitType *> > &tablep = AiHelpers.Upgrade; if (type.Slot > n) { // Oops not known. DebugPrint("%d: AiAddUpgradeToRequest I: Nothing known about `%s'\n" _C_ AiPlayer->Player->Index _C_ type.Ident.c_str()); return; } std::vector<CUnitType *> &table = tablep[type.Slot]; if (table.empty()) { // Oops not known. DebugPrint("%d: AiAddUpgradeToRequest II: Nothing known about `%s'\n" _C_ AiPlayer->Player->Index _C_ type.Ident.c_str()); return; } const int *unit_count = AiPlayer->Player->UnitTypesCount; for (unsigned int i = 0; i < table.size(); ++i) { // // The type is available // if (unit_count[table[i]->Slot]) { if (AiUpgradeTo(*table[i], type)) { return; } } } }
/** ** Assign worker to gather a certain resource. ** ** @param unit pointer to the unit. ** @param resource resource identification. ** ** @return 1 if the worker was assigned, 0 otherwise. */ static int AiAssignHarvester(CUnit &unit, int resource) { ResourceInfo *resinfo; // It can't. if (unit.Removed) { return 0; } resinfo = unit.Type->ResInfo[resource]; Assert(resinfo); if (resinfo->TerrainHarvester) { Vec2i forestPos; // // Code for terrain harvesters. Search for piece of terrain to mine. // if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 1000, unit.Player, unit.tilePos, &forestPos)) { CommandResourceLoc(unit, forestPos, FlushCommands); return 1; } // Ask the AI to explore... AiExplore(unit.tilePos, MapFieldLandUnit); } else { int exploremask = 0; // // Find a resource to harvest from. // CUnit *dest = UnitFindResource(unit, unit.tilePos.x, unit.tilePos.y, 1000, resource, true); if (dest) { //FIXME: rb - when workers can speedup building then such assign may be ok. //if(dest->CurrentAction() == UnitActionBuilt) //CommandBuildBuilding(unit, dest->tilePos, dest->Type, FlushCommands); //else CommandResource(unit, *dest, FlushCommands); return 1; } #if 0 //may this code touch needmask which will be reseted before next cycle if (resinfo->RefineryHarvester && (dest = UnitFindMiningArea(unit, unit.X, unit.Y, 1000, resource))) { int needmask; //int counter[UnitTypeMax]; // // Count the already made build requests. // //AiGetBuildRequestsCount(counter); int n = AiHelpers.Refinery[resource - 1].size(); for (int i = 0; i < n; ++i) { CUnitType *type = AiHelpers.Refinery[resource - 1][i]; //if (counter[type->Slot]) { // Already ordered. // return 0; //} if (!AiRequestedTypeAllowed(AiPlayer->Player, type)) { continue; } // // Check if resources available. // needmask = AiCheckUnitTypeCosts(type); if(!needmask && AiMakeUnit(type)) { AiBuildQueue newqueue; newqueue.Type = type; newqueue.Want = 1; newqueue.Made = 1; newqueue.X = dest->X; newqueue.Y = dest->Y; AiPlayer->UnitTypeBuilt.insert( AiPlayer->UnitTypeBuilt.begin(), newqueue); return 0; } } return 0; } #endif for (std::vector<CUnitType *>::iterator i = UnitTypes.begin(); i != UnitTypes.end(); i++) { if (*i && (*i)->GivesResource == resource) { switch ((*i)->UnitType) { case UnitTypeLand: exploremask |= MapFieldLandUnit; break; case UnitTypeFly: exploremask |= MapFieldAirUnit; break; case UnitTypeNaval: exploremask |= MapFieldSeaUnit; break; default: Assert(0); } } } // Ask the AI to explore AiExplore(unit.tilePos, exploremask); } // Failed. return 0; }
/** ** Build new units to reduce the food shortage. */ static bool AiRequestSupply() { // Don't request supply if we're sleeping. When the script starts it may // request a better unit than the one we pick here. If we only have enough // resources for one unit we don't want to build the wrong one. if (AiPlayer->SleepCycles != 0) { /* we still need supply */ return true; } // Count the already made build requests. int counter[UnitTypeMax]; AiGetBuildRequestsCount(AiPlayer, counter); struct cnode cache[16]; memset(cache, 0, sizeof(cache)); // // Check if we can build this? // int j = 0; const int n = AiHelpers.UnitLimit[0].size(); for (int i = 0; i < n; ++i) { CUnitType &type = *AiHelpers.UnitLimit[0][i]; if (counter[type.Slot]) { // Already ordered. #if defined(DEBUG) && defined(DebugRequestSupply) DebugPrint("%d: AiRequestSupply: Supply already build in %s\n" _C_ AiPlayer->Player->Index _C_ type->Name.c_str()); #endif return false; } if (!AiRequestedTypeAllowed(AiPlayer->Player, type)) { continue; } // // Check if resources available. // cache[j].needmask = AiCheckUnitTypeCosts(type); for (int c = 1; c < MaxCosts; ++c) { cache[j].unit_cost += type.Stats[AiPlayer->Player->Index].Costs[c]; } cache[j].unit_cost += type.Supply - 1; cache[j].unit_cost /= type.Supply; cache[j++].type = &type; Assert(j < 16); } if (j > 1) qsort(&cache, j, sizeof (struct cnode), cnode_cmp); if (j) { if (!cache[0].needmask) { CUnitType &type = *cache[0].type; if (AiMakeUnit(type, -1, -1)) { AiBuildQueue newqueue; newqueue.Type = &type; newqueue.Want = 1; newqueue.Made = 1; AiPlayer->UnitTypeBuilt.insert( AiPlayer->UnitTypeBuilt.begin(), newqueue); #if defined( DEBUG) && defined( DebugRequestSupply ) DebugPrint("%d: AiRequestSupply: build Supply in %s\n" _C_ AiPlayer->Player->Index _C_ type->Name.c_str()); #endif return false; } } AiPlayer->NeededMask |= cache[0].needmask; } #if defined( DEBUG) && defined( DebugRequestSupply ) std::string needed(""); for (int i = 1; i < MaxCosts; ++i) { if (cache[0].needmask & (1 << i)) { needed += ":"; switch (i) { case GoldCost: needed += "Gold<"; break; case WoodCost: needed += "Wood<"; break; case OilCost: needed += "Oil<"; break; default: needed += "unknown<"; break; } needed += '0' + i; needed += ">"; } } DebugPrint("%d: AiRequestSupply: needed build %s with %s resource\n" _C_ AiPlayer->Player->Index _C_ cache[0].type->Name.c_str() _C_ needed.c_str()); #endif return true; }