ability.duration > .... bug still exists
cosinussinus opened this issue · 11 comments
Is there ever a case where argParse
returning nil is valid, which i think is what happens here?
I'm thinking something more fundamental where RunCondition
returns false before it even gets to calling fn
if argParse
is provided on the condition but it returned nil
@bloerwald is the expert on non-existence and default values. Does DengSir/tdBattlePetScript#26 answer your question? I guess we may need to change this to just error instead
I'm not a programming expert and it might be bad practice, but how about:
Extension/Conditions.lua:
Addon:RegisterCondition('ability.duration', { type = 'compare', argParse = Util.ParseAbility }, function(owner, pet, ability)
local isUsable, currentCooldown, currentLockdown = C_PetBattles.GetAbilityState(owner, pet, ability)
return ability and currentCooldown or infinite
end)
-->
Addon:RegisterCondition('ability.duration', { type = 'compare', argParse = Util.ParseAbility }, function(owner, pet, ability)
local isUsable, currentCooldown, currentLockdown = C_PetBattles.GetAbilityState(owner, pet, ability)
return currentCooldown
end)
Core/Condition.lua:
function Condition:RunCondition(condition)
local owner, pet, cmd, arg, op, value = self:ParseCondition(condition)
local fn = self.apis[cmd]
local opts = self.opts[cmd]
if not fn then
error('Big Bang !!!!!!')
end
local res = fn(owner, pet, arg)
return opTabler[opts.type][op](res, value)
end
-->
function Condition:RunCondition(condition)
local owner, pet, cmd, arg, op, value = self:ParseCondition(condition)
local fn = self.apis[cmd]
local opts = self.opts[cmd]
if not fn then
error('Big Bang !!!!!!')
end
local res = fn(owner, pet, arg)
if res == nil and cmd == 'ability.duration' then
return false
end
return opTabler[opts.type][op](res, value)
end
I sadly just don't think there is a generic "correct" solution to this question, but I feel like "if condition invalid, false
" is wrong. Given
use(#1) [ self.ability(nonexistent).duration > 1 ]
use(#2)
the old implementation always did 1, the new one always does 2. Given
use(#2) [ self.ability(nonexistent).duration <= 1 ]
use(#1)
the old implementation also always did 1, the new one always does 1.
Which one does the user want? The user writing script A apparently wants always-2, but what does the user want given they wrote the script the other way around? Surely they also want 2, not 1?
This means that you can't write generic scripts like that if the condition evaluates to false. The old implementation might be unexpected, but it is consistent at least. a = not a
is a bad thing to do imo, even in an error case. I'd rather add support to make scripts error out at that point…
It seems fairly consistent to me if a condition is always false for an ability your pet doesn't have (so doesn't matter if your comparison is > 1
or <= 1
, both would fail the condition as it never does the comparison with something like #18 ).
That's pretty much be same behavior use
has where it just skips if you specify the name of a spell the current pet doesn't have.
Does a nonexistent ability have a duration? I don't think so. At least we can't prove it. So it should be false or an error.
.ability([^)]*).duration
exists in 798 out of 5895 scripts on xufu but only 109 scripts compare with <
or >
. There are only 47 scripts using .ability([^)]*).duration *>
, the only case that would change with the suggested patch. Whatever decision you take, I suggest to not do it quickly and discuss it with users, even if these are fringe cases, as it affects all other ability conditions.
In reality, most scripts I have actually looked at will not be influenced by this change: They all already guard against that case or they use duration < x
which doesn't change.
The following scripts are probably influenced (only relevant lines of script copied):
https://wow-petguide.com/Strategy/13027/Rogue_Azerite-Dragonkin
standby [ self.ability(only-with-pet1).duration > 1 & self.ability(only-with-pet1).duration > 1 ]
This is wrong in my definition of duration: It will always fire, while the suggested change only fires with pet1. Your change would fix it (or defining no-cooldown = 0).
https://wow-petguide.com/Strategy/8639/Snakes_on_a_Terrace
ability(Prowl:536) [enemy.ability(Dive:564).duration>2]
ability(Prowl:536) [enemy(#3).active & self(#1).active]
This is fine: only enemy 2 has Dive, so no change at all whether duration = inf or 0.
https://wow-petguide.com/Strategy/15167/Snakes_on_a_Terrace-Elemental
ability(Blazing Yip:1359) [self(#2).dead & self.ability(Puppies of the Flame:1354).duration>3]
Condition will be true, but only the pet having Puppies has Blazing, so no change.
https://wow-petguide.com/Strategy/10758/Airborne_Defense_Force-Dragonkin
use(Arcane Storm:589) [ !enemy(#2).active & enemy.round<4 ]
use(Arcane Storm:589) [ !weather(Arcane Winds:590) & enemy.ability(Toxic Fumes:2349).duration>1 ]
Only enemy 2 has Toxic Fumes. This condition might be true for Enemy 1&3 wrongly. Currently, it is true if [weather is not up, enemy #1 round < 4, enemy #3 round < 4]
, with change it is true if [enemy #1 round < 4, enemy #3 round < 4]
. That's a change, but it is irrelevant to the strategy if I understand correctly.
https://wow-petguide.com/Strategy/10828/Extra_Pieces-Critter
use(Stampede:163) [ self.ability(Dodge:312).duration>1 ]
Only pets with both abilities exist.
https://wow-petguide.com/Strategy/9603/Cursed_Spirit
-- 9.01 duration bug repair
if [enemy(#1).active]
use(Locust Swarm:1007) [enemy.ability(Ghostly Bite:654).duration>1]
endif
--
use(Scratch:119)
This script assumes that .ability(nonexistent).duration = 0
rather than inf
. Your change would be fine, but defining no-cooldown = 0 would do the same. Alternatively,
use(Scratch:119) [enemy.ability(Ghostly Bite:654).duration <= 1]
use(Locust Swarm:1007)
would do the same.
https://wow-petguide.com/Strategy/5940/Crypt_Fiend
if [ self.ability(Breath:115).usable ]
use(Decoy:334) [ enemy.ability(Nightmare:1731).duration<2 ]
use(Breath:115)
endif
dead code, no pets have Breath.
Note that I only looked at duration >
cases where missing-ability.duration = inf
would be an issue, not at any where missing-ability.duration = 0
. You can look at the strategies using duration >
https://wow-petguide.com/Strategy/32, https://wow-petguide.com/Strategy/2409, https://wow-petguide.com/Strategy/6845, https://wow-petguide.com/Strategy/2936, https://wow-petguide.com/Strategy/2938, https://wow-petguide.com/Strategy/2939, https://wow-petguide.com/Strategy/4779, https://wow-petguide.com/Strategy/3864, https://wow-petguide.com/Strategy/3172, https://wow-petguide.com/Strategy/8639, https://wow-petguide.com/Strategy/4047, https://wow-petguide.com/Strategy/5943, https://wow-petguide.com/Strategy/5920, https://wow-petguide.com/Strategy/5938, https://wow-petguide.com/Strategy/5939, https://wow-petguide.com/Strategy/5940, https://wow-petguide.com/Strategy/6267, https://wow-petguide.com/Strategy/6391, https://wow-petguide.com/Strategy/7431, https://wow-petguide.com/Strategy/7490, https://wow-petguide.com/Strategy/9603, https://wow-petguide.com/Strategy/9882, https://wow-petguide.com/Strategy/9894, https://wow-petguide.com/Strategy/9898, https://wow-petguide.com/Strategy/10379, https://wow-petguide.com/Strategy/10758, https://wow-petguide.com/Strategy/10828, https://wow-petguide.com/Strategy/10968, https://wow-petguide.com/Strategy/11822, https://wow-petguide.com/Strategy/11963, https://wow-petguide.com/Strategy/12152, https://wow-petguide.com/Strategy/12634, https://wow-petguide.com/Strategy/12544, https://wow-petguide.com/Strategy/13027, https://wow-petguide.com/Strategy/13743, https://wow-petguide.com/Strategy/14273, https://wow-petguide.com/Strategy/15167, https://wow-petguide.com/Strategy/15172, https://wow-petguide.com/Strategy/15223, https://wow-petguide.com/Strategy/15436, https://wow-petguide.com/Strategy/15759, https://wow-petguide.com/Strategy/16175, https://wow-petguide.com/Strategy/16340, https://wow-petguide.com/Strategy/16430, https://wow-petguide.com/Strategy/16581, https://wow-petguide.com/Strategy/16584, https://wow-petguide.com/Strategy/16585, or those using .ability.duration <
https://wow-petguide.com/Strategy/1277, https://wow-petguide.com/Strategy/1446, https://wow-petguide.com/Strategy/7046, https://wow-petguide.com/Strategy/1692, https://wow-petguide.com/Strategy/1807, https://wow-petguide.com/Strategy/2064, https://wow-petguide.com/Strategy/6919, https://wow-petguide.com/Strategy/3732, https://wow-petguide.com/Strategy/2976, https://wow-petguide.com/Strategy/3074, https://wow-petguide.com/Strategy/3162, https://wow-petguide.com/Strategy/4408, https://wow-petguide.com/Strategy/4072, https://wow-petguide.com/Strategy/4077, https://wow-petguide.com/Strategy/9310, https://wow-petguide.com/Strategy/5193, https://wow-petguide.com/Strategy/5943, https://wow-petguide.com/Strategy/9301, https://wow-petguide.com/Strategy/5920, https://wow-petguide.com/Strategy/5938, https://wow-petguide.com/Strategy/5939, https://wow-petguide.com/Strategy/5940, https://wow-petguide.com/Strategy/6601, https://wow-petguide.com/Strategy/6870, https://wow-petguide.com/Strategy/9322, https://wow-petguide.com/Strategy/7431, https://wow-petguide.com/Strategy/7766, https://wow-petguide.com/Strategy/9321, https://wow-petguide.com/Strategy/8816, https://wow-petguide.com/Strategy/9268, https://wow-petguide.com/Strategy/9238, https://wow-petguide.com/Strategy/9249, https://wow-petguide.com/Strategy/9257, https://wow-petguide.com/Strategy/9312, https://wow-petguide.com/Strategy/9302, https://wow-petguide.com/Strategy/9266, https://wow-petguide.com/Strategy/9267, https://wow-petguide.com/Strategy/9270, https://wow-petguide.com/Strategy/9374, https://wow-petguide.com/Strategy/10158, https://wow-petguide.com/Strategy/11526, https://wow-petguide.com/Strategy/10449, https://wow-petguide.com/Strategy/10522, https://wow-petguide.com/Strategy/10530, https://wow-petguide.com/Strategy/10684, https://wow-petguide.com/Strategy/10891, https://wow-petguide.com/Strategy/10932, https://wow-petguide.com/Strategy/12027, https://wow-petguide.com/Strategy/12788, https://wow-petguide.com/Strategy/12591, https://wow-petguide.com/Strategy/13167, https://wow-petguide.com/Strategy/13170, https://wow-petguide.com/Strategy/13172, https://wow-petguide.com/Strategy/13168, https://wow-petguide.com/Strategy/13169, https://wow-petguide.com/Strategy/13171, https://wow-petguide.com/Strategy/14891, https://wow-petguide.com/Strategy/14890, https://wow-petguide.com/Strategy/15257, https://wow-petguide.com/Strategy/15515, https://wow-petguide.com/Strategy/15759, https://wow-petguide.com/Strategy/15808 yourself, if you want.
Of course a non-existent ability doesn't have a cooldown, so that would be an error. I would instantly agree with an error since that's safe. My goal with the old ticket was making it semantically sane and somewhat-how-people-expect-it, while still holding the requirement of basic logic, i.e. a = a => a != not a
. I'd also agree on changing the definition to non-existent = 0
if users confirm that's what they expect. non-existent = silent ignore
does not seem to be what people want, most of the time.
From a programming perspective, I find a line being silently ignored completely weird. I don't feel like that's the same as with use (nonexistent)
since that's action vs condition. An action not working is different from the condition for using that action not working. x [true]
can still fail to execute. x [true]
should not not execute if the current pet doesn't have true
though.
What people expect:
[self.ability(nonexistent).duration>1] same as [self.ability(nonexistent).exists & ...duration>1] (implicit) --> false & error --> False comes first. So I think most people would expect a false.
What they don't expect:
[self.ability(nonexistent).duration>1] --> [self.ability(nonexistent).exists & ...duration>1] --> true & ('inf' > 1) --> True (#17 (comment))
[self.ability(nonexistent).duration>1] --> [self.ability(nonexistent).exists & ...duration>1] --> true & ('inf' > 1)
isn’t what is happening. [self.ability(nonexistent).duration>1] --> 'inf' > 1
. [self.ability(nonexistent).exists & self.ability(nonexistent).duration>1] --> FALSE & ('inf' > 1)
.
i have seen the script
use(ultimate) [ enemy.ability(dodge).duration > 0 ]
use(filler)
which surely wants to use ultimate if the enemy can’t ever dodge.
I‘ll remove myself from this discussion and let @axc450 decide.
I know under the hood is "[self.ability(nonexistent).duration>1] --> 'inf' > 1" happening. But I would expect that the ability will be checked first if the ability exists. Maybe it's wrong but that's what I would expect.
use(ultimate) [ enemy.ability(dodge).duration > 0 ]
use(filler)
--> I don't want to get the information whether the enemy can dodge or not. It's clear, I want to know the duration (>0). Big difference. And when the ability doesn't exists then I can't get duration informations --> error
Anyway, thanks for the time and effort to analyze the scripts from xufu.
Ultimately, I think the correct answer here is that things need to be redesigned internally so that functions stop returning bools and return actual errors which will halt the scripts on the offending line. There are good points on both sides but when you have something invalid, I dont think it should return a bool at all.
Leaving this issue open until we have had a proper think about how a redesign would work.