diff --git a/go.mod b/go.mod index a94c881..d45dd30 100644 --- a/go.mod +++ b/go.mod @@ -2,13 +2,13 @@ module github.com/araxiaonline/endgame-item-generator go 1.22.4 -replace github.com/araxiaonline/endgame-item-generator/models => ../models - require github.com/go-sql-driver/mysql v1.8.1 require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/thoas/go-funk v0.9.3 // indirect + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect ) diff --git a/go.sum b/go.sum index fca8bf3..cf1e8b8 100644 --- a/go.sum +++ b/go.sum @@ -8,11 +8,14 @@ github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/models/creatures.go b/internal/creatures/creatures.go similarity index 96% rename from models/creatures.go rename to internal/creatures/creatures.go index d671b29..e815c76 100644 --- a/models/creatures.go +++ b/internal/creatures/creatures.go @@ -1,10 +1,10 @@ -package models +package creatures import ( "errors" "fmt" - "github.com/araxiaonline/endgame-item-generator/utils" + "github.com/araxiaonline/endgame-item-generator/import/utils" _ "github.com/go-sql-driver/mysql" _ "github.com/jmoiron/sqlx" ) diff --git a/internal/items/item_test.go b/internal/items/item_test.go new file mode 100644 index 0000000..bf9ecaa --- /dev/null +++ b/internal/items/item_test.go @@ -0,0 +1,450 @@ +package items + +import ( + "io" + "log" + "math" + "reflect" + "testing" + + "golang.org/x/exp/rand" +) + +func TestGetPrimaryStat(t *testing.T) { + originalLog := log.Writer() + log.SetOutput(io.Discard) + defer log.SetOutput(originalLog) + + tests := []struct { + name string + item Item + wantStat int + wantValue int + expectError bool + }{ + { + name: "No primary stat found", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(1), StatValue1: ptrInt(10), + StatType2: ptrInt(2), StatValue2: ptrInt(20), + StatType3: ptrInt(12), StatValue3: ptrInt(15), + }, + wantStat: 0, + wantValue: 0, + expectError: false, + }, + { + name: "Primary stat found with higher value", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(3), StatValue1: ptrInt(10), // Agility + StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength + StatType3: ptrInt(5), StatValue3: ptrInt(15), // Intellect + }, + wantStat: 4, // Strength + wantValue: 20, + expectError: false, + }, + { + name: "Primary stat found with lower value", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(3), StatValue1: ptrInt(30), // Agility + StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength + StatType3: ptrInt(5), StatValue3: ptrInt(15), // Intellect + }, + wantStat: 3, // Agility + wantValue: 30, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotStat, gotValue, err := tt.item.GetPrimaryStat() + if (err != nil) != tt.expectError { + t.Errorf("GetPrimaryStat() error = %v, expectError %v", err, tt.expectError) + return + } + if gotStat != tt.wantStat { + t.Errorf("GetPrimaryStat() gotStat = %v, want %v", gotStat, tt.wantStat) + } + if gotValue != tt.wantValue { + t.Errorf("GetPrimaryStat() gotValue = %v, want %v", gotValue, tt.wantValue) + } + }) + } +} + +func TestGetStatList(t *testing.T) { + originalLog := log.Writer() + log.SetOutput(io.Discard) + defer log.SetOutput(originalLog) + + tests := []struct { + name string + item Item + want []int + expectError bool + }{ + { + name: "No stats available", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(0), StatValue1: ptrInt(0), + StatType2: ptrInt(0), StatValue2: ptrInt(0), + }, + want: []int{}, + expectError: false, + }, + { + name: "Multiple stats available", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(3), StatValue1: ptrInt(10), // Agility + StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength + StatType3: ptrInt(5), StatValue3: ptrInt(15), // Intellect + }, + want: []int{3, 4, 5}, + expectError: false, + }, + { + name: "Stats are ordered correctly", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(7), StatValue1: ptrInt(10), // Agility + StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength + StatType3: ptrInt(31), StatValue3: ptrInt(15), // Intellect + }, + want: []int{4, 7, 31}, + expectError: false, + }, + { + name: "Some stats are zero", + item: Item{ + Entry: 1, + Name: "Test Item", + StatType1: ptrInt(3), StatValue1: ptrInt(0), // Agility + StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength + StatType3: ptrInt(5), StatValue3: ptrInt(0), // Intellect + }, + want: []int{4}, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.item.GetStatList() + if (err != nil) != tt.expectError { + t.Errorf("GetStatList() error = %v, expectError %v", err, tt.expectError) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetStatList() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetDPS(t *testing.T) { + tests := []struct { + name string + item Item + wantDPS float64 + expectError bool + }{ + { + name: "Valid DPS calculation", + item: Item{ + MinDmg1: ptrInt(50), + MaxDmg1: ptrInt(70), + Delay: ptrFloat64(3000), + }, + wantDPS: 20.00, + expectError: false, + }, + { + name: "High damage DPS calculation", + item: Item{ + MinDmg1: ptrInt(100), + MaxDmg1: ptrInt(150), + Delay: ptrFloat64(2000), + }, + wantDPS: 62.50, + expectError: false, + }, + { + name: "Low damage DPS calculation", + item: Item{ + MinDmg1: ptrInt(10), + MaxDmg1: ptrInt(15), + Delay: ptrFloat64(1500), + }, + wantDPS: 8.33, + expectError: false, + }, + { + name: "Missing MinDmg1", + item: Item{ + MaxDmg1: ptrInt(70), + Delay: ptrFloat64(3000), + }, + wantDPS: 0, + expectError: true, + }, + { + name: "Missing MaxDmg1", + item: Item{ + MinDmg1: ptrInt(50), + Delay: ptrFloat64(3000), + }, + wantDPS: 0, + expectError: true, + }, + { + name: "Missing Delay", + item: Item{ + MinDmg1: ptrInt(50), + MaxDmg1: ptrInt(70), + }, + wantDPS: 0, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotDPS, err := tt.item.GetDPS() + if (err != nil) != tt.expectError { + t.Errorf("GetDPS() error = %v, expectError %v", err, tt.expectError) + return + } + if !tt.expectError && !almostEqual(gotDPS, tt.wantDPS, 0.01) { + t.Errorf("GetDPS() = %v, want %v", gotDPS, tt.wantDPS) + } + }) + } +} + +func TestScaleDPS(t *testing.T) { + tests := []struct { + name string + item Item + level int + wantDPSMin float64 + wantDPSMax float64 + expectError bool + }{ + { + name: "Valid Scale DPS calculation", + item: Item{ + ItemLevel: ptrInt(60), + Delay: ptrFloat64(3000), + MinDmg1: ptrInt(50), + MaxDmg1: ptrInt(70), + Subclass: ptrInt(4), // One-handed weapon + Quality: ptrInt(3), // Rare + }, + level: 70, + wantDPSMin: 53.0, // Expected DPS range due to randomness + wantDPSMax: 107.0, + expectError: false, + }, + { + name: "High level Scale DPS calculation", + item: Item{ + ItemLevel: ptrInt(80), + Delay: ptrFloat64(2000), + MinDmg1: ptrInt(150), + MaxDmg1: ptrInt(200), + Subclass: ptrInt(17), // Two-handed weapon + Quality: ptrInt(4), // Epic + }, + level: 100, + wantDPSMin: 120.0, // Expected DPS range due to randomness + wantDPSMax: 240.0, + expectError: false, + }, + { + name: "Low level Scale DPS calculation", + item: Item{ + ItemLevel: ptrInt(20), + Delay: ptrFloat64(1000), + MinDmg1: ptrInt(30), + MaxDmg1: ptrInt(50), + Subclass: ptrInt(2), // Ranged weapon + Quality: ptrInt(2), // Uncommon + }, + level: 25, + wantDPSMin: 21.0, // Expected DPS range due to randomness + wantDPSMax: 42.0, + expectError: false, + }, + { + name: "Missing ItemLevel", + item: Item{ + Delay: ptrFloat64(3000), + MinDmg1: ptrInt(50), + MaxDmg1: ptrInt(70), + Subclass: ptrInt(4), // One-handed weapon + Quality: ptrInt(3), // Rare + }, + level: 70, + wantDPSMin: 0, + wantDPSMax: 0, + expectError: true, + }, + { + name: "Missing Delay", + item: Item{ + ItemLevel: ptrInt(60), + MinDmg1: ptrInt(50), + MaxDmg1: ptrInt(70), + Subclass: ptrInt(4), // One-handed weapon + Quality: ptrInt(3), // Rare + }, + level: 70, + wantDPSMin: 0, + wantDPSMax: 0, + expectError: true, + }, + { + name: "Secondary damage scaling", + item: Item{ + ItemLevel: ptrInt(60), + Delay: ptrFloat64(3000), + MinDmg1: ptrInt(50), + MaxDmg1: ptrInt(70), + MinDmg2: ptrInt(25), + MaxDmg2: ptrInt(35), + Subclass: ptrInt(4), // One-handed weapon + Quality: ptrInt(3), // Rare + }, + level: 70, + wantDPSMin: 53.0, // Expected DPS range due to randomness + wantDPSMax: 107.0, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Seed the random number generator for consistent test results + rand.Seed(1) + + gotDPS, err := tt.item.ScaleDPS(tt.level) + if (err != nil) != tt.expectError { + t.Errorf("ScaleDPS() error = %v, expectError %v", err, tt.expectError) + return + } + if !tt.expectError && (gotDPS < tt.wantDPSMin || gotDPS > tt.wantDPSMax) { + t.Errorf("ScaleDPS() = %v, want between %v and %v", gotDPS, tt.wantDPSMin, tt.wantDPSMax) + } + }) + } +} + +func TestGetDpsModifier(t *testing.T) { + tests := []struct { + name string + item Item + wantModifier float64 + expectError bool + }{ + { + name: "Valid one-handed weapon modifier", + item: Item{ + Subclass: ptrInt(4), // One-handed weapon + Quality: ptrInt(3), // Rare + }, + wantModifier: 0.64 * 1.38, + expectError: false, + }, + { + name: "Valid two-handed weapon modifier", + item: Item{ + Subclass: ptrInt(17), // Two-handed weapon + Quality: ptrInt(4), // Epic + }, + wantModifier: 0.80 * 1.5, + expectError: false, + }, + { + name: "Valid ranged weapon modifier", + item: Item{ + Subclass: ptrInt(2), // Ranged weapon + Quality: ptrInt(2), // Uncommon + }, + wantModifier: 0.70 * 1.25, + expectError: false, + }, + { + name: "Valid wand modifier", + item: Item{ + Subclass: ptrInt(19), // Wand + Quality: ptrInt(4), // Epic + }, + wantModifier: 0.70 * 1.5, + expectError: false, + }, + { + name: "Invalid subclass", + item: Item{ + Subclass: ptrInt(99), // Invalid subclass + Quality: ptrInt(3), // Rare + }, + wantModifier: 0, + expectError: true, + }, + { + name: "Missing subclass", + item: Item{ + Quality: ptrInt(3), // Rare + }, + wantModifier: 0, + expectError: true, + }, + { + name: "Missing quality", + item: Item{ + Subclass: ptrInt(4), // One-handed weapon + }, + wantModifier: 0, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotModifier, err := tt.item.GetDpsModifier() + if (err != nil) != tt.expectError { + t.Errorf("GetDpsModifier() error = %v, expectError %v", err, tt.expectError) + return + } + if !tt.expectError && !almostEqual(gotModifier, tt.wantModifier, 0.01) { + t.Errorf("GetDpsModifier() = %v, want %v", gotModifier, tt.wantModifier) + } + }) + } +} + +// Helper function to return a pointer to an int +func ptrInt(i int) *int { + return &i +} + +func ptrFloat64(f float64) *float64 { + return &f +} + +func almostEqual(a, b, tolerance float64) bool { + return math.Abs(a-b) <= tolerance +} diff --git a/models/items.go b/internal/items/items.go similarity index 94% rename from models/items.go rename to internal/items/items.go index 9cc494b..33e5593 100644 --- a/models/items.go +++ b/internal/items/items.go @@ -1,4 +1,4 @@ -package models +package items import ( "errors" @@ -7,8 +7,7 @@ import ( "math" "math/rand/v2" "reflect" - - "github.com/araxiaonline/endgame-item-generator/utils" + "slices" ) /** @@ -171,28 +170,71 @@ var StatModifiers = map[int]float64{ 48: 0.65, // ITEM_MOD_BLOCK_VALUE } +// Get the primary stat for an item (strength, agility, intellect, spirit, stamina) func (item Item) GetPrimaryStat() (int, int, error) { var primaryStat int64 var primaryVal int64 - values := reflect.ValueOf(item) for i := 1; i < 11; i++ { - statType := values.FieldByName(fmt.Sprintf("StatType%v", i)).Elem().Int() - // first check if the stat type is not in the primary stats str, agi, intellect, spirit, stamina + + statType, err := item.GetField(fmt.Sprintf("StatType%v", i)) + if err != nil { + log.Printf("Failed to get stat type %v for item: %v", i, item.Name) + continue + } if statType < 3 || statType > 7 { continue } - statValue := values.FieldByName(fmt.Sprintf("StatValue%v", i)).Elem().Int() - if statValue > primaryVal { - primaryVal = statValue - primaryStat = statType + val, err := item.GetField(fmt.Sprintf("StatValue%v", i)) + + if err != nil { + log.Printf("Failed to get stat value %v for item: %v", i, item.Name) + continue + } + if val == 0 { + continue + } + + if int64(val) > primaryVal { + primaryVal = int64(val) + primaryStat = int64(statType) } } return int(primaryStat), int(primaryVal), nil } +/** + * Get the statIds for anitem as a slice of integers + * @return []int + */ +func (item Item) GetStatList() ([]int, error) { + + statList := []int{} + for i := 1; i < 11; i++ { + val, err := item.GetField(fmt.Sprintf("StatValue%v", i)) + + if err != nil { + log.Printf("Failed to get stat value %v for item: %v", i, item.Name) + continue + } + if val == 0 { + continue + } + + statId, err := item.GetField(fmt.Sprintf("StatType%v", i)) + if err != nil { + log.Printf("Failed to get stat type %v for item: %s", i, item.Name) + continue + } + statList = append(statList, statId) + slices.Sort(statList) + + } + return statList, nil +} + func (i Item) GetDpsModifier() (float64, error) { if i.Subclass == nil { return 0, fmt.Errorf("subclass on the item is not set") diff --git a/internal/items/items_mysql.go b/internal/items/items_mysql.go new file mode 100644 index 0000000..5ef49aa --- /dev/null +++ b/internal/items/items_mysql.go @@ -0,0 +1,160 @@ +package items + +import ( + "fmt" +) + +func GetItemFields(prefix string) string { + pre := "" + if prefix != "" { + pre = prefix + "." + } + + return ` + ` + pre + `entry, ` + pre + `name, ` + pre + `displayid, + quality, ItemLevel, class, subclass, inventoryType, + allowableClass, allowableRace, + armor,material, + requiredSkill, requiredLevel, + dmg_min1, dmg_max1, + dmg_min2,dmg_max2, + dmg_type1, dmg_type2, + delay, sheath, MaxDurability, + statsCount, + stat_type1, stat_value1, + stat_type2, stat_value2, + stat_type3, stat_value3, + stat_type4, stat_value4, + stat_type5, stat_value5, + stat_type6, stat_value6, + stat_type7, stat_value7, + stat_type8, stat_value8, + stat_type9, stat_value9, + stat_type10, stat_value10, + spellid_1, spellid_2, spellid_3, + spelltrigger_1, spelltrigger_2, spelltrigger_3` +} + +func ItemToSql(item Item, reqLevel int, difficulty int) string { + + entryBump := 20000000 + spellBump := 30000000 + if difficulty == 4 { + entryBump = 21000000 + } + if difficulty == 5 { + entryBump = 22000000 + } + + if *item.Quality == 4 { + spellBump = 31000000 + } + if *item.Quality == 5 { + spellBump = 32000000 + } + + spells := "" + if len(item.Spells) > 0 { + for i, spell := range item.Spells { + spells += spells.SpellToSql(spell, *item.Quality) + item.UpdateField(fmt.Sprintf("SpellId%v", i), spellBump+spell.ID) + } + } + + delete := fmt.Sprintf("DELETE FROM acore_world.item_template WHERE entry = %v;", entryBump+item.Entry) + + clone := fmt.Sprintf(` + INSERT INTO acore_world.item_template ( + entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, + BuyPrice, SellPrice, InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, + RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, RequiredCityRank, + RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, + stat_type1, stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, + stat_type5, stat_value5, stat_type6, stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, + stat_type9, stat_value9, stat_type10, stat_value10, ScalingStatDistribution, ScalingStatValue, + dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, nature_res, + frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, + spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, + spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, + spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3, + spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4, + spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5, + spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, + PageMaterial, startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, + MaxDurability, area, Map, BagFamily, TotemCategory, socketColor_1, socketContent_1, socketColor_2, + socketContent_2, socketColor_3, socketContent_3, socketBonus, GemProperties, RequiredDisenchantSkill, + ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, FoodType, + minMoneyLoot, maxMoneyLoot, flagsCustom, VerifiedBuild + ) + SELECT + entry + %v, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, + BuyPrice, SellPrice, InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, + RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, RequiredCityRank, + RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, + stat_type1, stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, + stat_type5, stat_value5, stat_type6, stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, + stat_type9, stat_value9, stat_type10, stat_value10, ScalingStatDistribution, ScalingStatValue, + dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, nature_res, + frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, + spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, + spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, + spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3, + spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4, + spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5, + spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, + PageMaterial, startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, + MaxDurability, area, Map, BagFamily, TotemCategory, socketColor_1, socketContent_1, socketColor_2, + socketContent_2, socketColor_3, socketContent_3, socketBonus, GemProperties, RequiredDisenchantSkill, + ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, FoodType, + minMoneyLoot, maxMoneyLoot, flagsCustom, VerifiedBuild + FROM acore_world.item_template as src + WHERE src.entry = %v ON DUPLICATE KEY UPDATE entry = src.entry + %v; + `, entryBump, item.Entry, entryBump) + + update := fmt.Sprintf(` + UPDATE acore_world.item_template + SET + Quality = %v, + ItemLevel = %v, + RequiredLevel = %v, + dmg_min1 = %v, + dmg_max1 = %v, + dmg_min2 = %v, + dmg_max2 = %v, + StatsCount = %v, + stat_type1 = %v, + stat_value1 = %v, + stat_type2 = %v, + stat_value2 = %v, + stat_type3 = %v, + stat_value3 = %v, + stat_type4 = %v, + stat_value4 = %v, + stat_type5 = %v, + stat_value5 = %v, + stat_type6 = %v, + stat_value6 = %v, + stat_type7 = %v, + stat_value7 = %v, + stat_type8 = %v, + stat_value8 = %v, + stat_type9 = %v, + stat_value9 = %v, + stat_type10 = %v, + stat_value10 = %v, + spellid_1 = %v, + spellid_2 = %v, + spellid_3 = %v, + RequiredDisenchantSkill = %v, + DisenchantID = %v, + SellPrice = FLOOR(100000 + (RAND() * 400001)), + Armor = %v + WHERE entry = %v; + `, *item.Quality, *item.ItemLevel, reqLevel, *item.MinDmg1, *item.MaxDmg1, *item.MinDmg2, *item.MaxDmg2, *item.StatsCount, + *item.StatType1, *item.StatValue1, *item.StatType2, *item.StatValue2, *item.StatType3, *item.StatValue3, *item.StatType4, *item.StatValue4, + *item.StatType5, *item.StatValue5, *item.StatType6, *item.StatValue6, *item.StatType7, *item.StatValue7, *item.StatType8, *item.StatValue8, + *item.StatType9, *item.StatValue9, *item.StatType10, *item.StatValue10, *item.SpellId1, *item.SpellId2, *item.SpellId3, 375, + 68, *item.Armor, entryBump+item.Entry) + + return fmt.Sprintf("%s %s \n %s \n %s", spells, delete, clone, update) +} diff --git a/models/maps.go b/internal/maps/maps.go similarity index 99% rename from models/maps.go rename to internal/maps/maps.go index 05700fe..4402b6c 100644 --- a/models/maps.go +++ b/internal/maps/maps.go @@ -1,4 +1,4 @@ -package models +package maps import ( "fmt" diff --git a/internal/pkg/db/mysql.go b/internal/pkg/db/mysql.go new file mode 100644 index 0000000..e5fc221 --- /dev/null +++ b/internal/pkg/db/mysql.go @@ -0,0 +1,43 @@ +package db + +import ( + "os" + + _ "github.com/go-sql-driver/mysql" + "github.com/jmoiron/sqlx" +) + +type MySql struct { + client *sqlx.DB +} + +type MySqlConfig struct { + Host string + User string + Password string + Database string +} + +func ConnectMySql(config *MySqlConfig) (*MySql, error) { + + if config == nil { + config = &MySqlConfig{ + Host: os.Getenv("DB_HOST"), + User: os.Getenv("DB_USER"), + Password: os.Getenv("DB_PASSWORD"), + Database: os.Getenv("DB_NAME"), + } + } + + connString := config.User + ":" + config.Password + "@tcp(" + config.Host + ")/" + config.Database + client, err := sqlx.Open("mysql", connString) + if err != nil { + return nil, err + } + + return &MySql{client: client}, nil +} + +func (db *MySql) Close() { + db.client.Close() +} diff --git a/internal/pkg/db/sqlite.go b/internal/pkg/db/sqlite.go new file mode 100644 index 0000000..3191267 --- /dev/null +++ b/internal/pkg/db/sqlite.go @@ -0,0 +1,23 @@ +package db + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +type SqlLite struct { + client *sql.DB +} + +func ConnectSqlLite(path string) (*SqlLite, error) { + client, err := sql.Open("sqlite3", path) + if err != nil { + return nil, err + } + return &SqlLite{client: client}, nil +} + +func (db *SqlLite) Close() { + db.client.Close() +} diff --git a/models/spells.go b/internal/spells/spells.go similarity index 98% rename from models/spells.go rename to internal/spells/spells.go index f1d1ac1..4ed7419 100644 --- a/models/spells.go +++ b/internal/spells/spells.go @@ -1,4 +1,4 @@ -package models +package spells import ( "fmt" @@ -7,7 +7,6 @@ import ( "strconv" "strings" - "github.com/araxiaonline/endgame-item-generator/utils" "github.com/thoas/go-funk" ) @@ -130,7 +129,7 @@ func (db Database) GetSpell(id int) (Spell, error) { } spell := Spell{} - sql := "SELECT " + utils.GetSpellFields() + " FROM `spell_dbc` WHERE ID = ? -- " + strconv.Itoa(id) + sql := "SELECT " + GetSpellFields() + " FROM `spell_dbc` WHERE ID = ? -- " + strconv.Itoa(id) err := db.client.Get(&spell, sql, id) if err != nil { diff --git a/converter.go b/internal/spells/spells_mysql.go similarity index 59% rename from converter.go rename to internal/spells/spells_mysql.go index 90939d8..22d892b 100644 --- a/converter.go +++ b/internal/spells/spells_mysql.go @@ -1,137 +1,39 @@ -package main +package spells import ( "fmt" - - "github.com/araxiaonline/endgame-item-generator/models" ) -// convert an item to a create sql statement -func ItemToSql(item models.Item, reqLevel int, difficulty int) string { - - entryBump := 20000000 - spellBump := 30000000 - if difficulty == 4 { - entryBump = 21000000 - } - if difficulty == 5 { - entryBump = 22000000 - } - - if *item.Quality == 4 { - spellBump = 31000000 - } - if *item.Quality == 5 { - spellBump = 32000000 - } - - spells := "" - if len(item.Spells) > 0 { - for i, spell := range item.Spells { - spells += SpellToSql(spell, *item.Quality) - item.UpdateField(fmt.Sprintf("SpellId%v", i), spellBump+spell.ID) - } - } - - delete := fmt.Sprintf("DELETE FROM acore_world.item_template WHERE entry = %v;", entryBump+item.Entry) - - clone := fmt.Sprintf(` - INSERT INTO acore_world.item_template ( - entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, - BuyPrice, SellPrice, InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, - RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, RequiredCityRank, - RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, - stat_type1, stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, - stat_type5, stat_value5, stat_type6, stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, - stat_type9, stat_value9, stat_type10, stat_value10, ScalingStatDistribution, ScalingStatValue, - dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, nature_res, - frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, - spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, - spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, - spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3, - spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4, - spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5, - spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, - PageMaterial, startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, - MaxDurability, area, Map, BagFamily, TotemCategory, socketColor_1, socketContent_1, socketColor_2, - socketContent_2, socketColor_3, socketContent_3, socketBonus, GemProperties, RequiredDisenchantSkill, - ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, FoodType, - minMoneyLoot, maxMoneyLoot, flagsCustom, VerifiedBuild - ) - SELECT - entry + %v, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, - BuyPrice, SellPrice, InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, - RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, RequiredCityRank, - RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, - stat_type1, stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, - stat_type5, stat_value5, stat_type6, stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, - stat_type9, stat_value9, stat_type10, stat_value10, ScalingStatDistribution, ScalingStatValue, - dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, nature_res, - frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, - spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, - spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, - spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3, - spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4, - spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5, - spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, - PageMaterial, startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, - MaxDurability, area, Map, BagFamily, TotemCategory, socketColor_1, socketContent_1, socketColor_2, - socketContent_2, socketColor_3, socketContent_3, socketBonus, GemProperties, RequiredDisenchantSkill, - ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, FoodType, - minMoneyLoot, maxMoneyLoot, flagsCustom, VerifiedBuild - FROM acore_world.item_template as src - WHERE src.entry = %v ON DUPLICATE KEY UPDATE entry = src.entry + %v; - `, entryBump, item.Entry, entryBump) - - update := fmt.Sprintf(` - UPDATE acore_world.item_template - SET - Quality = %v, - ItemLevel = %v, - RequiredLevel = %v, - dmg_min1 = %v, - dmg_max1 = %v, - dmg_min2 = %v, - dmg_max2 = %v, - StatsCount = %v, - stat_type1 = %v, - stat_value1 = %v, - stat_type2 = %v, - stat_value2 = %v, - stat_type3 = %v, - stat_value3 = %v, - stat_type4 = %v, - stat_value4 = %v, - stat_type5 = %v, - stat_value5 = %v, - stat_type6 = %v, - stat_value6 = %v, - stat_type7 = %v, - stat_value7 = %v, - stat_type8 = %v, - stat_value8 = %v, - stat_type9 = %v, - stat_value9 = %v, - stat_type10 = %v, - stat_value10 = %v, - spellid_1 = %v, - spellid_2 = %v, - spellid_3 = %v, - RequiredDisenchantSkill = %v, - DisenchantID = %v, - SellPrice = FLOOR(100000 + (RAND() * 400001)), - Armor = %v - WHERE entry = %v; - `, *item.Quality, *item.ItemLevel, reqLevel, *item.MinDmg1, *item.MaxDmg1, *item.MinDmg2, *item.MaxDmg2, *item.StatsCount, - *item.StatType1, *item.StatValue1, *item.StatType2, *item.StatValue2, *item.StatType3, *item.StatValue3, *item.StatType4, *item.StatValue4, - *item.StatType5, *item.StatValue5, *item.StatType6, *item.StatValue6, *item.StatType7, *item.StatValue7, *item.StatType8, *item.StatValue8, - *item.StatType9, *item.StatValue9, *item.StatType10, *item.StatValue10, *item.SpellId1, *item.SpellId2, *item.SpellId3, 375, - 68, *item.Armor, entryBump+item.Entry) - - return fmt.Sprintf("%s %s \n %s \n %s", spells, delete, clone, update) +func GetSpellFields() string { + return ` + ID, + Name_Lang_enUS, + Description_Lang_enUS, + AuraDescription_Lang_enUS, + ProcChance, + SpellLevel, + Effect_1, + Effect_2, + Effect_3, + EffectDieSides_1, + EffectDieSides_2, + EffectDieSides_3, + EffectRealPointsPerLevel_1, + EffectRealPointsPerLevel_2, + EffectRealPointsPerLevel_3, + EffectBasePoints_1, + EffectBasePoints_2, + EffectBasePoints_3, + EffectAura_1, + EffectAura_2, + EffectAura_3, + EffectBonusMultiplier_1, + EffectBonusMultiplier_2, + EffectBonusMultiplier_3 + ` } -func SpellToSql(spell models.Spell, quality int) string { +func SpellToSql(spell Spell, quality int) string { entryBump := 30000000 if quality == 4 { diff --git a/internal/utils/sqlbuillder.go b/internal/utils/sqlbuillder.go new file mode 100644 index 0000000..d4b585b --- /dev/null +++ b/internal/utils/sqlbuillder.go @@ -0,0 +1 @@ +package utils diff --git a/main.go b/main.go index 626aac9..7ae62bd 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,10 @@ package main import ( "flag" - "fmt" "io" "log" "os" - "github.com/araxiaonline/endgame-item-generator/models" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) @@ -16,13 +14,13 @@ func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) godotenv.Load() - models.Connect() + // database.models.Connect() debug := flag.Bool("debug", false, "Enable verbose logging inside generator") - itemLevel := flag.Int("ilvl", 305, "Specify the item level to start scaling from, expansion and difficulty modifiers scale up.") - difficulty := flag.Int("difficulty", 3, "set the difficulty of the dungeon, defaults to 3 (mythic) 4 (legendary) 5 (ascendant)") - levelUp := flag.Bool("levelUp", false, "Boss items require higher +1 level to equip, defaults to false") - baselevel := flag.Int("baselevel", 80, "set the base level for items to be used, defaults to 80 this is required for levelUp flag") + // itemLevel := flag.Int("ilvl", 305, "Specify the item level to start scaling from, expansion and difficulty modifiers scale up.") + // difficulty := flag.Int("difficulty", 3, "set the difficulty of the dungeon, defaults to 3 (mythic) 4 (legendary) 5 (ascendant)") + // levelUp := flag.Bool("levelUp", false, "Boss items require higher +1 level to equip, defaults to false") + // baselevel := flag.Int("baselevel", 80, "set the base level for items to be used, defaults to 80 this is required for levelUp flag") flag.Parse() @@ -32,119 +30,119 @@ func main() { log.SetOutput(io.Discard) } - if difficulty == nil || *difficulty < 3 || *difficulty > 5 { - log.Fatal("difficulty must be between 3-5") - os.Exit(1) - } + // if difficulty == nil || *difficulty < 3 || *difficulty > 5 { + // log.Fatal("difficulty must be between 3-5") + // os.Exit(1) + // } - // main loop - dungeons, err := models.DB.GetDungeons(-1) - if err != nil { - log.Panicf("failed to get dungeons for expansion %v error: %v", 0, err) - } + // // main loop + // dungeons, err := models.DB.GetDungeons(-1) + // if err != nil { + // log.Panicf("failed to get dungeons for expansion %v error: %v", 0, err) + // } - for _, dungeon := range dungeons { + // for _, dungeon := range dungeons { - log.Printf("+++++Dungeon: %s ID: %v level %v \n", dungeon.Name, dungeon.Id, dungeon.Level) + // log.Printf("+++++Dungeon: %s ID: %v level %v \n", dungeon.Name, dungeon.Id, dungeon.Level) - bosses, err := models.DB.GetBosses(dungeon.Id) - if err != nil { - log.Fatal("failed to get bosses") - } + // bosses, err := models.DB.GetBosses(dungeon.Id) + // if err != nil { + // log.Fatal("failed to get bosses") + // } - // Determine the scale value of the item based on expansion and dungeon level - scaleValue := *itemLevel - endGameDung := false - if dungeon.Level == 60 { - scaleValue += 10 - endGameDung = true - } + // // Determine the scale value of the item based on expansion and dungeon level + // scaleValue := *itemLevel + // endGameDung := false + // if dungeon.Level == 60 { + // scaleValue += 10 + // endGameDung = true + // } - if dungeon.ExpansionId == 1 && dungeon.Level <= 70 { - scaleValue += 3 - } + // if dungeon.ExpansionId == 1 && dungeon.Level <= 70 { + // scaleValue += 3 + // } - if dungeon.ExpansionId == 1 && dungeon.Level == 70 { - scaleValue += 12 - endGameDung = true - } + // if dungeon.ExpansionId == 1 && dungeon.Level == 70 { + // scaleValue += 12 + // endGameDung = true + // } - if dungeon.ExpansionId == 2 && dungeon.Level <= 80 { - scaleValue += 4 - } + // if dungeon.ExpansionId == 2 && dungeon.Level <= 80 { + // scaleValue += 4 + // } - if dungeon.ExpansionId == 2 && dungeon.Level == 80 { - scaleValue += 15 - endGameDung = true - } + // if dungeon.ExpansionId == 2 && dungeon.Level == 80 { + // scaleValue += 15 + // endGameDung = true + // } - // Apply difficuly modifiers for gear scale - // mythic: Bosses-Epic Gear (Purple) drops and Rare (Blue) for random drops (BOE) - // legendary: Bosses-Epic Gear (Purple) drops and Epic (Purple) for random drops (BOE) - // ascendant: Bosses-Legendary Gear (Yellow) drops and Epic (Purple) for random drops (BOE) - var bossQuality int - var boeQuality int + // // Apply difficuly modifiers for gear scale + // // mythic: Bosses-Epic Gear (Purple) drops and Rare (Blue) for random drops (BOE) + // // legendary: Bosses-Epic Gear (Purple) drops and Epic (Purple) for random drops (BOE) + // // ascendant: Bosses-Legendary Gear (Yellow) drops and Epic (Purple) for random drops (BOE) + // var bossQuality int + // var boeQuality int - if *difficulty == 4 { - bossQuality = 4 - boeQuality = 4 - } else if *difficulty == 5 { - bossQuality = 5 - boeQuality = 4 - } else { - bossQuality = 4 - boeQuality = 3 - } + // if *difficulty == 4 { + // bossQuality = 4 + // boeQuality = 4 + // } else if *difficulty == 5 { + // bossQuality = 5 + // boeQuality = 4 + // } else { + // bossQuality = 4 + // boeQuality = 3 + // } - for _, boss := range bosses { + // for _, boss := range bosses { - items, err := models.DB.GetBossLoot(boss.Entry) - log.Printf("++++++++++ Boss: %s Entry: %v has %v items\n", boss.Name, boss.Entry, len(items)) - if err != nil { - log.Fatal(err) - continue - } + // items, err := models.DB.GetBossLoot(boss.Entry) + // log.Printf("++++++++++ Boss: %s Entry: %v has %v items\n", boss.Name, boss.Entry, len(items)) + // if err != nil { + // log.Fatal(err) + // continue + // } - for _, item := range items { + // for _, item := range items { - _, error := item.ScaleItem(scaleValue, bossQuality) - if error != nil { - log.Printf("Failed to scale item: %v", error) - continue - } + // _, error := item.ScaleItem(scaleValue, bossQuality) + // if error != nil { + // log.Printf("Failed to scale item: %v", error) + // continue + // } - fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel) - if *levelUp && endGameDung { - fmt.Print(ItemToSql(item, *baselevel+1, *difficulty)) - } else { - fmt.Print(ItemToSql(item, *baselevel, *difficulty)) - } - } + // fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel) + // if *levelUp && endGameDung { + // fmt.Print(ItemToSql(item, *baselevel+1, *difficulty)) + // } else { + // fmt.Print(ItemToSql(item, *baselevel, *difficulty)) + // } + // } - } + // } - items2, err := models.DB.GetAddlDungeonDrops(dungeon.Id) - if err != nil { - log.Printf("failed to get additional loot for dungeon %v - err: %v", dungeon.Id, err) - } + // items2, err := models.DB.GetAddlDungeonDrops(dungeon.Id) + // if err != nil { + // log.Printf("failed to get additional loot for dungeon %v - err: %v", dungeon.Id, err) + // } - for _, item := range items2 { + // for _, item := range items2 { - // reduce item level of dungeon random drops since they are not boss fights - adjScaleValue := scaleValue - 4 + // // reduce item level of dungeon random drops since they are not boss fights + // adjScaleValue := scaleValue - 4 - _, error := item.ScaleItem(adjScaleValue, boeQuality) - if error != nil { - log.Printf("Failed to scale item: %v", error) - continue - } + // _, error := item.ScaleItem(adjScaleValue, boeQuality) + // if error != nil { + // log.Printf("Failed to scale item: %v", error) + // continue + // } - fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel) - fmt.Print(ItemToSql(item, *baselevel, *difficulty)) + // fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel) + // fmt.Print(ItemToSql(item, *baselevel, *difficulty)) - } - log.Printf("++++++++++ AdditionalLoot Count: %v\n", len(items2)) - } + // } + // log.Printf("++++++++++ Additional Count: %v\n", len(items2)) + // } - defer models.DB.Close() + // defer models.DB.Close() } diff --git a/models/connect.go b/models/connect.go deleted file mode 100644 index 3dafe86..0000000 --- a/models/connect.go +++ /dev/null @@ -1,27 +0,0 @@ -package models - -import ( - "os" - - _ "github.com/go-sql-driver/mysql" - "github.com/jmoiron/sqlx" -) - -type Database struct { - client *sqlx.DB -} - -var DB Database - -func Connect() { - var connString string = os.Getenv("DB_USER") + ":" + os.Getenv("DB_PASSWORD") + "@tcp(" + os.Getenv("DB_HOST") + ")/" + os.Getenv("DB_NAME") - var err error - DB.client, err = sqlx.Open("mysql", connString) - if err != nil { - panic(err.Error()) - } -} - -func (db Database) Close() { - db.client.Close() -} diff --git a/ascendant-scaled.sql b/prebuilt_sql/ascendant-scaled.sql similarity index 100% rename from ascendant-scaled.sql rename to prebuilt_sql/ascendant-scaled.sql diff --git a/legend-scaled.sql b/prebuilt_sql/legend-scaled.sql similarity index 100% rename from legend-scaled.sql rename to prebuilt_sql/legend-scaled.sql diff --git a/mythic-scaled.sql b/prebuilt_sql/mythic-scaled.sql similarity index 100% rename from mythic-scaled.sql rename to prebuilt_sql/mythic-scaled.sql diff --git a/scripts/localdb/items.db b/scripts/localdb/items.db new file mode 100644 index 0000000..3fbf719 Binary files /dev/null and b/scripts/localdb/items.db differ diff --git a/scripts/localdb/populate-endgame.go b/scripts/localdb/populate-endgame.go new file mode 100644 index 0000000..ba023ff --- /dev/null +++ b/scripts/localdb/populate-endgame.go @@ -0,0 +1,103 @@ +// package main + +// import ( +// "database/sql" +// "fmt" +// "log" +// "slices" +// "strconv" +// "strings" + +// // "github.com/araxiaonline/endgame-item-generator/internal/models" +// "github.com/araxiaonline/endgame-item-generator/internal/utils" +// "github.com/joho/godotenv" +// _ "github.com/mattn/go-sqlite3" +// ) + +// type EndGameItem struct { +// Entry int `db:"entry"` +// Class int `db:"class"` +// Subclass int `db:"subclass"` +// StatsList string `db:"stats_list"` +// } + +// func createItemsTable(db *sql.DB) { +// createItems := `CREATE TABLE IF NOT EXISTS items ( +// entry int unsigned NOT NULL DEFAULT '0', +// class tinyint unsigned NOT NULL DEFAULT '0', +// name varchar(250) NOT NULL DEFAULT '', +// Quality int unsigned NOT NULL DEFAULT '0', +// itemLevel int unsigned NOT NULL DEFAULT '0', +// subclass tinyint unsigned NOT NULL DEFAULT '0', +// stats_list varchar(250) NOT NULL DEFAULT '', +// PRIMARY KEY (entry) +// )` + +// _, err := db.Exec(createItems) +// if err != nil { +// log.Fatal(err) +// } +// } + +// func ConvertIntSliceToString(slice []int) string { +// sliceStr := make([]string, len(slice)) +// for i, v := range slice { +// sliceStr[i] = strconv.Itoa(v) +// } + +// return strings.Join(sliceStr, ",") +// } + +// func main() { + +// liteDb, err := sql.Open("sqlite3", "./items.db") +// if err != nil { +// log.Fatal(err) +// } +// godotenv.Load("../../.env") +// models.Connect() +// sqlDb := models.DB.Client() + +// defer liteDb.Close() +// defer sqlDb.Close() + +// // create the items table if it doesnt exist +// createItemsTable(liteDb) + +// // create the endgames items lookup table locally for items 200 and above +// var items []models.Item +// sql := ` +// SELECT ` + utils.GetItemFields("") + ` +// from acore_world.item_template +// where ItemLevel >= 200 and Quality >= 3 and ItemLevel < 290 +// AND name NOT LIKE 'NPC Equip%' and name NOT LIKE 'OLD%' +// AND name NOT LIKE '%(test)%' AND name NOT LIKE '%Deprecated%' +// AND name NOT LIKE '%Monster - %' +// AND ((class = 2 and subclass IN(0,1,2,3,4,5,6,7,8,10,11,12,13,15,16,17,18,19)) or ((class = 4 AND subclass IN (1,2,3,4,6)))) +// ` +// err = sqlDb.Select(&items, sql) +// if err != nil { +// log.Printf("Failed to get items: %v", err) +// } + +// for _, item := range items { +// var statsList []int +// for i := 1; i <= 10; i++ { +// val, _ := item.GetField(fmt.Sprintf("StatValue%v", i)) +// statId, _ := item.GetField(fmt.Sprintf("StatType%v", i)) +// if val != 0 { +// statsList = append(statsList, statId) +// } +// } +// slices.Sort(statsList) +// statsListStr := ConvertIntSliceToString(statsList) +// log.Printf("StatList %s for Item %v", statsListStr, item.Name) +// _, err = liteDb.Exec("INSERT OR IGNORE INTO items (entry, class, name, Quality, itemLevel, subclass, stats_list) VALUES (?, ?, ?, ?, ?,?,?)", item.Entry, *item.Class, item.Name, *item.Quality, *item.ItemLevel, *item.Subclass, statsListStr) +// if err != nil { +// log.Printf("Failed to insert item %v: %v", item.Entry, err) +// } + +// } + +// log.Printf("Items: %v", len(items)) +// } diff --git a/spells-list.txt b/spells-list.txt new file mode 100644 index 0000000..5d9d982 --- /dev/null +++ b/spells-list.txt @@ -0,0 +1,7 @@ +19451 - Frenzy - fast attack / damage (major) +25516 - Aura of Command - regen, attack speed, dmg (minor) +14538 - Aural Shock (reduce casting speed by 35%) (major) + +Grievous Bite +67719 Frost Fever = deals frost damage and reduces attack speed +Aura of Anger - Shadow damage over time \ No newline at end of file diff --git a/utils/sqlbuillder.go b/utils/sqlbuillder.go deleted file mode 100644 index dc8978c..0000000 --- a/utils/sqlbuillder.go +++ /dev/null @@ -1,61 +0,0 @@ -package utils - -func GetItemFields(prefix string) string { - pre := "" - if prefix != "" { - pre = prefix + "." - } - - return ` - ` + pre + `entry, ` + pre + `name, ` + pre + `displayid, - quality, ItemLevel, class, subclass, inventoryType, - allowableClass, allowableRace, - armor,material, - requiredSkill, requiredLevel, - dmg_min1, dmg_max1, - dmg_min2,dmg_max2, - dmg_type1, dmg_type2, - delay, sheath, MaxDurability, - statsCount, - stat_type1, stat_value1, - stat_type2, stat_value2, - stat_type3, stat_value3, - stat_type4, stat_value4, - stat_type5, stat_value5, - stat_type6, stat_value6, - stat_type7, stat_value7, - stat_type8, stat_value8, - stat_type9, stat_value9, - stat_type10, stat_value10, - spellid_1, spellid_2, spellid_3, - spelltrigger_1, spelltrigger_2, spelltrigger_3` -} - -func GetSpellFields() string { - return ` - ID, - Name_Lang_enUS, - Description_Lang_enUS, - AuraDescription_Lang_enUS, - ProcChance, - SpellLevel, - Effect_1, - Effect_2, - Effect_3, - EffectDieSides_1, - EffectDieSides_2, - EffectDieSides_3, - EffectRealPointsPerLevel_1, - EffectRealPointsPerLevel_2, - EffectRealPointsPerLevel_3, - EffectBasePoints_1, - EffectBasePoints_2, - EffectBasePoints_3, - EffectAura_1, - EffectAura_2, - EffectAura_3, - EffectBonusMultiplier_1, - EffectBonusMultiplier_2, - EffectBonusMultiplier_3 - ` -}