package main import ( "iter" "log/slog" "reflect" "strconv" ) type Row struct { File string GainSource string Speed int Stamina int Power int Guts int Wit int SP int NumSkills int Scenario int Character int Deck1 int Deck2 int Deck3 int Deck4 int Deck5 int Deck6 int Races int Wins int NightOwl bool // chara effect id 1 Slacker bool // 2 SkinOutbreak bool // 3 SlowMetabolism bool // 4 Migraine bool // 5 PracticePoor bool // 6 FastLearner bool // 7 Charming bool // 8 HotTopic bool // 9 PracticePerfect bool // 10 VeryGoodAtTraining bool // 11 } var headers []string func init() { t := reflect.TypeFor[Row]() for f := range t.Fields() { headers = append(headers, f.Name) } } func ismRows(key string, ism WorkIdleSingleMode) iter.Seq[Row] { return func(yield func(Row) bool) { pl := ism.ProgressLog races := len(pl.RaceHistory) var wins int for _, r := range pl.RaceHistory { if r.RaceHistory.ResultRank == 1 { wins++ } } var conditions [11]bool for _, v := range pl.CharaEffectLog { if v.CharaEffectID < 1 || v.CharaEffectID > 11 { slog.Warn("skipping unknown charaEffectID", slog.Int("id", int(v.CharaEffectID)), slog.Bool("active", v.IsActive)) continue } conditions[v.CharaEffectID-1] = true } row := Row{ File: key, Scenario: int(ism.Chara.ScenarioID), Character: int(ism.Chara.CardID), Deck1: int(pl.SupportCardGains[0].SupportCardID), Deck2: int(pl.SupportCardGains[1].SupportCardID), Deck3: int(pl.SupportCardGains[2].SupportCardID), Deck4: int(pl.SupportCardGains[3].SupportCardID), Deck5: int(pl.SupportCardGains[4].SupportCardID), Deck6: int(pl.SupportCardGains[5].SupportCardID), Races: races, Wins: wins, NightOwl: conditions[0], Slacker: conditions[1], SkinOutbreak: conditions[2], SlowMetabolism: conditions[3], Migraine: conditions[4], PracticePoor: conditions[5], FastLearner: conditions[6], Charming: conditions[7], HotTopic: conditions[8], PracticePerfect: conditions[9], VeryGoodAtTraining: conditions[10], } row.GainSource = "Events" row.Speed = int(pl.EventGain.Speed.Value) row.Stamina = int(pl.EventGain.Stamina.Value) row.Power = int(pl.EventGain.Power.Value) row.Guts = int(pl.EventGain.Guts.Value) row.Wit = int(pl.EventGain.Wiz.Value) row.SP = int(pl.EventGain.SkillPoint) row.NumSkills = len(pl.EventGain.SkillTips) if !yield(row) { return } row.GainSource = "Inspiration" row.Speed = int(pl.SuccessionGain.Speed.Value) row.Stamina = int(pl.SuccessionGain.Stamina.Value) row.Power = int(pl.SuccessionGain.Power.Value) row.Guts = int(pl.SuccessionGain.Guts.Value) row.Wit = int(pl.SuccessionGain.Wiz.Value) row.SP = int(pl.SuccessionGain.SkillPoint) row.NumSkills = len(pl.SuccessionGain.SkillTips) if !yield(row) { return } for _, sup := range pl.SupportCardGains { row.GainSource = strconv.Itoa(int(sup.SupportCardID)) row.Speed = int(sup.GainInfo.Speed.Value) row.Stamina = int(sup.GainInfo.Stamina.Value) row.Power = int(sup.GainInfo.Power.Value) row.Guts = int(sup.GainInfo.Guts.Value) row.Wit = int(sup.GainInfo.Wiz.Value) row.SP = int(sup.GainInfo.SkillPoint) row.NumSkills = len(sup.GainInfo.SkillTips) if !yield(row) { return } } } } func (r Row) csv() []string { return []string{ r.File, r.GainSource, strconv.Itoa(r.Speed), strconv.Itoa(r.Stamina), strconv.Itoa(r.Power), strconv.Itoa(r.Guts), strconv.Itoa(r.Wit), strconv.Itoa(r.SP), strconv.Itoa(r.NumSkills), strconv.Itoa(r.Scenario), strconv.Itoa(r.Character), strconv.Itoa(r.Deck1), strconv.Itoa(r.Deck2), strconv.Itoa(r.Deck3), strconv.Itoa(r.Deck4), strconv.Itoa(r.Deck5), strconv.Itoa(r.Deck6), strconv.Itoa(r.Races), strconv.Itoa(r.Wins), strconv.FormatBool(r.NightOwl), strconv.FormatBool(r.Slacker), strconv.FormatBool(r.SkinOutbreak), strconv.FormatBool(r.SlowMetabolism), strconv.FormatBool(r.Migraine), strconv.FormatBool(r.PracticePoor), strconv.FormatBool(r.FastLearner), strconv.FormatBool(r.Charming), strconv.FormatBool(r.HotTopic), strconv.FormatBool(r.PracticePerfect), strconv.FormatBool(r.VeryGoodAtTraining), } }