diff --git a/assets/db_inputs/wowhead_reforge_stats.json b/assets/db_inputs/wowhead_reforge_stats.json new file mode 100644 index 0000000000..6ba17577bd --- /dev/null +++ b/assets/db_inputs/wowhead_reforge_stats.json @@ -0,0 +1,450 @@ +{ + "113": { + "id": 113, + "i1": 6, + "s1": "spi", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "114": { + "id": 114, + "i1": 6, + "s1": "spi", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "115": { + "id": 115, + "i1": 6, + "s1": "spi", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "116": { + "id": 116, + "i1": 6, + "s1": "spi", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "117": { + "id": 117, + "i1": 6, + "s1": "spi", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "118": { + "id": 118, + "i1": 6, + "s1": "spi", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + }, + "119": { + "id": 119, + "i1": 6, + "s1": "spi", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "120": { + "id": 120, + "i1": 13, + "s1": "dodgertng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "121": { + "id": 121, + "i1": 13, + "s1": "dodgertng", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "122": { + "id": 122, + "i1": 13, + "s1": "dodgertng", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "123": { + "id": 123, + "i1": 13, + "s1": "dodgertng", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "124": { + "id": 124, + "i1": 13, + "s1": "dodgertng", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "125": { + "id": 125, + "i1": 13, + "s1": "dodgertng", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + }, + "126": { + "id": 126, + "i1": 13, + "s1": "dodgertng", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "127": { + "id": 127, + "i1": 14, + "s1": "parryrtng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "128": { + "id": 128, + "i1": 14, + "s1": "parryrtng", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "129": { + "id": 129, + "i1": 14, + "s1": "parryrtng", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "130": { + "id": 130, + "i1": 14, + "s1": "parryrtng", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "131": { + "id": 131, + "i1": 14, + "s1": "parryrtng", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "132": { + "id": 132, + "i1": 14, + "s1": "parryrtng", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + }, + "133": { + "id": 133, + "i1": 14, + "s1": "parryrtng", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "134": { + "id": 134, + "i1": 31, + "s1": "hitrtng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "135": { + "id": 135, + "i1": 31, + "s1": "hitrtng", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "136": { + "id": 136, + "i1": 31, + "s1": "hitrtng", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "137": { + "id": 137, + "i1": 31, + "s1": "hitrtng", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "138": { + "id": 138, + "i1": 31, + "s1": "hitrtng", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "139": { + "id": 139, + "i1": 31, + "s1": "hitrtng", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + }, + "140": { + "id": 140, + "i1": 31, + "s1": "hitrtng", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "141": { + "id": 141, + "i1": 32, + "s1": "critstrkrtng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "142": { + "id": 142, + "i1": 32, + "s1": "critstrkrtng", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "143": { + "id": 143, + "i1": 32, + "s1": "critstrkrtng", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "144": { + "id": 144, + "i1": 32, + "s1": "critstrkrtng", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "145": { + "id": 145, + "i1": 32, + "s1": "critstrkrtng", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "146": { + "id": 146, + "i1": 32, + "s1": "critstrkrtng", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + }, + "147": { + "id": 147, + "i1": 32, + "s1": "critstrkrtng", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "148": { + "id": 148, + "i1": 36, + "s1": "hastertng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "149": { + "id": 149, + "i1": 36, + "s1": "hastertng", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "150": { + "id": 150, + "i1": 36, + "s1": "hastertng", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "151": { + "id": 151, + "i1": 36, + "s1": "hastertng", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "152": { + "id": 152, + "i1": 36, + "s1": "hastertng", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "153": { + "id": 153, + "i1": 36, + "s1": "hastertng", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + }, + "154": { + "id": 154, + "i1": 36, + "s1": "hastertng", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "155": { + "id": 155, + "i1": 37, + "s1": "exprtng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "156": { + "id": 156, + "i1": 37, + "s1": "exprtng", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "157": { + "id": 157, + "i1": 37, + "s1": "exprtng", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "158": { + "id": 158, + "i1": 37, + "s1": "exprtng", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "159": { + "id": 159, + "i1": 37, + "s1": "exprtng", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "160": { + "id": 160, + "i1": 37, + "s1": "exprtng", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "161": { + "id": 161, + "i1": 37, + "s1": "exprtng", + "i2": 49, + "s2": "mastrtng", + "v": 0.4 + }, + "162": { + "id": 162, + "i1": 49, + "s1": "mastrtng", + "i2": 6, + "s2": "spi", + "v": 0.4 + }, + "163": { + "id": 163, + "i1": 49, + "s1": "mastrtng", + "i2": 13, + "s2": "dodgertng", + "v": 0.4 + }, + "164": { + "id": 164, + "i1": 49, + "s1": "mastrtng", + "i2": 14, + "s2": "parryrtng", + "v": 0.4 + }, + "165": { + "id": 165, + "i1": 49, + "s1": "mastrtng", + "i2": 31, + "s2": "hitrtng", + "v": 0.4 + }, + "166": { + "id": 166, + "i1": 49, + "s1": "mastrtng", + "i2": 32, + "s2": "critstrkrtng", + "v": 0.4 + }, + "167": { + "id": 167, + "i1": 49, + "s1": "mastrtng", + "i2": 36, + "s2": "hastertng", + "v": 0.4 + }, + "168": { + "id": 168, + "i1": 49, + "s1": "mastrtng", + "i2": 37, + "s2": "exprtng", + "v": 0.4 + } +} \ No newline at end of file diff --git a/proto/common.proto b/proto/common.proto index 90c6d14974..942fa6ba2d 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -140,6 +140,13 @@ message UnitStats { repeated double pseudo_stats = 2; } +message ReforgeStat { + int32 id = 1; + repeated Stat fromStat = 2; + repeated Stat toStat = 3; + double multiplier = 4; +} + enum ItemType { ItemTypeUnknown = 0; ItemTypeHead = 1; @@ -700,17 +707,11 @@ message PresetEncounter { repeated PresetTarget targets = 2; } -// Message for describing a reforged item -message Reforging { - Stat from_stat = 1; // Reforge from this stat - Stat to_stat = 2; // Reforge to this stat -} - message ItemSpec { int32 id = 2; int32 enchant = 3; repeated int32 gems = 4; - Reforging reforging = 5; //reforging information + int32 reforging = 5; //reforging id } message EquipmentSpec { @@ -721,6 +722,7 @@ message SimDatabase { repeated SimItem items = 1; repeated SimEnchant enchants = 2; repeated SimGem gems = 3; + repeated ReforgeStat reforge_stats = 6; } // Contains only the Item info needed by the sim. diff --git a/proto/ui.proto b/proto/ui.proto index 343ba90593..e4cdc9d116 100644 --- a/proto/ui.proto +++ b/proto/ui.proto @@ -23,6 +23,7 @@ message UIDatabase { repeated IconData spell_icons = 5; repeated GlyphID glyph_ids = 7; + repeated ReforgeStat reforge_stats = 12; } message UIZone { diff --git a/sim/core/database.go b/sim/core/database.go index 3f0bee795b..2e13f7a599 100644 --- a/sim/core/database.go +++ b/sim/core/database.go @@ -14,6 +14,7 @@ var WITH_DB = false var ItemsByID = map[int32]Item{} var GemsByID = map[int32]Gem{} var EnchantsByEffectID = map[int32]Enchant{} +var ReforgeStatsByID = map[int32]ReforgeStat{} func addToDatabase(newDB *proto.SimDatabase) { for _, v := range newDB.Items { @@ -33,43 +34,38 @@ func addToDatabase(newDB *proto.SimDatabase) { GemsByID[v.Id] = GemFromProto(v) } } -} -var reforgeableStats = []stats.Stat{ - stats.Spirit, - stats.Dodge, - stats.Parry, - stats.MeleeHit, - stats.SpellHit, - stats.SpellCrit, - stats.MeleeCrit, - stats.MeleeHaste, - stats.SpellHaste, - stats.Expertise, - //stats.Mastery + for _, v := range newDB.ReforgeStats { + if _, ok := ReforgeStatsByID[v.Id]; !ok { + ReforgeStatsByID[v.Id] = ReforgeStatFromProto(v) + } + } } -type Reforging struct { - FromStat proto.Stat - ToStat proto.Stat +type ReforgeStat struct { + ID int32 + FromStat []proto.Stat // Assuming Stat is an enum or int32 type you've defined elsewhere + ToStat []proto.Stat + Multiplier float64 } -func ReforgingFromProto(r *proto.Reforging) *Reforging { - if r == nil { - return nil - } - return &Reforging{ - FromStat: r.GetFromStat(), - ToStat: r.GetToStat(), +// ReforgeStatFromProto converts a protobuf ReforgeStat to a Go ReforgeStat +func ReforgeStatFromProto(protoStat *proto.ReforgeStat) ReforgeStat { + return ReforgeStat{ + ID: protoStat.GetId(), + FromStat: protoStat.GetFromStat(), + ToStat: protoStat.GetToStat(), + Multiplier: protoStat.GetMultiplier(), } } -func ReforgingToProto(s *Reforging) *proto.Reforging { - if s == nil { - return nil // Return nil if there's no reforging data - } - return &proto.Reforging{ - FromStat: s.FromStat, - ToStat: s.ToStat, + +// ReforgeStatToProto converts a Go ReforgeStat to a protobuf ReforgeStat +func ReforgeStatToProto(stat ReforgeStat) *proto.ReforgeStat { + return &proto.ReforgeStat{ + Id: stat.ID, + FromStat: stat.FromStat, + ToStat: stat.ToStat, + Multiplier: stat.Multiplier, } } @@ -96,7 +92,7 @@ type Item struct { // Modified for each instance of the item. Gems []Gem Enchant Enchant - Reforging *Reforging + Reforging *ReforgeStat //Internal use TempEnchant int32 @@ -122,12 +118,21 @@ func ItemFromProto(pData *proto.SimItem) Item { } func (item *Item) ToItemSpecProto() *proto.ItemSpec { - return &proto.ItemSpec{ - Id: item.ID, - Enchant: item.Enchant.EffectID, - Gems: MapSlice(item.Gems, func(gem Gem) int32 { return gem.ID }), - Reforging: ReforgingToProto(item.Reforging), + itemSpec := &proto.ItemSpec{ + Id: item.ID, + Enchant: item.Enchant.EffectID, + Gems: MapSlice(item.Gems, func(gem Gem) int32 { return gem.ID }), } + + // Check if Reforging is not nil before accessing ID + if item.Reforging != nil { + itemSpec.Reforging = item.Reforging.ID + } else { + // Explicitly set to 0 if Reforging is nil, for clarity (optional as zero value is default) + itemSpec.Reforging = 0 + } + + return itemSpec } type Enchant struct { @@ -162,7 +167,7 @@ type ItemSpec struct { ID int32 Enchant int32 Gems []int32 - Reforging *Reforging + Reforging int32 } type Equipment [proto.ItemSlot_ItemSlotRanged + 1]Item @@ -257,7 +262,7 @@ func ProtoToEquipmentSpec(es *proto.EquipmentSpec) EquipmentSpec { ID: item.Id, Enchant: item.Enchant, Gems: item.Gems, - Reforging: ReforgingFromProto(item.Reforging), + Reforging: item.Reforging, } } return coreEquip @@ -280,11 +285,12 @@ func NewItem(itemSpec ItemSpec) Item { // } } - if itemSpec.Reforging != nil { - if validateReforging(&item, *itemSpec.Reforging) { - item.Reforging = itemSpec.Reforging + if itemSpec.Reforging > 112 { // There is no id below 113 + reforge := ReforgeStatsByID[itemSpec.Reforging] + if validateReforging(&item, reforge) { + item.Reforging = &reforge } else { - panic(fmt.Sprintf("When validating reforging for item %d, the stat reforging for %s to %s could not be validated", itemSpec.ID, itemSpec.Reforging.FromStat.String(), itemSpec.Reforging.ToStat.String())) + panic(fmt.Sprintf("When validating reforging for item %d, the stat reforging id %d could not be validated", itemSpec.ID, itemSpec.Reforging)) } } @@ -309,29 +315,29 @@ func NewItem(itemSpec ItemSpec) Item { return item } -func validateReforging(item *Item, reforging Reforging) bool { - // Check if both from and to stats are reforgeable - fromStatReforgeable := false - toStatReforgeable := false - for _, stat := range reforgeableStats { - if stats.Stat(reforging.FromStat) == stat { - fromStatReforgeable = true - } - if stats.Stat(reforging.ToStat) == stat { - toStatReforgeable = true +func validateReforging(item *Item, reforging ReforgeStat) bool { + // Validate that the item can reforge these to stats + fromStatValid := false + for _, fromStat := range reforging.FromStat { + if item.Stats[fromStat] > 0 { + fromStatValid = true + break } } - - if !fromStatReforgeable || !toStatReforgeable { + if !fromStatValid { return false } - fromStatPresent := item.Stats[int(reforging.FromStat)] > 0 - toStatNotPresent := item.Stats[int(reforging.ToStat)] == 0 + toStatValid := false + for _, toStat := range reforging.ToStat { + if item.Stats[toStat] == 0 { + toStatValid = true + break + } + } - return fromStatPresent && toStatNotPresent + return toStatValid } - func NewEquipmentSet(equipSpec EquipmentSpec) Equipment { equipment := Equipment{} for _, itemSpec := range equipSpec { @@ -369,20 +375,27 @@ func (equipment *Equipment) Stats() stats.Stats { for _, item := range equipment { equipStats = equipStats.Add(item.Stats) - equipStats = equipStats.Add(item.Enchant.Stats) + // Apply reforging if item.Reforging != nil { reforgingChanges := stats.Stats{} - - fromStatValue := equipStats[item.Reforging.FromStat] - reduction := math.Floor(fromStatValue * 0.4) // Calculate 40% reduction floored - - reforgingChanges[item.Reforging.FromStat] = -reduction - reforgingChanges[item.Reforging.ToStat] = +reduction - - equipStats = equipStats.Add(reforgingChanges) // Apply reforging changes + for _, fromStat := range item.Reforging.FromStat { + if equipStats[fromStat] > 0 { + reduction := math.Floor(equipStats[fromStat] * 0.4) + reforgingChanges[fromStat] = -reduction + } + } + for _, toStat := range item.Reforging.FromStat { + if equipStats[toStat] > 0 { + increase := math.Floor(equipStats[toStat] * 0.4) + reforgingChanges[toStat] = +increase + } + } + equipStats = equipStats.Add(reforgingChanges) } + equipStats = equipStats.Add(item.Enchant.Stats) + for _, gem := range item.Gems { equipStats = equipStats.Add(gem.Stats) } diff --git a/tools/database/database.go b/tools/database/database.go index 7a18ffd83d..b22fff23ac 100644 --- a/tools/database/database.go +++ b/tools/database/database.go @@ -36,8 +36,9 @@ type WowDatabase struct { Zones map[int32]*proto.UIZone Npcs map[int32]*proto.UINPC - ItemIcons map[int32]*proto.IconData - SpellIcons map[int32]*proto.IconData + ItemIcons map[int32]*proto.IconData + SpellIcons map[int32]*proto.IconData + ReforgeStats map[int32]*proto.ReforgeStat Encounters []*proto.PresetEncounter GlyphIDs []*proto.GlyphID @@ -51,20 +52,22 @@ func NewWowDatabase() *WowDatabase { Zones: make(map[int32]*proto.UIZone), Npcs: make(map[int32]*proto.UINPC), - ItemIcons: make(map[int32]*proto.IconData), - SpellIcons: make(map[int32]*proto.IconData), + ItemIcons: make(map[int32]*proto.IconData), + SpellIcons: make(map[int32]*proto.IconData), + ReforgeStats: make(map[int32]*proto.ReforgeStat), } } func (db *WowDatabase) Clone() *WowDatabase { return &WowDatabase{ - Items: maps.Clone(db.Items), - Enchants: maps.Clone(db.Enchants), - Gems: maps.Clone(db.Gems), - Zones: maps.Clone(db.Zones), - Npcs: maps.Clone(db.Npcs), - ItemIcons: maps.Clone(db.ItemIcons), - SpellIcons: maps.Clone(db.SpellIcons), + Items: maps.Clone(db.Items), + Enchants: maps.Clone(db.Enchants), + Gems: maps.Clone(db.Gems), + Zones: maps.Clone(db.Zones), + Npcs: maps.Clone(db.Npcs), + ItemIcons: maps.Clone(db.ItemIcons), + SpellIcons: maps.Clone(db.SpellIcons), + ReforgeStats: maps.Clone(db.ReforgeStats), } } @@ -203,15 +206,16 @@ func (db *WowDatabase) ToUIProto() *proto.UIDatabase { }) return &proto.UIDatabase{ - Items: mapToSlice(db.Items), - Enchants: enchants, - Gems: mapToSlice(db.Gems), - Encounters: db.Encounters, - Zones: mapToSlice(db.Zones), - Npcs: mapToSlice(db.Npcs), - ItemIcons: mapToSlice(db.ItemIcons), - SpellIcons: mapToSlice(db.SpellIcons), - GlyphIds: db.GlyphIDs, + Items: mapToSlice(db.Items), + Enchants: enchants, + Gems: mapToSlice(db.Gems), + Encounters: db.Encounters, + Zones: mapToSlice(db.Zones), + Npcs: mapToSlice(db.Npcs), + ItemIcons: mapToSlice(db.ItemIcons), + SpellIcons: mapToSlice(db.SpellIcons), + GlyphIds: db.GlyphIDs, + ReforgeStats: mapToSlice(db.ReforgeStats), } } @@ -235,13 +239,14 @@ func ReadDatabaseFromJson(jsonStr string) *WowDatabase { } return &WowDatabase{ - Items: sliceToMap(dbProto.Items), - Enchants: enchants, - Gems: sliceToMap(dbProto.Gems), - Zones: sliceToMap(dbProto.Zones), - Npcs: sliceToMap(dbProto.Npcs), - ItemIcons: sliceToMap(dbProto.ItemIcons), - SpellIcons: sliceToMap(dbProto.SpellIcons), + Items: sliceToMap(dbProto.Items), + Enchants: enchants, + Gems: sliceToMap(dbProto.Gems), + Zones: sliceToMap(dbProto.Zones), + Npcs: sliceToMap(dbProto.Npcs), + ItemIcons: sliceToMap(dbProto.ItemIcons), + SpellIcons: sliceToMap(dbProto.SpellIcons), + ReforgeStats: sliceToMap(dbProto.ReforgeStats), } } @@ -280,6 +285,8 @@ func (db *WowDatabase) WriteJson(jsonFilePath string) { buffer.WriteString(",\n") tools.WriteProtoArrayToBuffer(uidb.Npcs, buffer, "npcs") buffer.WriteString(",\n") + tools.WriteProtoArrayToBuffer(uidb.ReforgeStats, buffer, "reforgeStats") + buffer.WriteString(",\n") tools.WriteProtoArrayToBuffer(uidb.ItemIcons, buffer, "itemIcons") buffer.WriteString(",\n") tools.WriteProtoArrayToBuffer(uidb.SpellIcons, buffer, "spellIcons") diff --git a/tools/database/gen_db/main.go b/tools/database/gen_db/main.go index b8931d749f..18b32335c3 100644 --- a/tools/database/gen_db/main.go +++ b/tools/database/gen_db/main.go @@ -60,6 +60,10 @@ func main() { } else if *genAsset == "wago-db2-items" { tools.WriteFile(fmt.Sprintf("%s/wago_db2_items.csv", inputsDir), tools.ReadWebRequired("https://wago.tools/db2/ItemSparse/csv?build=3.4.2.49311")) return + } else if *genAsset != "reforge-stats" { + //Todo: fill this when we have information from wowhead @ Neteyes - Gehennas + // For now, the version we have was taken from https://web.archive.org/web/20120201045249js_/http://www.wowhead.com/data=item-scaling + return } else if *genAsset != "db" { panic("Invalid gen value") } @@ -70,9 +74,13 @@ func main() { atlaslootDB := database.ReadDatabaseFromJson(tools.ReadFile(fmt.Sprintf("%s/atlasloot_db.json", inputsDir))) factionRestrictions := database.ParseItemFactionRestrictionsFromWagoDB(tools.ReadFile(fmt.Sprintf("%s/wago_db2_items.csv", inputsDir))) + // Todo: https://web.archive.org/web/20120201045249js_/http://www.wowhead.com/data=item-scaling + reforgeStats := database.ParseWowheadReforgeStats(tools.ReadFile(fmt.Sprintf("%s/wowhead_reforge_stats.json", inputsDir))) + db := database.NewWowDatabase() db.Encounters = core.PresetEncounters db.GlyphIDs = getGlyphIDsFromJson(fmt.Sprintf("%s/glyph_id_map.json", inputsDir)) + db.ReforgeStats = reforgeStats.ToProto() for _, response := range itemTooltips { if response.IsEquippable() { diff --git a/tools/database/wowhead_reforge_db.go b/tools/database/wowhead_reforge_db.go new file mode 100644 index 0000000000..92aa51a226 --- /dev/null +++ b/tools/database/wowhead_reforge_db.go @@ -0,0 +1,67 @@ +package database + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/tailscale/hujson" + "github.com/wowsims/wotlk/sim/core/proto" +) + +func ParseWowheadReforgeStats(contents string) WowheadReforgeStats { + var stats WowheadReforgeStats + standardized, err := hujson.Standardize([]byte(contents)) // Removes invalid JSON, such as trailing commas + if err != nil { + log.Fatalf("Failed to standardize json %s\n\n%s\n\n%s", err, contents[0:30], contents[len(contents)-30:]) + } + + err = json.Unmarshal(standardized, &stats) + if err != nil { + log.Fatalf("failed to parse wowhead item db to json %s\n\n%s", err, contents[0:30]) + } + fmt.Printf("\n--\nWowhead reforges loaded\n--\n") + return stats +} + +// statStringToEnum maps wowhead stat strings to their corresponding proto.Stat enum values +var statStringToEnum = map[string][]proto.Stat{ + "spi": {proto.Stat_StatSpirit}, + "dodgertng": {proto.Stat_StatDodge}, + "parryrtng": {proto.Stat_StatParry}, + "hitrtng": {proto.Stat_StatMeleeHit, proto.Stat_StatSpellHit}, + "critstrkrtng": {proto.Stat_StatMeleeCrit, proto.Stat_StatSpellCrit}, + "hastertng": {proto.Stat_StatMeleeHaste, proto.Stat_StatSpellHaste}, + "exprtng": {proto.Stat_StatExpertise}, + // "mastrtng" mapping needs to be defined when the appropriate proto.Stat value for mastery is available. + // "mastrtng": {proto.Stat_StatMastery}, +} + +func mapStringToStat(statString string) []proto.Stat { + return statStringToEnum[statString] // Directly return the slice from the map. +} + +func (reforgeStats WowheadReforgeStats) ToProto() map[int32]*proto.ReforgeStat { + protoStatsMap := make(map[int32]*proto.ReforgeStat) + for _, stat := range reforgeStats { + protoStat := &proto.ReforgeStat{ + Id: int32(stat.ReforgeID), + FromStat: mapStringToStat(stat.FromStat), // Assuming you have S1 and S2 fields in your struct + ToStat: mapStringToStat(stat.ToStat), + Multiplier: stat.ReforgeMultiplier, + } + protoStatsMap[protoStat.Id] = protoStat + } + return protoStatsMap +} + +type WowheadReforgeStat struct { + ReforgeID int `json:"id"` // Reforge ID used by game + FromID int `json:"i1"` // WH Stat ID to reforge from + FromStat string `json:"s1"` // WH Stat string to reforge from + ToID int `json:"i2"` // WH Stat ID to reforge to + ToStat string `json:"s2"` // WH Stat string to reforge to + ReforgeMultiplier float64 `json:"v"` // Multiplier for reforge, always 0.4 +} + +type WowheadReforgeStats map[string]WowheadReforgeStat