From d612eaa9ff3ac85a45d7e6ff4441e28ed7129280 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Sun, 25 Aug 2024 23:43:42 -0400 Subject: [PATCH] Tuned modifiers more and fixed bugs with how quality was being handled for legendaries --- cmd/dungeon-items/main.go | 23 ++- cmd/fetch-highlevel-items/main.go | 5 +- internal/config/modifier.go | 106 +++++++------- internal/db/mysql/creatures.go | 68 +++++++++ internal/db/mysql/items.go | 3 +- internal/db/sqlite/items.go | 21 +++ internal/items/items.go | 43 +++--- main.go | 228 ++++++++++++++---------------- 8 files changed, 285 insertions(+), 212 deletions(-) diff --git a/cmd/dungeon-items/main.go b/cmd/dungeon-items/main.go index af25599..c875a33 100644 --- a/cmd/dungeon-items/main.go +++ b/cmd/dungeon-items/main.go @@ -9,20 +9,19 @@ import ( "strings" "github.com/araxiaonline/endgame-item-generator/internal/db/mysql" + "github.com/araxiaonline/endgame-item-generator/internal/db/sqlite" "github.com/joho/godotenv" _ "github.com/mattn/go-sqlite3" ) -type DungeonItem struct { - Entry int `db:"entry"` - MapId int `db:"mapId"` - CreatureId int `db:"creatureId"` - Quality int `db:"Quality"` - Expansion int `db:"expansion"` - DungeonLevel int `db:"dungeonLevel"` -} - func createTable(db *sql.DB) { + + droptable := `DROP TABLE IF EXISTS dungeon_items` + _, err := db.Exec(droptable) + if err != nil { + log.Fatal(err) + } + createTable := `CREATE TABLE IF NOT EXISTS dungeon_items ( entry int unsigned NOT NULL DEFAULT '0', mapId tinyint unsigned NOT NULL DEFAULT '0', @@ -33,7 +32,7 @@ func createTable(db *sql.DB) { PRIMARY KEY (entry) )` - _, err := db.Exec(createTable) + _, err = db.Exec(createTable) if err != nil { log.Fatal(err) } @@ -96,7 +95,7 @@ func main() { for _, dungItem := range dbItems { - insertItem := DungeonItem{ + insertItem := sqlite.DungeonItem{ Entry: dungItem.Entry, MapId: dungeon.Id, Quality: *dungItem.Quality, @@ -117,7 +116,7 @@ func main() { } for _, dungItem := range dbItems { - insertItem := DungeonItem{ + insertItem := sqlite.DungeonItem{ Entry: dungItem.Entry, MapId: dungeon.Id, Quality: *dungItem.Quality, diff --git a/cmd/fetch-highlevel-items/main.go b/cmd/fetch-highlevel-items/main.go index 62a6df1..ff19e9b 100644 --- a/cmd/fetch-highlevel-items/main.go +++ b/cmd/fetch-highlevel-items/main.go @@ -52,7 +52,7 @@ func ConvertIntSliceToString(slice []int) string { func main() { godotenv.Load("../../.env") - liteDb, err := sql.Open("sqlite3", "./items.db") + liteDb, err := sql.Open("sqlite3", "../../data/items.db") if err != nil { log.Fatal(err) } @@ -79,11 +79,12 @@ func main() { sql := ` SELECT ` + mysql.GetItemFields("") + ` from acore_world.item_template - where ItemLevel >= 200 and Quality >= 3 and ItemLevel < 290 + where ItemLevel >= 220 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 (0,1,2,3,4,6)))) + AND stat_type1 != 32 AND stat_type2 != 32 AND stat_type3 != 32 AND stat_type4 != 32 AND stat_type5 != 32 AND stat_type6 != 32 AND stat_type7 != 32 AND stat_type8 != 32 AND stat_type9 != 32 AND stat_type10 != 32 ` err = mysqlDb.Select(&dbItems, sql) if err != nil { diff --git a/internal/config/modifier.go b/internal/config/modifier.go index c13c8e3..acde6a9 100644 --- a/internal/config/modifier.go +++ b/internal/config/modifier.go @@ -12,14 +12,14 @@ var InvTypeModifiers = map[int]float64{ 10: 0.625, // Hands 11: 1.0, // Finger 13: 0.42, // One-Hand (not to confuse with Off-Hand = 22) - 14: 0.56, // Shield (class = armor, not weapon even if in weapon slot) + 14: 0.66, // Shield (class = armor, not weapon even if in weapon slot) 15: 0.32, // Ranged (Bows) (see also Ranged right = 26) - 16: 0.56, // Back + 16: 0.66, // Back 17: 1.0, // Two-Hand 18: 1.0, // Bag (assuming same as Chest for simplicity) 19: 1.0, // Tabard (assuming same as Chest for simplicity) 20: 1.0, // Robe (see also Chest = 5) - 21: 1.0, // Main hand + 21: 0.85, // Main hand 22: 0.42, // Off Hand weapons (see also One-Hand = 13) 23: 0.56, // Held in Off-Hand (class = armor, not weapon even if in weapon slot) 24: 1.0, // Ammo (assuming same as Chest for simplicity) @@ -29,12 +29,12 @@ var InvTypeModifiers = map[int]float64{ } var QualityModifiers = map[int]float64{ - 0: 1.0, // Common - 1: 1.1, // Uncommon - 2: 1.2, // Rare - 3: 1.3, // Epic - 4: 1.5, // Legendary - 5: 1.7, // Artifact + 0: 0.8, // Poor + 1: 0.9, // Common + 2: 1.0, // UnCommon + 3: 1.2, // Rare + 4: 1.4, // Epic + 5: 5.0, // Legendary } var MaterialModifiers = map[int]float64{ @@ -93,48 +93,48 @@ var StatModifiers = map[int]float64{ } var ScalingFactor = map[int]float64{ - 0: 1.1, // ITEM_MOD_MANA - 1: 1.1, // ITEM_MOD_HEALTH - 3: 1.1, // ITEM_MOD_AGILITY - 4: 1.1, // ITEM_MOD_STRENGTH - 5: 1.1, // ITEM_MOD_INTELLECT - 6: 1.1, // ITEM_MOD_SPIRIT - 7: 2.0, // ITEM_MOD_STAMINA - 12: 1.1, // ITEM_MOD_DEFENSE_SKILL_RATING - 13: 1.1, // ITEM_MOD_DODGE_RATING - 14: 1.1, // ITEM_MOD_PARRY_RATING - 15: 1.1, // ITEM_MOD_BLOCK_RATING - 16: 1.1, // ITEM_MOD_HIT_MELEE_RATING - 17: 1.1, // ITEM_MOD_HIT_RANGED_RATING - 18: 1.1, // ITEM_MOD_HIT_SPELL_RATING - 19: 1.1, // ITEM_MOD_CRIT_MELEE_RATING - 20: 1.1, // ITEM_MOD_CRIT_RANGED_RATING - 21: 1.1, // ITEM_MOD_CRIT_SPELL_RATING - 22: 1.1, // ITEM_MOD_HIT_TAKEN_MELEE_RATING - 23: 1.1, // ITEM_MOD_HIT_TAKEN_RANGED_RATING - 24: 1.1, // ITEM_MOD_HIT_TAKEN_SPELL_RATING - 25: 1.1, // ITEM_MOD_CRIT_TAKEN_MELEE_RATING - 26: 1.1, // ITEM_MOD_CRIT_TAKEN_RANGED_RATING - 27: 1.1, // ITEM_MOD_CRIT_TAKEN_SPELL_RATING - 28: 1.1, // ITEM_MOD_HASTE_MELEE_RATING - 29: 1.1, // ITEM_MOD_HASTE_RANGED_RATING - 30: 1.1, // ITEM_MOD_HASTE_SPELL_RATING - 31: 1.1, // ITEM_MOD_HIT_RATING - 32: 1.1, // ITEM_MOD_CRIT_RATING - 33: 1.1, // ITEM_MOD_HIT_TAKEN_RATING - 34: 1.1, // ITEM_MOD_CRIT_TAKEN_RATING - 35: 1.1, // ITEM_MOD_RESILIENCE_RATING - 36: 1.1, // ITEM_MOD_HASTE_RATING - 37: 1.1, // ITEM_MOD_EXPERTISE_RATING - 38: 1.5, // ITEM_MOD_ATTACK_POWER - 39: 1.1, // ITEM_MOD_RANGED_ATTACK_POWER - 40: 1.1, // ITEM_MOD_FERAL_ATTACK_POWER (not used as of 3.3) - 41: 1.7, // ITEM_MOD_SPELL_HEALING_DONE - 42: 1.7, // ITEM_MOD_SPELL_DAMAGE_DONE - 43: 1.5, // ITEM_MOD_MANA_REGENERATION - 44: 1.0, // ITEM_MOD_ARMOR_PENETRATION_RATING - 45: 1.7, // ITEM_MOD_SPELL_POWER - 46: 1.1, // ITEM_MOD_HEALTH_REGEN - 47: 1.0, // ITEM_MOD_SPELL_PENETRATION - 48: 1.1, // ITEM_MOD_BLOCK_VALUE + 0: 1.1, // ITEM_MOD_MANA + 1: 1.5, // ITEM_MOD_HEALTH + 3: 1.2, // ITEM_MOD_AGILITY + 4: 1.2, // ITEM_MOD_STRENGTH + 5: 1.2, // ITEM_MOD_INTELLECT + 6: 1.3, // ITEM_MOD_SPIRIT + 7: 1.85, // ITEM_MOD_STAMINA + 12: 1.1, // ITEM_MOD_DEFENSE_SKILL_RATING + 13: 1.1, // ITEM_MOD_DODGE_RATING + 14: 1.1, // ITEM_MOD_PARRY_RATING + 15: 1.1, // ITEM_MOD_BLOCK_RATING + 16: 1.1, // ITEM_MOD_HIT_MELEE_RATING + 17: 1.1, // ITEM_MOD_HIT_RANGED_RATING + 18: 1.1, // ITEM_MOD_HIT_SPELL_RATING + 19: 1.2, // ITEM_MOD_CRIT_MELEE_RATING + 20: 1.2, // ITEM_MOD_CRIT_RANGED_RATING + 21: 1.2, // ITEM_MOD_CRIT_SPELL_RATING + 22: 1.3, // ITEM_MOD_HIT_TAKEN_MELEE_RATING + 23: 1.3, // ITEM_MOD_HIT_TAKEN_RANGED_RATING + 24: 1.3, // ITEM_MOD_HIT_TAKEN_SPELL_RATING + 25: 1.3, // ITEM_MOD_CRIT_TAKEN_MELEE_RATING + 26: 1.3, // ITEM_MOD_CRIT_TAKEN_RANGED_RATING + 27: 1.3, // ITEM_MOD_CRIT_TAKEN_SPELL_RATING + 28: 1.15, // ITEM_MOD_HASTE_MELEE_RATING + 29: 1.15, // ITEM_MOD_HASTE_RANGED_RATING + 30: 1.15, // ITEM_MOD_HASTE_SPELL_RATING + 31: 1.1, // ITEM_MOD_HIT_RATING + 32: 1.25, // ITEM_MOD_CRIT_RATING + 33: 1.3, // ITEM_MOD_HIT_TAKEN_RATING + 34: 1.3, // ITEM_MOD_CRIT_TAKEN_RATING + 35: 1.0, // ITEM_MOD_RESILIENCE_RATING + 36: 1.15, // ITEM_MOD_HASTE_RATING + 37: 0.8, // ITEM_MOD_EXPERTISE_RATING + 38: 1.45, // ITEM_MOD_ATTACK_POWER + 39: 1.45, // ITEM_MOD_RANGED_ATTACK_POWER + 40: 1.45, // ITEM_MOD_FERAL_ATTACK_POWER (not used as of 3.3) + 41: 1.4, // ITEM_MOD_SPELL_HEALING_DONE + 42: 1.4, // ITEM_MOD_SPELL_DAMAGE_DONE + 43: 1.3, // ITEM_MOD_MANA_REGENERATION + 44: 1.1, // ITEM_MOD_ARMOR_PENETRATION_RATING + 45: 1.5, // ITEM_MOD_SPELL_POWER + 46: 1.3, // ITEM_MOD_HEALTH_REGEN + 47: 1.0, // ITEM_MOD_SPELL_PENETRATION + 48: 1.0, // ITEM_MOD_BLOCK_VALUE } diff --git a/internal/db/mysql/creatures.go b/internal/db/mysql/creatures.go index 4d203ad..88a9587 100644 --- a/internal/db/mysql/creatures.go +++ b/internal/db/mysql/creatures.go @@ -12,6 +12,70 @@ type Boss struct { ExperienceModifier int `db:"ExperienceModifier"` } +var BossIDs = map[int]bool{ + 11520: true, // Taragaman the Hungerer (Ragefire Chasm) + 3654: true, // Mutanus the Devourer (Wailing Caverns) + 639: true, // Edwin VanCleef (The Deadmines) + 4275: true, // Archmage Arugal (Shadowfang Keep) + 4829: true, // Aku'mai (Blackfathom Deeps) + 1716: true, // Bazil Thredd (Stormwind Stockade) + 7800: true, // Mekgineer Thermaplugg (Gnomeregan) + 4421: true, // Charlga Razorflank (Razorfen Kraul) + 4543: true, // Bloodmage Thalnos (Scarlet Monastery Graveyard) + 6487: true, // Arcanist Doan (Scarlet Monastery Library) + 3975: true, // Herod (Scarlet Monastery Armory) + 3977: true, // High Inquisitor Whitemane (Scarlet Monastery Cathedral) + 7358: true, // Amnennar the Coldbringer (Razorfen Downs) + 2748: true, // Archaedas (Uldaman) + 7267: true, // Chief Ukorz Sandscalp (Zul'Farrak) + 12201: true, // Princess Theradras (Maraudon) + 8443: true, // Avatar of Hakkar (Sunken Temple) + 9019: true, // Emperor Dagran Thaurissan (Blackrock Depths) + 9568: true, // Overlord Wyrmthalak (Lower Blackrock Spire) + 10363: true, // General Drakkisath (Upper Blackrock Spire) + 11492: true, // Alzzin the Wildshaper (Dire Maul East) + 11489: true, // Tendris Warpwood (Dire Maul West) + 11501: true, // King Gordok (Dire Maul North) + 10440: true, // Baron Rivendare (Stratholme Undead Side) + 10813: true, // Balnazzar (Stratholme Live Side) + 1853: true, // Darkmaster Gandling (Scholomance) + + 17307: true, // Vazruden (Hellfire Ramparts) + 17536: true, // Nazan (Hellfire Ramparts) + 17377: true, // Keli'dan the Breaker (The Blood Furnace) + 16808: true, // Warchief Kargath Bladefist (The Shattered Halls) + 17942: true, // Quagmirran (The Slave Pens) + 17826: true, // Swamplord Musel'ek (The Underbog) + 17798: true, // Warlord Kalithresh (The Steamvault) + 18344: true, // Nexus-Prince Shaffar (Mana-Tombs) + 18373: true, // Exarch Maladaar (Auchenai Crypts) + 18473: true, // Talon King Ikiss (Sethekk Halls) + 18708: true, // Murmur (Shadow Labyrinth) + 19220: true, // Pathaleon the Calculator (The Mechanar) + 17977: true, // Warp Splinter (The Botanica) + 20912: true, // Harbinger Skyriss (The Arcatraz) + 17881: true, // Aeonus (The Black Morass) + 18096: true, // Epoch Hunter (Old Hillsbrad Foothills) + 24664: true, // Kael'thas Sunstrider (Magisters' Terrace) + + 23954: true, // Ingvar the Plunderer (Utgarde Keep) + 26723: true, // Keristrasza (The Nexus) + 29120: true, // Anub'arak (Azjol-Nerub) + 29311: true, // Herald Volazj (Ahn'kahet: The Old Kingdom) + 26632: true, // The Prophet Tharon'ja (Drak'Tharon Keep) + 31134: true, // Cyanigosa (Violet Hold) + 29306: true, // Gal'darah (Gundrak) + 27978: true, // Sjonnir the Ironshaper (Halls of Stone) + 28923: true, // Loken (Halls of Lightning) + 27656: true, // Ley-Guardian Eregos (The Oculus) + 26533: true, // Mal'Ganis (Culling of Stratholme) + 26861: true, // King Ymiron (Utgarde Pinnacle) + 35451: true, // The Black Knight (Trial of the Champion) + 36502: true, // Devourer of Souls (Forge of Souls) + 36658: true, // Scourgelord Tyrannus (Pit of Saron) + 37226: true, // The Lich King (Halls of Reflection) +} + func (db *MySqlDb) GetBosses(mapId int) ([]Boss, error) { if mapId == 0 { @@ -101,3 +165,7 @@ func (db *MySqlDb) GetBossLoot(bossId int) ([]DbItem, error) { return items, nil } + +func IsFinalBoss(bossId int) bool { + return BossIDs[bossId] +} diff --git a/internal/db/mysql/items.go b/internal/db/mysql/items.go index cfeff82..a59da60 100644 --- a/internal/db/mysql/items.go +++ b/internal/db/mysql/items.go @@ -82,7 +82,8 @@ func (db *MySqlDb) GetItem(entry int) (DbItem, error) { func (db *MySqlDb) GetRarePlusItems(limit, offset int) ([]DbItem, error) { items := []DbItem{} - sql := "SELECT " + GetItemFields("") + " FROM item_template WHERE Quality >= 3 and Quality <= 5 and (class = 2 or class = 4)" + sql := "SELECT " + GetItemFields("") + " FROM item_template WHERE Quality >= 3 and Quality <= 5 and (class = 2 or class = 4) " + sql += "and subclass != 20 AND entry < 20000000 ORDER BY entry ASC" if limit != 0 && offset != 0 { sql += fmt.Sprintf("LIMIT %v OFFSET %v", limit, offset) diff --git a/internal/db/sqlite/items.go b/internal/db/sqlite/items.go index 180437b..b167085 100644 --- a/internal/db/sqlite/items.go +++ b/internal/db/sqlite/items.go @@ -19,6 +19,15 @@ type HighLevelItem struct { StatsList string `db:"stats_list"` } +type DungeonItem struct { + Entry int `db:"entry"` + MapId int `db:"mapId"` + CreatureId int `db:"creatureId"` + Quality int `db:"Quality"` + Expansion int `db:"expansion"` + DungeonLevel int `db:"dungeonLevel"` +} + func (db *SqlLite) GetItem(entry int) (HighLevelItem, error) { if entry == 0 { return HighLevelItem{}, fmt.Errorf("entry cannot be 0") @@ -79,6 +88,18 @@ func (db *SqlLite) GetRandItem(class, subclass int, statsList []int, end bool) ( return rndItem, nil } +func (db *SqlLite) GetItemFromDungeon(itemEntry int) (DungeonItem, error) { + item := DungeonItem{} + sql := "SELECT * FROM dungeon_items WHERE entry = ?" + + err := db.Get(&item, sql, itemEntry) + if err != nil { + return item, err + } + + return item, nil +} + func intSliceToString(slice []int) string { str := fmt.Sprint(slice) str = strings.Trim(str, "[]") diff --git a/internal/items/items.go b/internal/items/items.go index 5df40c7..10dab41 100644 --- a/internal/items/items.go +++ b/internal/items/items.go @@ -8,6 +8,7 @@ import ( "math/rand/v2" "reflect" "slices" + "strings" "github.com/araxiaonline/endgame-item-generator/internal/config" "github.com/araxiaonline/endgame-item-generator/internal/db/mysql" @@ -151,12 +152,12 @@ func (i Item) GetDpsModifier() (float64, error) { typeModifier := 0.0 // Is a One-Handed Weapon if *i.Subclass == 0 || *i.Subclass == 4 || *i.Subclass == 13 || *i.Subclass == 15 || *i.Subclass == 7 { - typeModifier = 0.64 + typeModifier = 0.58 } // Is a Two-Handed Weapon if *i.Subclass == 1 || *i.Subclass == 5 || *i.Subclass == 6 || *i.Subclass == 8 || *i.Subclass == 10 || *i.Subclass == 17 { - typeModifier = 0.80 + typeModifier = 0.85 } // Ranged Weapons @@ -172,15 +173,7 @@ func (i Item) GetDpsModifier() (float64, error) { qualityModifier := 1.0 // Add the quality modifier for the DPS calculation - if *i.Quality == 2 { - qualityModifier = 1.25 - } - if *i.Quality == 3 { - qualityModifier = 1.38 - } - if *i.Quality == 4 { - qualityModifier = 1.5 - } + qualityModifier = config.QualityModifiers[*i.Quality] if typeModifier == 0 { return 0, fmt.Errorf("Item subclass is not a weapon %v", *i.Subclass) @@ -221,17 +214,14 @@ func (item *Item) ScaleDPS(oldLevel, level int) (float64, error) { return 0.0, err } - scalingFactor := math.Pow(float64(level)/float64(oldLevel), 1.1) + scalingFactor := math.Pow(float64(level)/float64(oldLevel), 1.012) dps := modifier * float64(level) * scalingFactor adjDps := (dps * (*item.Delay / 1000) / 100) //(((Y8*Y4)/100))*((100 - Y5)) Forumula from Weapon Item Genertor - minimum := adjDps * float64(100-(rand.IntN(25)+22)) - maximum := adjDps * float64(100+(rand.IntN(25)+28)) - - minimum = math.Ceil(minimum) - maximum = math.Ceil(maximum) + minimum := adjDps * float64(100-(rand.IntN(15)+22)) + maximum := adjDps * float64(100+(rand.IntN(15)+28)) // If the weapon has secondary damage, scale that as well based on the ratio of the primary damage if *item.MinDmg2 != 0 && *item.MaxDmg2 != 0 { @@ -240,6 +230,9 @@ func (item *Item) ScaleDPS(oldLevel, level int) (float64, error) { minimum2 := ratioMin * float64(minimum) maximum2 := ratioMax * float64(maximum) + minimum2 = math.Ceil(minimum2) + maximum2 = math.Ceil(maximum2) + item.MinDmg2 = &minimum2 item.MaxDmg2 = &maximum2 @@ -248,6 +241,9 @@ func (item *Item) ScaleDPS(oldLevel, level int) (float64, error) { maximum = maximum - float64(maximum2)*0.85 } + minimum = math.Ceil(minimum) + maximum = math.Ceil(maximum) + // item.MinDmg1 = &minimum // var min int = int(minimum) // var max int = int(maximum) @@ -444,7 +440,7 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) { *item.ItemLevel = itemLevel // if an item quality is being forced than use it intead - if itemQuality != 0 { + if *item.Quality < itemQuality { *item.Quality = itemQuality } @@ -469,6 +465,7 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) { if len(convStats) != 0 { item.UpdateField(fmt.Sprintf("SpellId%v", i+1), 0) + item.UpdateField(fmt.Sprintf("SpellTrigger%v", i+1), 0) } allSpellStats = append(allSpellStats, convStats...) @@ -859,6 +856,9 @@ func ItemToSql(item Item, reqLevel int, difficulty int) string { spellid_1 = %v, spellid_2 = %v, spellid_3 = %v, + spelltrigger_1 = %v, + spelltrigger_2 = %v, + spelltrigger_3 = %v, socketColor_1 = %v, socketContent_1 = %v, socketColor_2 = %v, @@ -872,11 +872,12 @@ func ItemToSql(item Item, reqLevel int, difficulty int) string { SellPrice = FLOOR(100000 + (RAND() * 400001)), Armor = %v WHERE entry = %v; - `, *item.Quality, name, *item.ItemLevel, reqLevel, *item.MinDmg1, *item.MaxDmg1, *item.MinDmg2, *item.MaxDmg2, *item.StatsCount, + `, *item.Quality, strings.ReplaceAll(name, "'", "''"), *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, *item.SocketColor1, *item.SocketContent1, - *item.SocketColor2, *item.SocketContent2, *item.SocketColor3, *item.SocketContent3, *item.SocketBonus, *item.GemProperties, + *item.StatType9, *item.StatValue9, *item.StatType10, *item.StatValue10, *item.SpellId1, *item.SpellId2, *item.SpellId3, *item.SpellTrigger1, *item.SpellTrigger2, + *item.SpellTrigger3, *item.SocketColor1, *item.SocketContent1, *item.SocketColor2, *item.SocketContent2, + *item.SocketColor3, *item.SocketContent3, *item.SocketBonus, *item.GemProperties, 375, 68, *item.Armor, entryBump+item.Entry) return fmt.Sprintf("%s %s \n %s \n %s", spellList, delete, clone, update) diff --git a/main.go b/main.go index c9fd0b1..fd79619 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "io" "log" "os" + "strings" "github.com/araxiaonline/endgame-item-generator/internal/db/mysql" "github.com/araxiaonline/endgame-item-generator/internal/db/sqlite" @@ -22,7 +23,7 @@ func main() { // 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.") + itemLevel := flag.Int("ilvl", 300, "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") @@ -75,6 +76,11 @@ func main() { for itr, dbItem := range rareItems { item := items.ItemFromDbItem(dbItem) + // if it is a rare item then we need to scale it up to epic + if *item.Quality < 5 { + *item.Quality = 4 + } + statsList, err := item.GetStatList() if err != nil { log.Fatal(err) @@ -84,7 +90,7 @@ func main() { log.Printf("Item: %v Entry: %v StatsList: %v\n", item.Name, item.Entry, statsList) rndItem, err := sqliteDb.GetRandItem(*item.Class, *item.Subclass, statsList, false) if err != nil { - log.Fatal(err) + log.Print(err) continue } @@ -100,131 +106,107 @@ func main() { continue } - // Print all the status for the item that was copied - // log.Printf("Item Name: %v Stat1: %v Stat2: %v Stat3: %v Stat4: %v Stat5: %v Stat6: %v Stat7: %v Stat8: %v \n", - // item.Entry, *item.StatValue1, *item.StatValue2, *item.StatValue3, *item.StatValue4, *item.StatValue5, *item.StatValue6, *item.StatValue7, *item.StatValue8) + // Take the high level item that has been selected for stats and remap to current item - item.ApplyStats(items.ItemFromDbItem(highLevelItem)) + // Now apply logic build out the different difficulties and item levels based on source of item + lookupItem, err := sqliteDb.GetItemFromDungeon(item.Entry) + if err != nil { + if !strings.Contains(err.Error(), "no rows in result set") { + log.Fatalf("failed to lookup item %v from dungeon: %v", item.Entry, err) + } + } - item.ScaleItem(*itemLevel, 3) - // log.Printf("Item Name: %v Stat1: %v Stat2: %v Stat3: %v Stat4: %v Stat5: %v Stat6: %v Stat7: %v Stat8: %v \n", - // item.Name, *item.StatValue1, *item.StatValue2, *item.StatValue3, *item.StatValue4, *item.StatValue5, *item.StatValue6, *item.StatValue7, *item.StatValue8) + // Start at the item level and scale up based details about the source of the item - fmt.Print(items.ItemToSql(item, 80, 3)) + // if the item is from a dungeon and not a boss and is a craftable / world item so set to base level + if lookupItem.Entry == 0 { - if itr > 600 { - break + Scale(highLevelItem, &item, *itemLevel, 3) + fmt.Print(items.ItemToSql(item, *baselevel, *difficulty)) + continue + } + + // if the item is from a dungeon and not a boss item + if lookupItem.CreatureId == 0 { + + if lookupItem.DungeonLevel < 60 { + Scale(highLevelItem, &item, *itemLevel+5, 3) + fmt.Print(items.ItemToSql(item, *baselevel, *difficulty)) + } + + if lookupItem.DungeonLevel == 60 && lookupItem.Expansion == 0 { + Scale(highLevelItem, &item, *itemLevel+10, 3) + fmt.Print(items.ItemToSql(item, *baselevel+2, *difficulty)) + } + + if lookupItem.DungeonLevel < 70 && lookupItem.Expansion == 1 { + Scale(highLevelItem, &item, *itemLevel+7, 3) + fmt.Print(items.ItemToSql(item, *baselevel, *difficulty)) + } + + if lookupItem.DungeonLevel == 70 && lookupItem.Expansion == 1 { + Scale(highLevelItem, &item, *itemLevel+10, 3) + fmt.Print(items.ItemToSql(item, *baselevel+2, *difficulty)) + } + + if lookupItem.DungeonLevel < 80 && lookupItem.Expansion == 2 { + Scale(highLevelItem, &item, *itemLevel+7, 3) + } + + if lookupItem.DungeonLevel == 80 && lookupItem.Expansion == 2 { + Scale(highLevelItem, &item, *itemLevel+10, 3) + fmt.Print(items.ItemToSql(item, *baselevel+2, *difficulty)) + } + } else { + + var finalBonus int = 0 + var quality int = 4 + // check if it is the final boss + if mysql.IsFinalBoss(lookupItem.CreatureId) { + finalBonus = 5 + quality = 5 + } + // if the item is from a boss fight + if lookupItem.DungeonLevel < 60 { + Scale(highLevelItem, &item, *itemLevel+9+finalBonus, quality) + fmt.Print(items.ItemToSql(item, *baselevel, *difficulty)) + } + + if lookupItem.DungeonLevel == 60 && lookupItem.Expansion == 0 { + Scale(highLevelItem, &item, *itemLevel+17+finalBonus, quality) + fmt.Print(items.ItemToSql(item, *baselevel+2, *difficulty)) + } + + if lookupItem.DungeonLevel < 70 && lookupItem.Expansion == 1 { + Scale(highLevelItem, &item, *itemLevel+10+finalBonus, quality) + fmt.Print(items.ItemToSql(item, *baselevel, *difficulty)) + } + + if lookupItem.DungeonLevel == 70 && lookupItem.Expansion == 1 { + Scale(highLevelItem, &item, *itemLevel+19+finalBonus, quality) + fmt.Print(items.ItemToSql(item, *baselevel+2, *difficulty)) + } + + if lookupItem.DungeonLevel < 80 && lookupItem.Expansion == 2 { + Scale(highLevelItem, &item, *itemLevel+12+finalBonus, quality) + } + + if lookupItem.DungeonLevel == 80 && lookupItem.Expansion == 2 { + Scale(highLevelItem, &item, *itemLevel+22+finalBonus, quality) + fmt.Print(items.ItemToSql(item, *baselevel+3, *difficulty)) + } + } + + fmt.Printf("\n -- Item Updated: %v Entry: %v\n", item.Name, item.Entry) + if itr >= 300 { + // os.exit(0) } } - - // // 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 { - - // 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") - // } - - // // 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 += 12 - // endGameDung = true - // } - - // if dungeon.ExpansionId == 2 && dungeon.Level <= 80 { - // scaleValue += 4 - // } - - // 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 - - // if *difficulty == 4 { - // bossQuality = 4 - // boeQuality = 4 - // } else if *difficulty == 5 { - // bossQuality = 5 - // boeQuality = 4 - // } else { - // bossQuality = 4 - // boeQuality = 3 - // } - - // 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 - // } - - // for _, item := range items { - - // _, 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)) - // } - // } - - // } - - // 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 { - - // // 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 - // } - - // fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel) - // fmt.Print(ItemToSql(item, *baselevel, *difficulty)) - - // } - // log.Printf("++++++++++ Additional Count: %v\n", len(items2)) - // } - - // defer models.DB.Close() +} + +func Scale(highLevelItem mysql.DbItem, item *items.Item, itemLevel, quality int) { + item.ApplyStats(items.ItemFromDbItem(highLevelItem)) + item.ScaleItem(itemLevel, quality) + log.Printf("Item Name: %v Stat1: %v Stat2: %v Stat3: %v Stat4: %v Stat5: %v Stat6: %v Stat7: %v Stat8: %v \n", + item.Name, *item.StatValue1, *item.StatValue2, *item.StatValue3, *item.StatValue4, *item.StatValue5, *item.StatValue6, *item.StatValue7, *item.StatValue8) }