Added Maps and updated logging methods

This commit is contained in:
2024-07-07 23:50:34 -04:00
parent 1427a11e85
commit 23ff680a12
4 changed files with 221 additions and 126 deletions

48
main.go
View File

@@ -1,8 +1,11 @@
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"github.com/araxiaonline/endgame-item-generator/models"
_ "github.com/go-sql-driver/mysql"
@@ -15,9 +18,18 @@ func main() {
godotenv.Load()
models.Connect()
debug := flag.Bool("debug", false, "Enable verbose logging inside generator")
flag.Parse()
if *debug {
log.SetOutput(os.Stdout)
} else {
log.SetOutput(io.Discard)
}
bosses, err := models.DB.GetBosses(229)
if err != nil {
log.Fatal(err)
log.Fatal("failed to get bosses")
}
for _, boss := range bosses {
@@ -30,30 +42,7 @@ func main() {
for _, item := range items {
fmt.Printf("\nItem %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel)
// item.GetStatPercents()
// if *item.SpellId1 != 0 {
// spell, err := models.DB.GetSpell(*item.SpellId1)
// if err != nil {
// log.Printf("failed to get the spell: %v error: %v", *item.SpellId1, err)
// }
// log.Printf("Spell %v Spell Effects 1: %v 2: %v, 3: %v \n", spell.Name, spell.Effect1, spell.Effect2, spell.Effect3)
// log.Printf("Spell Aura 1: %v 2: %v, 3: %v \n", spell.EffectAura1, spell.EffectAura2, spell.EffectAura3)
// convStats, err := spell.ConvertToStats()
// if err != nil {
// log.Printf("Failed to convert spell to stats: %v", err)
// }
// scaleItemStats := item.GetStatPercents(convStats)
// for statId, stat := range scaleItemStats {
// log.Printf("StatId: %v Type: %s Value: %v Percent: %v", statId, stat.Type, stat.Value, stat.Percent)
// }
// log.Printf("Scaled Spell Stats: %v\n", convStats)
// }
log.Printf("\nItem %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel)
_, error := item.ScaleItem(320, 3)
fmt.Print(ItemToSql(item, 80, 3))
@@ -65,18 +54,9 @@ func main() {
if err != nil {
log.Fatal(err)
}
// fmt.Println(stat, value)
}
}
// iLevel := 219
// qual := 3
// delay := 2.60
// sub := 0
// myItem := models.Item{Name: "Hypnotic Blade", ItemLevel: &iLevel, Quality: &qual, Delay: &delay, Subclass: &sub}
// dps, err := myItem.ScaleDPS()
// log.Printf("Item %s DPS: %.1f", myItem.Name, dps)
defer models.DB.Close()
}

View File

@@ -71,6 +71,14 @@ type Item struct {
Spells []Spell
}
// Use for storing item stats for all stats that will be scaled.
type ItemStat struct {
Value int
Percent float64
Type string
AdjValue float64
}
var InvTypeModifiers = map[int]float64{
1: 0.813, // Head
2: 1.0, // Neck
@@ -163,13 +171,6 @@ var StatModifiers = map[int]float64{
48: 0.65, // ITEM_MOD_BLOCK_VALUE
}
type ItemStat struct {
Value int
Percent float64
Type string
AdjValue float64
}
func (item Item) GetPrimaryStat() (int, int, error) {
var primaryStat int64
var primaryVal int64
@@ -488,7 +489,7 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) {
log.Printf("DPS: %.1f scaled up from previous dps %v", dps, predps)
}
item.CleanSpells()
item.cleanSpells()
// Item is scaled now we have to determine if there are additional spell effects that need scaled.
// this will be as simple as possible as the effects will just be a percentage of the item stats.
@@ -515,87 +516,6 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) {
}
func (item *Item) emptyStats() {
*item.StatType1 = 0
*item.StatValue1 = 0
*item.StatType2 = 0
*item.StatValue2 = 0
*item.StatType3 = 0
*item.StatValue3 = 0
*item.StatType4 = 0
*item.StatValue4 = 0
*item.StatType5 = 0
*item.StatValue5 = 0
*item.StatType6 = 0
*item.StatValue6 = 0
*item.StatType7 = 0
*item.StatValue7 = 0
*item.StatType8 = 0
*item.StatValue8 = 0
*item.StatType9 = 0
*item.StatValue9 = 0
*item.StatType10 = 0
*item.StatValue10 = 0
}
func (item *Item) addStats(stats map[int]*ItemStat) {
item.emptyStats()
i := 1
// itemValue := reflect.ValueOf(item).Elem() // Get value of underlying struct
for statId, stat := range stats {
if i > 10 {
break
}
statTypeField := fmt.Sprintf("StatType%d", i)
statValueField := fmt.Sprintf("StatValue%d", i)
// Update the item with new stats from scaling
item.UpdateField(statTypeField, statId)
item.UpdateField(statValueField, stat.Value)
// Get the stats for logging purposes
// tmpType, _ := item.GetField(statTypeField)
// tmpStat, _ := item.GetField(statValueField)
// log.Printf("Updated %s to %v, %s to %v", statTypeField, tmpType, statValueField, tmpStat)
i++
}
}
// Cleans up spells from the item that have been converted to stats and leaves only the ones that are not
func (item *Item) CleanSpells() {
spells, err := item.GetSpells()
if err != nil {
log.Printf("Failed to get spells for item: %v", err)
return
}
if len(spells) == 0 {
return
}
for i := 1; i < 4; i++ {
for _, spell := range spells {
currentId, err := item.GetField(fmt.Sprintf("SpellId%v", i))
if err != nil {
log.Printf("ERROR: Failed to get spell id %v err: %v", i, err)
continue
}
if currentId == 0 {
continue
}
if currentId == spell.ID {
item.UpdateField(fmt.Sprintf("SpellId%v", i), 0)
log.Printf("Removed spell %v from spellSlot: %v", spell.Name, fmt.Sprintf("SpellId%v", i))
}
}
}
}
func (item *Item) GetField(fieldName string) (int, error) {
itemValue := reflect.ValueOf(item).Elem()
field := itemValue.FieldByName(fieldName)
@@ -633,6 +553,87 @@ func (item *Item) UpdateField(fieldName string, value int) {
}
}
func (item *Item) emptyStats() {
*item.StatType1 = 0
*item.StatValue1 = 0
*item.StatType2 = 0
*item.StatValue2 = 0
*item.StatType3 = 0
*item.StatValue3 = 0
*item.StatType4 = 0
*item.StatValue4 = 0
*item.StatType5 = 0
*item.StatValue5 = 0
*item.StatType6 = 0
*item.StatValue6 = 0
*item.StatType7 = 0
*item.StatValue7 = 0
*item.StatType8 = 0
*item.StatValue8 = 0
*item.StatType9 = 0
*item.StatValue9 = 0
*item.StatType10 = 0
*item.StatValue10 = 0
}
// Cleans up spells from the item that have been converted to stats and leaves only the ones that are not
func (item *Item) cleanSpells() {
spells, err := item.GetSpells()
if err != nil {
log.Printf("Failed to get spells for item: %v", err)
return
}
if len(spells) == 0 {
return
}
for i := 1; i < 4; i++ {
for _, spell := range spells {
currentId, err := item.GetField(fmt.Sprintf("SpellId%v", i))
if err != nil {
log.Printf("ERROR: Failed to get spell id %v err: %v", i, err)
continue
}
if currentId == 0 {
continue
}
if currentId == spell.ID {
item.UpdateField(fmt.Sprintf("SpellId%v", i), 0)
log.Printf("Removed spell %v from spellSlot: %v", spell.Name, fmt.Sprintf("SpellId%v", i))
}
}
}
}
func (item *Item) addStats(stats map[int]*ItemStat) {
item.emptyStats()
i := 1
// itemValue := reflect.ValueOf(item).Elem() // Get value of underlying struct
for statId, stat := range stats {
if i > 10 {
break
}
statTypeField := fmt.Sprintf("StatType%d", i)
statValueField := fmt.Sprintf("StatValue%d", i)
// Update the item with new stats from scaling
item.UpdateField(statTypeField, statId)
item.UpdateField(statValueField, stat.Value)
// Get the stats for logging purposes
// tmpType, _ := item.GetField(statTypeField)
// tmpStat, _ := item.GetField(statValueField)
// log.Printf("Updated %s to %v, %s to %v", statTypeField, tmpType, statValueField, tmpStat)
i++
}
}
// Scale formula ((ItemLevel * QualityModifier * ItemTypeModifier)^1.7095 * %ofStats) ^ (1/1.7095)) / StatModifier
func scaleStat(itemLevel int, itemType int, itemQuality int, percOfStat float64, statModifier float64) int {
scaledUp := math.Pow((float64(itemLevel)*QualityModifiers[itemQuality]*InvTypeModifiers[itemType]), 1.7095) * percOfStat

94
models/maps.go Normal file
View File

@@ -0,0 +1,94 @@
package models
import "fmt"
type Dungeon struct {
Id int `db:"Id"`
Name string `db:"Name"`
Level int
ExpansionId int `db:"ExpansionId"`
}
var dungeons = map[int]int{
// Classic WoW dungeons
389: 18, // Ragefire Chasm
43: 25, // Wailing Caverns
36: 23, // The Deadmines
33: 30, // Shadowfang Keep
34: 30, // The Stockade
48: 32, // Blackfathom Deeps
90: 38, // Gnomeregan
47: 40, // Razorfen Kraul
189: 45, // Scarlet Monastery (Graveyard)
289: 60, // Scholomance
329: 60, // Stratholme
229: 60, // Blackrock Spire (Lower)
230: 60, // Blackrock Spire (Upper)
429: 60, // Dire Maul
209: 54, // Zul'Farrak
349: 55, // Maraudon
269: 57, // Temple of Atal'Hakkar
// The Burning Crusade dungeons
540: 62, // Hellfire Citadel: Hellfire Ramparts
542: 63, // Hellfire Citadel: The Blood Furnace
547: 64, // Coilfang Reservoir: The Slave Pens
546: 65, // Coilfang Reservoir: The Underbog
557: 66, // Auchindoun: Mana-Tombs
558: 67, // Auchindoun: Auchenai Crypts
560: 68, // Caverns of Time: Old Hillsbrad Foothills
556: 69, // Auchindoun: Sethekk Halls
545: 70, // Coilfang Reservoir: The Steamvault
555: 70, // Auchindoun: Shadow Labyrinth
543: 70, // Hellfire Citadel: The Shattered Halls
553: 70, // Tempest Keep: The Botanica
554: 70, // Tempest Keep: The Mechanar
552: 70, // Tempest Keep: The Arcatraz
585: 70, // Magisters' Terrace
// Wrath of the Lich King dungeons
70: 72, // Utgarde Keep
129: 74, // Azjol-Nerub
619: 75, // Ahn'kahet: The Old Kingdom
576: 73, // The Nexus
600: 76, // Drak'Tharon Keep
608: 77, // The Violet Hold
604: 78, // Gundrak
599: 79, // Halls of Stone
602: 80, // Halls of Lightning
578: 79, // The Oculus
575: 80, // Utgarde Pinnacle
595: 80, // The Culling of Stratholme
650: 80, // Trial of the Champion
632: 80, // The Forge of Souls
658: 80, // Pit of Saron
668: 80, // Halls of Reflection
}
func (db Database) GetDungeons(expansionId int) ([]Dungeon, error) {
dungeons := []Dungeon{}
sql := `
SELECT ID as Id, MapName_Lang_enUS as Name, ExpansionID as ExpansionId
FROM map_dbc
WHERE InstanceType = 1 AND Name NOT LIKE '%unused%';
`
var err error
if expansionId != -1 {
sql = sql + "AND ExpansionID = ?"
err = db.client.Select(&dungeons, sql, expansionId)
} else {
err = db.client.Select(&dungeons, sql)
}
if err != nil {
return nil, fmt.Errorf("failed to get dungeons %v", err)
}
return dungeons, nil
}
func addLevels(dungeons []Dungeon) {
for _, dungeon := range dungeons {
dungeon.Level = 60
}
}

View File

@@ -388,13 +388,14 @@ func parseStatDesc(desc string) int {
return 0
}
// Scales a spell effect
// Scales a spell effect, means creating a new spell with the same effect but scaled to a new item level, then passing
// back the new spellId, In order to be predictable I will use 30000000 for rare, 31000000 for epic, 32000000 for legendary
// An example of this might on hit do $s1 nature damage over $d seconds. We would just scale the $s1 value
// based on the formula below. This assumes that Blizzard has already balanced the spell bonus against the
// stats on the item level and quality. This is a big assumption as the stats are not penalized
// from having the extra damage. This could really create some unique sought after weapons that exploit this.
// modified ratio ((s1 / existing iLevel) * newIlevel) * (0.20 Rare or 0.30 Epic or 0.4 for Legendary).
func (s *Spell) ScaleSpell(itemLevel int, itemQuality int) error {
func (s *Spell) ScaleSpell(itemLevel int, itemQuality int) (int, error) {
qualModifier := map[int]float64{
3: 0.20,
@@ -402,15 +403,26 @@ func (s *Spell) ScaleSpell(itemLevel int, itemQuality int) error {
5: 0.40,
}
idBump := 30000000
if itemQuality == 4 {
idBump = 31000000
}
if itemQuality == 5 {
idBump = 32000000
}
// direct damage types
dd := [...]int{2, 9, 10}
didScale := false
// Causes direct damage
if s.Effect1 != 0 && funk.Contains(dd, s.Effect1) {
s.EffectBasePoints1 = int(float64(s.EffectBasePoints1) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality])
didScale = true
}
if s.Effect2 != 0 && funk.Contains(dd, s.Effect1) {
s.EffectBasePoints2 = int(float64(s.EffectBasePoints2) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality])
didScale = true
}
// Restores a Power / Mana
@@ -418,26 +430,34 @@ func (s *Spell) ScaleSpell(itemLevel int, itemQuality int) error {
// skip anyhing else that is not mana as they are flat values
if strings.Contains(s.Description, "Mana") {
s.EffectBasePoints1 = int(float64(s.EffectBasePoints1) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality] * 0.75)
didScale = true
}
}
// Scales a stat buff
if s.Effect1 != 0 && s.Effect1 == 35 {
s.EffectBasePoints1 = int(float64(s.EffectBasePoints1) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality])
didScale = true
}
if s.Effect1 != 0 && s.Effect2 == 35 {
s.EffectBasePoints2 = int(float64(s.EffectBasePoints2) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality])
didScale = true
}
// Handle special aura effects
if s.EffectAura1 != 0 && s.EffectAura1 == 3 && s.Effect1 == 6 {
s.EffectBasePoints1 = int(float64(s.EffectBasePoints1) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality])
didScale = true
}
// Damage Shield Increase Scale due to HP curve
if s.EffectAura1 != 0 && s.EffectAura1 == 15 && s.Effect1 == 6 {
s.EffectBasePoints1 = int(float64(s.EffectBasePoints1) / float64(s.SpellLevel) * float64(itemLevel) * qualModifier[itemQuality] * 1.50)
didScale = true
}
return nil
if !didScale {
return 0, fmt.Errorf("did not qualify to be scaled in ScaleSpell %v (%v)", s.Name, s.ID)
}
return idBump + s.ID, nil
}