phase 0.3
This commit is contained in:
commit
4a94bfc4e7
52 changed files with 11653 additions and 0 deletions
258
internal/model/model.go
Normal file
258
internal/model/model.go
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
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"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue