258 lines
8.3 KiB
Go
258 lines
8.3 KiB
Go
|
|
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"}
|