169 lines
6.5 KiB
C++
169 lines
6.5 KiB
C++
#include "game/ai/tasks.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace sots::ai {
|
|
namespace {
|
|
|
|
struct Row {
|
|
const char* name;
|
|
int priority;
|
|
};
|
|
|
|
// Indexed by task type id. Both retired ids keep their priority and carry no name.
|
|
constexpr Row kTable[kTaskTypeCount] = {
|
|
/* 0x00 */ {"AITSteamroll", 1250},
|
|
/* 0x01 */ {"AITExplore", 600},
|
|
/* 0x02 */ {"AITExploreInForce", 550},
|
|
/* 0x03 */ {"AITEscortGate", 700},
|
|
/* 0x04 */ {"AITEscortGateInvade", 400},
|
|
/* 0x05 */ {"AITEscortGateInvadeGoal", 950},
|
|
/* 0x06 */ {"AITDeployGateAt", 1400},
|
|
/* 0x07 */ {"AITColonize", 900},
|
|
/* 0x08 */ {"AITColonizeGoal", 970},
|
|
/* 0x09 */ {"AITColonizeAt", 1300},
|
|
/* 0x0a */ {"AITInvade", 500},
|
|
/* 0x0b */ {"AITInvadeGate", 1000},
|
|
/* 0x0c */ {"AITInvadeGoal", 930},
|
|
/* 0x0d */ {"", 200},
|
|
/* 0x0e */ {"AITDefendColonyIncoming", 1100},
|
|
/* 0x0f */ {"", 300},
|
|
/* 0x10 */ {"AITDefendGateIncoming", 1200},
|
|
/* 0x11 */ {"AITKillEasterEgg", 800},
|
|
/* 0x12 */ {"AITInterceptEnemy", 850},
|
|
/* 0x13 */ {"AITMining", 350},
|
|
/* 0x14 */ {"AITMiningReturn", 375},
|
|
/* 0x15 */ {"AITAttackBlockade", 100},
|
|
/* 0x16 */ {"AITAdvanceIdleShips", 0},
|
|
/* 0x17 */ {"AITStockFreighters", 50},
|
|
/* 0x18 */ {"AITRespondAttackSystem", 980},
|
|
/* 0x19 */ {"AITRespondDefendSystem", 990},
|
|
/* 0x1a */ {"AITNodeBore", 1275},
|
|
/* 0x1b */ {"AITBuildStations", 910},
|
|
/* 0x1c */ {"AITBuildPoliceShips", 75},
|
|
/* 0x1d */ {"AITBuildDeepScanShips", 60},
|
|
/* 0x1e */ {"AITRaid", 399},
|
|
/* 0x1f */ {"AITRetrieveArtifact", 1},
|
|
/* 0x20 */ {"AITReturnArtifact", 2},
|
|
};
|
|
|
|
// The two artifact tasks ignore their table entries entirely and return these instead. Keeping
|
|
// them here rather than in kTable is deliberate: the table values are real, reachable through
|
|
// nothing, and a future reader who "fixes" the table would be wrong.
|
|
constexpr int kRetrieveArtifactPriority = 1260;
|
|
constexpr int kReturnArtifactPriority = 1261;
|
|
|
|
constexpr bool InRange(TaskType t) {
|
|
const int i = static_cast<int>(t);
|
|
return i >= 0 && i < kTaskTypeCount;
|
|
}
|
|
|
|
// The shared tail every arm ends with, in call order.
|
|
void AppendCommonTail(std::vector<TaskType>& out, bool policeShips, bool deepScanShips) {
|
|
out.push_back(TaskType::InterceptEnemy);
|
|
out.push_back(TaskType::Mining);
|
|
out.push_back(TaskType::MiningReturn);
|
|
out.push_back(TaskType::AdvanceIdleShips);
|
|
out.push_back(TaskType::Raid);
|
|
out.push_back(TaskType::StockFreighters);
|
|
out.push_back(TaskType::AttackBlockade);
|
|
out.push_back(TaskType::BuildStations);
|
|
if (policeShips) out.push_back(TaskType::BuildPoliceShips);
|
|
if (deepScanShips) out.push_back(TaskType::BuildDeepScanShips);
|
|
}
|
|
|
|
// The goal group: one creator that builds four families.
|
|
void AppendGoalGroup(std::vector<TaskType>& out) {
|
|
out.push_back(TaskType::ColonizeGoal);
|
|
out.push_back(TaskType::EscortGateInvadeGoal);
|
|
out.push_back(TaskType::Invade);
|
|
out.push_back(TaskType::InvadeGoal);
|
|
}
|
|
|
|
// The two defensive families, behind the policy gate. DefendGateIncoming is Hiver-only.
|
|
void AppendDefensive(std::vector<TaskType>& out, bool policyNonZero, sim::Species species) {
|
|
if (!policyNonZero) return;
|
|
if (species == sim::Species::Hiver) out.push_back(TaskType::DefendGateIncoming);
|
|
out.push_back(TaskType::DefendColonyIncoming);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const char* TaskTypeName(TaskType t) { return InRange(t) ? kTable[static_cast<int>(t)].name : ""; }
|
|
|
|
int TablePriority(TaskType t) { return InRange(t) ? kTable[static_cast<int>(t)].priority : 0; }
|
|
|
|
int PriorityOf(const RankedTask& t, const TaskPriorityPolicy& policy) {
|
|
switch (t.type) {
|
|
case TaskType::RetrieveArtifact:
|
|
return kRetrieveArtifactPriority;
|
|
case TaskType::ReturnArtifact:
|
|
return kReturnArtifactPriority;
|
|
case TaskType::Invade:
|
|
return t.priorityFlagBit0 ? policy.flaggedInvade : TablePriority(t.type);
|
|
case TaskType::EscortGateInvade:
|
|
return t.priorityFlagBit0 ? policy.flaggedEscortGateInvade : TablePriority(t.type);
|
|
case TaskType::AttackBlockade:
|
|
return t.overridePriority ? t.priority : TablePriority(t.type);
|
|
default:
|
|
return TablePriority(t.type);
|
|
}
|
|
}
|
|
|
|
void Rank(std::vector<RankedTask>& tasks, const TaskPriorityPolicy& policy) {
|
|
// std::stable_sort, not the introsort in game/config/msvc_sort.h: the original sorts a
|
|
// std::list, and list::sort is a merge sort -- stable by construction, in every library.
|
|
// The comparison is a strict greater-than on the priority, so equal keys never move.
|
|
std::stable_sort(tasks.begin(), tasks.end(),
|
|
[&policy](const RankedTask& a, const RankedTask& b) {
|
|
return PriorityOf(a, policy) > PriorityOf(b, policy);
|
|
});
|
|
}
|
|
|
|
std::vector<TaskType> CreationOrder(sim::Species species, bool policyNonZero) {
|
|
std::vector<TaskType> out;
|
|
if (BuildsNoTasks(species)) return out;
|
|
|
|
if (species == sim::Species::Hiver) {
|
|
out.push_back(TaskType::Steamroll);
|
|
out.push_back(TaskType::Colonize);
|
|
out.push_back(TaskType::ColonizeAt);
|
|
AppendDefensive(out, policyNonZero, species);
|
|
out.push_back(TaskType::EscortGateInvade);
|
|
out.push_back(TaskType::InvadeGate);
|
|
out.push_back(TaskType::DeployGateAt);
|
|
out.push_back(TaskType::EscortGate);
|
|
AppendGoalGroup(out);
|
|
AppendCommonTail(out, /*policeShips=*/true, /*deepScanShips=*/true);
|
|
return out;
|
|
}
|
|
|
|
if (species == sim::Species::Zuul) {
|
|
out.push_back(TaskType::Steamroll);
|
|
out.push_back(TaskType::NodeBore);
|
|
out.push_back(TaskType::Colonize);
|
|
out.push_back(TaskType::ColonizeAt);
|
|
AppendDefensive(out, policyNonZero, species);
|
|
out.push_back(TaskType::KillEasterEgg);
|
|
out.push_back(TaskType::Invade);
|
|
out.push_back(TaskType::ExploreInForce);
|
|
AppendGoalGroup(out);
|
|
AppendCommonTail(out, /*policeShips=*/false, /*deepScanShips=*/true);
|
|
return out;
|
|
}
|
|
|
|
// Human, Tarkas, Liir, Morrigi.
|
|
out.push_back(TaskType::Steamroll);
|
|
out.push_back(TaskType::Colonize);
|
|
out.push_back(TaskType::ColonizeAt);
|
|
AppendDefensive(out, policyNonZero, species);
|
|
out.push_back(TaskType::KillEasterEgg);
|
|
out.push_back(TaskType::Invade);
|
|
out.push_back(TaskType::Explore);
|
|
out.push_back(TaskType::ExploreInForce);
|
|
AppendGoalGroup(out);
|
|
AppendCommonTail(out, /*policeShips=*/true, /*deepScanShips=*/true);
|
|
return out;
|
|
}
|
|
|
|
} // namespace sots::ai
|