-
Notifications
You must be signed in to change notification settings - Fork 84
Test/itemeffects #1491
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Test/itemeffects #1491
Conversation
NerdEgghead
left a comment
There was a problem hiding this 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
| map<int32, ScalingItemProperties> scaling_options = 13; // keys are the all ItemLevelState variants that this item could potentially have | ||
| repeated ItemEffect item_effects = 14; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
proto/spell.proto
Outdated
| enum ItemEffectType { | ||
| NONE = 0; | ||
| PROC = 1; | ||
| ON_USE = 2; | ||
| } |
There was a problem hiding this comment.
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;
}
proto/spell.proto
Outdated
| // Contains only the Enchant info needed by the sim. | ||
| message SimEnchant { | ||
| int32 effect_id = 1; | ||
| repeated double stats = 2; | ||
| ItemEffect enchant_effect = 3; | ||
| } |
There was a problem hiding this comment.
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.
proto/spell.proto
Outdated
| int32 spell_id = 1; | ||
| string label = 9; |
There was a problem hiding this comment.
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.
proto/spell.proto
Outdated
| int32 rppm_scale = 4; | ||
| map<int32, double> spec_modifiers = 5; |
There was a problem hiding this comment.
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.
tools/database/dbc/item_effect.go
Outdated
| src := total.ToProtoMap() | ||
|
|
||
| m := make(map[int32]float64, len(src)) | ||
| maps.Copy(m, src) | ||
|
|
||
| return &proto.ScalingItemEffectProperties{Stats: m} |
There was a problem hiding this comment.
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?
tools/database/dbc/item_effect.go
Outdated
| 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 | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 😄
tools/database/dbc/spell_effect.go
Outdated
| case effect.EffectAura == A_MOD_RANGED_ATTACK_POWER: | ||
| if scalesWithIlvl && effect.Coefficient != 0 && spell.Attributes[8]&0x1000 != 0 { |
There was a problem hiding this comment.
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)
tools/database/dbc/spell_effect.go
Outdated
| propPoints := dbcInstance.RandomPropertiesByIlvl[ilvl][proto.ItemQuality_ItemQualityEpic][0] // Epic 0 for items | ||
| stats[proto.Stat_StatRangedAttackPower] = math.Floor(float64(propPoints) * effect.Coefficient) |
There was a problem hiding this comment.
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)
}
tools/database/dbc/spell_effect.go
Outdated
| if statMod := RatingModToStat[rating]; statMod != -1 { | ||
| if scalesWithIlvl && effect.Coefficient != 0 && spell.Attributes[8]&0x1000 == 0 { |
There was a problem hiding this comment.
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
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
NerdEgghead
left a comment
There was a problem hiding this 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
There was a problem hiding this comment.
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?
proto/spell.proto
Outdated
| string buff_name = 9; | ||
| ItemEffectType type = 2; |
There was a problem hiding this comment.
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?
proto/spell.proto
Outdated
| // The key represents a Class enum key | ||
| map<int32, double> spec_modifiers = 5; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
| factory_StatBonusEffect(config, func(agent core.Agent) ExtraSpellInfo { | ||
| factory_StatBonusEffect(config, func(agent core.Agent, _ proto.ItemLevelState) ExtraSpellInfo { |
There was a problem hiding this comment.
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.
sim/common/shared/shared_utils.go
Outdated
| procSpell = extraSpell(agent) | ||
| procSpell = extraSpell(agent, proto.ItemLevelState_Base) |
There was a problem hiding this comment.
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.
tools/database/dbc/item_effect.go
Outdated
| // Parses a UIItem and loops through Scaling Options for that item. | ||
| func MergeItemEffectsForAllStatesNew(parsed *proto.UIItem) *proto.ItemEffect { |
There was a problem hiding this comment.
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.
tools/database/dbc/item_effect.go
Outdated
| if parsed.Id == 92127 { | ||
| fmt.Println("hello2", parsed, parsed.Ilvl, opt.Ilvl, opt) | ||
| } |
There was a problem hiding this comment.
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?
tools/database/dbc/item_effect.go
Outdated
|
|
||
| func realPpmScale(spell Spell) int { |
There was a problem hiding this comment.
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.
tools/database/dbc/item_effect.go
Outdated
|
|
||
| func realPpmModifier(spell Spell, itemLevel int) (float64, map[int32]float64) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
tools/database/dbc/spell_effect.go
Outdated
| const BASE_LEVEL = 85 | ||
| const BASE_LEVEL = 90 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
proto/spell.proto
Outdated
| int32 effect_duration = 2; // seconds | ||
|
|
There was a problem hiding this comment.
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.
sim/core/character.go
Outdated
| func (character *Character) GetConjuredCD() *Timer { | ||
| return character.GetOrInitTimer(&character.conjuredCD) | ||
| return character.GetOrInitSpellCategoryTimer(30) | ||
| } | ||
| func (character *Character) GetPotionCD() *Timer { | ||
| return character.GetOrInitTimer(&character.potionCD) |
There was a problem hiding this comment.
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.
tools/database/dbc/item_effect.go
Outdated
| for _, se := range dbcInstance.SpellEffects[id] { | ||
| if s := se.ParseStatEffect(sp.HasAttributeAt(11, 0x4), itemLevel); s != &emptyStats { |
There was a problem hiding this comment.
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.
proto/spell.proto
Outdated
| double ppm = 3; | ||
| int32 rppm_scale = 4; |
There was a problem hiding this comment.
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.
tools/database/dbc/spell_effect.go
Outdated
| 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] |
There was a problem hiding this comment.
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).
Test/itemeffects
No description provided.