Three fixups the Phase 1-3 work missed (would bite the first time an admin
flipped on a real event):
* IsDisplayTips + TipsId added to ColosseumSeasonConfig. Drives
Wizard/ColosseumEntryInfoTask.cs:129 (NeedsFirstTips = is_display_tips == 1).
The wire DTO already had the properties; the season config didn't carry
them, so admins had no way to gate the first-time tutorial tip popup.
* colosseum_id is now emitted from SeasonId. Wire field was always-absent
before (DTO had the property but builder never set it); client fell back
to 0 and couldn't disambiguate cups.
* ColosseumLobbyInfoBuilder.Build now takes ColosseumRoundsConfig + nowUtc
and derives the wire is_round_period / now_round / next_round / start_time
/ end_time from the rounds schedule against the current clock. Previous
hardcode (is_round_period: true, now_round: 1, event-window dates) would
have shown "Round 1 ends 2026-07-31" instead of the actual round's end.
Per the client parser at ColosseumEntryInfoTask.cs:111-126:
- inside a round → emit that round's window + is_round_period=true
- between rounds → emit next round's start as start_time + next_round
- pre-event → next_round=1, start_time=event start
- post-event → anchor to last round so the client doesn't NRE
- no rounds configured → fall back to event-level window
Why these were missed: I implemented ColosseumSeasonConfig from the plan's
explicit field list rather than cross-checking against the captured
colosseum.json wire shape; I added ColosseumId to the wire DTO without
wiring the builder side; and no test exercised a mid-event scenario with
realistic dates — Phase 2's E2E hand-edits run.RoundId via
PromoteRunToRoundAsync instead of letting the controller derive it.
* 10 new builder unit tests cover off-season / mid-round / between-rounds /
pre-event / post-event / empty-rounds-fallback + the new field surfacing.
Full suite: 1355/1355 (was 1345 + 10).
77 lines
3.2 KiB
C#
77 lines
3.2 KiB
C#
using SVSim.Database.Enums;
|
|
|
|
namespace SVSim.Database.Models.Config;
|
|
|
|
/// <summary>
|
|
/// Event-level configuration for an active Arena Colosseum (Grand Prix) season. Default
|
|
/// <see cref="ShippedDefaults"/> emits <c>IsColosseumPeriod = false</c> so the lobby read
|
|
/// endpoints render an empty "no event scheduled" payload without crashing the client.
|
|
/// Flipping the event on is an admin operation per
|
|
/// <c>docs/operations/grand-prix-event-setup.md</c> — write a row to <c>GameConfigs</c>.
|
|
/// </summary>
|
|
[ConfigSection("ColosseumSeason")]
|
|
public class ColosseumSeasonConfig
|
|
{
|
|
/// <summary>Master gate. <c>false</c> = lobby reads render an empty info block and
|
|
/// entry rejects. The client (<c>Wizard/ColosseumEntryInfoTask.cs</c>) reads this and
|
|
/// skips parsing the rest of the colosseum_info object.</summary>
|
|
public bool IsColosseumPeriod { get; set; }
|
|
|
|
/// <summary>Stamped onto every <see cref="ViewerArenaColosseumRun"/> at entry time.</summary>
|
|
public int SeasonId { get; set; }
|
|
|
|
public string ColosseumName { get; set; } = "";
|
|
|
|
/// <summary>Bracket format. Stamped onto the run at entry time.</summary>
|
|
public Format DeckFormat { get; set; } = Format.Rotation;
|
|
|
|
/// <summary>Server stores bool; the wire shape is the STRING "0"/"1" per Wizard/ColosseumEntryInfoTask.cs's
|
|
/// <c>jsonData.ToString() == "1"</c> parse. The response DTO converts at serialization time.</summary>
|
|
public bool IsNormalTwoPick { get; set; }
|
|
|
|
/// <summary>Wire string used by the client as a theme/color code. Empty when not in special mode.</summary>
|
|
public string IsSpecialMode { get; set; } = "";
|
|
|
|
public string? AnnounceId { get; set; }
|
|
|
|
public DateTime EventStartTime { get; set; }
|
|
public DateTime EventEndTime { get; set; }
|
|
|
|
/// <summary>How many bracket entries get eliminated in the final round before champion-determination.</summary>
|
|
public int FinalRoundEliminateCount { get; set; }
|
|
|
|
public string CardPoolName { get; set; } = "";
|
|
|
|
/// <summary>Card-set ids used as the 2-Pick / Chaos draft pool override for this season.
|
|
/// Phase 3 reads this through <c>ArenaTwoPickCardPoolService</c> instead of
|
|
/// <c>ChallengeConfig.PoolCardSetIds</c>.</summary>
|
|
public List<int> PoolCardSetIds { get; set; } = new();
|
|
|
|
public int RupyCost { get; set; }
|
|
public int TicketCost { get; set; }
|
|
public int CrystalCost { get; set; }
|
|
|
|
public bool IsAllowedFreeEntry { get; set; }
|
|
|
|
public bool IsAllCardEnabled { get; set; }
|
|
|
|
/// <summary>Number of strategies offered per pick in 2-Pick Chaos mode.</summary>
|
|
public int StrategyPickNum { get; set; }
|
|
|
|
/// <summary>Drives the client's first-time tutorial tip popup for this cup
|
|
/// (<c>Wizard/ColosseumEntryInfoTask.cs:129</c>: <c>NeedsFirstTips = (is_display_tips == 1)</c>).
|
|
/// Server emits wire int 0/1.</summary>
|
|
public bool IsDisplayTips { get; set; }
|
|
|
|
/// <summary>Tutorial tip content id, paired with <see cref="IsDisplayTips"/>. Wire int.</summary>
|
|
public int TipsId { get; set; }
|
|
|
|
public DateTime SalesPeriodStart { get; set; }
|
|
public DateTime SalesPeriodEnd { get; set; }
|
|
|
|
public static ColosseumSeasonConfig ShippedDefaults() => new()
|
|
{
|
|
IsColosseumPeriod = false,
|
|
};
|
|
}
|