载入中...
搜索中...
未找到
RTSMatch.cpp
浏览该文件的文档.
1#include "rts/RTSMatch.h"
2
3#include <algorithm>
4#include <cmath>
5#include <set>
6#include <string>
7#include <utility>
8
9namespace eve::rts {
10namespace {
11
12void emit(Match& match, std::string kind, Faction* faction, int team, std::string reason = {}) {
13 auto state = match.state();
14 match.events()->values.push_back({++state->updateSequence, std::move(kind),
15 faction == nullptr ? SubjectRef{} : faction->identity()->subject,
16 team, std::move(reason)});
17}
18
19Match::Participants::Entry* participant(Match& match, Faction& faction) {
20 const auto found = std::find_if(match.participants()->entries.begin(), match.participants()->entries.end(),
21 [&](auto& value) { return value.faction.resolve() == &faction; });
22 return found == match.participants()->entries.end() ? nullptr : &*found;
23}
24
25void eliminate(Match& match, Match::Participants::Entry& entry, std::string reason, bool surrendered = false) {
26 if (entry.eliminated) return;
27 entry.eliminated = true;
28 entry.surrendered = surrendered;
29 entry.reason = reason;
30 auto* faction = dynamic_cast<Faction*>(entry.faction.resolve());
31 emit(match, "faction_eliminated", faction, entry.team, std::move(reason));
32}
33
34bool owns(const FactionLink& link, Faction& faction) { return link.resolve() == &faction; }
35
36bool survives(Faction& faction, const Match::Rules& rules) {
37 if (rules.rule == VictoryRule::DestroyHeadquarters) {
38 auto buildings = ecs::View<Building, Building::Definition, Building::Faction, Building::Integrity,
39 Building::Construction>();
40 for (auto it = buildings.begin(); it != buildings.end(); ++it) {
41 auto [definition, owner, integrity, construction] = *it;
42 if (owns(owner->link, faction) && integrity->alive && integrity->state.health > 0.0 &&
43 construction->progress >= 1.0f && definition->id.name() == rules.archetype)
44 return true;
45 }
46 return false;
47 }
48 auto units = ecs::View<Unit, Unit::Faction, Unit::Durability>();
49 for (auto it = units.begin(); it != units.end(); ++it) {
50 auto [owner, durability] = *it;
51 if (owns(owner->link, faction) && durability->alive && durability->state.health > 0.0) return true;
52 }
53 auto buildings = ecs::View<Building, Building::Faction, Building::Integrity>();
54 for (auto it = buildings.begin(); it != buildings.end(); ++it) {
55 auto [owner, integrity] = *it;
56 if (owns(owner->link, faction) && integrity->alive && integrity->state.health > 0.0) return true;
57 }
58 return false;
59}
60
61void destroyFactionEntities(Faction& faction) {
62 auto units = ecs::View<Unit, Unit::Faction, Unit::Durability>();
63 for (auto it = units.begin(); it != units.end(); ++it) {
64 auto [owner, durability] = *it;
65 if (owns(owner->link, faction)) {
66 durability->alive = false;
67 durability->state.health = 0.0;
68 }
69 }
70 auto buildings = ecs::View<Building, Building::Faction, Building::Integrity>();
71 for (auto it = buildings.begin(); it != buildings.end(); ++it) {
72 auto [owner, integrity] = *it;
73 if (owns(owner->link, faction)) {
74 integrity->alive = false;
75 integrity->state.health = 0.0;
76 }
77 }
78}
79
80void settleTeams(Match& match) {
81 std::set<int> teams;
82 for (auto& entry : match.participants()->entries)
83 if (!entry.eliminated && entry.faction.resolve() != nullptr) teams.insert(entry.team);
84 if (teams.size() > 1) return;
85 match.state()->phase = MatchPhase::Finished;
86 match.state()->winningTeam = teams.empty() ? -1 : *teams.begin();
87 emit(match, teams.empty() ? "match_draw" : "match_won", nullptr, match.state()->winningTeam,
88 teams.empty() ? "no_survivors" : "last_team_standing");
89}
90
91} // namespace
92
94 if (match.state()->phase != MatchPhase::Setup)
96 DiagnosticCode::Conflict, "cannot add an RTS participant after match start", "match", {}, "rts.match"));
97 if (participant(match, faction) != nullptr)
98 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "RTS faction is already a participant",
99 "faction", {}, "rts.match"));
100 auto link = FactionLink::bind(ecs::handle_of(&faction));
101 if (!link) return Result<void>::failure(link.status());
102 match.participants()->entries.push_back({std::move(link).takeValue(), team, false, false, {}});
104}
105
107 if (match.state()->phase != MatchPhase::Setup)
109 Diagnostic::error(DiagnosticCode::Conflict, "RTS match is not in setup", "match.phase", {}, "rts.match"));
110 if (match.participants()->entries.size() < 2)
112 "RTS match requires at least two factions", "participants", {},
113 "rts.match"));
114 for (const auto& entry : match.participants()->entries)
115 if (entry.faction.resolve() == nullptr)
117 DiagnosticCode::StaleHandle, "RTS match contains a stale faction", "participants", {}, "rts.match"));
118 const auto& rules = *match.rules();
119 if (rules.rule == VictoryRule::DestroyHeadquarters && rules.archetype.empty())
121 DiagnosticCode::InvalidArgument, "headquarters victory requires an archetype", "rules", {}, "rts.match"));
122 if (rules.rule == VictoryRule::ResourceTarget &&
123 (rules.archetype.empty() || rules.targetValue <= 0.0 || !std::isfinite(rules.targetValue)))
125 DiagnosticCode::InvalidArgument, "resource victory requires a positive target", "rules", {}, "rts.match"));
126 match.state()->phase = MatchPhase::Running;
127 match.state()->winningTeam = -1;
128 emit(match, "match_started", nullptr, -1);
130}
131
133 if (match.state()->phase != MatchPhase::Running)
135 Diagnostic::error(DiagnosticCode::Conflict, "RTS match is not running", "match.phase", {}, "rts.match"));
136 auto* entry = participant(match, faction);
137 if (entry == nullptr || entry->eliminated)
139 DiagnosticCode::NotFound, "RTS faction is not an active participant", "faction", {}, "rts.match"));
140 destroyFactionEntities(faction);
141 eliminate(match, *entry, "surrender", true);
142 settleTeams(match);
144}
145
147 if (match.state()->phase != MatchPhase::Running)
149 std::size_t eliminated = 0;
150 if (match.rules()->rule == VictoryRule::ResourceTarget) {
151 if (!resources)
153 DiagnosticCode::PreconditionViolation, "resource victory requires an authoritative economy query",
154 "resources", {}, "rts.match"));
155 for (auto& entry : match.participants()->entries) {
156 if (entry.eliminated) continue;
157 auto* faction = dynamic_cast<Faction*>(entry.faction.resolve());
158 if (faction == nullptr) {
159 eliminate(match, entry, "stale_faction");
160 ++eliminated;
161 continue;
162 }
163 auto balance = resources(*faction, match.rules()->archetype);
164 if (!balance) return Result<std::size_t>::failure(balance.status());
165 if (balance.value() >= match.rules()->targetValue) {
166 match.state()->phase = MatchPhase::Finished;
167 match.state()->winningTeam = entry.team;
168 emit(match, "match_won", faction, entry.team, "resource_target");
170 }
171 }
172 } else {
173 for (auto& entry : match.participants()->entries) {
174 if (entry.eliminated) continue;
175 auto* faction = dynamic_cast<Faction*>(entry.faction.resolve());
176 if (faction == nullptr || !survives(*faction, *match.rules())) {
177 eliminate(match, entry, faction == nullptr ? "stale_faction" :
178 match.rules()->rule == VictoryRule::DestroyHeadquarters ? "headquarters_destroyed" :
179 "annihilated");
180 ++eliminated;
181 }
182 }
183 }
184 settleTeams(match);
185 return Result<std::size_t>::success(eliminated,
187}
188
189} // namespace eve::rts
double value
std::unordered_map< std::string, QuestRuntime > entries
TokenKind kind
std::vector< std::weak_ptr< DeviceBytes > > resources
Definition OnnxGpgpu.cpp:55
Deterministic match lifecycle and victory systems.
bool found
std::vector< UnitCandidate > units
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Faction domain root; membership is a set of typed runtime handles.
Definition RTSTypes.h:1178
static Result< std::size_t > step(Match &match, const MatchResourceQuery &resources={})
Evaluate the configured victory rule and settle a winner or draw.
Definition RTSMatch.cpp:146
static Result< void > start(Match &match)
Validate rules/participants and transition Setup to Running.
Definition RTSMatch.cpp:106
static Result< void > addParticipant(Match &match, Faction &faction, int team)
Add one live faction before match start.
Definition RTSMatch.cpp:93
static Result< void > surrender(Match &match, Faction &faction)
Eliminate a participant and all of its live RTS entities.
Definition RTSMatch.cpp:132
Independent match composition root; factions may participate in different matches.
Definition RTSTypes.h:1295
std::function< Result< double >(Faction &, std::string_view)> MatchResourceQuery
Reads a resource balance from the game-owned canonical economy account.
Definition RTSMatch.h:16
EntityLink< FactionLinkTag > FactionLink
Definition RTSTypes.h:285
::eve::SubjectRef SubjectRef
Strong reference to a runtime subject.
Definition Targeting.h:76