Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d61878c
Add itemeffects
neteyesking May 7, 2025
a59e4d7
Hi
neteyesking May 7, 2025
0c5033d
Fixes
neteyesking May 6, 2025
1a7da20
Add rppm and maxstacks
neteyesking May 7, 2025
1189160
Run item effects and enchants from DB
neteyesking May 8, 2025
bbe6768
Correct scaling selector
neteyesking May 8, 2025
026e1b3
Clean up
neteyesking May 8, 2025
6f4b4b5
Fix imports
neteyesking May 8, 2025
858a8c1
Fixes
ToxicKevinFerm May 13, 2025
d7b778f
Trying something
ToxicKevinFerm May 14, 2025
98f4799
Cleanup and fix tests
ToxicKevinFerm May 17, 2025
ea33d9a
Fix build errors
NerdEgghead May 17, 2025
29f8547
Update test files
NerdEgghead May 17, 2025
765f258
Merge branch 'master' into test/itemeffects
NerdEgghead May 17, 2025
6da8bea
Merge branch 'master' into test/itemeffects
NerdEgghead May 18, 2025
afc3866
Revert to using the item name rather than the buff name as the
NerdEgghead May 18, 2025
17b6d03
Merge branch 'master' into test/itemeffects
NerdEgghead May 19, 2025
4130f78
Merge branch 'master' into test/itemeffects
NerdEgghead May 20, 2025
3015a4e
Fix ppm
ToxicKevinFerm May 24, 2025
e1b1471
Merge branch 'master' into test/itemeffects
ToxicKevinFerm May 24, 2025
dc1c2ef
Remake db
ToxicKevinFerm May 24, 2025
10b9217
Fix scaling
ToxicKevinFerm May 25, 2025
883e24a
Fix issues
ToxicKevinFerm May 25, 2025
378976f
Merge branch 'master' into test/itemeffects
NerdEgghead May 25, 2025
fca85a2
Fix test passing and remove coefficient scaling
ToxicKevinFerm May 25, 2025
d1ee7ac
Merge branch 'master' into test/itemeffects
NerdEgghead May 25, 2025
fea5316
Merge branch 'test/itemeffects' of https://github.com/wowsims/cata in…
NerdEgghead May 25, 2025
6d957b7
Delete deprecated on-use factory functions
NerdEgghead May 26, 2025
074a114
Deleted deprecated fields from shared.ProcStatBonusEffect
NerdEgghead May 26, 2025
8661a52
Addressed PR comments
ToxicKevinFerm May 27, 2025
a7e910d
Fixes
ToxicKevinFerm May 27, 2025
b18d574
Fix comments
ToxicKevinFerm May 27, 2025
89ac11b
Fix RPPM
ToxicKevinFerm May 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified assets/database/db.bin
Binary file not shown.
7,659 changes: 3,831 additions & 3,828 deletions assets/database/db.json

Large diffs are not rendered by default.

Binary file modified assets/database/leftover_db.bin
Binary file not shown.
17,839 changes: 8,921 additions & 8,918 deletions assets/database/leftover_db.json

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions proto/common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -925,11 +925,6 @@ enum EnchantType {
EnchantTypeOffHand = 5;
}

// Contains only the Enchant info needed by the sim.
message SimEnchant {
int32 effect_id = 1;
repeated double stats = 2;
}

// Contains only the Gem info needed by the sim.
message SimGem {
Expand Down
1 change: 1 addition & 0 deletions proto/db.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ message SimItem {
int32 set_id = 12;

map<int32, ScalingItemProperties> scaling_options = 13; // keys are the all ItemLevelState variants that this item could potentially have
repeated ItemEffect item_effects = 14;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be an array? Are there any relevant cases where we need to process multiple effects for a single item?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NerdEgghead I think trinkets like this:

Trinkets like these will require custom implementations for the second effect regardless, it's only the simple stat effect that we can handle automatically with factory functions. If those are the only examples, then it would be better to just remove the complex effect during pre-processing and dump only the stat buff effect.

}

message Consumable {
Expand Down
46 changes: 46 additions & 0 deletions proto/spell.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,52 @@ package proto;
option go_package = "./proto";

import "common.proto";
enum ItemEffectType {
NONE = 0;
PROC = 1;
ON_USE = 2;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stick with the same naming convention we've been using for our other enums:

enum ItemEffectType {
    EffectTypeUnknown = 0;
    EffectTypeProc = 1;
    EffectTypeOnUse = 2;
}

// Contains only the Enchant info needed by the sim.
message SimEnchant {
int32 effect_id = 1;
repeated double stats = 2;
ItemEffect enchant_effect = 3;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make more sense for this to live in db.proto, unless that would cause import cycles.


message ScalingItemEffectProperties {
// keys are the numeric values of proto.common.Stat
map<int32, double> stats = 1;
double rppm_ilvl_modifier = 2;
}
message ItemEffect {
int32 spell_id = 1;
string label = 9;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call these buff_id and buff_name for clarity.

ItemEffectType type = 2;
int32 effect_duration = 3; // seconds

map<int32, ScalingItemEffectProperties> scaling_options = 4;

oneof effect {
ProcEffect proc = 7;
OnUseEffect on_use = 8;
}
}

message ProcEffect {
double proc_chance = 1; // e.g. 0.20 = 20%
double rppm = 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call it ppm rather than rppm so that the name will make sense in both expansions.

int32 rppm_scale = 4;
map<int32, double> spec_modifiers = 5;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarify what the keys represent in a comment.

int32 icd = 2; // internal cooldown in seconds
}

message OnUseEffect {
int32 cooldown = 1; // seconds between uses

int32 category_id = 11;

int32 category_cooldown = 8;
}

message SpellEffect {
int32 id = 1;
Expand Down
2 changes: 2 additions & 0 deletions proto/ui.proto
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ message UIItem {

FactionRestriction faction_restriction = 25;
map<int32, ScalingItemProperties> scaling_options = 29; // keys are the other ilvl variants that this item could potentially have
repeated ItemEffect item_effects = 30;
}

enum Expansion {
Expand Down Expand Up @@ -205,6 +206,7 @@ message UIEnchant {
// Classes that are allowed to use the enchant. Empty indicates no special class restrictions.
repeated Class class_allowlist = 11;
Profession required_profession = 12;
ItemEffect enchant_effect = 14;
}

message UIGem {
Expand Down
53 changes: 33 additions & 20 deletions sim/common/shared/shared_utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package shared

import (
"fmt"
"time"

"github.com/wowsims/cata/sim/core"
Expand Down Expand Up @@ -97,30 +98,46 @@ func factory_StatBonusEffect(config ProcStatBonusEffect, extraSpell func(agent c

effectFn(effectID, func(agent core.Agent) {
character := agent.GetCharacter()

var eligibleSlots []proto.ItemSlot
var procEffect *proto.ItemEffect
scalingSelector := proto.ItemLevelState_Base
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the MoP version of this, you can change

type ApplyEffect func(Agent)

to

type ApplyEffect func(Agent, proto.ItemLevelState)

and it should be fairly straightforward to generalize. More or less, all you'd need to do is update the code within

func (equipment *Equipment) applyItemEffects()

to now call applyItemEffect(agent, eq.UpgradeState), and likewise for applyEnchantEffect().

In fact, I think you can even do this refactor within the Cata PR so that it's easy to directly port to MoP.

if isEnchant {
eligibleSlots = character.ItemSwap.EligibleSlotsForEffect(effectID)

ench := core.EnchantsByEffectID[effectID]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make an analog to core.GetItemByID(id int32) *Item for core.GetEnchantByEffectID(id int32) *Enchant and call that here, so that you avoid copying the entire Enchant struct by value.

if ench.EnchantEffect.GetProc() != nil {
procEffect = ench.EnchantEffect
}
} else {
eligibleSlots = character.ItemSwap.EligibleSlotsForItem(effectID)
}

procID := core.ActionID{SpellID: config.AuraID}
if procID.IsEmptyAction() {
procID = core.ActionID{ItemID: config.ItemID}
item := character.Equipment.GetItemById(effectID)
if item != nil && len(item.ItemEffects) > 0 {
scalingSelector = item.UpgradeStep
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For performance reasons, I would recommend refactoring to the approach I described earlier, where scalingSelector is passed as an additional input to core.ApplyEffect rather than computed internally. This allows you to just do

item := core.GetItemByID(effectID)

which is a simple map look-up rather than needing to loop over the Equipment.

for _, effect := range item.ItemEffects {
if effect.GetProc() != nil {
procEffect = effect
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified if you collapse item.ItemEffects down to just the single buff effect we care about during dbc pre-processing.

}
}
procAura := character.NewTemporaryStatsAura(config.Name+" Proc", procID, config.Bonus, config.Duration)

if procEffect == nil {
err, _ := fmt.Printf("Error getting proc effect for item/enchant %v", effectID)
panic(err)
}
proc := procEffect.GetProc()
procAction := core.ActionID{SpellID: procEffect.SpellId}
procAura := character.NewTemporaryStatsAura(core.Ternary(config.Name != "", config.Name, procEffect.Label)+" Proc", procAction, stats.FromProtoMap(procEffect.ScalingOptions[int32(scalingSelector)].Stats), time.Second*time.Duration(procEffect.EffectDuration))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the parsing robust enough that you can eliminate the dependence on config.Name entirely, and always use procEffect.BuffName here? The label on the StatBuffAura shouldn't have any impact on rng seeds so it should be safe to change without impacting unit tests; it's the label on the ProcTrigger config that dictates the rng seed.

var dpm *core.DynamicProcManager
if (config.PPM != 0) && (config.ProcMask == core.ProcMaskUnknown) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be if (proc.Ppm != 0) to eliminate the dependence on the deprecated config.PPM field.

if isEnchant {
dpm = character.AutoAttacks.NewDynamicProcManagerForEnchant(effectID, config.PPM, 0)
dpm = character.AutoAttacks.NewDynamicProcManagerForEnchant(effectID, proc.Rppm, 0)
} else {
dpm = character.AutoAttacks.NewDynamicProcManagerForWeaponEffect(effectID, config.PPM, 0)
dpm = character.AutoAttacks.NewDynamicProcManagerForWeaponEffect(effectID, proc.Rppm, 0)
}
}

procAura.CustomProcCondition = config.CustomProcCondition

var customHandler CustomProcHandler
if config.CustomProcCondition != nil {
customHandler = func(sim *core.Simulation, procAura *core.StatBuffAura) {
Expand All @@ -134,12 +151,10 @@ func factory_StatBonusEffect(config ProcStatBonusEffect, extraSpell func(agent c
}
}
}

var procSpell ExtraSpellInfo
if extraSpell != nil {
procSpell = extraSpell(agent)
}

handler := func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
if customHandler != nil {
customHandler(sim, procAura)
Expand All @@ -150,25 +165,22 @@ func factory_StatBonusEffect(config ProcStatBonusEffect, extraSpell func(agent c
}
}
}

triggerAura := core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{
ActionID: triggerActionID,
Name: config.Name,
Name: core.Ternary(config.Name != "", config.Name, procEffect.Label),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Cata version, you can just keep this as config.Name always so that the rng seed is the same as before. For the MoP version, you can do procEffect.BuffName + " Trigger", and then we won't need a Name field in the config at all anymore.

Callback: config.Callback,
ProcMask: config.ProcMask,
Outcome: config.Outcome,
Harmful: config.Harmful,
ProcChance: config.ProcChance,
PPM: config.PPM,
ProcChance: proc.ProcChance,
PPM: proc.Rppm,
DPM: dpm,
ICD: config.ICD,
ICD: time.Second * time.Duration(proc.Icd),
Handler: handler,
})

if config.ICD != 0 {
if proc.Icd != 0 {
procAura.Icd = triggerAura.Icd
}

if isEnchant {
character.ItemSwap.RegisterEnchantProcWithSlots(effectID, triggerAura, eligibleSlots)
} else {
Expand Down Expand Up @@ -255,6 +267,7 @@ func NewParryActive(itemID int32, bonus float64, duration time.Duration, cooldow
func NewMasteryActive(itemID int32, bonus float64, duration time.Duration, cooldown time.Duration) {
CreateOffensiveStatActive(itemID, duration, cooldown, stats.Stats{stats.MasteryRating: bonus})
}
func NewDbcStatsActive(itemID int32) {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea for how to construct this:

// In core/major_cooldown.go
type SharedCooldownCategory byte

const (
    CooldownCategoryUnknown SharedCooldownCategory = iota
    CooldownCategoryNone
    CooldownCategoryOffensive
    CooldownCategoryDefensive
)

func CooldownCategoryFromID(categoryID int32) SharedCooldownCategory {
    // Neteyes big brain implementation here
}

// In shared_utils.go
func NewSimpleStatActive(itemID int32) {
    core.NewItemEffect(itemID, func(agent core.Agent, scalingSelector proto.ItemLevelState) {
        item := core.GetItemByID(itemID)
        if item == nil {
            panic(fmt.Sprintf("No item with ID: %d", itemID))
        }

        itemEffect := item.ItemEffect // Assuming it can be collapsed to one relevant effect per item in pre-processing
        if itemEffect == nil {
            panic(fmt.Printf("No effect data for item with ID: %d", itemID))
        }

        onUseData := itemEffect.GetOnUse()
        if onUseData == nil {
            panic(fmt.Printf("Item effect for item with ID: %d is not an active effect!", itemID))
        }

        spellConfig := core.SpellConfig{
            ActionID: core.ActionID{ItemID: itemID},
        }

        character = agent.GetCharacter()
        spellConfig.Cast.CD = core.Cooldown{
            Timer:      character.NewTimer(),
            Duration: time.Duration(onUseData.Cooldown) * time.Second,
        }

        sharedCDCategory := core.CooldownCategoryFromID(onUseData.CategoryId)
        if sharedCDCategory == core.CooldownCategoryUnknown {
            panic(fmt.Printf("Unrecognized shared cooldown category for item with ID: %d", itemID))
        } else if sharedCDCategory != core.CooldownCategoryNone {
            sharedCDDuration := time.Duration(onUseData.categoryCooldown) * time.Second

            var sharedCDTimer *core.Timer
            if sharedCDCategory == core.CooldownCategoryOffensive {
                sharedCDTimer = character.GetOffensiveTrinketCD()
            } else {
                sharedCDTimer = character.GetDefensiveTrinketCD()
            }

            spellConfig.Cast.SharedCD = core.Cooldown{
                Timer:      sharedCDTimer,
                Duration: sharedCDDuration,
            }
        }

        core.RegisterTemporaryStatsOnUseCD(character, itemEffect.BuffName, stats.FromProtoMap(itemEffect.ScalingOptions[int32(scalingSelector)].Stats), time.Second*time.Duration(itemEffect.EffectDuration), spellConfig)
    })
}

With an implementation like this, you should be able to rip out all of the shared.NewXActive() methods and their associated factory functions, which should greatly simplify the codebase overall.

type StackingStatBonusCD struct {
Name string
Expand Down
30 changes: 26 additions & 4 deletions sim/core/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type Item struct {
ScalingOptions map[int32]*proto.ScalingItemProperties
RandPropPoints int32
UpgradeStep proto.ItemLevelState
ItemEffects []*proto.ItemEffect
}

func ItemFromProto(pData *proto.SimItem) Item {
Expand All @@ -169,6 +170,7 @@ func ItemFromProto(pData *proto.SimItem) Item {
SetName: pData.SetName,
SetID: pData.SetId,
ScalingOptions: pData.ScalingOptions,
ItemEffects: pData.ItemEffects,
}
}

Expand Down Expand Up @@ -207,14 +209,16 @@ func RandomSuffixFromProto(pData *proto.ItemRandomSuffix) RandomSuffix {
}

type Enchant struct {
EffectID int32 // Used by UI to apply effect to tooltip
Stats stats.Stats
EffectID int32 // Used by UI to apply effect to tooltip
Stats stats.Stats
EnchantEffect *proto.ItemEffect
}

func EnchantFromProto(pData *proto.SimEnchant) Enchant {
return Enchant{
EffectID: pData.EffectId,
Stats: stats.FromProtoArray(pData.Stats),
EffectID: pData.EffectId,
Stats: stats.FromProtoArray(pData.Stats),
EnchantEffect: pData.EnchantEffect,
}
}

Expand Down Expand Up @@ -361,6 +365,24 @@ func (equipment *Equipment) containsItemInSlots(itemID int32, possibleSlots []pr
})
}

func (equipment *Equipment) GetEnchantByEffectID(effectID int32) *Enchant {
for _, item := range equipment {
if item.Enchant.EffectID == effectID {
return &item.Enchant
}
}
return nil
}

func (equipment *Equipment) GetItemById(itemID int32) *Item {
for _, item := range equipment {
if item.ID == itemID {
return &item
}
}
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be deleted with the proposed change to the core.ApplyEffect function signature.


func (equipment *Equipment) ToEquipmentSpecProto() *proto.EquipmentSpec {
return &proto.EquipmentSpec{
Items: MapSlice(equipment[:], func(item Item) *proto.ItemSpec {
Expand Down
6 changes: 4 additions & 2 deletions sim/core/database_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func init() {
SetName: item.SetName,
SetId: item.SetId,
ScalingOptions: item.ScalingOptions,
ItemEffects: item.ItemEffects,
}
}

Expand All @@ -51,8 +52,9 @@ func init() {

for i, enchant := range db.Enchants {
simDB.Enchants[i] = &proto.SimEnchant{
EffectId: enchant.EffectId,
Stats: enchant.Stats,
EffectId: enchant.EffectId,
Stats: enchant.Stats,
EnchantEffect: enchant.EnchantEffect,
}
}

Expand Down
5 changes: 5 additions & 0 deletions sim/core/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ func SpellSchoolFromProto(p proto.SpellSchool) SpellSchool {
}
}

const (
RPPM_HASTE = 1 << (iota)
RPPM_CRIT
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's cleaner to define a custom type for these:

type RppmModifier byte

const (
    RppmModifierUnknown RppmModifier = iota
    RppmModifierHaste
    RppmModifierCrit
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also do these definitions need to live in core, or can they exist solely within dbc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They will need to live in core because thats what we use to decide if the rppm scaled with haste or crit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, clean up the code here to use a custom type called RppmModifier or RppmScaling.


/*
outcome roll hit/miss/crit/glance (assigns Outcome mask) -> If Hit, Crit Roll -> damage (applies metrics) -> trigger proc

Expand Down
1 change: 0 additions & 1 deletion sim/core/stats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,6 @@ func (stats Stats) ToProtoArray() []float64 {
// shared indices between the two.
return stats[:ProtoStatsLen]
}

func (stats Stats) ToProtoMap() map[int32]float64 {
m := make(map[int32]float64, ProtoStatsLen)
for i := 0; i < int(ProtoStatsLen); i++ {
Expand Down
5 changes: 3 additions & 2 deletions sim/lib/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,9 @@ func getDatabase(itemIds *int32, numItems int32, enchantIds *int32, numEnchants
for i, enchantId := range eids {
enchant := core.EnchantsByEffectID[enchantId]
simDB.Enchants[i] = &proto.SimEnchant{
EffectId: enchant.EffectID,
Stats: enchant.Stats[:],
EffectId: enchant.EffectID,
Stats: enchant.Stats[:],
EnchantEffect: enchant.EnchantEffect,
}
}
for i, gemId := range gids {
Expand Down
2 changes: 1 addition & 1 deletion tools/database/dbc/consumable.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (consumable *Consumable) GetStatModifiers() *stats.Stats {
if effect.ID != 0 {
if spellEffects, ok := dbcInstance.SpellEffects[effect.SpellID]; ok {
for _, spellEffect := range spellEffects {
stat := spellEffect.ParseStatEffect()
stat := spellEffect.ParseStatEffect(false, 0)
stats.AddInplace(stat)
}
}
Expand Down
26 changes: 22 additions & 4 deletions tools/database/dbc/dbc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ type DBC struct {
ItemArmorTotal map[int]ItemArmorTotal
ArmorLocation map[int]ArmorLocation
SpellScalings map[int]SpellScaling
Consumables map[int]Consumable // Item ID
ItemEffects map[int]ItemEffect // Parent Item ID
Consumables map[int]Consumable // Item ID
ItemEffects map[int]ItemEffect // Parent Item ID
ItemEffectsByParentID map[int][]ItemEffect // new
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't your existing ItemEffects field already map by parent ID?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No ItemEffects is keyed by effect id, the comment is wrong.
So, an item can have several effects and sometimes I need to be able to look up both. For example for consumables or or enchants.

}

func NewDBC() *DBC {
Expand All @@ -46,6 +47,7 @@ func NewDBC() *DBC {
Consumables: make(map[int]Consumable),
ItemEffects: make(map[int]ItemEffect),
SpellScalings: make(map[int]SpellScaling),
ItemEffectsByParentID: make(map[int][]ItemEffect),
}
}

Expand Down Expand Up @@ -173,12 +175,28 @@ func (d *DBC) loadItemEffects(filename string) error {
}
}

for i := range effects {
effect := effects[i]
// Initialize maps if needed
if d.ItemEffects == nil {
d.ItemEffects = make(map[int]ItemEffect)
}
if d.ItemEffectsByParentID == nil {
d.ItemEffectsByParentID = make(map[int][]ItemEffect)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't these already get initialized within NewDBC()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump.


// Populate both maps
for _, effect := range effects {
// Single lookup by effect ID
d.ItemEffects[effect.ID] = effect
// Grouping by parent item ID
d.ItemEffectsByParentID[effect.ParentItemID] = append(
d.ItemEffectsByParentID[effect.ParentItemID],
effect,
)
}

return nil
}

func (d *DBC) loadRandomPropertiesByIlvl(filename string) error {
data, err := ReadGzipFile(filename)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions tools/database/dbc/enchant.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ func (enchant *Enchant) ToProto() *proto.UIEnchant {
Quality: enchant.Quality.ToProto(),
RequiredProfession: GetProfession(enchant.RequiredProfession),
}
eff := ItemEffect{TriggerType: 1, SpellID: enchant.SpellId}
uiEnchant.EnchantEffect = eff.ToProto(0, 0)
if uiEnchant.EnchantEffect.GetOnUse() == nil && uiEnchant.EnchantEffect.GetProc() == nil {
uiEnchant.EnchantEffect = nil
}
if enchant.FDID == 0 {
uiEnchant.Icon = "trade_engraving"
}
Expand Down
Loading
Loading