/** ** Assign workers to collect resources. ** ** If we have a shortage of a resource, let many workers collecting this. ** If no shortage, split workers to all resources. */ static void AiCollectResources() { std::vector<CUnit *> units_assigned[MaxCosts]; // Worker assigned to resource std::vector<CUnit *> units_unassigned[MaxCosts]; // Unassigned workers int num_units_with_resource[MaxCosts]; int num_units_assigned[MaxCosts]; int num_units_unassigned[MaxCosts]; int percent[MaxCosts]; int priority_resource[MaxCosts]; int priority_needed[MaxCosts]; int wanted[MaxCosts]; int total_harvester = 0; memset(num_units_with_resource, 0, sizeof(num_units_with_resource)); memset(num_units_unassigned, 0, sizeof(num_units_unassigned)); memset(num_units_assigned, 0, sizeof(num_units_assigned)); // Collect statistics about the current assignment const int n = AiPlayer->Player->GetUnitCount(); for (int i = 0; i < n; ++i) { CUnit &unit = AiPlayer->Player->GetUnit(i); if (!unit.Type->Harvester) { continue; } // See if it's assigned already if (unit.Orders.size() == 1 && unit.CurrentAction() == UnitActionResource) { const COrder_Resource &order = *static_cast<COrder_Resource *>(unit.CurrentOrder()); const int c = order.GetCurrentResource(); units_assigned[c].push_back(&unit); num_units_assigned[c]++; total_harvester++; continue; } // Ignore busy units. ( building, fighting, ... ) if (!unit.IsIdle()) { continue; } // Send workers with resources back home. if (unit.ResourcesHeld) { const int c = unit.CurrentResource; num_units_with_resource[c]++; CommandReturnGoods(unit, 0, FlushCommands); total_harvester++; continue; } // Look what the unit can do for (int c = 1; c < MaxCosts; ++c) { if (unit.Type->ResInfo[c]) { units_unassigned[c].push_back(&unit); num_units_unassigned[c]++; } } ++total_harvester; } if (!total_harvester) { return; } memset(wanted, 0, sizeof(wanted)); int percent_total = 100; for (int c = 1; c < MaxCosts; ++c) { percent[c] = AiPlayer->Collect[c]; if ((AiPlayer->NeededMask & (1 << c))) { // Double percent if needed percent_total += percent[c]; percent[c] <<= 1; } } // Turn percent values into harvester numbers. for (int c = 1; c < MaxCosts; ++c) { if (percent[c]) { // Wanted needs to be representative. if (total_harvester < 5) { wanted[c] = 1 + (percent[c] * 5) / percent_total; } else { wanted[c] = 1 + (percent[c] * total_harvester) / percent_total; } } } // Initialise priority & mapping for (int c = 0; c < MaxCosts; ++c) { priority_resource[c] = c; priority_needed[c] = wanted[c] - num_units_assigned[c] - num_units_with_resource[c]; if (c && num_units_assigned[c] > 1) { //first should go workers with lower ResourcesHeld value std::sort(units_assigned[c].begin(), units_assigned[c].end(), CmpWorkers); } } CUnit *unit; do { // sort resources by priority for (int i = 0; i < MaxCosts; ++i) { for (int j = i + 1; j < MaxCosts; ++j) { if (priority_needed[j] > priority_needed[i]) { std::swap(priority_needed[i], priority_needed[j]); std::swap(priority_resource[i], priority_resource[j]); } } } unit = NULL; // Try to complete each ressource in the priority order for (int i = 0; i < MaxCosts; ++i) { int c = priority_resource[i]; // If there is a free worker for c, take it. if (num_units_unassigned[c]) { // Take the unit. while (0 < num_units_unassigned[c] && !AiAssignHarvester(*units_unassigned[c][0], c)) { // can't assign to c => remove from units_unassigned ! units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]]; units_unassigned[c].pop_back(); } // unit is assigned if (0 < num_units_unassigned[c]) { unit = units_unassigned[c][0]; units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]]; units_unassigned[c].pop_back(); // remove it from other ressources for (int j = 0; j < MaxCosts; ++j) { if (j == c || !unit->Type->ResInfo[j]) { continue; } for (int k = 0; k < num_units_unassigned[j]; ++k) { if (units_unassigned[j][k] == unit) { units_unassigned[j][k] = units_unassigned[j][--num_units_unassigned[j]]; units_unassigned[j].pop_back(); break; } } } } } // Else : Take from already assigned worker with lower priority. if (!unit) { // Take from lower priority only (i+1). for (int j = i + 1; j < MaxCosts && !unit; ++j) { // Try to move worker from src_c to c const int src_c = priority_resource[j]; // Don't complete with lower priority ones... if (wanted[src_c] > wanted[c] || (wanted[src_c] == wanted[c] && num_units_assigned[src_c] <= num_units_assigned[c] + 1)) { continue; } for (int k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) { unit = units_assigned[src_c][k]; Assert(unit->CurrentAction() == UnitActionResource); COrder_Resource &order = *static_cast<COrder_Resource *>(unit->CurrentOrder()); if (order.IsGatheringFinished()) { //worker returning with resource continue; } // unit can't harvest : next one if (!unit->Type->ResInfo[c] || !AiAssignHarvester(*unit, c)) { unit = NULL; continue; } // Remove from src_c units_assigned[src_c][k] = units_assigned[src_c][--num_units_assigned[src_c]]; units_assigned[src_c].pop_back(); // j need one more priority_needed[j]++; } } } // We just moved an unit. Adjust priority & retry if (unit) { // i got a new unit. priority_needed[i]--; // Recompute priority now break; } } } while (unit); // Unassigned units there can't be assigned ( ie : they can't move to ressource ) // IDEA : use transporter here. }
/** ** Assign workers to collect resources. ** ** If we have a shortage of a resource, let many workers collecting this. ** If no shortage, split workers to all resources. */ static void AiCollectResources() { CUnit *units_with_resource[MaxCosts][UnitMax]; // Worker with resource CUnit *units_assigned[MaxCosts][UnitMax]; // Worker assigned to resource CUnit *units_unassigned[MaxCosts][UnitMax]; // Unassigned workers int num_units_with_resource[MaxCosts]; int num_units_assigned[MaxCosts]; int num_units_unassigned[MaxCosts]; int c; int src_c; int i; int j; int k; int n; CUnit **units; int percent[MaxCosts]; int percent_total; int priority_resource[MaxCosts]; int priority_needed[MaxCosts]; int wanted[MaxCosts]; int total_harvester; memset(num_units_with_resource, 0, sizeof(num_units_with_resource)); memset(num_units_unassigned, 0, sizeof(num_units_unassigned)); memset(num_units_assigned, 0, sizeof(num_units_assigned)); // // Collect statistics about the current assignment // total_harvester = 0; n = AiPlayer->Player->TotalNumUnits; units = AiPlayer->Player->Units; for (i = 0; i < n; ++i) { CUnit &unit = *units[i]; if (!unit.Type->Harvester) { continue; } c = unit.CurrentResource; // // See if it's assigned already // if (c && unit.OrderCount == 1 && unit.CurrentAction() == UnitActionResource) { units_assigned[c][num_units_assigned[c]++] = &unit; total_harvester++; continue; } // // Ignore busy units. ( building, fighting, ... ) // if (!unit.IsIdle()) { continue; } // // Send workers with resources back home. // if (unit.ResourcesHeld && c) { units_with_resource[c][num_units_with_resource[c]++] = &unit; CommandReturnGoods(unit, 0, FlushCommands); total_harvester++; continue; } // // Look what the unit can do // for (c = 1; c < MaxCosts; ++c) { if (unit.Type->ResInfo[c]) { units_unassigned[c][num_units_unassigned[c]++] = &unit; } } ++total_harvester; } if(!total_harvester) { return; } memset(wanted, 0, sizeof(wanted)); percent_total = 100; for (c = 1; c < MaxCosts; ++c) { percent[c] = AiPlayer->Collect[c]; if ((AiPlayer->NeededMask & (1 << c))) { // Double percent if needed percent_total += percent[c]; percent[c] <<= 1; } } // // Turn percent values into harvester numbers. // for (c = 1; c < MaxCosts; ++c ) { if(percent[c]) { // Wanted needs to be representative. if (total_harvester < 5) { wanted[c] = 1 + (percent[c] * 5) / percent_total; } else { wanted[c] = 1 + (percent[c] * total_harvester) / percent_total; } } } // // Initialise priority & mapping // for (c = 0; c < MaxCosts; ++c) { priority_resource[c] = c; priority_needed[c] = wanted[c] - num_units_assigned[c] - num_units_with_resource[c]; if (c && num_units_assigned[c] > 1) { //first should go workers with lower ResourcesHeld value qsort(units_assigned[c], num_units_assigned[c], sizeof(CUnit*), CmpWorkers); } } CUnit* unit; do { // // sort resources by priority // for (i = 0; i < MaxCosts; ++i) { for (j = i + 1; j < MaxCosts; ++j) { if (priority_needed[j] > priority_needed[i]) { c = priority_needed[j]; priority_needed[j] = priority_needed[i]; priority_needed[i] = c; c = priority_resource[j]; priority_resource[j] = priority_resource[i]; priority_resource[i] = c; } } } unit = NoUnitP; // // Try to complete each ressource in the priority order // for (i = 0; i < MaxCosts; ++i) { c = priority_resource[i]; // // If there is a free worker for c, take it. // if (num_units_unassigned[c]) { // Take the unit. j = 0; while (j < num_units_unassigned[c] && !AiAssignHarvester(*units_unassigned[c][j], c)) { // can't assign to c => remove from units_unassigned ! units_unassigned[c][j] = units_unassigned[c][--num_units_unassigned[c]]; } // unit is assigned if (j < num_units_unassigned[c]) { unit = units_unassigned[c][j]; units_unassigned[c][j] = units_unassigned[c][--num_units_unassigned[c]]; // remove it from other ressources for (j = 0; j < MaxCosts; ++j) { if (j == c || !unit->Type->ResInfo[j]) { continue; } for (k = 0; k < num_units_unassigned[j]; ++k) { if (units_unassigned[j][k] == unit) { units_unassigned[j][k] = units_unassigned[j][--num_units_unassigned[j]]; break; } } } } } // // Else : Take from already assigned worker with lower priority. // if (!unit) { // Take from lower priority only (i+1). for (j = i + 1; j < MaxCosts && !unit; ++j) { // Try to move worker from src_c to c src_c = priority_resource[j]; // Don't complete with lower priority ones... if (wanted[src_c] > wanted[c] || (wanted[src_c] == wanted[c] && num_units_assigned[src_c] <= num_units_assigned[c] + 1)) { continue; } for (k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) { unit = units_assigned[src_c][k]; if (unit->SubAction >= 65 /* SUB_STOP_GATHERING */ ) { //worker returning with resource continue; } // unit can't harvest : next one if (!unit->Type->ResInfo[c] || !AiAssignHarvester(*unit, c)) { unit = NoUnitP; continue; } // Remove from src_c units_assigned[src_c][k] = units_assigned[src_c][--num_units_assigned[src_c]]; // j need one more priority_needed[j]++; } } } // // We just moved an unit. Adjust priority & retry // if (unit) { // i got a new unit. priority_needed[i]--; // Add to the assigned units_assigned[c][num_units_assigned[c]++] = unit; // Recompute priority now break; } } } while (unit); // Unassigned units there can't be assigned ( ie : they can't move to ressource ) // IDEA : use transporter here. }
/** ** Assign workers to collect resources. ** ** If we have a shortage of a resource, let many workers collecting this. ** If no shortage, split workers to all resources. */ static void AiCollectResources() { std::vector<CUnit *> units_assigned[MaxCosts]; // Worker assigned to resource std::vector<CUnit *> units_unassigned[MaxCosts]; // Unassigned workers int num_units_with_resource[MaxCosts]; int num_units_assigned[MaxCosts]; int num_units_unassigned[MaxCosts]; int percent[MaxCosts]; int priority_resource[MaxCosts]; int priority_needed[MaxCosts]; int wanted[MaxCosts]; int total_harvester = 0; memset(num_units_with_resource, 0, sizeof(num_units_with_resource)); memset(num_units_unassigned, 0, sizeof(num_units_unassigned)); memset(num_units_assigned, 0, sizeof(num_units_assigned)); // Collect statistics about the current assignment const int n = AiPlayer->Player->GetUnitCount(); for (int i = 0; i < n; ++i) { CUnit &unit = AiPlayer->Player->GetUnit(i); //Wyrmgus start // if (!unit.Type->BoolFlag[HARVESTER_INDEX].value) { if (!unit.Type->BoolFlag[HARVESTER_INDEX].value || !unit.Active) { //Wyrmgus end continue; } // See if it's assigned already if (unit.Orders.size() == 1 && unit.CurrentAction() == UnitActionResource) { const COrder_Resource &order = *static_cast<COrder_Resource *>(unit.CurrentOrder()); const int c = order.GetCurrentResource(); units_assigned[c].push_back(&unit); num_units_assigned[c]++; total_harvester++; continue; } // Ignore busy units. ( building, fighting, ... ) if (!unit.IsIdle()) { continue; } // Send workers with resources back home. if (unit.ResourcesHeld) { const int c = unit.CurrentResource; num_units_with_resource[c]++; CommandReturnGoods(unit, 0, FlushCommands); total_harvester++; continue; } // Look what the unit can do for (int c = 1; c < MaxCosts; ++c) { if (unit.Type->ResInfo[c]) { units_unassigned[c].push_back(&unit); num_units_unassigned[c]++; } } ++total_harvester; } if (!total_harvester) { return; } memset(wanted, 0, sizeof(wanted)); int percent_total = 100; for (int c = 1; c < MaxCosts; ++c) { percent[c] = AiPlayer->Collect[c]; if ((AiPlayer->NeededMask & (1 << c))) { // Double percent if needed percent_total += percent[c]; percent[c] <<= 1; } } // Turn percent values into harvester numbers. for (int c = 1; c < MaxCosts; ++c) { if (percent[c]) { // Wanted needs to be representative. if (total_harvester < 5) { wanted[c] = 1 + (percent[c] * 5) / percent_total; } else { wanted[c] = 1 + (percent[c] * total_harvester) / percent_total; } } } // Initialise priority & mapping for (int c = 0; c < MaxCosts; ++c) { priority_resource[c] = c; priority_needed[c] = wanted[c] - num_units_assigned[c] - num_units_with_resource[c]; if (c && num_units_assigned[c] > 1) { //first should go workers with lower ResourcesHeld value std::sort(units_assigned[c].begin(), units_assigned[c].end(), CmpWorkers); } } CUnit *unit; do { // sort resources by priority for (int i = 0; i < MaxCosts; ++i) { for (int j = i + 1; j < MaxCosts; ++j) { if (priority_needed[j] > priority_needed[i]) { std::swap(priority_needed[i], priority_needed[j]); std::swap(priority_resource[i], priority_resource[j]); } } } unit = NULL; // Try to complete each resource in the priority order for (int i = 0; i < MaxCosts; ++i) { int c = priority_resource[i]; // If there is a free worker for c, take it. if (num_units_unassigned[c]) { // Take the unit. while (0 < num_units_unassigned[c] && !AiAssignHarvester(*units_unassigned[c][0], c)) { // can't assign to c => remove from units_unassigned ! units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]]; units_unassigned[c].pop_back(); } // unit is assigned if (0 < num_units_unassigned[c]) { unit = units_unassigned[c][0]; units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]]; units_unassigned[c].pop_back(); // remove it from other ressources for (int j = 0; j < MaxCosts; ++j) { if (j == c || !unit->Type->ResInfo[j]) { continue; } for (int k = 0; k < num_units_unassigned[j]; ++k) { if (units_unassigned[j][k] == unit) { units_unassigned[j][k] = units_unassigned[j][--num_units_unassigned[j]]; units_unassigned[j].pop_back(); break; } } } } } // Else : Take from already assigned worker with lower priority. if (!unit) { // Take from lower priority only (i+1). for (int j = i + 1; j < MaxCosts && !unit; ++j) { // Try to move worker from src_c to c const int src_c = priority_resource[j]; //Wyrmgus start //don't reassign workers from one resource to another, that is too expensive performance-wise (this could be re-implemented if the AI is altered to keep track of found resource spots break; //Wyrmgus end //Wyrmgus start // don't reassign if the src_c resource has no workers, or if the new resource has 0 "wanted" if (num_units_assigned[src_c] == 0 || !wanted[c]) { continue; } //Wyrmgus end // Don't complete with lower priority ones... //Wyrmgus start /* if (wanted[src_c] > wanted[c] || (wanted[src_c] == wanted[c] && num_units_assigned[src_c] <= num_units_assigned[c] + 1)) { */ if (wanted[src_c] && ((num_units_assigned[src_c] + 1) * 100 / wanted[src_c]) <= (num_units_assigned[c] * 100 / wanted[c])) { // what matters is the percent of "wanted" fulfilled, not the absolute quantity of needed workers for that resource; add one worker to num_units_assigned[src_c] so that you won't get one worker being shuffled back and forth //Wyrmgus end //Wyrmgus start // continue; if (num_units_assigned[c] != 0) { // if there are no workers for that resource, allow reshuffling regardless of proportion calculation, so that if the game starts with few workers (like two), the AI isn't stuck gathering only the first resource it finds continue; } //Wyrmgus end } //Wyrmgus start if (num_units_assigned[src_c] <= wanted[src_c]) { // don't reassign if the src_c resource already has less workers than desired if (num_units_assigned[c] != 0) { continue; } } //Wyrmgus end //Wyrmgus start // for (int k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) { for (int k = num_units_assigned[src_c] - 1; k >= 0; --k) { // unit may be NULL now, continue instead of breaking loop if so //Wyrmgus end unit = units_assigned[src_c][k]; //Wyrmgus start if (!unit) { continue; } //Wyrmgus end Assert(unit->CurrentAction() == UnitActionResource); COrder_Resource &order = *static_cast<COrder_Resource *>(unit->CurrentOrder()); if (order.IsGatheringFinished()) { //worker returning with resource continue; } //Wyrmgus start if (unit->Removed || unit->CurrentAction() == UnitActionBuild) { //if unit is removed or is currently building something, it can't be told to harvest (do this here so that AiAssignHarvester returning false later on is only because of unit type not being able to harvest something) unit = NULL; continue; } //Wyrmgus end // unit can't harvest : next one if (!unit->Type->ResInfo[c] || !AiAssignHarvester(*unit, c)) { unit = NULL; continue; } // Remove from src_c units_assigned[src_c][k] = units_assigned[src_c][--num_units_assigned[src_c]]; units_assigned[src_c].pop_back(); // j need one more priority_needed[j]++; //Wyrmgus start break; //only reassign one worker per time //Wyrmgus end } } } // We just moved an unit. Adjust priority & retry if (unit) { // i got a new unit. priority_needed[i]--; // Recompute priority now break; } } } while (unit); // Unassigned units there can't be assigned ( ie : they can't move to ressource ) // IDEA : use transporter here. }