mirror of
https://github.com/araxiaonline/wow-item-generator.git
synced 2026-06-13 03:02:22 -04:00
Major changes and refactor of all code to break things out more and add in more testing
This commit is contained in:
4
go.mod
4
go.mod
@@ -2,13 +2,13 @@ module github.com/araxiaonline/endgame-item-generator
|
||||
|
||||
go 1.22.4
|
||||
|
||||
replace github.com/araxiaonline/endgame-item-generator/models => ../models
|
||||
|
||||
require github.com/go-sql-driver/mysql v1.8.1
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/thoas/go-funk v0.9.3 // indirect
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
|
||||
)
|
||||
|
||||
3
go.sum
3
go.sum
@@ -8,11 +8,14 @@ github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
|
||||
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package models
|
||||
package creatures
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/araxiaonline/endgame-item-generator/utils"
|
||||
"github.com/araxiaonline/endgame-item-generator/import/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/jmoiron/sqlx"
|
||||
)
|
||||
450
internal/items/item_test.go
Normal file
450
internal/items/item_test.go
Normal file
@@ -0,0 +1,450 @@
|
||||
package items
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
func TestGetPrimaryStat(t *testing.T) {
|
||||
originalLog := log.Writer()
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(originalLog)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
wantStat int
|
||||
wantValue int
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "No primary stat found",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(1), StatValue1: ptrInt(10),
|
||||
StatType2: ptrInt(2), StatValue2: ptrInt(20),
|
||||
StatType3: ptrInt(12), StatValue3: ptrInt(15),
|
||||
},
|
||||
wantStat: 0,
|
||||
wantValue: 0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Primary stat found with higher value",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(3), StatValue1: ptrInt(10), // Agility
|
||||
StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength
|
||||
StatType3: ptrInt(5), StatValue3: ptrInt(15), // Intellect
|
||||
},
|
||||
wantStat: 4, // Strength
|
||||
wantValue: 20,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Primary stat found with lower value",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(3), StatValue1: ptrInt(30), // Agility
|
||||
StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength
|
||||
StatType3: ptrInt(5), StatValue3: ptrInt(15), // Intellect
|
||||
},
|
||||
wantStat: 3, // Agility
|
||||
wantValue: 30,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotStat, gotValue, err := tt.item.GetPrimaryStat()
|
||||
if (err != nil) != tt.expectError {
|
||||
t.Errorf("GetPrimaryStat() error = %v, expectError %v", err, tt.expectError)
|
||||
return
|
||||
}
|
||||
if gotStat != tt.wantStat {
|
||||
t.Errorf("GetPrimaryStat() gotStat = %v, want %v", gotStat, tt.wantStat)
|
||||
}
|
||||
if gotValue != tt.wantValue {
|
||||
t.Errorf("GetPrimaryStat() gotValue = %v, want %v", gotValue, tt.wantValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStatList(t *testing.T) {
|
||||
originalLog := log.Writer()
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(originalLog)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
want []int
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "No stats available",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(0), StatValue1: ptrInt(0),
|
||||
StatType2: ptrInt(0), StatValue2: ptrInt(0),
|
||||
},
|
||||
want: []int{},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Multiple stats available",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(3), StatValue1: ptrInt(10), // Agility
|
||||
StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength
|
||||
StatType3: ptrInt(5), StatValue3: ptrInt(15), // Intellect
|
||||
},
|
||||
want: []int{3, 4, 5},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Stats are ordered correctly",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(7), StatValue1: ptrInt(10), // Agility
|
||||
StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength
|
||||
StatType3: ptrInt(31), StatValue3: ptrInt(15), // Intellect
|
||||
},
|
||||
want: []int{4, 7, 31},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Some stats are zero",
|
||||
item: Item{
|
||||
Entry: 1,
|
||||
Name: "Test Item",
|
||||
StatType1: ptrInt(3), StatValue1: ptrInt(0), // Agility
|
||||
StatType2: ptrInt(4), StatValue2: ptrInt(20), // Strength
|
||||
StatType3: ptrInt(5), StatValue3: ptrInt(0), // Intellect
|
||||
},
|
||||
want: []int{4},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.item.GetStatList()
|
||||
if (err != nil) != tt.expectError {
|
||||
t.Errorf("GetStatList() error = %v, expectError %v", err, tt.expectError)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetStatList() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDPS(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
wantDPS float64
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Valid DPS calculation",
|
||||
item: Item{
|
||||
MinDmg1: ptrInt(50),
|
||||
MaxDmg1: ptrInt(70),
|
||||
Delay: ptrFloat64(3000),
|
||||
},
|
||||
wantDPS: 20.00,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "High damage DPS calculation",
|
||||
item: Item{
|
||||
MinDmg1: ptrInt(100),
|
||||
MaxDmg1: ptrInt(150),
|
||||
Delay: ptrFloat64(2000),
|
||||
},
|
||||
wantDPS: 62.50,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Low damage DPS calculation",
|
||||
item: Item{
|
||||
MinDmg1: ptrInt(10),
|
||||
MaxDmg1: ptrInt(15),
|
||||
Delay: ptrFloat64(1500),
|
||||
},
|
||||
wantDPS: 8.33,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Missing MinDmg1",
|
||||
item: Item{
|
||||
MaxDmg1: ptrInt(70),
|
||||
Delay: ptrFloat64(3000),
|
||||
},
|
||||
wantDPS: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing MaxDmg1",
|
||||
item: Item{
|
||||
MinDmg1: ptrInt(50),
|
||||
Delay: ptrFloat64(3000),
|
||||
},
|
||||
wantDPS: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing Delay",
|
||||
item: Item{
|
||||
MinDmg1: ptrInt(50),
|
||||
MaxDmg1: ptrInt(70),
|
||||
},
|
||||
wantDPS: 0,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotDPS, err := tt.item.GetDPS()
|
||||
if (err != nil) != tt.expectError {
|
||||
t.Errorf("GetDPS() error = %v, expectError %v", err, tt.expectError)
|
||||
return
|
||||
}
|
||||
if !tt.expectError && !almostEqual(gotDPS, tt.wantDPS, 0.01) {
|
||||
t.Errorf("GetDPS() = %v, want %v", gotDPS, tt.wantDPS)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScaleDPS(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
level int
|
||||
wantDPSMin float64
|
||||
wantDPSMax float64
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Valid Scale DPS calculation",
|
||||
item: Item{
|
||||
ItemLevel: ptrInt(60),
|
||||
Delay: ptrFloat64(3000),
|
||||
MinDmg1: ptrInt(50),
|
||||
MaxDmg1: ptrInt(70),
|
||||
Subclass: ptrInt(4), // One-handed weapon
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
level: 70,
|
||||
wantDPSMin: 53.0, // Expected DPS range due to randomness
|
||||
wantDPSMax: 107.0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "High level Scale DPS calculation",
|
||||
item: Item{
|
||||
ItemLevel: ptrInt(80),
|
||||
Delay: ptrFloat64(2000),
|
||||
MinDmg1: ptrInt(150),
|
||||
MaxDmg1: ptrInt(200),
|
||||
Subclass: ptrInt(17), // Two-handed weapon
|
||||
Quality: ptrInt(4), // Epic
|
||||
},
|
||||
level: 100,
|
||||
wantDPSMin: 120.0, // Expected DPS range due to randomness
|
||||
wantDPSMax: 240.0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Low level Scale DPS calculation",
|
||||
item: Item{
|
||||
ItemLevel: ptrInt(20),
|
||||
Delay: ptrFloat64(1000),
|
||||
MinDmg1: ptrInt(30),
|
||||
MaxDmg1: ptrInt(50),
|
||||
Subclass: ptrInt(2), // Ranged weapon
|
||||
Quality: ptrInt(2), // Uncommon
|
||||
},
|
||||
level: 25,
|
||||
wantDPSMin: 21.0, // Expected DPS range due to randomness
|
||||
wantDPSMax: 42.0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Missing ItemLevel",
|
||||
item: Item{
|
||||
Delay: ptrFloat64(3000),
|
||||
MinDmg1: ptrInt(50),
|
||||
MaxDmg1: ptrInt(70),
|
||||
Subclass: ptrInt(4), // One-handed weapon
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
level: 70,
|
||||
wantDPSMin: 0,
|
||||
wantDPSMax: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing Delay",
|
||||
item: Item{
|
||||
ItemLevel: ptrInt(60),
|
||||
MinDmg1: ptrInt(50),
|
||||
MaxDmg1: ptrInt(70),
|
||||
Subclass: ptrInt(4), // One-handed weapon
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
level: 70,
|
||||
wantDPSMin: 0,
|
||||
wantDPSMax: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Secondary damage scaling",
|
||||
item: Item{
|
||||
ItemLevel: ptrInt(60),
|
||||
Delay: ptrFloat64(3000),
|
||||
MinDmg1: ptrInt(50),
|
||||
MaxDmg1: ptrInt(70),
|
||||
MinDmg2: ptrInt(25),
|
||||
MaxDmg2: ptrInt(35),
|
||||
Subclass: ptrInt(4), // One-handed weapon
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
level: 70,
|
||||
wantDPSMin: 53.0, // Expected DPS range due to randomness
|
||||
wantDPSMax: 107.0,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Seed the random number generator for consistent test results
|
||||
rand.Seed(1)
|
||||
|
||||
gotDPS, err := tt.item.ScaleDPS(tt.level)
|
||||
if (err != nil) != tt.expectError {
|
||||
t.Errorf("ScaleDPS() error = %v, expectError %v", err, tt.expectError)
|
||||
return
|
||||
}
|
||||
if !tt.expectError && (gotDPS < tt.wantDPSMin || gotDPS > tt.wantDPSMax) {
|
||||
t.Errorf("ScaleDPS() = %v, want between %v and %v", gotDPS, tt.wantDPSMin, tt.wantDPSMax)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDpsModifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
wantModifier float64
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Valid one-handed weapon modifier",
|
||||
item: Item{
|
||||
Subclass: ptrInt(4), // One-handed weapon
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
wantModifier: 0.64 * 1.38,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid two-handed weapon modifier",
|
||||
item: Item{
|
||||
Subclass: ptrInt(17), // Two-handed weapon
|
||||
Quality: ptrInt(4), // Epic
|
||||
},
|
||||
wantModifier: 0.80 * 1.5,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid ranged weapon modifier",
|
||||
item: Item{
|
||||
Subclass: ptrInt(2), // Ranged weapon
|
||||
Quality: ptrInt(2), // Uncommon
|
||||
},
|
||||
wantModifier: 0.70 * 1.25,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid wand modifier",
|
||||
item: Item{
|
||||
Subclass: ptrInt(19), // Wand
|
||||
Quality: ptrInt(4), // Epic
|
||||
},
|
||||
wantModifier: 0.70 * 1.5,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid subclass",
|
||||
item: Item{
|
||||
Subclass: ptrInt(99), // Invalid subclass
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
wantModifier: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing subclass",
|
||||
item: Item{
|
||||
Quality: ptrInt(3), // Rare
|
||||
},
|
||||
wantModifier: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing quality",
|
||||
item: Item{
|
||||
Subclass: ptrInt(4), // One-handed weapon
|
||||
},
|
||||
wantModifier: 0,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotModifier, err := tt.item.GetDpsModifier()
|
||||
if (err != nil) != tt.expectError {
|
||||
t.Errorf("GetDpsModifier() error = %v, expectError %v", err, tt.expectError)
|
||||
return
|
||||
}
|
||||
if !tt.expectError && !almostEqual(gotModifier, tt.wantModifier, 0.01) {
|
||||
t.Errorf("GetDpsModifier() = %v, want %v", gotModifier, tt.wantModifier)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to return a pointer to an int
|
||||
func ptrInt(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
func ptrFloat64(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
||||
func almostEqual(a, b, tolerance float64) bool {
|
||||
return math.Abs(a-b) <= tolerance
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package items
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
"reflect"
|
||||
|
||||
"github.com/araxiaonline/endgame-item-generator/utils"
|
||||
"slices"
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -171,28 +170,71 @@ var StatModifiers = map[int]float64{
|
||||
48: 0.65, // ITEM_MOD_BLOCK_VALUE
|
||||
}
|
||||
|
||||
// Get the primary stat for an item (strength, agility, intellect, spirit, stamina)
|
||||
func (item Item) GetPrimaryStat() (int, int, error) {
|
||||
var primaryStat int64
|
||||
var primaryVal int64
|
||||
|
||||
values := reflect.ValueOf(item)
|
||||
for i := 1; i < 11; i++ {
|
||||
statType := values.FieldByName(fmt.Sprintf("StatType%v", i)).Elem().Int()
|
||||
// first check if the stat type is not in the primary stats str, agi, intellect, spirit, stamina
|
||||
|
||||
statType, err := item.GetField(fmt.Sprintf("StatType%v", i))
|
||||
if err != nil {
|
||||
log.Printf("Failed to get stat type %v for item: %v", i, item.Name)
|
||||
continue
|
||||
}
|
||||
if statType < 3 || statType > 7 {
|
||||
continue
|
||||
}
|
||||
|
||||
statValue := values.FieldByName(fmt.Sprintf("StatValue%v", i)).Elem().Int()
|
||||
if statValue > primaryVal {
|
||||
primaryVal = statValue
|
||||
primaryStat = statType
|
||||
val, err := item.GetField(fmt.Sprintf("StatValue%v", i))
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to get stat value %v for item: %v", i, item.Name)
|
||||
continue
|
||||
}
|
||||
if val == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if int64(val) > primaryVal {
|
||||
primaryVal = int64(val)
|
||||
primaryStat = int64(statType)
|
||||
}
|
||||
}
|
||||
|
||||
return int(primaryStat), int(primaryVal), nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the statIds for anitem as a slice of integers
|
||||
* @return []int
|
||||
*/
|
||||
func (item Item) GetStatList() ([]int, error) {
|
||||
|
||||
statList := []int{}
|
||||
for i := 1; i < 11; i++ {
|
||||
val, err := item.GetField(fmt.Sprintf("StatValue%v", i))
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to get stat value %v for item: %v", i, item.Name)
|
||||
continue
|
||||
}
|
||||
if val == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
statId, err := item.GetField(fmt.Sprintf("StatType%v", i))
|
||||
if err != nil {
|
||||
log.Printf("Failed to get stat type %v for item: %s", i, item.Name)
|
||||
continue
|
||||
}
|
||||
statList = append(statList, statId)
|
||||
slices.Sort(statList)
|
||||
|
||||
}
|
||||
return statList, nil
|
||||
}
|
||||
|
||||
func (i Item) GetDpsModifier() (float64, error) {
|
||||
if i.Subclass == nil {
|
||||
return 0, fmt.Errorf("subclass on the item is not set")
|
||||
160
internal/items/items_mysql.go
Normal file
160
internal/items/items_mysql.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package items
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func GetItemFields(prefix string) string {
|
||||
pre := ""
|
||||
if prefix != "" {
|
||||
pre = prefix + "."
|
||||
}
|
||||
|
||||
return `
|
||||
` + pre + `entry, ` + pre + `name, ` + pre + `displayid,
|
||||
quality, ItemLevel, class, subclass, inventoryType,
|
||||
allowableClass, allowableRace,
|
||||
armor,material,
|
||||
requiredSkill, requiredLevel,
|
||||
dmg_min1, dmg_max1,
|
||||
dmg_min2,dmg_max2,
|
||||
dmg_type1, dmg_type2,
|
||||
delay, sheath, MaxDurability,
|
||||
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,
|
||||
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.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)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package maps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
43
internal/pkg/db/mysql.go
Normal file
43
internal/pkg/db/mysql.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type MySql struct {
|
||||
client *sqlx.DB
|
||||
}
|
||||
|
||||
type MySqlConfig struct {
|
||||
Host string
|
||||
User string
|
||||
Password string
|
||||
Database string
|
||||
}
|
||||
|
||||
func ConnectMySql(config *MySqlConfig) (*MySql, error) {
|
||||
|
||||
if config == nil {
|
||||
config = &MySqlConfig{
|
||||
Host: os.Getenv("DB_HOST"),
|
||||
User: os.Getenv("DB_USER"),
|
||||
Password: os.Getenv("DB_PASSWORD"),
|
||||
Database: os.Getenv("DB_NAME"),
|
||||
}
|
||||
}
|
||||
|
||||
connString := config.User + ":" + config.Password + "@tcp(" + config.Host + ")/" + config.Database
|
||||
client, err := sqlx.Open("mysql", connString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MySql{client: client}, nil
|
||||
}
|
||||
|
||||
func (db *MySql) Close() {
|
||||
db.client.Close()
|
||||
}
|
||||
23
internal/pkg/db/sqlite.go
Normal file
23
internal/pkg/db/sqlite.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type SqlLite struct {
|
||||
client *sql.DB
|
||||
}
|
||||
|
||||
func ConnectSqlLite(path string) (*SqlLite, error) {
|
||||
client, err := sql.Open("sqlite3", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SqlLite{client: client}, nil
|
||||
}
|
||||
|
||||
func (db *SqlLite) Close() {
|
||||
db.client.Close()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package spells
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/araxiaonline/endgame-item-generator/utils"
|
||||
"github.com/thoas/go-funk"
|
||||
)
|
||||
|
||||
@@ -130,7 +129,7 @@ func (db Database) GetSpell(id int) (Spell, error) {
|
||||
}
|
||||
|
||||
spell := Spell{}
|
||||
sql := "SELECT " + utils.GetSpellFields() + " FROM `spell_dbc` WHERE ID = ? -- " + strconv.Itoa(id)
|
||||
sql := "SELECT " + GetSpellFields() + " FROM `spell_dbc` WHERE ID = ? -- " + strconv.Itoa(id)
|
||||
|
||||
err := db.client.Get(&spell, sql, id)
|
||||
if err != nil {
|
||||
@@ -1,137 +1,39 @@
|
||||
package main
|
||||
package spells
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/araxiaonline/endgame-item-generator/models"
|
||||
)
|
||||
|
||||
// convert an item to a create sql statement
|
||||
func ItemToSql(item models.Item, reqLevel int, difficulty int) string {
|
||||
|
||||
entryBump := 20000000
|
||||
spellBump := 30000000
|
||||
if difficulty == 4 {
|
||||
entryBump = 21000000
|
||||
}
|
||||
if difficulty == 5 {
|
||||
entryBump = 22000000
|
||||
func GetSpellFields() string {
|
||||
return `
|
||||
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
|
||||
`
|
||||
}
|
||||
|
||||
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 += 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)
|
||||
}
|
||||
|
||||
func SpellToSql(spell models.Spell, quality int) string {
|
||||
func SpellToSql(spell Spell, quality int) string {
|
||||
|
||||
entryBump := 30000000
|
||||
if quality == 4 {
|
||||
1
internal/utils/sqlbuillder.go
Normal file
1
internal/utils/sqlbuillder.go
Normal file
@@ -0,0 +1 @@
|
||||
package utils
|
||||
242
main.go
242
main.go
@@ -2,12 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/araxiaonline/endgame-item-generator/models"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
@@ -16,13 +14,13 @@ func main() {
|
||||
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
godotenv.Load()
|
||||
models.Connect()
|
||||
// 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)")
|
||||
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")
|
||||
// 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")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
@@ -32,119 +30,119 @@ func main() {
|
||||
log.SetOutput(io.Discard)
|
||||
}
|
||||
|
||||
if difficulty == nil || *difficulty < 3 || *difficulty > 5 {
|
||||
log.Fatal("difficulty must be between 3-5")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// main loop
|
||||
dungeons, err := models.DB.GetDungeons(-1)
|
||||
if err != nil {
|
||||
log.Panicf("failed to get dungeons for expansion %v error: %v", 0, err)
|
||||
}
|
||||
|
||||
for _, dungeon := range dungeons {
|
||||
|
||||
log.Printf("+++++Dungeon: %s ID: %v level %v \n", dungeon.Name, dungeon.Id, dungeon.Level)
|
||||
|
||||
bosses, err := models.DB.GetBosses(dungeon.Id)
|
||||
if err != nil {
|
||||
log.Fatal("failed to get bosses")
|
||||
}
|
||||
|
||||
// Determine the scale value of the item based on expansion and dungeon level
|
||||
scaleValue := *itemLevel
|
||||
endGameDung := false
|
||||
if dungeon.Level == 60 {
|
||||
scaleValue += 10
|
||||
endGameDung = true
|
||||
}
|
||||
|
||||
if dungeon.ExpansionId == 1 && dungeon.Level <= 70 {
|
||||
scaleValue += 3
|
||||
}
|
||||
|
||||
if dungeon.ExpansionId == 1 && dungeon.Level == 70 {
|
||||
scaleValue += 12
|
||||
endGameDung = true
|
||||
}
|
||||
|
||||
if dungeon.ExpansionId == 2 && dungeon.Level <= 80 {
|
||||
scaleValue += 4
|
||||
}
|
||||
|
||||
if dungeon.ExpansionId == 2 && dungeon.Level == 80 {
|
||||
scaleValue += 15
|
||||
endGameDung = true
|
||||
}
|
||||
|
||||
// Apply difficuly modifiers for gear scale
|
||||
// mythic: Bosses-Epic Gear (Purple) drops and Rare (Blue) for random drops (BOE)
|
||||
// legendary: Bosses-Epic Gear (Purple) drops and Epic (Purple) for random drops (BOE)
|
||||
// ascendant: Bosses-Legendary Gear (Yellow) drops and Epic (Purple) for random drops (BOE)
|
||||
var bossQuality int
|
||||
var boeQuality int
|
||||
|
||||
if *difficulty == 4 {
|
||||
bossQuality = 4
|
||||
boeQuality = 4
|
||||
} else if *difficulty == 5 {
|
||||
bossQuality = 5
|
||||
boeQuality = 4
|
||||
} else {
|
||||
bossQuality = 4
|
||||
boeQuality = 3
|
||||
}
|
||||
|
||||
for _, boss := range bosses {
|
||||
|
||||
items, err := models.DB.GetBossLoot(boss.Entry)
|
||||
log.Printf("++++++++++ Boss: %s Entry: %v has %v items\n", boss.Name, boss.Entry, len(items))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
|
||||
_, error := item.ScaleItem(scaleValue, bossQuality)
|
||||
if error != nil {
|
||||
log.Printf("Failed to scale item: %v", error)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel)
|
||||
if *levelUp && endGameDung {
|
||||
fmt.Print(ItemToSql(item, *baselevel+1, *difficulty))
|
||||
} else {
|
||||
fmt.Print(ItemToSql(item, *baselevel, *difficulty))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
items2, err := models.DB.GetAddlDungeonDrops(dungeon.Id)
|
||||
if err != nil {
|
||||
log.Printf("failed to get additional loot for dungeon %v - err: %v", dungeon.Id, err)
|
||||
}
|
||||
|
||||
for _, item := range items2 {
|
||||
|
||||
// reduce item level of dungeon random drops since they are not boss fights
|
||||
adjScaleValue := scaleValue - 4
|
||||
|
||||
_, error := item.ScaleItem(adjScaleValue, boeQuality)
|
||||
if error != nil {
|
||||
log.Printf("Failed to scale item: %v", error)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel)
|
||||
fmt.Print(ItemToSql(item, *baselevel, *difficulty))
|
||||
|
||||
}
|
||||
log.Printf("++++++++++ AdditionalLoot Count: %v\n", len(items2))
|
||||
}
|
||||
|
||||
defer models.DB.Close()
|
||||
// if difficulty == nil || *difficulty < 3 || *difficulty > 5 {
|
||||
// log.Fatal("difficulty must be between 3-5")
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// // main loop
|
||||
// dungeons, err := models.DB.GetDungeons(-1)
|
||||
// if err != nil {
|
||||
// log.Panicf("failed to get dungeons for expansion %v error: %v", 0, err)
|
||||
// }
|
||||
|
||||
// for _, dungeon := range dungeons {
|
||||
|
||||
// log.Printf("+++++Dungeon: %s ID: %v level %v \n", dungeon.Name, dungeon.Id, dungeon.Level)
|
||||
|
||||
// bosses, err := models.DB.GetBosses(dungeon.Id)
|
||||
// if err != nil {
|
||||
// log.Fatal("failed to get bosses")
|
||||
// }
|
||||
|
||||
// // Determine the scale value of the item based on expansion and dungeon level
|
||||
// scaleValue := *itemLevel
|
||||
// endGameDung := false
|
||||
// if dungeon.Level == 60 {
|
||||
// scaleValue += 10
|
||||
// endGameDung = true
|
||||
// }
|
||||
|
||||
// if dungeon.ExpansionId == 1 && dungeon.Level <= 70 {
|
||||
// scaleValue += 3
|
||||
// }
|
||||
|
||||
// if dungeon.ExpansionId == 1 && dungeon.Level == 70 {
|
||||
// scaleValue += 12
|
||||
// endGameDung = true
|
||||
// }
|
||||
|
||||
// if dungeon.ExpansionId == 2 && dungeon.Level <= 80 {
|
||||
// scaleValue += 4
|
||||
// }
|
||||
|
||||
// if dungeon.ExpansionId == 2 && dungeon.Level == 80 {
|
||||
// scaleValue += 15
|
||||
// endGameDung = true
|
||||
// }
|
||||
|
||||
// // Apply difficuly modifiers for gear scale
|
||||
// // mythic: Bosses-Epic Gear (Purple) drops and Rare (Blue) for random drops (BOE)
|
||||
// // legendary: Bosses-Epic Gear (Purple) drops and Epic (Purple) for random drops (BOE)
|
||||
// // ascendant: Bosses-Legendary Gear (Yellow) drops and Epic (Purple) for random drops (BOE)
|
||||
// var bossQuality int
|
||||
// var boeQuality int
|
||||
|
||||
// if *difficulty == 4 {
|
||||
// bossQuality = 4
|
||||
// boeQuality = 4
|
||||
// } else if *difficulty == 5 {
|
||||
// bossQuality = 5
|
||||
// boeQuality = 4
|
||||
// } else {
|
||||
// bossQuality = 4
|
||||
// boeQuality = 3
|
||||
// }
|
||||
|
||||
// for _, boss := range bosses {
|
||||
|
||||
// items, err := models.DB.GetBossLoot(boss.Entry)
|
||||
// log.Printf("++++++++++ Boss: %s Entry: %v has %v items\n", boss.Name, boss.Entry, len(items))
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// for _, item := range items {
|
||||
|
||||
// _, error := item.ScaleItem(scaleValue, bossQuality)
|
||||
// if error != nil {
|
||||
// log.Printf("Failed to scale item: %v", error)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel)
|
||||
// if *levelUp && endGameDung {
|
||||
// fmt.Print(ItemToSql(item, *baselevel+1, *difficulty))
|
||||
// } else {
|
||||
// fmt.Print(ItemToSql(item, *baselevel, *difficulty))
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// items2, err := models.DB.GetAddlDungeonDrops(dungeon.Id)
|
||||
// if err != nil {
|
||||
// log.Printf("failed to get additional loot for dungeon %v - err: %v", dungeon.Id, err)
|
||||
// }
|
||||
|
||||
// for _, item := range items2 {
|
||||
|
||||
// // reduce item level of dungeon random drops since they are not boss fights
|
||||
// adjScaleValue := scaleValue - 4
|
||||
|
||||
// _, error := item.ScaleItem(adjScaleValue, boeQuality)
|
||||
// if error != nil {
|
||||
// log.Printf("Failed to scale item: %v", error)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// fmt.Printf("\n-- Item %v Entry: %v ItemLevel %v \n", item.Name, item.Entry, *item.ItemLevel)
|
||||
// fmt.Print(ItemToSql(item, *baselevel, *difficulty))
|
||||
|
||||
// }
|
||||
// log.Printf("++++++++++ Additional Count: %v\n", len(items2))
|
||||
// }
|
||||
|
||||
// defer models.DB.Close()
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
client *sqlx.DB
|
||||
}
|
||||
|
||||
var DB Database
|
||||
|
||||
func Connect() {
|
||||
var connString string = os.Getenv("DB_USER") + ":" + os.Getenv("DB_PASSWORD") + "@tcp(" + os.Getenv("DB_HOST") + ")/" + os.Getenv("DB_NAME")
|
||||
var err error
|
||||
DB.client, err = sqlx.Open("mysql", connString)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (db Database) Close() {
|
||||
db.client.Close()
|
||||
}
|
||||
BIN
scripts/localdb/items.db
Normal file
BIN
scripts/localdb/items.db
Normal file
Binary file not shown.
103
scripts/localdb/populate-endgame.go
Normal file
103
scripts/localdb/populate-endgame.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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))
|
||||
// }
|
||||
7
spells-list.txt
Normal file
7
spells-list.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
19451 - Frenzy - fast attack / damage (major)
|
||||
25516 - Aura of Command - regen, attack speed, dmg (minor)
|
||||
14538 - Aural Shock (reduce casting speed by 35%) (major)
|
||||
|
||||
Grievous Bite
|
||||
67719 Frost Fever = deals frost damage and reduces attack speed
|
||||
Aura of Anger - Shadow damage over time
|
||||
@@ -1,61 +0,0 @@
|
||||
package utils
|
||||
|
||||
func GetItemFields(prefix string) string {
|
||||
pre := ""
|
||||
if prefix != "" {
|
||||
pre = prefix + "."
|
||||
}
|
||||
|
||||
return `
|
||||
` + pre + `entry, ` + pre + `name, ` + pre + `displayid,
|
||||
quality, ItemLevel, class, subclass, inventoryType,
|
||||
allowableClass, allowableRace,
|
||||
armor,material,
|
||||
requiredSkill, requiredLevel,
|
||||
dmg_min1, dmg_max1,
|
||||
dmg_min2,dmg_max2,
|
||||
dmg_type1, dmg_type2,
|
||||
delay, sheath, MaxDurability,
|
||||
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,
|
||||
spellid_1, spellid_2, spellid_3,
|
||||
spelltrigger_1, spelltrigger_2, spelltrigger_3`
|
||||
}
|
||||
|
||||
func GetSpellFields() string {
|
||||
return `
|
||||
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
|
||||
`
|
||||
}
|
||||
Reference in New Issue
Block a user