please help,
I attempt to recreate an ability, simply an activable barrage (for those familiar with Dota, it's like Medusa's Split Shot)
It was based off Defend (to achieve damage factor) ability which gives hidden spellbook containing Barrage when activated.
I managed to finish the ability, it was working as intended until I found some intriguing bug happening after few testing.
After few minutes (not specifically after certain time or after some condition met), the Hero instantly dies upon activating said ability.
this death is not attributed to any killing unit or player, and there is no trigger that calls KillUnit().
I've searched for possible known bug regarding Defend, Barrage, and Spellbook ability.
I found a related thread that said using spellbook to abuse changing max hp with item ability might kill said unit if certain condition is met (involving upgrades too though my ability didn't use any hp changing effect that can trigger same effect).
I've used BJDebugMsg to at least pinpoint where the problem might be,
for start, the debug message display correct behavior on any case (turn on/off, death while ability on/off, ability is given at correct level),
and when the bug starts to happen, it displays that the unit is issued an order to "undefend" twice on activation before dying (I assume that's a trait of having the ability and using UnitDex library that uses undefend) which didn't happen before.
below is the trigger of said ability, I have tried using few different method to handle add/removing Barrage to avoid crash due to removing ability from dead unit.
Attempt #2 is using the simple method used in early version of Dota's Medusa's Split Shot (found in the unprotected old version) which hopefully solves the problem (it didn't)
all attempt works on testing and display the same problem after further testing.
for now, I assume this got something to do with UnitDex's undefend.
I haven't tried using different base ability (e.g. Immolation or Mana Shield) though it won't give the damage factor feature that comes with Defend ability.
anyone has similar experience on before? or know what causing this?
or is there other workaround to achieve the same result?
thank you in advance.
I attempt to recreate an ability, simply an activable barrage (for those familiar with Dota, it's like Medusa's Split Shot)
It was based off Defend (to achieve damage factor) ability which gives hidden spellbook containing Barrage when activated.
I managed to finish the ability, it was working as intended until I found some intriguing bug happening after few testing.
After few minutes (not specifically after certain time or after some condition met), the Hero instantly dies upon activating said ability.
this death is not attributed to any killing unit or player, and there is no trigger that calls KillUnit().
I've searched for possible known bug regarding Defend, Barrage, and Spellbook ability.
I found a related thread that said using spellbook to abuse changing max hp with item ability might kill said unit if certain condition is met (involving upgrades too though my ability didn't use any hp changing effect that can trigger same effect).
I've used BJDebugMsg to at least pinpoint where the problem might be,
for start, the debug message display correct behavior on any case (turn on/off, death while ability on/off, ability is given at correct level),
and when the bug starts to happen, it displays that the unit is issued an order to "undefend" twice on activation before dying (I assume that's a trait of having the ability and using UnitDex library that uses undefend) which didn't happen before.
below is the trigger of said ability, I have tried using few different method to handle add/removing Barrage to avoid crash due to removing ability from dead unit.
JASS:
scope SplitShot initializer init
globals
private constant integer ABIL_ID = 'A01Y'
private constant integer DUMMY_ID_1 = 'D00N'
private constant integer DUMMY_ID_2 = 'D00O'
private constant real INTERVAL = 0.03125
private constant string ORDER_ON = "defend"
private constant string ORDER_OFF = "undefend"
private trigger TRIG
private Table SS = 0
private integer ORDER_ON_ID
private integer ORDER_OFF_ID
endglobals
private struct Data
unit hero
timer t
integer lv
integer id
boolean onSS
endstruct
private function onLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
local Data data = GetTimerData(t)
// check if the Hero still exist and not repicked or removed from game
if HERO[data.id] == null then
// call UnitRemoveAbility(data.hero,DUMMY_ID_1)
call SS.remove(GetUnitId(data.hero))
set data.hero = null
set data.t = null
call data.destroy()
call ReleaseTimer(t)
set t = null
return
endif
// check if Hero died, automatically order off
if GetWidgetLife(data.hero) < .405 then
set data.onSS = false
set t = null
return
endif
if not data.onSS or GetUnitAbilityLevel(data.hero,'DOOM') > 0 then
// if GetWidgetLife(data.hero) > .405 then
// call UnitRemoveAbility(data.hero,DUMMY_ID_1)
call IssueImmediateOrder(data.hero,ORDER_OFF)
call PauseTimer(t)
// endif
endif
set t = null
endfunction
private function onOrder takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer orderid = GetIssuedOrderId()
local Data data
if orderid == ORDER_ON_ID then
// if GetWidgetLife(u) > .405 then
set data = SS[GetUnitId(u)]
set data.onSS = true
call UnitAddAbility(data.hero,DUMMY_ID_1)
call SetUnitAbilityLevel(data.hero,DUMMY_ID_1,data.lv)
call TimerStart(data.t,INTERVAL,true,function onLoop)
// endif
elseif orderid == ORDER_OFF_ID then
// if GetWidgetLife(u) > .405 then
set data = SS[GetUnitId(u)]
set data.onSS = false
call UnitRemoveAbility(data.hero,DUMMY_ID_1)
call PauseTimer(data.t)
// endif
endif
set u = null
return false
endfunction
private function onLearn takes nothing returns nothing
local unit u = GetTriggerUnit()
local Data data
if GetLearnedSkill() == ABIL_ID then
if GetLearnedSkillLevel() == 1 then
call TriggerRegisterUnitEvent(TRIG,u,EVENT_UNIT_ISSUED_ORDER)
set data = Data.create()
set data.hero = u
set data.t = NewTimer()
set data.lv = 1
set data.id = GetPlayerId(GetOwningPlayer(u))
set data.onSS = false
call SetTimerData(data.t,data)
set SS[GetUnitId(u)] = data
else
set data = SS[GetUnitId(u)]
set data.lv = GetUnitAbilityLevel(u,ABIL_ID)
endif
endif
set u = null
endfunction
private function init takes nothing returns nothing
local integer i = 0
//disable ability so it doesn't show up in the command card
loop
exitwhen i > (MAX_PLAYERS+1)
call SetPlayerAbilityAvailable(Player(i),DUMMY_ID_1,false)
set i = i + 1
endloop
set TRIG = CreateTrigger()
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function onLearn)
call TriggerAddCondition(TRIG,Condition(function onOrder))
set SS = Table.create()
call XE_PreloadAbility(ABIL_ID)
call XE_PreloadAbility(DUMMY_ID_1)
call XE_PreloadAbility(DUMMY_ID_2)
set ORDER_ON_ID = OrderId(ORDER_ON)
set ORDER_OFF_ID = OrderId(ORDER_OFF)
endfunction
endscope
JASS:
scope SplitShot initializer init
globals
private constant integer ABIL_ID = 'A01Y'
private constant integer DUMMY_ID_1 = 'D00N'
private constant integer DUMMY_ID_2 = 'D00O'
private constant real INTERVAL = 0.03125
private constant string ORDER_ON = "defend"
private constant string ORDER_OFF = "undefend"
private integer ORDER_ON_ID
private integer ORDER_OFF_ID
endglobals
private function switchOff takes nothing returns nothing
local unit u = GetDyingUnit()
if GetUnitAbilityLevel(u,ABIL_ID) > 0 then
call UnitRemoveAbility(u,DUMMY_ID_1)
endif
set u = null
endfunction
private function onOrder takes nothing returns nothing
local unit v = GetTriggerUnit()
if GetUnitAbilityLevel(v,ABIL_ID) > 0 and GetIssuedOrderId()==ORDER_ON_ID and GetUnitAbilityLevel(v,DUMMY_ID_1) == 0 then
call UnitAddAbility(v,DUMMY_ID_1)
call SetUnitAbilityLevel(v,DUMMY_ID_2,GetUnitAbilityLevel(v,ABIL_ID))
endif
if GetUnitAbilityLevel(v,ABIL_ID) > 0 and GetIssuedOrderId()==ORDER_OFF_ID and GetUnitAbilityLevel(v,DUMMY_ID_1) > 0 then
call UnitRemoveAbility(v,DUMMY_ID_1)
endif
set v = null
endfunction
private function init takes nothing returns nothing
local integer i = 0
//disable ability so it doesn't show up in the command card
loop
exitwhen i > (MAX_PLAYERS+1)
call SetPlayerAbilityAvailable(Player(i),DUMMY_ID_1,false)
set i = i + 1
endloop
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function onOrder)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function switchOff)
call XE_PreloadAbility(ABIL_ID)
call XE_PreloadAbility(DUMMY_ID_1)
call XE_PreloadAbility(DUMMY_ID_2)
set ORDER_ON_ID = OrderId(ORDER_ON)
set ORDER_OFF_ID = OrderId(ORDER_OFF)
endfunction
endscope
JASS:
scope SplitShot initializer init
globals
private constant integer ABIL_ID = 'A01Y'
private constant integer DUMMY_ID_1 = 'D00N'
private constant integer DUMMY_ID_2 = 'D00O'
private constant real INTERVAL = 0.03125
private constant string ORDER_ON = "defend"
private constant string ORDER_OFF = "undefend"
private trigger TRIG
private Table SS = 0
private integer ORDER_ON_ID
private integer ORDER_OFF_ID
endglobals
private struct Data
unit hero
timer t
integer lv
integer id
boolean onSS
method onDestroy takes nothing returns nothing
call SS.remove(GetUnitId(.hero))
set .hero = null
set .t = null
endmethod
endstruct
private function onLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
local Data data = GetTimerData(t)
// check if the Hero still exist and not repicked or removed from game
if HERO[data.id] == null then
call data.destroy()
call ReleaseTimer(t)
set t = null
return
endif
// check if Hero died
if GetWidgetLife(data.hero) < .405 then
set data.onSS = false
set t = null
return
endif
if not data.onSS or GetUnitAbilityLevel(data.hero,'DOOM') > 0 then
if GetUnitAbilityLevel(data.hero,DUMMY_ID_1) > 0 then
call BJDebugMsg(GetUnitName(data.hero)+" remove SplitShot1 " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_1)))
call BJDebugMsg(GetUnitName(data.hero)+" remove SplitShot2 " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_2)))
call UnitRemoveAbility(data.hero,DUMMY_ID_1)
call BJDebugMsg(GetUnitName(data.hero)+" removed SplitShot1 " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_1)))
call BJDebugMsg(GetUnitName(data.hero)+" removed SplitShot2 " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_2)))
endif
endif
set t = null
endfunction
private function onOrder takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer orderid = GetIssuedOrderId()
local Data data
if orderid == ORDER_ON_ID then
set data = SS[GetUnitId(u)]
set data.onSS = true
call UnitAddAbility(data.hero,DUMMY_ID_1)
call BJDebugMsg(GetUnitName(data.hero)+" on SplitShot " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_2)))
call SetUnitAbilityLevel(data.hero,DUMMY_ID_2,data.lv)
call BJDebugMsg(GetUnitName(data.hero)+" on SplitShot " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_2)))
elseif orderid == ORDER_OFF_ID then
set data = SS[GetUnitId(u)]
set data.onSS = false
call BJDebugMsg(GetUnitName(data.hero)+" off SplitShot " + I2S(GetUnitAbilityLevel(data.hero,DUMMY_ID_2)))
endif
set u = null
return false
endfunction
private function onLearn takes nothing returns nothing
local unit u = GetTriggerUnit()
local Data data
if GetLearnedSkill() == ABIL_ID then
if GetLearnedSkillLevel() == 1 then
call TriggerRegisterUnitEvent(TRIG,u,EVENT_UNIT_ISSUED_ORDER)
set data = Data.create()
set data.hero = u
set data.t = NewTimer()
set data.lv = 1
set data.id = GetPlayerId(GetOwningPlayer(u))
set data.onSS = false
set SS[GetUnitId(u)] = data
call SetTimerData(data.t,data)
call TimerStart(data.t,INTERVAL,true,function onLoop)
call BJDebugMsg(GetUnitName(data.hero)+" learn SplitShot " + I2S(GetUnitAbilityLevel(u,ABIL_ID)))
else
set data = SS[GetUnitId(u)]
set data.lv = GetUnitAbilityLevel(u,ABIL_ID)
call BJDebugMsg(GetUnitName(data.hero)+" learn SplitShot " + I2S(GetUnitAbilityLevel(u,ABIL_ID)))
endif
endif
set u = null
endfunction
private function init takes nothing returns nothing
local integer i = 0
//disable ability so it doesn't show up in the command card
loop
exitwhen i > (MAX_PLAYERS+1)
call SetPlayerAbilityAvailable(Player(i),DUMMY_ID_1,false)
set i = i + 1
endloop
set TRIG = CreateTrigger()
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function onLearn)
call TriggerAddCondition(TRIG,Condition(function onOrder))
set SS = Table.create()
call XE_PreloadAbility(ABIL_ID)
call XE_PreloadAbility(DUMMY_ID_1)
call XE_PreloadAbility(DUMMY_ID_2)
set ORDER_ON_ID = OrderId(ORDER_ON)
set ORDER_OFF_ID = OrderId(ORDER_OFF)
endfunction
endscope
Attempt #2 is using the simple method used in early version of Dota's Medusa's Split Shot (found in the unprotected old version) which hopefully solves the problem (it didn't)
all attempt works on testing and display the same problem after further testing.
for now, I assume this got something to do with UnitDex's undefend.
I haven't tried using different base ability (e.g. Immolation or Mana Shield) though it won't give the damage factor feature that comes with Defend ability.
anyone has similar experience on before? or know what causing this?
or is there other workaround to achieve the same result?
thank you in advance.