Struct arrays and knockback problems

Jonnycakes

New Member
Reaction score
6
Hi, I am working on (what I think is) a fairly complex spell. It runs, but there are still a couple of things that aren't working quite right and I am not sure why. I will list the problems in a list:

1. Every time multiple instances of the spell are cast, I get the debug message, "Double free of type: deluge__data". I don't know why-I thought I structured the code that gets rid of unused slots in the struct array for data (d).

2. The same thing happens when multiple units are hit with the spell. "Double free of type: deluge__units." I think this is related to number 1, as I structured it the same way.

3. The knockback for units isn't working how I want it to. It seems like units hit by the spell aren't being knocked back every time the timer callback function is called (every .04 seconds). Also, units get stunned when they shouldn't-I think this is either because they aren't getting paused when they should, so they move while being knocked back screwing up the condition that checks if they have actually been moved, or because said condition is triggering when it shouldn't.

Here is my code-please point out any errors you see, related or not:
JASS:
scope deluge initializer init


private struct data //struct for spell instance

    unit caster
    real x
    real y
    real angle
    real r
    integer level
    player p
    group gr
    
endstruct

private struct units //struct for unit hit by the spell

    unit u
    real r
    real x
    real y
    real angle
    
endstruct


globals

    private timer time=CreateTimer()
    private integer ins=0 //instances of the spell
    private integer inu=0 //instances of units being knocked back by the spell
    private data array d
    private units array e
    
endglobals


private function conditions takes nothing returns boolean

    return GetSpellAbilityId()=='A00R'
    
endfunction


private function filter takes nothing returns boolean

    return true
    
endfunction


private function trees takes nothing returns nothing
    
    call KillDestructable(GetEnumDestructable())
    
endfunction


private function looper takes nothing returns nothing
    local integer i=1
    local group g=CreateGroup()
    local unit u
    local rect rct
    local real x
    local real y
    
    loop
        exitwhen i>ins 
        if d<i>.r&lt;0 then //if the spell is over, set instances minus one and restructure the array-here is one problem
            call d<i>.destroy()
            if ins&gt;1 and i!=ins then
                set d<i>=d[ins]
                call d[ins].destroy()
                set i=i-1
            endif
            set ins=ins-1
        else
            set d<i>.x=d<i>.x+50.*Cos(d<i>.angle)  //spell actions-moving spell, getting units hit by it, killing trees
            set d<i>.y=d<i>.y+50.*Sin(d<i>.angle) 
            call DestroyEffect(AddSpecialEffect(&quot;Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl&quot;, d<i>.x, d<i>.y))
            set d<i>.r=d<i>.r-.04
            call GroupEnumUnitsInRange(g, d<i>.x, d<i>.y, 100., Filter(function filter))
            loop
                set u=FirstOfGroup(g)
                exitwhen u==null
                call GroupRemoveUnit(g, u)
                if not IsUnitAlly(u, d<i>.p) and not IsUnitInGroup(u, d<i>.gr) then //this sets up a struct for a unit affected by the spell
                    set inu=inu+1
                    set e[inu]=units.create()
                    set e[inu].u=u
                    set e[inu].x=GetUnitX(u)
                    set e[inu].y=GetUnitY(u)
                    set e[inu].r=1.
                    set e[inu].angle=Atan2(e[inu].y-d<i>.y, e[inu].x-d<i>.x)
                    call UnitDamageTarget(d<i>.caster, u, I2R(d<i>.level)*50.+100, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
                    call DestroyEffect(AddSpecialEffect(&quot;&quot;, d<i>.x, d<i>.y))
                    call GroupAddUnit(d<i>.gr, u)
                endif
            endloop
            set rct=Rect(d<i>.x-100., d<i>.y-100., d<i>.x+100., d<i>.y+100.) //kills trees
            call EnumDestructablesInRect(rct, Filter(function filter), function trees) 
            call RemoveRect(rct)
        endif
        set i=i+1
    endloop
    
    set i=1
    loop
        exitwhen i&gt;inu
        if e<i>.r&lt;0 then //this is for getting rid of unused instances of the units struct
            call PauseUnit(e<i>.u, false)
            call e<i>.destroy()
            if inu&gt;1 and i!=inu then
                set e<i>=e[inu]
                call e[inu].destroy()
                set i=i-1
            endif
            set inu=inu-1
        else
            set x=e<i>.x //actions for units-knockback and pause
            set y=e<i>.y
            set e<i>.x=e<i>.x+40.*Cos(e<i>.angle)
            set e<i>.y=e<i>.y+40.*Sin(e<i>.angle) 
            call DestroyEffect(AddSpecialEffect(&quot;Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl&quot;, e<i>.x, e<i>.y))
            call SetUnitPosition(e<i>.u, e<i>.x, e<i>.y)
            set x=GetUnitX(e<i>.u)-e<i>.x
            set y=GetUnitY(e<i>.u)-e<i>.y
            if SquareRoot(x*x+y*y)&lt;10. then //stuns, ends knockback, and unpauses if a unit hits something
                set e<i>.r=-1.
                set u=CreateUnit(Player(8), &#039;h009&#039;, e<i>.x, e<i>.y, 0.)
                call UnitApplyTimedLife(u, &#039;Bhwd&#039;, 2.)
                call IssueTargetOrder(u, &quot;impale&quot;, e<i>.u)
            else
                call PauseUnit(e<i>.u, true)
                set e<i>.r=e<i>.r-.04
            endif
        endif
        set i=i+1
    endloop
    
    if ins+inu==0 then
        call PauseTimer(time)
    endif
    
    set g=null
    set u=null
    set rct=null

endfunction


private function actions takes nothing returns nothing

    local data this=data.create()
    local location l=GetSpellTargetLoc()
    local real x=GetLocationX(l)
    local real y=GetLocationY(l)
    call RemoveLocation(l)
    set l=null
    set this.caster=GetTriggerUnit()
    set this.x=GetUnitX(this.caster)
    set this.y=GetUnitY(this.caster)
    set this.angle=Atan2(y-this.y, x-this.x)
    set this.p=GetOwningPlayer(this.caster)
    set this.level=GetUnitAbilityLevel(this.caster, &#039;A00R&#039;)
    set this.r=I2R(this.level)*0.+1.8
    set this.gr=CreateGroup()
    set ins=ins+1
    set d[ins]=this
    if ins==1 and inu==0 then
        call TimerStart(time, .04, true, function looper)
    endif
    
endfunction


//===========================================================================
private function init takes nothing returns nothing

    local trigger t=CreateTrigger()
    local integer i=0
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set i=i+1
        exitwhen i==8
    endloop
    call TriggerAddCondition(t, Condition(function conditions))
    call TriggerAddAction(t, function actions)
    
endfunction


endscope</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
 

ertaboy356b

Old School Gamer
Reaction score
86
Hi, you should learn how to use Systems such as KeyTimers and TimerUtils.. This is pretty easy actually.. I think I made something a while back.. I have the map I am working on now. I will upload it so you can look..

Here's the trigger I made:

JASS:

scope DKKnock initializer Trig

    globals
        private trigger Trigger = CreateTrigger()
        private constant integer ABID = &#039;A00N&#039;
        private constant real Chance = 15
        private constant real BaseDamage = 25
        private constant real Increment = 20
        private constant real Range = 250
        public integer DS = 0
        public integer array Off
        public unit array DS_Target
    endglobals
    
    private struct data
        unit u
        unit target
        integer ticks
        effect e
        real angle
    endstruct
    
    private function Move takes nothing returns boolean
        local data d = data(KT_GetData())
        local real x = GetUnitX(d.target) + 10 * Cos(d.angle * bj_DEGTORAD)
        local real y = GetUnitY(d.target) + 10 * Sin(d.angle * bj_DEGTORAD)
        call SetUnitX(d.target,x)
        call SetUnitY(d.target,y)
        set d.e = AddSpecialEffect(&quot;Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl&quot;,x,y)
        call DestroyEffect(d.e)
        set d.ticks = d.ticks - 1
        if d.ticks == 0 or GetWidgetLife(d.target) &lt; 1 then
            call PauseUnit(d.target,false)
            set d.u = null
            set d.target = null
            set d.e = null
            call d.destroy()
            return true
        endif
        return false
    endfunction
    
    private function Act takes nothing returns nothing
        local data d = data.create()
        set d.u = GetAttacker()
        set d.target = GetTriggerUnit()
        set d.ticks = R2I(Range / 10)
        set d.angle = AngleBetweenXY(GetUnitX(d.u),GetUnitY(d.u),GetUnitX(d.target),GetUnitY(d.target))
        call PauseUnit(d.target,true)
        call UnitDamageTarget(d.u,d.target,BaseDamage + (GetUnitAbilityLevel(d.u,ABID)* Increment),true,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_MAGIC,WEAPON_TYPE_WHOKNOWS)
        call KT_Add(function Move,d,0.03)
        if DS == 1 then
            set DS = 0
        endif
        if DS_Target[GetUnitIndex(d.u)] != null then
            set DS_Target[GetUnitIndex(d.u)] = null
        endif
    endfunction
    
    private function Cond takes nothing returns boolean
        return Off[GetUnitIndex(GetAttacker())] != 1 and GetUnitAbilityLevel(GetAttacker(),ABID) &gt; 0 and (GetRandomReal(0,100) &lt;= Chance or (DS == 1 and DS_Target[GetUnitIndex(GetAttacker())] == GetTriggerUnit()))
    endfunction

    private function Trig takes nothing returns nothing
        call TriggerRegisterAnyUnitEventBJ(Trigger,EVENT_PLAYER_UNIT_ATTACKED)
        call TriggerAddAction(Trigger,function Act)
        call TriggerAddCondition(Trigger, Condition(function Cond))
    endfunction

endscope


This trigger is using KeyTimers and PUI.. The triggers are in the map..
 

Attachments

  • map.w3x
    417.8 KB · Views: 137

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
This trigger is using KeyTimers and PUI.. The triggers are in the map..
You are not using PUI properly.
 

Jonnycakes

New Member
Reaction score
6
Hi, you should learn how to use Systems such as KeyTimers and TimerUtils.. This is pretty easy actually.. I think I made something a while back.. I have the map I am working on now. I will upload it so you can look..

That's all fine and good, and I may eventually choose to use those systems, but what is wrong with my trigger? Those systems would solve the double clear for the struct arrays, but I don't think those systems will help with the odd knockback from my spell. And I still want to know what is wrong regardless. Is it wrong not to use those systems? What if I want to get this trigger right before I look into using them (I do)?
 

ertaboy356b

Old School Gamer
Reaction score
86
This trigger is using KeyTimers and PUI.. The triggers are in the map..
You are not using PUI properly.

How should I use PUI then?

@ TOPIC

for 1 and 2, I think using a global struct is a bad idea.. How about using a local struct instead, then destroying them after the spell is done.. Since variables are getting reused a lot, some variables might have been overridden by the other when multiple instances of the spell is executed..

So for example

if we have these:

JASS:
struct data
   unit u
endstruct

globals
   data MyData
endglobals

function Action takes nothing returns nothing
   set MyData.u = GetTriggerUnit()
endfunction


This is a global struct.. So everytime the function "Action" is called, MyData.u gets overridden.. So for multiple instances of the spell that happens alot, it would get overriden alot making glitches in the process.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
How should I use PUI then?
JASS:
struct data
//! runtextmacro PUI()
endstruct

JASS:
set data[whichUnit] = &lt;vars&gt; //Attach struct.
data[whichUnit]//return attached struct.
&lt;vars&gt;.release//Remove attached struct.
 

Jonnycakes

New Member
Reaction score
6
How should I use PUI then?

@ TOPIC

for 1 and 2, I think using a global struct is a bad idea.. How about using a local struct instead, then destroying them after the spell is done.. Since variables are getting reused a lot, some variables might have been overridden by the other when multiple instances of the spell is executed..

So for example

if we have these:

JASS:
struct data
   unit u
endstruct

globals
   data MyData
endglobals

function Action takes nothing returns nothing
   set MyData.u = GetTriggerUnit()
endfunction


This is a global struct.. So everytime the function "Action" is called, MyData.u gets overridden.. So for multiple instances of the spell that happens alot, it would get overriden alot making glitches in the process.

Ah, but if you pay attention the global structs that I am using are arrays, and should be properly indexed so as to not be overwritten. Each instance of the spell is supposed to create a new index in the array, each of which gets looped through for each instance of the spell going on. Like I said, it works fine for single instances of the spell (and I think it even works with multiple instances), but I get a "double clear" error message.

EDIT: Also, how would I use local structs to pass data between functions? With a timer callback, don't the structs NEED to be global?
 

ertaboy356b

Old School Gamer
Reaction score
86
If you use KeyTimers system, you can do this:

JASS:

struct data
   unit u
endstruct

function TimerFunc takes nothing returns boolean
   local data d = data(KT_GetData()) // this will get the data from the previous function
   call KillUnit(d.u)
   call d.destroy() // Don&#039;t forget to destroy the local struct when the timer is about to end.
   return true // If you return true, the timer will end, if false, the timer will recall itself after the timeout
endfunction

function Act takes nothing returns nothing
    local data d = data.create()
    set d.u = GetTriggerUnit()
    call KT_Add(function TimerFunc, d, 0.03) // This is a KT Function, arguments are &quot;function, struct, timeout&quot;
endfunction
 

Jonnycakes

New Member
Reaction score
6
Understood, but what about without KeyTimers? Thanks for trying to help, but we both just keep saying the same things over and over. The solution will probably come to me some time in the future.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Monovertex Monovertex:
    How are you all? :D
    +1
  • Ghan Ghan:
    Howdy
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top