mirror of
https://github.com/araxiaonline/wow-item-generator.git
synced 2026-06-13 03:02:22 -04:00
350 lines
11 KiB
Go
350 lines
11 KiB
Go
package spells
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/araxiaonline/endgame-item-generator/internal/db/mysql"
|
|
)
|
|
|
|
// SpellTheme represents different magical themes for spell assignment
|
|
type SpellTheme int
|
|
|
|
const (
|
|
ThemeNone SpellTheme = iota
|
|
ThemeFrost
|
|
ThemeFire
|
|
ThemeNature
|
|
ThemeShadow
|
|
ThemeArcane
|
|
ThemeHoly
|
|
)
|
|
|
|
// String returns the string representation of a SpellTheme
|
|
func (t SpellTheme) String() string {
|
|
switch t {
|
|
case ThemeFrost:
|
|
return "Frost"
|
|
case ThemeFire:
|
|
return "Fire"
|
|
case ThemeNature:
|
|
return "Nature"
|
|
case ThemeShadow:
|
|
return "Shadow"
|
|
case ThemeArcane:
|
|
return "Arcane"
|
|
case ThemeHoly:
|
|
return "Holy"
|
|
default:
|
|
return "None"
|
|
}
|
|
}
|
|
|
|
// ThematicSpellAssigner handles assignment of thematic spells to items
|
|
type ThematicSpellAssigner struct {
|
|
db *mysql.MySqlDb
|
|
}
|
|
|
|
// NewThematicSpellAssigner creates a new thematic spell assigner
|
|
func NewThematicSpellAssigner(db *mysql.MySqlDb) *ThematicSpellAssigner {
|
|
return &ThematicSpellAssigner{db: db}
|
|
}
|
|
|
|
// ShouldHaveProc determines if an item should have a proc spell based on its properties
|
|
func (tsa *ThematicSpellAssigner) ShouldHaveProc(itemClass, itemSubclass, itemLevel, quality int, itemName string) bool {
|
|
log.Printf("DEBUG: ShouldHaveProc - Class: %d, Subclass: %d, Level: %d, Quality: %d, Name: %s",
|
|
itemClass, itemSubclass, itemLevel, quality, itemName)
|
|
|
|
// Weapons should have procs
|
|
if itemClass == 2 {
|
|
log.Printf("DEBUG: Item is a weapon (class 2)")
|
|
// Higher quality items more likely to have procs
|
|
if quality >= 3 && itemLevel >= 40 {
|
|
log.Printf("DEBUG: Weapon qualifies - quality %d >= 3 and level %d >= 40", quality, itemLevel)
|
|
return true
|
|
} else {
|
|
log.Printf("DEBUG: Weapon does not qualify - quality %d < 3 or level %d < 40", quality, itemLevel)
|
|
}
|
|
}
|
|
|
|
// Trinkets should have procs
|
|
if itemClass == 4 && itemSubclass == 0 {
|
|
log.Printf("DEBUG: Item is a trinket - qualifies for proc")
|
|
return true
|
|
}
|
|
|
|
// Jewelry with thematic names
|
|
if itemClass == 4 && (itemSubclass == 1 || itemSubclass == 2) { // Neck, Ring
|
|
theme := tsa.DetectThemeFromName(itemName)
|
|
if theme != ThemeNone {
|
|
log.Printf("DEBUG: Jewelry with theme %s - qualifies for proc", theme.String())
|
|
return true
|
|
}
|
|
}
|
|
|
|
log.Printf("DEBUG: Item does not qualify for proc")
|
|
return false
|
|
}
|
|
|
|
// DetectThemeFromName analyzes item name and description to determine magical theme
|
|
func (tsa *ThematicSpellAssigner) DetectThemeFromName(itemName string) SpellTheme {
|
|
name := strings.ToLower(itemName)
|
|
|
|
// Frost theme keywords - spells like Frostbolt, Frost Nova, Ice Lance
|
|
frostKeywords := []string{
|
|
"frost", "cold", "ice", "frozen", "chill", "winter", "arctic", "glacial",
|
|
"coldrage", "frostbite", "icicle", "blizzard", "freeze", "shiver", "crystal",
|
|
"permafrost", "hoarfrost", "rimefang", "icecrown", "northrend", "wintergrasp",
|
|
"frostmourne", "sindragosa", "arthas", "lich", "undead", "scourge",
|
|
}
|
|
|
|
// Fire theme keywords - spells like Fireball, Flame Strike, Pyroblast
|
|
fireKeywords := []string{
|
|
"fire", "flame", "burn", "ember", "inferno", "molten", "volcanic",
|
|
"blazing", "scorching", "flaming", "ignite", "pyre", "magma", "lava",
|
|
"sulfuron", "ragnaros", "flamewaker", "firelands", "phoenix", "immolate",
|
|
"conflagrate", "incinerate", "combustion", "pyroblast", "scorch",
|
|
}
|
|
|
|
// Nature theme keywords - spells like Lightning Bolt, Earth Shock, Entangling Roots
|
|
natureKeywords := []string{
|
|
"nature", "earth", "storm", "lightning", "thunder", "wind", "leaf",
|
|
"thorn", "root", "growth", "poison", "venom", "toxic", "acid", "druid",
|
|
"shaman", "elemental", "totem", "spirit", "wolf", "bear", "cat", "tree",
|
|
"hurricane", "typhoon", "earthquake", "rockbiter", "windfury", "stormstrike",
|
|
"maelstrom", "chain", "healing", "rejuvenation", "regrowth", "lifebloom",
|
|
}
|
|
|
|
// Shadow theme keywords - spells like Shadow Bolt, Drain Life, Fear
|
|
shadowKeywords := []string{
|
|
"shadow", "dark", "void", "curse", "doom", "death", "soul", "spirit",
|
|
"nightmare", "corruption", "plague", "blight", "necro", "warlock", "priest",
|
|
"undead", "bone", "skull", "grave", "tomb", "crypt", "wraith", "specter",
|
|
"banshee", "lich", "drain", "siphon", "vampiric", "life", "mana", "burn",
|
|
"fear", "horror", "terror", "agony", "torment", "suffering", "pain",
|
|
}
|
|
|
|
// Arcane theme keywords - spells like Arcane Missiles, Polymorph, Counterspell
|
|
arcaneKeywords := []string{
|
|
"arcane", "magic", "mystic", "enchant", "spell", "mana", "ethereal",
|
|
"astral", "cosmic", "temporal", "dimensional", "mage", "wizard", "sorcerer",
|
|
"intellect", "wisdom", "knowledge", "study", "tome", "grimoire", "staff",
|
|
"orb", "crystal", "gem", "power", "energy", "force", "teleport", "portal",
|
|
"polymorph", "counterspell", "dispel", "silence", "interrupt",
|
|
}
|
|
|
|
// Holy theme keywords - spells like Heal, Flash of Light, Consecration
|
|
holyKeywords := []string{
|
|
"holy", "divine", "blessed", "sacred", "light", "radiant", "celestial",
|
|
"righteous", "pure", "sanctified", "hallowed", "paladin", "priest", "cleric",
|
|
"angel", "seraph", "cherub", "heaven", "paradise", "salvation", "redemption",
|
|
"consecration", "blessing", "grace", "mercy", "faith", "devotion", "prayer",
|
|
"heal", "cure", "restore", "renew", "resurrect", "revive", "sanctuary",
|
|
}
|
|
|
|
// Check each theme
|
|
for _, keyword := range frostKeywords {
|
|
if strings.Contains(name, keyword) {
|
|
return ThemeFrost
|
|
}
|
|
}
|
|
|
|
for _, keyword := range fireKeywords {
|
|
if strings.Contains(name, keyword) {
|
|
return ThemeFire
|
|
}
|
|
}
|
|
|
|
for _, keyword := range natureKeywords {
|
|
if strings.Contains(name, keyword) {
|
|
return ThemeNature
|
|
}
|
|
}
|
|
|
|
for _, keyword := range shadowKeywords {
|
|
if strings.Contains(name, keyword) {
|
|
return ThemeShadow
|
|
}
|
|
}
|
|
|
|
for _, keyword := range arcaneKeywords {
|
|
if strings.Contains(name, keyword) {
|
|
return ThemeArcane
|
|
}
|
|
}
|
|
|
|
for _, keyword := range holyKeywords {
|
|
if strings.Contains(name, keyword) {
|
|
return ThemeHoly
|
|
}
|
|
}
|
|
|
|
return ThemeNone
|
|
}
|
|
|
|
// FindThematicSpell finds an appropriate spell for the given theme
|
|
func (tsa *ThematicSpellAssigner) FindThematicSpell(theme SpellTheme, itemClass, itemSubclass int) (int, error) {
|
|
log.Printf("Finding thematic spell for theme: %s, class: %d, subclass: %d", theme.String(), itemClass, itemSubclass)
|
|
|
|
// First, try to find existing scaled spells (31000000+)
|
|
scaledSpellId, err := tsa.findScaledThematicSpell(theme, itemClass, itemSubclass)
|
|
if err == nil && scaledSpellId > 0 {
|
|
log.Printf("Found existing scaled spell: %d for theme %s", scaledSpellId, theme.String())
|
|
return scaledSpellId, nil
|
|
}
|
|
|
|
// If no scaled spell found, look in spell_dbc for base spells
|
|
baseSpellId, err := tsa.findBaseThematicSpell(theme, itemClass, itemSubclass)
|
|
if err == nil && baseSpellId > 0 {
|
|
log.Printf("Found base spell: %d for theme %s", baseSpellId, theme.String())
|
|
return baseSpellId, nil
|
|
}
|
|
|
|
return 0, fmt.Errorf("no suitable spell found for theme %s", theme.String())
|
|
}
|
|
|
|
// findScaledThematicSpell looks for existing scaled spells (31000000+) that match the theme
|
|
func (tsa *ThematicSpellAssigner) findScaledThematicSpell(theme SpellTheme, itemClass, itemSubclass int) (int, error) {
|
|
// Define spell pools for each theme (existing scaled spells starting from 31000000)
|
|
// These would be spells that have already been scaled by the system
|
|
scaledSpellPools := map[SpellTheme][]int{
|
|
ThemeFrost: {
|
|
// Start with high-priority frost spells for weapons/trinkets
|
|
31013439, // Scaled Frostbolt (damage + slow)
|
|
31012737, // Scaled Frost Armor (defensive)
|
|
31021401, // Scaled Frost Nova (AoE slow)
|
|
},
|
|
ThemeFire: {
|
|
// Fire damage spells
|
|
31013140, // Scaled Fireball (direct damage)
|
|
31008413, // Scaled Fire Nova (AoE damage)
|
|
31011564, // Scaled Flame Strike (ground effect)
|
|
},
|
|
ThemeNature: {
|
|
// Nature/Lightning spells
|
|
31000421, // Scaled Lightning Bolt (instant damage)
|
|
31008050, // Scaled Earth Shock (damage + interrupt)
|
|
31010414, // Scaled Entangling Roots (root effect)
|
|
},
|
|
ThemeShadow: {
|
|
// Shadow/Dark magic spells
|
|
31000980, // Scaled Shadow Bolt (shadow damage)
|
|
31007648, // Scaled Drain Life (damage + heal)
|
|
31011671, // Scaled Shadow Word: Pain (DoT)
|
|
},
|
|
ThemeArcane: {
|
|
// Arcane magic spells
|
|
31005143, // Scaled Arcane Missiles (channeled damage)
|
|
31001953, // Scaled Blink (teleport)
|
|
31000118, // Scaled Polymorph (crowd control)
|
|
},
|
|
ThemeHoly: {
|
|
// Holy/Light spells
|
|
31000139, // Scaled Heal (direct healing)
|
|
31010060, // Scaled Flash of Light (fast heal)
|
|
31001244, // Scaled Divine Favor (buff)
|
|
},
|
|
}
|
|
|
|
spells, exists := scaledSpellPools[theme]
|
|
if !exists || len(spells) == 0 {
|
|
return 0, fmt.Errorf("no scaled spells available for theme %s", theme.String())
|
|
}
|
|
|
|
// For weapons, prefer damage spells; for trinkets/jewelry, prefer utility
|
|
if itemClass == 2 { // Weapons
|
|
// Return first damage spell (they're ordered by preference)
|
|
return spells[0], nil
|
|
}
|
|
|
|
// For trinkets/jewelry, prefer utility spells (later in list)
|
|
if len(spells) > 1 {
|
|
return spells[len(spells)-1], nil
|
|
}
|
|
|
|
return spells[0], nil
|
|
}
|
|
|
|
// findBaseThematicSpell looks in spell_dbc for base spells that match the theme
|
|
func (tsa *ThematicSpellAssigner) findBaseThematicSpell(theme SpellTheme, itemClass, itemSubclass int) (int, error) {
|
|
// Define base spell pools for each theme
|
|
baseSpellPools := map[SpellTheme][]int{
|
|
ThemeFrost: {
|
|
13439, // Frostbolt
|
|
21401, // Frost Nova
|
|
12737, // Frost Armor
|
|
11113, // Blast Wave (Frost)
|
|
},
|
|
ThemeFire: {
|
|
13140, // Fireball
|
|
8413, // Fire Nova
|
|
11564, // Flame Strike
|
|
11129, // Combustion
|
|
},
|
|
ThemeNature: {
|
|
421, // Lightning Bolt
|
|
8050, // Earth Shock
|
|
10414, // Entangling Roots
|
|
16689, // Nature's Grasp
|
|
},
|
|
ThemeShadow: {
|
|
980, // Shadow Bolt
|
|
7648, // Drain Life
|
|
11671, // Shadow Word: Pain
|
|
18265, // Siphon Soul
|
|
},
|
|
ThemeArcane: {
|
|
5143, // Arcane Missiles
|
|
1953, // Blink
|
|
118, // Polymorph
|
|
12051, // Evocation
|
|
},
|
|
ThemeHoly: {
|
|
139, // Heal
|
|
1244, // Divine Favor
|
|
10060, // Flash of Light
|
|
10308, // Hammer of Justice
|
|
},
|
|
}
|
|
|
|
spells, exists := baseSpellPools[theme]
|
|
if !exists || len(spells) == 0 {
|
|
return 0, fmt.Errorf("no base spells available for theme %s", theme.String())
|
|
}
|
|
|
|
// Verify spell exists in database
|
|
for _, spellId := range spells {
|
|
_, err := tsa.db.GetSpell(spellId)
|
|
if err == nil {
|
|
return spellId, nil
|
|
}
|
|
}
|
|
|
|
return 0, fmt.Errorf("no valid base spells found for theme %s", theme.String())
|
|
}
|
|
|
|
// AssignThematicSpell assigns a thematic spell to an item if appropriate
|
|
func (tsa *ThematicSpellAssigner) AssignThematicSpell(itemEntry, itemClass, itemSubclass, itemLevel, quality int, itemName string) (int, error) {
|
|
// Check if item should have a proc
|
|
if !tsa.ShouldHaveProc(itemClass, itemSubclass, itemLevel, quality, itemName) {
|
|
return 0, fmt.Errorf("item does not qualify for proc assignment")
|
|
}
|
|
|
|
// Detect theme from name
|
|
theme := tsa.DetectThemeFromName(itemName)
|
|
if theme == ThemeNone {
|
|
// For weapons without clear theme, don't assign a spell
|
|
return 0, fmt.Errorf("no clear theme detected for item: %s", itemName)
|
|
}
|
|
|
|
// Find appropriate spell
|
|
spellId, err := tsa.FindThematicSpell(theme, itemClass, itemSubclass)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to find thematic spell: %v", err)
|
|
}
|
|
|
|
log.Printf("Assigned spell %d (theme: %s) to item %s (%d)", spellId, theme.String(), itemName, itemEntry)
|
|
return spellId, nil
|
|
}
|