- 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)
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
example:
Blizzard (the classic blizzard remade for Gui Damage Engine)
and stop/finish channeling trigger
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
timer periodic trigger
then u can just call like this
regen
dot
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
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
!!!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
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
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