Skip to main content

Win Conditions

A win condition is a reusable rule that inspects the round's Context and determines whether the round has been won. Phases decide when to evaluate a win condition. The win condition decides whether a winner exists.

Defining a win condition​

Register a win condition with WinCondition.new(name, evaluator):

RoundKit.WinCondition.new("LastAlive", function(context)
local players = context:GetPlayers()
local alive = {}

for _, player in ipairs(players) do
-- This metric should ideally be set by having an player.Character.Humanoid.Died event inside a phase.
if context:GetMetric(player, "Alive") ~= false then
table.insert(alive, player)
end
end

if #alive == 1 then
return {
Type = "Player",
Winners = alive,
}
end

return nil
end)

The evaluator receives the current Context and returns either:

  • nil if the round should continue.
  • A WinOutcome if a winner has been determined.
export type WinOutcome = {
Type: "Player" | "Team" | "Draw" | "None",
Winners: { Player | Team },
}

Register each win condition once, typically when your game starts. Registering the same name more than once raises an error.

Using a win condition​

Reference a registered win condition by name, or pass a WinCondition instance directly.

RoundKit.new({
Phases = Phases,
InitialPhase = "Lobby",
WinCondition = "LastAlive",
})

Win conditions are evaluated only while the active phase has EvaluateWinCondition = true (see Defining Phases).

When the evaluator returns a WinOutcome, RoundKit fires the OnWinConditionMet event.

Active = {
Name = "Active",
EvaluateWinCondition = true,

OnEnter = function(context)
context:Connect("OnWinConditionMet", function(outcome)
context:SetGlobalMetric("LastOutcome", outcome)
context:TransitionTo("Results")
end)
end,
}

A win condition never transitions phases directly. The phase decides how to respond to the outcome.

Combining win conditions​

Multiple win conditions can be combined with AnyOf and AllOf.

RoundKit.WinCondition.new("TimeLimit", function(context)
if context:GetElapsedTime() >= 120 then
return {
Type = "Draw",
Winners = {},
}
end

return nil
end)

RoundKit.new({
Phases = Phases,
InitialPhase = "Lobby",
WinCondition = RoundKit.WinCondition.AnyOf({
"LastAlive",
"TimeLimit",
}),
})

AnyOf​

Evaluates each child condition in order and returns the first non nil WinOutcome.

Use it when any condition should end the round.

AllOf​

Evaluates every child condition and returns a WinOutcome only after all of them succeed.

AllOf assumes every child agrees on the outcome. It returns the first successful WinOutcome; it does not merge multiple outcomes.

Both combinators accept registered condition names or WinCondition instances, and can be nested to build more complex logic.

The next section covers Player & Global State, which stores the data accessed through Context.