package model import "time" // ExcursionPhase tracks where the hero is within a mini-adventure session. // The lifecycle is: Out → Wild → Return → (back to road, phase cleared). type ExcursionPhase string const ( ExcursionNone ExcursionPhase = "" ExcursionOut ExcursionPhase = "out" // moving off-road into the forest ExcursionWild ExcursionPhase = "wild" // in the wilderness (encounters happen here) ExcursionReturn ExcursionPhase = "return" // returning to the road (encounters still possible) ) // ExcursionSession holds the live state of an active mini-adventure (off-road excursion). // When Phase == ExcursionNone the session is inactive and all other fields are zero-valued. type ExcursionSession struct { Phase ExcursionPhase StartedAt time.Time // OutUntil marks the end of the out phase (hero reached full depth); derived from depth/speed. OutUntil time.Time // WildUntil marks the end of the wild phase; once reached the hero begins returning. WildUntil time.Time // ReturnUntil marks the deadline for the return phase; once reached the hero is back on road. ReturnUntil time.Time // DepthWorldUnits is the max perpendicular distance from the road spine for this session. DepthWorldUnits float64 // RoadFreezeWaypoint / RoadFreezeFraction capture road progress at the moment the hero // left the road, so it can be restored exactly when the excursion ends. RoadFreezeWaypoint int RoadFreezeFraction float64 } // Active reports whether an excursion session is in progress. func (s *ExcursionSession) Active() bool { return s.Phase != ExcursionNone } // ExcursionPersisted is the JSON-serialisable subset of ExcursionSession stored in the // heroes.town_pause JSONB column so that reconnect / offline catch-up can resume mid-adventure. type ExcursionPersisted struct { Phase string `json:"phase,omitempty"` StartedAt *time.Time `json:"startedAt,omitempty"` OutUntil *time.Time `json:"outUntil,omitempty"` WildUntil *time.Time `json:"wildUntil,omitempty"` ReturnUntil *time.Time `json:"returnUntil,omitempty"` DepthWorldUnits float64 `json:"depthWorldUnits,omitempty"` RoadFreezeWaypoint int `json:"roadFreezeWaypoint,omitempty"` RoadFreezeFraction float64 `json:"roadFreezeFraction,omitempty"` }