Skip to content

Conversation

@ToxicKevinFerm
Copy link
Contributor

No description provided.

Copy link
Contributor

@NerdEgghead NerdEgghead left a comment

Choose a reason for hiding this comment

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

Great start, this will hugely simplify our very messy on-use item factories once it's finished!

proto/db.proto Outdated
Comment on lines 38 to 39
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.

Comment on lines 7 to 11
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;
}

Comment on lines 12 to 17
// 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.

Comment on lines 25 to 26
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.

Comment on lines 41 to 42
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.

Comment on lines 161 to 166
src := total.ToProtoMap()

m := make(map[int32]float64, len(src))
maps.Copy(m, src)

return &proto.ScalingItemEffectProperties{Stats: m}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you just directly populate the Stats field with src rather than allocating another map and copying to it first?

Comment on lines 203 to 232
func MergeItemEffectsForAllStates(parsed *proto.UIItem) []*proto.ItemEffect {
itemID := int(parsed.Id)
raws := dbcInstance.ItemEffectsByParentID[itemID]
var merged []*proto.ItemEffect

for idx := range raws {
var base *proto.ItemEffect

for key, props := range parsed.ScalingOptions {
state := proto.ItemLevelState(key)
ilvl := int(props.Ilvl)
slice := ParseItemEffects(itemID, ilvl, state)
eff := slice[idx]

if base == nil {
base = eff
} else {
base.ScalingOptions[key] = eff.ScalingOptions[key]
}
}
if base == nil {
continue
}

if len(base.ScalingOptions) > 0 {
merged = append(merged, base)
}
}
return merged
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok I see where you're handling the full set of ItemLevelState options now. This seems very inefficient though, since you're allocating and populating a whole other proto.ItemEffect struct for every variant of the item. Wouldn't it be better to create the base proto.ItemEffect only once per item, and then only update the ScalingOptions map within the loop over ItemLevelState options rather than merging a full proto?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I built myself into a bit of a corner when I realized I had to recursively step through spell triggers and also scale the stats array to the item level 😄

Comment on lines 217 to 218
case effect.EffectAura == A_MOD_RANGED_ATTACK_POWER:
if scalesWithIlvl && effect.Coefficient != 0 && spell.Attributes[8]&0x1000 != 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Pre-cache this check before the switch rather than copy-pasting it three times:

useRandPropPoints := scalesWithIlvl && (effect.Coefficient != 0) && (spell.Attributes[8]&0x1000 != 0)

Comment on lines 219 to 220
propPoints := dbcInstance.RandomPropertiesByIlvl[ilvl][proto.ItemQuality_ItemQualityEpic][0] // Epic 0 for items
stats[proto.Stat_StatRangedAttackPower] = math.Floor(float64(propPoints) * effect.Coefficient)
Copy link
Contributor

Choose a reason for hiding this comment

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

Likewise, define a helper for this calculation so you're only coding the logic once:

func (effect *SpellEffect) CalcRandomPropStatValue(ilvl int) float64 {
    propPoints := dbcInstance.RandomPropertiesByIlvl[ilvl][proto.ItemQuality_ItemQualityEpic][0] // Epic 0 for items
    return math.Floor(float64(propPoints) * effect.Coefficient)
}

Comment on lines 270 to 271
if statMod := RatingModToStat[rating]; statMod != -1 {
if scalesWithIlvl && effect.Coefficient != 0 && spell.Attributes[8]&0x1000 == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

You're doing spell.Attributes[8]&0x1000 == 0 here when it's been != 0 the previous three times, is that intended?

 On branch test/itemeffects
 Changes to be committed:
	modified:   ui/core/components/individual_sim_ui/bulk_tab.tsx
	modified:   ui/core/proto_utils/gear.ts
 On branch test/itemeffects
 Changes to be committed:
	modified:   proto/common.proto
	modified:   sim/core/TestProtoVersioning.results
	modified:   sim/death_knight/blood/TestBlood.results
	modified:   sim/death_knight/frost/TestFrost.results
	modified:   sim/death_knight/unholy/TestUnholy.results
	modified:   sim/druid/balance/TestBalance.results
	modified:   sim/druid/feral/TestFeral.results
	modified:   sim/druid/guardian/TestGuardian.results
	modified:   sim/hunter/beast_mastery/TestBM.results
	modified:   sim/hunter/marksmanship/TestMM.results
	modified:   sim/hunter/survival/TestSV.results
	modified:   sim/mage/arcane/TestArcane.results
	modified:   sim/mage/fire/TestFire.results
	modified:   sim/paladin/protection/TestProtection.results
	modified:   sim/paladin/retribution/TestRetribution.results
	modified:   sim/priest/shadow/TestShadow.results
	modified:   sim/rogue/assassination/TestAssassination.results
	modified:   sim/rogue/combat/TestCombat.results
	modified:   sim/rogue/subtlety/TestSubtlety.results
	modified:   sim/shaman/elemental/TestElemental.results
	modified:   sim/shaman/enhancement/TestEnhancement.results
	modified:   sim/warlock/affliction/TestAffliction.results
	modified:   sim/warlock/demonology/TestDemonology.results
	modified:   sim/warlock/destruction/TestDestruction.results
	modified:   sim/warrior/arms/TestArms.results
	modified:   sim/warrior/fury/TestFury.results
	modified:   sim/warrior/protection/TestProtectionWarrior.results
StatBuffAura label. Fixes test failures in configs with a Normal +
Heroic version of the same trinket both equipped.

 On branch test/itemeffects
 Changes to be committed:
	modified:   sim/common/shared/shared_utils.go
	modified:   sim/death_knight/blood/TestBlood.results
	modified:   sim/mage/arcane/TestArcane.results
	modified:   sim/paladin/retribution/TestRetribution.results
	modified:   sim/rogue/assassination/TestAssassination.results
	modified:   sim/rogue/combat/TestCombat.results
	modified:   sim/rogue/subtlety/TestSubtlety.results
	modified:   sim/warrior/arms/TestArms.results
@github-actions github-actions bot removed the Priest label May 25, 2025
 On branch test/itemeffects
 Changes to be committed:
	modified:   sim/common/cata/stat_bonus_cds.go
	modified:   sim/common/shared/shared_utils.go
 On branch test/itemeffects
 Changes to be committed:
	modified:   sim/common/cata/enchant_effects.go
	modified:   sim/common/cata/stat_bonus_procs.go
	modified:   sim/common/shared/shared_utils.go
	modified:   sim/common/tbc/stat_bonus_procs.go
	modified:   sim/common/wotlk/enchant_effects.go
	modified:   sim/common/wotlk/stat_bonus_procs.go
Copy link
Contributor

@NerdEgghead NerdEgghead left a comment

Choose a reason for hiding this comment

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

Nearly there!

hjej.xt Outdated
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this file? Does it need to be committed?

Comment on lines 21 to 22
string buff_name = 9;
ItemEffectType type = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think the type field is actually used anywhere, can it be deleted?

Comment on lines 37 to 38
// The key represents a Class enum key
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.

Should this be called class_modifiers instead if the keys are classes rather than specs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should actually be a spec key so i fix it

Comment on lines -54 to +49
factory_StatBonusEffect(config, func(agent core.Agent) ExtraSpellInfo {
factory_StatBonusEffect(config, func(agent core.Agent, _ proto.ItemLevelState) ExtraSpellInfo {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a note that if these types of trinkets are still relevant in MoP, then we should probably convert the MinDmg, MaxDmg, and BonusCoefficient fields of shared.DamageEffect from floats into map[proto.ItemLevelState]float64 types.

Comment on lines 140 to 145
procSpell = extraSpell(agent)
procSpell = extraSpell(agent, 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.

You should call this with the actual itemLevelState value for generality, so that the code will still work in MoP if we generalize shared.DamageEffect to include scaling properties.

Comment on lines 181 to 182
// Parses a UIItem and loops through Scaling Options for that item.
func MergeItemEffectsForAllStatesNew(parsed *proto.UIItem) *proto.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 think you need the "New" in the name anymore.

Comment on lines 203 to 205
if parsed.Id == 92127 {
fmt.Println("hello2", parsed, parsed.Ilvl, opt.Ilvl, opt)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe a holdover from debugging?

Comment on lines 218 to 219

func realPpmScale(spell Spell) int {
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably want to take in a *Spell pointer here instead, in order to avoid copying the input on every call.

Comment on lines 231 to 232

func realPpmModifier(spell Spell, itemLevel int) (float64, map[int32]float64) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

Comment on lines 12 to 13
const BASE_LEVEL = 85
const BASE_LEVEL = 90
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this maybe why you were getting the wrong stat values for the boosted trinkets?

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, trinkets dont use base level

Comment on lines 17 to 18
int32 effect_duration = 2; // seconds

Copy link
Contributor

Choose a reason for hiding this comment

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

Call this one effect_duration_ms as well so that the units are consistent across the message.

Comment on lines 726 to 730
func (character *Character) GetConjuredCD() *Timer {
return character.GetOrInitTimer(&character.conjuredCD)
return character.GetOrInitSpellCategoryTimer(30)
}
func (character *Character) GetPotionCD() *Timer {
return character.GetOrInitTimer(&character.potionCD)
Copy link
Contributor

Choose a reason for hiding this comment

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

Make the same change for GetPotionCD() as well where you use the correct category ID for it, so that you can also delete potionCD from the Character struct.

Comment on lines 152 to 153
for _, se := range dbcInstance.SpellEffects[id] {
if s := se.ParseStatEffect(sp.HasAttributeAt(11, 0x4), itemLevel); s != &emptyStats {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think s != &emptyStats can ever evaluate false, since you're comparing pointers rather than the arrays themselves. So even if s points to another empty Stats array, the expression will still evaluate as true.

Comment on lines 29 to 30
double ppm = 3;
int32 rppm_scale = 4;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is rppm_scale here intended to represent a core.RppmModifier? If so, then it would be cleaner to make RppmScalingType a proto enum rather than a back-end type.

Comment on lines 212 to 215
spell := dbcInstance.Spells[effect.SpellID]
// if not we get class scaling based on the spell
scale := effect.ScalingClass()
spell := dbcInstance.Spells[effect.ID]
return dbcInstance.SpellScalings[core.TernaryInt(spell.MaxScalingLevel > BASE_LEVEL, BASE_LEVEL, spell.MaxScalingLevel)].Values[scale]
Copy link
Contributor

Choose a reason for hiding this comment

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

core.TernaryInt(spell.MaxScalingLevel > BASE_LEVEL, BASE_LEVEL, spell.MaxScalingLevel) can be simplified to just min(spell.MaxScalingLevel, BASE_LEVEL).

@NerdEgghead NerdEgghead merged commit 60386b3 into master May 28, 2025
2 checks passed
@NerdEgghead NerdEgghead deleted the test/itemeffects branch May 28, 2025 01:35
ToxicKevinFerm pushed a commit to wowsims/mop that referenced this pull request May 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants