package main import ( "iter" "reflect" "strconv" "strings" ) 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 Deck1LB int Deck1Exp int Deck2 int Deck2LB int Deck2Exp int Deck3 int Deck3LB int Deck3Exp int Deck4 int Deck4LB int Deck4Exp int Deck5 int Deck5LB int Deck5Exp int Deck6 int Deck6LB int Deck6Exp int Races int Wins int GoldSkills 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 OtherConds []string } var headers []string func init() { t := reflect.TypeFor[Row]() for f := range t.Fields() { headers = append(headers, f.Name) } } func elem[T any, S ~[]T](s S, i int) T { if i < 0 || i >= len(s) { var zero T return zero } return s[i] } 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 var otherconds []string for _, v := range pl.CharaEffectLog { if v.CharaEffectID < 1 || v.CharaEffectID > 11 { otherconds = append(otherconds, strconv.Itoa(int(v.CharaEffectID))) continue } conditions[v.CharaEffectID-1] = true } var golds int for _, t := range ism.End.Chara.SkillTips { if t.Rarity == 2 { golds++ } } row := Row{ File: key, Scenario: int(ism.ProgressInfo.Chara.ScenarioID), Character: int(ism.ProgressInfo.Chara.CardID), Deck1: int(pl.SupportCardGains[0].SupportCardID), Deck1LB: int(elem(ism.End.Chara.SupportCards, 0).LimitBreakCount), Deck1Exp: int(elem(ism.End.Chara.SupportCards, 0).Exp), Deck2: int(pl.SupportCardGains[1].SupportCardID), Deck2LB: int(elem(ism.End.Chara.SupportCards, 1).LimitBreakCount), Deck2Exp: int(elem(ism.End.Chara.SupportCards, 1).Exp), Deck3: int(pl.SupportCardGains[2].SupportCardID), Deck3LB: int(elem(ism.End.Chara.SupportCards, 2).LimitBreakCount), Deck3Exp: int(elem(ism.End.Chara.SupportCards, 2).Exp), Deck4: int(pl.SupportCardGains[3].SupportCardID), Deck4LB: int(elem(ism.End.Chara.SupportCards, 3).LimitBreakCount), Deck4Exp: int(elem(ism.End.Chara.SupportCards, 3).Exp), Deck5: int(pl.SupportCardGains[4].SupportCardID), Deck5LB: int(elem(ism.End.Chara.SupportCards, 4).LimitBreakCount), Deck5Exp: int(elem(ism.End.Chara.SupportCards, 4).Exp), Deck6: int(pl.SupportCardGains[5].SupportCardID), Deck6LB: int(elem(ism.End.Chara.SupportCards, 5).LimitBreakCount), Deck6Exp: int(elem(ism.End.Chara.SupportCards, 5).Exp), Races: races, Wins: wins, GoldSkills: golds, 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], OtherConds: otherconds, } 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.Deck1LB), strconv.Itoa(r.Deck1Exp), strconv.Itoa(r.Deck2), strconv.Itoa(r.Deck2LB), strconv.Itoa(r.Deck2Exp), strconv.Itoa(r.Deck3), strconv.Itoa(r.Deck3LB), strconv.Itoa(r.Deck3Exp), strconv.Itoa(r.Deck4), strconv.Itoa(r.Deck4LB), strconv.Itoa(r.Deck4Exp), strconv.Itoa(r.Deck5), strconv.Itoa(r.Deck5LB), strconv.Itoa(r.Deck5Exp), strconv.Itoa(r.Deck6), strconv.Itoa(r.Deck6LB), strconv.Itoa(r.Deck6Exp), strconv.Itoa(r.Races), strconv.Itoa(r.Wins), strconv.Itoa(r.GoldSkills), 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), strings.Join(r.OtherConds, ", "), } }