bool CBuilderCAI::MoveInBuildRange(const float3& pos, float radius, const bool checkMoveTypeForFailed) { if (!IsInBuildRange(pos, radius)) { if ( checkMoveTypeForFailed && owner->moveType->progressState == AMoveType::Failed && f3SqDist(goalPos, pos) > 1.0f // check if the AMoveType::Failed belongs to the same goal position ) { // don't call SetGoal() it would reset moveType->progressState and so later code couldn't check if the movecmd failed return false; } // too far away, start a move command SetGoal(pos, owner->pos, GetBuildRange(radius) * 0.9f); return false; } if (owner->unitDef->IsAirUnit()) { StopMoveAndKeepPointing(pos, radius); } else { StopMoveAndKeepPointing(goalPos, goalRadius); } return true; }
inline bool CBuilderCAI::OutOfImmobileRange(const Command& cmd) const { if (owner->unitDef->canmove) { return false; // builder can move } if (((cmd.options & INTERNAL_ORDER) == 0) || (cmd.params.size() != 1)) { return false; // not an internal object targetted command } const int objID = cmd.params[0]; const CWorldObject* obj = uh->GetUnit(objID); if (obj == NULL) { // features don't move, but maybe the unit was transported? obj = featureHandler->GetFeature(objID - uh->MaxUnits()); if (obj == NULL) { return false; } } switch (cmd.GetID()) { case CMD_REPAIR: case CMD_RECLAIM: case CMD_RESURRECT: case CMD_CAPTURE: { if (!IsInBuildRange(obj)) { return true; } break; } } return false; }
bool CBuilderCAI::MoveInBuildRange(const float3& objPos, float objRadius, const bool checkMoveTypeForFailed) { if (!IsInBuildRange(objPos, objRadius)) { // NOTE: // ignore the fail-check if we are an aircraft, movetype code // is unreliable wrt. setting it correctly and causes (landed) // aircraft to discard orders const bool checkFailed = (checkMoveTypeForFailed && !owner->unitDef->IsAirUnit()); // check if the AMoveType::Failed belongs to the same goal position const bool haveFailed = (owner->moveType->progressState == AMoveType::Failed && f3SqDist(goalPos, objPos) > 1.0f); if (checkFailed && haveFailed) { // don't call SetGoal() it would reset moveType->progressState // and so later code couldn't check if the move command failed return false; } // too far away, start a move command SetGoal(objPos, owner->pos, GetBuildRange(objRadius) * 0.9f); return false; } if (owner->unitDef->IsAirUnit()) { StopMoveAndKeepPointing(objPos, objRadius, false); } else { StopMoveAndKeepPointing(goalPos, goalRadius, false); } return true; }
void CBuilderCAI::GiveCommandReal(const Command& c, bool fromSynced) { if (!AllowedCommand(c, fromSynced)) return; // don't guard yourself if ((c.GetID() == CMD_GUARD) && (c.params.size() == 1) && ((int)c.params[0] == owner->id)) { return; } // stop building/reclaiming/... if the new command is not queued, i.e. replaces our current activity // FIXME should happen just before CMobileCAI::GiveCommandReal? (the new cmd can still be skipped!) if ((c.GetID() != CMD_WAIT && c.GetID() != CMD_SET_WANTED_MAX_SPEED) && !(c.options & SHIFT_KEY)) { if (nonQueingCommands.find(c.GetID()) == nonQueingCommands.end()) { building = false; static_cast<CBuilder*>(owner)->StopBuild(); } } if (buildOptions.find(c.GetID()) != buildOptions.end()) { if (c.params.size() < 3) return; BuildInfo bi; bi.pos = c.GetPos(0); if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByID(-c.GetID()); bi.pos = CGameHelper::Pos2BuildPos(bi, true); // We are a static building, check if the buildcmd is in range if (!owner->unitDef->canmove) { if (!IsInBuildRange(bi.pos, GetBuildOptionRadius(bi.def, c.GetID()))) { return; } } const CUnit* nanoFrame = NULL; // check if the buildpos is blocked if (IsBuildPosBlocked(bi, &nanoFrame)) return; // if it is a nanoframe help to finish it if (nanoFrame != NULL) { Command c2(CMD_REPAIR, c.options | INTERNAL_ORDER, nanoFrame->id); CMobileCAI::GiveCommandReal(c2, fromSynced); CMobileCAI::GiveCommandReal(c, fromSynced); return; } } else { if (c.GetID() < 0) return; } CMobileCAI::GiveCommandReal(c, fromSynced); }
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::FindCaptureTargetAndCapture(const float3& pos, float radius, unsigned char options, bool healthyOnly) { const std::vector<CUnit*> &cu = qf->GetUnits(pos, radius); std::vector<CUnit*>::const_iterator ui; const CUnit* best = NULL; float bestDist = 1.0e30f; bool stationary = false; for (ui = cu.begin(); ui != cu.end(); ++ui) { CUnit* unit = *ui; if ((((options & CONTROL_KEY) && owner->team != unit->team) || !teamHandler->Ally(owner->allyteam, unit->allyteam)) && (unit != owner) && (unit->losStatus[owner->allyteam] & (LOS_INRADAR|LOS_INLOS)) && !unit->beingBuilt && unit->unitDef->capturable) { if(unit->isMoving && stationary) { // capture stationary targets first continue; } if(healthyOnly && unit->health < unit->maxHealth && unit->captureProgress <= 0.0f) { continue; } float dist = f3SqDist(unit->pos, owner->pos); if(dist < bestDist || (!stationary && !unit->isMoving)) { if (!owner->unitDef->canmove && !IsInBuildRange(unit)) { continue; } if(!stationary && !unit->isMoving) { stationary = true; } bestDist = dist; best = unit; } } } if (best) { Command nc(CMD_CAPTURE, options | INTERNAL_ORDER); nc.params.push_back(best->id); commandQue.push_front(nc); return true; } return false; }
bool CBuilderCAI::FindResurrectableFeatureAndResurrect(const float3& pos, float radius, unsigned char options, bool freshOnly) { const std::vector<CFeature*> &features = qf->GetFeaturesExact(pos, radius); const CFeature* best = NULL; float bestDist = 1.0e30f; for (std::vector<CFeature*>::const_iterator fi = features.begin(); fi != features.end(); ++fi) { const CFeature* f = *fi; if (f->def->destructable && f->udef != NULL) { if (!f->IsInLosForAllyTeam(owner->allyteam)) { continue; } if (freshOnly && f->reclaimLeft < 1.0f && f->resurrectProgress <= 0.0f) { continue; } float dist = f3SqDist(f->pos, owner->pos); if (dist < bestDist) { // dont lock-on to units outside of our reach (for immobile builders) if (owner->immobile && !IsInBuildRange(f)) { continue; } if(!(options & CONTROL_KEY) && IsFeatureBeingReclaimed(f->id, owner)) { continue; } bestDist = dist; best = f; } } } if (best) { Command c2(CMD_RESURRECT, options | INTERNAL_ORDER); c2.params.push_back(uh->MaxUnits() + best->id); commandQue.push_front(c2); return true; } return false; }
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::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::GiveCommandReal(const Command& c, bool fromSynced) { if (!AllowedCommand(c, fromSynced)) return; // don't guard yourself if ((c.GetID() == CMD_GUARD) && (c.params.size() == 1) && ((int)c.params[0] == owner->id)) { return; } // stop current build if the new command is a not queued and replaces the current buidcmd //FIXME should happen just before CMobileCAI::GiveCommandReal? (the new cmd can still be skipped!) if (!(c.options & SHIFT_KEY) && nonQueingCommands.find(c.GetID()) == nonQueingCommands.end() && c.GetID() != CMD_WAIT) { building = false; ((CBuilder*) owner)->StopBuild(); } map<int,string>::iterator boi = buildOptions.find(c.GetID()); if (boi != buildOptions.end()) { if (c.params.size() < 3) { return; } BuildInfo bi; bi.pos = c.GetPos(0); if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); bi.pos = helper->Pos2BuildPos(bi, true); // We are a static building, check if the buildcmd is in range if (!owner->unitDef->canmove) { const float radius = GetBuildOptionRadius(bi.def, c.GetID()); if (!IsInBuildRange(bi.pos, radius)) { return; } } // check if the buildpos is blocked if it is a nanoframe help to finish it //FIXME finish it just if it is of the same unitdef? CFeature* feature = NULL; if (!uh->TestUnitBuildSquare(bi, feature, owner->allyteam, true)) { if (!feature && owner->unitDef->canAssist) { const int yardxpos = int(bi.pos.x + 4) / SQUARE_SIZE; const int yardypos = int(bi.pos.z + 4) / SQUARE_SIZE; const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); const CUnit* u = dynamic_cast<const CUnit*>(s); if ( (u != NULL) && u->beingBuilt && (u->buildProgress == 0.0f) && (!u->soloBuilder || (u->soloBuilder == owner)) ) { Command c2(CMD_REPAIR, c.options | INTERNAL_ORDER); c2.params.push_back(u->id); CMobileCAI::GiveCommandReal(c2); CMobileCAI::GiveCommandReal(c); } } return; } } CMobileCAI::GiveCommandReal(c); }
inline bool CBuilderCAI::IsInBuildRange(const CWorldObject* obj) const { return IsInBuildRange(obj->pos, obj->radius); }
bool CBuilderCAI::FindRepairTargetAndRepair(const float3& pos, float radius, unsigned char options, bool attackEnemy, bool builtOnly) { const std::vector<CUnit*> &cu = qf->GetUnitsExact(pos, radius); const CUnit* best = NULL; float bestDist = 1.0e30f; bool haveEnemy = false; bool trySelfRepair = false; bool stationary = false; const float& maxSpeed = owner->moveType->GetMaxSpeed(); float uspeed = 0.0f; for (std::vector<CUnit*>::const_iterator ui = cu.begin(); ui != cu.end(); ++ui) { CUnit* unit = *ui; if (teamHandler->Ally(owner->allyteam, unit->allyteam)) { if (!haveEnemy && (unit->health < unit->maxHealth)) { // don't help allies build unless set on roam if (unit->beingBuilt && owner->team != unit->team && (owner->moveState != MOVESTATE_ROAM)) { continue; } // don't help factories produce units when set on hold pos if (unit->beingBuilt && unit->moveDef && (owner->moveState == MOVESTATE_HOLDPOS)) { continue; } // don't repair stuff that can't be repaired if (!unit->beingBuilt && !unit->unitDef->repairable) { continue; } // don't assist or repair if can't assist or repair if (!( unit->beingBuilt && owner->unitDef->canAssist) && !(!unit->beingBuilt && owner->unitDef->canRepair)) { continue; } if (unit->soloBuilder && (unit->soloBuilder != owner)) { continue; } if (unit == owner) { trySelfRepair = true; continue; } if(unit->isMoving && stationary) { // repair stationary targets first continue; } if(builtOnly && unit->beingBuilt) { continue; } float dist = f3SqDist(unit->pos, owner->pos); if (unit->isMoving) { uspeed = unit->speed.Length2D(); dist *= 1.0f + std::max(uspeed - maxSpeed, 0.0f); // avoid targets that are faster than our max speed } if (dist < bestDist || (!stationary && !unit->isMoving)) { // dont lock-on to units outside of our reach (for immobile builders) if ((owner->immobile || (unit->isMoving && !TargetInterceptable(unit, uspeed))) && !IsInBuildRange(unit)) { continue; } // don't repair stuff that's being reclaimed if (!(options & CONTROL_KEY) && IsUnitBeingReclaimed(unit, owner)) { continue; } if(!stationary && !unit->isMoving) { stationary = true; } bestDist = dist; best = unit; } } } else { if (!attackEnemy || !owner->unitDef->canAttack || (owner->maxRange <= 0) ) continue; if (!(unit->losStatus[owner->allyteam] & (LOS_INRADAR | LOS_INLOS))) continue; const float dist = f3SqDist(unit->pos, owner->pos); if ((dist < bestDist) || !haveEnemy) { if (owner->immobile && ((dist - unit->radius) > owner->maxRange)) { continue; } best = unit; bestDist = dist; haveEnemy = true; } } } if (best == NULL) { if (trySelfRepair && owner->unitDef->canSelfRepair && (owner->health < owner->maxHealth)) { best = owner; } else { return false; } } if (!haveEnemy) { if (attackEnemy) { PushOrUpdateReturnFight(); } Command cmd(CMD_REPAIR, options | INTERNAL_ORDER); cmd.params.push_back(best->id); cmd.params.push_back(pos.x); cmd.params.push_back(pos.y); cmd.params.push_back(pos.z); cmd.params.push_back(radius); commandQue.push_front(cmd); } else { PushOrUpdateReturnFight(); // attackEnemy must be true Command cmd(CMD_ATTACK, options | INTERNAL_ORDER); cmd.params.push_back(best->id); commandQue.push_front(cmd); } return true; }
int CBuilderCAI::FindReclaimTarget(const float3& pos, float radius, unsigned char cmdopt, ReclaimOption recoptions, float bestStartDist) const { const bool noResCheck = recoptions & REC_NORESCHECK; const bool recUnits = recoptions & REC_UNITS; const bool recNonRez = recoptions & REC_NONREZ; const bool recEnemy = recoptions & REC_ENEMY; const bool recEnemyOnly = recoptions & REC_ENEMYONLY; const bool recSpecial = recoptions & REC_SPECIAL; const CSolidObject* best = NULL; float bestDist = bestStartDist; bool stationary = false; bool metal = false; int rid = -1; if (recUnits || recEnemy || recEnemyOnly) { const std::vector<CUnit*>& units = qf->GetUnitsExact(pos, radius); for (std::vector<CUnit*>::const_iterator ui = units.begin(); ui != units.end(); ++ui) { const CUnit* u = *ui; if (u == owner) continue; if (!u->unitDef->reclaimable) continue; if (!((!recEnemy && !recEnemyOnly) || !teamHandler->Ally(owner->allyteam, u->allyteam))) continue; if (!(u->losStatus[owner->allyteam] & (LOS_INRADAR|LOS_INLOS))) continue; // reclaim stationary targets first if (u->isMoving && stationary) continue; // do not reclaim friendly builders that are busy if (u->unitDef->builder && teamHandler->Ally(owner->allyteam, u->allyteam) && !u->commandAI->commandQue.empty()) continue; const float dist = f3SqDist(u->pos, owner->pos); if (dist < bestDist || (!stationary && !u->isMoving)) { if (owner->immobile && !IsInBuildRange(u)) { continue; } if(!stationary && !u->isMoving) { stationary = true; } bestDist = dist; best = u; } } if (best) rid = best->id; } if ((!best || !stationary) && !recEnemyOnly) { best = NULL; const CTeam* team = teamHandler->Team(owner->team); const std::vector<CFeature*>& features = qf->GetFeaturesExact(pos, radius); for (std::vector<CFeature*>::const_iterator fi = features.begin(); fi != features.end(); ++fi) { const CFeature* f = *fi; if (f->def->reclaimable && (recSpecial || f->def->autoreclaim) && (!recNonRez || !(f->def->destructable && f->udef != NULL)) ) { if (recSpecial && metal && f->def->metal <= 0.0) { continue; } const float dist = f3SqDist(f->pos, owner->pos); if ((dist < bestDist || (recSpecial && !metal && f->def->metal > 0.0)) && (noResCheck || ((f->def->metal > 0.0f) && (team->metal < team->metalStorage)) || ((f->def->energy > 0.0f) && (team->energy < team->energyStorage))) ) { if (!f->IsInLosForAllyTeam(owner->allyteam)) { continue; } if (!owner->unitDef->canmove && !IsInBuildRange(f)) { continue; } if (!(cmdopt & CONTROL_KEY) && IsFeatureBeingResurrected(f->id, owner)) { continue; } if (recSpecial && !metal && f->def->metal > 0.0f) { metal = true; } bestDist = dist; best = f; } } } if (best) rid = uh->MaxUnits() + best->id; } return rid; }