Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/mkmccarty/TokenTimeBoostBot/src/boost"
"github.com/mkmccarty/TokenTimeBoostBot/src/bottools"
"github.com/mkmccarty/TokenTimeBoostBot/src/config"
"github.com/mkmccarty/TokenTimeBoostBot/src/ei"
"github.com/mkmccarty/TokenTimeBoostBot/src/events"
"github.com/mkmccarty/TokenTimeBoostBot/src/farmerstate"
"github.com/mkmccarty/TokenTimeBoostBot/src/menno"
Expand Down Expand Up @@ -1083,12 +1082,6 @@ func main() {
log.Println(err.Error())
}
}
if event.Name == ei.MissionConfigPath {
log.Println("modified file:", event.Name)
if !ei.ReloadMissionConfig() {
log.Println("Failed to reload mission config")
}
}
}
case err, ok := <-watcher.Errors:
if !ok {
Expand All @@ -1103,10 +1096,6 @@ func main() {
if err != nil {
log.Fatal(err)
}
err = watcher.Add(ei.MissionConfigPath)
if err != nil {
log.Fatal(err)
}

stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
Expand Down
182 changes: 2 additions & 180 deletions src/ei/ei_missions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ package ei

import (
"encoding/json"
"log"
"os"
"sort"
"strconv"
"time"
)

const missionJSON = `{"ships":[
Expand All @@ -23,194 +18,21 @@ const missionJSON = `{"ships":[
{"name": "Atreggies Henliner","art":"atreggies","duration":["2d","3d","4d"]}
]}`

// MissionConfigPath is the on-disk config used for mission and artifact data.
const MissionConfigPath = "ttbb-data/ei-afx-config.json"

// ShipData holds data for each mission ship
type ShipData struct {
Name string `json:"Name"`
Art string `json:"Art"`
ArtDev string `json:"ArtDev"`
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArtDev is added to ShipData but the embedded missionJSON does not provide an artDev/ArtDev field and there are no references to ShipData.ArtDev in the codebase, so this field is currently always empty and adds dead surface area. Either remove it until it’s used, or update the JSON (and tags) plus consuming code to populate and use it.

Suggested change
ArtDev string `json:"ArtDev"`

Copilot uses AI. Check for mistakes.
Duration []string `json:"Duration"`
}

type missionData struct {
Ships []ShipData `json:"ships"`
}

type missionConfig struct {
MissionParameters []missionParameter `json:"missionParameters"`
ArtifactParameters []artifactParameter `json:"artifactParameters"`
CraftingLevelInfos []craftingLevelInfo `json:"craftingLevelInfos"`
}

type craftingLevelInfo struct {
XpRequired int `json:"xpRequired"`
RarityMult float64 `json:"rarityMult"`
}

type artifactParameter struct {
Spec artifactSpec `json:"spec"`
BaseQuality float64 `json:"baseQuality"`
Value float64 `json:"value"`
OddsMultiplier float64 `json:"oddsMultiplier"`
CraftingPrice float64 `json:"craftingPrice"`
CraftingPriceLow float64 `json:"craftingPriceLow"`
CraftingPriceDomain float64 `json:"craftingPriceDomain"`
CraftingPriceCurve float64 `json:"craftingPriceCurve"`
CraftingXp float64 `json:"craftingXp"`
}

type artifactSpec struct {
Name string `json:"name"`
Level string `json:"level"`
Rarity string `json:"rarity"`
}

type missionParameter struct {
Ship string `json:"ship"`
Durations []missionDuration `json:"durations"`
}

type missionDuration struct {
DurationType string `json:"durationType"`
Seconds int `json:"seconds"`
Quality float64 `json:"quality"`
MinQuality float64 `json:"minQuality"`
MaxQuality float64 `json:"maxQuality"`
Capacity int `json:"capacity"`
}

// MissionArt holds the mission art and durations loaded from JSON
var MissionArt missionData

// ArtifactParameters holds artifact parameters loaded from JSON
var ArtifactParameters []artifactParameter

// CraftingLevelInfos holds crafting level info loaded from JSON
var CraftingLevelInfos []craftingLevelInfo

var missionShipInfo = map[string]ShipData{
"CHICKEN_ONE": {Name: "Chicken One", Art: "chicken1"},
"CHICKEN_NINE": {Name: "Chicken Nine", Art: "chicken9"},
"CHICKEN_HEAVY": {Name: "Chicken Heavy", Art: "chickenheavy"},
"BCR": {Name: "BCR", Art: "bcr"},
"MILLENIUM_CHICKEN": {Name: "Quintillion Chicken", Art: "milleniumchicken"},
"CORELLIHEN_CORVETTE": {Name: "Cornish-Hen Corvette", Art: "corellihencorvette"},
"GALEGGTICA": {Name: "Galeggtica", Art: "galeggtica"},
"CHICKFIANT": {Name: "Defihent", Art: "defihent"},
"VOYEGGER": {Name: "Voyegger", Art: "voyegger"},
"HENERPRISE": {Name: "Henerprise", Art: "henerprise"},
"ATREGGIES": {Name: "Atreggies Henliner", Art: "atreggies"},
}

func init() {
if !loadMissionDataFromConfig(MissionConfigPath) {
_ = json.Unmarshal([]byte(missionJSON), &MissionArt)
}
}

// ReloadMissionConfig reloads mission, artifact, and crafting data from disk.
func ReloadMissionConfig() bool {
return loadMissionDataFromConfig(MissionConfigPath)
}

func loadMissionDataFromConfig(path string) bool {
data, err := os.ReadFile(path)
if err != nil {
log.Printf("Mission config read failed: %v", err)
return false
}

var cfg missionConfig
if err := json.Unmarshal(data, &cfg); err != nil {
log.Printf("Mission config parse failed: %v", err)
return false
}

ArtifactParameters = cfg.ArtifactParameters
CraftingLevelInfos = cfg.CraftingLevelInfos

var md missionData
for _, param := range cfg.MissionParameters {
info, ok := missionShipInfo[param.Ship]
if !ok {
continue
}
info.Duration = pickMissionDurations(param.Durations)
md.Ships = append(md.Ships, info)
}

if len(md.Ships) == 0 {
return false
}

MissionArt = md
return true
}

func pickMissionDurations(durations []missionDuration) []string {
preferred := []string{"SHORT", "LONG", "EPIC"}
byType := make(map[string]int, len(durations))
for _, d := range durations {
if d.Seconds <= 0 {
continue
}
byType[d.DurationType] = d.Seconds
}

var result []string
for _, key := range preferred {
if seconds, ok := byType[key]; ok {
result = append(result, formatMissionDuration(seconds))
}
}

if len(result) > 0 {
return result
}

sort.Slice(durations, func(i, j int) bool {
return durations[i].Seconds < durations[j].Seconds
})
for _, d := range durations {
if d.Seconds <= 0 {
continue
}
result = append(result, formatMissionDuration(d.Seconds))
if len(result) == 3 {
break
}
}

return result
}

func formatMissionDuration(seconds int) string {
d := time.Duration(seconds) * time.Second
if d <= 0 {
return "0m"
}

days := d / (24 * time.Hour)
d -= days * 24 * time.Hour
hours := d / time.Hour
d -= hours * time.Hour
minutes := d / time.Minute

parts := ""
if days > 0 {
parts += formatDurationPart(int(days), "d")
}
if hours > 0 {
parts += formatDurationPart(int(hours), "h")
}
if minutes > 0 || parts == "" {
parts += formatDurationPart(int(minutes), "m")
}

return parts
}

func formatDurationPart(value int, unit string) string {
return strconv.Itoa(value) + unit
_ = json.Unmarshal([]byte(missionJSON), &MissionArt)
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json.Unmarshal error is ignored in init; if missionJSON is ever edited into invalid JSON, MissionArt will silently remain empty and callers indexing MissionArt.Ships[...] can panic or show wrong data. Handle the error (e.g., panic/log.Fatal) or at least assert len(MissionArt.Ships) > 0 after unmarshal.

Suggested change
_ = json.Unmarshal([]byte(missionJSON), &MissionArt)
if err := json.Unmarshal([]byte(missionJSON), &MissionArt); err != nil {
panic(err)
}
if len(MissionArt.Ships) == 0 {
panic("missionJSON unmarshaled successfully but contains no ships")
}

Copilot uses AI. Check for mistakes.
}
Comment on lines 36 to 38
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the on-disk mission config loader removed, consider adding a small unit test that unmarshals missionJSON and asserts ship count/order matches the MissionInfo.Spaceship enum values. This would protect callers that index MissionArt.Ships[shipID] from silent breakage if the embedded JSON is edited.

Copilot uses AI. Check for mistakes.
Loading