• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[vJASS] I am I coding structs wrong?

Status
Not open for further replies.
Level 2
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?

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
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):
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
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! :D
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Could you be more specific on describing the errors, the current description is vague. It's also good if you could provide a test map.
 
Level 2
Joined
Nov 8, 2012
Messages
7
Thanks for the quick reply!

The problems are quite tricky to outline because they occur so randomly. I'll attach the map that's given the most headaches so far.

Test the map and type "-setup ffa" without the quotes. The game plays fine for a while, then weird things start to happen; items appear out of nowhere, the AI starts to get all squirly...

On revision, it seems most of the problems there are just leaks, but the items problem makes no sense to me at all.

EDIT: vBulletin's attachment system is retarded, here's a dropbox link: https://www.dropbox.com/s/ih0vagvv87yt6el/ElementalTournament.w3x
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
Fix the code indenting... seriously..


Are you sure its an issue with this code? nothing in it seems to be "incorrect"
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Maybe one of the systems you're using is conflicting with another? You said that they were functioning properly before you used the said systems. Try disabling some of the systems.
 
Level 2
Joined
Nov 8, 2012
Messages
7
Thanks chobibo, I'll give that a try.

Sorry about that Arhowk, The Helper's jass tags nuked my indenting! I'll try and fix them. I'll just have to be thorough, the problems are probably hiding my piles of code. I'll start another thread for the items problem.

My struct usage is fine, right?
 
Status
Not open for further replies.
Top