- Joined
- Nov 8, 2012
- Messages
- 7
Greetings, The Hive! I'm trying my luck out over here, it seems The Helper is dead :[.
This time I'm concerned about my new coding method I started using since I saw it in an older spell. I can create callback struct spells and functions without the need for struct attachment systems like TimerUtils or ABC.
However, since I've started using this system, my games have been throwing out the weirdest errors and random code meltdowns (everything that could go wrong goes wrong, and things that used to work perfectly suddenly freak out).
If you have some spare time on your hands, could you please let me know if it's this coding method, or if it's something else I'm doing in this code?
So that's my new coding system. To save your time, the suspected culprits are:
- method destroy (I've never known the proper syntax for destroying structs)
- .allocate() & .deallocate() (same reasoning as above)
- using an empty global GROUP & static method SuckUnits (tip I received ages ago for leakless groups)
Understand that almost every spell designed for multiple simultaneous instances has been coded this way, including a periodic AI system and over 60 spells. It's at a point where I spend a month coding and then the game I've made is unplayable.
For comparison, here's an older code from an older game that always works perfectly (using Cohadar's ABC timer system):
Any input is greatly appreciated!
PS: Just in case: if you're just checking out this post and like the spells I've posted, help yourself to the code credit-free, I don't really care about who uses my stuff!
This time I'm concerned about my new coding method I started using since I saw it in an older spell. I can create callback struct spells and functions without the need for struct attachment systems like TimerUtils or ABC.
However, since I've started using this system, my games have been throwing out the weirdest errors and random code meltdowns (everything that could go wrong goes wrong, and things that used to work perfectly suddenly freak out).
If you have some spare time on your hands, could you please let me know if it's this coding method, or if it's something else I'm doing in this code?
JASS:
//ADEFLW_26_111111_BLACKHOLE
scope BlackHole initializer init
private keyword BLACK
globals
private constant integer ABILCODE = 'A026'
private constant integer DUMMY_ID = 'n018'
private constant real SPEED = 1000
private constant real SUCK_SPEED = 1000
private constant real AREA = 400
private constant real KILL_AREA = 50
private integer I = 0
private BLACK array DATA
private timer TIMER = CreateTimer()
endglobals
private struct BLACK
unit caster
unit dummy
real tX
real tY
static thistype tempDATA
method destroy takes nothing returns nothing
set .caster = null
call KillUnit(.dummy)
set .dummy = null
set .tX = 0
set .tY = 0
call .deallocate()
endmethod
static method create takes unit caster, real tX, real tY returns BLACK
local BLACK a = BLACK.allocate()
set a.caster= caster
set a.dummy = CreateUnit(NEUTRAL_PASSIVE,DUMMY_ID,GetUnitX(caster),GetUnitY(caster),0)
set a.tX = tX
set a.tY = tY
return a
endmethod
static method SuckUnits takes nothing returns boolean
local thistype this = thistype.tempDATA
local unit u = GetFilterUnit()
local real angle
local real distance
if CheckTarget(.caster,u) then
set angle = Atan2(GetUnitY(.dummy)-GetUnitY(u),GetUnitX(.dummy)-GetUnitX(u))
set distance = AREA-DistanceUnits(.dummy,u)
call SetUnitX(u,GetUnitX(u)+(distance/AREA)*SUCK_SPEED*INTERVAL*Cos(angle))
call SetUnitY(u,GetUnitY(u)+(distance/AREA)*SUCK_SPEED*INTERVAL*Sin(angle))
if DistanceUnits(.dummy,u) <= KILL_AREA then
set PLAYER_DEATH_MESSAGE[GetPlayerId(GetOwningPlayer(u))] = "Sucked into a black hole"
call UnitDamageTarget(.caster,u,10000,false,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,null)
endif
endif
set u = null
return false
endmethod
method Reposition takes real tX, real tY returns nothing
set .tX = tX
set .tY = tY
endmethod
method Tick takes nothing returns nothing
local real X = GetUnitX(.dummy)
local real Y = GetUnitY(.dummy)
local real angle = Atan2(.tY-Y,.tX-X)
local real distance = DistancePoints(X,Y,.tX,.tY)
if distance > KILL_AREA then
call SetUnitPosition(.dummy,X+SPEED*INTERVAL*Cos(angle),Y+SPEED*INTERVAL*Sin(angle))
endif
set thistype.tempDATA = this
call GroupEnumUnitsInRange(GROUP,X,Y,AREA,Filter(function thistype.SuckUnits))
endmethod
endstruct
private function callback takes nothing returns nothing
local BLACK a
local integer i = 1
loop
exitwhen i > I
set a = DATA
if GetWidgetLife(a.caster) <= 0 or GetUnitAbilityLevel(a.caster,ABILCODE) <= 0 then
call a.destroy()
set DATA = DATA
set I = I-1
set i=i-1
else
call a.Tick()
endif
set i = i+1
endloop
if I == 0 then
call PauseTimer(TIMER)
endif
endfunction
private function Actions takes nothing returns nothing
local integer i = 1
local boolean end = false
local BLACK a
loop
exitwhen i > I or end
set a = DATA
if GetTriggerUnit() == a.caster then
call a.Reposition(GetSpellTargetX(),GetSpellTargetY())
set end = true
endif
set i = i+1
endloop
if end == false then
set a = BLACK.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
set I = I+1
set DATA = a
endif
if I == 1 then
call TimerStart(TIMER,INTERVAL,true,function callback)
endif
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == ABILCODE
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_CHANNEL)
call TriggerAddCondition(t,Condition(function Conditions))
call TriggerAddAction(t,function Actions)
set t = null
call DestroyTrigger(t)
endfunction
endscope
- method destroy (I've never known the proper syntax for destroying structs)
- .allocate() & .deallocate() (same reasoning as above)
- using an empty global GROUP & static method SuckUnits (tip I received ages ago for leakless groups)
Understand that almost every spell designed for multiple simultaneous instances has been coded this way, including a periodic AI system and over 60 spells. It's at a point where I spend a month coding and then the game I've made is unplayable.
For comparison, here's an older code from an older game that always works perfectly (using Cohadar's ABC timer system):
JASS:
//==========================================================
//Ball Lightning - 'A00T'
//==========================================================
scope EnergyWave initializer init
globals
private group EW_damagedgroup
private unit EW_tempunit
endglobals
private struct EWstruct
unit caster
real vertexes
real distance
real maxdist
method onDestroy takes nothing returns nothing
set .caster = null
set .vertexes = 0
set .distance = 0
set .maxdist = 0
endmethod
endstruct
private function EWGC takes nothing returns boolean
if IsUnitInGroup(GetFilterUnit(), EW_damagedgroup) == true then
return false
elseif IsPlayerEnemy(GetOwningPlayer(GetFilterUnit()), GetOwningPlayer(EW_tempunit)) == false then
return false
elseif IsUnitAliveBJ(GetFilterUnit()) == false then
return false
elseif IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) then
return false
elseif IsUnitOwnedByPlayer(GetFilterUnit(), Player(PLAYER_NEUTRAL_PASSIVE)) == true or IsUnitOwnedByPlayer(GetFilterUnit(), Player(PLAYER_NEUTRAL_AGGRESSIVE)) then
return true
endif
return true
endfunction
private function Damage takes unit caster, unit target, real dmg returns nothing
call UnitDamageTarget(caster, target, dmg, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\ChimaeraLightningMissile\\ChimaeraLightningMissile.mdl", GetUnitX(target), GetUnitY(target)))
endfunction
private function callback takes nothing returns nothing
local timer t = GetExpiredTimer()
local EWstruct a = GetTimerStructA(t)
local real uX = GetUnitX(a.caster)
local real uY = GetUnitY(a.caster)
local real offsetX = 0
local real offsetY = 0
local real anglediff = 0
local real angle = 0
local integer loopval = 0
local group damagegroup
local unit u
if a.distance < a.maxdist and IsUnitAliveBJ(a.caster) == true then
set anglediff = 360/a.vertexes
loop
exitwhen loopval > a.vertexes
set damagegroup = CreateGroup()
set angle = loopval*anglediff
set offsetX = uX+a.distance*Cos(angle*bj_DEGTORAD)
set offsetY = uY+a.distance*Sin(angle*bj_DEGTORAD)
set EW_tempunit = a.caster
call GroupEnumUnitsInRange(damagegroup, offsetX, offsetY, 100, Filter(function EWGC))
set EW_tempunit = null
loop
set u = FirstOfGroup(damagegroup)
exitwhen u == null
call Damage(a.caster, u, 50*GetUnitAbilityLevel(a.caster, 'A00T'))
call GroupAddUnit(EW_damagedgroup, u)
call GroupRemoveUnit(damagegroup, u)
set u = null
endloop
call DestroyGroup(damagegroup)
call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl", offsetX, offsetY))
set loopval = loopval+1
endloop
set loopval = 0
set a.vertexes = a.vertexes+3
set a.distance = a.distance+75.00
else
call PauseTimer(t)
call DestroyTimer(t)
call a.destroy()
call ClearTimerStructA(t)
call DestroyGroup(EW_damagedgroup)
endif
set u = null
endfunction
private function EWA takes nothing returns nothing
local timer t = CreateTimer()
local EWstruct a = EWstruct.create()
set EW_damagedgroup = CreateGroup()
set a.caster = GetTriggerUnit()
set a.vertexes = 3
set a.distance = 50
set a.maxdist = 300+100*GetUnitAbilityLevel(a.caster, 'A00T')
call SetTimerStructA(t, a)
call TimerStart(t, 0.05, true, function callback)
set t = null
endfunction
private function EWC takes nothing returns boolean
return GetSpellAbilityId() == 'A00T'
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function EWC))
call TriggerAddAction(t, function EWA)
endfunction
endscope
PS: Just in case: if you're just checking out this post and like the spells I've posted, help yourself to the code credit-free, I don't really care about who uses my stuff!