added high level item lookups, full refactors of database side of things and regernated end game items

This commit is contained in:
2024-08-24 01:44:48 -04:00
parent abdcde30a3
commit 16c1a79ee6
22 changed files with 732 additions and 444 deletions

View File

@@ -0,0 +1,114 @@
package main
import (
"database/sql"
"fmt"
"slices"
"log"
"os"
"strconv"
"strings"
"github.com/araxiaonline/endgame-item-generator/internal/db/mysql"
"github.com/araxiaonline/endgame-item-generator/internal/items"
"github.com/joho/godotenv"
_ "github.com/mattn/go-sqlite3"
)
type EndGameItem struct {
Entry int `db:"entry"`
Class int `db:"class"`
Subclass int `db:"subclass"`
StatsList string `db:"stats_list"`
}
func createItemsTable(db *sql.DB) {
createItems := `CREATE TABLE IF NOT EXISTS items (
entry int unsigned NOT NULL DEFAULT '0',
class tinyint unsigned NOT NULL DEFAULT '0',
name varchar(250) NOT NULL DEFAULT '',
Quality int unsigned NOT NULL DEFAULT '0',
itemLevel int unsigned NOT NULL DEFAULT '0',
subclass tinyint unsigned NOT NULL DEFAULT '0',
stats_list varchar(250) NOT NULL DEFAULT '',
PRIMARY KEY (entry)
)`
_, err := db.Exec(createItems)
if err != nil {
log.Fatal(err)
}
}
func ConvertIntSliceToString(slice []int) string {
sliceStr := make([]string, len(slice))
for i, v := range slice {
sliceStr[i] = strconv.Itoa(v)
}
return strings.Join(sliceStr, ",")
}
func main() {
godotenv.Load("../../.env")
liteDb, err := sql.Open("sqlite3", "./items.db")
if err != nil {
log.Fatal(err)
}
mysqlDb, err := mysql.Connect(&mysql.MySqlConfig{
Host: os.Getenv("DB_HOST"),
User: os.Getenv("DB_USER"),
Password: os.Getenv("DB_PASSWORD"),
Database: os.Getenv("DB_NAME"),
})
if err != nil {
log.Fatal(err)
os.Exit(1)
}
defer liteDb.Close()
defer mysqlDb.Close()
// create the items table if it doesnt exist
createItemsTable(liteDb)
// create the endgames items lookup table locally for items 200 and above
var dbItems []mysql.DbItem
sql := `
SELECT ` + mysql.GetItemFields("") + `
from acore_world.item_template
where ItemLevel >= 200 and Quality >= 3 and ItemLevel < 290
AND name NOT LIKE 'NPC Equip%' and name NOT LIKE 'OLD%'
AND name NOT LIKE '%(test)%' AND name NOT LIKE '%Deprecated%'
AND name NOT LIKE '%Monster - %'
AND ((class = 2 and subclass IN(0,1,2,3,4,5,6,7,8,10,11,12,13,15,16,17,18,19)) or ((class = 4 AND subclass IN (0,1,2,3,4,6))))
`
err = mysqlDb.Select(&dbItems, sql)
if err != nil {
log.Printf("Failed to get items: %v", err)
}
for _, dbItem := range dbItems {
item := items.ItemFromDbItem(dbItem)
var statsList []int
for i := 1; i <= 10; i++ {
val, _ := item.GetField(fmt.Sprintf("StatValue%v", i))
statId, _ := item.GetField(fmt.Sprintf("StatType%v", i))
if val != 0 {
statsList = append(statsList, statId)
}
}
slices.Sort(statsList)
statsListStr := ConvertIntSliceToString(statsList)
log.Printf("StatList %s for Item %v", statsListStr, item.Name)
_, err = liteDb.Exec("INSERT OR IGNORE INTO items (entry, class, name, Quality, itemLevel, subclass, stats_list) VALUES (?, ?, ?, ?, ?,?,?)", item.Entry, *item.Class, item.Name, *item.Quality, *item.ItemLevel, *item.Subclass, statsListStr)
if err != nil {
log.Printf("Failed to insert item %v: %v", item.Entry, err)
}
}
log.Printf("Items: %v", len(dbItems))
}

BIN
data/items.db Normal file

Binary file not shown.

BIN
endgame-item-generator Executable file

Binary file not shown.

View File

@@ -86,7 +86,7 @@ var StatModifiers = map[int]float64{
42: 0.5, // ITEM_MOD_SPELL_DAMAGE_DONE
43: 2.5, // ITEM_MOD_MANA_REGENERATION
44: 1.0, // ITEM_MOD_ARMOR_PENETRATION_RATING
45: 0.5, // ITEM_MOD_SPELL_POWER
45: 0.50, // 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

View File

@@ -1,8 +0,0 @@
package creatures
type Boss struct {
Entry int
Name string
ScriptName string `db:"ScriptName"`
ExperienceModifier int `db:"ExperienceModifier"`
}

View File

@@ -3,18 +3,22 @@ package mysql
import (
"errors"
"fmt"
"github.com/araxiaonline/endgame-item-generator/internal/creatures"
"github.com/araxiaonline/endgame-item-generator/internal/items"
)
func (db *MySqlDb) GetBosses(mapId int) ([]creatures.Boss, error) {
type Boss struct {
Entry int
Name string
ScriptName string `db:"ScriptName"`
ExperienceModifier int `db:"ExperienceModifier"`
}
func (db *MySqlDb) GetBosses(mapId int) ([]Boss, error) {
if mapId == 0 {
return nil, errors.New("mapId cannot be 0")
}
bosses := []creatures.Boss{}
bosses := []Boss{}
var sql string
// 540 is pre-classic dungeons so XP Multiplier is best way to determine bosses / rare mobs
@@ -38,13 +42,13 @@ func (db *MySqlDb) GetBosses(mapId int) ([]creatures.Boss, error) {
return bosses, nil
}
func (db *MySqlDb) GetBossLoot(bossId int) ([]items.Item, error) {
func (db *MySqlDb) GetBossLoot(bossId int) ([]DbItem, error) {
if bossId == 0 {
return nil, errors.New("bossId cannot be 0")
}
// This will first find items that are not in the reference boss loot table
items := []items.Item{}
items := []DbItem{}
fields := GetItemFields("")
sql := `
SELECT ` + fields + `
@@ -77,7 +81,7 @@ func (db *MySqlDb) GetBossLoot(bossId int) ([]items.Item, error) {
return items, nil
}
refItems := []items.Item{}
refItems := []DbItem{}
// For each reference we now need to get the items and add them to the items slice
for _, ref := range references {

View File

@@ -2,25 +2,100 @@ package mysql
import (
"fmt"
"github.com/araxiaonline/endgame-item-generator/internal/items"
)
func (db *MySqlDb) GetItem(entry int) (items.Item, error) {
type DbItem struct {
Entry int
Name string
DisplayId int `db:"displayid"`
Quality *int
ItemLevel *int `db:"ItemLevel"`
Class *int
Subclass *int
Armor *int `db:"armor"`
Material *int `db:"material"`
InventoryType *int `db:"inventoryType"`
AllowableClass *int `db:"allowableClass"`
AllowableRace *int `db:"allowableRace"`
RequiredSkill *int `db:"requiredSkill"`
RequiredLevel *int `db:"requiredLevel"`
Durability *int `db:"MaxDurability"`
MinDmg1 *float64 `db:"dmg_min1"`
MaxDmg1 *float64 `db:"dmg_max1"`
MinDmg2 *float64 `db:"dmg_min2"`
MaxDmg2 *float64 `db:"dmg_max2"`
DmgType1 *int `db:"dmg_type1"`
DmgType2 *int `db:"dmg_type2"`
Delay *float64
Sheath *int
StatsCount *int `db:"statsCount"`
StatType1 *int `db:"stat_type1"`
StatValue1 *int `db:"stat_value1"`
StatType2 *int `db:"stat_type2"`
StatValue2 *int `db:"stat_value2"`
StatType3 *int `db:"stat_type3"`
StatValue3 *int `db:"stat_value3"`
StatType4 *int `db:"stat_type4"`
StatValue4 *int `db:"stat_value4"`
StatType5 *int `db:"stat_type5"`
StatValue5 *int `db:"stat_value5"`
StatType6 *int `db:"stat_type6"`
StatValue6 *int `db:"stat_value6"`
StatType7 *int `db:"stat_type7"`
StatValue7 *int `db:"stat_value7"`
StatType8 *int `db:"stat_type8"`
StatValue8 *int `db:"stat_value8"`
StatType9 *int `db:"stat_type9"`
StatValue9 *int `db:"stat_value9"`
StatType10 *int `db:"stat_type10"`
StatValue10 *int `db:"stat_value10"`
SpellId1 *int `db:"spellid_1"`
SpellId2 *int `db:"spellid_2"`
SpellId3 *int `db:"spellid_3"`
SpellTrigger1 *int `db:"spelltrigger_1"`
SpellTrigger2 *int `db:"spelltrigger_2"`
SpellTrigger3 *int `db:"spelltrigger_3"`
SocketColor1 *int `db:"socketColor_1"`
SocketContent1 *int `db:"socketContent_1"`
SocketColor2 *int `db:"socketColor_2"`
SocketContent2 *int `db:"socketContent_2"`
SocketColor3 *int `db:"socketColor_3"`
SocketContent3 *int `db:"socketContent_3"`
SocketBonus *int `db:"socketBonus"`
GemProperties *int `db:"GemProperties"`
}
func (db *MySqlDb) GetItem(entry int) (DbItem, error) {
if entry == 0 {
return items.Item{}, fmt.Errorf("entry cannot be 0")
return DbItem{}, fmt.Errorf("entry cannot be 0")
}
item := items.Item{}
item := DbItem{}
sql := "SELECT " + GetItemFields("") + " FROM item_template WHERE entry = ?"
err := db.Get(&item, sql, entry)
if err != nil {
return items.Item{}, err
return DbItem{}, err
}
return item, nil
}
func (db *MySqlDb) GetRarePlusItems(limit, offset int) ([]DbItem, error) {
items := []DbItem{}
sql := "SELECT " + GetItemFields("") + " FROM item_template WHERE Quality >= 3 and Quality <= 5 and (class = 2 or class = 4)"
if limit != 0 && offset != 0 {
sql += fmt.Sprintf("LIMIT %v OFFSET %v", limit, offset)
}
err := db.Select(&items, sql)
if err != nil {
return []DbItem{}, err
}
return items, nil
}
func GetItemFields(prefix string) string {
pre := ""
if prefix != "" {
@@ -49,130 +124,7 @@ func GetItemFields(prefix string) string {
stat_type9, stat_value9,
stat_type10, stat_value10,
spellid_1, spellid_2, spellid_3,
spelltrigger_1, spelltrigger_2, spelltrigger_3`
}
func ItemToSql(item Item, reqLevel int, difficulty int) string {
entryBump := 20000000
spellBump := 30000000
if difficulty == 4 {
entryBump = 21000000
}
if difficulty == 5 {
entryBump = 22000000
}
if *item.Quality == 4 {
spellBump = 31000000
}
if *item.Quality == 5 {
spellBump = 32000000
}
spells := ""
if len(item.Spells) > 0 {
for i, spell := range item.Spells {
spells.
spells += spells.SpellToSql(spell, *item.Quality)
item.UpdateField(fmt.Sprintf("SpellId%v", i), spellBump+spell.ID)
}
}
delete := fmt.Sprintf("DELETE FROM acore_world.item_template WHERE entry = %v;", entryBump+item.Entry)
clone := fmt.Sprintf(`
INSERT INTO acore_world.item_template (
entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount,
BuyPrice, SellPrice, InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel,
RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, RequiredCityRank,
RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount,
stat_type1, stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4,
stat_type5, stat_value5, stat_type6, stat_value6, stat_type7, stat_value7, stat_type8, stat_value8,
stat_type9, stat_value9, stat_type10, stat_value10, ScalingStatDistribution, ScalingStatValue,
dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, nature_res,
frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1,
spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2,
spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2,
spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3,
spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4,
spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5,
spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID,
PageMaterial, startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset,
MaxDurability, area, Map, BagFamily, TotemCategory, socketColor_1, socketContent_1, socketColor_2,
socketContent_2, socketColor_3, socketContent_3, socketBonus, GemProperties, RequiredDisenchantSkill,
ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, FoodType,
minMoneyLoot, maxMoneyLoot, flagsCustom, VerifiedBuild
)
SELECT
entry + %v, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount,
BuyPrice, SellPrice, InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel,
RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, RequiredCityRank,
RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount,
stat_type1, stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4,
stat_type5, stat_value5, stat_type6, stat_value6, stat_type7, stat_value7, stat_type8, stat_value8,
stat_type9, stat_value9, stat_type10, stat_value10, ScalingStatDistribution, ScalingStatValue,
dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, nature_res,
frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1,
spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2,
spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2,
spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3,
spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4,
spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5,
spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID,
PageMaterial, startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset,
MaxDurability, area, Map, BagFamily, TotemCategory, socketColor_1, socketContent_1, socketColor_2,
socketContent_2, socketColor_3, socketContent_3, socketBonus, GemProperties, RequiredDisenchantSkill,
ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, FoodType,
minMoneyLoot, maxMoneyLoot, flagsCustom, VerifiedBuild
FROM acore_world.item_template as src
WHERE src.entry = %v ON DUPLICATE KEY UPDATE entry = src.entry + %v;
`, entryBump, item.Entry, entryBump)
update := fmt.Sprintf(`
UPDATE acore_world.item_template
SET
Quality = %v,
ItemLevel = %v,
RequiredLevel = %v,
dmg_min1 = %v,
dmg_max1 = %v,
dmg_min2 = %v,
dmg_max2 = %v,
StatsCount = %v,
stat_type1 = %v,
stat_value1 = %v,
stat_type2 = %v,
stat_value2 = %v,
stat_type3 = %v,
stat_value3 = %v,
stat_type4 = %v,
stat_value4 = %v,
stat_type5 = %v,
stat_value5 = %v,
stat_type6 = %v,
stat_value6 = %v,
stat_type7 = %v,
stat_value7 = %v,
stat_type8 = %v,
stat_value8 = %v,
stat_type9 = %v,
stat_value9 = %v,
stat_type10 = %v,
stat_value10 = %v,
spellid_1 = %v,
spellid_2 = %v,
spellid_3 = %v,
RequiredDisenchantSkill = %v,
DisenchantID = %v,
SellPrice = FLOOR(100000 + (RAND() * 400001)),
Armor = %v
WHERE entry = %v;
`, *item.Quality, *item.ItemLevel, reqLevel, *item.MinDmg1, *item.MaxDmg1, *item.MinDmg2, *item.MaxDmg2, *item.StatsCount,
*item.StatType1, *item.StatValue1, *item.StatType2, *item.StatValue2, *item.StatType3, *item.StatValue3, *item.StatType4, *item.StatValue4,
*item.StatType5, *item.StatValue5, *item.StatType6, *item.StatValue6, *item.StatType7, *item.StatValue7, *item.StatType8, *item.StatValue8,
*item.StatType9, *item.StatValue9, *item.StatType10, *item.StatValue10, *item.SpellId1, *item.SpellId2, *item.SpellId3, 375,
68, *item.Armor, entryBump+item.Entry)
return fmt.Sprintf("%s %s \n %s \n %s", spells, delete, clone, update)
spelltrigger_1, spelltrigger_2, spelltrigger_3,
socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3,
socketBonus, GemProperties`
}

View File

@@ -2,8 +2,6 @@ package mysql
import (
"fmt"
"github.com/araxiaonline/endgame-item-generator/internal/items"
)
type Dungeon struct {
@@ -104,10 +102,10 @@ func (db *MySqlDb) GetDungeons(expansionId int) ([]Dungeon, error) {
}
// Gets a list of other rare+ items that drop in a specific instance.
func (db *MySqlDb) GetAddlDungeonDrops(instanceId int) ([]items.Item, error) {
func (db *MySqlDb) GetAddlDungeonDrops(instanceId int) ([]DbItem, error) {
fields := items.GetItemFields("it")
var items []items.Item
fields := GetItemFields("it")
var items []DbItem
sql := fmt.Sprintf(`
SELECT `+fields+`
from

View File

@@ -1,6 +1,7 @@
package mysql
import (
"errors"
"os"
_ "github.com/go-sql-driver/mysql"
@@ -41,6 +42,16 @@ func Connect(config *MySqlConfig) (*MySqlDb, error) {
return MySql, nil
}
func (db *MySqlDb) Close() {
db.Close()
func GetDb() (*MySqlDb, error) {
if MySql == nil {
return nil, errors.New("mysql not connected")
}
return MySql, nil
}
func (db *MySqlDb) Close() {
if db.DB != nil {
db.DB.Close()
}
}

View File

@@ -3,22 +3,47 @@ package mysql
import (
"fmt"
"strconv"
"github.com/araxiaonline/endgame-item-generator/internal/spells"
)
func (db *MySqlDb) GetSpell(id int) (spells.Spell, error) {
type DbSpell struct {
ID int `db:"ID"`
Name string `db:"Name_Lang_enUS"`
Description string `db:"Description_Lang_enUS"`
AuraDescription string `db:"AuraDescription_Lang_enUS"`
ProcChance int `db:"ProcChance"`
SpellLevel int `db:"SpellLevel"`
Effect1 int `db:"Effect_1"`
Effect2 int `db:"Effect_2"`
Effect3 int `db:"Effect_3"`
EffectDieSides1 int `db:"EffectDieSides_1"`
EffectDieSides2 int `db:"EffectDieSides_2"`
EffectDieSides3 int `db:"EffectDieSides_3"`
EffectRealPointsPerLevel1 int `db:"EffectRealPointsPerLevel_1"`
EffectRealPointsPerLevel2 int `db:"EffectRealPointsPerLevel_2"`
EffectRealPointsPerLevel3 int `db:"EffectRealPointsPerLevel_3"`
EffectBasePoints1 int `db:"EffectBasePoints_1"`
EffectBasePoints2 int `db:"EffectBasePoints_2"`
EffectBasePoints3 int `db:"EffectBasePoints_3"`
EffectAura1 int `db:"EffectAura_1"`
EffectAura2 int `db:"EffectAura_2"`
EffectAura3 int `db:"EffectAura_3"`
EffectBonusMultiplier1 int `db:"EffectBonusMultiplier_1"`
EffectBonusMultiplier2 int `db:"EffectBonusMultiplier_2"`
EffectBonusMultiplier3 int `db:"EffectBonusMultiplier_3"`
}
func (db *MySqlDb) GetSpell(id int) (DbSpell, error) {
if id == 0 {
return spells.Spell{}, fmt.Errorf("id cannot be 0")
return DbSpell{}, fmt.Errorf("id cannot be 0")
}
spell := spells.Spell{}
spell := DbSpell{}
sql := "SELECT " + GetSpellFields() + " FROM `spell_dbc` WHERE ID = ? -- " + strconv.Itoa(id)
err := db.Get(&spell, sql, id)
if err != nil {
return spells.Spell{}, fmt.Errorf("failed to get spell: %v", err)
return DbSpell{}, fmt.Errorf("failed to get spell: %v", err)
}
return spell, nil
@@ -52,96 +77,3 @@ func GetSpellFields() string {
EffectBonusMultiplier_3
`
}
func SpellToSql(spell spells.Spell, quality int) string {
entryBump := 30000000
if quality == 4 {
entryBump = 31000000
}
if quality == 5 {
entryBump = 32000000
}
insert := fmt.Sprintf(`
INSERT IGNORE INTO acore_world.spell_dbc (
ID, 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
) 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)
update := fmt.Sprintf(`
UPDATE acore_world.spell_dbc
SET EffectBasePoints_1 = %v, EffectBasePoints_2 = %v
WHERE ID = %v;`, spell.EffectBasePoints1, spell.EffectBasePoints2, entryBump+spell.ID)
return fmt.Sprintf("\n %s \n %s \n", insert, update)
}

View File

@@ -0,0 +1,86 @@
package sqlite
import (
"errors"
"fmt"
"log"
"strings"
"github.com/araxiaonline/endgame-item-generator/internal/db/mysql"
)
type HighLevelItem struct {
Entry int `db:"entry"`
Class *int `db:"class"`
Name string `db:"name"`
Quality int `db:"Quality"`
ItemLevel int `db:"itemLevel"`
Subclass *int `db:"subclass"`
StatsList string `db:"stats_list"`
}
func (db *SqlLite) GetItem(entry int) (HighLevelItem, error) {
if entry == 0 {
return HighLevelItem{}, fmt.Errorf("entry cannot be 0")
}
item := HighLevelItem{}
sql := "SELECT " + mysql.GetItemFields("") + " FROM item_template WHERE entry = ?"
err := db.Get(&item, sql, entry)
if err != nil {
return HighLevelItem{}, err
}
return item, nil
}
// This gets a random item that is close in stats type to the lower level items with some randomness
func (db *SqlLite) GetRandItem(class, subclass int, statsList []int, end bool) (HighLevelItem, error) {
rndItem := HighLevelItem{}
var statsTxt string
var err error
var sql string
// if we have a stats_list try to match by that first, if not then just select a random item from the class and subclass
if len(statsList) == 0 {
sql = "SELECT * FROM items WHERE class = ? and subclass = ? ORDER BY RANDOM() LIMIT 1"
err = db.Get(&rndItem, sql, class, subclass)
} else {
// convert the array of ints to a commas string for lookup
statsTxt = intSliceToString(statsList)
sql = "SELECT * FROM items WHERE class = ? and subclass = ? and stats_list like ? ORDER BY RANDOM() LIMIT 1"
err = db.Get(&rndItem, sql, class, subclass, statsTxt+"%")
}
if err != nil {
// if we hit the last check and still no item match then return an error
if end {
msg := fmt.Sprintf("Failed to find a matching item class %v subclass %v statsTxt %v", class, subclass, statsTxt)
return HighLevelItem{}, errors.New(msg)
}
// if there was not a remove the last stat and try again
if err.Error() == "sql: no rows in result set" {
if len(statsList) == 0 {
return db.GetRandItem(class, subclass, statsList, true)
} else {
statsList = statsList[:len(statsList)-1]
return db.GetRandItem(class, subclass, statsList, false)
}
}
log.Fatalf("Error getting random sql: %v error: %v", sql, err)
return HighLevelItem{}, err
}
return rndItem, nil
}
func intSliceToString(slice []int) string {
str := fmt.Sprint(slice)
str = strings.Trim(str, "[]")
return strings.ReplaceAll(str, " ", ",")
}

View File

@@ -1,23 +1,33 @@
package sqlite
import (
"database/sql"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
type SqlLite struct {
client *sql.DB
*sqlx.DB
}
var SqlLiteDb *SqlLite
func Connect(path string) (*SqlLite, error) {
client, err := sql.Open("sqlite3", path)
client, err := sqlx.Open("sqlite3", path)
if err != nil {
return nil, err
}
return &SqlLite{client: client}, nil
SqlLiteDb = &SqlLite{client}
return SqlLiteDb, nil
}
func GetDb() (*SqlLite, error) {
return SqlLiteDb, nil
}
func (db *SqlLite) Close() {
db.client.Close()
if db.DB != nil {
db.DB.Close()
}
}

View File

@@ -181,8 +181,8 @@ func TestGetDPS(t *testing.T) {
name: "Valid DPS calculation",
item: Item{
DbItem: mysql.DbItem{
MinDmg1: ptrInt(50),
MaxDmg1: ptrInt(70),
MinDmg1: ptrFloat64(50),
MaxDmg1: ptrFloat64(70),
Delay: ptrFloat64(3000),
},
},
@@ -193,8 +193,8 @@ func TestGetDPS(t *testing.T) {
name: "High damage DPS calculation",
item: Item{
DbItem: mysql.DbItem{
MinDmg1: ptrInt(100),
MaxDmg1: ptrInt(150),
MinDmg1: ptrFloat64(100),
MaxDmg1: ptrFloat64(150),
Delay: ptrFloat64(2000),
},
},
@@ -205,8 +205,8 @@ func TestGetDPS(t *testing.T) {
name: "Low damage DPS calculation",
item: Item{
DbItem: mysql.DbItem{
MinDmg1: ptrInt(10),
MaxDmg1: ptrInt(15),
MinDmg1: ptrFloat64(10),
MaxDmg1: ptrFloat64(15),
Delay: ptrFloat64(1500),
},
},
@@ -217,7 +217,7 @@ func TestGetDPS(t *testing.T) {
name: "Missing MinDmg1",
item: Item{
DbItem: mysql.DbItem{
MaxDmg1: ptrInt(70),
MaxDmg1: ptrFloat64(70),
Delay: ptrFloat64(3000),
},
},
@@ -228,7 +228,7 @@ func TestGetDPS(t *testing.T) {
name: "Missing MaxDmg1",
item: Item{
DbItem: mysql.DbItem{
MinDmg1: ptrInt(50),
MinDmg1: ptrFloat64(50),
Delay: ptrFloat64(3000),
},
},
@@ -239,8 +239,8 @@ func TestGetDPS(t *testing.T) {
name: "Missing Delay",
item: Item{
DbItem: mysql.DbItem{
MinDmg1: ptrInt(50),
MaxDmg1: ptrInt(70),
MinDmg1: ptrFloat64(50),
MaxDmg1: ptrFloat64(70),
},
},
wantDPS: 0,
@@ -277,8 +277,8 @@ func TestScaleDPS(t *testing.T) {
DbItem: mysql.DbItem{
ItemLevel: ptrInt(60),
Delay: ptrFloat64(3000),
MinDmg1: ptrInt(50),
MaxDmg1: ptrInt(70),
MinDmg1: ptrFloat64(50),
MaxDmg1: ptrFloat64(70),
Subclass: ptrInt(4), // One-handed weapon
Quality: ptrInt(3), // Rare
},
@@ -294,8 +294,8 @@ func TestScaleDPS(t *testing.T) {
DbItem: mysql.DbItem{
ItemLevel: ptrInt(80),
Delay: ptrFloat64(2000),
MinDmg1: ptrInt(150),
MaxDmg1: ptrInt(200),
MinDmg1: ptrFloat64(150),
MaxDmg1: ptrFloat64(200),
Subclass: ptrInt(17), // Two-handed weapon
Quality: ptrInt(4), // Epic
},
@@ -311,8 +311,8 @@ func TestScaleDPS(t *testing.T) {
DbItem: mysql.DbItem{
ItemLevel: ptrInt(20),
Delay: ptrFloat64(1000),
MinDmg1: ptrInt(30),
MaxDmg1: ptrInt(50),
MinDmg1: ptrFloat64(30),
MaxDmg1: ptrFloat64(50),
Subclass: ptrInt(2), // Ranged weapon
Quality: ptrInt(2), // Uncommon
},
@@ -327,8 +327,8 @@ func TestScaleDPS(t *testing.T) {
item: Item{
DbItem: mysql.DbItem{
Delay: ptrFloat64(3000),
MinDmg1: ptrInt(50),
MaxDmg1: ptrInt(70),
MinDmg1: ptrFloat64(50),
MaxDmg1: ptrFloat64(70),
Subclass: ptrInt(4), // One-handed weapon
Quality: ptrInt(3), // Rare
},
@@ -343,8 +343,8 @@ func TestScaleDPS(t *testing.T) {
item: Item{
DbItem: mysql.DbItem{
ItemLevel: ptrInt(60),
MinDmg1: ptrInt(50),
MaxDmg1: ptrInt(70),
MinDmg1: ptrFloat64(50),
MaxDmg1: ptrFloat64(70),
Subclass: ptrInt(4), // One-handed weapon
Quality: ptrInt(3), // Rare
},
@@ -360,10 +360,10 @@ func TestScaleDPS(t *testing.T) {
DbItem: mysql.DbItem{
ItemLevel: ptrInt(60),
Delay: ptrFloat64(3000),
MinDmg1: ptrInt(50),
MaxDmg1: ptrInt(70),
MinDmg2: ptrInt(25),
MaxDmg2: ptrInt(35),
MinDmg1: ptrFloat64(50),
MaxDmg1: ptrFloat64(70),
MinDmg2: ptrFloat64(25),
MaxDmg2: ptrFloat64(35),
Subclass: ptrInt(4), // One-handed weapon
Quality: ptrInt(3), // Rare
},

View File

@@ -33,6 +33,13 @@ type ItemStat struct {
AdjValue float64
}
// Create a new item from the database item
func ItemFromDbItem(dbItem mysql.DbItem) Item {
return Item{
DbItem: dbItem,
}
}
// Get the primary stat for an item (strength, agility, intellect, spirit, stamina)
func (item Item) GetPrimaryStat() (int, int, error) {
var primaryStat int64
@@ -75,6 +82,31 @@ func (item Item) GetPrimaryStat() (int, int, error) {
func (item Item) GetStatList() ([]int, error) {
statList := []int{}
// Also need to get spells that on the item that convert to stats
spells, err := item.GetSpells()
if err != nil {
log.Printf("Failed to get spells for item: %v", err)
return nil, err
}
for _, spell := range spells {
convStats, err := spell.ConvertToStats()
if err != nil {
log.Printf("Failed to convert spell to stats: %v for spell %v", err, spell.Name)
continue
}
if len(convStats) == 0 {
continue
}
for _, convStat := range convStats {
statList = append(statList, convStat.StatType)
}
}
for i := 1; i < 11; i++ {
val, err := item.GetField(fmt.Sprintf("StatValue%v", i))
@@ -190,8 +222,8 @@ func (item *Item) ScaleDPS(level int) (float64, error) {
if item.MinDmg2 != nil && item.MaxDmg2 != nil {
ratioMin := float64(*item.MinDmg2) / float64(*item.MinDmg1)
ratioMax := float64(*item.MaxDmg2) / float64(*item.MaxDmg1)
minimum2 := int(ratioMin * float64(minimum))
maximum2 := int(ratioMax * float64(maximum))
minimum2 := ratioMin * float64(minimum)
maximum2 := ratioMax * float64(maximum)
item.MinDmg2 = &minimum2
item.MaxDmg2 = &maximum2
@@ -199,14 +231,13 @@ func (item *Item) ScaleDPS(level int) (float64, error) {
// In order to balance the original scale of the secondary damage from primary
minimum = minimum - float64(minimum2)*0.75
maximum = maximum - float64(maximum2)*0.75
}
// item.MinDmg1 = &minimum
var min int = int(minimum)
var max int = int(maximum)
item.MinDmg1 = &min
item.MaxDmg1 = &max
// var min int = int(minimum)
// var max int = int(maximum)
item.MinDmg1 = &minimum
item.MaxDmg1 = &maximum
return dps, nil
}
@@ -225,7 +256,7 @@ func (item Item) GetStatPercents(spellStats []spells.ConvItemStat) map[int]*Item
continue
}
adjValue := float64(statValue) / config.StatModifiers[int(statType)]
adjValue := float64(statValue) * config.StatModifiers[int(statType)]
statBudget += adjValue
statMap[int(statType)] = &ItemStat{
Value: int(statValue),
@@ -337,6 +368,42 @@ func (item *Item) GetNonStatSpells() ([]spells.Spell, error) {
return nonStatSpells, nil
}
// Applies status of one item to another overwriting the current stats
func (item *Item) ApplyStats(otherItem Item) (success bool, err error) {
for i := 1; i < 11; i++ {
statType, err := otherItem.GetField(fmt.Sprintf("StatType%v", i))
if err != nil {
return false, err
}
statValue, err := otherItem.GetField(fmt.Sprintf("StatValue%v", i))
if err != nil {
return false, err
}
item.UpdateField(fmt.Sprintf("StatType%v", i), statType)
item.UpdateField(fmt.Sprintf("StatValue%v", i), statValue)
}
if otherItem.SocketColor1 != nil {
item.SocketColor1 = otherItem.SocketColor1
item.SocketContent1 = otherItem.SocketContent1
}
if otherItem.SocketColor2 != nil {
item.SocketColor2 = otherItem.SocketColor2
item.SocketContent2 = otherItem.SocketContent2
}
if otherItem.SocketColor3 != nil {
item.SocketColor3 = otherItem.SocketColor3
item.SocketContent3 = otherItem.SocketContent3
}
return true, nil
}
// Stat Formula scaler
// Ceiling of ((ItemLevel * QualityModifier * ItemTypeModifier)^1.7095 * %ofStats) ^ (1/1.7095)) / StatModifier
// i.e) Green Strength Helmet (((100 * 1.1 * 1.0)^1.705) * 1)^(1/1.7095) / 1.0 = 110 Strength on item
@@ -440,7 +507,7 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) {
item.Spells = []spells.Spell{}
// 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)
// 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)
if err != nil {
log.Printf("Failed to scale spell: %v, Spell %v", err, spell.ID)
@@ -455,7 +522,7 @@ func (item *Item) ScaleItem(itemLevel int, itemQuality int) (bool, error) {
item.UpdateField(fmt.Sprintf("SpellId%v", spell.ItemSpellSlot), newId)
item.Spells = append(item.Spells, spell)
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)
// 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)
}
return true, nil
@@ -601,7 +668,7 @@ func (item *Item) addStats(stats map[int]*ItemStat) {
// 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)*config.QualityModifiers[itemQuality]*config.InvTypeModifiers[itemType]), 1.7095) * percOfStat
scaledUp := (math.Pow((float64(itemLevel)*config.QualityModifiers[itemQuality]*config.InvTypeModifiers[itemType]), 1.7095) * percOfStat)
// leaving modifier off for now but not changing signature in case I need to add it back
_ = statModifier
@@ -719,6 +786,14 @@ func ItemToSql(item Item, reqLevel int, difficulty int) string {
spellid_1 = %v,
spellid_2 = %v,
spellid_3 = %v,
socketColor_1 = %v,
socketContent_1 = %v,
socketColor_2 = %v,
socketContent_2 = %v,
socketColor_3 = %v,
socketContent_3 = %v,
socketBonus = %v,
GemProperties = %v,
RequiredDisenchantSkill = %v,
DisenchantID = %v,
SellPrice = FLOOR(100000 + (RAND() * 400001)),
@@ -727,8 +802,9 @@ func ItemToSql(item Item, reqLevel int, difficulty int) string {
`, *item.Quality, *item.ItemLevel, reqLevel, *item.MinDmg1, *item.MaxDmg1, *item.MinDmg2, *item.MaxDmg2, *item.StatsCount,
*item.StatType1, *item.StatValue1, *item.StatType2, *item.StatValue2, *item.StatType3, *item.StatValue3, *item.StatType4, *item.StatValue4,
*item.StatType5, *item.StatValue5, *item.StatType6, *item.StatValue6, *item.StatType7, *item.StatValue7, *item.StatType8, *item.StatValue8,
*item.StatType9, *item.StatValue9, *item.StatType10, *item.StatValue10, *item.SpellId1, *item.SpellId2, *item.SpellId3, 375,
68, *item.Armor, entryBump+item.Entry)
*item.StatType9, *item.StatValue9, *item.StatType10, *item.StatValue10, *item.SpellId1, *item.SpellId2, *item.SpellId3, *item.SocketColor1, *item.SocketContent1,
*item.SocketColor2, *item.SocketContent2, *item.SocketColor3, *item.SocketContent3, *item.SocketBonus, *item.GemProperties,
375, 68, *item.Armor, entryBump+item.Entry)
return fmt.Sprintf("%s %s \n %s \n %s", spellList, delete, clone, update)
}

View File

@@ -7,6 +7,7 @@ import (
"strings"
"github.com/araxiaonline/endgame-item-generator/internal/config"
"github.com/araxiaonline/endgame-item-generator/internal/db/mysql"
"github.com/thoas/go-funk"
)
@@ -27,6 +28,7 @@ var SpellAuraEffects = [...]int{
13, // Modifies Spell Damage Done
15, // Modifies Damage Shield
22, // Modifies Resistance
29, // Modifies Strength
34, // Modifies HEalth
85, // Modifies Mana Regen
99, // Modifies Attack Power
@@ -39,6 +41,7 @@ var SpellAuraEffects = [...]int{
var AuraEffectsStatMap = map[int]int{
8: 46,
13: 45,
29: 4,
85: 43,
99: 38,
124: 38,
@@ -93,34 +96,10 @@ type SpellEffect struct {
CalculatedMax int
}
// DB Mapping from spell_dbc
type Spell struct {
ID int `db:"ID"`
Name string `db:"Name_Lang_enUS"`
Description string `db:"Description_Lang_enUS"`
AuraDescription string `db:"AuraDescription_Lang_enUS"`
ProcChance int `db:"ProcChance"`
SpellLevel int `db:"SpellLevel"`
Effect1 int `db:"Effect_1"`
Effect2 int `db:"Effect_2"`
Effect3 int `db:"Effect_3"`
EffectDieSides1 int `db:"EffectDieSides_1"`
EffectDieSides2 int `db:"EffectDieSides_2"`
EffectDieSides3 int `db:"EffectDieSides_3"`
EffectRealPointsPerLevel1 int `db:"EffectRealPointsPerLevel_1"`
EffectRealPointsPerLevel2 int `db:"EffectRealPointsPerLevel_2"`
EffectRealPointsPerLevel3 int `db:"EffectRealPointsPerLevel_3"`
EffectBasePoints1 int `db:"EffectBasePoints_1"`
EffectBasePoints2 int `db:"EffectBasePoints_2"`
EffectBasePoints3 int `db:"EffectBasePoints_3"`
EffectAura1 int `db:"EffectAura_1"`
EffectAura2 int `db:"EffectAura_2"`
EffectAura3 int `db:"EffectAura_3"`
EffectBonusMultiplier1 int `db:"EffectBonusMultiplier_1"`
EffectBonusMultiplier2 int `db:"EffectBonusMultiplier_2"`
EffectBonusMultiplier3 int `db:"EffectBonusMultiplier_3"`
ItemSpellSlot int
Scaled bool
mysql.DbSpell
Scaled bool
ItemSpellSlot int
}
func calcMaxValue(base int, sides int) int {
@@ -225,14 +204,13 @@ func (s Spell) HasAuraEffect() bool {
}
func AuraEffectCanBeConv(effect int) bool {
statMods := [...]int{8, 13, 22, 34, 85, 99, 124, 135, 189}
return funk.Contains(statMods, effect)
return funk.Contains(SpellAuraEffects, effect)
}
// Lookup details about the effect and return the stat type -1 indicates not found
func convertAuraEffect(effect int) int {
if !funk.Contains(AuraEffectsStatMap, effect) {
log.Printf("effect %v not found in SpellEffectStatMap skipping", effect)
// log.Printf("effect %v not found in SpellEffectStatMap skipping", effect)
return -1
}
@@ -251,8 +229,8 @@ func (s Spell) ConvertToStats() ([]ConvItemStat, error) {
if s.ID == 9397 {
log.Printf("Spell: %v AuraEffect1: %v AuraEffect2: %v AuraEffect3: %v", s.Name, s.EffectAura1, s.EffectAura2, s.EffectAura3)
}
var seen []int
for _, e := range effects {
if !AuraEffectCanBeConv(e.Effect) {
@@ -282,14 +260,14 @@ func (s Spell) ConvertToStats() ([]ConvItemStat, error) {
// Handle special stat case where 189 is catch all for crit, dodge, parry, hit, haste, expertise
if s.Effect1 != 0 && s.Effect1 == 6 && (s.EffectAura1 == 189 || s.EffectAura1 == 123) {
log.Printf("Special case for spell aura effect: %v", s.Description)
// log.Printf("Special case for spell aura effect: %v", s.Description)
statId := parseStatDesc(s.Description)
if statId == 0 {
log.Printf("Could not determine stat for spell aura effect description: %v", s.Name)
}
// if statId == 0 {
// // log.Printf("Could not determine stat for spell aura effect description: %v", s.Name)
// }
calced := calcMaxValue(s.EffectBasePoints1, s.EffectDieSides1)
log.Printf("StatId: %v Calced: %v", statId, calced)
// log.Printf("StatId: %v Calced: %v", statId, calced)
stats = append(stats, ConvItemStat{
StatType: statId,
StatValue: calced,
@@ -297,6 +275,12 @@ func (s Spell) ConvertToStats() ([]ConvItemStat, error) {
})
}
// if len(stats) == 0 {
// // log.Printf("Failed to Convert Spell to Stats: %v AuraEffect1: %v AuraEffect2: %v AuraEffect3: %v", s.Name, s.EffectAura1, s.EffectAura2, s.EffectAura3)
// } else {
// // log.Printf("Converted Spell to Stats: %v AuraEffect1: %v AuraEffect2: %v AuraEffect3: %v", s.Name, s.EffectAura1, s.EffectAura2, s.EffectAura3)
// }
return stats, nil
}
@@ -447,3 +431,96 @@ func (s *Spell) ScaleSpell(fromItemLevel int, itemLevel int, itemQuality int) (i
s.Scaled = true
return idBump + s.ID, nil
}
func SpellToSql(spell Spell, quality int) string {
entryBump := 30000000
if quality == 4 {
entryBump = 31000000
}
if quality == 5 {
entryBump = 32000000
}
insert := fmt.Sprintf(`
INSERT IGNORE INTO acore_world.spell_dbc (
ID, 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
) 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)
update := fmt.Sprintf(`
UPDATE acore_world.spell_dbc
SET EffectBasePoints_1 = %v, EffectBasePoints_2 = %v
WHERE ID = %v;`, spell.EffectBasePoints1, spell.EffectBasePoints2, entryBump+spell.ID)
return fmt.Sprintf("\n %s \n %s \n", insert, update)
}

View File

@@ -0,0 +1,60 @@
package spells
import (
"testing"
"github.com/araxiaonline/endgame-item-generator/internal/db/mysql"
)
func TestCanBeConverted(t *testing.T) {
tests := []struct {
name string
spell Spell
expected bool
}{
{
name: "Spell with non-aura effect",
spell: Spell{
DbSpell: mysql.DbSpell{
Effect1: 1,
},
},
expected: false,
},
{
name: "Spell with aura effect that can be converted",
spell: Spell{
DbSpell: mysql.DbSpell{
EffectAura1: 8,
},
},
expected: true,
},
{
name: "Spell with mixed effects",
spell: Spell{
DbSpell: mysql.DbSpell{
Effect1: 1,
EffectAura1: 8,
},
},
expected: false,
},
{
name: "Spell with no effects",
spell: Spell{
DbSpell: mysql.DbSpell{},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.spell.CanBeConverted()
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}

95
main.go
View File

@@ -6,6 +6,10 @@ import (
"log"
"os"
"github.com/araxiaonline/endgame-item-generator/internal/db/mysql"
"github.com/araxiaonline/endgame-item-generator/internal/db/sqlite"
"github.com/araxiaonline/endgame-item-generator/internal/items"
_ "github.com/go-sql-driver/mysql"
"github.com/joho/godotenv"
)
@@ -17,23 +21,98 @@ func main() {
// database.models.Connect()
debug := flag.Bool("debug", false, "Enable verbose logging inside generator")
// itemLevel := flag.Int("ilvl", 305, "Specify the item level to start scaling from, expansion and difficulty modifiers scale up.")
// difficulty := flag.Int("difficulty", 3, "set the difficulty of the dungeon, defaults to 3 (mythic) 4 (legendary) 5 (ascendant)")
itemLevel := flag.Int("ilvl", 305, "Specify the item level to start scaling from, expansion and difficulty modifiers scale up.")
difficulty := flag.Int("difficulty", 3, "set the difficulty of the dungeon, defaults to 3 (mythic) 4 (legendary) 5 (ascendant)")
// levelUp := flag.Bool("levelUp", false, "Boss items require higher +1 level to equip, defaults to false")
// baselevel := flag.Int("baselevel", 80, "set the base level for items to be used, defaults to 80 this is required for levelUp flag")
baselevel := flag.Int("baselevel", 80, "set the base level for items to be used, defaults to 80 this is required for levelUp flag")
flag.Parse()
if difficulty == nil || *difficulty < 3 || *difficulty > 5 {
log.Fatal("difficulty must be between 3-5")
os.Exit(1)
}
if itemLevel == nil || *itemLevel < 280 {
log.Fatal("item level must be greater than 280")
os.Exit(1)
}
if baselevel == nil || *baselevel < 0 {
log.Fatal("base level must be greater than 80")
os.Exit(1)
}
if *debug {
log.SetOutput(os.Stdout)
} else {
log.SetOutput(io.Discard)
}
// if difficulty == nil || *difficulty < 3 || *difficulty > 5 {
// log.Fatal("difficulty must be between 3-5")
// os.Exit(1)
// }
// Connect to Mysql
mysqlDb, err := mysql.Connect(&mysql.MySqlConfig{
Host: os.Getenv("DB_HOST"),
User: os.Getenv("DB_USER"),
Password: os.Getenv("DB_PASSWORD"),
Database: os.Getenv("DB_NAME"),
})
if err != nil {
log.Fatal(err)
}
// Connect to SqlList for EndGame Mapping
sqliteDb, err := sqlite.Connect("./data/items.db")
if err != nil {
log.Fatal(err)
}
rareItems, err := mysqlDb.GetRarePlusItems(0, 0)
if err != nil {
log.Fatal(err)
}
for itr, dbItem := range rareItems {
item := items.ItemFromDbItem(dbItem)
statsList, err := item.GetStatList()
if err != nil {
log.Fatal(err)
continue
}
log.Printf("Item: %v Entry: %v StatsList: %v\n", item.Name, item.Entry, statsList)
rndItem, err := sqliteDb.GetRandItem(*item.Class, *item.Subclass, statsList, false)
if err != nil {
log.Fatal(err)
continue
}
if rndItem == (sqlite.HighLevelItem{}) {
log.Fatalf("Failed to get random item for %v Entry: %v\n", item.Name, item.Entry)
}
log.Printf("Random Item: %v Entry: %v\n", rndItem.Name, rndItem.Entry)
highLevelItem, err := mysqlDb.GetItem(rndItem.Entry)
if err != nil {
log.Fatal(err)
continue
}
// Print all the status for the item that was copied
log.Printf("Item Name: %v Stat1: %v Stat2: %v Stat3: %v Stat4: %v Stat5: %v Stat6: %v Stat7: %v Stat8: %v \n",
item.Entry, *item.StatValue1, *item.StatValue2, *item.StatValue3, *item.StatValue4, *item.StatValue5, *item.StatValue6, *item.StatValue7, *item.StatValue8)
item.ApplyStats(items.ItemFromDbItem(highLevelItem))
item.ScaleItem(*itemLevel, 4)
log.Printf("Item Name: %v Stat1: %v Stat2: %v Stat3: %v Stat4: %v Stat5: %v Stat6: %v Stat7: %v Stat8: %v \n",
item.Name, *item.StatValue1, *item.StatValue2, *item.StatValue3, *item.StatValue4, *item.StatValue5, *item.StatValue6, *item.StatValue7, *item.StatValue8)
if itr > 100 {
break
}
}
// // main loop
// dungeons, err := models.DB.GetDungeons(-1)

Binary file not shown.

View File

@@ -1,103 +0,0 @@
// package main
// import (
// "database/sql"
// "fmt"
// "log"
// "slices"
// "strconv"
// "strings"
// // "github.com/araxiaonline/endgame-item-generator/internal/models"
// "github.com/araxiaonline/endgame-item-generator/internal/utils"
// "github.com/joho/godotenv"
// _ "github.com/mattn/go-sqlite3"
// )
// type EndGameItem struct {
// Entry int `db:"entry"`
// Class int `db:"class"`
// Subclass int `db:"subclass"`
// StatsList string `db:"stats_list"`
// }
// func createItemsTable(db *sql.DB) {
// createItems := `CREATE TABLE IF NOT EXISTS items (
// entry int unsigned NOT NULL DEFAULT '0',
// class tinyint unsigned NOT NULL DEFAULT '0',
// name varchar(250) NOT NULL DEFAULT '',
// Quality int unsigned NOT NULL DEFAULT '0',
// itemLevel int unsigned NOT NULL DEFAULT '0',
// subclass tinyint unsigned NOT NULL DEFAULT '0',
// stats_list varchar(250) NOT NULL DEFAULT '',
// PRIMARY KEY (entry)
// )`
// _, err := db.Exec(createItems)
// if err != nil {
// log.Fatal(err)
// }
// }
// func ConvertIntSliceToString(slice []int) string {
// sliceStr := make([]string, len(slice))
// for i, v := range slice {
// sliceStr[i] = strconv.Itoa(v)
// }
// return strings.Join(sliceStr, ",")
// }
// func main() {
// liteDb, err := sql.Open("sqlite3", "./items.db")
// if err != nil {
// log.Fatal(err)
// }
// godotenv.Load("../../.env")
// models.Connect()
// sqlDb := models.DB.Client()
// defer liteDb.Close()
// defer sqlDb.Close()
// // create the items table if it doesnt exist
// createItemsTable(liteDb)
// // create the endgames items lookup table locally for items 200 and above
// var items []models.Item
// sql := `
// SELECT ` + utils.GetItemFields("") + `
// from acore_world.item_template
// where ItemLevel >= 200 and Quality >= 3 and ItemLevel < 290
// AND name NOT LIKE 'NPC Equip%' and name NOT LIKE 'OLD%'
// AND name NOT LIKE '%(test)%' AND name NOT LIKE '%Deprecated%'
// AND name NOT LIKE '%Monster - %'
// AND ((class = 2 and subclass IN(0,1,2,3,4,5,6,7,8,10,11,12,13,15,16,17,18,19)) or ((class = 4 AND subclass IN (1,2,3,4,6))))
// `
// err = sqlDb.Select(&items, sql)
// if err != nil {
// log.Printf("Failed to get items: %v", err)
// }
// for _, item := range items {
// var statsList []int
// for i := 1; i <= 10; i++ {
// val, _ := item.GetField(fmt.Sprintf("StatValue%v", i))
// statId, _ := item.GetField(fmt.Sprintf("StatType%v", i))
// if val != 0 {
// statsList = append(statsList, statId)
// }
// }
// slices.Sort(statsList)
// statsListStr := ConvertIntSliceToString(statsList)
// log.Printf("StatList %s for Item %v", statsListStr, item.Name)
// _, err = liteDb.Exec("INSERT OR IGNORE INTO items (entry, class, name, Quality, itemLevel, subclass, stats_list) VALUES (?, ?, ?, ?, ?,?,?)", item.Entry, *item.Class, item.Name, *item.Quality, *item.ItemLevel, *item.Subclass, statsListStr)
// if err != nil {
// log.Printf("Failed to insert item %v: %v", item.Entry, err)
// }
// }
// log.Printf("Items: %v", len(items))
// }