skyrama_tui/internal/model/model.go

258 lines
8.3 KiB
Go
Raw Normal View History

2026-06-04 20:51:20 +03:00
package model
import (
"database/sql"
"time"
)
// ─── User ────────────────────────────────────────────────────────────────────
type User struct {
ID int32
Username string
PasswordHash string
CreatedAt time.Time
DeletedAt sql.NullTime
}
// ─── Airport ─────────────────────────────────────────────────────────────────
type Airport struct {
ID uint32
Name string
UserID uint32
OriginCountryID uint16
Cash uint64
CurrentPassengers uint32
MaxPassengerCapacity uint32
CurrentFuel uint32
MaxFuelCapacity uint32
FuelGenerationRate uint32
PassengerGenerationRate uint32
ResourcesLastUpdatedAt time.Time
IsAvailable bool
IsCentral bool
PlayerLevel uint32
ExperiencePoints uint64
StorageCap uint32
OperationCap uint32
IsDeleted bool
}
// CentralAirport is a system-owned destination port (one per continent), shown
// in the Fleet dispatch picker. Joined with its owning faction for flavour.
type CentralAirport struct {
AirportID uint32
Continent string
Name string
FactionName string
LeaderName string
}
// FacilityStatus is the occupancy of one operation-building class (a runway or
// service bay of a given aircraft size): how many exist vs. how many are busy.
type FacilityStatus struct {
OperationType string // "runway" | "service"
Size string // "small" | "medium" | "large" | "huge"
Total int
Busy int
}
// Faction is the NPC owner of a continent's central port — lore / future content.
type Faction struct {
ID uint32
Name string
Continent string
LeaderName string
Background string
UserID int32
CentralAirportID uint32
}
// ─── Country ─────────────────────────────────────────────────────────────────
type Country struct {
ID uint16
Name string
}
// ─── Plane (template) ────────────────────────────────────────────────────────
type Plane struct {
ID uint32
Name string
MinLevel uint32
OperationCostFuel uint32
OperationCostCash uint32
MaxPassengers sql.NullInt32
TravelTimeInSeconds uint32
OperationType string // "passenger" | "cargo"
AircraftSize string // "small" | "medium" | "large" | "huge"
AircraftType string // "plane" | "helicopter" | "seaplane" | "spaceship"
BuyingCostCash uint32
CompletedFlightExperience uint32
CompletedFlightCashReward sql.NullInt32
CompletedFlightCargoReward sql.NullInt32
BoardingTimeSec uint32
FuelingTimeSec uint32
}
// ─── Player Plane ─────────────────────────────────────────────────────────────
type PlayerPlane struct {
ID uint32
AirportID uint32
PlaneID uint32
AssignedHangarID uint32
CurrentBuildingID sql.NullInt32
Status string // "idle"|"maintenance"|"boarding"|"taxiing"|"flying"|"unloading"
UpdatedAt time.Time
PurchasedAt time.Time
FlightStartTime sql.NullTime
DestinationAirportID sql.NullInt32
DestinationName sql.NullString
// Joined from planes
PlaneName string
AircraftSize string
AircraftType string
OperationType string
TravelTimeSec uint32
FuelCost uint32
BuyingCost uint32
MaxPassengers sql.NullInt32
CashReward sql.NullInt32
ExpReward uint32
BoardingTimeSec uint32
FuelingTimeSec uint32
// RemainingSec is the seconds left in the current timed phase, computed
// server-side (MySQL NOW()) in GetFleet so the countdown shares the exact
// clock used by the landing / service-advance checks. Avoids Go-vs-MySQL
// clock skew that made planes land early.
RemainingSec int
}
// TimeRemainingSeconds returns seconds left in the current timed phase
// (boarding, taxiing/fueling, or flying). The value is computed in SQL
// (see GetFleet) so it agrees with the DB-side landing check.
func (p *PlayerPlane) TimeRemainingSeconds() int {
if p.RemainingSec < 0 {
return 0
}
return p.RemainingSec
}
// ─── Leveling ─────────────────────────────────────────────────────────────────
//
// XP is cumulative. Advancing from level L to L+1 costs 500*(L+1) XP, so:
// L0→L1 = 500, L1→L2 = 1000, L2→L3 = 1500 …
// Total XP to reach the start of level L = 500 * L*(L+1)/2.
// XPToNextLevel is the XP span of the current level (level → level+1).
func XPToNextLevel(level uint32) int {
return 500 * (int(level) + 1)
}
// cumulativeXP is the total XP required to be at the start of `level`.
func cumulativeXP(level uint32) uint64 {
l := uint64(level)
return 500 * l * (l + 1) / 2
}
// LevelForXP returns the level that the given total XP corresponds to.
func LevelForXP(xp uint64) uint32 {
level := uint32(0)
for cumulativeXP(level+1) <= xp {
level++
}
return level
}
// XPIntoLevel returns how much of the current level's span has been earned.
func XPIntoLevel(level uint32, xp uint64) int {
base := cumulativeXP(level)
if xp < base {
return 0
}
return int(xp - base)
}
// ─── Building ─────────────────────────────────────────────────────────────────
type Building struct {
ID uint32
Name string
MinLevel uint32
ConstructionCostCash uint32
UpgradeTier uint8
BuildingType string
// storage
Capacity sql.NullInt32
StorageType sql.NullString
StorageSubtype sql.NullString
// operation
OperationSize sql.NullString
OperationType sql.NullString
// passive
PassiveType sql.NullString
PassiveRate sql.NullInt32
}
// AirportBuilding is an instance owned by a player
type AirportBuilding struct {
ID uint32
AirportID uint32
BuildingID uint32
Level uint32
// Joined from buildings
Name string
BuildingType string
ConstructionCostCash uint32
OperationSize sql.NullString
OperationType sql.NullString
Capacity sql.NullInt32
StorageType sql.NullString
}
// ─── Product ──────────────────────────────────────────────────────────────────
type Product struct {
ID uint16
Name string
}
// ─── Contract ────────────────────────────────────────────────────────────────
type Contract struct {
ID uint32
Title string
RequiredProductID uint16
RequiredAmount uint32
CurrentAmountDelivered uint32
IsCompleted bool
ExpiresAt time.Time
// Joined
ProductName string
}
func (c *Contract) ProgressPct() int {
if c.RequiredAmount == 0 {
return 100
}
return int((c.CurrentAmountDelivered * 100) / c.RequiredAmount)
}
// ─── App state helpers ────────────────────────────────────────────────────────
// Tab indices
const (
TabAirport = 0
TabFleet = 1
TabBuildings = 2
TabContracts = 3
TabShop = 4
)
var TabNames = []string{"✈ Airport", "🛩 Fleet", "🏗 Buildings", "📋 Contracts", "🛒 Shop"}