• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Mui Spell Solutions/Variations

Status
Not open for further replies.
Level 17
Joined
Nov 13, 2006
Messages
1,814
when i posted my 1st spells i used simple locals and triggersleepaction (alias Wait in gui)

!!!notice, for every spell u can use same hashtable so dont need another for channel abilities, 1 for fast things because timer id is unique, dont will overwrited)

since that i tryed involve this, so i show few example:

1. Variation for missile kind's

Advantage:
- need 1, maximum 2 hashtable (second only if u want filter if a unit already got damage or no)
- Easily readable

Disadvantage:
- use more timer, but i think its pretty low disadvantage since with this the whole system more readabl.e and also since this spell types are fast, dont running same time too much timer, mostly timer lifetime around few second

example:
Distance Shrink (move caster foward with x distance if dont have anything what block the caster way)

JASS:
function Trig_Distance_Shrink_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A009'
endfunction

function Shrinking takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local integer cv
    local unit u = LoadUnitHandle(udg_Spell_Table, id, 1)
    local unit pu
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local real ang = LoadReal(udg_Spell_Table, id, 2)
    local real nx = LoadReal(udg_Spell_Table, id, 5) + 50 * Cos(ang * bj_DEGTORAD)
    local real ny = LoadReal(udg_Spell_Table, id, 6) + 50 * Sin(ang * bj_DEGTORAD)
    local real distance = LoadReal(udg_Spell_Table, id, 3) - 50
    local integer cliff = LoadInteger(udg_Spell_Table, id, 4)

    if distance > 0 and IsTerrainWalkable(nx, ny) and cliff==GetTerrainCliffLevel(nx, ny) then
        call SaveReal(udg_Spell_Table, id, 3, distance)
        call SaveReal(udg_Spell_Table, id, 5, nx)
        call SaveReal(udg_Spell_Table, id, 6, ny)
        call GroupEnumUnitsInRange(udg_UG, nx, ny, 60, null)
        loop
            set pu = FirstOfGroup(udg_UG)
            exitwhen (pu==null)
            if IsUnitEnemy(pu, GetOwningPlayer(u)) then
                set cv = GetUnitUserData(pu)
                if LoadInteger(udg_Misc_Table, id, cv) == 0 then
                     call AddUnitBuff (pu, 37, -200, 5, -1, "overhead")
                endif
                call SaveInteger(udg_Misc_Table, id, cv, 1)
            endif
            call GroupRemoveUnit(udg_UG, pu)
        endloop
        call DestroyEffect(AddSpecialEffect ("Abilities\\Spells\\Orc\\Shockwave\\ShockwaveMissile.mdl", nx , ny))
        call TimerStart(t, 0.03, false, function Shrinking)

    else
        call SetUnitVertexColor(u, 255, 255, 255, 255)
        if GetLocalPlayer() == GetOwningPlayer(u) then
            call ClearSelection()
            call SelectUnit(u, true)
        endif
        call SetUnitPosition(u, nx, ny)
        call PauseTimer(t)
        call DestroyTimer(t)
        call FlushChildHashtable(udg_Spell_Table, id)
        call FlushChildHashtable(udg_Misc_Table, id)
    endif

    set t = null
    set u = null
    set pu = null
endfunction

function Trig_Distance_Shrink_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real ang = GetUnitFacing(u)
    local integer cliff = GetTerrainCliffLevel(GetUnitX(u), GetUnitY(u))
    local timer t = CreateTimer()
    local integer alv = GetUnitAbilityLevel(u, 'A009')    
    local integer id = GetHandleId(t)
    local real distance = 350+alv*50

    call SetUnitVertexColor(u, 255, 255, 255, 0)

    call SaveUnitHandle(udg_Spell_Table, id, 1, u)
    call SaveReal(udg_Spell_Table, id, 2, ang)
    call SaveReal(udg_Spell_Table, id, 3, distance)
    call SaveInteger(udg_Spell_Table, id, 4, cliff)
    call SaveReal(udg_Spell_Table, id, 5, GetUnitX(u))
    call SaveReal(udg_Spell_Table, id, 6, GetUnitY(u))
    call TimerStart(t, 0.03, false, function Shrinking)

    set u = null
    set t = null
endfunction

//===========================================================================
function InitTrig_Distance_Shrink takes nothing returns nothing
    set gg_trg_Distance_Shrink = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Distance_Shrink, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Distance_Shrink, Condition( function Trig_Distance_Shrink_Conditions ) )
    call TriggerAddAction( gg_trg_Distance_Shrink, function Trig_Distance_Shrink_Actions )
endfunction


2. Channeling kind's

Advantage:
- need 1 hashtable (need only for store the timer id, rest we can read from variable arrays)
- Easily readable
- Easiliy configurable

Notice
- we dont need to make string check each time just need a trigger then we can do just boolean check if channeling interrupted or no

Disadvantage:
- use more timer, but i think this also not problem if u use more kind of channeling ability with different period, channeling abilities also running mostly with 1sec or longer periods so i dont think its laggy :D

example:
Blizzard (the classic blizzard remade for Gui Damage Engine)


JASS:
function Trig_Blizzard_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A008'
endfunction

function BlizzardTimer takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit u = LoadUnitHandle(udg_Channel_Table, id, 1)
    local integer cv = GetUnitUserData(u)
    local unit pu
    local real x = udg_Channel_X[cv]
    local real y = udg_Channel_Y[cv]
    local real mana = GetUnitState(u, UNIT_STATE_MANA)
    local integer i = 0
     
    set udg_Channel_Dur[cv] = udg_Channel_Dur[cv] + 1

    if mana >= udg_Channel_ManaCost[cv] and udg_Channel_Boolean[cv] then
        set udg_Channel_Count[cv] = udg_Channel_Count[cv] + 1
        call SetUnitState (u, UNIT_STATE_MANA, (mana - udg_Channel_ManaCost[cv]))
        loop
            exitwhen i > 15
            call DestroyEffect(AddSpecialEffect ("Abilities\\Spells\\Human\\Blizzard\\BlizzardTarget.mdl", x + 250 * Cos(GetRandomReal(0, 360.00) * bj_DEGTORAD) , y + 250 * Sin(GetRandomReal(0, 360.00) * bj_DEGTORAD)))
            set i = i + 1
        endloop
    endif

    if udg_Channel_Dur[cv] > 1 then
        set udg_Channel_Count[cv] = udg_Channel_Count[cv] - 1
        call GroupEnumUnitsInRange(udg_UG, x, y, 300.00, null)
            loop
                set pu = FirstOfGroup(udg_UG)
                exitwhen (pu==null)
                if IsUnitEnemy(pu, GetOwningPlayer(u)) then
                    call AddUnitBuff (pu, 37, -200, 5, -1, "overhead")
                    set udg_DamageEventType = 2
                    call UnitDamageTarget(u, pu, udg_Channel_Dmg[cv], true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                endif
                call GroupRemoveUnit(udg_UG, pu)
            endloop
    endif

    if udg_Channel_Count[cv] == 0 then
        call PauseTimer(t)
        call DestroyTimer(t)
        call FlushChildHashtable(udg_Channel_Table, id)
        if udg_Channel_CasterSF[cv] != null then
            call IssueImmediateOrder( u, "stop" )
            call DestroyEffect(udg_Channel_CasterSF[cv])

        endif
    else
        call TimerStart(t, 1, false, function BlizzardTimer)
    endif

    set t = null
    set u = null
    set pu = null
endfunction

function Trig_Blizzard_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real x = GetSpellTargetX()
    local real y = GetSpellTargetY()
    local real radius = 300.00
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    local integer cv = GetUnitUserData(u)
    local integer alv = GetUnitAbilityLevel(u, 'A008')
    local real dmg = alv * udg_TotalSpellPower[GetPlayerId(GetTriggerPlayer())+1] / 5 + udg_TotalSpellPower[GetPlayerId(GetTriggerPlayer())+1] +1


    set udg_Channel_Boolean[cv] = true
    set udg_Channel_X[cv] = x
    set udg_Channel_Y[cv] = y
    set udg_Channel_ManaCost[cv] = 10
    set udg_Channel_Dmg[cv] = dmg
    if GetUnitState(u, UNIT_STATE_MANA) >= 10 then
        set udg_Channel_Count[cv] = 0
        set udg_Channel_Dur[cv] = 0
        call SaveUnitHandle(udg_Channel_Table, id, 1, u)
        call TimerStart(t, 0.5, false, function BlizzardTimer)
        set udg_Channel_CasterSF[cv] = AddSpecialEffectTarget("Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl", u, "origin")
    endif
    set t = null
    set u = null
endfunction

//===========================================================================
function InitTrig_Blizzard takes nothing returns nothing
    set gg_trg_Blizzard = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Blizzard, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Blizzard, Condition( function Trig_Blizzard_Conditions ) )
    call TriggerAddAction( gg_trg_Blizzard, function Trig_Blizzard_Actions )
endfunction

and stop/finish channeling trigger
JASS:
function Trig_Change_crap_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local integer cv = GetUnitUserData(u)
    if udg_H_Channel[cv] == true then
        set udg_H_Channel[cv] = false
    endif
    if udg_Channel_Boolean[cv] == true then
        set udg_Channel_Boolean[cv] = false
    endif
    set u = null
endfunction

//===========================================================================
function InitTrig_Change_crap takes nothing returns nothing
    set gg_trg_Change_crap = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Change_crap, EVENT_PLAYER_UNIT_SPELL_FINISH )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Change_crap, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddAction( gg_trg_Change_crap, function Trig_Change_crap_Actions )
endfunction



3. Over Time kind's (like heal overtime,damage over time, regeneration over time)

Advantage:
- use variables and linked list, i think its enough fast way
- use only 1 timer

Disadvantage:
- less configurable the timer periods but i dont know if need to change the 1sec interval

example:
Heal/Damage (classic things)


this to map header
JASS:
function AddDot takes unit u, unit ut, integer dottype, real dmg, integer dur, integer eff returns nothing
    set udg_Dot_Index = udg_Dot_Index + 1
    set udg_Dot_Caster[udg_Dot_Index] = u
    set udg_Dot_Target[udg_Dot_Index] = ut
    set udg_Dot_Damage[udg_Dot_Index] = dmg
    set udg_Dot_Type[udg_Dot_Index] = dottype
    set udg_Dot_Timer[udg_Dot_Index] = dur
    set udg_Dot_Effect[udg_Dot_Index] = eff
endfunction

function RemoveDot takes integer index returns nothing
    if index == udg_Dot_Index then
        set udg_Dot_Caster[udg_Dot_Index] = null
        set udg_Dot_Target[udg_Dot_Index] = null
        set udg_Dot_Damage[udg_Dot_Index] = 0
        set udg_Dot_Type[udg_Dot_Index] = 0
        set udg_Dot_Timer[udg_Dot_Index] = 0
        set udg_Dot_Effect[udg_Dot_Index] = 0
    else
        set udg_Dot_Caster[index] = udg_Dot_Caster[udg_Dot_Index]
        set udg_Dot_Target[index] = udg_Dot_Target[udg_Dot_Index]
        set udg_Dot_Damage[index] = udg_Dot_Damage[udg_Dot_Index]
        set udg_Dot_Type[index] = udg_Dot_Type[udg_Dot_Index]
        set udg_Dot_Timer[index] = udg_Dot_Timer[udg_Dot_Index]
        set udg_Dot_Effect[index] = udg_Dot_Effect[udg_Dot_Index]
    endif
    set udg_Dot_Index = udg_Dot_Index - 1
endfunction

timer periodic trigger

JASS:
function Trig_Timmer_Trigger_Actions takes nothing returns nothing
local integer i = 0
    loop
        exitwhen i > udg_Dot_Index
        
        if IsUnitType(udg_Dot_Caster[i], UNIT_TYPE_DEAD) or IsUnitType(udg_Dot_Target[i], UNIT_TYPE_DEAD) then
            call RemoveDot(i)
        else

            set udg_Dot_Timer[i] = udg_Dot_Timer[i] - 1
   
            if udg_Dot_Effect[i] > - 1 then
                call DestroyEffect(AddSpecialEffectTarget(udg_Effects[udg_Dot_Effect[i]], udg_Dot_Target[i], "chest"))
            endif
            if udg_Dot_Type[i] == 100 then
                call SetWidgetLife(udg_Dot_Target[i], GetWidgetLife(udg_Dot_Target[i]) + udg_Dot_Damage[i])
            elseif udg_Dot_Type[i] == 200 then
                call SetUnitState (udg_Dot_Target[i], UNIT_STATE_MANA, (GetUnitState(udg_Dot_Target[i], UNIT_STATE_MANA) - udg_Dot_Damage[i]))
            else
                set udg_DamageEventType = udg_Dot_Type[i]
                call UnitDamageTarget(udg_Dot_Caster[i], udg_Dot_Target[i], udg_Dot_Damage[i], true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            endif
        endif



        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Timmer_Trigger takes nothing returns nothing
    set gg_trg_Timmer_Trigger = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Timmer_Trigger, 1.00 )
    call TriggerAddAction( gg_trg_Timmer_Trigger, function Trig_Timmer_Trigger_Actions )
endfunction

then u can just call like this

regen
JASS:
function UltimateRegen_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00Q'
endfunction

function Trig_UltimateRegen_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer duration = 30
local integer effectid = 13
//i saved the strings for special effects to a string array and i just store this way the string array index what i want for this heal over timer
local integer overtimeid = 100
//if overtime 100 then i handle like heal, else its deal damage
local real maxhp = GetUnitState( u,UNIT_STATE_MAX_LIFE)
local real amount = maxhp * GetUnitAbilityLevel(u, 'A00Q') / 100.00

call AddDot (u, u,overtimeid,amount,duration,13)

set u = null
endfunction

//===========================================================================
function InitTrig_Ultimate_Regeneration takes nothing returns nothing
    set gg_trg_Ultimate_Regeneration = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Ultimate_Regeneration, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Ultimate_Regeneration, Condition( function UltimateRegen_Conditions ) )
    call TriggerAddAction( gg_trg_Ultimate_Regeneration, function Trig_UltimateRegen_Actions )
endfunction

dot
JASS:
function Trig_Dotw_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A02L'
endfunction

function Trig_Dotw_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer dmgtype=1
local integer dur=30
local integer effectid=7
local integer alv = GetUnitAbilityLevel(u, 'A02L')
local real dmg = 25 * alv + GetHeroStr(u,true)*alv
call AddDot (GetTriggerUnit(), GetSpellTargetUnit(),dmgtype,dmg,dur,effectid)
set u = null
endfunction

//===========================================================================
function InitTrig_Mad_cut takes nothing returns nothing
    set gg_trg_Mad_cut = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mad_cut, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Mad_cut, Condition( function Trig_Dotw_Conditions ) )
    call TriggerAddAction( gg_trg_Mad_cut, function Trig_Dotw_Actions )
endfunction



4. Theoretical/Custom Buff types or linked list in linked list style

Advantage:
- use variables and linked list +hashtables
- use only 1 timer and with this timer u can check every buff for each buffed unit
- where u can use this? u can make your custom buff, custom accurancy system buff, usefull for custom slowdown, custom damage increase or damage reduction buff/debuff, life steal or just for make a buff from revive (u can add a dummy spellbook, inside it the reincarnation then remove if duration is over) so its usefull, but i think have many solution, mainly in vjass have structs, just i dont sue vjass :D

Disadvantage:
- maybe the bigger hash usage? (i use 3-4 too,1 for order.1 for timer, 1 for amount and 1 for special effects)


example:
custom buff system


map header
JASS:
function RemoveUnitBuff takes integer id, integer index, boolean check returns nothing
    local integer cv = GetUnitUserData( udg_Buff_Unit[id] )
    local integer buffid
    local real spd
    local integer amount 
    local integer t 
    local integer i = 1
    local integer times
    local unit u = udg_Buff_Unit[id]
if not check then
    set buffid = LoadInteger( udg_Buff_Index_Table, cv, index )
    set amount = LoadInteger( udg_Buff_Amount, cv, buffid ) * -1
    set t = LoadInteger( udg_Buff_Index_Table, cv, buffid )
	if index != udg_Buff_Unit_Index[cv] then
	   call SaveInteger( udg_Buff_Index_Table, cv, index, LoadInteger( udg_Buff_Index_Table, cv, udg_Buff_Unit_Index[cv] ) )
	endif
	
	set udg_Buff_Unit_Index[cv] = udg_Buff_Unit_Index[cv] - 1
	call DestroyEffect ( LoadEffectHandle(udg_Buff_EffectTable, cv, buffid))	

    call SaveInteger( udg_Buff_Timer, cv, buffid, 0 )
    call SaveInteger( udg_Buff_Amount, cv, buffid, 0 )
    if buffid == 1 then
        set udg_B_Crit[cv] = udg_B_Crit[cv] + amount
    elseif buffid == 2 then
        set udg_B_CritDmg[cv] = udg_B_CritDmg[cv] + amount
    elseif buffid == 3 then
        set udg_B_Eva[cv] = udg_B_Eva[cv] + amount
 //till u want the buffs
     endif


    if udg_Buff_Unit_Index[cv] == 0 then
          set udg_Buff_Unit_Id[cv] = 0
       if id != udg_Buff_Index then
          set udg_Buff_Unit[id] = udg_Buff_Unit[udg_Buff_Index]
          set udg_Buff_Unit_Id[GetUnitUserData(udg_Buff_Unit[id])] = id
          call RemoveUnitBuff (id, 0, true)
       else

       endif
        set udg_Buff_Index = udg_Buff_Index - 1
        call FlushChildHashtable(udg_Buff_Index_Table, cv)
        call FlushChildHashtable(udg_Buff_EffectTable, cv)
        call FlushChildHashtable(udg_Buff_Timer, cv)
        call FlushChildHashtable(udg_Buff_Amount, cv)
    endif
else
loop
exitwhen id > udg_Buff_Index
    set cv = GetUnitUserData( udg_Buff_Unit[id] )
            loop
                exitwhen i > udg_Buff_Unit_Index[cv]
                set buffid = LoadInteger(udg_Buff_Index_Table, cv, i)
                set times = LoadInteger(udg_Buff_Timer, cv, buffid)
                if times == 0 then
                     call RemoveUnitBuff (id, i, false)
                endif
                set i = i + 1
            endloop
set id=id+1
endloop
endif
set u = null
endfunction

function ClearUnitBuff takes integer id returns nothing
    local unit u = udg_Buff_Unit[id]
    local integer e
    local integer v
    local integer i = 0
    local integer cv = GetUnitUserData(u)
    local integer buffid
    local integer po
    local integer amount
    local real spd

            loop
                exitwhen i > udg_Buff_Unit_Index[cv]
                set buffid = LoadInteger(udg_Buff_Index_Table, cv, i)
                call RemoveUnitBuff (id, i, false)
                set i = i + 1
            endloop
endfunction


function AddUnitBuff takes unit u, integer buffid, integer amount, integer dur, integer effectid, string apoint returns nothing
    local integer cv = GetUnitUserData(u)
    local integer times = LoadInteger( udg_Buff_Timer, cv, buffid )
    local integer oldv = LoadInteger( udg_Buff_Amount, cv, buffid )
    local integer diff = amount - oldv
    local integer pl = GetPlayerId(GetOwningPlayer(u)) + 1
    local real spd

    if udg_Buff_Unit_Id[cv] == 0 then
	set udg_Buff_Index = udg_Buff_Index + 1
        set udg_Buff_Unit[udg_Buff_Index] = u
        set udg_Buff_Unit_Id[cv] = udg_Buff_Index
    
    endif

    if times == 0 then
	    set udg_Buff_Unit_Index[cv] = udg_Buff_Unit_Index[cv] + 1
		call SaveInteger( udg_Buff_Index_Table, cv, udg_Buff_Unit_Index[cv], buffid )
		
       if effectid > - 1 then
	    call SaveEffectHandle(udg_Buff_EffectTable, cv, buffid, AddSpecialEffectTarget( udg_Effects[effectid], u, apoint ))
        endif
    endif
	
    call SaveInteger( udg_Buff_Amount, cv, buffid, amount )
    call SaveInteger( udg_Buff_Timer, cv, buffid, dur )

    if buffid == 1 then
        set udg_B_Crit[cv] = udg_B_Crit[cv] + diff
    elseif buffid == 2 then
        set udg_B_CritDmg[cv] = udg_B_CritDmg[cv] + diff
    elseif buffid == 3 then
        set udg_B_Eva[cv] = udg_B_Eva[cv] + diff
//etc till u have buff     ..................................
    endif
 
endfunction
 
Status
Not open for further replies.
Top