-
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
Changes from 8 commits
d61878c
a59e4d7
0c5033d
1a7da20
1189160
bbe6768
026e1b3
6f4b4b5
858a8c1
d7b778f
98f4799
ea33d9a
29f8547
765f258
6da8bea
afc3866
17b6d03
4130f78
3015a4e
e1b1471
dc1c2ef
10b9217
883e24a
378976f
fca85a2
d1ee7ac
fea5316
6d957b7
074a114
8661a52
a7e910d
b18d574
89ac11b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,52 @@ package proto; | |
| option go_package = "./proto"; | ||
|
|
||
| import "common.proto"; | ||
| enum ItemEffectType { | ||
| NONE = 0; | ||
| PROC = 1; | ||
| ON_USE = 2; | ||
| } | ||
|
||
| // Contains only the Enchant info needed by the sim. | ||
| message SimEnchant { | ||
| int32 effect_id = 1; | ||
| repeated double stats = 2; | ||
| ItemEffect enchant_effect = 3; | ||
| } | ||
|
||
|
|
||
| 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; | ||
|
||
| 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; | ||
|
||
| int32 rppm_scale = 4; | ||
| map<int32, double> spec_modifiers = 5; | ||
|
||
| 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; | ||
|
|
||
| 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" | ||
|
|
@@ -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 | ||
|
||
| if isEnchant { | ||
| eligibleSlots = character.ItemSwap.EligibleSlotsForEffect(effectID) | ||
|
|
||
| ench := core.EnchantsByEffectID[effectID] | ||
|
||
| 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 | ||
|
||
| for _, effect := range item.ItemEffects { | ||
| if effect.GetProc() != nil { | ||
| procEffect = effect | ||
| } | ||
| } | ||
|
||
| } | ||
| } | ||
| 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)) | ||
|
||
| var dpm *core.DynamicProcManager | ||
| if (config.PPM != 0) && (config.ProcMask == core.ProcMaskUnknown) { | ||
|
||
| 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) { | ||
|
|
@@ -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) | ||
|
|
@@ -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), | ||
|
||
| 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 { | ||
|
|
@@ -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) {} | ||
|
|
||
|
||
| type StackingStatBonusCD struct { | ||
| Name string | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
|
@@ -169,6 +170,7 @@ func ItemFromProto(pData *proto.SimItem) Item { | |
| SetName: pData.SetName, | ||
| SetID: pData.SetId, | ||
| ScalingOptions: pData.ScalingOptions, | ||
| ItemEffects: pData.ItemEffects, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -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, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
| } | ||
|
||
|
|
||
| func (equipment *Equipment) ToEquipmentSpecProto() *proto.EquipmentSpec { | ||
| return &proto.EquipmentSpec{ | ||
| Items: MapSlice(equipment[:], func(item Item) *proto.ItemSpec { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -274,6 +274,11 @@ func SpellSchoolFromProto(p proto.SpellSchool) SpellSchool { | |
| } | ||
| } | ||
|
|
||
| const ( | ||
| RPPM_HASTE = 1 << (iota) | ||
| RPPM_CRIT | ||
| ) | ||
|
||
|
|
||
| /* | ||
| outcome roll hit/miss/crit/glance (assigns Outcome mask) -> If Hit, Crit Roll -> damage (applies metrics) -> trigger proc | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
||
| } | ||
|
|
||
| func NewDBC() *DBC { | ||
|
|
@@ -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), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -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) | ||
| } | ||
|
||
|
|
||
| // 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 { | ||
|
|
||
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.
@NerdEgghead I think trinkets like this:
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.
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.