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); }
CEnemyUnit* CAttackTask::FindBestTarget(CCircuitUnit* unit, float& minSqDist) { CCircuitAI* circuit = manager->GetCircuit(); CTerrainManager* terrainManager = circuit->GetTerrainManager(); CThreatMap* threatMap = circuit->GetThreatMap(); const AIFloat3& pos = unit->GetUnit()->GetPos(); STerrainMapArea* area = unit->GetArea(); float power = threatMap->GetUnitThreat(unit); int canTargetCat = unit->GetCircuitDef()->GetTargetCategory(); CEnemyUnit* bestTarget = nullptr; minSqDist = std::numeric_limits<float>::max(); threatMap->SetThreatType(unit); const CCircuitAI::EnemyUnits& enemies = circuit->GetEnemyUnits(); for (auto& kv : enemies) { CEnemyUnit* enemy = kv.second; if (enemy->IsHidden() || (threatMap->GetThreatAt(enemy->GetPos()) >= power) || !terrainManager->CanMoveToPos(area, enemy->GetPos())) { continue; } if (((canTargetCat & circuit->GetWaterCategory()) == 0) && (enemy->GetPos().y < -SQUARE_SIZE * 4)) { continue; } CCircuitDef* edef = enemy->GetCircuitDef(); if ((edef != nullptr) && ((edef->GetCategory() & canTargetCat) == 0)) { continue; } float sqDist = pos.SqDistance2D(enemy->GetPos()); if (sqDist < minSqDist) { bestTarget = enemy; minSqDist = sqDist; } } return bestTarget; }