void CEnergyGrid::Init() { for (auto& kv : circuit->GetCircuitDefs()) { const std::map<std::string, std::string>& customParams = kv.second->GetUnitDef()->GetCustomParams(); auto it = customParams.find("pylonrange"); if (it != customParams.end()) { pylonRanges[kv.first] = utils::string_to_float(it->second); } } // FIXME: const names const char* names[] = {"armestor", "armsolar", "armwin"}; for (const char* name : names) { CCircuitDef* cdef = circuit->GetCircuitDef(name); rangePylons[pylonRanges[cdef->GetId()]] = cdef->GetId(); } CMetalManager* metalManager = circuit->GetMetalManager(); const CMetalData::Clusters& clusters = metalManager->GetClusters(); const CMetalData::Graph& clusterGraph = metalManager->GetGraph(); linkedClusters.resize(clusters.size(), false); links.reserve(boost::num_edges(clusterGraph)); CMetalData::Graph::edge_iterator edgeIt, edgeEnd; std::tie(edgeIt, edgeEnd) = boost::edges(clusterGraph); for (; edgeIt != edgeEnd; ++edgeIt) { const CMetalData::EdgeDesc& edgeId = *edgeIt; int idx0 = boost::source(edgeId, clusterGraph); int idx1 = boost::target(edgeId, clusterGraph); links.emplace_back(idx0, clusters[idx0].geoCentr, idx1, clusters[idx1].geoCentr); } linkIt = boost::make_iterator_property_map(&links[0], boost::get(&CMetalData::SEdge::index, clusterGraph)); ownedClusters = CMetalData::Graph(boost::num_vertices(clusterGraph)); }
CCircuitDef* CAllyTeam::GetFactoryToBuild(CCircuitAI* circuit) { for (int i = 0; i < factoryBuilds.size(); ++i) { CCircuitDef* cdef = circuit->GetCircuitDef(factoryBuilds[factoryIdx]); if (cdef->IsAvailable()) { return cdef; } AdvanceFactoryIdx(); } return nullptr; }
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 CEnergyGrid::ReadConfig() { const Json::Value& root = circuit->GetSetupManager()->GetConfig(); const std::string& cfgName = circuit->GetSetupManager()->GetConfigName(); const Json::Value& pylon = root["economy"]["energy"]["pylon"]; for (const Json::Value& pyl : pylon) { CCircuitDef* cdef = circuit->GetCircuitDef(pyl.asCString()); if (cdef == nullptr) { circuit->LOG("CONFIG %s: has unknown UnitDef '%s'", cfgName.c_str(), pyl.asCString()); } else { rangePylons[pylonRanges[cdef->GetId()]] = cdef->GetId(); } } }
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 IFighterTask::AssignTo(CCircuitUnit* unit) { IUnitTask::AssignTo(unit); CCircuitDef* cdef = unit->GetCircuitDef(); attackPower += cdef->GetPower(); if (unit->GetShield() != nullptr) { shields.insert(unit); } if (unit->HasDGun()) { const float range = std::max(cdef->GetDGunRange() * 1.1f, cdef->GetLosRadius()); CDGunAction* act = new CDGunAction(unit, range); unit->PushBack(act); } }
void CThreatMap::DelEnemyUnit(const CEnemyUnit* e) { CCircuitDef* cdef = e->GetCircuitDef(); if (cdef == nullptr) { DelEnemyUnitAll(e); return; } if (cdef->HasAntiAir()) { DelEnemyAir(e); } if (cdef->HasAntiLand() || cdef->HasAntiWater()) { DelEnemyAmph(e); } DelDecloaker(e); }
void CThreatMap::AddEnemyMetal(const CEnemyUnit* e, const float scale) { CCircuitDef* cdef = e->GetCircuitDef(); assert(cdef != nullptr); const float cost = cdef->GetCost() * scale; if (cdef->IsAbleToFly()) { airMetal += cost; } else if (cdef->IsMobile()) { STerrainMapMobileType* mt = circuit->GetTerrainManager()->GetMobileTypeById(cdef->GetMobileId()); if (mt->maxElevation > SQUARE_SIZE * 5) { landMetal += cost; } if (mt->minElevation < -SQUARE_SIZE * 5) { waterMetal += cost; } } else { STerrainMapImmobileType* it = circuit->GetTerrainManager()->GetImmobileTypeById(cdef->GetImmobileId()); if (it->maxElevation > SQUARE_SIZE * 5) { landMetal += cost; } if (it->minElevation < -SQUARE_SIZE * 5) { waterMetal += cost; } if (cdef->HasAntiLand()) { staticMetal += cost; } } }
void CThreatMap::AddEnemyUnit(const CEnemyUnit* e, const float scale) { CCircuitDef* cdef = e->GetCircuitDef(); if (cdef == nullptr) { AddEnemyUnitAll(e, scale); return; } if (cdef->IsAntiAir()) { AddEnemyUnit(e, airThreat, scale); } if (cdef->IsAntiLand()) { AddEnemyUnit(e, landThreat, scale); } if (cdef->IsAntiWater()) { AddEnemyUnit(e, waterThreat, scale); } AddDecloaker(e, scale); }
void CPathFinder::SetMapData(CCircuitUnit* unit, CThreatMap* threatMap, int frame) { CCircuitDef* cdef = unit->GetCircuitDef(); STerrainMapMobileType::Id mobileTypeId = cdef->GetMobileId(); bool* moveArray = (mobileTypeId < 0) ? airMoveArray : moveArrays[mobileTypeId]; float* costArray; if ((unit->GetPos(frame).y < .0f) && !cdef->IsSonarStealth()) { costArray = threatMap->GetAmphThreatArray(); // cloak doesn't work under water } else if (unit->GetUnit()->IsCloaked()) { costArray = threatMap->GetCloakThreatArray(); } else if (cdef->IsAbleToFly()) { costArray = threatMap->GetAirThreatArray(); } else if (cdef->IsAmphibious()) { costArray = threatMap->GetAmphThreatArray(); } else { costArray = threatMap->GetSurfThreatArray(); } micropather->SetMapData(moveArray, costArray); }
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; }
void CThreatMap::SetEnemyUnitRange(CEnemyUnit* e) const { CCircuitDef* cdef = e->GetCircuitDef(); assert(cdef != nullptr); const int slack = DEFAULT_SLACK * (cdef->IsMobile() ? 4 : 2); int range; int maxRange; range = cdef->HasAntiAir() ? ((int)cdef->GetMaxRange(CCircuitDef::RangeType::AIR) + slack) / squareSize : 0; e->SetRange(CEnemyUnit::RangeType::AIR, range); maxRange = range; range = cdef->HasAntiLand() ? ((int)cdef->GetMaxRange(CCircuitDef::RangeType::LAND) + slack) / squareSize : 0; e->SetRange(CEnemyUnit::RangeType::LAND, range); maxRange = std::max(maxRange, range); range = cdef->HasAntiWater() ? ((int)cdef->GetMaxRange(CCircuitDef::RangeType::WATER) + slack) / squareSize : 0; e->SetRange(CEnemyUnit::RangeType::WATER, range); maxRange = std::max(maxRange, range); e->SetRange(CEnemyUnit::RangeType::MAX, maxRange); e->SetRange(CEnemyUnit::RangeType::CLOAK, GetCloakRange(e)); }
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); }
void CAllyTeam::Init(CCircuitAI* circuit) { if (initCount++ > 0) { return; } TeamRulesParam* trp = circuit->GetTeam()->GetTeamRulesParamByName("start_box_id"); if (trp != nullptr) { int boxId = trp->GetValueFloat(); startBox = circuit->GetGameAttribute()->GetSetupData().GetStartBox(boxId); delete trp; } metalManager = std::make_shared<CMetalManager>(circuit, &circuit->GetGameAttribute()->GetMetalData()); if (metalManager->HasMetalSpots() && !metalManager->HasMetalClusters() && !metalManager->IsClusterizing()) { metalManager->ClusterizeMetal(); } // Init after parallel clusterization circuit->GetScheduler()->RunParallelTask(CGameTask::emptyTask, std::make_shared<CGameTask>(&CMetalManager::Init, metalManager)); energyLink = std::make_shared<CEnergyGrid>(circuit); defence = std::make_shared<CDefenceMatrix>(circuit); pathfinder = std::make_shared<CPathFinder>(&circuit->GetGameAttribute()->GetTerrainData()); // TODO: Move factory selection into CFactoryManager? // Can't figure how as this should work per ally team. const char* factories[] = { "factorycloak", "factoryamph", "factoryhover", "factoryjump", "factoryshield", "factoryspider", "factorytank", "factoryveh", "factoryplane", "factorygunship", "factoryship", }; const int size = sizeof(factories) / sizeof(factories[0]); factoryBuilds.reserve(size); std::map<STerrainMapMobileType::Id, float> percents; CTerrainData& terrainData = circuit->GetGameAttribute()->GetTerrainData(); const std::vector<STerrainMapImmobileType>& immobileType = terrainData.areaData0.immobileType; const std::vector<STerrainMapMobileType>& mobileType = terrainData.areaData0.mobileType; for (const char* fac : factories) { CCircuitDef* cdef = circuit->GetCircuitDef(fac); STerrainMapImmobileType::Id itId = cdef->GetImmobileId(); if ((itId < 0) || !immobileType[itId].typeUsable) { continue; } STerrainMapMobileType::Id mtId = cdef->GetMobileId(); if (mtId < 0) { factoryBuilds.push_back(cdef->GetId()); percents[cdef->GetId()] = 60.0 + rand() / (float)RAND_MAX * 50.0; } else if (mobileType[mtId].typeUsable) { factoryBuilds.push_back(cdef->GetId()); float shift = rand() / (float)RAND_MAX * 40.0 - 20.0; percents[cdef->GetId()] = mobileType[mtId].areaLargest->percentOfMap + shift; } } auto cmp = [circuit, &percents](const CCircuitDef::Id aId, const CCircuitDef::Id bId) { return percents[aId] > percents[bId]; }; std::sort(factoryBuilds.begin(), factoryBuilds.end(), cmp); }