HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Power of Corruption spell - Water Shield

07-09-2007, 04:06 AM#1
moyack
Hi:

From now on I'm going to fill this section with my problems in JASS for my project Power of Corruption.

I've developed one spell called Water shield, as a replacement of frost armor. This spell gives to the allied a shield which absorbs a random percentage of damage, up to 50% of the damage dealt.

Because I'm using dynamic triggers, I decided to test one Vex's experiments, where you use the conditional function instead of the action function. You can see here.

After hours of testing, it doesn't work, and I don't know if I'm skipping something or what. If somebody ask me: "why aren't you using structs" the answer is simple: this is one spell that I did some months ago as a testing, and I'm on the conversion process. In fact, it worked before I did the modifications with the conditionals.

Here's the code. The yellow parts shows where the script creates the dymamic triggers:

Collapse JASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                            Water Shield Spell.                                              *
//*                                                By Moyack.                                                   *
//*                                                  V.1.0.                                                     *
//*                                                                                                             *
//***************************************************************************************************************
library WaterShield initializer InitTrig_WaterShield requires CSSafety, HandleVars
//***************************************************************************************************************
//* The constant functions where you can modify the ability properties
//*
private constant function WaterShield_SpellID takes nothing returns integer
    // Dummy Ability Rawcode. Spell based on FROST ARMOR
    return 'A01F'
endfunction

private constant function WaterShield_BuffID takes nothing returns integer
    // Dummy buff Rawcode. Buff based on FROST ARMOR
    return 'B00T'
endfunction

private constant function WaterShield_dt takes nothing returns real
    // number of times that verify 
    return 0.1
endfunction

private constant function WaterShield_Chance takes integer level returns real
    // Returns the chance of absorbing any damage
    return 0.5 + 0.25 * (level - 1)
endfunction


//***************************************************************************************************************
//* The custom functions
//*

private function WaterShield_Damage takes nothing returns boolean
    // Shows the effect and control the damage dealt to the shield's owner
    local unit c = GetTriggerUnit()
    local timer t = GetHandleTimer(c, "timer")
    local integer l = GetHandleInt(t, "level")
    local real ch = GetRandomReal(0., WaterShield_Chance(l) * 100.)
    local real dam = (100. - ch) * GetEventDamage() / 100.
    local texttag tx = CreateTextTag()
    call SetWidgetLife(GetTriggerUnit(), GetWidgetLife(GetTriggerUnit()) + dam)
    call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(WaterShield_SpellID(), EFFECT_TYPE_SPECIAL, 0), GetTriggerUnit(), "chest"))
    call SetTextTagText(tx, I2S(R2I(ch)) + "%", 0.023)
    call SetTextTagPosUnit(tx, c, 5.)
    call SetTextTagColor(tx, 0, 174, 239, 0)
    call SetTextTagVelocity(tx, 0., 120. * 0.071 / 128.)
    call SetTextTagPermanent(tx, false)
    call SetTextTagLifespan(tx, 3.)
    call SetTextTagFadepoint(tx, 2.)
    call SetTextTagVisibility(tx, true)
    set c = null
    set t = null
    return false
endfunction

private function WaterShield_loop takes nothing returns nothing
    //Checks when the spell ends so it can clean the variables
    local timer tm = GetExpiredTimer()
    if GetUnitAbilityLevel(GetHandleUnit(tm, "target"), WaterShield_BuffID()) == 0 then
        call DestroyTrigger(GetHandleTrigger(tm, "trigger"))
        call FlushHandleLocals(GetHandleUnit(tm, "target"))
        call FlushHandleLocals(tm)
        call ReleaseTimer(tm)
    endif
    set tm = null
endfunction
//***************************************************************************************************************
//*                                                                                                             *
//*                                      Water Shield Casting Functions                                         *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* Water Shield Control casting condition
//*
private function WaterShield_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == WaterShield_SpellID()
endfunction

private function WaterShield_Actions takes nothing returns nothing
        // Creates the trigger that will detect the damage to the target unit
    local timer tm = NewTimer()
    local boolexpr Cnd = Condition(function WaterShield_Damage)
    call SetHandleHandle(GetSpellTargetUnit(), "timer", tm)
    call SetHandleHandle(tm, "caster", GetTriggerUnit())
    call SetHandleHandle(tm, "target", GetSpellTargetUnit())
    call SetHandleInt(tm, "level", GetUnitAbilityLevel(GetSpellAbilityUnit(), WaterShield_SpellID()))
    call SetHandleHandle(tm, "trigger", CreateTrigger())
    call TriggerRegisterUnitEvent(GetHandleTrigger(tm, "trigger"), GetSpellTargetUnit(), EVENT_UNIT_DAMAGED)
    call TriggerAddCondition(GetHandleTrigger(tm, "trigger"), Cnd)
    call TimerStart(tm, WaterShield_dt(), true, function WaterShield_loop)
    call DestroyBoolExpr(Cnd)
    set tm = null
    set Cnd = null
endfunction

//===========================================================================
function InitTrig_WaterShield takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function WaterShield_Conditions ) )
    call TriggerAddAction(t, function WaterShield_Actions)
    call Preload(GetAbilityEffectById(WaterShield_SpellID(), EFFECT_TYPE_SPECIAL, 0))
    set t = null
endfunction

endlibrary


Thanks for your help
07-09-2007, 05:34 AM#2
PitzerMike
You shouldn't the destroy the condition if you don't want to corrupt the trigger.
Remove the line
Collapse JASS:
call DestroyBoolExpr(Cnd)
07-10-2007, 02:10 AM#3
moyack
Lovely!!! it works!!! thanks PitzerMike :)

Because I needed to keep the Boolexpr until the spell is finished, I implemented the structs so we can handle better that handle.

Here's the code of this spell so far, working in the best way XD

Collapse Water Shield converted totally to vJASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                            Water Shield Spell.                                              *
//*                                                By Moyack.                                                   *
//*                                                  V.2.0.                                                     *
//*                                                                                                             *
//***************************************************************************************************************
library WaterShield initializer InitTrig_WaterShield requires CSSafety, HandleVars

struct watershield
    unit c
    unit t
    integer lvl
    trigger trg
    boolexpr b
    
    static method create takes unit caster, unit target, integer SpellLevel, code DamFunc returns watershield
        local watershield w = watershield.allocate()
        set w.c = caster
        set w.t = target
        set w.lvl = SpellLevel
        set w.trg = CreateTrigger()
        set w.b = Condition(DamFunc)
        return w
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyTrigger(.trg)
        call DestroyBoolExpr(.b)
        set .trg = null
        set .b = null
    endmethod
endstruct

struct wscatcher
    watershield ws
    timer t
    static method create takes watershield w returns wscatcher
        local wscatcher wc = wscatcher.allocate()
        set wc.ws = w
        set wc.t = NewTimer()
        return wc
    endmethod
    
    method onDestroy takes nothing returns nothing
        call watershield.destroy(.ws)
        call ReleaseTimer(.t)
    endmethod
endstruct

globals
//***************************************************************************************************************
//* The constant functions where you can modify the ability properties
//*
    private constant integer WaterShield_SpellID = 'A01F' // Dummy Ability Rawcode. Spell based on FROST ARMOR
    private constant integer WaterShield_BuffID  = 'B00T' // Dummy buff Rawcode. Buff based on FROST ARMOR
    private constant real    WaterShield_dt      = 0.1    // number of times that the spell verify the buff
endglobals

private constant function WaterShield_Chance takes integer level returns real
    // Returns the chance of absorbing any damage
    return 0.75 + 0.25 * (level - 1)
endfunction


//***************************************************************************************************************
//* The custom functions
//*

private function WaterShield_Damage takes nothing returns boolean
    local unit c = GetTriggerUnit()
    local timer tm = GetHandleTimer(c, "timer")
    local wscatcher wc = wscatcher(GetHandleInt(tm, "wc"))
    local integer l = wc.ws.lvl
    local real ch = GetRandomReal(0., WaterShield_Chance(l) * 100.)
    local real dam = ch * GetEventDamage() / 100.
    local texttag tx = CreateTextTag()
    call SetWidgetLife(c, GetWidgetLife(c) + dam)
    call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(WaterShield_SpellID, EFFECT_TYPE_SPECIAL, 0), GetTriggerUnit(), "chest"))
    call SetTextTagText(tx, I2S(R2I(ch)) + "%", 0.023)
    call SetTextTagPosUnit(tx, c, 5.)
    call SetTextTagColor(tx, 0, 174, 239, 0)
    call SetTextTagVelocity(tx, 0., 350. * 0.071 / 128.)
    call SetTextTagPermanent(tx, false)
    call SetTextTagLifespan(tx, 3.)
    call SetTextTagFadepoint(tx, 2.)
    call SetTextTagVisibility(tx, true)
    set c = null
    set tm = null
    return false
endfunction

private function WaterShield_loop takes nothing returns nothing
    local timer tm = GetExpiredTimer()
    local wscatcher wc = wscatcher(GetHandleInt(tm, "wc"))
    if GetUnitAbilityLevel(wc.ws.t, WaterShield_BuffID) == 0 then
        call wc.destroy()
    endif
    set tm = null
endfunction
//***************************************************************************************************************
//*                                                                                                             *
//*                                      Water Shield Casting Functions                                         *
//*                                                                                                             *
//***************************************************************************************************************

//***************************************************************************************************************
//* Water Shield Control casting condition
//*
private function WaterShield_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == WaterShield_SpellID
endfunction

private function WaterShield_Actions takes nothing returns nothing
    local watershield ws = watershield.create(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetSpellAbilityUnit(), WaterShield_SpellID), function WaterShield_Damage)
    local wscatcher wc = wscatcher.create(ws)
    call TriggerRegisterUnitEvent(ws.trg, ws.t, EVENT_UNIT_DAMAGED)
    call TriggerAddCondition(ws.trg, ws.b)
    call SetHandleInt(wc.t, "wc", wc)
    call SetHandleHandle(ws.t, "timer", wc.t)
    call TimerStart(wc.t, WaterShield_dt, true, function WaterShield_loop)
endfunction

//===========================================================================
function InitTrig_WaterShield takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function WaterShield_Conditions ) )
    call TriggerAddAction(t, function WaterShield_Actions)
    call Preload(GetAbilityEffectById(WaterShield_SpellID, EFFECT_TYPE_SPECIAL, 0))
    set t = null
endfunction

endlibrary

Now with this, this spell creates and releases the timer easily. I have now other questions:

Is there any way to replace the GetHandleInt / SetHandleInt functions, so I can say bye to HandleVars??
07-10-2007, 11:48 AM#4
HyperActive
Good to see PoC advancing.
07-10-2007, 06:16 PM#5
PitzerMike
Yeah, let's just hope the league of anti corruption won't step in.
07-10-2007, 06:34 PM#6
Captain Griffen
All BoolExprs created from a particular condition are the same handle. They are odd creatures. Destroying a 'local' boolexpr can destroy others.
07-10-2007, 07:31 PM#7
moyack
Quote:
Originally Posted by Captain Griffen
All BoolExprs created from a particular condition are the same handle. They are odd creatures. Destroying a 'local' boolexpr can destroy others.
I've noticed that odd situation, but I was not sure if it was true. So it would be better if I do something like this:

Collapse JASS:
globals
//***************************************************************************************************************
//* The constant functions where you can modify the ability properties
//*
    private constant integer WaterShield_SpellID = 'A01F' // Dummy Ability Rawcode. Spell based on FROST ARMOR
    private constant integer WaterShield_BuffID  = 'B00T' // Dummy buff Rawcode. Buff based on FROST ARMOR
    private constant real    WaterShield_dt      = 0.1    // number of times that the spell verify the buff
// Become this boolexpr a glbal variable which should be initialized at the beginning and not destroyed.
        private boolexpr ShieldScript
endglobals

...

function InitTrig_WaterShield takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function WaterShield_Conditions ) )
    call TriggerAddAction(t, function WaterShield_Actions)
    call Preload(GetAbilityEffectById(WaterShield_SpellID, EFFECT_TYPE_SPECIAL, 0))
    set ShieldScript = Condition(function WaterShield_Damage)
    set t = null
endfunction
07-10-2007, 08:18 PM#8
Toadcop
does the alot of "stars" (*) making your script more awesome ? ... xD

well boolexpr are like strings they are stored in some table and they need to be inited only 1 time per function name. to destroy them it's a pure imbecility :freak:
07-10-2007, 08:54 PM#9
moyack
Quote:
does the alot of "stars" (*) making your script more awesome ?
probably :p

I like how it looks, that's all, plus it helps me to separate parts of the code, making more readable to me.
07-11-2007, 06:49 AM#10
Toadcop
moyack ;)

Quote:
Because I'm using dynamic triggers, I decided to test one Vex's experiments, where you use the conditional function instead of the action function. You can see here.
well =) it's not really Vexorian's something =) there are many pararel persons who have "discoverd" it. (including me :P)