• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] JASS Spell Crashes Game... Eventually

Status
Not open for further replies.
Level 9
Joined
May 28, 2007
Messages
365
JASS:
scope Cleave initializer Init

private struct data
    unit target          = null
    unit caster          = null
    group knockGroup     = null
    real v               = 0.  
    real angle           = 0.
endstruct

globals
    private boolexpr filter = null
    private data tempData   
endglobals

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00N'
endfunction

private function Filt takes nothing returns boolean 
    local unit t = GetFilterUnit()
    if( IsUnitEnemy(t,GetOwningPlayer(tempData.caster)) and GetWidgetLife(t)>0.405 and not IsUnitType(t, UNIT_TYPE_MECHANICAL)and not IsUnitInGroup(t, tempData.knockGroup) ) then
        call GroupAddUnit(tempData.knockGroup, t) 
        call UnitAddAbility(t, 'A00O')
    endif    
    return FALSE
endfunction

private function KnockbackGroup takes nothing returns nothing 
    local unit u = GetEnumUnit()
    local real dx = 0.
    local real dy = 0.
    
    set dx = PolarProjectionX(GetUnitX(u), tempData.v, tempData.angle)
    set dy = PolarProjectionY(GetUnitY(u), tempData.v, tempData.angle)     
   
    if IsTerrainWalkable(dx, dy) then
        call SetUnitX(u, dx)
        call SetUnitY(u, dy)
    endif    
endfunction
 
private function Callback takes nothing returns nothing 
    local timer myTimer = GetExpiredTimer()
    local data d = GetTimerData(myTimer)
    local unit target = null
    
    set d.v = d.v*0.925
    
    set tempData = d
    // Enum and add new units to the knockback group
    call GroupEnumUnitsInRange(tempGroup, GetUnitX(d.target), GetUnitY(d.target), 125, filter)  
     // Knockback the entire group
    call ForGroup(d.knockGroup, function KnockbackGroup)     
    
    if d.v <= 5 then       
        loop
            set target = FirstOfGroup(d.knockGroup)
            exitwhen target == null 
            call UnitRemoveAbility(target, 'A00O')
            call GroupRemoveUnit(d.knockGroup, target)
        endloop

        call ReleaseGroup(d.knockGroup)         
        call ReleaseTimer(myTimer)
        call d.destroy()
    endif    
endfunction

private function Actions takes nothing returns nothing
    local timer myTimer = NewTimer()
    local unit u = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    local real amount = 50+GetHeroLevel(u)*10+GetUnitStat(u, ATTACK_POWER)*0.4*BarbarianDamageModifier(u,t)    
    local data d = data.create()
   
    set d.caster = u
    set d.target = t
    set d.knockGroup = NewGroup()
    set d.angle = AngleBetweenPointsXY(GetUnitX(u), GetUnitY(u), GetUnitX(t), GetUnitY(t))
    set d.v = 40.
    
    call GroupAddUnit(d.knockGroup, t)
    call UnitAddAbility(t, 'A00O')    
    
    call UnitDamage(u, t, PHYSICAL, amount)
    
    call SetTimerData(myTimer, d)
    call TimerStart(myTimer, 0.03, true, function Callback)
endfunction

function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    set filter = Condition(function Filt)
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Conditions ) )
    call TriggerAddAction( t, function Actions )
    set t = null
endfunction
endscope

So this is my code for a spell. When used, it seems to work fine. However, the spell starts to crash after a certain time. I'm not sure if it's based on how many times the spell was used, or if it has to do with the maps game time.

The crash is unrelated to the custom functions that are being used such as UnitDamage, which is why I didn't post the code for those functions.

I once commented out the timer and the struct, and the crashes stopped.

Is there something I'm doing wrong?

I'm also attaching this, because I think this is the problem.

l
JASS:
ibrary IsTerrainWalkable initializer Init

//*****************************************************************
//*  IsTerrainWalkable
//*
//*  rewritten in vJass by: Anitarf
//*  original implementation: Vexorian
//*
//*  A function for checking if a point is pathable for ground
//*  units (it does so by attempting to move an item there and
//*  checking where it ended up), typically used to stop sliding
//*  units before they end up stuck in trees. If the point is not
//*  pathable, the function will also determine the nearest point
//*  that is (the point where the item ends up).
//*****************************************************************

    globals
        // this value is how far from a point the item may end up for the point to be considered pathable
        private constant real MAX_RANGE = 10.0
        
        // the following two variables are set to the position of the item after each pathing check
        // that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
        public real X = 0.0
        public real Y = 0.0

    
// END OF CALIBRATION SECTION    
// ================================================================

        private rect r
        private item check
        private item array hidden
        private integer hiddenMax = 0
    endglobals

    private function Init takes nothing returns nothing
        set check = CreateItem('ciri',0,0)
        call SetItemVisible(check,false)
        set r = Rect(0.0,0.0,128.0,128.0)
    endfunction

    private function HideBothersomeItem takes nothing returns nothing
        if IsItemVisible(GetEnumItem()) then
            set hidden[hiddenMax]=GetEnumItem()
            call SetItemVisible(hidden[hiddenMax],false)
            set hiddenMax=hiddenMax+1
        endif
    endfunction

// ================================================================
    
    function IsTerrainWalkable takes real x, real y returns boolean
        // first, hide any items in the area so they don't get in the way of our item
        call MoveRectTo(r, x,y)
        call EnumItemsInRect(r,null,function HideBothersomeItem)
        // try to move the check item and get it's coordinates
        call SetItemPosition(check,x,y)//this unhides the item...
        set X = GetItemX(check)
        set Y = GetItemY(check)
        call SetItemVisible(check,false)//...so we must hide it again
        // before returning, unhide any items that got hidden at the start
        loop
            exitwhen hiddenMax<=0
            set hiddenMax=hiddenMax-1
            call SetItemVisible(hidden[hiddenMax],true)
            set hidden[hiddenMax]=null
        endloop
        // return pathability status
        return (x-X)*(x-X)+(y-Y)*(y-Y) < MAX_RANGE*MAX_RANGE
    endfunction
    
endlibrary
 
Last edited:
Level 13
Joined
Mar 16, 2008
Messages
941
I think I know it.
JASS:
    call ForGroup(d.knockGroup, function KnockbackGroup)

JASS:
private function KnockbackGroup takes nothing returns nothing
    local data d = GetTimerData(GetExpiredTimer())
    local unit u = GetEnumUnit()
    local real dx = 0.
    local real dy = 0.

    set dx = PolarProjectionX(GetUnitX(u), d.v, d.angle)
    set dy = PolarProjectionY(GetUnitY(u), d.v, d.angle)

    if IsTerrainWalkable(dx, dy) then
        call SetUnitX(u, dx)
        call SetUnitY(u, dy)
    endif
endfunction
GetExpiredTimer() in functions that were not the callback can cause crashes afaik.
The same goes for the filter function.
Also, you should first release the timer, then destroy the struct.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
Maybe not related to your problem but...

  • Make your Init function private.
  • You don't need to set your trigger variable to null.
  • Use GroupEnumUnitsInArea since you're using GroupUtils anyway. (GroupUtils was updated.)
  • You may want to use RefreshGroup for d.knockGroup because you're using a FirstOfGroup loop.
  • Set local handle variables to null after you're done using them otherwise they will leak. (ex: myTimer, t, and u)
  • You don't even need to have the variables u and t in your Actions function, just do set d.caster = GetTriggerUnit() and set d.target = GetSpellTargetUnit() after declaring d and use them in place of u and t.
  • What is tempGroup? Could you have just used ENUM_GROUP instead?
  • Why FALSE instead of false in your function Filt? Just curious.
The spell actually works though, right?
Does the crash occur irregularly? Or does it happen after x casts of the ability? What do you mean by "certain time"?

Try making the timer do nothing except loop. If it still crashes, you'll know that the crash is caused by the timer itself and that you're doing something wrong.
 
I think I know it.
JASS:
    call ForGroup(d.knockGroup, function KnockbackGroup)

JASS:
private function KnockbackGroup takes nothing returns nothing
    local data d = GetTimerData(GetExpiredTimer())
    local unit u = GetEnumUnit()
    local real dx = 0.
    local real dy = 0.

    set dx = PolarProjectionX(GetUnitX(u), d.v, d.angle)
    set dy = PolarProjectionY(GetUnitY(u), d.v, d.angle)

    if IsTerrainWalkable(dx, dy) then
        call SetUnitX(u, dx)
        call SetUnitY(u, dy)
    endif
endfunction
GetExpiredTimer() in functions that were not the callback can cause crashes afaik.
The same goes for the filter function.
Also, you should first release the timer, then destroy the struct.

and even if it won't cause an error, that would cause bugs because whenever the timer isn't present then the value of dat would be 0 which could overwrite again and again for every for group call you make...
 
Status
Not open for further replies.
Top