HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Trigger failing when target is dead

03-20-2010, 12:44 PM#1
Michael Peppers
Long story short, I have a spell based on Mana Burn that transforms the caster into a copy of the targeted unit for some time (30 seconds).

During a MP test, a player using this spell found himself without both the caster and the copy-unit... he told me that the target unit died from the Mana Burn damage (20).
What bugs me is that, if the copy-unit hasn't been created for this reason, why was this effect set m.e = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", u, "overhead") shown anyway? (There's no way that it could have been shown because of other triggers or abilities)

Does anybody know why the "replacement" unit wasn't (probably) created but the effect was still shown?

Collapse Assimilate:
scope Assimilate initializer Init

private struct morph
    unit c
    effect e
    integer i
    integer o
    real m
    integer n
endstruct

private function Morph takes nothing returns nothing
local morph m = morph(GetTimerData(GetExpiredTimer()))
local player p = GetOwningPlayer(m.c)
local real x = GetUnitX(m.c)
local real y = GetUnitY(m.c)
local real f = GetUnitFacing(m.c)
local real l = (GetUnitState(m.c, UNIT_STATE_LIFE) / GetUnitState(m.c, UNIT_STATE_MAX_LIFE))
local boolean s = IsUnitSelected(m.c, GetLocalPlayer())
local unit u = null
if GetWidgetLife(m.c) > 0.405 then
    if m.n == 0 then
        set m.m = GetUnitState(m.c, UNIT_STATE_MANA)
    else
        call DestroyEffect(m.e)
    endif
    call KillUnit(m.c)
    call RemoveUnit(m.c)
    call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBoltImpact.mdl", x, y))
    set u = CreateUnit(p, m.i, x, y, f)
    if (GetUnitState(u, UNIT_STATE_MAX_LIFE) * l) > 50 then
        call SetWidgetLife(u, GetUnitState(u, UNIT_STATE_MAX_LIFE) * l)
    else
        call SetWidgetLife(u, 50)
    endif
    if s then
        call SelectUnit(u, true)
    endif
    if m.n == 0 then
        set m.e = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", u, "overhead")
        call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MAX_MANA) / 2)
        call UnitAddAbility(u, 'Bed1')
        set m.i = m.o
        set m.c = u
        set m.n = 1
        call UnitApplyTimedLife(u, 'Beb2', 30)
        call TimerStart(GetExpiredTimer(), 29.9, false, function Morph)
    else
        call SetUnitState(u, UNIT_STATE_MANA, m.m)
        call ReleaseTimer(GetExpiredTimer())
        call morph.destroy(m)
    endif
else
    if m.e != null then
        call DestroyEffect(m.e)
    endif
    call morph.destroy(m)
    call ReleaseTimer(GetExpiredTimer())
endif
set u = null
set p = null
endfunction

private function Main takes nothing returns boolean
local morph m
local real dx = 0
local real dy = 0
local real d = 0
local timer t = null
if GetSpellAbilityId() == 'Bea3' then
    set m = morph.create()
    set m.i = GetUnitTypeId(GetSpellTargetUnit())
    set m.o = GetUnitTypeId(GetTriggerUnit())
    set m.n = 0
    set m.c = GetTriggerUnit()
    set t = NewTimer()
    set dx = GetUnitX(GetSpellTargetUnit()) - GetUnitX(m.c)
    set dy = GetUnitY(GetSpellTargetUnit()) - GetUnitY(m.c)
    set d = SquareRoot(dx * dx + dy * dy)
    call SetTimerData(t, m)
    call TimerStart(t, d, false, function Morph)
endif
return false
endfunction

//===========================================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()

    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Main))

endfunction

endscope

FYI, 'Bed1' is a buffplacer.

And no, I want only the problem to be fixed, don't bug me with posts like "oh, you could write this or that in a different way, it'd look better".
03-20-2010, 02:04 PM#2
Ammorth
Add a check to make sure m.i is not 0 (maybe m.o aswell).
03-20-2010, 02:16 PM#3
Michael Peppers
Done a quick test, and even when the target is killed by the spell, m.i returns the right value and the spell fires correctly (tried on a Sorceress), so either there was some other stuff happening around and no one noticed or that bug is unit-dependant. Or I misunderstood what the player said. I will let you know, sorry guys =P
03-21-2010, 12:34 AM#4
Anitarf
One thing to look into would be what happens if you kill a unit with it that does not leave a corpse, such as a wisp (or a regular unit killed by an artillery attack, but that doesn't apply here since we're doing the killing with mana burn): those units are removed from memory pretty much immediately when they die. However, the spell event should fire before the damage event, so even when such units are killed by the spell the function GetUnitTypeId should return the correct value where you use it. Still, this is the only way I can think of it being unit-dependant so it's worth checking out.

Another thing to consider is if the caster had almost no hp at the time of the switch and then switched to a unit that has much smaller max hp. If for example the caster had 1/100hp and switched to a unit with 20maxhp, its life would now be set to 0.2hp which would kill it immediately.

Quote:
Originally Posted by Michael Peppers
And no, I want only the problem to be fixed, don't bug me with posts like "oh, you could write this or that in a different way, it'd look better".
Sorry, but some things can't be ingored. Your use of locations, for starters, just too ugly. Also, stuffing everything into one unreadable Morph function instead of having two, which would likely take fewer lines of code in the end. Also, in your Main function, that last if statement after the "else", serves no purpose other than to crash the thread by reading an uninitialized variable. The similar if statement that you have at the end of the Morph function is not needed since you already know the expiring timer and m can't be null/0 if this function was executed.
03-21-2010, 02:28 AM#5
Michael Peppers
Quote:
Originally Posted by Anitarf
One thing to look into would be what happens if you kill a unit with it that does not leave a corpse, such as a wisp (or a regular unit killed by an artillery attack, but that doesn't apply here since we're doing the killing with mana burn): those units are removed from memory pretty much immediately when they die. However, the spell event should fire before the damage event, so even when such units are killed by the spell the function GetUnitTypeId should return the correct value where you use it. Still, this is the only way I can think of it being unit-dependant so it's worth checking out.
This has been in my mind for quite some time (I mean the possibility of losing the Id because of damage). Still, I fear that, since this spell uses a timer (the one with the distance calculation) to make the whole ability look right ingame, the Id would be lost anyway during the "Morph" trigger... and I can't seem to find a way to make it work in this case. (Or maybe... Hide + Invulnerability + Timed Life in case of a unit that has a so low life that it could be killed by the spell... but sounds rude)
Quote:
Originally Posted by Anitarf
Another thing to consider is if the caster had almost no hp at the time of the switch and then switched to a unit that has much smaller max hp. If for example the caster had 1/100hp and switched to a unit with 20maxhp, its life would now be set to 0.2hp which would kill it immediately.
Didn't think about that. Alright, just fixed it.
Quote:
Originally Posted by Anitarf
Sorry, but some things can't be ingored. Your use of locations, for starters, just too ugly.
Maybe I shouldn't have said that, but most times I needed a trigger problem solved most posts were about "fancy coding" and not serious feedback. (no offence to anyone)

Alright, locations removed. I'm not much into using them anyway, and also that BJ forces me to create locations even when its code needs reals, so... yeah, it's the right thing to do.
Quote:
Originally Posted by Anitarf
Also, stuffing everything into one unreadable Morph function instead of having two, which would likely take fewer lines of code in the end.
Not much more code, and my stuff is perfectly readable to me. Of course, if this was not a spell meant for personal use but for a submission I would've coded it differently.
Quote:
Originally Posted by Anitarf
Also, in your Main function, that last if statement after the "else", serves no purpose other than to crash the thread by reading an uninitialized variable. The similar if statement that you have at the end of the Morph function is not needed since you already know the expiring timer and m can't be null/0 if this function was executed.
Yeah, you're right, bad habits...

What else can I tell you? Uhm... thanks.
03-21-2010, 01:53 PM#6
Anitarf
Quote:
Originally Posted by Michael Peppers
This has been in my mind for quite some time (I mean the possibility of losing the Id because of damage). Still, I fear that, since this spell uses a timer (the one with the distance calculation) to make the whole ability look right ingame, the Id would be lost anyway during the "Morph" trigger... and I can't seem to find a way to make it work in this case. (Or maybe... Hide + Invulnerability + Timed Life in case of a unit that has a so low life that it could be killed by the spell... but sounds rude)
The thing is, you store the unit's id to a variable the moment the spell is cast, so it really shouldn't matter if the unit still exists when the timer expires. So unless you actually tested it and confirmed this to be the problem, I doubt it really is, it was just the only possibility of it depending on the unit type that I could think of.
03-21-2010, 05:07 PM#7
Michael Peppers
Quote:
Originally Posted by Anitarf
The thing is, you store the unit's id to a variable the moment the spell is cast, so it really shouldn't matter if the unit still exists when the timer expires. So unless you actually tested it and confirmed this to be the problem, I doubt it really is, it was just the only possibility of it depending on the unit type that I could think of.
I couldn't test if that problem happens, so I simply displayed my doubts, since I experienced problems like that before in another spell.

Anyway, just tested it, and yes, it returns the correct value even if the unit with that id is long since dead.

Also, units probably won't get removed from memory immediately, but after their Death animation (or corpse explosion or something like that) if they don't leave any corpse, if this supposition is right I'm already safe. (but I can't test it now)