Tom_Kazansky
--- wraith it ! ---
- Reaction score
- 157
I made a buff system, here is its code, it's shortened (removed some functions):
[/spoiler]
this method:
this method take one argument of type GBD (Generic Buff Data)
GBD is used to pass data to "event functions" (function interface), see the chkEventFunc block.
problem I ran into: code in these "event functions" could change the data in the GBD so sometime it's NOT good.
example:
- a unit stomps the ground, Stun is applied to nearby enemies
- one of affected units (let call it "A") has "stun duration reduction"; after applied for "A", duration is changed.
- if it's the last unit: no problem. if it's not the last unit: duration is changed, so...
that's example for "duration", there also case for "bonus" (unit/hero stats)
solutions I think of:
- re-set the duration/bonus before applying to new unit (currently using this)
- make a copy of GBD
GBD struct has many members, calling a function to copy those would take a little bit of performance, right? it's fine or just ONE call but what if there are 10 or 20 or more? what's your opinions?
thanks for reading!
JASS:
scope CustomBuff
globals
constant integer BUFF_STANDARD = 0
constant integer BUFF_STACKING = 1
constant integer BUFF_DURATION_NEW = 0 //take new duration
constant integer BUFF_DURATION_HIGH = 1 //compare old/new duration, take the higher
constant integer BUFF_DURATION_ADD = 2 //add the old and new duration
endglobals
function interface TBuff_Event takes tBuff tb returns nothing
function interface TBuff_EventX takes tBuff tb, tBuff tbo returns nothing
function interface TBuff_EventR takes tBuff tb, unit remover returns nothing
struct tBuffAttribute
boolean pos
boolean neg
boolean psy
boolean mag
boolean aura
boolean univ
integer lvl
boolean silence
boolean slow
boolean abnormal
boolean stealable
integer category = BUFF_STANDARD
integer durType = BUFF_DURATION_NEW
boolean removeOnDead = true
static method create takes integer id returns thistype
local thistype this = thistype.allocate()
set this.pos = HaveSavedBoolean(tj_ABILITYHASH, id, RegisterBuffData_IP)
set this.neg = HaveSavedBoolean(tj_ABILITYHASH, id, RegisterBuffData_IN)
set this.psy = HaveSavedBoolean( tj_ABILITYHASH, id, RegisterBuffData_IPh)
set this.mag = HaveSavedBoolean( tj_ABILITYHASH, id, RegisterBuffData_IM )
set this.aura = HaveSavedBoolean(tj_ABILITYHASH, id, RegisterBuffData_IA)
set this.univ = HaveSavedBoolean(tj_ABILITYHASH, id, RegisterBuffData_IU)
set this.lvl = LoadInteger(tj_ABILITYHASH, id, RegisterBuffData_BPL)
set this.silence = HaveSavedBoolean(tj_ABILITYHASH, id, Register_SilenceState)
set this.slow = HaveSavedBoolean(tj_ABILITYHASH, id, Register_SlowState)
set this.abnormal = HaveSavedBoolean(tj_ABILITYHASH, id, Register_AbnormalState)
set this.stealable = HaveSavedBoolean(tj_ABILITYHASH, id, RegisterBuffData_IS)
set this.durType = LoadInteger(tj_ABILITYHASH, id, RegisterBuffData_DT)
return this
endmethod
method check takes integer lvl, integer pos, integer neg, integer psy, integer mag, integer aura, integer univ, integer abn, integer silence, integer slow returns boolean
if ( pos==0 or (pos>0 and (pos==1)==this.pos) ) and /*
*/ ( neg==0 or (neg>0 and (neg==1)==this.neg) ) and /*
*/ ( psy==0 or (psy>0 and (psy==1)==this.psy) ) and /*
*/ ( mag==0 or (mag>0 and (mag==1)==this.mag) ) and /*
*/ ( aura==0 or (aura>0 and (aura==1)==this.aura) ) and /*
*/ ( univ==0 or (univ>0 and (univ==1)==this.univ) ) and /*
*/ ( abn==0 or (abn>0 and (abn==1)==this.abnormal) ) and /*
*/ ( silence==0 or (silence>0 and (silence==1)==this.silence) ) and /*
*/ ( slow==0 or (slow>0 and (slow==1)==this.slow )) and /*
*/ this.lvl<=lvl then
return true
endif
return false
endmethod
method copy takes nothing returns thistype
local thistype d = thistype.allocate()
set d.pos = .pos
set d.neg = .neg
set d.psy = .psy
set d.mag = .mag
set d.aura = .aura
set d.univ = .univ
set d.lvl = .lvl
set d.silence = .silence
set d.slow = .slow
set d.abnormal = .abnormal
set d.stealable = .stealable
set d.category = .category
set d.durType = .durType
set d.removeOnDead = .removeOnDead
return d
endmethod
endstruct
struct tBuff
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
static thistype theList
//------------------------------------------------------------------
unit caster
unit target
integer id
integer lvl
string bonus
string bonusEx
real dur // -1: not check
real baseDur //
//------------------------------------------------------------------
real progress = 0.
real tickTime = BUFF_PERIOD
integer data = 0
boolean firstTick = true
thistype leadRef = 0
Table refData = 0
integer refIndex = 0
integer refPos = 0
integer refMax = 0
integer refCount = 0
tBuffAttribute atr = 0
//------------------------------------------------------------------
TBuff_Event onTick = 0
TBuff_Event onEnd = 0
TBuff_EventR onRemove = 0
TBuff_EventX onReApply = 0
TBuff_EventR onKill = 0
TBuff_EventR onDead = 0
TBuff_Event onDetach = 0
TBuff_Event doAttach = 0 //mainly for data of buff
TBuff_EventX doDuplicate = 0 //mainly for data of buff
//------------------------------------------------------------------
implement LinkedList //Kenny's
//---
private method periodic takes nothing returns nothing
//-----
if GetUnitTypeId(.target)==0 then
call .detachThis()//call .stopPeriodic()
call .onEnd.evaluate(this)
call .destroy()
return
endif
//-----
if .dur > 0. then
set .dur = .dur - BUFF_PERIOD
endif
if .onTick != 0 then
set .progress = .progress + BUFF_PERIOD
if .progress == .tickTime or .progress > .tickTime then
set .progress = .progress - .tickTime
call .onTick.evaluate(this)
endif
endif
if .firstTick then
set .firstTick = false
return
endif
if (.dur>-1 and .dur<=0.000001) or GetUnitAbilityLevel(.target,.id)==0 then
call this.terminate()
endif
endmethod
static method HandlePeriodic takes nothing returns nothing
local thistype this = thistype.theList.head
local thistype tmp
loop
exitwhen 0==this
set tmp = this.next
call this.periodic()
set this = tmp
endloop
endmethod
static method Apply takes GBD gd returns thistype
local thistype this
local thistype ret
local tBuff_Apply ufunc
local tBuff_Apply func
local integer i
local integer j
set this = thistype.create()
set this.caster = gd.source
set this.target = gd.target
set this.id = gd.id
set this.lvl = gd.lvl
set this.bonus = gd.bonus
set this.bonusEx = gd.bonusEx
set this.dur = gd.dur
set this.baseDur= gd.baseDur
set this.atr = gd.bAtr.copy()
set this.refMax = gd.refMax
set this.refIndex = gd.refIndex
set this.tickTime = gd.tickTime
set this.onTick = gd.onTick
set this.onRemove = gd.onRemove
set this.onEnd = gd.onEnd
set this.onReApply = gd.onReApply
set this.onDetach = gd.onDetach
set this.doAttach = gd.doAttach
set this.doDuplicate = gd.doDuplicate
set this.onKill = gd.onKill
set this.onDead = gd.onDead
if gd.chkEventFunc then
// event onBuff Apply
set ufunc = tBuff_Apply.Data(GetUnitId(gd.source))
set i = 0
set j = ufunc.size
loop
exitwhen i==ufunc.size
set func = ufunc<i>
if func.cast then
call func.evt.evaluate(gd,func.spellId,func.spellLv)
if gd.target==null then
if not gd.permanent then
call gd.destroy()
endif
return 0
endif
endif
if j!=ufunc.size then
set i = i - 1
set j = ufunc.size
endif
set i = i + 1
endloop
set ufunc = tBuff_Apply.Data(GetUnitId(gd.target))
set i = 0
set j = ufunc.size
loop
exitwhen i==ufunc.size
set func = ufunc<i>
if not func.cast then
call func.evt.evaluate(gd,func.spellId,func.spellLv)
if gd.target==null then
if not gd.permanent then
call gd.destroy()
endif
return 0
endif
endif
if j!=ufunc.size then
set i = i - 1
set j = ufunc.size
endif
set i = i + 1
endloop
endif
if gd.bAtr.category==BUFF_STACKING then
if gd.refMax==0 then
set ret = this.AddStackIndex()
else
set ret = this.AddStack()
endif
else
set ret = this.Add()
endif
if not gd.permanent then
call gd.destroy()
endif
return ret
endmethod
endstruct
endscope
</i></i>
[/spoiler]
this method:
JASS:
//....
static method Apply takes GBD gd returns thistype
local thistype this
local thistype ret
local tBuff_Apply ufunc
local tBuff_Apply func
local integer i
local integer j
set this = thistype.create()
set this.caster = gd.source
set this.target = gd.target
set this.id = gd.id
set this.lvl = gd.lvl
set this.bonus = gd.bonus
set this.bonusEx = gd.bonusEx
set this.dur = gd.dur
set this.baseDur= gd.baseDur
set this.atr = gd.bAtr.copy()
set this.refMax = gd.refMax
set this.refIndex = gd.refIndex
set this.tickTime = gd.tickTime
set this.onTick = gd.onTick
set this.onRemove = gd.onRemove
set this.onEnd = gd.onEnd
set this.onReApply = gd.onReApply
set this.onDetach = gd.onDetach
set this.doAttach = gd.doAttach
set this.doDuplicate = gd.doDuplicate
set this.onKill = gd.onKill
set this.onDead = gd.onDead
if gd.chkEventFunc then
// event onBuff Apply
set ufunc = tBuff_Apply.Data(GetUnitId(gd.source))
set i = 0
set j = ufunc.size
loop
exitwhen i==ufunc.size
set func = ufunc<i>
if func.cast then
call func.evt.evaluate(gd,func.spellId,func.spellLv)
if gd.target==null then
if not gd.permanent then
call gd.destroy()
endif
return 0
endif
endif
if j!=ufunc.size then
set i = i - 1
set j = ufunc.size
endif
set i = i + 1
endloop
set ufunc = tBuff_Apply.Data(GetUnitId(gd.target))
set i = 0
set j = ufunc.size
loop
exitwhen i==ufunc.size
set func = ufunc<i>
if not func.cast then
call func.evt.evaluate(gd,func.spellId,func.spellLv)
if gd.target==null then
if not gd.permanent then
call gd.destroy()
endif
return 0
endif
endif
if j!=ufunc.size then
set i = i - 1
set j = ufunc.size
endif
set i = i + 1
endloop
endif
if gd.bAtr.category==BUFF_STACKING then
if gd.refMax==0 then
set ret = this.AddStackIndex()
else
set ret = this.AddStack()
endif
else
set ret = this.Add()
endif
if not gd.permanent then
call gd.destroy()
endif
return ret
endmethod
</i></i>
this method take one argument of type GBD (Generic Buff Data)
JASS:
struct GBD //General Buff Data
static thistype glb = 0
unit source
unit target
integer id
integer lvl = 1
string bonus
string bonusEx //bonus for headRef (Master Stack)
real dur = -1
real baseDur = -1
integer refIndex = 0
integer refMax = 0
tBuffAttribute bAtr
real tickTime = 0.
TBuff_Event onTick = 0
TBuff_Event onEnd = 0
TBuff_EventR onRemove = 0
TBuff_EventX onReApply = 0
TBuff_Event onDetach = 0
TBuff_Event doAttach = 0
TBuff_EventX doDuplicate = 0
TBuff_Event onKill = 0
TBuff_Event onDead = 0
integer srcAbi = 0
integer srcAbilvl = 1
integer srcAbiElement = SPELL_ELEMENT_NON
integer srcAbiType = SPELL_TYPE_NON
integer affectType = SPELL_AFFECT_STarget
boolean chkEventFunc = true
boolean permanent = false
method destroy takes nothing returns nothing
call this.bAtr.destroy()
call this.deallocate()
endmethod
method operator Dur= takes real r returns nothing
set this.dur = r
set this.baseDur = r
endmethod
static method create takes unit c, unit a, integer id, string bonus, real dur returns thistype
local thistype this = thistype.allocate()
set this.source = c
set this.target = a
set this.id = id
set this.bonus = bonus
if dur>0 then
set this.dur = dur
set this.baseDur = dur
endif
set this.bAtr = tBuffAttribute.create(id)
return this
endmethod
static method createS takes unit c, unit a, integer id, string bonus, real dur, integer sMax returns thistype
local thistype this = thistype.allocate()
set this.source = c
set this.target = a
set this.id = id
set this.bonus = bonus
if dur>0 then
set this.dur = dur
set this.baseDur = dur
endif
set this.refMax = sMax
set this.bAtr = tBuffAttribute.create(id)
set this.bAtr.category = BUFF_STACKING
return this
endmethod
endstruct
GBD is used to pass data to "event functions" (function interface), see the chkEventFunc block.
problem I ran into: code in these "event functions" could change the data in the GBD so sometime it's NOT good.
example:
- a unit stomps the ground, Stun is applied to nearby enemies
- one of affected units (let call it "A") has "stun duration reduction"; after applied for "A", duration is changed.
- if it's the last unit: no problem. if it's not the last unit: duration is changed, so...
that's example for "duration", there also case for "bonus" (unit/hero stats)
solutions I think of:
- re-set the duration/bonus before applying to new unit (currently using this)
- make a copy of GBD
JASS:
//....
static method Apply takes GBD gd returns thistype
local thistype this
local thistype ret
local tBuff_Apply ufunc
local tBuff_Apply func
local integer i
local integer j
set gd = gd.Copy()
set this = thistype.create()
set this.caster = gd.source
set this.target = gd.target
set this.id = gd.id
set this.lvl = gd.lvl
set this.bonus = gd.bonus
set this.bonusEx = gd.bonusEx
set this.dur = gd.dur
set this.baseDur= gd.baseDur
//.........
call gd.destroy() // this "gd" is the copy
return ret
endmethod
GBD struct has many members, calling a function to copy those would take a little bit of performance, right? it's fine or just ONE call but what if there are 10 or 20 or more? what's your opinions?
thanks for reading!