HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Strange Issue with Immolation

05-07-2007, 12:44 AM#1
moyack
Hi guys.

I'm working with some spells, and I've been having problems with one ability based on Immolation. The spell is called Soul Control, and it takes control of the best enemy unit nearest to the caster, but it drains mana proportionally to the level of the controlled unit.

Everything works fine except in one thing. If the caster dies while the immolation effect is active, when I revive it, it reborns paused (I can select it, but I looks disabled)

I've checked if I have any bug in the code, but it looks fine.

Is probably the ability itself?? Am I missing something??

Any help will be really appreciated, thanks beforehand.

EDIT: If you want to catch the bug, the caster must have very few life and mana at the same time.

Map Link and spell code




Collapse Soul Control Spell:
//***************************************************************************************************************
//*                                                                                                             *
//*                                            Soul Control Spell.                                              *
//*                                                By Moyack.                                                   *
//*                                                  V.1.0.                                                     *
//*                                                                                                             *
//***************************************************************************************************************
library SoulControl requires HandleVars, CSSafety
//***************************************************************************************************************
//* Here's the constant functions where you can modify the ability properties
//*
constant function SoulControl_SpellID takes nothing returns integer
    //is the ability code of the spell. This spell MUST be based on immolation
    return 'A000' 
endfunction

constant function SoulControl_BuffID takes nothing returns integer
    //is the buff of the spell
    return 'B000' 
endfunction

constant function SoulControl_Radius takes nothing returns real
    return 800. 
endfunction

constant function SoulControl_ManaDrain takes integer level returns real
    return 2. - 0.5 * (level - 1) 
endfunction

//***************************************************************************************************************
//*                                                                                                             *
//*                                      Soul Control Casting Functions                                         *
//*                                                                                                             *
//***************************************************************************************************************
function SoulControl_GetEnemy takes nothing returns boolean
    local boolean b1 = IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit()))
    local boolean b2 = IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false
    local boolean b3 = IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == false
    local boolean b4 = GetWidgetLife(GetFilterUnit()) > 0.405
    return b1 and b2 and b3 and b4
endfunction

function SoulControl_DrainMana takes nothing returns nothing
    local unit c = GetHandleUnit(GetExpiredTimer(), "caster")
    local real m = GetUnitState(c, UNIT_STATE_MANA)
    local integer l = GetUnitAbilityLevel(c, SoulControl_SpellID())
    set m = m - (0.1 * SoulControl_ManaDrain(l) * GetUnitLevel(GetHandleUnit(c, "soul")))
    call SetUnitState(c, UNIT_STATE_MANA, m)
    if m <= 0. then
        call SetUnitOwner(GetHandleUnit(c, "soul"), Player(GetHandleInt(c, "ep")), true)
        call DestroyEffect(GetHandleEffect(c, "fx"))
        call FlushHandleLocals(GetExpiredTimer())
        call ReleaseTimer(GetExpiredTimer())
        call FlushHandleLocals(c)
    endif
    set c = null    
endfunction
//***************************************************************************************************************
//* Soul Control casting condition
//*
function SoulControl_ConditionsOn takes nothing returns boolean
    local boolean b1 = GetIssuedOrderId() == OrderId("immolation")
    local boolean b2 = GetUnitAbilityLevel(GetTriggerUnit(), SoulControl_SpellID()) > 0
    return b1 and b2
endfunction

function SoulControl_ConditionsOff takes nothing returns boolean
    local boolean b1 = GetIssuedOrderId() == OrderId("unimmolation")
    local boolean b2 = GetUnitAbilityLevel(GetTriggerUnit(), SoulControl_SpellID()) > 0
    return b1 and b2
endfunction

//***************************************************************************************************************
//* Soul Control casting Actions
//*
function SoulControl_ActionsOn takes nothing returns nothing
    local unit c = GetTriggerUnit()
    local group g = CreateGroup()
    local boolexpr b = Condition(function SoulControl_GetEnemy)
    local unit u
    local real l = 0.
    local timer t = NewTimer()
    call GroupEnumUnitsInRange(g, GetUnitX(c), GetUnitY(c), SoulControl_Radius(), b)
    call DestroyBoolExpr(b)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        if GetWidgetLife(u) + GetUnitState(u, UNIT_STATE_MANA) > l then
            set l = GetWidgetLife(u) + GetUnitState(u, UNIT_STATE_MANA)
            call SetHandleHandle(c, "soul", u)
        endif
        call GroupRemoveUnit(g, u)
    endloop
    call DestroyGroup(g)
    if GetHandleUnit(c, "soul") == null then
        call IssueImmediateOrder(c, "stop")
        call IssueImmediateOrder(c, "unimmolation")
        call ReleaseTimer(t)
    else
        call DestroyEffect(AddSpecialEffect(GetAbilityEffectById(SoulControl_SpellID(), EFFECT_TYPE_SPECIAL, 1), GetUnitX(c), GetUnitY(c)))
        call SetHandleHandle(t, "caster", c)
        call SetHandleHandle(c, "timer", t)
        call SetHandleInt(c, "ep", GetPlayerId(GetOwningPlayer(GetHandleUnit(c, "soul"))))
        call SetUnitOwner(GetHandleUnit(c, "soul"), GetOwningPlayer(c), true)
        call SetHandleHandle(c, "fx", AddSpellEffectTargetById(SoulControl_SpellID(), EFFECT_TYPE_TARGET, GetHandleUnit(c, "soul"), "overhead"))
        call TimerStart(t, 0.1, true, function SoulControl_DrainMana)
    endif
    set g = null
    set b = null
    set c = null
endfunction

function SoulControl_ActionsOff takes nothing returns nothing
    local unit c = GetTriggerUnit()
    local unit s = GetHandleUnit(c, "soul")
    if s != null then
        call SetUnitOwner(s, Player(GetHandleInt(c, "ep")), true)
        call DestroyEffect(GetHandleEffect(c, "fx"))
        call FlushHandleLocals(GetHandleTimer(c, "timer"))
        call ReleaseTimer(GetHandleTimer(c, "timer"))        
    endif
    call FlushHandleLocals(c)
    set c = null
    set s = null
endfunction

endlibrary
//===========================================================================
function InitTrig_SoulControl takes nothing returns nothing
    local trigger t = CreateTrigger()
    call Preload(GetAbilityEffectById(SoulControl_BuffID(), EFFECT_TYPE_TARGET, 0))
    call Preload(GetAbilityEffectById(SoulControl_SpellID(), EFFECT_TYPE_SPECIAL, 0))
    call Preload(GetAbilityEffectById(SoulControl_SpellID(), EFFECT_TYPE_SPECIAL, 1))
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerAddCondition( t, Condition( function SoulControl_ConditionsOn ) )
    call TriggerAddAction( t, function SoulControl_ActionsOn )
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerAddCondition( t, Condition( function SoulControl_ConditionsOff ) )
    call TriggerAddAction( t, function SoulControl_ActionsOff )
    set t = null
endfunction


Collapse CSSafety Library:
//******************************************************************************************
//*
//* CSSafety 14.0
//* ¯¯¯¯¯¯¯¯
//*
//*  Utilities to make things safer. Currently this simply includes a timer recycling
//* Stack. Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer
//* you no longer have to care about setting timers to null nor about timer related issues
//* with the handle index stack.
//*
//******************************************************************************************
library CSSafety
//==========================================================================================
globals
    timer array cs_timers
    integer cs_timern = 0
endglobals

//==========================================================================================
function NewTimer takes nothing returns timer
    if (cs_timern==0) then
        return CreateTimer()
    endif
 set cs_timern=cs_timern-1
 return cs_timers[cs_timern]
endfunction

//==========================================================================================
function ReleaseTimer takes timer t returns nothing
    call PauseTimer(t)
    if (cs_timern==8191) then
        debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

        //stack is full, the map already has much more troubles than the chance of bug
        call DestroyTimer(t)
    else
        set cs_timers[cs_timern]=t
        set cs_timern=cs_timern+1
    endif    
endfunction

endlibrary


function InitTrig_CSSafety takes nothing returns nothing
endfunction

Collapse HandleVars Library:
//Kattana Handle Vars - Library Version
library HandleVars initializer InitGC

globals
    gamecache gc = null
endglobals
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction

private function InitGC takes nothing returns nothing
    call FlushGameCache(InitGameCache("Test.w3v"))
    set gc = InitGameCache("Test.w3v") 
endfunction

// ===========================
function LocalVars takes nothing returns gamecache
    if gc == null then
       call InitGC()
    endif
    return gc
endfunction

function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
    if value==false then
        call FlushStoredBoolean(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreBoolean(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleBoolean takes handle subject, string name returns boolean
    return GetStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction

function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleGroup takes handle subject, string name returns group
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleLightning takes handle subject, string name returns lightning
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleWidget takes handle subject, string name returns widget
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

endlibrary

//===========================================================================
function InitTrig_HandleVars takes nothing returns nothing
endfunction

Attached Files
File type: w3xSpell Test.w3x (39.0 KB)
05-07-2007, 02:26 AM#2
Szythe
I tested the map out and found no problems (except one small unrelated thing). When the hero died while the immolation was active, and was then resurrected, the hero could walk around fine, and his ability worked perfectly normally.

The small bug is that if the unit you are controlling dies, you should order the hero to unimmolation.
05-07-2007, 02:35 AM#3
Ammorth
I had the same problem you stated moyack.
05-07-2007, 02:37 AM#4
Szythe
Maybe I didn't exactly understand his problem, and didn't notice what it was when it happened? Could you explain it in more detail perhaps. The ability functioned correctly for me even after revival, and even if it was active when the hero died.
05-07-2007, 02:38 AM#5
TheSecretArts
Try on res, making a unit duplicate and removing the afflicted frozen unit
05-07-2007, 02:48 AM#6
moyack
Quote:
Originally Posted by Szythe
The small bug is that if the unit you are controlling dies, you should order the hero to unimmolation
Ohh yes, that's something that I have to fix, but this bug appeared first and it's very important for me to fix that.

I've tested more and I've found when it really happens, when the life and the mana of the caster are very low at the same time (let's say, 5% life and mana). So try to put the caster in this state and test, you will have more chances to see this weird bug.
05-07-2007, 02:50 AM#7
TheSecretArts
try having the drain caused by a unit having immation burn buff or such...
05-07-2007, 03:17 AM#8
Szythe
I can't test it right now, but I'll get to it as soon as I finish this game of DotA (God help my soul)
05-07-2007, 03:22 AM#9
TheSecretArts
eww dota hope that my suggestions help
05-07-2007, 04:09 AM#10
Szythe
I got the bug to occur, trying to find a solution

Got it.

Trigger:
Untitled Trigger 001
Collapse Events
Unit - Lar Akhrol 0004 <gen> Takes damage
Collapse Conditions
(Damage taken) Greater than or equal to (Life of Lar Akhrol 0004 <gen>)
Collapse Actions
Unit - Remove All buffs from Lar Akhrol 0004 <gen>
Unit - Order Lar Akhrol 0004 <gen> to Night Elf Demon Hunter - Deactivate Immolation

JASSify it, but thats the basics. If you ask me, its quite an ingenious solution
05-07-2007, 04:56 AM#11
Toink
Maybe add a limit, like when his hp starts to become low disable/deactivate/whatever it?
05-07-2007, 05:08 AM#12
Szythe
Theres no need. The damage taken event always fires precisely before the unit actually takes the damage, so if a fatal blow is going to be dealt, the last thing that happens before the unit actually takes the damage is this trigger will be run. I added the "remove buffs" part in case the unit is stunned etc. This should be 100% foolproof.
05-07-2007, 05:15 AM#13
Toink
There is such a thing called using timers that expire in 0 seconds.

I heard blu say this..(I think I got it right..)

Use the Unit takes damage event. Store everything you need in variables, attach them to a timer, run the timer and make it expire in 0 seconds and call a function. Use the called function for whatever you want.