package core import ( "encoding/csv" "os" "path/filepath" "strconv" ) const ( TypeIDSolarSystem = 5 GroupIDStation = 15 GroupIDStargate = 10 ) var AllowedRegionIDs = map[int]bool{ 10000002: true, // The Forge 10000016: true, // Lonetrek 10000030: true, // Heimatar 10000033: true, // The Citadel 10000032: true, // Sinq Laison 10000042: true, // Metropolis 10000064: true, // Essence 10000037: true, // Everyshore 10000043: true, // Domain 10000036: true, // Devoid 10000052: true, // Kador 10000067: true, // Genesis 10000068: true, // Verge Vendor 10000001: true, // Derelik 10000020: true, // Tash-Murkon } type Position struct { X float64 `json:"x"` Y float64 `json:"y"` Z float64 `json:"z"` } type Station struct { IsStation bool `json:"is_station"` ItemName string `json:"item_name"` Security float64 `json:"security"` Position Position `json:"position"` SolarSystemID int `json:"solar_system_id"` RegionID int `json:"region_id"` RegionName string `json:"region_name"` SolarSystemName string `json:"solar_system_name"` SolarSystemPosition Position `json:"solar_system_position"` Jumps []int `json:"jumps"` } type SolarSystem struct { ItemName string `json:"item_name"` Position Position `json:"position"` RegionID int `json:"region_id"` RegionName string `json:"region_name"` } type Item struct { TypeID int `json:"type_id"` GroupID int `json:"group_id"` TypeName string `json:"type_name"` Description string `json:"description"` Mass *float64 `json:"mass"` Volume *float64 `json:"volume"` Capacity *float64 `json:"capacity"` } // DataLoader handles loading data from CSV files type DataLoader struct { BaseDir string } func NewDataLoader(baseDir string) *DataLoader { return &DataLoader{BaseDir: baseDir} } func (dl *DataLoader) ReadRegions() (map[int]string, error) { file, err := os.Open(filepath.Join(dl.BaseDir, "mapRegions.csv")) if err != nil { return nil, err } defer file.Close() reader := csv.NewReader(file) records, err := reader.ReadAll() if err != nil { return nil, err } regions := make(map[int]string) if len(records) > 0 { records = records[1:] // Skip header } for _, row := range records { regionID, err := strconv.Atoi(row[0]) if err != nil { continue } regions[regionID] = row[1] } return regions, nil } func (dl *DataLoader) LoadMapData() (map[int]*Station, map[int]*SolarSystem, error) { regions, err := dl.ReadRegions() if err != nil { return nil, nil, err } file, err := os.Open(filepath.Join(dl.BaseDir, "mapDenormalize.csv")) if err != nil { return nil, nil, err } defer file.Close() reader := csv.NewReader(file) // Some fields might be mismatched or have commas in strings, standard csv covers it records, err := reader.ReadAll() if err != nil { return nil, nil, err } stations := make(map[int]*Station) solarSystems := make(map[int]*SolarSystem) if len(records) > 0 { records = records[1:] } for _, row := range records { if len(row) < 15 { continue } regionIDStr := row[5] if regionIDStr == "None" || regionIDStr == "" { continue } regionID, err := strconv.Atoi(regionIDStr) if err != nil || !AllowedRegionIDs[regionID] { continue } itemID, _ := strconv.Atoi(row[0]) typeID, _ := strconv.Atoi(row[1]) groupID, _ := strconv.Atoi(row[2]) solarSystemID := 0 if row[3] != "None" && row[3] != "" { solarSystemID, _ = strconv.Atoi(row[3]) } x, _ := strconv.ParseFloat(row[7], 64) y, _ := strconv.ParseFloat(row[8], 64) z, _ := strconv.ParseFloat(row[9], 64) itemName := row[11] security, _ := strconv.ParseFloat(row[12], 64) regionName := regions[regionID] if regionName == "" { regionName = "Unknown" } if typeID == TypeIDSolarSystem { solarSystems[itemID] = &SolarSystem{ ItemName: itemName, Position: Position{X: x, Y: y, Z: z}, RegionID: regionID, RegionName: regionName, } } else if groupID == GroupIDStation || groupID == GroupIDStargate { stations[itemID] = &Station{ IsStation: groupID == GroupIDStation, ItemName: itemName, Security: security, Position: Position{X: x, Y: y, Z: z}, SolarSystemID: solarSystemID, RegionID: regionID, Jumps: []int{}, } } } // Update station data for _, station := range stations { if ss, ok := solarSystems[station.SolarSystemID]; ok { station.SolarSystemName = ss.ItemName station.SolarSystemPosition = ss.Position station.RegionID = ss.RegionID if name, exists := regions[ss.RegionID]; exists { station.RegionName = name } else { station.RegionName = "Unknown" } } } return stations, solarSystems, nil } func parseOptionalFloat(val string) *float64 { if val == "None" || val == "" { return nil } f, err := strconv.ParseFloat(val, 64) if err != nil { return nil } return &f } func (dl *DataLoader) LoadItems() (map[int]*Item, error) { file, err := os.Open(filepath.Join(dl.BaseDir, "invTypes.csv")) if err != nil { return nil, err } defer file.Close() reader := csv.NewReader(file) records, err := reader.ReadAll() if err != nil { return nil, err } items := make(map[int]*Item) if len(records) > 0 { records = records[1:] } for _, row := range records { if len(row) < 7 { continue } typeID, err := strconv.Atoi(row[0]) if err != nil { continue } groupID, _ := strconv.Atoi(row[1]) items[typeID] = &Item{ TypeID: typeID, GroupID: groupID, TypeName: row[2], Description: row[3], Mass: parseOptionalFloat(row[4]), Volume: parseOptionalFloat(row[5]), Capacity: parseOptionalFloat(row[6]), } } return items, nil } func (dl *DataLoader) LoadStarGateConnections() ([][2]int, error) { file, err := os.Open(filepath.Join(dl.BaseDir, "mapJumps.csv")) if err != nil { return nil, err } defer file.Close() reader := csv.NewReader(file) records, err := reader.ReadAll() if err != nil { return nil, err } var connections [][2]int if len(records) > 0 { records = records[1:] } for _, row := range records { if len(row) < 2 { continue } from, err1 := strconv.Atoi(row[0]) to, err2 := strconv.Atoi(row[1]) if err1 == nil && err2 == nil { connections = append(connections, [2]int{from, to}) } } return connections, nil }