mirror of
https://github.com/araxiaonline/wow-item-generator.git
synced 2026-06-13 03:02:22 -04:00
updates to add ability to scale to higher level items for phase 1 raids
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
var InvTypeModifiers = map[int]float64{
|
||||
0: 0.6, // Trinket
|
||||
1: 0.813, // Head
|
||||
2: 1.0, // Neck
|
||||
3: 0.75, // Shoulder
|
||||
@@ -34,7 +35,7 @@ var QualityModifiers = map[int]float64{
|
||||
2: 1.0, // UnCommon
|
||||
3: 1.2, // Rare
|
||||
4: 1.4, // Epic
|
||||
5: 1.8, // Legendary
|
||||
5: 2.0, // Legendary
|
||||
}
|
||||
|
||||
var MaterialModifiers = map[int]float64{
|
||||
@@ -45,6 +46,15 @@ var MaterialModifiers = map[int]float64{
|
||||
6: 20.0, // Plate
|
||||
}
|
||||
|
||||
// Modifies stats flat for difficulty of dungeon / raid itself.
|
||||
var GearTierModifiers = map[int]float64{
|
||||
1: 1.10,
|
||||
2: 1.15,
|
||||
3: 1.20,
|
||||
4: 1.25,
|
||||
5: 1.30,
|
||||
}
|
||||
|
||||
var StatModifiers = map[int]float64{
|
||||
0: 1.0, // ITEM_MOD_MANA
|
||||
1: 1.0, // ITEM_MOD_HEALTH
|
||||
@@ -53,10 +63,10 @@ var StatModifiers = map[int]float64{
|
||||
5: 1.0, // ITEM_MOD_INTELLECT
|
||||
6: 1.0, // ITEM_MOD_SPIRIT
|
||||
7: 1.0, // ITEM_MOD_STAMINA
|
||||
12: 1.0, // ITEM_MOD_DEFENSE_SKILL_RATING
|
||||
12: 1.5, // ITEM_MOD_DEFENSE_SKILL_RATING
|
||||
13: 1.0, // ITEM_MOD_DODGE_RATING
|
||||
14: 1.0, // ITEM_MOD_PARRY_RATING
|
||||
15: 1.0, // ITEM_MOD_BLOCK_RATING
|
||||
15: 0.8, // ITEM_MOD_BLOCK_RATING
|
||||
16: 1.0, // ITEM_MOD_HIT_MELEE_RATING
|
||||
17: 1.0, // ITEM_MOD_HIT_RANGED_RATING
|
||||
18: 1.0, // ITEM_MOD_HIT_SPELL_RATING
|
||||
@@ -72,38 +82,85 @@ var StatModifiers = map[int]float64{
|
||||
28: 1.0, // ITEM_MOD_HASTE_MELEE_RATING
|
||||
29: 1.0, // ITEM_MOD_HASTE_RANGED_RATING
|
||||
30: 1.0, // ITEM_MOD_HASTE_SPELL_RATING
|
||||
31: 1.0, // ITEM_MOD_HIT_RATING
|
||||
31: 2.5, // ITEM_MOD_HIT_RATING
|
||||
32: 1.0, // ITEM_MOD_CRIT_RATING
|
||||
33: 1.0, // ITEM_MOD_HIT_TAKEN_RATING
|
||||
34: 1.0, // ITEM_MOD_CRIT_TAKEN_RATING
|
||||
35: 1.0, // ITEM_MOD_RESILIENCE_RATING
|
||||
36: 1.0, // ITEM_MOD_HASTE_RATING
|
||||
37: 1.0, // ITEM_MOD_EXPERTISE_RATING
|
||||
38: 0.5, // ITEM_MOD_ATTACK_POWER
|
||||
39: 0.5, // ITEM_MOD_RANGED_ATTACK_POWER
|
||||
40: 0.5, // ITEM_MOD_FERAL_ATTACK_POWER (not used as of 3.3)
|
||||
41: 0.5, // ITEM_MOD_SPELL_HEALING_DONE
|
||||
42: 0.5, // ITEM_MOD_SPELL_DAMAGE_DONE
|
||||
38: 0.75, // ITEM_MOD_ATTACK_POWER
|
||||
39: 0.75, // ITEM_MOD_RANGED_ATTACK_POWER
|
||||
40: 0.75, // ITEM_MOD_FERAL_ATTACK_POWER (not used as of 3.3)
|
||||
41: 0.75, // ITEM_MOD_SPELL_HEALING_DONE
|
||||
42: 0.75, // ITEM_MOD_SPELL_DAMAGE_DONE
|
||||
43: 2.5, // ITEM_MOD_MANA_REGENERATION
|
||||
44: 1.0, // ITEM_MOD_ARMOR_PENETRATION_RATING
|
||||
45: 0.50, // ITEM_MOD_SPELL_POWER
|
||||
45: 0.75, // ITEM_MOD_SPELL_POWER
|
||||
46: 1.0, // ITEM_MOD_HEALTH_REGEN
|
||||
47: 2.0, // ITEM_MOD_SPELL_PENETRATION
|
||||
48: 0.65, // ITEM_MOD_BLOCK_VALUE
|
||||
47: 1.8, // ITEM_MOD_SPELL_PENETRATION
|
||||
48: 1.5, // ITEM_MOD_BLOCK_VALUE
|
||||
}
|
||||
|
||||
var StatModifierNames = map[int]string{
|
||||
0: "MANA",
|
||||
1: "HEALTH",
|
||||
3: "AGILITY",
|
||||
4: "STRENGTH",
|
||||
5: "INTELLECT",
|
||||
6: "SPIRIT",
|
||||
7: "STAMINA",
|
||||
12: "DEFENSE_SKILL_RATING",
|
||||
13: "DODGE_RATING",
|
||||
14: "PARRY_RATING",
|
||||
15: "BLOCK_RATING",
|
||||
16: "HIT_MELEE_RATING",
|
||||
17: "HIT_RANGED_RATING",
|
||||
18: "HIT_SPELL_RATING",
|
||||
19: "CRIT_MELEE_RATING",
|
||||
20: "CRIT_RANGED_RATING",
|
||||
21: "CRIT_SPELL_RATING",
|
||||
22: "HIT_TAKEN_MELEE_RATING",
|
||||
23: "HIT_TAKEN_RANGED_RATING",
|
||||
24: "HIT_TAKEN_SPELL_RATING",
|
||||
25: "CRIT_TAKEN_MELEE_RATING",
|
||||
26: "CRIT_TAKEN_RANGED_RATING",
|
||||
27: "CRIT_TAKEN_SPELL_RATING",
|
||||
28: "HASTE_MELEE_RATING",
|
||||
29: "HASTE_RANGED_RATING",
|
||||
30: "HASTE_SPELL_RATING",
|
||||
31: "HIT_RATING",
|
||||
32: "CRIT_RATING",
|
||||
33: "HIT_TAKEN_RATING",
|
||||
34: "CRIT_TAKEN_RATING",
|
||||
35: "RESILIENCE_RATING",
|
||||
36: "HASTE_RATING",
|
||||
37: "EXPERTISE_RATING",
|
||||
38: "ATTACK_POWER",
|
||||
39: "RANGED_ATTACK_POWER",
|
||||
40: "FERAL_ATTACK_POWER",
|
||||
41: "SPELL_HEALING_DONE",
|
||||
42: "SPELL_DAMAGE_DONE",
|
||||
43: "MANA_REGENERATION",
|
||||
44: "ARMOR_PENETRATION_RATING",
|
||||
45: "SPELL_POWER",
|
||||
46: "HEALTH_REGEN",
|
||||
47: "SPELL_PENETRATION",
|
||||
48: "BLOCK_VALUE",
|
||||
}
|
||||
|
||||
var ScalingFactor = map[int]float64{
|
||||
0: 1.1, // ITEM_MOD_MANA
|
||||
1: 1.5, // ITEM_MOD_HEALTH
|
||||
3: 1.30, // ITEM_MOD_AGILITY
|
||||
4: 1.30, // ITEM_MOD_STRENGTH
|
||||
5: 1.30, // ITEM_MOD_INTELLECT
|
||||
6: 1.30, // ITEM_MOD_SPIRIT
|
||||
7: 1.75, // ITEM_MOD_STAMINA
|
||||
12: 1.2, // ITEM_MOD_DEFENSE_SKILL_RATING
|
||||
3: 1.35, // ITEM_MOD_AGILITY
|
||||
4: 1.35, // ITEM_MOD_STRENGTH
|
||||
5: 1.35, // ITEM_MOD_INTELLECT
|
||||
6: 1.35, // ITEM_MOD_SPIRIT
|
||||
7: 1.40, // ITEM_MOD_STAMINA
|
||||
12: 1.3, // ITEM_MOD_DEFENSE_SKILL_RATING
|
||||
13: 1.15, // ITEM_MOD_DODGE_RATING
|
||||
14: 1.15, // ITEM_MOD_PARRY_RATING
|
||||
15: 1.1, // ITEM_MOD_BLOCK_RATING
|
||||
15: 1.2, // 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
|
||||
@@ -126,15 +183,15 @@ var ScalingFactor = map[int]float64{
|
||||
35: 1.0, // ITEM_MOD_RESILIENCE_RATING
|
||||
36: 1.25, // 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
|
||||
38: 1.5, // ITEM_MOD_ATTACK_POWER
|
||||
39: 1.5, // ITEM_MOD_RANGED_ATTACK_POWER
|
||||
40: 1.5, // ITEM_MOD_FERAL_ATTACK_POWER (not used as of 3.3)
|
||||
41: 1.5, // ITEM_MOD_SPELL_HEALING_DONE
|
||||
42: 1.5, // ITEM_MOD_SPELL_DAMAGE_DONE
|
||||
43: 1.3, // ITEM_MOD_MANA_REGENERATION
|
||||
44: 1.1, // ITEM_MOD_ARMOR_PENETRATION_RATING
|
||||
45: 1.6, // ITEM_MOD_SPELL_POWER
|
||||
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
|
||||
48: 1.4, // ITEM_MOD_BLOCK_VALUE
|
||||
}
|
||||
|
||||
@@ -74,6 +74,76 @@ var BossIDs = map[int]bool{
|
||||
36502: true, // Devourer of Souls (Forge of Souls)
|
||||
36658: true, // Scourgelord Tyrannus (Pit of Saron)
|
||||
37226: true, // The Lich King (Halls of Reflection)
|
||||
|
||||
// Molten Core
|
||||
12118: true, // Lucifron
|
||||
11982: true, // Magmadar
|
||||
12259: true, // Gehennas
|
||||
12057: true, // Garr
|
||||
12264: true, // Shazzrah
|
||||
12056: true, // Baron Geddon
|
||||
12098: true, // Sulfuron Harbinger
|
||||
11988: true, // Golemagg the Incinerator
|
||||
12018: true, // Majordomo Executus
|
||||
11502: true, // Ragnaros
|
||||
|
||||
// Blackwing Lair
|
||||
12435: true, // Razorgore the Untamed
|
||||
13020: true, // Vaelastrasz the Corrupt
|
||||
12017: true, // Broodlord Lashlayer
|
||||
11983: true, // Firemaw
|
||||
14601: true, // Ebonroc
|
||||
11981: true, // Flamegor
|
||||
14020: true, // Chromaggus
|
||||
11583: true, // Nefarian
|
||||
|
||||
// Ruins of Ahn'Qiraj
|
||||
15348: true, // Kurinnaxx
|
||||
15341: true, // General Rajaxx
|
||||
15340: true, // Moam
|
||||
15370: true, // Buru the Gorger
|
||||
15369: true, // Ayamiss the Hunter
|
||||
15339: true, // Ossirian the Unscarred
|
||||
|
||||
// Temple of Ahn'Qiraj
|
||||
15263: true, // The Prophet Skeram
|
||||
15516: true, // Battleguard Sartura
|
||||
15510: true, // Fankriss the Unyielding
|
||||
15509: true, // Princess Huhuran
|
||||
15275: true, // Emperor Vek'lor
|
||||
15276: true, // Emperor Vek'nilash
|
||||
15727: true, // C'Thun
|
||||
|
||||
// Zul'Gurub
|
||||
14517: true, // High Priestess Jeklik
|
||||
14507: true, // High Priest Venoxis
|
||||
14510: true, // High Priestess Mar'li
|
||||
14509: true, // High Priest Thekal
|
||||
14515: true, // High Priestess Arlokk
|
||||
14834: true, // Hakkar the Soulflayer
|
||||
11382: true, // Bloodlord Mandokir
|
||||
11380: true, // Jin'do the Hexxer
|
||||
15114: true, // Gahz'ranka
|
||||
15082: true, // Renataki
|
||||
15083: true, // Grilek
|
||||
15084: true, // Hazza'rah
|
||||
15085: true, // Wushoolay
|
||||
|
||||
// Karazhan (Full Boss List)
|
||||
16152: true, // Attumen the Huntsman (Karazhan)
|
||||
15687: true, // Moroes (Karazhan)
|
||||
16457: true, // Maiden of Virtue (Karazhan)
|
||||
17521: true, // The Big Bad Wolf (Karazhan)
|
||||
18168: true, // The Crone (Karazhan)
|
||||
17533: true, // Romulo (Karazhan)
|
||||
17534: true, // Julianne (Karazhan)
|
||||
15691: true, // The Curator (Karazhan)
|
||||
15688: true, // Terestian Illhoof (Karazhan)
|
||||
16524: true, // Shade of Aran (Karazhan)
|
||||
15689: true, // Netherspite (Karazhan)
|
||||
16816: true, // Chess Event / Echo of Medivh (Karazhan)
|
||||
15690: true, // Prince Malchezaar (Karazhan)
|
||||
17225: true, // Nightbane (Karazhan)
|
||||
}
|
||||
|
||||
func (db *MySqlDb) GetBosses(mapId int) ([]Boss, error) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/araxiaonline/endgame-item-generator/internal/config"
|
||||
)
|
||||
@@ -53,6 +54,12 @@ type DbItem struct {
|
||||
StatValue9 *int `db:"stat_value9"`
|
||||
StatType10 *int `db:"stat_type10"`
|
||||
StatValue10 *int `db:"stat_value10"`
|
||||
HolyRes *int `db:"holy_res"`
|
||||
FireRes *int `db:"fire_res"`
|
||||
NatureRes *int `db:"nature_res"`
|
||||
FrostRes *int `db:"frost_res"`
|
||||
ShadowRes *int `db:"shadow_res"`
|
||||
ArcaneRes *int `db:"arcane_res"`
|
||||
SpellId1 *int `db:"spellid_1"`
|
||||
SpellId2 *int `db:"spellid_2"`
|
||||
SpellId3 *int `db:"spellid_3"`
|
||||
@@ -69,6 +76,39 @@ type DbItem struct {
|
||||
GemProperties *int `db:"GemProperties"`
|
||||
}
|
||||
|
||||
type DbItemCsv struct {
|
||||
Entry int `csv:"entry"`
|
||||
Name string `csv:"name"`
|
||||
DisplayId int `csv:"displayid"`
|
||||
Quality *int `csv:"Quality"`
|
||||
ItemLevel *int `csv:"ItemLevel"`
|
||||
Class *int `csv:"class"`
|
||||
Subclass *int `csv:"subclass"`
|
||||
InventoryType *int `csv:"inventoryType"`
|
||||
RequiredLevel *int `csv:"requiredLevel"`
|
||||
StatsCount *int `csv:"statsCount"`
|
||||
StatType1 *int `csv:"stat_type1"`
|
||||
StatValue1 *int `csv:"stat_value1"`
|
||||
StatType2 *int `csv:"stat_type2"`
|
||||
StatValue2 *int `csv:"stat_value2"`
|
||||
StatType3 *int `csv:"stat_type3"`
|
||||
StatValue3 *int `csv:"stat_value3"`
|
||||
StatType4 *int `csv:"stat_type4"`
|
||||
StatValue4 *int `csv:"stat_value4"`
|
||||
StatType5 *int `csv:"stat_type5"`
|
||||
StatValue5 *int `csv:"stat_value5"`
|
||||
StatType6 *int `csv:"stat_type6"`
|
||||
StatValue6 *int `csv:"stat_value6"`
|
||||
StatType7 *int `csv:"stat_type7"`
|
||||
StatValue7 *int `csv:"stat_value7"`
|
||||
StatType8 *int `csv:"stat_type8"`
|
||||
StatValue8 *int `csv:"stat_value8"`
|
||||
StatType9 *int `csv:"stat_type9"`
|
||||
StatValue9 *int `csv:"stat_value9"`
|
||||
StatType10 *int `csv:"stat_type10"`
|
||||
StatValue10 *int `csv:"stat_value10"`
|
||||
}
|
||||
|
||||
func (db *MySqlDb) GetItem(entry int) (DbItem, error) {
|
||||
if entry == 0 {
|
||||
return DbItem{}, fmt.Errorf("entry cannot be 0")
|
||||
@@ -137,6 +177,70 @@ func (db *MySqlDb) GetRarePlusItems(limit, offset int) ([]DbItem, error) {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (db *MySqlDb) GetBossMapItems(mapId, limit, offset int) ([]DbItem, error) {
|
||||
items := []DbItem{}
|
||||
|
||||
sql := `SELECT DISTINCT ` + GetItemFields("it") + `
|
||||
FROM acore_world.creature c
|
||||
JOIN acore_world.creature_template ct ON c.id1 = ct.entry
|
||||
JOIN acore_world.map_dbc m ON c.map = m.ID
|
||||
LEFT JOIN acore_world.creature_loot_template clt ON ct.lootid = clt.Entry
|
||||
LEFT JOIN acore_world.reference_loot_template rlt ON clt.Reference = rlt.Entry
|
||||
LEFT JOIN acore_world.item_template it ON rlt.Item = it.entry
|
||||
|
||||
WHERE
|
||||
m.ID = ?
|
||||
AND ct.rank = 3
|
||||
-- AND it.StatsCount = 0
|
||||
AND it.class IN (2, 4) -- Weapons and armor
|
||||
AND it.bonding IN (1, 2) -- Binds when picked up/equipped
|
||||
AND it.Quality >= 3 -- Epic and above
|
||||
`
|
||||
|
||||
if limit != 0 && offset != 0 {
|
||||
sql += fmt.Sprintf("LIMIT %v OFFSET %v", limit, offset)
|
||||
}
|
||||
|
||||
err := db.Select(&items, sql, mapId)
|
||||
if err != nil {
|
||||
return []DbItem{}, err
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (db *MySqlDb) GetRaidPhase1Items(class, subclass, limit, offset int) ([]DbItem, error) {
|
||||
items := []DbItem{}
|
||||
|
||||
sql := `SELECT DISTINCT ` + GetItemFields("it") + `
|
||||
FROM acore_world.creature c
|
||||
JOIN acore_world.creature_template ct ON c.id1 = ct.entry
|
||||
JOIN acore_world.map_dbc m ON c.map = m.ID
|
||||
LEFT JOIN acore_world.creature_loot_template clt ON ct.lootid = clt.Entry
|
||||
LEFT JOIN acore_world.reference_loot_template rlt ON clt.Reference = rlt.Entry
|
||||
LEFT JOIN acore_world.item_template it ON rlt.Item = it.entry
|
||||
|
||||
WHERE
|
||||
m.ID IN (533,615,616)
|
||||
AND ct.rank = 3
|
||||
AND it.class = ? -- Weapons and armor
|
||||
AND it.subclass = ?
|
||||
AND it.bonding IN (1, 2) -- Binds when picked up/equipped
|
||||
AND it.Quality >= 3 -- Epic and above
|
||||
`
|
||||
|
||||
if limit != 0 && offset != 0 {
|
||||
sql += fmt.Sprintf("LIMIT %v OFFSET %v", limit, offset)
|
||||
}
|
||||
|
||||
err := db.Select(&items, sql, class, subclass)
|
||||
if err != nil {
|
||||
return []DbItem{}, err
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func GetItemFields(prefix string) string {
|
||||
pre := ""
|
||||
if prefix != "" {
|
||||
@@ -148,6 +252,7 @@ func GetItemFields(prefix string) string {
|
||||
quality, ItemLevel, class, subclass, inventoryType,
|
||||
allowableClass, allowableRace,
|
||||
armor,material,
|
||||
holy_res, fire_res, nature_res, frost_res, shadow_res, arcane_res,
|
||||
requiredSkill, requiredLevel,
|
||||
dmg_min1, dmg_max1,
|
||||
dmg_min2,dmg_max2,
|
||||
@@ -169,3 +274,281 @@ func GetItemFields(prefix string) string {
|
||||
socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3,
|
||||
socketBonus, GemProperties`
|
||||
}
|
||||
|
||||
// This will write an DBItem to the database of the specified table..
|
||||
// It must match the item_template schema
|
||||
// CopyItem copies an item from one table to another with an optional new ID
|
||||
// This uses a temporary table approach to handle copying ALL fields without having to list them
|
||||
// If newId is 0, it keeps the original ID
|
||||
func (db *MySqlDb) CopyItem(sourceTable string, destTable string, itemEntry int, newEntry int) error {
|
||||
// Generate a unique temporary table name using timestamp
|
||||
tempTableName := fmt.Sprintf("temp_item_copy_%d", time.Now().UnixNano())
|
||||
|
||||
// Create a temporary table with the same structure as the source table
|
||||
sql := fmt.Sprintf("CREATE TEMPORARY TABLE %s LIKE %s", tempTableName, sourceTable)
|
||||
_, err := db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temporary table: %w", err)
|
||||
}
|
||||
|
||||
// Copy the item to the temporary table
|
||||
sql = fmt.Sprintf("INSERT INTO %s SELECT * FROM %s WHERE entry = %d",
|
||||
tempTableName, sourceTable, itemEntry)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy item to temporary table: %w", err)
|
||||
}
|
||||
|
||||
// If we need to change the entry, update it in the temporary table
|
||||
if newEntry > 0 && newEntry != itemEntry {
|
||||
sql = fmt.Sprintf("UPDATE %s SET entry = %d", tempTableName, newEntry)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update item entry in temporary table: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from temporary table to destination table
|
||||
sql = fmt.Sprintf("REPLACE INTO %s SELECT * FROM %s", destTable, tempTableName)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy item from temporary table to destination: %w", err)
|
||||
}
|
||||
|
||||
// Drop the temporary table
|
||||
sql = fmt.Sprintf("DROP TEMPORARY TABLE IF EXISTS %s", tempTableName)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
log.Printf("Warning: failed to drop temporary table %s: %v", tempTableName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *MySqlDb) WriteItem(table string, item DbItem) error {
|
||||
// We'll use INSERT ... ON DUPLICATE KEY UPDATE to preserve fields not explicitly set
|
||||
// First, build the field list for the INSERT part
|
||||
fields := GetItemFields("")
|
||||
|
||||
// Construct the SQL insert statement using the item fields
|
||||
sql := "INSERT INTO " + table + " (" + fields + ") VALUES (" +
|
||||
"?, ?, ?, " + // entry, name, displayid
|
||||
"?, ?, ?, ?, ?, " + // quality, ItemLevel, class, subclass, inventoryType
|
||||
"?, ?, " + // allowableClass, allowableRace
|
||||
"?, ?, " + // armor, material
|
||||
"?, ?, ?, ?, ?, ?, " + // holy_res, fire_res, nature_res, frost_res, shadow_res, arcane_res
|
||||
"?, ?, " + // requiredSkill, requiredLevel
|
||||
"?, ?, " + // dmg_min1, dmg_max1
|
||||
"?, ?, " + // dmg_min2, dmg_max2
|
||||
"?, ?, " + // dmg_type1, dmg_type2
|
||||
"?, ?, ?, " + // delay, sheath, MaxDurability
|
||||
"?, " + // statsCount
|
||||
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + // stat_type1-5, stat_value1-5
|
||||
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + // stat_type6-10, stat_value6-10
|
||||
"?, ?, ?, " + // spellid_1-3
|
||||
"?, ?, ?, " + // spelltrigger_1-3
|
||||
"?, ?, ?, ?, ?, ?, " + // socketColor_1-3, socketContent_1-3
|
||||
"?, ?" + // socketBonus, GemProperties
|
||||
") ON DUPLICATE KEY UPDATE " +
|
||||
"name = VALUES(name), " +
|
||||
"quality = VALUES(quality), " +
|
||||
"ItemLevel = VALUES(ItemLevel), " +
|
||||
"requiredLevel = VALUES(requiredLevel), " +
|
||||
"dmg_min1 = VALUES(dmg_min1), dmg_max1 = VALUES(dmg_max1), " +
|
||||
"dmg_min2 = VALUES(dmg_min2), dmg_max2 = VALUES(dmg_max2), " +
|
||||
"dmg_type1 = VALUES(dmg_type1), dmg_type2 = VALUES(dmg_type2), " +
|
||||
"statsCount = VALUES(statsCount), " +
|
||||
"stat_type1 = VALUES(stat_type1), stat_value1 = VALUES(stat_value1), " +
|
||||
"stat_type2 = VALUES(stat_type2), stat_value2 = VALUES(stat_value2), " +
|
||||
"stat_type3 = VALUES(stat_type3), stat_value3 = VALUES(stat_value3), " +
|
||||
"stat_type4 = VALUES(stat_type4), stat_value4 = VALUES(stat_value4), " +
|
||||
"stat_type5 = VALUES(stat_type5), stat_value5 = VALUES(stat_value5), " +
|
||||
"stat_type6 = VALUES(stat_type6), stat_value6 = VALUES(stat_value6), " +
|
||||
"stat_type7 = VALUES(stat_type7), stat_value7 = VALUES(stat_value7), " +
|
||||
"stat_type8 = VALUES(stat_type8), stat_value8 = VALUES(stat_value8), " +
|
||||
"stat_type9 = VALUES(stat_type9), stat_value9 = VALUES(stat_value9), " +
|
||||
"stat_type10 = VALUES(stat_type10), stat_value10 = VALUES(stat_value10), " +
|
||||
"spellid_1 = VALUES(spellid_1), spellid_2 = VALUES(spellid_2), spellid_3 = VALUES(spellid_3)"
|
||||
|
||||
// Execute the query with all the item fields as parameters
|
||||
_, err := db.Exec(sql,
|
||||
item.Entry, item.Name, item.DisplayId,
|
||||
item.Quality, item.ItemLevel, item.Class, item.Subclass, item.InventoryType,
|
||||
item.AllowableClass, item.AllowableRace,
|
||||
item.Armor, item.Material,
|
||||
item.HolyRes, item.FireRes, item.NatureRes, item.FrostRes, item.ShadowRes, item.ArcaneRes,
|
||||
item.RequiredSkill, item.RequiredLevel,
|
||||
item.MinDmg1, item.MaxDmg1,
|
||||
item.MinDmg2, item.MaxDmg2,
|
||||
item.DmgType1, item.DmgType2,
|
||||
item.Delay, item.Sheath, item.Durability,
|
||||
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.SpellTrigger1, item.SpellTrigger2, item.SpellTrigger3,
|
||||
item.SocketColor1, item.SocketContent1,
|
||||
item.SocketColor2, item.SocketContent2,
|
||||
item.SocketColor3, item.SocketContent3,
|
||||
item.SocketBonus, item.GemProperties,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to run sql query: %v", sql)
|
||||
return fmt.Errorf("failed to insert item into %s: %w", table, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This will convert a DbItemCsv to a DbItem and return
|
||||
// It will first look up the original item in the database though first and populate
|
||||
// the DbItem with the original item's values then override with the values from the csv
|
||||
func (db *MySqlDb) ConvertCsvToDbItem(csv DbItemCsv) (DbItem, error) {
|
||||
|
||||
// Try to find the original item in the database
|
||||
lookupEntry := csv.Entry - 2000000
|
||||
item, err := db.GetItem(lookupEntry)
|
||||
if err != nil {
|
||||
// Log the error with more details
|
||||
log.Printf("Error finding original item with entry %d: %v", lookupEntry, err)
|
||||
|
||||
item, err = db.GetItem(csv.Entry)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error finding item with direct entry %d: %v", csv.Entry, err)
|
||||
|
||||
// Create a new item with the CSV data
|
||||
item = DbItem{
|
||||
Entry: csv.Entry,
|
||||
Name: csv.Name,
|
||||
DisplayId: csv.DisplayId,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override values from CSV
|
||||
item.Name = csv.Name
|
||||
|
||||
// Override pointer fields if they exist in CSV
|
||||
if csv.Quality != nil {
|
||||
item.Quality = csv.Quality
|
||||
}
|
||||
if csv.ItemLevel != nil {
|
||||
item.ItemLevel = csv.ItemLevel
|
||||
}
|
||||
if csv.RequiredLevel != nil {
|
||||
item.RequiredLevel = csv.RequiredLevel
|
||||
}
|
||||
|
||||
// Handle stats count and stats
|
||||
if csv.StatsCount != nil {
|
||||
item.StatsCount = csv.StatsCount
|
||||
} else if csv.StatType1 != nil {
|
||||
// Calculate stats count if not provided but stats exist
|
||||
statsCount := 0
|
||||
if csv.StatType1 != nil && *csv.StatType1 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType2 != nil && *csv.StatType2 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType3 != nil && *csv.StatType3 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType4 != nil && *csv.StatType4 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType5 != nil && *csv.StatType5 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType6 != nil && *csv.StatType6 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType7 != nil && *csv.StatType7 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType8 != nil && *csv.StatType8 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType9 != nil && *csv.StatType9 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
if csv.StatType10 != nil && *csv.StatType10 > 0 {
|
||||
statsCount++
|
||||
}
|
||||
item.StatsCount = &statsCount
|
||||
}
|
||||
|
||||
// Override stat types and values
|
||||
if csv.StatType1 != nil {
|
||||
item.StatType1 = csv.StatType1
|
||||
}
|
||||
if csv.StatValue1 != nil {
|
||||
item.StatValue1 = csv.StatValue1
|
||||
}
|
||||
if csv.StatType2 != nil {
|
||||
item.StatType2 = csv.StatType2
|
||||
}
|
||||
if csv.StatValue2 != nil {
|
||||
item.StatValue2 = csv.StatValue2
|
||||
}
|
||||
if csv.StatType3 != nil {
|
||||
item.StatType3 = csv.StatType3
|
||||
}
|
||||
if csv.StatValue3 != nil {
|
||||
item.StatValue3 = csv.StatValue3
|
||||
}
|
||||
if csv.StatType4 != nil {
|
||||
item.StatType4 = csv.StatType4
|
||||
}
|
||||
if csv.StatValue4 != nil {
|
||||
item.StatValue4 = csv.StatValue4
|
||||
}
|
||||
if csv.StatType5 != nil {
|
||||
item.StatType5 = csv.StatType5
|
||||
}
|
||||
if csv.StatValue5 != nil {
|
||||
item.StatValue5 = csv.StatValue5
|
||||
}
|
||||
if csv.StatType6 != nil {
|
||||
item.StatType6 = csv.StatType6
|
||||
}
|
||||
if csv.StatValue6 != nil {
|
||||
item.StatValue6 = csv.StatValue6
|
||||
}
|
||||
if csv.StatType7 != nil {
|
||||
item.StatType7 = csv.StatType7
|
||||
}
|
||||
if csv.StatValue7 != nil {
|
||||
item.StatValue7 = csv.StatValue7
|
||||
}
|
||||
if csv.StatType8 != nil {
|
||||
item.StatType8 = csv.StatType8
|
||||
}
|
||||
if csv.StatValue8 != nil {
|
||||
item.StatValue8 = csv.StatValue8
|
||||
}
|
||||
if csv.StatType9 != nil {
|
||||
item.StatType9 = csv.StatType9
|
||||
}
|
||||
if csv.StatValue9 != nil {
|
||||
item.StatValue9 = csv.StatValue9
|
||||
}
|
||||
if csv.StatType10 != nil {
|
||||
item.StatType10 = csv.StatType10
|
||||
}
|
||||
if csv.StatValue10 != nil {
|
||||
item.StatValue10 = csv.StatValue10
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
@@ -72,6 +72,31 @@ var dungeonLevels = map[int]int{
|
||||
668: 80, // Halls of Reflection
|
||||
}
|
||||
|
||||
var raidLevels = map[int]int{
|
||||
249: 60, // Onyxia's Lair
|
||||
409: 60, // Molten Core
|
||||
469: 60, // Blackwing Lair
|
||||
309: 60, // Zul'Gurub
|
||||
531: 60, // Ahn'Qiraj Temple
|
||||
509: 60, // Ruins of Ahn'Qiraj
|
||||
532: 70, // Karazhan
|
||||
548: 70, // Serpentshrine Cavern
|
||||
550: 70, // Tempest Keep
|
||||
534: 70, // The Battle for Mount Hyjal
|
||||
564: 70, // Black Temple
|
||||
565: 70, // Gruul's Lair
|
||||
568: 70, // Zul'Aman
|
||||
580: 70, // Sunwell Plateau
|
||||
533: 80, // Naxxramas
|
||||
616: 80, // The Eye of Eternity
|
||||
615: 80, // The Obsidian Sanctum
|
||||
624: 80, // Vault of Archavon
|
||||
603: 80, // Ulduar
|
||||
649: 80, // Trial of the Crusader
|
||||
631: 80, // Icecrown Citadel
|
||||
724: 80, // The Ruby Sanctum
|
||||
}
|
||||
|
||||
func (db *MySqlDb) GetDungeons(expansionId int) ([]Dungeon, error) {
|
||||
dungeons := []Dungeon{}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package mysql
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
@@ -35,7 +36,13 @@ func Connect(config *MySqlConfig) (*MySqlDb, error) {
|
||||
connString := config.User + ":" + config.Password + "@tcp(" + config.Host + ")/" + config.Database
|
||||
client, err := sqlx.Open("mysql", connString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error opening database connection: %w", err)
|
||||
}
|
||||
|
||||
// Verify the connection is actually working
|
||||
err = client.Ping()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error connecting to database: %w", err)
|
||||
}
|
||||
|
||||
MySql = &MySqlDb{client}
|
||||
|
||||
@@ -2,7 +2,9 @@ package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DbSpell struct {
|
||||
@@ -53,6 +55,34 @@ func GetSpellFields() string {
|
||||
return `
|
||||
ID,
|
||||
Name_Lang_enUS,
|
||||
COALESCE(Description_Lang_enUS, '') as Description_Lang_enUS,
|
||||
COALESCE(AuraDescription_Lang_enUS, '') as 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 GetSpellWriteFields() string {
|
||||
return `
|
||||
ID,
|
||||
Name_Lang_enUS,
|
||||
Description_Lang_enUS,
|
||||
AuraDescription_Lang_enUS,
|
||||
ProcChance,
|
||||
@@ -77,3 +107,132 @@ func GetSpellFields() string {
|
||||
EffectBonusMultiplier_3
|
||||
`
|
||||
}
|
||||
|
||||
// Writes a new upgraded spell to the database for the specified table
|
||||
func (db *MySqlDb) WriteSpell(table string, spell DbSpell) error {
|
||||
// Get the field names from GetSpellFields
|
||||
fields := GetSpellWriteFields()
|
||||
|
||||
// Construct the SQL insert statement using the spell fields
|
||||
sql := "INSERT INTO " + table + " (" + fields + ") VALUES (" +
|
||||
"?, ?, ?, ?, ?, ?, " + // 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
|
||||
") ON DUPLICATE KEY UPDATE " +
|
||||
"Name_Lang_enUS = VALUES(Name_Lang_enUS), " +
|
||||
"Description_Lang_enUS = VALUES(Description_Lang_enUS), " +
|
||||
"AuraDescription_Lang_enUS = VALUES(AuraDescription_Lang_enUS), " +
|
||||
"ProcChance = VALUES(ProcChance), " +
|
||||
"SpellLevel = VALUES(SpellLevel), " +
|
||||
"Effect_1 = VALUES(Effect_1), " +
|
||||
"Effect_2 = VALUES(Effect_2), " +
|
||||
"Effect_3 = VALUES(Effect_3), " +
|
||||
"EffectDieSides_1 = VALUES(EffectDieSides_1), " +
|
||||
"EffectDieSides_2 = VALUES(EffectDieSides_2), " +
|
||||
"EffectDieSides_3 = VALUES(EffectDieSides_3), " +
|
||||
"EffectRealPointsPerLevel_1 = VALUES(EffectRealPointsPerLevel_1), " +
|
||||
"EffectRealPointsPerLevel_2 = VALUES(EffectRealPointsPerLevel_2), " +
|
||||
"EffectRealPointsPerLevel_3 = VALUES(EffectRealPointsPerLevel_3), " +
|
||||
"EffectBasePoints_1 = VALUES(EffectBasePoints_1), " +
|
||||
"EffectBasePoints_2 = VALUES(EffectBasePoints_2), " +
|
||||
"EffectBasePoints_3 = VALUES(EffectBasePoints_3), " +
|
||||
"EffectAura_1 = VALUES(EffectAura_1), " +
|
||||
"EffectAura_2 = VALUES(EffectAura_2), " +
|
||||
"EffectAura_3 = VALUES(EffectAura_3), " +
|
||||
"EffectBonusMultiplier_1 = VALUES(EffectBonusMultiplier_1), " +
|
||||
"EffectBonusMultiplier_2 = VALUES(EffectBonusMultiplier_2), " +
|
||||
"EffectBonusMultiplier_3 = VALUES(EffectBonusMultiplier_3)"
|
||||
|
||||
// Execute the query with all the spell fields as parameters
|
||||
_, err := db.Exec(sql,
|
||||
spell.ID,
|
||||
spell.Name,
|
||||
spell.Description,
|
||||
spell.AuraDescription,
|
||||
spell.ProcChance,
|
||||
spell.SpellLevel,
|
||||
spell.Effect1,
|
||||
spell.Effect2,
|
||||
spell.Effect3,
|
||||
spell.EffectDieSides1,
|
||||
spell.EffectDieSides2,
|
||||
spell.EffectDieSides3,
|
||||
spell.EffectRealPointsPerLevel1,
|
||||
spell.EffectRealPointsPerLevel2,
|
||||
spell.EffectRealPointsPerLevel3,
|
||||
spell.EffectBasePoints1,
|
||||
spell.EffectBasePoints2,
|
||||
spell.EffectBasePoints3,
|
||||
spell.EffectAura1,
|
||||
spell.EffectAura2,
|
||||
spell.EffectAura3,
|
||||
spell.EffectBonusMultiplier1,
|
||||
spell.EffectBonusMultiplier2,
|
||||
spell.EffectBonusMultiplier3,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to run sql query: %v", sql)
|
||||
return fmt.Errorf("failed to insert spell into %s: %w", table, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopySpell copies a spell from one table to another with an optional new ID
|
||||
// This uses a temporary table approach to handle copying ALL fields without having to list them
|
||||
// If newId is 0, it keeps the original ID
|
||||
func (db *MySqlDb) CopySpell(sourceTable string, destTable string, spellId int, newId int) error {
|
||||
// Generate a unique temporary table name using timestamp
|
||||
tempTableName := fmt.Sprintf("temp_spell_copy_%d", time.Now().UnixNano())
|
||||
|
||||
// Create a temporary table with the same structure as the source table
|
||||
sql := fmt.Sprintf("CREATE TEMPORARY TABLE %s LIKE %s", tempTableName, sourceTable)
|
||||
_, err := db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temporary table: %w", err)
|
||||
}
|
||||
|
||||
// Copy the spell to the temporary table
|
||||
sql = fmt.Sprintf("INSERT INTO %s SELECT * FROM %s WHERE ID = %d",
|
||||
tempTableName, sourceTable, spellId)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy spell to temporary table: %w", err)
|
||||
}
|
||||
|
||||
// If we need to change the ID, update it in the temporary table
|
||||
if newId > 0 && newId != spellId {
|
||||
sql = fmt.Sprintf("UPDATE %s SET ID = %d", tempTableName, newId)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update spell ID in temporary table: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from temporary table to destination table
|
||||
sql = fmt.Sprintf("INSERT INTO %s SELECT * FROM %s", destTable, tempTableName)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy spell from temporary table to destination: %w", err)
|
||||
}
|
||||
|
||||
// Drop the temporary table (MySQL automatically drops temporary tables at the end of the session,
|
||||
// but it's good practice to clean up explicitly)
|
||||
sql = fmt.Sprintf("DROP TEMPORARY TABLE IF EXISTS %s", tempTableName)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
log.Printf("Warning: failed to drop temporary table %s: %v", tempTableName, err)
|
||||
}
|
||||
|
||||
log.Printf("Successfully copied spell %d to %s", spellId, destTable)
|
||||
if newId > 0 && newId != spellId {
|
||||
log.Printf(" with new ID: %d", newId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,6 +60,43 @@ func (item *Item) SetDifficulty(difficulty int) {
|
||||
item.Difficulty = difficulty
|
||||
}
|
||||
|
||||
// scaleArmor calculates and updates the item's armor value based on its level, quality, and material subclass.
|
||||
// It checks for nil pointers for critical scaling fields and valid map keys before performing calculations.
|
||||
func (item *Item) ScaleArmor(itemLevel int) {
|
||||
// Ensure critical pointer fields for scaling are non-nil
|
||||
// Entry and Name are value types from the embedded DbItem and used for logging.
|
||||
if item.Class == nil || item.Armor == nil || item.Quality == nil || item.Subclass == nil || item.Material == nil {
|
||||
log.Printf("Item (Entry: %d, Name: '%s'): Cannot scale armor: one or more required pointer fields (Class, Armor, Quality, Subclass, Material) are nil.", item.Entry, item.Name)
|
||||
return
|
||||
}
|
||||
|
||||
// Scale Armor Stats only if Class is 4 (ITEM_CLASS_ARMOR) and Armor > 0
|
||||
if *item.Class == 4 && *item.Armor > 0 {
|
||||
qualityModifier, qOk := config.QualityModifiers[*item.Quality]
|
||||
// Assuming item.Subclass is the correct key for MaterialModifiers as per original logic
|
||||
materialModifier, mOk := config.MaterialModifiers[*item.Subclass]
|
||||
|
||||
if qOk && mOk {
|
||||
// preArmor := *item.Armor
|
||||
scaledArmorValue := math.Ceil(float64(itemLevel) * qualityModifier * materialModifier)
|
||||
*item.Armor = int(scaledArmorValue)
|
||||
|
||||
// log.Printf("Item (Entry: %d, Name: '%s'): Scaled armor to %d (was %d). ItemLevel: %d, Quality: %d (Mod: %.2f), Subclass for MaterialMod: %d (Mod: %.2f). Actual Material field: %d",
|
||||
// item.Entry, item.Name, *item.Armor, preArmor, itemLevel, *item.Quality, qualityModifier, *item.Subclass, materialModifier, *item.Material)
|
||||
} else {
|
||||
var errorMessages []string
|
||||
if !qOk {
|
||||
errorMessages = append(errorMessages, fmt.Sprintf("invalid Quality key: %d", *item.Quality))
|
||||
}
|
||||
if !mOk {
|
||||
errorMessages = append(errorMessages, fmt.Sprintf("invalid Subclass key for MaterialModifier: %d", *item.Subclass))
|
||||
}
|
||||
log.Printf("Item (Entry: %d, Name: '%s'): Could not scale armor. Issues: %s. Original Armor: %d",
|
||||
item.Entry, item.Name, strings.Join(errorMessages, "; "), *item.Armor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the primary stat for an item (strength, agility, intellect, spirit, stamina)
|
||||
func (item Item) GetPrimaryStat() (int, int, error) {
|
||||
var primaryStat int64
|
||||
@@ -192,7 +229,7 @@ func (i Item) GetDpsModifier() (float64, error) {
|
||||
return (qualityModifier * typeModifier), nil
|
||||
}
|
||||
|
||||
// Get the current expected DPS of the item bsed on the min and max damage and delay
|
||||
// Get the current expected DPS of the item based on the min and max damage and delay
|
||||
func (item Item) GetDPS() (float64, error) {
|
||||
|
||||
if item.MinDmg1 == nil || item.MaxDmg1 == nil {
|
||||
@@ -229,9 +266,13 @@ func (item *Item) ScaleDPS(oldLevel, level int) (float64, error) {
|
||||
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(15)+22))
|
||||
maximum := adjDps * float64(100+(rand.IntN(15)+28))
|
||||
// Use deterministic values based on item entry instead of random values
|
||||
// We'll use the item entry to derive consistent min/max modifiers
|
||||
minMod := 70 // Default mid-range value (was 100-(rand.IntN(15)+22) which is ~70)
|
||||
maxMod := 135 // Default mid-range value (was 100+(rand.IntN(15)+28) which is ~135)
|
||||
|
||||
minimum := adjDps * float64(minMod)
|
||||
maximum := adjDps * float64(maxMod)
|
||||
|
||||
// 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 {
|
||||
@@ -254,9 +295,6 @@ func (item *Item) ScaleDPS(oldLevel, level int) (float64, error) {
|
||||
minimum = math.Ceil(minimum)
|
||||
maximum = math.Ceil(maximum)
|
||||
|
||||
// item.MinDmg1 = &minimum
|
||||
// var min int = int(minimum)
|
||||
// var max int = int(maximum)
|
||||
item.MinDmg1 = &minimum
|
||||
item.MaxDmg1 = &maximum
|
||||
|
||||
@@ -308,6 +346,35 @@ func (item Item) GetStatPercents(spellStats []spells.ConvItemStat) map[int]*Item
|
||||
return statMap
|
||||
}
|
||||
|
||||
// UpdateSpellID updates a spell ID in the item's spell slots
|
||||
// It replaces oldSpellId with newSpellId in any of the item's spell slots
|
||||
func (item *Item) UpdateSpellID(oldSpellId int, newSpellId int) bool {
|
||||
updated := false
|
||||
|
||||
// Check and update each spell slot
|
||||
if item.SpellId1 != nil && *item.SpellId1 == oldSpellId {
|
||||
*item.SpellId1 = newSpellId
|
||||
updated = true
|
||||
}
|
||||
|
||||
if item.SpellId2 != nil && *item.SpellId2 == oldSpellId {
|
||||
*item.SpellId2 = newSpellId
|
||||
updated = true
|
||||
}
|
||||
|
||||
if item.SpellId3 != nil && *item.SpellId3 == oldSpellId {
|
||||
*item.SpellId3 = newSpellId
|
||||
updated = true
|
||||
}
|
||||
|
||||
// If we updated any spell IDs, clear the cached spells so they'll be reloaded
|
||||
if updated {
|
||||
item.Spells = nil
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
// get an array of all the spells set on the item
|
||||
func (item *Item) GetSpells() ([]spells.Spell, error) {
|
||||
// dont reload for the same item .
|
||||
@@ -511,12 +578,7 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) {
|
||||
*item.StatsCount = len(allStats)
|
||||
|
||||
// Scale Armor Stats
|
||||
if *item.Class == 4 && *item.Armor > 0 {
|
||||
preArmor := *item.Armor
|
||||
*item.Armor = int(math.Ceil(float64(itemLevel) * config.QualityModifiers[*item.Quality] * config.MaterialModifiers[*item.Subclass]))
|
||||
|
||||
log.Printf("New Armor: %v scaled up from previous armor %v material is %v", *item.Armor, preArmor, *item.Material)
|
||||
}
|
||||
item.ScaleArmor(itemLevel)
|
||||
|
||||
// If the item is a weapon scale the DPS
|
||||
if *item.Class == 2 && *item.MinDmg1 > 0 {
|
||||
@@ -555,20 +617,32 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) {
|
||||
// Spells that can not be scaled into stats must get new spells scaled and created
|
||||
for _, spell := range otherSpells {
|
||||
// log.Printf(" --^^^^^^--------SPELL --- Spell %v (%v) Effect %v AuraEffect %v Spell Desc: %v basePoints %v", spell.Name, spell.ID, spell.Effect1, spell.EffectAura1, spell.Description, spell.EffectBasePoints1)
|
||||
newId, err := spell.ScaleSpell(fromItemLevel, itemLevel, *item.Quality)
|
||||
// Use ForceScaleSpell instead of ScaleSpell to ensure all spells are scaled properly
|
||||
// Determine tier based on item level
|
||||
tier := 1
|
||||
if itemLevel >= 200 {
|
||||
tier = 5
|
||||
} else if itemLevel >= 175 {
|
||||
tier = 4
|
||||
} else if itemLevel >= 150 {
|
||||
tier = 3
|
||||
} else if itemLevel >= 125 {
|
||||
tier = 2
|
||||
}
|
||||
|
||||
log.Printf("Scaling spell %v (ID: %v) with tier %d modifier", spell.Name, spell.ID, tier)
|
||||
err := spell.ForceScaleSpell(fromItemLevel, itemLevel, *item.Quality, tier)
|
||||
if err != nil {
|
||||
log.Printf("Failed to scale spell: %v, Spell %v", err, spell.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
if newId == 0 {
|
||||
log.Printf("Failed to scale spell: %v, Spell %v", err, spell.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
item.UpdateField(fmt.Sprintf("SpellId%v", spell.ItemSpellSlot), newId)
|
||||
// ForceScaleSpell modifies the spell in place, so we use the original spell ID
|
||||
item.UpdateField(fmt.Sprintf("SpellId%v", spell.ItemSpellSlot), spell.ID)
|
||||
item.Spells = append(item.Spells, spell)
|
||||
|
||||
// do one last check on all setting StatsCount based on how many stats have been set
|
||||
|
||||
// log.Printf(" --SCALED---SPELL --- Spell %v (%v) Effect %v AuraEffect %v Spell Desc: %v basePoints %v", spell.Name, spell.ID, spell.Effect1, spell.EffectAura1, spell.Description, spell.EffectBasePoints1)
|
||||
}
|
||||
|
||||
@@ -797,6 +871,271 @@ func correctSpellAttackPower(item *Item, allStats map[int]*ItemStat) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will determine the class type that would be the user of the item
|
||||
* Melee Strength Attacker: 1
|
||||
* Melee Agility Attacker: 2
|
||||
* Ranged Attacker: 3
|
||||
* Mage: 4
|
||||
* Healer: 5
|
||||
* Tank: 6
|
||||
* Generic: 7 (Could not determine)
|
||||
* @return int
|
||||
**/
|
||||
func (item *Item) GetClassUserType() int {
|
||||
|
||||
// loop over the stats and check if any of them are parry, defense, block
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
|
||||
// Tanking weapons will have defensive stats on them
|
||||
if statTypePtr == STAT.ParryRating || statTypePtr == STAT.DefenseSkillRating || statTypePtr == STAT.BlockRating || statTypePtr == STAT.BlockValue {
|
||||
return 6
|
||||
}
|
||||
|
||||
// Check for a healer stats like MP5 and Spell Healing Done
|
||||
if statTypePtr == STAT.ManaRegeneration || statTypePtr == STAT.SpellHealingDone {
|
||||
return 5
|
||||
}
|
||||
|
||||
// Check for a Mage stat if they have spell penetration we know it is a mage
|
||||
if statTypePtr == STAT.SpellPenetration {
|
||||
return 4
|
||||
}
|
||||
|
||||
if statTypePtr == STAT.RangedAttackPower || statTypePtr == STAT.CritRangedRating || statTypePtr == STAT.HitRangedRating {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
// For armor we can use the type to determine the class type
|
||||
if *item.Class == 4 {
|
||||
// if the item is cloth its a mage and did not have healer stats just treat as a mage item
|
||||
if *item.Material == 1 && *item.InventoryType != 16 {
|
||||
return 4
|
||||
}
|
||||
|
||||
// If it is plate and not a tank then it is a strength melee attack
|
||||
if *item.Material == 4 {
|
||||
return 1
|
||||
}
|
||||
|
||||
// If it is mail/leather armor then it is limited to Mage, Agility Fighter
|
||||
if *item.Material == 2 || *item.Material == 3 {
|
||||
// check for spellpower, spellcrit, spellhit, intellect
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.SpellPower || statTypePtr == STAT.CritSpellRating ||
|
||||
statTypePtr == STAT.HitSpellRating || statTypePtr == STAT.Intellect || statTypePtr == STAT.Spirit {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
// Do some weapon checks
|
||||
if *item.Class == 2 {
|
||||
// If it is a fist weapon or ranged throwing weapons its agility class type
|
||||
if *item.Subclass == 13 || *item.Subclass == 16 {
|
||||
return 2
|
||||
}
|
||||
|
||||
if *item.Subclass == 19 {
|
||||
return 4
|
||||
}
|
||||
|
||||
// if it is a polearm or spear 17 or 6 and strength then its strength class type
|
||||
if *item.Subclass == 17 || *item.Subclass == 6 {
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Strength {
|
||||
return 1
|
||||
}
|
||||
|
||||
// or attack power
|
||||
if statTypePtr == STAT.AttackPower {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise check for agility
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Agility {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
// last assume it is a healer
|
||||
return 5
|
||||
}
|
||||
|
||||
if *item.Subclass == 2 || *item.Subclass == 3 || *item.Subclass == 18 {
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Strength {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
// Most specific cases have been addressed now just use the base stats to make a decision for the remaining
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Spirit {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Intellect {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Strength {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.Agility {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
// If it is attack power melee haste melee crit or anything else then it is a agility
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.AttackPower || statTypePtr == STAT.HasteMeleeRating || statTypePtr == STAT.CritMeleeRating {
|
||||
return 7
|
||||
}
|
||||
}
|
||||
|
||||
// If it is spell power spell crit spell hit then it is a mage
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.SpellPower || statTypePtr == STAT.CritSpellRating || statTypePtr == STAT.HitSpellRating {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
// If it is ranged attack power ranged haste ranged crit or anything else then it is a ranged
|
||||
for i := 1; i <= 7; i++ {
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statTypePtr, _ := item.GetField(statTypeField)
|
||||
if statTypePtr == STAT.RangedAttackPower || statTypePtr == STAT.HasteRangedRating || statTypePtr == STAT.CritRangedRating {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
return 7
|
||||
}
|
||||
|
||||
func (item *Item) ApplyTierModifiers(optionalTier ...int) {
|
||||
// Use provided tier or default to 0 if not set
|
||||
var tier int
|
||||
if len(optionalTier) > 0 {
|
||||
tier = optionalTier[0]
|
||||
} else {
|
||||
tier = 0
|
||||
}
|
||||
|
||||
// Default tier modifier is 1.0 (no modification)
|
||||
tierModifier := 1.0
|
||||
|
||||
// This is a necessary bonus to catch gear up from previous v2 version
|
||||
catchUpBonus := 1.5
|
||||
|
||||
// If tier is valid (1-5), get the modifier from config
|
||||
if tier > 0 && tier <= 5 {
|
||||
if mod, ok := config.GearTierModifiers[tier]; ok {
|
||||
tierModifier = mod
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tier modifier to all stats on the item
|
||||
for i := 1; i <= 10; i++ {
|
||||
// Get the stat type and value fields using reflection
|
||||
statTypeField := fmt.Sprintf("StatType%d", i)
|
||||
statValueField := fmt.Sprintf("StatValue%d", i)
|
||||
|
||||
// Get the current values
|
||||
statTypePtr, err1 := item.GetField(statTypeField)
|
||||
statValuePtr, err2 := item.GetField(statValueField)
|
||||
|
||||
// Skip if any errors or if stat type is 0 or stat value is 0
|
||||
if err1 != nil || err2 != nil || statTypePtr == 0 || statValuePtr == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the stat modifier (inverse of the cost modifier)
|
||||
statModifier, ok := config.StatModifiers[statTypePtr]
|
||||
if !ok {
|
||||
statModifier = 1.0
|
||||
}
|
||||
|
||||
// Inverse of the stat modifier (e.g., 0.5 cost means 2.0 multiplier)
|
||||
inverseModifier := 1.0
|
||||
if statModifier > 0 {
|
||||
inverseModifier = 1.0 / statModifier
|
||||
}
|
||||
|
||||
// Apply tier modifier and stat modifier
|
||||
newValue := int(float64(statValuePtr) * tierModifier * inverseModifier * catchUpBonus)
|
||||
|
||||
// Update the item's stat value
|
||||
item.UpdateField(statValueField, newValue)
|
||||
|
||||
// We've already updated the field directly with UpdateField above
|
||||
// No need to update StatsMap as we're focusing on the direct stat values
|
||||
}
|
||||
|
||||
// Apply tier modifier to spells
|
||||
// spells, err := item.GetSpells()
|
||||
// if err == nil && len(spells) > 0 {
|
||||
// for i := range spells {
|
||||
// // Get the item level
|
||||
// currentLevel := 0
|
||||
// if item.ItemLevel != nil {
|
||||
// currentLevel = *item.ItemLevel
|
||||
// }
|
||||
|
||||
// // Get the item quality
|
||||
// quality := 2 // Default to uncommon
|
||||
// if item.Quality != nil {
|
||||
// quality = *item.Quality
|
||||
// }
|
||||
|
||||
// // Scale spells with the tier modifier
|
||||
// spells[i].ForceScaleSpell(currentLevel, currentLevel, quality, tier)
|
||||
// }
|
||||
|
||||
// // Update the item's spells
|
||||
// item.Spells = spells
|
||||
// }
|
||||
}
|
||||
|
||||
func ItemToSql(item Item, reqLevel int, difficulty int) string {
|
||||
|
||||
fmt.Printf("-- Required level: %v\n", reqLevel)
|
||||
|
||||
@@ -432,6 +432,342 @@ func (s *Spell) ScaleSpell(fromItemLevel int, itemLevel int, itemQuality int) (i
|
||||
return idBump + s.ID, nil
|
||||
}
|
||||
|
||||
// ForceScaleSpell is an enhanced version of ScaleSpell that scales ANY spell regardless of effect type.
|
||||
// It applies quality modifiers and special handling for different effect types, using a progression-based
|
||||
// scaling curve with diminishing returns for large item level differences.
|
||||
// The tier parameter (1-5) applies an additional modifier from GearTierModifiers plus a 5% bonus.
|
||||
// Also updates spell names to reflect new values when applicable.
|
||||
func (s *Spell) ForceScaleSpell(fromItemLevel int, toItemLevel int, itemQuality int, tier ...int) error {
|
||||
// Store original base point values before scaling
|
||||
// Add 1 to each value to account for the way WoW stores spell effect values
|
||||
// (the displayed value is often BasePoints+1)
|
||||
originalBasePoints1 := s.EffectBasePoints1 + 1
|
||||
originalBasePoints2 := s.EffectBasePoints2 + 1
|
||||
originalBasePoints3 := s.EffectBasePoints3 + 1
|
||||
// Get quality modifier from config package
|
||||
qualModifier, exists := config.QualityModifiers[itemQuality]
|
||||
if !exists {
|
||||
// Default to 1.0 if quality not found
|
||||
qualModifier = 1.0
|
||||
}
|
||||
|
||||
if itemQuality == 5 {
|
||||
qualModifier = 1.2 // Legendary
|
||||
} else {
|
||||
qualModifier = 1.0
|
||||
}
|
||||
tierModifier := 1.00
|
||||
|
||||
// Apply tier modifier if provided
|
||||
if len(tier) > 0 && tier[0] >= 1 && tier[0] <= 5 {
|
||||
// Get the tier modifier from config
|
||||
if mod, exists := config.GearTierModifiers[tier[0]]; exists {
|
||||
tierModifier *= mod // Apply tier modifier on top of base 5% bonus
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tier modifier to quality modifier
|
||||
qualModifier *= tierModifier
|
||||
|
||||
// Calculate item level difference and ratio
|
||||
ilevelDiff := toItemLevel - fromItemLevel
|
||||
|
||||
// Use a simple linear scaling based on item level ratio
|
||||
// This provides a straightforward and predictable scaling that's easy to understand
|
||||
levelRatio := float64(toItemLevel) / float64(fromItemLevel)
|
||||
|
||||
// Effect type categorization
|
||||
directDamageEffects := [...]int{2, 9, 10} // School damage, % weapon damage, etc.
|
||||
statBuffEffects := [...]int{35, 29, 99, 124} // Apply stat, add flat stat, etc.
|
||||
periodEffects := [...]int{6, 77} // Apply aura, periodic trigger
|
||||
|
||||
// Scale Effect1
|
||||
if s.EffectBasePoints1 != 0 {
|
||||
effectMultiplier := 1.0
|
||||
|
||||
// Check for attack power and spell power in the description regardless of effect type
|
||||
if strings.Contains(s.Description, "attack power") ||
|
||||
strings.Contains(s.Description, "Attack Power") ||
|
||||
strings.Contains(s.Description, "spell power") ||
|
||||
strings.Contains(s.Description, "Spell Power") ||
|
||||
strings.Contains(s.Description, "healing") ||
|
||||
strings.Contains(s.Description, "Healing") {
|
||||
effectMultiplier = 2.0 // Higher multiplier for attack/spell power
|
||||
}
|
||||
|
||||
// Determine effect category and apply appropriate multiplier
|
||||
if s.Effect1 != 0 {
|
||||
// Direct damage effects scale more aggressively at higher item levels
|
||||
if funk.Contains(directDamageEffects, s.Effect1) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005) // +0.5% per every 10 item levels
|
||||
}
|
||||
|
||||
// Flat Base Stat modifier for all other stats.
|
||||
if funk.Contains(statBuffEffects, s.Effect1) && effectMultiplier < 1.5 {
|
||||
effectMultiplier = 1.45
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for aura effects
|
||||
if s.EffectAura1 != 0 {
|
||||
// DOT effects (Aura 3: Periodic Damage)
|
||||
if s.EffectAura1 == 3 && funk.Contains(periodEffects, s.Effect1) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
|
||||
// Scale DOTs more with higher quality items
|
||||
if itemQuality >= 5 { // Legendary or higher
|
||||
effectMultiplier += 0.5
|
||||
}
|
||||
}
|
||||
|
||||
// HOT effects (Aura 8: Periodic Heal)
|
||||
if s.EffectAura1 == 8 && funk.Contains(periodEffects, s.Effect1) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
// Healing scales slightly higher than damage
|
||||
}
|
||||
|
||||
// Damage Shield effects (Aura 15)
|
||||
if s.EffectAura1 == 15 && funk.Contains(periodEffects, s.Effect1) {
|
||||
// Damage shields scale with item level difference
|
||||
effectMultiplier = 1.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
if effectMultiplier > 2.5 {
|
||||
effectMultiplier = 2.5 // Cap at 2.5x
|
||||
}
|
||||
}
|
||||
|
||||
// Proc chance effects (various auras)
|
||||
if s.ProcChance > 0 && s.ProcChance < 100 {
|
||||
// For proc effects, we might want to scale the effect more aggressively
|
||||
// since they don't happen all the time
|
||||
procFactor := 100.0 / float64(s.ProcChance) // Inverse of proc chance
|
||||
// Limit the proc factor to avoid excessive scaling
|
||||
if procFactor > 2.0 {
|
||||
procFactor = 2.0
|
||||
}
|
||||
effectMultiplier += math.Sqrt(procFactor) // Scale by square root of proc factor
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for mana restoration
|
||||
if s.Effect1 == 30 {
|
||||
if strings.Contains(s.Description, "Mana") || strings.Contains(s.Description, "mana") {
|
||||
// Mana effects scale with level but with diminishing returns
|
||||
effectMultiplier = 1.0 + (math.Log10(float64(ilevelDiff+1)) * 0.3)
|
||||
}
|
||||
}
|
||||
// Apply the scaling with the appropriate multiplier
|
||||
s.EffectBasePoints1 = int(float64(s.EffectBasePoints1) * levelRatio * qualModifier * effectMultiplier)
|
||||
}
|
||||
|
||||
// Scale Effect2 with similar logic
|
||||
if s.EffectBasePoints2 != 0 {
|
||||
effectMultiplier := 1.0
|
||||
|
||||
// Check for attack power and spell power in the description regardless of effect type
|
||||
if strings.Contains(s.Description, "attack power") ||
|
||||
strings.Contains(s.Description, "Attack Power") ||
|
||||
strings.Contains(s.Description, "spell power") ||
|
||||
strings.Contains(s.Description, "Spell Power") ||
|
||||
strings.Contains(s.Description, "healing") ||
|
||||
strings.Contains(s.Description, "Healing") {
|
||||
effectMultiplier = 2.0 // Higher multiplier for attack/spell power
|
||||
}
|
||||
|
||||
// Determine effect category and apply appropriate multiplier
|
||||
if s.Effect2 != 0 {
|
||||
// Direct damage effects scale more aggressively at higher item levels
|
||||
if funk.Contains(directDamageEffects, s.Effect2) {
|
||||
// Scale damage more aggressively for higher item levels
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005) // +0.5% per every 10 item levels
|
||||
}
|
||||
|
||||
// Flat Base Stat modifier for all other stats.
|
||||
if funk.Contains(statBuffEffects, s.Effect2) && effectMultiplier < 1.5 {
|
||||
effectMultiplier = 1.45
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for aura effects
|
||||
if s.EffectAura2 != 0 {
|
||||
// DOT effects (Aura 3: Periodic Damage)
|
||||
if s.EffectAura2 == 3 && funk.Contains(periodEffects, s.Effect2) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
|
||||
// Scale DOTs more with higher quality items
|
||||
if itemQuality >= 5 { // Legendary or higher
|
||||
effectMultiplier += 0.5
|
||||
}
|
||||
}
|
||||
|
||||
// HOT effects (Aura 8: Periodic Heal)
|
||||
if s.EffectAura2 == 8 && funk.Contains(periodEffects, s.Effect2) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
// Healing scales slightly higher than damage
|
||||
}
|
||||
|
||||
// Damage Shield effects (Aura 15)
|
||||
if s.EffectAura2 == 15 && funk.Contains(periodEffects, s.Effect2) {
|
||||
// Damage shields scale with item level difference
|
||||
effectMultiplier = 1.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
if effectMultiplier > 2.5 {
|
||||
effectMultiplier = 2.5 // Cap at 2.5x
|
||||
}
|
||||
}
|
||||
|
||||
// Proc chance effects (various auras)
|
||||
if s.ProcChance > 0 && s.ProcChance < 100 {
|
||||
// For proc effects, we might want to scale the effect more aggressively
|
||||
// since they don't happen all the time
|
||||
procFactor := 100.0 / float64(s.ProcChance) // Inverse of proc chance
|
||||
// Limit the proc factor to avoid excessive scaling
|
||||
if procFactor > 2.0 {
|
||||
procFactor = 2.0
|
||||
}
|
||||
effectMultiplier += math.Sqrt(procFactor) // Scale by square root of proc factor
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for mana restoration
|
||||
if s.Effect2 == 30 {
|
||||
if strings.Contains(s.Description, "Mana") || strings.Contains(s.Description, "mana") {
|
||||
// Mana effects scale with level but with diminishing returns
|
||||
effectMultiplier = 1.0 + (math.Log10(float64(ilevelDiff+1)) * 0.3)
|
||||
}
|
||||
}
|
||||
// Apply the scaling with the appropriate multiplier
|
||||
s.EffectBasePoints2 = int(float64(s.EffectBasePoints2) * levelRatio * qualModifier * effectMultiplier)
|
||||
}
|
||||
|
||||
// Scale Effect3 with similar logic
|
||||
if s.EffectBasePoints3 != 0 {
|
||||
effectMultiplier := 1.0
|
||||
|
||||
// Check for attack power and spell power in the description regardless of effect type
|
||||
if strings.Contains(s.Description, "attack power") ||
|
||||
strings.Contains(s.Description, "Attack Power") ||
|
||||
strings.Contains(s.Description, "spell power") ||
|
||||
strings.Contains(s.Description, "Spell Power") ||
|
||||
strings.Contains(s.Description, "healing") ||
|
||||
strings.Contains(s.Description, "Healing") {
|
||||
effectMultiplier = 2.0 // Higher multiplier for attack/spell power
|
||||
}
|
||||
|
||||
// Determine effect category and apply appropriate multiplier
|
||||
if s.Effect3 != 0 {
|
||||
// Direct damage effects scale more aggressively at higher item levels
|
||||
if funk.Contains(directDamageEffects, s.Effect3) {
|
||||
// Scale damage more aggressively for higher item levels
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005) // +0.5% per every 10 item levels
|
||||
}
|
||||
|
||||
// Flat Base Stat modifier for all other stats.
|
||||
if funk.Contains(statBuffEffects, s.Effect3) && effectMultiplier < 1.5 {
|
||||
effectMultiplier = 1.45
|
||||
}
|
||||
}
|
||||
// Special handling for aura effects
|
||||
if s.EffectAura3 != 0 {
|
||||
// DOT effects (Aura 3: Periodic Damage)
|
||||
if s.EffectAura3 == 3 && funk.Contains(periodEffects, s.Effect3) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
|
||||
// Scale DOTs more with higher quality items
|
||||
if itemQuality >= 5 { // Legendary or higher
|
||||
effectMultiplier += 0.5
|
||||
}
|
||||
}
|
||||
|
||||
// HOT effects (Aura 8: Periodic Heal)
|
||||
if s.EffectAura3 == 8 && funk.Contains(periodEffects, s.Effect3) {
|
||||
effectMultiplier = 2.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
// Healing scales slightly higher than damage
|
||||
}
|
||||
|
||||
// Damage Shield effects (Aura 15)
|
||||
if s.EffectAura3 == 15 && funk.Contains(periodEffects, s.Effect3) {
|
||||
// Damage shields scale with item level difference
|
||||
effectMultiplier = 1.5 + (float64(toItemLevel) * 0.1 * 0.005)
|
||||
if effectMultiplier > 2.5 {
|
||||
effectMultiplier = 2.5 // Cap at 2.5x
|
||||
}
|
||||
}
|
||||
|
||||
// Proc chance effects (various auras)
|
||||
if s.ProcChance > 0 && s.ProcChance < 100 {
|
||||
// For proc effects, we might want to scale the effect more aggressively
|
||||
// since they don't happen all the time
|
||||
procFactor := 100.0 / float64(s.ProcChance) // Inverse of proc chance
|
||||
// Limit the proc factor to avoid excessive scaling
|
||||
if procFactor > 2.0 {
|
||||
procFactor = 2.0
|
||||
}
|
||||
effectMultiplier += math.Sqrt(procFactor) // Scale by square root of proc factor
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for mana restoration
|
||||
if s.Effect3 == 30 {
|
||||
if strings.Contains(s.Description, "Mana") || strings.Contains(s.Description, "mana") {
|
||||
// Mana effects scale with level but with diminishing returns
|
||||
effectMultiplier = 1.0 + (math.Log10(float64(ilevelDiff+1)) * 0.3)
|
||||
}
|
||||
}
|
||||
// Apply the scaling with the appropriate multiplier
|
||||
s.EffectBasePoints3 = int(float64(s.EffectBasePoints3) * levelRatio * qualModifier * effectMultiplier)
|
||||
}
|
||||
|
||||
// Update spell name with new values if it contains the old values
|
||||
// This helps keep the spell name in sync with the actual effect values
|
||||
newValues := []int{
|
||||
s.EffectBasePoints1 + 1, // Add 1 to match WoW's display format
|
||||
s.EffectBasePoints2 + 1,
|
||||
s.EffectBasePoints3 + 1,
|
||||
}
|
||||
|
||||
// Store the original name before any modifications
|
||||
originalName := s.Name
|
||||
|
||||
// Try different formats of the number that might appear in the name
|
||||
for i, oldValue := range []int{originalBasePoints1, originalBasePoints2, originalBasePoints3} {
|
||||
if oldValue <= 1 { // Skip if original value is 0 or 1 (likely not meaningful)
|
||||
continue
|
||||
}
|
||||
|
||||
newValue := newValues[i]
|
||||
if newValue <= 1 { // Skip if new value is 0 or 1 (likely not meaningful)
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip if values are the same (no change needed)
|
||||
if oldValue == newValue {
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert both old and new values to strings
|
||||
oldValueStr := fmt.Sprintf("%d", oldValue)
|
||||
newValueStr := fmt.Sprintf("%d", newValue)
|
||||
|
||||
// Only replace if the old value actually appears in the name
|
||||
if strings.Contains(s.Name, oldValueStr) {
|
||||
s.Name = strings.Replace(s.Name, oldValueStr, newValueStr, -1)
|
||||
//log.Printf("Updated ID %d spell name from '%s' to '%s' (replaced %s with %s)",
|
||||
// s.ID, originalName, s.Name, oldValueStr, newValueStr)
|
||||
}
|
||||
|
||||
// Also try with a plus sign (e.g., "+15" in name)
|
||||
oldValueWithPlus := "+" + oldValueStr
|
||||
newValueWithPlus := "+" + newValueStr
|
||||
if strings.Contains(s.Name, oldValueWithPlus) {
|
||||
s.Name = strings.Replace(s.Name, oldValueWithPlus, newValueWithPlus, -1)
|
||||
log.Printf("Updated spell name from '%s' to '%s' (replaced %s with %s)",
|
||||
originalName, s.Name, oldValueWithPlus, newValueWithPlus)
|
||||
}
|
||||
}
|
||||
|
||||
s.Scaled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func SpellToSql(spell Spell, quality int) string {
|
||||
|
||||
entryBump := 30000000
|
||||
@@ -514,6 +850,42 @@ func SpellToSql(spell Spell, quality int) string {
|
||||
MaxTargetLevel, SpellClassSet, SpellClassMask_1, SpellClassMask_2, SpellClassMask_3, MaxTargets, DefenseType, PreventionType, StanceBarOrder,
|
||||
EffectChainAmplitude_1, EffectChainAmplitude_2, EffectChainAmplitude_3, MinFactionID, MinReputation, RequiredAuraVision, RequiredTotemCategoryID_1,
|
||||
RequiredTotemCategoryID_2, RequiredAreasID, SchoolMask, RuneCostID, SpellMissileID, PowerDisplayID, EffectBonusMultiplier_1, EffectBonusMultiplier_2,
|
||||
EffectBonusMultiplier_3, SpellDescriptionVariableID, SpellDifficultyID
|
||||
) SELECT
|
||||
ID + %v, Category, DispelType, Mechanic, Attributes, AttributesEx, AttributesEx2, AttributesEx3, AttributesEx4,
|
||||
AttributesEx5, AttributesEx6, AttributesEx7, ShapeshiftMask, unk_320_2, ShapeshiftExclude, unk_320_3, Targets,
|
||||
TargetCreatureType, RequiresSpellFocus, FacingCasterFlags, CasterAuraState, TargetAuraState, ExcludeCasterAuraState,
|
||||
ExcludeTargetAuraState, CasterAuraSpell, TargetAuraSpell, ExcludeCasterAuraSpell, ExcludeTargetAuraSpell, CastingTimeIndex,
|
||||
RecoveryTime, CategoryRecoveryTime, InterruptFlags, AuraInterruptFlags, ChannelInterruptFlags, ProcTypeMask, ProcChance,
|
||||
ProcCharges, MaxLevel, BaseLevel, SpellLevel, DurationIndex, PowerType, ManaCost, ManaCostPerLevel, ManaPerSecond,
|
||||
ManaPerSecondPerLevel, RangeIndex, Speed, ModalNextSpell, CumulativeAura, Totem_1, Totem_2, Reagent_1, Reagent_2, Reagent_3,
|
||||
Reagent_4, Reagent_5, Reagent_6, Reagent_7, Reagent_8, ReagentCount_1, ReagentCount_2, ReagentCount_3, ReagentCount_4,
|
||||
ReagentCount_5, ReagentCount_6, ReagentCount_7, ReagentCount_8, EquippedItemClass, EquippedItemSubclass, EquippedItemInvTypes,
|
||||
Effect_1, Effect_2, Effect_3, EffectDieSides_1, EffectDieSides_2, EffectDieSides_3, EffectRealPointsPerLevel_1,
|
||||
EffectRealPointsPerLevel_2, EffectRealPointsPerLevel_3, EffectBasePoints_1, EffectBasePoints_2, EffectBasePoints_3,
|
||||
EffectMechanic_1, EffectMechanic_2, EffectMechanic_3, ImplicitTargetA_1, ImplicitTargetA_2, ImplicitTargetA_3, ImplicitTargetB_1,
|
||||
ImplicitTargetB_2, ImplicitTargetB_3, EffectRadiusIndex_1, EffectRadiusIndex_2, EffectRadiusIndex_3, EffectAura_1,
|
||||
EffectAura_2, EffectAura_3, EffectAuraPeriod_1, EffectAuraPeriod_2, EffectAuraPeriod_3, EffectMultipleValue_1, EffectMultipleValue_2,
|
||||
EffectMultipleValue_3, EffectChainTargets_1, EffectChainTargets_2, EffectChainTargets_3, EffectItemType_1, EffectItemType_2,
|
||||
EffectItemType_3, EffectMiscValue_1, EffectMiscValue_2, EffectMiscValue_3, EffectMiscValueB_1, EffectMiscValueB_2, EffectMiscValueB_3,
|
||||
EffectTriggerSpell_1, EffectTriggerSpell_2, EffectTriggerSpell_3, EffectPointsPerCombo_1, EffectPointsPerCombo_2, EffectPointsPerCombo_3,
|
||||
EffectSpellClassMaskA_1, EffectSpellClassMaskA_2, EffectSpellClassMaskA_3, EffectSpellClassMaskB_1, EffectSpellClassMaskB_2,
|
||||
EffectSpellClassMaskB_3, EffectSpellClassMaskC_1, EffectSpellClassMaskC_2, EffectSpellClassMaskC_3, SpellVisualID_1, SpellVisualID_2,
|
||||
SpellIconID, ActiveIconID, SpellPriority, Name_Lang_enUS, Name_Lang_enGB, Name_Lang_koKR, Name_Lang_frFR, Name_Lang_deDE,
|
||||
Name_Lang_enCN, Name_Lang_zhCN, Name_Lang_enTW, Name_Lang_zhTW, Name_Lang_esES, Name_Lang_esMX, Name_Lang_ruRU, Name_Lang_ptPT,
|
||||
Name_Lang_ptBR, Name_Lang_itIT, Name_Lang_Unk, Name_Lang_Mask, NameSubtext_Lang_enUS, NameSubtext_Lang_enGB, NameSubtext_Lang_koKR,
|
||||
NameSubtext_Lang_frFR, NameSubtext_Lang_deDE, NameSubtext_Lang_enCN, NameSubtext_Lang_zhCN, NameSubtext_Lang_enTW, NameSubtext_Lang_zhTW,
|
||||
NameSubtext_Lang_esES, NameSubtext_Lang_esMX, NameSubtext_Lang_ruRU, NameSubtext_Lang_ptPT, NameSubtext_Lang_ptBR, NameSubtext_Lang_itIT,
|
||||
NameSubtext_Lang_Unk, NameSubtext_Lang_Mask, Description_Lang_enUS, Description_Lang_enGB, Description_Lang_koKR, Description_Lang_frFR,
|
||||
Description_Lang_deDE, Description_Lang_enCN, Description_Lang_zhCN, Description_Lang_enTW, Description_Lang_zhTW, Description_Lang_esES,
|
||||
Description_Lang_esMX, Description_Lang_ruRU, Description_Lang_ptPT, Description_Lang_ptBR, Description_Lang_itIT, Description_Lang_Unk,
|
||||
Description_Lang_Mask, AuraDescription_Lang_enUS, AuraDescription_Lang_enGB, AuraDescription_Lang_koKR, AuraDescription_Lang_frFR,
|
||||
AuraDescription_Lang_deDE, AuraDescription_Lang_enCN, AuraDescription_Lang_zhCN, AuraDescription_Lang_enTW, AuraDescription_Lang_zhTW,
|
||||
AuraDescription_Lang_esES, AuraDescription_Lang_esMX, AuraDescription_Lang_ruRU, AuraDescription_Lang_ptPT, AuraDescription_Lang_ptBR,
|
||||
AuraDescription_Lang_itIT, AuraDescription_Lang_Unk, AuraDescription_Lang_Mask, ManaCostPct, StartRecoveryCategory, StartRecoveryTime,
|
||||
MaxTargetLevel, SpellClassSet, SpellClassMask_1, SpellClassMask_2, SpellClassMask_3, MaxTargets, DefenseType, PreventionType, StanceBarOrder,
|
||||
EffectChainAmplitude_1, EffectChainAmplitude_2, EffectChainAmplitude_3, MinFactionID, MinReputation, RequiredAuraVision, RequiredTotemCategoryID_1,
|
||||
RequiredTotemCategoryID_2, RequiredAreasID, SchoolMask, RuneCostID, SpellMissileID, PowerDisplayID, EffectBonusMultiplier_1, EffectBonusMultiplier_2,
|
||||
EffectBonusMultiplier_3, SpellDescriptionVariableID, SpellDifficultyID from acore_world.spell_dbc as src
|
||||
WHERE src.ID = %v ON DUPLICATE KEY UPDATE ID = src.ID + %v;`, entryBump, spell.ID, entryBump)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user