GeoSus
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
using GeoSus.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Sub-phase derived client-side from the timestamps in MeetingStartedPayload.
|
||||
/// Server doesn't broadcast a discrete "discussion ended" event - it embeds
|
||||
/// DiscussionEndTime and VotingEndTime in the meeting-start event and gates
|
||||
/// vote acceptance on those timestamps. We compute the matching client view
|
||||
/// by comparing UtcNow to those values every frame.
|
||||
/// </summary>
|
||||
public enum MeetingSubPhase
|
||||
{
|
||||
Arrival, // before ArrivalDeadline; players are still en route to meeting point
|
||||
Discussion, // arrival deadline passed; talk only, votes server-rejected
|
||||
Voting, // discussion ended; votes accepted until VotingEndTime
|
||||
Resolved // VotingClosed received OR votingEndTime in the past
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Single source of truth for all in-game state on the client.
|
||||
/// Updated exclusively by GameManager_Network; read by GameManager_UI.
|
||||
@@ -12,6 +28,15 @@ public class GameState
|
||||
public PlayerRole? MyRole { get; set; }
|
||||
public bool IsDead { get; set; }
|
||||
|
||||
// ── Settings (P13b) ───────────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// Per-lobby settings snapshot from the server. Populated on
|
||||
/// LobbyJoined / LobbyCreated; immutable for the lifetime of the lobby.
|
||||
/// Null on old server builds - callers must use null-coalescing fallbacks
|
||||
/// to whatever default they previously hardcoded.
|
||||
/// </summary>
|
||||
public GameSettings Settings { get; set; }
|
||||
|
||||
// ── Tasks ─────────────────────────────────────────────────────────────────
|
||||
public List<GameTask> MyTasks { get; set; } = new List<GameTask>();
|
||||
public HashSet<string> MyCompletedTaskIds { get; set; } = new HashSet<string>();
|
||||
@@ -22,13 +47,69 @@ public class GameState
|
||||
public List<PlayerInfo> Players { get; set; } = new List<PlayerInfo>();
|
||||
|
||||
// ── Meeting ───────────────────────────────────────────────────────────────
|
||||
public MeetingStartedPayload ActiveMeeting { get; set; }
|
||||
public VotingClosedPayload LastVoteResult { get; set; }
|
||||
public HashSet<string> VotedPlayerIds { get; set; } = new HashSet<string>();
|
||||
public MeetingStartedPayload ActiveMeeting { get; set; }
|
||||
public VotingClosedPayload LastVoteResult { get; set; }
|
||||
public HashSet<string> VotedPlayerIds { get; set; } = new HashSet<string>();
|
||||
public HashSet<string> ArrivedPlayerIds { get; set; } = new HashSet<string>();
|
||||
|
||||
/// <summary>Per-voter latest vote target. Voter ClientUuid -> target ClientUuid, or VoteSkip for skip.</summary>
|
||||
public Dictionary<string, string> VoterTargets { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>Live vote tallies, keyed by target ClientUuid or VoteSkip. Derived from VoterTargets.</summary>
|
||||
public Dictionary<string, int> VoteTallies { get; set; } = new Dictionary<string, int>();
|
||||
|
||||
/// <summary>Local player's latest vote target. Null = haven't voted; VoteSkip = skip; otherwise target ClientUuid.</summary>
|
||||
public string MyVoteTarget { get; set; }
|
||||
|
||||
/// <summary>Sentinel for "skip" votes in VoterTargets / VoteTallies / MyVoteTarget.</summary>
|
||||
public const string VoteSkip = "__SKIP__";
|
||||
|
||||
/// <summary>
|
||||
/// Derive the current meeting sub-phase from ActiveMeeting + LastVoteResult.
|
||||
/// Returns Arrival when no meeting is active (caller should also gate on Phase).
|
||||
/// </summary>
|
||||
public MeetingSubPhase GetMeetingSubPhase()
|
||||
{
|
||||
if (LastVoteResult != null) return MeetingSubPhase.Resolved;
|
||||
var m = ActiveMeeting;
|
||||
if (m == null) return MeetingSubPhase.Arrival;
|
||||
var now = DateTime.UtcNow;
|
||||
if (now >= m.VotingEndTime) return MeetingSubPhase.Resolved;
|
||||
if (m.DiscussionEndTime.HasValue && now < m.DiscussionEndTime.Value)
|
||||
{
|
||||
// Server enforces: arrival deadline AND discussion-end gate voting.
|
||||
// While arrival is still open we surface "Arrival" so players know
|
||||
// others may still be travelling; once arrival deadline passes we
|
||||
// surface "Discussion" until the voting window opens.
|
||||
return now < m.ArrivalDeadline ? MeetingSubPhase.Arrival : MeetingSubPhase.Discussion;
|
||||
}
|
||||
return MeetingSubPhase.Voting;
|
||||
}
|
||||
|
||||
/// <summary>End-of-current-sub-phase boundary as a UTC DateTime, used for countdown rendering.</summary>
|
||||
public DateTime GetMeetingSubPhaseDeadline(MeetingSubPhase sub)
|
||||
{
|
||||
var m = ActiveMeeting;
|
||||
if (m == null) return DateTime.UtcNow;
|
||||
switch (sub)
|
||||
{
|
||||
case MeetingSubPhase.Arrival:
|
||||
return m.ArrivalDeadline;
|
||||
case MeetingSubPhase.Discussion:
|
||||
return m.DiscussionEndTime ?? m.VotingEndTime;
|
||||
case MeetingSubPhase.Voting:
|
||||
return m.VotingEndTime;
|
||||
default:
|
||||
return m.VotingEndTime;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Sabotage ──────────────────────────────────────────────────────────────
|
||||
public SabotageStartedPayload ActiveSabotage { get; set; }
|
||||
|
||||
/// <summary>StationIds currently being repaired (server broadcasts RepairStarted/RepairStopped).</summary>
|
||||
public HashSet<string> ActiveRepairs { get; set; } = new HashSet<string>();
|
||||
|
||||
// ── End game ──────────────────────────────────────────────────────────────
|
||||
public GameEndedPayload GameEndData { get; set; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user