You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
5.8 KiB
Go
135 lines
5.8 KiB
Go
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).
|
|
// For KindTown, Phase is usually ExcursionWild while using attractor movement during wander/rest.
|
|
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)
|
|
)
|
|
|
|
// ExcursionKind distinguishes roadside rest vs walking adventure vs in-town tour.
|
|
type ExcursionKind string
|
|
|
|
const (
|
|
ExcursionKindNone ExcursionKind = ""
|
|
ExcursionKindRoadside ExcursionKind = "roadside"
|
|
ExcursionKindAdventure ExcursionKind = "adventure"
|
|
ExcursionKindTown ExcursionKind = "town"
|
|
)
|
|
|
|
// TownTourPhase is the sub-state machine while ExcursionKind == town (StateInTown).
|
|
type TownTourPhase string
|
|
|
|
const (
|
|
TownTourPhaseWander TownTourPhase = "wander"
|
|
TownTourPhaseNpcApproach TownTourPhase = "npc_approach"
|
|
TownTourPhaseNpcWelcome TownTourPhase = "npc_welcome"
|
|
TownTourPhaseNpcService TownTourPhase = "npc_service"
|
|
TownTourPhaseRest TownTourPhase = "rest"
|
|
)
|
|
|
|
// ExcursionSession holds the live state of an active mini-adventure (off-road excursion) or town tour.
|
|
// When Phase == ExcursionNone the session is inactive and all other fields are zero-valued (except Kind for town cleared on leave).
|
|
type ExcursionSession struct {
|
|
Kind ExcursionKind
|
|
Phase ExcursionPhase
|
|
StartedAt time.Time
|
|
|
|
// OutUntil / WildUntil / ReturnUntil: legacy time-based FSM (ignored when Kind is set).
|
|
OutUntil time.Time
|
|
WildUntil time.Time
|
|
ReturnUntil time.Time
|
|
|
|
// DepthWorldUnits is used to place forest attractors (perpendicular distance from road spine).
|
|
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
|
|
|
|
// Attractor-based movement (Kind != ""): hero walks in world space toward AttractorX/Y.
|
|
StartX, StartY float64
|
|
AttractorX, AttractorY float64
|
|
AttractorSet bool
|
|
|
|
// Adventure-only: wall-time when wandering should end (then return to road).
|
|
AdventureEndsAt time.Time
|
|
// Adventure / town wander: next time to pick a new wander attractor (wild phase).
|
|
WanderNextAt time.Time
|
|
// PendingReturnAfterCombat: adventure timer elapsed; wait for combat end then enter return phase.
|
|
PendingReturnAfterCombat bool
|
|
|
|
// --- Town tour (Kind == ExcursionKindTown) ---
|
|
TownTourPhase string
|
|
// TownTourEndsAt: wall-time when the hero should leave the town (may defer until idle).
|
|
TownTourEndsAt time.Time
|
|
TownTourNpcID int64
|
|
// Stand point near NPC during approach / welcome / service.
|
|
TownTourStandX float64
|
|
TownTourStandY float64
|
|
// TownWelcomeUntil: npc_welcome phase deadline (30s, shifted while dialog open).
|
|
TownWelcomeUntil time.Time
|
|
// TownServiceUntil: npc_service phase max wall time (4 min, shifted while UI open).
|
|
TownServiceUntil time.Time
|
|
// TownRestUntil: in-town rest phase end.
|
|
TownRestUntil time.Time
|
|
TownExitPending bool
|
|
// Client has NPCDialog (welcome or service) open — shifts welcome/service deadlines.
|
|
TownTourDialogOpen bool
|
|
// Client has NPCInteraction panel open — shifts service deadline; with dialog shifts welcome too.
|
|
TownTourInteractionOpen bool
|
|
}
|
|
|
|
// Active reports whether an excursion session is in progress.
|
|
func (s *ExcursionSession) Active() bool {
|
|
if s == nil {
|
|
return false
|
|
}
|
|
if s.Kind == ExcursionKindTown {
|
|
return s.TownTourPhase != ""
|
|
}
|
|
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 {
|
|
Kind string `json:"kind,omitempty"`
|
|
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"`
|
|
StartX float64 `json:"startX,omitempty"`
|
|
StartY float64 `json:"startY,omitempty"`
|
|
AttractorX float64 `json:"attractorX,omitempty"`
|
|
AttractorY float64 `json:"attractorY,omitempty"`
|
|
AttractorSet bool `json:"attractorSet,omitempty"`
|
|
AdventureEndsAt *time.Time `json:"adventureEndsAt,omitempty"`
|
|
WanderNextAt *time.Time `json:"wanderNextAt,omitempty"`
|
|
PendingReturnAfterCombat bool `json:"pendingReturnAfterCombat,omitempty"`
|
|
|
|
TownTourPhase string `json:"townTourPhase,omitempty"`
|
|
TownTourEndsAt *time.Time `json:"townTourEndsAt,omitempty"`
|
|
TownTourNpcID int64 `json:"townTourNpcId,omitempty"`
|
|
TownTourStandX float64 `json:"townTourStandX,omitempty"`
|
|
TownTourStandY float64 `json:"townTourStandY,omitempty"`
|
|
TownWelcomeUntil *time.Time `json:"townWelcomeUntil,omitempty"`
|
|
TownServiceUntil *time.Time `json:"townServiceUntil,omitempty"`
|
|
TownRestUntil *time.Time `json:"townRestUntil,omitempty"`
|
|
TownExitPending bool `json:"townExitPending,omitempty"`
|
|
TownTourDialogOpen bool `json:"townTourDialogOpen,omitempty"`
|
|
TownTourInteractionOpen bool `json:"townTourInteractionOpen,omitempty"`
|
|
}
|