initial commit

This commit is contained in:
2026-07-28 01:30:29 -04:00
commit 74fd6dcbee
7 changed files with 2801 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
module git.sunturtle.xyz/zephyr/ismdata
go 1.26.5
require (
git.sunturtle.xyz/zephyr/horse v0.0.0-20260725155724-79d4e7609089
github.com/google/go-cmp v0.7.0
)
+4
View File
@@ -0,0 +1,4 @@
git.sunturtle.xyz/zephyr/horse v0.0.0-20260725155724-79d4e7609089 h1:6Li/Q/KxUeH8KaN46iYKjbkthP5fAXXC6DkpMgxFSFA=
git.sunturtle.xyz/zephyr/horse v0.0.0-20260725155724-79d4e7609089/go.mod h1:Kqy1/kbqYbQuRe2RqUWJ1XfWjBzORXuto8/DXoqRMI4=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+231
View File
@@ -0,0 +1,231 @@
package main
import "git.sunturtle.xyz/zephyr/horse"
type SupportCardID int32 // package horse doesn't have this yet
type WorkIdleSingleMode struct {
Chara SingleModeChara `json:"charaInfo"`
StartTime int64 `json:"startTime"`
EndTime int64 `json:"endTime"`
ProgressLog IdleSingleModeProgressLog `json:"progressLogInfo"`
}
type Skill struct {
SkillID horse.SkillID `json:"skill_id"`
Level int32 `json:"level"`
}
type SkillTip struct {
GroupID horse.SkillGroupID `json:"group_id"`
Rarity int32 `json:"rarity"`
Level int32 `json:"level"`
}
type SingleModeSupportCard struct {
Position int32 `json:"position"`
SupportCardID SupportCardID `json:"support_card_id"`
LimitBreakCount int32 `json:"limit_break_count"`
Exp int32 `json:"exp"`
TrainingPartnerState int32 `json:"training_partner_state"`
OwnerViewerID int64 `json:"owner_viewer_id"`
RentalType int32 `json:"rental_type"`
}
type GroupOuting struct {
CharaID horse.CharacterID `json:"chara_id"`
IsOuting int32 `json:"is_outing"`
StoryStep int32 `json:"story_step"`
}
type Evaluation struct {
TargetID int32 `json:"target_id"`
Evaluation int32 `json:"evaluation"`
IsOuting int32 `json:"is_outing"`
StoryStep int32 `json:"story_step"`
IsAppear int32 `json:"is_appear"`
GroupOutingInfo []GroupOuting `json:"group_outing_info_array"`
}
type TrainingLevel struct {
CommandID int32 `json:"command_id"`
Level int32 `json:"level"`
}
type GuestOuting struct {
SupportCardID SupportCardID `json:"support_card_id"`
StoryStep int32 `json:"story_step"`
GroupOutingInfo []GroupOuting `json:"group_outing_info_array"`
}
type SingleModeSkillUpgrade struct {
ConditionID int32 `json:"condition_id"`
TotalCount int32 `json:"total_count"`
CurrentCount int32 `json:"current_count"`
}
type SingleModeChara struct {
SingleModeCharaID int32 `json:"single_mode_chara_id"`
CardID horse.UmaID `json:"card_id"`
CharaGrade int32 `json:"chara_grade"`
Speed int32 `json:"speed"`
Stamina int32 `json:"stamina"`
Power int32 `json:"power"`
Wiz int32 `json:"wiz"`
Guts int32 `json:"guts"`
Vital int32 `json:"vital"`
MaxSpeed int32 `json:"max_speed"`
MaxStamina int32 `json:"max_stamina"`
MaxPower int32 `json:"max_power"`
MaxWiz int32 `json:"max_wiz"`
MaxGuts int32 `json:"max_guts"`
DefaultMaxSpeed int32 `json:"default_max_speed"`
DefaultMaxStamina int32 `json:"default_max_stamina"`
DefaultMaxPower int32 `json:"default_max_power"`
DefaultMaxWiz int32 `json:"default_max_wiz"`
DefaultMaxGuts int32 `json:"default_max_guts"`
MaxVital int32 `json:"max_vital"`
Motivation int32 `json:"motivation"`
Fans int32 `json:"fans"`
Rarity int32 `json:"rarity"`
RaceProgramID int32 `json:"race_program_id"`
ReserveRaceProgramID int32 `json:"reserve_race_program_id"`
RaceRunningStyle int32 `json:"race_running_style"`
IsShortRace int32 `json:"is_short_race"`
TalentLevel int32 `json:"talent_level"`
Skills []Skill `json:"skill_array"`
DisableSkillIDs []horse.SkillID `json:"disable_skill_id_array"`
SkillTips []SkillTip `json:"skill_tips_array"`
SupportCards []SingleModeSupportCard `json:"support_card_array"`
SuccessionTrainedCharaID1 int32 `json:"succession_trained_chara_id_1"`
SuccessionTrainedCharaID2 int32 `json:"succession_trained_chara_id_2"`
ProperDistanceShort horse.AptitudeLevel `json:"proper_distance_short"`
ProperDistanceMile horse.AptitudeLevel `json:"proper_distance_mile"`
ProperDistanceMiddle horse.AptitudeLevel `json:"proper_distance_middle"`
ProperDistanceLong horse.AptitudeLevel `json:"proper_distance_long"`
ProperRunningStyleNige horse.AptitudeLevel `json:"proper_running_style_nige"`
ProperRunningStyleSenko horse.AptitudeLevel `json:"proper_running_style_senko"`
ProperRunningStyleSashi horse.AptitudeLevel `json:"proper_running_style_sashi"`
ProperRunningStyleOikomi horse.AptitudeLevel `json:"proper_running_style_oikomi"`
ProperGroundTurf horse.AptitudeLevel `json:"proper_ground_turf"`
ProperGroundDirt horse.AptitudeLevel `json:"proper_ground_dirt"`
Turn int32 `json:"turn"`
SkillPoint int32 `json:"skill_point"`
ShortCutState int32 `json:"short_cut_state"`
State int32 `json:"state"`
PlayingState int32 `json:"playing_state"`
ScenarioID horse.ScenarioID `json:"scenario_id"`
RouteID int32 `json:"route_id"`
StartTime string `json:"start_time"`
Evaluations []Evaluation `json:"evaluation_info_array"`
TrainingLevels []TrainingLevel `json:"training_level_info_array"`
NicknameIDs []int32 `json:"nickname_id_array"`
CharaEffectIDs []int32 `json:"chara_effect_id_array"`
RouteRaceIDs []int32 `json:"route_race_id_array"`
GuestOutings []GuestOuting `json:"guest_outing_info_array"`
SkillUpgrades []SingleModeSkillUpgrade `json:"skill_upgrade_info_array"`
}
type CharaEffectLog struct {
CharaEffectID int32 `json:"charaEffectId"`
IsActive bool `json:"isActive"`
}
type IdleSingleModeSignedInt struct {
Sign int32 `json:"sign"`
Value int32 `json:"value"`
}
type IdleSingleModeGainInfo struct {
Speed IdleSingleModeSignedInt `json:"speed"`
Stamina IdleSingleModeSignedInt `json:"stamina"`
Power IdleSingleModeSignedInt `json:"power"`
Wiz IdleSingleModeSignedInt `json:"wiz"`
Guts IdleSingleModeSignedInt `json:"guts"`
MaxSpeed int32 `json:"maxSpeed"`
MaxStamina int32 `json:"maxStamina"`
MaxPower int32 `json:"maxPower"`
MaxWiz int32 `json:"maxWiz"`
MaxGuts int32 `json:"maxGuts"`
ProperDistanceShort horse.AptitudeLevel `json:"properDistanceShort"`
ProperDistanceMile horse.AptitudeLevel `json:"properDistanceMile"`
ProperDistanceMiddle horse.AptitudeLevel `json:"properDistanceMiddle"`
ProperDistanceLong horse.AptitudeLevel `json:"properDistanceLong"`
ProperRunningStyleNige horse.AptitudeLevel `json:"properRunningStyleNige"`
ProperRunningStyleSenko horse.AptitudeLevel `json:"properRunningStyleSenko"`
ProperRunningStyleSashi horse.AptitudeLevel `json:"properRunningStyleSashi"`
ProperRunningStyleOikomi horse.AptitudeLevel `json:"properRunningStyleOikomi"`
ProperGroundTurf horse.AptitudeLevel `json:"properGroundTurf"`
ProperGroundDirt horse.AptitudeLevel `json:"properGroundDirt"`
SkillPoint int32 `json:"skillPoint"`
SkillTips []SkillTip `json:"skillTips"`
}
type IdleSingleModeSupportCardGain struct {
SupportCardID SupportCardID `json:"supportCardId"`
GainInfo IdleSingleModeGainInfo `json:"gainInfo"`
}
type Factor struct {
FactorID horse.SparkID `json:"factorId"`
Level int32 `json:"level"`
}
type IdleSingleModeSuccessionFactorGain struct {
Year int32 `json:"year"`
GainFactorInfo []Factor `json:"gainFactorInfoArray"`
}
type SingleRaceHistory struct {
Turn int32 `json:"turn"`
ProgramID int32 `json:"program_id"`
Weather int32 `json:"weather"`
GroundCondition int32 `json:"ground_condition"`
RunningStyle int32 `json:"running_style"`
ResultRank int32 `json:"result_rank"`
FrameOrder int32 `json:"frame_order"`
NpcCount int32 `json:"npc_count"`
}
type RaceReward struct {
ItemType int32 `json:"item_type"`
ItemID int32 `json:"item_id"`
ItemNum int32 `json:"item_num"`
}
type RaceRewardLimit struct {
RewardID int32 `json:"reward_id"`
ItemType int32 `json:"item_type"`
ItemID int32 `json:"item_id"`
ItemNum int32 `json:"item_num"`
RestCount int32 `json:"rest_count"`
}
type CharaRaceReward struct {
ResultRank int32 `json:"result_rank"`
ResultTime int32 `json:"result_time"`
RaceReward []RaceReward `json:"race_reward"`
RaceRewardBonus []RaceReward `json:"race_reward_bonus"`
RaceRewardPlusBonus []RaceReward `json:"race_reward_plus_bonus"`
RaceRewardBonusWin []RaceReward `json:"race_reward_bonus_win"`
RaceRewardLimit []RaceRewardLimit `json:"race_reward_limit"`
GainedFans int32 `json:"gained_fans"`
CampaignIDs []int32 `json:"campaign_id_array"`
}
type IdleSingleModeRaceHistory struct {
RaceHistory SingleRaceHistory `json:"race_history"`
RaceRewardInfo CharaRaceReward `json:"race_reward_info"`
LoseTipsID int32 `json:"lose_tips_id"`
}
type IdleSingleModeProgressLog struct {
CharaEffectLog []CharaEffectLog `json:"charaEffectLog"`
SupportCardGains []IdleSingleModeSupportCardGain `json:"supportCardGainInfo"`
EventGain IdleSingleModeGainInfo `json:"eventGainInfo"`
SuccessionGain IdleSingleModeGainInfo `json:"successionGainInfo"`
SuccessionFactorGains []IdleSingleModeSuccessionFactorGain `json:"successionFactorGainArray"`
RaceHistory []IdleSingleModeRaceHistory `json:"raceHistoryArray"`
GainSkillIDs []horse.SkillID `json:"gainSkillIdArray"`
TotalSkillPoint int32 `json:"totalSkillPoint"`
}
+810
View File
@@ -0,0 +1,810 @@
package main
import (
_ "embed"
"encoding/json"
"strings"
"testing"
"git.sunturtle.xyz/zephyr/horse"
"github.com/google/go-cmp/cmp"
)
//go:embed testdata.json
var testJSON string
func TestModel(t *testing.T) {
want := WorkIdleSingleMode{
Chara: SingleModeChara{
SingleModeCharaID: 1823,
CardID: 102401,
CharaGrade: 1,
Speed: 107,
Stamina: 118,
Power: 103,
Wiz: 147,
Guts: 133,
Vital: 100,
MaxSpeed: 1616,
MaxStamina: 1300,
MaxPower: 1318,
MaxWiz: 1332,
MaxGuts: 1516,
DefaultMaxSpeed: 1200,
DefaultMaxStamina: 1200,
DefaultMaxPower: 1200,
DefaultMaxWiz: 1200,
DefaultMaxGuts: 1200,
MaxVital: 100,
Motivation: 3,
Fans: 1,
Rarity: 4,
RaceProgramID: 0,
ReserveRaceProgramID: 0,
RaceRunningStyle: 1,
IsShortRace: 0,
TalentLevel: 5,
Skills: []Skill{
{SkillID: 100241, Level: 2},
},
DisableSkillIDs: []horse.SkillID{},
SkillTips: []SkillTip{
{GroupID: 90014, Rarity: 1, Level: 2},
{GroupID: 90020, Rarity: 1, Level: 1},
},
SupportCards: []SingleModeSupportCard{
{
Position: 1,
SupportCardID: 30010,
LimitBreakCount: 4,
Exp: 118185,
TrainingPartnerState: 0,
OwnerViewerID: 0,
RentalType: 0,
},
{
Position: 2,
SupportCardID: 30108,
LimitBreakCount: 4,
Exp: 118185,
TrainingPartnerState: 0,
OwnerViewerID: 0,
RentalType: 0,
},
{
Position: 3,
SupportCardID: 30062,
LimitBreakCount: 4,
Exp: 118185,
TrainingPartnerState: 0,
OwnerViewerID: 0,
RentalType: 0,
},
{
Position: 4,
SupportCardID: 30107,
LimitBreakCount: 4,
Exp: 118185,
TrainingPartnerState: 0,
OwnerViewerID: 0,
RentalType: 0,
},
{
Position: 5,
SupportCardID: 30028,
LimitBreakCount: 4,
Exp: 118185,
TrainingPartnerState: 0,
OwnerViewerID: 0,
RentalType: 0,
},
{
Position: 6,
SupportCardID: 30052,
LimitBreakCount: 4,
Exp: 118185,
TrainingPartnerState: 0,
OwnerViewerID: 818784439809,
RentalType: 0,
},
},
SuccessionTrainedCharaID1: 4637,
SuccessionTrainedCharaID2: 4670,
ProperDistanceShort: horse.AptitudeLvD,
ProperDistanceMile: horse.AptitudeLvC,
ProperDistanceMiddle: horse.AptitudeLvA,
ProperDistanceLong: horse.AptitudeLvA,
ProperRunningStyleNige: horse.AptitudeLvA,
ProperRunningStyleSenko: horse.AptitudeLvA,
ProperRunningStyleSashi: horse.AptitudeLvB,
ProperRunningStyleOikomi: horse.AptitudeLvB,
ProperGroundTurf: horse.AptitudeLvA,
ProperGroundDirt: horse.AptitudeLvA,
Turn: 1,
SkillPoint: 0,
ShortCutState: 0,
State: 0,
PlayingState: 1,
ScenarioID: 3,
RouteID: 14,
StartTime: "2026-07-27 15:37:29",
Evaluations: []Evaluation{
{
TargetID: 1,
Evaluation: 15,
IsOuting: 0,
StoryStep: 0,
IsAppear: 1,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 2,
Evaluation: 20,
IsOuting: 0,
StoryStep: 0,
IsAppear: 1,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 3,
Evaluation: 30,
IsOuting: 0,
StoryStep: 0,
IsAppear: 1,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 4,
Evaluation: 30,
IsOuting: 0,
StoryStep: 0,
IsAppear: 1,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 5,
Evaluation: 35,
IsOuting: 0,
StoryStep: 0,
IsAppear: 1,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 6,
Evaluation: 30,
IsOuting: 0,
StoryStep: 0,
IsAppear: 0,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 101,
Evaluation: 0,
IsOuting: 0,
StoryStep: 0,
IsAppear: 0,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 102,
Evaluation: 0,
IsOuting: 0,
StoryStep: 0,
IsAppear: 0,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 103,
Evaluation: 0,
IsOuting: 0,
StoryStep: 0,
IsAppear: 0,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 104,
Evaluation: 0,
IsOuting: 0,
StoryStep: 0,
IsAppear: 0,
GroupOutingInfo: []GroupOuting{},
},
{
TargetID: 106,
Evaluation: 0,
IsOuting: 0,
StoryStep: 0,
IsAppear: 0,
GroupOutingInfo: []GroupOuting{},
},
},
TrainingLevels: []TrainingLevel{
{CommandID: 101, Level: 1},
{CommandID: 102, Level: 1},
{CommandID: 103, Level: 1},
{CommandID: 105, Level: 1},
{CommandID: 106, Level: 1},
{CommandID: 601, Level: 5},
{CommandID: 602, Level: 5},
{CommandID: 603, Level: 5},
{CommandID: 604, Level: 5},
{CommandID: 605, Level: 5},
},
NicknameIDs: []int32{},
CharaEffectIDs: []int32{},
RouteRaceIDs: []int32{
145,
146,
147,
148,
149,
150,
151,
152,
153,
154,
155,
156,
},
GuestOutings: []GuestOuting{},
SkillUpgrades: []SingleModeSkillUpgrade{},
},
StartTime: 1785166649,
EndTime: 1785169649,
ProgressLog: IdleSingleModeProgressLog{
CharaEffectLog: []CharaEffectLog{
{CharaEffectID: 7, IsActive: true},
},
SupportCardGains: []IdleSingleModeSupportCardGain{
{
SupportCardID: 30010,
GainInfo: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 56},
Stamina: IdleSingleModeSignedInt{Value: 33},
Power: IdleSingleModeSignedInt{Value: 33},
Wiz: IdleSingleModeSignedInt{Value: 193},
Guts: IdleSingleModeSignedInt{Value: 33},
SkillPoint: 319,
SkillTips: []SkillTip{
{GroupID: 20019, Rarity: 1, Level: 1},
{GroupID: 20001, Rarity: 1, Level: 1},
},
},
},
{
SupportCardID: 30108,
GainInfo: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 54},
Stamina: IdleSingleModeSignedInt{Value: 35},
Power: IdleSingleModeSignedInt{Value: 35},
Wiz: IdleSingleModeSignedInt{Value: 135},
Guts: IdleSingleModeSignedInt{Value: 35},
SkillPoint: 408,
SkillTips: []SkillTip{
{GroupID: 20157, Rarity: 1, Level: 4},
{GroupID: 20203, Rarity: 1, Level: 4},
{GroupID: 20156, Rarity: 1, Level: 4},
},
},
},
{
SupportCardID: 30062,
GainInfo: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 31},
Stamina: IdleSingleModeSignedInt{Value: 184},
Power: IdleSingleModeSignedInt{Value: 31},
Wiz: IdleSingleModeSignedInt{Value: 31},
Guts: IdleSingleModeSignedInt{Value: 65},
SkillPoint: 234,
SkillTips: []SkillTip{
{GroupID: 20127, Rarity: 1, Level: 2},
},
},
},
{
SupportCardID: 30107,
GainInfo: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 136},
Stamina: IdleSingleModeSignedInt{Value: 35},
Power: IdleSingleModeSignedInt{Value: 88},
Wiz: IdleSingleModeSignedInt{Value: 35},
Guts: IdleSingleModeSignedInt{Value: 35},
SkillPoint: 287,
SkillTips: []SkillTip{
{GroupID: 20043, Rarity: 1, Level: 3},
{GroupID: 20127, Rarity: 1, Level: 3},
{GroupID: 20124, Rarity: 1, Level: 3},
},
},
},
{
SupportCardID: 30028,
GainInfo: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 116},
Stamina: IdleSingleModeSignedInt{Value: 33},
Power: IdleSingleModeSignedInt{Value: 86},
Wiz: IdleSingleModeSignedInt{Value: 33},
Guts: IdleSingleModeSignedInt{Value: 33},
SkillPoint: 284,
SkillTips: []SkillTip{
{GroupID: 20043, Rarity: 1, Level: 1},
{GroupID: 20076, Rarity: 1, Level: 3},
{GroupID: 20033, Rarity: 1, Level: 3},
{GroupID: 20118, Rarity: 1, Level: 3},
},
},
},
{
SupportCardID: 30052,
GainInfo: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 68},
Stamina: IdleSingleModeSignedInt{Value: 28},
Power: IdleSingleModeSignedInt{Value: 28},
Wiz: IdleSingleModeSignedInt{Value: 28},
Guts: IdleSingleModeSignedInt{Value: 68},
SkillPoint: 553,
SkillTips: []SkillTip{},
},
},
},
EventGain: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 817},
Stamina: IdleSingleModeSignedInt{Value: 313},
Power: IdleSingleModeSignedInt{Value: 427},
Wiz: IdleSingleModeSignedInt{Value: 498},
Guts: IdleSingleModeSignedInt{Value: 300},
SkillPoint: 1819,
SkillTips: []SkillTip{
{GroupID: 20166, Rarity: 2, Level: 3},
{GroupID: 20071, Rarity: 2, Level: 1},
{GroupID: 20027, Rarity: 1, Level: 2},
{GroupID: 20059, Rarity: 1, Level: 1},
{GroupID: 21007, Rarity: 1, Level: 1},
{GroupID: 21007, Rarity: 2, Level: 3},
{GroupID: 20021, Rarity: 1, Level: 1},
{GroupID: 20043, Rarity: 1, Level: 1},
{GroupID: 20043, Rarity: 2, Level: 1},
{GroupID: 20127, Rarity: 2, Level: 1},
{GroupID: 20135, Rarity: 1, Level: 1},
{GroupID: 20118, Rarity: 1, Level: 2},
{GroupID: 20207, Rarity: 1, Level: 1},
{GroupID: 20137, Rarity: 1, Level: 1},
{GroupID: 20209, Rarity: 1, Level: 1},
},
},
SuccessionGain: IdleSingleModeGainInfo{
Speed: IdleSingleModeSignedInt{Value: 67},
Stamina: IdleSingleModeSignedInt{Value: 0},
Power: IdleSingleModeSignedInt{Value: 94},
Wiz: IdleSingleModeSignedInt{Value: 118},
Guts: IdleSingleModeSignedInt{Value: 59},
MaxSpeed: 22,
MaxStamina: 2,
MaxPower: 28,
MaxWiz: 45,
MaxGuts: 22,
ProperDistanceShort: 0,
ProperDistanceMile: 1,
ProperDistanceMiddle: 0,
ProperDistanceLong: 0,
ProperRunningStyleNige: 0,
ProperRunningStyleSenko: 0,
ProperRunningStyleSashi: 0,
ProperRunningStyleOikomi: 0,
ProperGroundTurf: 0,
ProperGroundDirt: 5,
SkillPoint: 0,
SkillTips: []SkillTip{
{GroupID: 90014, Rarity: 1, Level: 2},
{GroupID: 90020, Rarity: 1, Level: 2},
{GroupID: 90019, Rarity: 1, Level: 2},
{GroupID: 20166, Rarity: 1, Level: 3},
{GroupID: 20233, Rarity: 1, Level: 2},
{GroupID: 20160, Rarity: 1, Level: 4},
{GroupID: 21005, Rarity: 1, Level: 3},
{GroupID: 20019, Rarity: 1, Level: 3},
{GroupID: 21001, Rarity: 1, Level: 4},
{GroupID: 20035, Rarity: 1, Level: 1},
{GroupID: 20091, Rarity: 1, Level: 4},
{GroupID: 20126, Rarity: 1, Level: 2},
{GroupID: 20159, Rarity: 1, Level: 2},
{GroupID: 20125, Rarity: 1, Level: 1},
{GroupID: 20007, Rarity: 1, Level: 1},
},
},
SuccessionFactorGains: []IdleSingleModeSuccessionFactorGain{
{
Year: 1,
GainFactorInfo: []Factor{
{FactorID: 103, Level: 0},
{FactorID: 10200101, Level: 0},
{FactorID: 503, Level: 0},
{FactorID: 1202, Level: 0},
{FactorID: 10140102, Level: 0},
{FactorID: 503, Level: 0},
{FactorID: 1202, Level: 0},
{FactorID: 403, Level: 0},
{FactorID: 3202, Level: 0},
{FactorID: 302, Level: 0},
{FactorID: 1203, Level: 0},
{FactorID: 302, Level: 0},
{FactorID: 1203, Level: 0},
},
},
{
Year: 2,
GainFactorInfo: []Factor{
{FactorID: 103, Level: 0},
{FactorID: 1000802, Level: 0},
{FactorID: 2016603, Level: 0},
{FactorID: 2023302, Level: 0},
{FactorID: 503, Level: 0},
{FactorID: 2102103, Level: 0},
{FactorID: 503, Level: 0},
{FactorID: 2016002, Level: 0},
{FactorID: 2100502, Level: 0},
{FactorID: 403, Level: 0},
{FactorID: 2001902, Level: 0},
{FactorID: 302, Level: 0},
{FactorID: 302, Level: 0},
{FactorID: 1203, Level: 0},
{FactorID: 10190102, Level: 0},
},
},
{
Year: 3,
GainFactorInfo: []Factor{
{FactorID: 103, Level: 0},
{FactorID: 2003501, Level: 0},
{FactorID: 2009102, Level: 0},
{FactorID: 10200101, Level: 0},
{FactorID: 503, Level: 0},
{FactorID: 1002002, Level: 0},
{FactorID: 2012502, Level: 0},
{FactorID: 503, Level: 0},
{FactorID: 2012602, Level: 0},
{FactorID: 2015902, Level: 0},
{FactorID: 403, Level: 0},
{FactorID: 302, Level: 0},
{FactorID: 302, Level: 0},
},
},
},
RaceHistory: []IdleSingleModeRaceHistory{
{
RaceHistory: SingleRaceHistory{
Turn: 12,
ProgramID: 1067,
Weather: 2,
GroundCondition: 2,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 2,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 400},
},
RaceRewardBonus: []RaceReward{},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 1398,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 44,
ProgramID: 168,
Weather: 1,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 11,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 11, ItemID: 83, ItemNum: 2},
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 30, ItemID: 110, ItemNum: 40},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 23976,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 48,
ProgramID: 81,
Weather: 1,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 4,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 11, ItemID: 91, ItemNum: 2},
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 30, ItemID: 110, ItemNum: 40},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 59940,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 54,
ProgramID: 12,
Weather: 2,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 7,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 13387,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 56,
ProgramID: 4,
Weather: 1,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 6,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 11, ItemID: 74, ItemNum: 2},
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 30, ItemID: 110, ItemNum: 40},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 29970,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 60,
ProgramID: 74,
Weather: 2,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 8,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 11, ItemID: 80, ItemNum: 2},
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 7, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 800},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 29970,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 68,
ProgramID: 76,
Weather: 1,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 14,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 11, ItemID: 84, ItemNum: 2},
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 7, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 800},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 29970,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 72,
ProgramID: 81,
Weather: 2,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 6,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 11, ItemID: 91, ItemNum: 2},
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 800},
{ItemType: 30, ItemID: 110, ItemNum: 40},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 59940,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 74,
ProgramID: 2031,
Weather: 2,
GroundCondition: 2,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 10,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 1200},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 13986,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 76,
ProgramID: 2130,
Weather: 1,
GroundCondition: 1,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 1,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 1200},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 30, ItemID: 110, ItemNum: 80},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 19980,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
{
RaceHistory: SingleRaceHistory{
Turn: 78,
ProgramID: 2228,
Weather: 2,
GroundCondition: 2,
RunningStyle: 1,
ResultRank: 1,
FrameOrder: 4,
NpcCount: 0,
},
RaceRewardInfo: CharaRaceReward{
ResultRank: 1,
ResultTime: 0,
RaceReward: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 1200},
{ItemType: 11, ItemID: 10, ItemNum: 2},
},
RaceRewardBonus: []RaceReward{
{ItemType: 91, ItemID: 59, ItemNum: 1200},
{ItemType: 30, ItemID: 110, ItemNum: 80},
},
RaceRewardPlusBonus: []RaceReward{},
RaceRewardBonusWin: []RaceReward{},
RaceRewardLimit: []RaceRewardLimit{},
GainedFans: 59940,
CampaignIDs: []int32{223},
},
LoseTipsID: 0,
},
},
GainSkillIDs: []horse.SkillID{},
TotalSkillPoint: 3904,
},
}
var got WorkIdleSingleMode
d := json.NewDecoder(strings.NewReader(testJSON))
d.DisallowUnknownFields()
if err := d.Decode(&got); err != nil {
t.Error(err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong data (+got/-want):\n%s", diff)
}
}
+61
View File
@@ -0,0 +1,61 @@
package main
import (
"bufio"
"encoding/csv"
"encoding/json"
"flag"
"log/slog"
"os"
"slices"
)
func main() {
var outf string
flag.StringVar(&outf, "o", "idle_single_mode.csv", "output CSV `file`")
flag.Parse()
out, err := os.Create(outf)
if err != nil {
slog.Error("create output", slog.Any("err", err))
os.Exit(1)
}
b := bufio.NewWriter(out)
defer b.Flush()
w := csv.NewWriter(b)
defer w.Flush()
if err := w.Write(headers); err != nil {
slog.Error("write header", slog.Any("err", err))
}
files := flag.Args()
slices.Sort(files)
for _, in := range files {
dofile(w, in)
}
}
func dofile(w *csv.Writer, name string) {
log := slog.With(slog.String("file", name))
log.Info("process")
f, err := os.Open(name)
if err != nil {
log.Error("open", slog.Any("err", err))
return
}
defer f.Close()
var ism WorkIdleSingleMode
d := json.NewDecoder(f)
d.DisallowUnknownFields()
if err := d.Decode(&ism); err != nil {
log.Error("decode", slog.Any("err", err))
return
}
for row := range ismRows(name, ism.ProgressLog) {
if err := w.Write(row.csv()); err != nil {
log.Error("write row", slog.Any("err", err))
}
}
}
+161
View File
@@ -0,0 +1,161 @@
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
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 IdleSingleModeProgressLog) iter.Seq[Row] {
return func(yield func(Row) bool) {
races := len(ism.RaceHistory)
var wins int
for _, r := range ism.RaceHistory {
if r.RaceHistory.ResultRank == 1 {
wins++
}
}
var conditions [11]bool
for _, v := range ism.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,
Deck1: int(ism.SupportCardGains[0].SupportCardID),
Deck2: int(ism.SupportCardGains[1].SupportCardID),
Deck3: int(ism.SupportCardGains[2].SupportCardID),
Deck4: int(ism.SupportCardGains[3].SupportCardID),
Deck5: int(ism.SupportCardGains[4].SupportCardID),
Deck6: int(ism.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(ism.EventGain.Speed.Value)
row.Stamina = int(ism.EventGain.Stamina.Value)
row.Power = int(ism.EventGain.Power.Value)
row.Guts = int(ism.EventGain.Guts.Value)
row.Wit = int(ism.EventGain.Wiz.Value)
row.SP = int(ism.EventGain.SkillPoint)
row.NumSkills = len(ism.EventGain.SkillTips)
if !yield(row) {
return
}
row.GainSource = "Inspiration"
row.Speed = int(ism.SuccessionGain.Speed.Value)
row.Stamina = int(ism.SuccessionGain.Stamina.Value)
row.Power = int(ism.SuccessionGain.Power.Value)
row.Guts = int(ism.SuccessionGain.Guts.Value)
row.Wit = int(ism.SuccessionGain.Wiz.Value)
row.SP = int(ism.SuccessionGain.SkillPoint)
row.NumSkills = len(ism.SuccessionGain.SkillTips)
if !yield(row) {
return
}
for _, sup := range ism.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.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),
}
}
+1526
View File
File diff suppressed because it is too large Load Diff