void CBuilderCAI::ExecuteStop(Command& c) { CBuilder* builder = (CBuilder*) owner; building = false; builder->StopBuild(); CMobileCAI::ExecuteStop(c); }
void CBuilderCAI::ExecuteCapture(Command& c) { // not all builders are capture-capable by default if (!owner->unitDef->canCapture) return; CBuilder* builder = (CBuilder*) owner; if (c.params.size() == 1 || c.params.size() == 5) { // capture unit CUnit* unit = uh->GetUnit(c.params[0]); if (unit == NULL) { FinishCommand(); return; } if (c.params.size() == 5) { const float3 pos(c.params[1], c.params[2], c.params[3]); const float radius = c.params[4] + 100; // do not walk too far outside capture area if (((pos - unit->pos).SqLength2D() > (radius * radius) || (builder->curCapture == unit && unit->isMoving && !IsInBuildRange(unit)))) { StopMove(); FinishCommand(); return; } } if (unit->unitDef->capturable && unit->team != owner->team && UpdateTargetLostTimer(unit->id)) { if (MoveInBuildRange(unit)) { builder->SetCaptureTarget(unit); } } else { StopMove(); FinishCommand(); } } else if (c.params.size() == 4) { // area capture const float3 pos = c.GetPos(0); const float radius = c.params[3]; builder->StopBuild(); if (FindCaptureTargetAndCapture(pos, radius, c.options, (c.options & META_KEY))) { inCommand = false; SlowUpdate(); return; } if (!(c.options & ALT_KEY)) { FinishCommand(); } } else { FinishCommand(); } return; }
bool CBuilderCAI::ReclaimObject(CSolidObject* object) { CBuilder* builder = (CBuilder*) owner; if (MoveInBuildRange(object)) { builder->SetReclaimTarget(object); } else { if (owner->moveType->progressState == AMoveType::Failed) { return false; } } return true; }
bool CBuilderCAI::ResurrectObject(CFeature *feature) { CBuilder* builder = (CBuilder*) owner; if (MoveInBuildRange(feature, true)) { builder->SetResurrectTarget(feature); } else { if (owner->moveType->progressState == AMoveType::Failed) { return false; } } return true; }
int main() { CBuilder cbldr; try { cbldr.build(); } catch(int) { std::cout << "Giving up." << std::endl; return 1; } return 0; }
void CBuilderCAI::ExecuteRestore(Command& c) { if (!owner->unitDef->canRestore) return; CBuilder* builder = (CBuilder*) owner; if (inCommand) { if (!builder->terraforming) { FinishCommand(); } } else if (owner->unitDef->canRestore) { const float3 pos(c.params[0], ground->GetHeightReal(c.params[0], c.params[2]), c.params[2]); const float radius = std::min(c.params[3], 200.0f); if (MoveInBuildRange(pos, radius * 0.7f)) { builder->StartRestore(pos, radius); inCommand = true; } } return; }
void CBuilderCAI::ExecuteCapture(Command& c) { assert(owner->unitDef->canCapture); CBuilder* fac = (CBuilder*) owner; if (c.params.size() == 1) { //capture unit CUnit* unit = uh->units[(int)c.params[0]]; if (unit && unit->unitDef->capturable && unit->team != owner->team && UpdateTargetLostTimer((int) c.params[0])) { if (f3SqDist(unit->pos, fac->pos) < Square(fac->buildDistance + unit->radius - 8)) { StopMove(); fac->SetCaptureTarget(unit); owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance * 0.9f + unit->radius, false); } else { if (f3SqDist(goalPos, unit->pos) > 1) { SetGoal(unit->pos,owner->pos, fac->buildDistance * 0.9f + unit->radius); } } } else { StopMove(); FinishCommand(); } } else { // capture area float3 pos(c.params[0], c.params[1], c.params[2]); float radius = c.params[3]; if (FindCaptureTargetAndCapture(pos, radius, c.options)) { inCommand = false; SlowUpdate(); return; } if (!(c.options & ALT_KEY)) { FinishCommand(); } } return; }
void CBuilderCAI::ExecuteReclaim(Command& c) { CBuilder* builder = (CBuilder*) owner; // not all builders are reclaim-capable by default if (!owner->unitDef->canReclaim) return; if (c.params.size() == 1 || c.params.size() == 5) { const int signedId = (int) c.params[0]; if (signedId < 0) { LOG_L(L_WARNING, "Trying to reclaim unit or feature with id < 0 (%i), aborting.", signedId); return; } const unsigned int uid = signedId; //FIXME add a per-unit solution to better balance the load? const bool checkForBetterTarget = (gs->frameNum % (5 * UNIT_SLOWUPDATE_RATE)) < UNIT_SLOWUPDATE_RATE; if (checkForBetterTarget && (c.options & INTERNAL_ORDER) && (c.params.size() >= 5)) { // regular check if there is a closer reclaim target CSolidObject* obj; if (uid >= uh->MaxUnits()) { obj = featureHandler->GetFeature(uid - uh->MaxUnits()); } else { obj = uh->GetUnit(uid); } if (obj) { const float3 pos(c.params[1], c.params[2], c.params[3]); const float radius = c.params[4]; const float curdist = pos.SqDistance2D(obj->pos); const bool recUnits = !!(c.options & META_KEY); const bool recEnemyOnly = (c.options & META_KEY) && (c.options & CONTROL_KEY); const bool recSpecial = !!(c.options & CONTROL_KEY); ReclaimOption recopt = REC_NORESCHECK; if (recUnits) recopt |= REC_UNITS; if (recEnemyOnly) recopt |= REC_ENEMYONLY; if (recSpecial) recopt |= REC_SPECIAL; const int rid = FindReclaimTarget(pos, radius, c.options, recopt, curdist); if ((rid > 0) && (rid != uid)) { FinishCommand(); RemoveUnitFromReclaimers(owner); RemoveUnitFromFeatureReclaimers(owner); return; } } } if (uid >= uh->MaxUnits()) { // reclaim feature CFeature* feature = featureHandler->GetFeature(uid - uh->MaxUnits()); if (feature != NULL) { bool featureBeingResurrected = IsFeatureBeingResurrected(feature->id, owner); featureBeingResurrected &= (c.options & INTERNAL_ORDER) && !(c.options & CONTROL_KEY); if (featureBeingResurrected || !ReclaimObject(feature)) { StopMove(); FinishCommand(); RemoveUnitFromFeatureReclaimers(owner); } else { AddUnitToFeatureReclaimers(owner); } } else { StopMove(); FinishCommand(); RemoveUnitFromFeatureReclaimers(owner); } RemoveUnitFromReclaimers(owner); } else { // reclaim unit CUnit* unit = uh->GetUnit(uid); if (unit != NULL && c.params.size() == 5) { const float3 pos(c.params[1], c.params[2], c.params[3]); const float radius = c.params[4] + 100.0f; // do not walk too far outside reclaim area const bool outOfReclaimRange = (pos.SqDistance2D(unit->pos) > radius * radius) || (builder->curReclaim == unit && unit->isMoving && !IsInBuildRange(unit)); const bool busyAlliedBuilder = unit->unitDef->builder && !unit->commandAI->commandQue.empty() && teamHandler->Ally(owner->allyteam, unit->allyteam); if (outOfReclaimRange || busyAlliedBuilder) { StopMove(); RemoveUnitFromReclaimers(owner); FinishCommand(); RemoveUnitFromFeatureReclaimers(owner); return; } } if (unit != NULL && unit != owner && unit->unitDef->reclaimable && UpdateTargetLostTimer(unit->id) && unit->AllowedReclaim(owner)) { if (!ReclaimObject(unit)) { StopMove(); FinishCommand(); } else { AddUnitToReclaimers(owner); } } else { RemoveUnitFromReclaimers(owner); FinishCommand(); } RemoveUnitFromFeatureReclaimers(owner); } } else if (c.params.size() == 4) { // area reclaim const float3 pos = c.GetPos(0); const float radius = c.params[3]; const bool recUnits = !!(c.options & META_KEY); const bool recEnemyOnly = (c.options & META_KEY) && (c.options & CONTROL_KEY); const bool recSpecial = !!(c.options & CONTROL_KEY); RemoveUnitFromReclaimers(owner); RemoveUnitFromFeatureReclaimers(owner); builder->StopBuild(); ReclaimOption recopt = REC_NORESCHECK; if (recUnits) recopt |= REC_UNITS; if (recEnemyOnly) recopt |= REC_ENEMYONLY; if (recSpecial) recopt |= REC_SPECIAL; if (FindReclaimTargetAndReclaim(pos, radius, c.options, recopt)) { inCommand = false; SlowUpdate(); return; } if(!(c.options & ALT_KEY)){ FinishCommand(); } } else { // wrong number of parameters RemoveUnitFromReclaimers(owner); RemoveUnitFromFeatureReclaimers(owner); FinishCommand(); } }
void CBuilderCAI::ExecuteGuard(Command& c) { if (!owner->unitDef->canGuard) return; CBuilder* builder = (CBuilder*) owner; CUnit* guardee = uh->GetUnit(c.params[0]); if (guardee == NULL) { FinishCommand(); return; } if (guardee == owner) { FinishCommand(); return; } if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; } if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; } if (CBuilder* b = dynamic_cast<CBuilder*>(guardee)) { if (b->terraforming) { if (MoveInBuildRange(b->terraformCenter, b->terraformRadius * 0.7f)) { builder->HelpTerraform(b); } else { StopSlowGuard(); } return; } else if (b->curReclaim && owner->unitDef->canReclaim) { StopSlowGuard(); if (!ReclaimObject(b->curReclaim)) { StopMove(); } return; } else if (b->curResurrect && owner->unitDef->canResurrect) { StopSlowGuard(); if (!ResurrectObject(b->curResurrect)) { StopMove(); } return; } else { builder->StopBuild(); } const bool pushRepairCommand = ( b->curBuild != NULL) && ( b->curBuild->soloBuilder == NULL || b->curBuild->soloBuilder == owner) && (( b->curBuild->beingBuilt && owner->unitDef->canAssist) || ( !b->curBuild->beingBuilt && owner->unitDef->canRepair)); if (pushRepairCommand) { StopSlowGuard(); Command nc(CMD_REPAIR, c.options); nc.params.push_back(b->curBuild->id); commandQue.push_front(nc); inCommand = false; SlowUpdate(); return; } } if (CFactory* fac = dynamic_cast<CFactory*>(guardee)) { const bool pushRepairCommand = ( fac->curBuild != NULL) && ( fac->curBuild->soloBuilder == NULL || fac->curBuild->soloBuilder == owner) && (( fac->curBuild->beingBuilt && owner->unitDef->canAssist) || (!fac->curBuild->beingBuilt && owner->unitDef->canRepair)); if (pushRepairCommand) { StopSlowGuard(); Command nc(CMD_REPAIR, c.options); nc.params.push_back(fac->curBuild->id); commandQue.push_front(nc); inCommand = false; // SlowUpdate(); return; } } if (!(c.options & CONTROL_KEY) && IsUnitBeingReclaimed(guardee, owner)) return; const float3 pos = guardee->pos; const float radius = (guardee->immobile) ? guardee->radius : guardee->radius * 0.8f; // in case of mobile units reduce radius a bit if (MoveInBuildRange(pos, radius)) { StartSlowGuard(guardee->moveType->GetMaxSpeed()); const bool pushRepairCommand = ( guardee->health < guardee->maxHealth) && ( guardee->soloBuilder == NULL || guardee->soloBuilder == owner) && (( guardee->beingBuilt && owner->unitDef->canAssist) || (!guardee->beingBuilt && owner->unitDef->canRepair)); if (pushRepairCommand) { StopSlowGuard(); Command nc(CMD_REPAIR, c.options); nc.params.push_back(guardee->id); commandQue.push_front(nc); inCommand = false; return; } else { NonMoving(); } } else { StopSlowGuard(); } }
void CBuilderCAI::ExecuteRepair(Command& c) { // not all builders are repair-capable by default if (!owner->unitDef->canRepair) return; CBuilder* builder = (CBuilder*) owner; if (c.params.size() == 1 || c.params.size() == 5) { // repair unit CUnit* unit = uh->GetUnit(c.params[0]); if (unit == NULL) { FinishCommand(); return; } if (tempOrder && owner->moveState == MOVESTATE_MANEUVER) { // limit how far away we go if (LinePointDist(commandPos1, commandPos2, unit->pos) > 500) { StopMove(); FinishCommand(); return; } } if (c.params.size() == 5) { const float3 pos(c.params[1], c.params[2], c.params[3]); const float radius = c.params[4] + 100.0f; // do not walk too far outside repair area if ((pos - unit->pos).SqLength2D() > radius * radius || (unit->isMoving && (((c.options & INTERNAL_ORDER) && !TargetInterceptable(unit, unit->speed.Length2D())) || builder->curBuild == unit) && !IsInBuildRange(unit))) { StopMove(); FinishCommand(); return; } } // do not consider units under construction irreparable // even if they can be repaired bool canRepairUnit = true; canRepairUnit &= ((unit->beingBuilt) || (unit->unitDef->repairable && (unit->health < unit->maxHealth))); canRepairUnit &= ((unit != owner) || owner->unitDef->canSelfRepair); canRepairUnit &= (!unit->soloBuilder || (unit->soloBuilder == owner)); canRepairUnit &= (!(c.options & INTERNAL_ORDER) || (c.options & CONTROL_KEY) || !IsUnitBeingReclaimed(unit, owner)); canRepairUnit &= (UpdateTargetLostTimer(unit->id) != 0); if (canRepairUnit) { if (MoveInBuildRange(unit)) { builder->SetRepairTarget(unit); } } else { StopMove(); FinishCommand(); } } else if (c.params.size() == 4) { // area repair const float3 pos = c.GetPos(0); const float radius = c.params[3]; builder->StopBuild(); if (FindRepairTargetAndRepair(pos, radius, c.options, false, (c.options & META_KEY))) { inCommand = false; SlowUpdate(); return; } if (!(c.options & ALT_KEY)) { FinishCommand(); } } else { FinishCommand(); } return; }
void CBuilderCAI::ExecuteBuildCmd(Command& c) { CBuilder* builder = (CBuilder*) owner; map<int, string>::iterator boi = buildOptions.find(c.GetID()); if (boi == buildOptions.end()) return; if (!inCommand) { BuildInfo bi; bi.pos.x = floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.z = floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.y = c.params[1]; if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); CFeature* f = NULL; uh->TestUnitBuildSquare(bi, f, owner->allyteam, true); if (f != NULL) { if (!bi.def->isFeature || bi.def->wreckName != f->def->name) { ReclaimFeature(f); } else { FinishCommand(); } } else { inCommand = true; build.Parse(c); ExecuteBuildCmd(c); } return; } assert(build.def); const float radius = GetBuildOptionRadius(build.def, c.GetID()); if (building) { MoveInBuildRange(build.pos, radius); if (!builder->curBuild && !builder->terraforming) { building = false; StopMove(); // cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; builder->StopBuild(); CancelRestrictedUnit(boi->second); } } else { if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); return; } build.pos = helper->Pos2BuildPos(build, true); // we are on the way to the buildpos, meanwhile it can happen // that another builder already finished our buildcmd or blocked // the buildpos with another building (skip our buildcmd then) const bool checkBuildPos = (gs->frameNum % (5 * UNIT_SLOWUPDATE_RATE)) < UNIT_SLOWUPDATE_RATE; //FIXME add a per-unit solution to better balance the load? CFeature* feature = NULL; if (checkBuildPos && !uh->TestUnitBuildSquare(build, feature, owner->allyteam, true)) { if (!feature) { const int yardxpos = int(build.pos.x + 4) / SQUARE_SIZE; const int yardypos = int(build.pos.z + 4) / SQUARE_SIZE; const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); const CUnit* u = dynamic_cast<const CUnit*>(s); if (u != NULL) { const bool canAssist = u->beingBuilt && owner->unitDef->canAssist && (!u->soloBuilder || (u->soloBuilder == owner)); if ((u->unitDef != build.def) || !canAssist) { StopMove(); FinishCommand(); return; } } } } if (MoveInBuildRange(build.pos, radius, true)) { if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build)) { StopMove(); // cancel KeepPointingTo FinishCommand(); } else if (!teamHandler->Team(owner->team)->AtUnitLimit()) { // unit-limit not yet reached CFeature* f = NULL; bool waitstance = false; if (builder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) { building = true; } else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) { inCommand = false; ReclaimFeature(f); } else if (!waitstance) { const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize); const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE; // tell everything within the radius of the soon-to-be buildee // to get out of the way; using the model radius is not correct // because this can be shorter than half the footprint diagonal helper->BuggerOff(build.pos, std::max(radius, fpRadius), false, true, owner->team, NULL); NonMoving(); } } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); } } } } }
void CBuilderCAI::SlowUpdate() { if (commandQue.empty()) { CMobileCAI::SlowUpdate(); return; } if (owner->stunned) { return; } CBuilder* fac = (CBuilder*)owner; Command& c = commandQue.front(); if (OutOfImmobileRange(c)) { FinishCommand(); return; } map<int, string>::iterator boi = buildOptions.find(c.id); if (!owner->beingBuilt && boi != buildOptions.end()) { const UnitDef* ud = unitDefHandler->GetUnitByName(boi->second); const float radius = GetUnitDefRadius(ud, c.id); if (inCommand) { if (building) { if (f3SqDist(build.pos, fac->pos) > Square(fac->buildDistance + radius - 8.0f)) { owner->moveType->StartMoving(build.pos, fac->buildDistance * 0.5f + radius); } else { StopMove(); // needed since above startmoving cancels this owner->moveType->KeepPointingTo(build.pos, (fac->buildDistance + radius) * 0.6f, false); } if (!fac->curBuild && !fac->terraforming) { building = false; StopMove(); //cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; fac->StopBuild(); CancelRestrictedUnit(boi->second); } } else { build.Parse(c); if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); } else { build.pos = helper->Pos2BuildPos(build); const float sqdist = f3SqDist(build.pos, fac->pos); if ((sqdist < Square(fac->buildDistance * 0.6f + radius)) || (!owner->unitDef->canmove && (sqdist <= Square(fac->buildDistance + radius - 8.0f)))) { StopMove(); if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build.pos)) { FinishCommand(); } else if (uh->maxUnits > (int) teamHandler->Team(owner->team)->units.size()) { // max unitlimit reached buildRetries++; owner->moveType->KeepPointingTo(build.pos, fac->buildDistance * 0.7f + radius, false); if (fac->StartBuild(build) || (buildRetries > 20)) { building = true; } else { ENTER_MIXED; if ((owner->team == gu->myTeam) && !(buildRetries & 7)) { logOutput.Print("%s: Build pos blocked", owner->unitDef->humanName.c_str()); logOutput.SetLastMsgPos(owner->pos); } ENTER_SYNCED; helper->BuggerOff(build.pos, radius); NonMoving(); } } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); } } SetGoal(build.pos, owner->pos, fac->buildDistance * 0.4f + radius); } } } } else { //!inCommand BuildInfo bi; bi.pos.x=floor(c.params[0]/SQUARE_SIZE+0.5f)*SQUARE_SIZE; bi.pos.z=floor(c.params[2]/SQUARE_SIZE+0.5f)*SQUARE_SIZE; bi.pos.y=c.params[1]; CFeature* f=0; if (c.params.size()==4) bi.buildFacing = int(c.params[3]); bi.def = unitDefHandler->GetUnitByName(boi->second); uh->TestUnitBuildSquare(bi,f,owner->allyteam); if (f) { if (!owner->unitDef->canReclaim || !f->def->reclaimable) { // FIXME user shouldn't be able to queue buildings on top of features // in the first place (in this case). StopMove(); FinishCommand(); } else { Command c2; c2.id=CMD_RECLAIM; c2.options=0; c2.params.push_back(f->id+MAX_UNITS); commandQue.push_front(c2); SlowUpdate(); //this assumes that the reclaim command can never return directly without having reclaimed the target } } else { inCommand=true; SlowUpdate(); } } return; } switch (c.id) { case CMD_STOP: { ExecuteStop(c); return; } case CMD_REPAIR: { ExecuteRepair(c); return; } case CMD_CAPTURE: { ExecuteCapture(c); return; } case CMD_GUARD: { ExecuteGuard(c); return; } case CMD_RECLAIM: { ExecuteReclaim(c); return; } case CMD_RESURRECT: { ExecuteResurrect(c); return; } case CMD_PATROL: { ExecutePatrol(c); return; } case CMD_FIGHT: { ExecuteFight(c); return; } case CMD_RESTORE: { ExecuteRestore(c); return; } default: { CMobileCAI::SlowUpdate(); return; } } }