void BuildHandler::AddTask (aiTask *task) { tasks.add(task); if (task->resourceType>=0) ai->taskHandlers[task->resourceType]->AddTask (task); BuildTable::UDef *cd = buildTable.GetCachedDef (task->def->id); if (cd->IsBuilding ()) { task->flags |= BT_BUILDING; if (task->pos.x >= 0.0f) buildMap.Mark (task->def,task->pos,true); } if (cd->IsBuilder ()) task->MarkBuilder (); if (task->resourceType < 0) { TakeResources (ai->ResourceValue (task->def->energyCost, task->def->metalCost)); task->MarkAllocated (); } task->startFrame=ai->cb->GetCurrentFrame(); currentUnitBuilds[task->def->id-1] ++; ChatDebugPrintf (ai->cb, "Added task: %s (%s)", task->def->name.c_str(), task->resourceType >= 0 ? ai->taskHandlers[task->resourceType]->GetName() : "BuildHandler"); }
void BuildTask::Initialize (CGlobals *g) { if (resourceType>=0) { TaskFactory *tf = g->taskFactories [resourceType]; tf->AddTask (this); isAllocated = false; } else { g->resourceManager->TakeResources (g->resourceManager->ResourceValue (def->energyCost,def->metalCost)); isAllocated=true; } BuildTable::UDef *cd = buildTable.GetCachedDef (def->id); if (cd->IsBuilding ()) { flags |= BT_BUILDING; // Find a build position SetupBuildLocation (g); } if (cd->IsBuilder ()) MarkBuilder (); g->taskManager->currentUnitBuilds[def->id-1] ++; }
bool BuildHandler::DoInitialBuildOrders () { // initial build orders completed? assert (config.InitialOrders); if (initialBuildOrderTask) return true; for (int a=0;a<initialBuildOrderState.size();a++) { int &state = initialBuildOrderState[a]; if (state < config.InitialOrders->builds[a]->count) { int type=-1; int id=config.InitialOrders->builds[a]->def; BuildTable::UDef *cd = buildTable.GetCachedDef(id); CfgList* info = config.InitialOrders->builds[a]->info; const char *handler = info ? info->GetLiteral ("Handler") : 0; if (handler) { for (int h=0;h<NUM_TASK_TYPES;h++) { if(!STRCASECMP(handler, handlerStr[h])) { type=h; break; } } if (h == BTF_Force) { ChatMsgPrintf (ai->cb, "Error: No force units possible in the initial build orders.\n"); state=config.InitialOrders->builds[a]->count; return false; } } else { if(!cd->IsBuilder()) { ChatMsgPrintf (ai->cb, "Error in initial buildorder list: unit %s is not a builder\n", cd->name.c_str()); state=config.InitialOrders->builds[a]->count; return false; } } aiTask *t = initialBuildOrderTask = AddTask (buildTable.GetDef(id), type); t->MarkAllocated (); state ++; return true; } } // all initial build tasks are done if (!initialBuildOrdersFinished) { ChatDebugPrintf (ai->cb,"Initial build orders finished."); DistributeResources (); initialBuildOrdersFinished=true; } return false; }
void BuildHandler::InitBuilderTypes (const UnitDef* commdr) { for (int a=0;a<buildTable.numDefs;a++) { BuildTable::UDef *d = &buildTable.deflist [a]; // Builder? if (!d->IsBuilder() || d->IsShip()) continue; // Can it be build directly or indirectly? BuildPathNode& bpn = buildTable.GetBuildPath (commdr->id, a); if (bpn.id >= 0) builderTypes.push_back (a + 1); } }
// default version finds a build position in a safe sector that is not filled. bool BuildTask::InitBuildPos (CGlobals *g) { BuildTable::UDef* cd = buildTable.GetCachedDef (def->id); // Find a sector to build in float3 st (g->map->baseCenter.x, 0.0f, g->map->baseCenter.y); int2 sector = g->map->FindSafeBuildingSector (st, aiConfig.safeSectorRadius, cd->IsBuilder () ? BLD_FACTORY : 1); if (sector.x < 0) return false; // find a position for the unit float3 tpos = float3(sector.x+0.5f,0.0f,sector.y+0.5f)*g->map->mblocksize; if(!g->buildmap->FindBuildPosition (def, tpos, pos)) return false; return true; }
bool BuildHandler::FindTaskBuildPos (aiTask *t, BuildUnit*lead) { float3 pos; BuildTable::UDef* cd = buildTable.GetCachedDef (t->def->id); float3 builderPos = ai->cb->GetUnitPos (lead->id); // Find a sector to build in if (t->def->extractsMetal > 0) { while(1) { // Find an empty metal spot float3 st (ai->map.baseCenter.x, 0.0f, ai->map.baseCenter.y); int2 msect = ai->metalmap.GetEmptySpot (st, &ai->map, false); if(msect.x < 0) { // no spots left return false; } MetalSpotInfo *info = ai->metalmap.Get (msect.x,msect.y); pos = float3(info->spotpos.x, 0.0f, info->spotpos.y) * SQUARE_SIZE; if (buildMap.FindBuildPosition (t->def, pos, t->pos)) { ai->metalmap.MarkSpot (msect, true); pos = t->pos; // logPrintf ("Estimated metal production of extractor: %f\n", def->extractsMetal * info->metalProduction); break; } ai->metalmap.MarkSpot (msect, true);// spill the metalspot as it seems it can't be used anyway } } else // it's a non-metal extractor { int2 sector = ai->map.RequestSafeBuildingSector (builderPos, aiConfig.safeSectorRadius, cd->IsBuilder () ? BLD_FACTORY : 1); if (sector.x < 0) return false; // find a position for the unit pos = float3(sector.x+0.5f,0.0f,sector.y+0.5f)*ai->map.mblocksize; if(!buildMap.FindBuildPosition (t->def, pos, t->pos)) return false; pos = t->pos; } // Mark the unit on the buildmap buildMap.Mark (t->def, pos, true); return true; }
void BuildHandler::OrderNewBuilder() { float bestScore; int best=0; for (int a=0;a<builderTypes.size();a++) { assert (builderTypes[a]); BuildTable::UDef *cd = buildTable.GetCachedDef (builderTypes[a]); if (cd->IsBuilding()) continue; // Find the best unit we can use to build this builder const vector<int>& bb = *cd->buildby; int numBuilders=0; for (int i=0;i<bb.size();i++) { int n = currentBuilders [bb[i]]; numBuilders += abs(n) + (n<0)?2:0; // an inactive builder equals 2 extra active builders } float score = std::min((1+numBuilders),3) * sqrtf(cd->numBuildOptions) * cd->buildSpeed / ai->ResourceValue(cd->cost); if (!best || score > bestScore) { best = builderTypes[a]; bestScore = score; } } if (best) { aiTask *t = AddTask (buildTable.GetDef (best),-1); ChatDebugPrintf (ai->cb, "Added task to increase build speed: %s\n", t->def->name.c_str()); } }
void BuildHandler::InitiateTask (aiTask *task) { BuildUnit *lead = 0; assert (!task->constructors.empty()); assert (!task->lead); // Find a new lead builder for (int a=0;a<task->constructors.size();a++) { BuildUnit *b = task->constructors [a]; if (task->constructors[a]->tasks.front () != task) continue; if (buildTable.UnitCanBuild (b->def, task->def)) { lead = b; break; } } if (!lead) { for (int a=0;a<task->constructors.size();a++) { BuildUnit *u = task->constructors[a]; task->DeleteDeathDependence (u); u->DependentDied (task); u->DeleteDeathDependence (task); } task->constructors.clear(); task->buildSpeed = 0.0f; return; } float3 builderPos = ai->cb->GetUnitPos (lead->id); Command c; BuildTable::UDef* cd = buildTable.GetCachedDef (task->def->id); if (cd->IsBuilding()) { if (!FindTaskBuildPos (task,lead)) return; c.params.resize(3); c.params[0]=task->pos.x; c.params[1]=task->pos.y; c.params[2]=task->pos.z; MapInfo *i = ai->map.GetMapInfo (task->pos); if (i)i->buildstate += cd->IsBuilder () ? BLD_FACTORY : 1; if (aiConfig.debug) ai->cb->DrawUnit (task->def->name.c_str(), task->pos, 0.0f, 800, ai->cb->GetMyTeam(), true, true); } else task->pos = builderPos; c.id = -task->def->id; ai->cb->GiveOrder (lead->id, &c); task->lead = lead; }
void BuildHandler::FindNewJob (BuildUnit *u) { BuildTable::UDef *cd = buildTable.GetCachedDef (u->def->id); int curFrame=ai->cb->GetCurrentFrame(); if (cd->IsBuilding ()) { // factory - find a task that is not yet initialized for (int a=0;a<tasks.size();a++) { aiTask *t = tasks[a]; if (!t->depends && t->constructors.empty () && !t->IsBuilding() && buildTable.UnitCanBuild (u->def,t->def)) { SetBuilderToTask (u, t); return; } } } else { // mobile builder float3 builderPos = ai->cb->GetUnitPos(u->id); const UnitDef *ud = u->def; aiTask *best=0; float bestScore; float moveSpeed = ai->cb->GetUnitSpeed (u->id); for(int a=0;a<tasks.size();a++) { aiTask *t = tasks[a]; if (t->depends || !t->AreResourcesAllocated()) continue; float hp = t->unit ? ai->cb->GetUnitHealth(t->unit->id) : 0.0f; float buildTimeLeft = u->def->buildTime*((u->def->health-hp)/u->def->health); //can we build this directly or is there someone who can start it for us bool canBuildThis=buildTable.UnitCanBuild (u->def, t->def) ; if (canBuildThis || t->lead) { float buildTime=buildTimeLeft/(t->buildSpeed+ud->buildSpeed); float distance=0.0f; if (t->lead || t->def->extractsMetal) distance = (t->pos-builderPos).Length(); // position has been estimated for metal extractors else distance = 40 * SQUARE_SIZE; // assume a constant distance float moveTime=max(0.01f,(distance-150)/moveSpeed*10); float travelMod=buildTime/(buildTime+moveTime); //units prefer stuff with low travel time compared to build time float finishMod=t->def->buildTime/(buildTimeLeft+t->def->buildTime*0.1f); //units prefer stuff that is nearly finished float canBuildThisMod=canBuildThis?1.5f:1; //units prefer to do stuff they have in their build options (less risk of guarded unit dying etc) float ageMod=20+sqrtf((float)(curFrame+1)-t->startFrame); // float buildSpeedMod=info->buildSpeed/(qb->totalBuildSpeed+info->buildSpeed); float score=finishMod*canBuildThisMod*travelMod*ageMod; if(!best||score>bestScore) { bestScore=score; best = t; } } } if (best) SetBuilderToTask (u, best); } }
void BuildTask::InitializeLeadBuilder (CGlobals *g) { BuildUnit *leadc = 0;// lead builder candidate assert (!constructors.empty()); assert (!lead); // Find a new lead builder for (int a=0; a<constructors.size(); a++) { BuildUnit *b = constructors [a]; if (constructors[a]->tasks.front () != this) continue; if (buildTable.UnitCanBuild (b->def, def)) { leadc = b; break; } } if (!leadc) // No candidate for lead builder? { for (int a=0; a<constructors.size(); a++) { BuildUnit *u = constructors[a]; DeleteDeathDependence (u); u->DependentDied (this); u->DeleteDeathDependence (this); } constructors.clear(); buildSpeed = 0.0f; return; } float3 builderPos = g->cb->GetUnitPos (leadc->id); Command c; BuildTable::UDef* cd = buildTable.GetCachedDef (def->id); if (cd->IsBuilding()) { if (def->extractsMetal && spotID>=0) { MetalSpot *spot = g->metalmap->GetSpot (spotID); if (spot->unit) { assert (spot->unit->def->extractsMetal < def->extractsMetal); // create a reclaim task and make this depends on the reclaim task ReclaimUnitTask *rt = new ReclaimUnitTask; g->taskManager->AddTask (rt); AddDeathDependence (rt); depends = rt; rt->SetReclaimTarget (spot->unit); return; } } if (pos.x < 0.0f) { if (!SetupBuildLocation (g)) return; } c.params.resize(3); c.params[0]=pos.x; c.params[1]=pos.y; c.params[2]=pos.z; if (aiConfig.debug) g->cb->DrawUnit (def->name.c_str(), pos, 0.0f, 800, g->cb->GetMyTeam(), true, true); } else pos = builderPos; c.id = -def->id; g->cb->GiveOrder (leadc->id, &c); lead = leadc; }