void CAttackTask::Execute(CCircuitUnit* unit, bool isUpdating) { Unit* u = unit->GetUnit(); CCircuitAI* circuit = manager->GetCircuit(); CTerrainManager* terrainManager = circuit->GetTerrainManager(); float minSqDist; CEnemyUnit* bestTarget = FindBestTarget(unit, minSqDist); if (bestTarget == nullptr) { if (!isUpdating) { float x = rand() % (terrainManager->GetTerrainWidth() + 1); float z = rand() % (terrainManager->GetTerrainHeight() + 1); position = AIFloat3(x, circuit->GetMap()->GetElevationAt(x, z), z); } } else { position = bestTarget->GetPos(); float range = u->GetMaxRange(); if (minSqDist < range * range) { u->Attack(bestTarget->GetUnit(), UNIT_COMMAND_OPTION_INTERNAL_ORDER, circuit->GetLastFrame() + FRAMES_PER_SEC * 300); return; } } u->Fight(position, UNIT_COMMAND_OPTION_INTERNAL_ORDER, circuit->GetLastFrame() + FRAMES_PER_SEC * 300); }
void CRetreatTask::Update() { CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); bool isExecute = (++updCount % 4 == 0); auto assignees = units; for (CCircuitUnit* unit : assignees) { Unit* u = unit->GetUnit(); const float healthPerc = u->GetHealth() / u->GetMaxHealth(); bool isRepaired; // FIXME: Wait until 101.0 engine // if (ass->GetShield() != nullptr) { // isRepaired = (healthPerc > 0.98f) && unit->IsShieldCharged(0.9f)); // } else { isRepaired = healthPerc > 0.98f; // } if (isRepaired && !unit->IsDisarmed(frame)) { RemoveAssignee(unit); } else if (unit->IsForceExecute() || isExecute) { Execute(unit); } else { unit->Update(circuit); } } }
void CRetreatTask::OnUnitIdle(CCircuitUnit* unit) { CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); CFactoryManager* factoryManager = circuit->GetFactoryManager(); AIFloat3 haven = (repairer != nullptr) ? repairer->GetPos(frame) : factoryManager->GetClosestHaven(unit); if (haven == -RgtVector) { haven = circuit->GetSetupManager()->GetBasePos(); } if (unit->GetCircuitDef()->IsPlane()) { // force rearm/repair | CMD_FIND_PAD unit->GetUnit()->Fight(haven, UNIT_COMMAND_OPTION_RIGHT_MOUSE_KEY, frame + FRAMES_PER_SEC * 60); return; } const float maxDist = factoryManager->GetAssistDef()->GetBuildDistance(); const AIFloat3& unitPos = unit->GetPos(frame); if (unitPos.SqDistance2D(haven) > maxDist * maxDist) { // TODO: push MoveAction into unit? to avoid enemy fire unit->GetUnit()->MoveTo(haven, UNIT_COMMAND_OPTION_RIGHT_MOUSE_KEY, frame + FRAMES_PER_SEC * 1); // TODO: Add fail counter? } else { // TODO: push WaitAction into unit // unit->GetUnit()->ExecuteCustomCommand(CMD_PRIORITY, {0.0f}); AIFloat3 pos = unitPos; const float size = SQUARE_SIZE * 16; CTerrainManager* terrainManager = circuit->GetTerrainManager(); float centerX = terrainManager->GetTerrainWidth() / 2; float centerZ = terrainManager->GetTerrainHeight() / 2; pos.x += (pos.x > centerX) ? size : -size; pos.z += (pos.z > centerZ) ? size : -size; AIFloat3 oldPos = pos; terrainManager->CorrectPosition(pos); if (oldPos.SqDistance2D(pos) > SQUARE_SIZE * SQUARE_SIZE) { pos = unitPos; pos.x += (pos.x > centerX) ? -size : size; pos.z += (pos.z > centerZ) ? -size : size; } CTerrainManager::TerrainPredicate predicate = [unitPos](const AIFloat3& p) { return unitPos.SqDistance2D(p) > SQUARE(SQUARE_SIZE * 8); }; pos = terrainManager->FindBuildSite(unit->GetCircuitDef(), pos, maxDist, UNIT_COMMAND_BUILD_NO_FACING, predicate); unit->GetUnit()->PatrolTo(pos); IUnitAction* act = static_cast<IUnitAction*>(unit->End()); if (act->IsAny(IUnitAction::Mask::MOVE | IUnitAction::Mask::FIGHT)) { static_cast<ITravelAction*>(act)->SetFinished(true); } } }
void CBWaitTask::OnUnitDamaged(CCircuitUnit* unit, CEnemyUnit* attacker) { CCircuitAI* circuit = manager->GetCircuit(); const int frame = circuit->GetLastFrame(); CCircuitDef* cdef = unit->GetCircuitDef(); const float healthPerc = unit->GetHealthPercent(); if ((healthPerc > cdef->GetRetreat()) && !unit->IsDisarmed(frame)) { return; } CRetreatTask* task = manager->GetCircuit()->GetBuilderManager()->EnqueueRetreat(); manager->AssignTask(unit, task); }
void CDefendTask::Execute(CCircuitUnit* unit) { CCircuitAI* circuit = manager->GetCircuit(); CTerrainManager* terrainManager = circuit->GetTerrainManager(); AIFloat3 pos = utils::get_radial_pos(position, SQUARE_SIZE * 32); terrainManager->CorrectPosition(pos); pos = terrainManager->FindBuildSite(unit->GetCircuitDef(), pos, 300.0f, UNIT_COMMAND_BUILD_NO_FACING); TRY_UNIT(circuit, unit, unit->GetUnit()->Fight(pos, UNIT_COMMAND_OPTION_RIGHT_MOUSE_KEY, circuit->GetLastFrame() + FRAMES_PER_SEC * 60); unit->GetUnit()->SetWantedMaxSpeed(MAX_UNIT_SPEED); ) }
void CSuperTask::Update() { CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); CCircuitUnit* unit = *units.begin(); CCircuitDef* cdef = unit->GetCircuitDef(); if (cdef->IsHoldFire()) { if (targetFrame + (cdef->GetReloadTime() + TARGET_DELAY) > frame) { if ((State::ENGAGE == state) && (targetFrame + TARGET_DELAY <= frame)) { TRY_UNIT(circuit, unit, unit->GetUnit()->Stop(); ) state = State::ROAM; } return; }
void CArtilleryTask::Execute(CCircuitUnit* unit, bool isUpdating) { IUnitAction* act = static_cast<IUnitAction*>(unit->End()); if (!act->IsAny(IUnitAction::Mask::MOVE | IUnitAction::Mask::FIGHT | IUnitAction::Mask::JUMP)) { return; } ITravelAction* travelAction = static_cast<ITravelAction*>(act); CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); const AIFloat3& pos = unit->GetPos(frame); std::shared_ptr<F3Vec> pPath = std::make_shared<F3Vec>(); CEnemyUnit* bestTarget = FindTarget(unit, pos, *pPath); if (bestTarget != nullptr) { TRY_UNIT(circuit, unit, unit->GetUnit()->Attack(bestTarget->GetUnit(), UNIT_COMMAND_OPTION_RIGHT_MOUSE_KEY, frame + FRAMES_PER_SEC * 60); unit->GetUnit()->ExecuteCustomCommand(CMD_UNIT_SET_TARGET, {(float)bestTarget->GetId()}); )
void IFighterTask::OnUnitDamaged(CCircuitUnit* unit, CEnemyUnit* attacker) { CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); CCircuitDef* cdef = unit->GetCircuitDef(); Unit* u = unit->GetUnit(); const float healthPerc = u->GetHealth() / u->GetMaxHealth(); if (unit->GetShield() != nullptr) { const float minShield = circuit->GetSetupManager()->GetEmptyShield(); if ((healthPerc > cdef->GetRetreat()) && unit->IsShieldCharged(minShield)) { if (cdef->IsRoleHeavy() && (healthPerc < 0.9f)) { circuit->GetBuilderManager()->EnqueueRepair(IBuilderTask::Priority::NOW, unit); } return; } } else if ((healthPerc > cdef->GetRetreat()) && !unit->IsDisarmed(frame)) { if (cdef->IsRoleHeavy() && (healthPerc < 0.9f)) { circuit->GetBuilderManager()->EnqueueRepair(IBuilderTask::Priority::NOW, unit); } return; } else if (healthPerc < 0.2f) { // stuck units workaround: they don't shoot and don't see distant threat CRetreatTask* task = manager->GetCircuit()->GetMilitaryManager()->EnqueueRetreat(); manager->AssignTask(unit, task); return; } CThreatMap* threatMap = circuit->GetThreatMap(); const float range = cdef->GetMaxRange(); if ((target == nullptr) || !target->IsInLOS()) { CRetreatTask* task = circuit->GetMilitaryManager()->EnqueueRetreat(); manager->AssignTask(unit, task); return; } const AIFloat3& pos = unit->GetPos(frame); if ((target->GetPos().SqDistance2D(pos) > SQUARE(range)) || (threatMap->GetThreatAt(unit, pos) * 2 > threatMap->GetUnitThreat(unit))) { CRetreatTask* task = circuit->GetMilitaryManager()->EnqueueRetreat(); manager->AssignTask(unit, task); return; } cowards.insert(unit); }
IBuilderTask::IBuilderTask(ITaskManager* mgr, Priority priority, CCircuitDef* buildDef, const AIFloat3& position, BuildType type, float cost, bool isShake, int timeout) : IUnitTask(mgr, priority, Type::BUILDER) , position(position) , isShake(isShake) , buildDef(buildDef) , buildType(type) , buildPower(.0f) , cost(cost) , target(nullptr) , buildPos(-RgtVector) , facing(UNIT_COMMAND_BUILD_NO_FACING) , nextTask(nullptr) , timeout(timeout) , buildFails(0) { CCircuitAI* circuit = manager->GetCircuit(); lastTouched = circuit->GetLastFrame(); savedIncome = circuit->GetEconomyManager()->GetAvgMetalIncome(); }
void CRetreatTask::CheckRepairer(CCircuitUnit* unit) { CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); CPathFinder* pathfinder = circuit->GetPathfinder(); AIFloat3 startPos = (*units.begin())->GetPos(frame); AIFloat3 endPos; float range; bool isRepairer = (repairer != nullptr); if (isRepairer) { endPos = repairer->GetPos(frame); range = pathfinder->GetSquareSize(); } else { CFactoryManager* factoryManager = circuit->GetFactoryManager(); endPos = factoryManager->GetClosestHaven(unit); if (endPos == -RgtVector) { endPos = circuit->GetSetupManager()->GetBasePos(); } range = factoryManager->GetAssistDef()->GetBuildDistance() * 0.6f + pathfinder->GetSquareSize(); } circuit->GetTerrainManager()->CorrectPosition(startPos); pathfinder->SetMapData(unit, circuit->GetThreatMap(), frame); float prevCost = pathfinder->PathCost(startPos, endPos, range); if (isRepairer && repairer->GetCircuitDef()->IsMobile()) { prevCost /= 4; } endPos = unit->GetPos(frame); float nextCost = pathfinder->PathCost(startPos, endPos, range); if (unit->GetCircuitDef()->IsMobile()) { nextCost /= 4; } if (prevCost > nextCost) { SetRepairer(unit); } }
void CRetreatTask::Execute(CCircuitUnit* unit) { IUnitAction* act = static_cast<IUnitAction*>(unit->End()); if (!act->IsAny(IUnitAction::Mask::MOVE | IUnitAction::Mask::FIGHT)) { return; } ITravelAction* travelAction = static_cast<ITravelAction*>(act); CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame(); CPathFinder* pathfinder = circuit->GetPathfinder(); AIFloat3 startPos = unit->GetPos(frame); AIFloat3 endPos; float range; if (repairer != nullptr) { endPos = repairer->GetPos(frame); range = pathfinder->GetSquareSize(); } else { CFactoryManager* factoryManager = circuit->GetFactoryManager(); endPos = factoryManager->GetClosestHaven(unit); if (endPos == -RgtVector) { endPos = circuit->GetSetupManager()->GetBasePos(); } range = factoryManager->GetAssistDef()->GetBuildDistance() * 0.6f + pathfinder->GetSquareSize(); } std::shared_ptr<F3Vec> pPath = std::make_shared<F3Vec>(); pathfinder->SetMapData(unit, circuit->GetThreatMap(), frame); pathfinder->MakePath(*pPath, startPos, endPos, range); if (pPath->empty()) { pPath->push_back(endPos); } travelAction->SetPath(pPath); unit->Update(circuit); }
void IBuilderTask::Execute(CCircuitUnit* unit) { Unit* u = unit->GetUnit(); u->ExecuteCustomCommand(CMD_PRIORITY, {static_cast<float>(priority)}); CCircuitAI* circuit = manager->GetCircuit(); if (target != nullptr) { Unit* tu = target->GetUnit(); u->Build(target->GetCircuitDef()->GetUnitDef(), tu->GetPos(), tu->GetBuildingFacing(), UNIT_COMMAND_OPTION_INTERNAL_ORDER, circuit->GetLastFrame() + FRAMES_PER_SEC * 60); return; } CTerrainManager* terrainManager = circuit->GetTerrainManager(); UnitDef* buildUDef = buildDef->GetUnitDef(); if (buildPos != -RgtVector) { if (circuit->GetMap()->IsPossibleToBuildAt(buildUDef, buildPos, facing)) { u->Build(buildUDef, buildPos, facing, UNIT_COMMAND_OPTION_INTERNAL_ORDER, circuit->GetLastFrame() + FRAMES_PER_SEC * 60); return; } else { terrainManager->RemoveBlocker(buildDef, buildPos, facing); // FIXME: If enemy blocked position then reset will have no effect // terrain->ResetBuildFrame(); } } circuit->GetThreatMap()->SetThreatType(unit); // FIXME: Replace const 999.0f with build time? if (circuit->IsAllyAware() && (cost > 999.0f)) { // circuit->UpdateFriendlyUnits(); auto friendlies = std::move(circuit->GetCallback()->GetFriendlyUnitsIn(position, cost)); for (Unit* au : friendlies) { CCircuitUnit* alu = circuit->GetFriendlyUnit(au); if (alu == nullptr) { continue; } if ((*alu->GetCircuitDef() == *buildDef) && au->IsBeingBuilt()) { const AIFloat3& pos = au->GetPos(); if (terrainManager->CanBuildAt(unit, pos)) { u->Build(buildUDef, pos, au->GetBuildingFacing(), UNIT_COMMAND_OPTION_INTERNAL_ORDER, circuit->GetLastFrame() + FRAMES_PER_SEC * 60); utils::free_clear(friendlies); return; } } } utils::free_clear(friendlies); } // Alter/randomize position AIFloat3 pos; if (isShake) { AIFloat3 offset((float)rand() / RAND_MAX - 0.5f, 0.0f, (float)rand() / RAND_MAX - 0.5f); pos = position + offset * SQUARE_SIZE * 16; } else { pos = position; } const float searchRadius = 200.0f * SQUARE_SIZE; FindBuildSite(unit, pos, searchRadius); if (buildPos != -RgtVector) { terrainManager->AddBlocker(buildDef, buildPos, facing); u->Build(buildUDef, buildPos, facing, UNIT_COMMAND_OPTION_INTERNAL_ORDER, circuit->GetLastFrame() + FRAMES_PER_SEC * 60); } else { // TODO: Select new proper BasePos, like near metal cluster. int terWidth = terrainManager->GetTerrainWidth(); int terHeight = terrainManager->GetTerrainHeight(); float x = terWidth / 4 + rand() % (int)(terWidth / 2 + 1); float z = terHeight / 4 + rand() % (int)(terHeight / 2 + 1); AIFloat3 pos(x, circuit->GetMap()->GetElevationAt(x, z), z); circuit->GetSetupManager()->SetBasePos(pos); // Fallback to Guard/Assist/Patrol manager->FallbackTask(unit); } }
void CDefendTask::Update() { ++updCount; /* * Merge tasks if possible */ if (updCount % 32 == 1) { CMilitaryManager* militaryManager = static_cast<CMilitaryManager*>(manager); if ((attackPower >= maxPower) || !militaryManager->GetTasks(check).empty()) { IFighterTask* task = militaryManager->EnqueueTask(promote); decltype(units) tmpUnits = units; for (CCircuitUnit* unit : tmpUnits) { manager->AssignTask(unit, task); } // manager->DoneTask(this); // NOTE: RemoveAssignee will abort task return; } ISquadTask* task = GetMergeTask(); if (task != nullptr) { task->Merge(this); units.clear(); manager->AbortTask(this); return; } } /* * No regroup */ bool isExecute = (updCount % 8 == 2); if (!isExecute) { for (CCircuitUnit* unit : units) { isExecute |= unit->IsForceExecute(); } if (!isExecute) { return; } } else { ISquadTask::Update(); if (leader == nullptr) { // task aborted return; } } /* * Update target */ FindTarget(); CCircuitAI* circuit = manager->GetCircuit(); int frame = circuit->GetLastFrame() + FRAMES_PER_SEC * 60; state = State::ROAM; if (target != nullptr) { state = State::ENGAGE; for (CCircuitUnit* unit : units) { unit->Attack(target->GetPos(), frame); } } else { for (CCircuitUnit* unit : units) { AIFloat3 pos = utils::get_radial_pos(position, SQUARE_SIZE * 32); TRY_UNIT(circuit, unit, unit->GetUnit()->Fight(pos, UNIT_COMMAND_OPTION_RIGHT_MOUSE_KEY, frame); unit->GetUnit()->SetWantedMaxSpeed(MAX_UNIT_SPEED); ) } } }
void CBReclaimTask::Update() { if (!isMetal) { return; } CCircuitAI* circuit = manager->GetCircuit(); if (circuit->GetEconomyManager()->IsMetalFull()) { manager->AbortTask(this); } else if (!units.empty()) { /* * Update reclaim position */ // FIXME: Works only with 1 task per worker CCircuitUnit* unit = *units.begin(); int frame = circuit->GetLastFrame(); const AIFloat3& pos = unit->GetPos(frame); auto enemies = std::move(circuit->GetCallback()->GetEnemyUnitsIn(pos, 300.0f)); if (!enemies.empty()) { for (Unit* enemy : enemies) { if ((enemy != nullptr) && enemy->IsBeingBuilt()) { unit->GetUnit()->ReclaimUnit(enemy, UNIT_COMMAND_OPTION_INTERNAL_ORDER, frame + FRAMES_PER_SEC * 60); utils::free_clear(enemies); return; } } utils::free_clear(enemies); } auto features = std::move(circuit->GetCallback()->GetFeaturesIn(pos, 500.0f)); if (!features.empty()) { CTerrainManager* terrainManager = circuit->GetTerrainManager(); circuit->GetThreatMap()->SetThreatType(unit); AIFloat3 reclPos; float minSqDist = std::numeric_limits<float>::max(); Resource* metalRes = circuit->GetEconomyManager()->GetMetalRes(); for (Feature* feature : features) { AIFloat3 featPos = feature->GetPosition(); terrainManager->CorrectPosition(featPos); // Impulsed flying feature if (!terrainManager->CanBuildAt(unit, featPos)) { continue; } FeatureDef* featDef = feature->GetDef(); float reclaimValue = featDef->GetContainedResource(metalRes)/* * feature->GetReclaimLeft()*/; delete featDef; if (reclaimValue < 1.0f) { continue; } float sqDist = pos.SqDistance2D(featPos); if (sqDist < minSqDist) { reclPos = featPos; minSqDist = sqDist; } } if (minSqDist < std::numeric_limits<float>::max()) { const float radius = 8.0f; // unit->GetCircuitDef()->GetBuildDistance(); unit->GetUnit()->ReclaimInArea(reclPos, radius, UNIT_COMMAND_OPTION_INTERNAL_ORDER, frame + FRAMES_PER_SEC * 60); } utils::free_clear(features); } } }