Spell Pudge Wars Meat Hook

I changed the max distance and the CHAINEVERY and MAXCHAINS values so that a maximum distance of about 6000 could be reached.

Anyway, I read through the code and decided to try and optimise it and fix some issues I had.

Its coming along well. Then angles change a bit faster when rebounding and it runs a little smoother.

I am going to try and add pathing checks for higher ground and trees, not just buildings, later on.

If you are interested, I will post it up when I am done.
 
I wanted to make an uber long chain so I did 1 wc3 unit per chain and 400 chains and 450 + 550 speed, and I got that hook shield thingy >.>

EDIT: LOL I made goblin land mines! I started pooping the glaive every time i fired the chain

EDIT2: Aww man! I wanted to use a frost wyrm so it looks like its biting the unit, is there a way to make the frost wyrm change the way its facing everytime the chain changes its facing point?
 
I changed the max distance and the CHAINEVERY and MAXCHAINS values so that a maximum distance of about 6000 could be reached.

Anyway, I read through the code and decided to try and optimise it and fix some issues I had.

Its coming along well. Then angles change a bit faster when rebounding and it runs a little smoother.

I am going to try and add pathing checks for higher ground and trees, not just buildings, later on.

If you are interested, I will post it up when I am done.

if you do so, you could start using dynamic arrays (linked list) for it like tossrock did in his map. would be easier to structure.

the best way to fix this code would be to totally rewrite it, since its (imo) quite bad.
 
if you do so, you could start using dynamic arrays (linked list) for it like tossrock did in his map. would be easier to structure.

the best way to fix this code would be to totally rewrite it, since its (imo) quite bad.

I was thinking of a way to use a unit list type of linked list to store units, but I couldn't think of any. I knew I needed a struct for the list and one for the links/nodes of the list, I just wasn't sure of implementation. My mate on the other hand knew how tossrock did it, and helped me out with it. So yeah that part is done.

I'll post the code when I'm finished just to show you. So far its quite long.

Oh and I ripped off your deflection angle mathematics, hope you don't mind. :p

For Trollvottel:

JASS:
//------------------------------------------------------------------------------------\\
//                           Pudge Wars Meat Hook [v1]                                \\
//                                   By kenny!                                        \\
//                            Constructed using vJASS                                 \\
//              Requires NewGen WE, GTrigger & Linked List Module                     \\
//------------------------------------------------------------------------------------\\

scope MeatHookRevisited

    globals
        private constant integer    ABIL_ID          = 'A000'
        private constant integer    LINK_ID          = 'h001'
        private constant integer    HEAD_ID          = 'h000'
        private constant integer    CROW_ID          = 'Amrf'
        private constant integer    LOCUST_ID        = 'Aloc'
        private constant integer    ANIM_INDEX       = 2
        
        private constant real       INTERVAL         = 0.03125
        private constant real       MIN_HEIGHT       = 40.00
        private constant real       UNIT_RADIUS      = 100.00
        private constant real       BUILD_RADIUS     = 140.00
        private constant real       TIME_SCALE       = 2.00
        private constant real       HEAD_SCALE       = 1.40
        private constant real       LINK_SCALE       = 1.60
        private constant real       FLY_HEIGHT       = 75.00
        private constant real       DISTANCE         = 75.00
        
        private constant string     BLOOD_SFX        = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
        private constant string     BLOOD_POINT      = "chest"
        private constant string     HEADSHOT_TEXT    = "HEADSHOT!"
        private constant string     TEXT_COLOUR      = "|c00ff0000"
        
        private constant attacktype A_TYPE           = ATTACK_TYPE_CHAOS
        private constant damagetype D_TYPE           = DAMAGE_TYPE_UNIVERSAL
        private constant weapontype W_TYPE           = WEAPON_TYPE_WHOKNOWS
        
        private constant boolean    ALLOW_PRELOAD    = true
        private constant boolean    ALLOW_BOUNCE     = true
        private constant boolean    ALLOW_GRAPPLE    = false
        private constant boolean    ALLOW_HEADSHOT   = true
        private constant boolean    ABOVE_CASTER     = false
    endglobals

    //---------------------------------------------------------------------\\
    private constant function MAXDIST takes integer lvl returns real
        return 4800.00 // 800.00 + (400.00 * lvl)
    endfunction

    private constant function DAMAGE takes integer lvl returns real
        return 0.00 + (100.00 * lvl)
    endfunction
    
    private constant function SPEED takes integer lvl returns real
        return 1200.00 + (0.00  * lvl)
    endfunction
    
    private function FilterUnits takes unit caster, unit filter returns boolean
        return GetUnitFlyHeight(filter) < MIN_HEIGHT and GetUnitAbilityLevel(filter,LOCUST_ID) < 1
    endfunction
    
    private function StartSoundEx takes nothing returns nothing
        call StartSound(gg_snd_headshot)
    endfunction
    
    //---------------------------------------------------------------------\\
    //                                                                     \\
    //  DO NOT TOUCH PAST THIS POINT UNLESS YOU KNOW WHAT YOUR ARE DOING!  \\
    //                                                                     \\
    //---------------------------------------------------------------------\\

    private struct Link
    
        thistype next = 0
        thistype prev = 0
        thistype data = 0
        unit     link = null
        
        method onDestroy takes nothing returns nothing            
            set this.next = 0
            set this.prev = 0
            set this.data = 0
            
            if this.link != null then
                call RemoveUnit(this.link)
                set this.link = null
            endif
        endmethod
        
    endstruct
   
    //---------------------------------------------------------------------\\
    private struct List

        Link    first = 0
        Link    last  = 0
        Link    data  = 0
        integer size  = 0

        method attach takes nothing returns Link
            local Link curr = Link.create()
        
            if this.size == 0 then
                set this.first = curr
            elseif this.size == 1 then
                set curr.prev = this.first
                set this.first.next = curr
            elseif this.size > 1 then
                set curr.prev = this.last
                set this.last.next = curr
            endif
        
            set this.last = curr
            set this.data = curr
            set this.size = this.size + 1
        
            return curr
        endmethod

        method detach takes Link curr returns nothing
            local Link new = curr.prev
            
            if curr.next != 0 and curr.prev != 0 then
                set curr.prev.next = curr.next
                set curr.next.prev = curr.prev
            endif
            
            if this.first == curr then
                set this.first = curr.next
            endif
        
            if this.last == curr then
                set this.last = curr.prev
            endif
        
            call RemoveUnit(curr.link)
            set curr.link = null
        
            set this.size = this.size - 1
            set curr.data = new
        endmethod

        method onDestroy takes nothing returns nothing
            local Link linkx = this.data
            local Link linkz = 0
        
            loop
                exitwhen linkx == 0
                set linkz = linkx.data
                call linkx.destroy()
                set linkx = linkz
            endloop
        endmethod
    
    endstruct
   
   //---------------------------------------------------------------------\\
    private struct Hook
        implement Linked_List
    
        unit     cast  = null
        unit     targ  = null
        unit     head  = null
        player   own   = null
        
        real     ang   = 0.00
        real     dist  = 0.00
        real     cos   = 0.00
        real     sin   = 0.00
        real     speed = 0.00
        
        integer  level = 0
        List     list  = 0
        
        boolean  ext   = true
        boolean  hit   = false
        boolean  done  = false
        boolean  grap  = false
        
        static thistype List  = 0
        static timer    Timer = null
        static group    Group = null
        static boolexpr Build = null
        static boolexpr Unit  = null
        
        static real     MaxX  = 0.00
        static real     MaxY  = 0.00
        static real     MinX  = 0.00
        static real     MinY  = 0.00

        static method safeY takes real y returns real
            if y < thistype.MinY then
                return thistype.MinY
            elseif y > thistype.MaxY then 
                return thistype.MaxY
            endif
            return y
        endmethod
        
        static method isPointInMap takes real x, real y returns boolean
            return x > thistype.MinX and x < thistype.MaxX and y > thistype.MinY and y < thistype.MaxY
        endmethod
        
        static method isTarget takes unit u returns boolean
            local thistype this = thistype.List.getFirst()
            
            loop
                exitwhen this == 0
                
                if this.targ == u then
                    return true
                endif
                
                set this = this.getNext()
            endloop
            
            return false
        endmethod
        
        static method isGrappling takes unit u returns boolean
            local thistype this = thistype.List.getFirst()
            
            loop
                exitwhen this == 0
                
                if this.grap == true and this.cast == u then
                    return true
                endif
                
                set this = this.getNext()
            endloop
            
            return false
        endmethod
        
        static method textTag takes real x, real y returns nothing
            local texttag tt = CreateTextTag ()
            
            call SetTextTagPos(tt,x,y,0.00)
            call SetTextTagPermanent(tt,false)
            call SetTextTagText(tt,TEXT_COLOUR + HEADSHOT_TEXT + "|r",0.023)
            call SetTextTagVelocity(tt,Cos(bj_PI * 0.50) * 0.0355,Sin(bj_PI * 0.50) * 0.0355)
            call SetTextTagLifespan(tt,3.00)
            call SetTextTagFadepoint(tt,1.50)
            
            set tt = null
        endmethod
        
        static method unitFilt takes nothing returns boolean
            return GetWidgetLife(GetFilterUnit()) > 0.406 and IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) == false
        endmethod
        
        static method buildingFilt takes nothing returns boolean
            return GetWidgetLife(GetFilterUnit()) > 0.406 and IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) == true
        endmethod
        
        static method extraFilt takes unit caster, unit filter returns boolean
            return caster != filter and GetUnitTypeId(filter) != LINK_ID and GetUnitTypeId(filter) != HEAD_ID
        endmethod

        static method create takes nothing returns thistype
            local thistype this  = thistype.allocate()
            local location loc   = GetSpellTargetLoc()
            local real     locx  = GetLocationX(loc)
            local real     locy  = GetLocationY(loc)
            local real     casx  = 0.00
            local real     casy  = 0.00
            
            set this.cast  = GetTriggerUnit()
            set this.own   = GetOwningPlayer(this.cast)
            set casx       = GetUnitX(this.cast)
            set casy       = GetUnitY(this.cast)
            set this.ang   = Atan2((locy - casy),(locx - casx))
            set this.cos   = Cos(this.ang)
            set this.sin   = Sin(this.ang)
            set this.list  = List.create()
            set this.level = GetUnitAbilityLevel(this.cast,ABIL_ID)
            set this.speed = SPEED(this.level) * INTERVAL

            set this.head  = CreateUnit(this.own,HEAD_ID,casx + this.speed * this.cos,casy + this.speed * this.sin,this.ang * bj_DEGTORAD)
            set this.list.attach().link = this.head
                    
            call SetUnitTimeScale(this.head,TIME_SCALE)
            call UnitAddAbility(this.head,CROW_ID)
            call UnitRemoveAbility(this.head,CROW_ID)
            call SetUnitFlyHeight(this.head,FLY_HEIGHT,0.00)
            call SetUnitScale(this.head,HEAD_SCALE,HEAD_SCALE,HEAD_SCALE)
            
            call SetUnitAnimationByIndex(this.cast,ANIM_INDEX)

            if thistype.List.addToEnd(this) == thistype.List.getFirst() then
                call TimerStart(thistype.Timer,INTERVAL,true,function thistype.handler)
            endif
            
            call RemoveLocation(loc)
            set loc = null
            
            return this
        endmethod
        
        static method handler takes nothing returns nothing
            local thistype this = thistype.List.getFirst()
            
            loop
                exitwhen this == 0
                
                if this.done == true and this.ext == false then
                    call this.destroy()
                else
                    if this.ext == true then
                        call this.extend()
                    else
                        call this.retract()
                    endif
                endif
                
                set this = this.getNext()
            endloop
            
            if thistype.List.getFirst() == 0 then
                call PauseTimer(thistype.Timer)
            endif
        endmethod
        
        method extend takes nothing returns boolean
            local Link linkx = 0
            local unit temp  = null
            local unit prev  = null
            local real casx  = GetUnitX(this.cast)
            local real casy  = GetUnitY(this.cast)
            local real newx  = 0.00
            local real newy  = 0.00
            local real temx  = 0.00
            local real temy  = 0.00
            local real ang2  = 0.00
            local real dist  = 0.00
            
            if this.dist >= MAXDIST(this.level) or this.hit == true then
                call BJDebugMsg(I2S(this.list.size))
                set this.ext = false
                return true
            else
                set temp = this.list.last.link
                set temx = GetUnitX(temp)
                set temy = GetUnitY(temp)
                    
                set dist = SquareRoot((temx - casx) * (temx - casx) + (temy - casy) * (temy - casy))
                    
                if dist > this.speed then    
                    loop
                        exitwhen dist <= this.speed
                        set ang2 = Atan2((casy - temy),(casx - temx))
                        set temx = temx + this.speed * Cos(ang2)
                        set temy = temy + this.speed * Sin(ang2)
                        
                        set linkx      = this.list.attach()
                        set linkx.link = CreateUnit(this.own,LINK_ID,temx,temy,(ang2 * bj_RADTODEG) + 180.00)
                        
                        call UnitAddAbility(linkx.link,CROW_ID)
                        call UnitRemoveAbility(linkx.link,CROW_ID)
                        call SetUnitFlyHeight(linkx.link,FLY_HEIGHT,0.00)
                        call SetUnitScale(linkx.link,LINK_SCALE,LINK_SCALE,LINK_SCALE)
                        
                        set temp = linkx.link
                        set temx = GetUnitX(temp)
                        set temy = GetUnitY(temp)
                        set dist = dist - this.speed
                    endloop
                endif
                
                set linkx = this.list.first
                set temp  = linkx.link
                set temx  = GetUnitX(temp)
                set temy  = GetUnitY(temp)
                
                loop
                    set prev = linkx.next.link
                    set newx = GetUnitX(prev)
                    set newy = GetUnitY(prev)
                
                    if ((temx - newx) * (temx - newx) + (temy - newy) * (temy - newy)) < this.speed * this.speed then
                        call this.list.detach(linkx.next)
                    else
                        exitwhen true
                    endif
                endloop

                loop
                    exitwhen linkx == 0
                    
                    set temp = linkx.link
                    
                    set temx = GetUnitX(temp)
                    set temy = GetUnitY(temp)
                    
                    if temp == this.head then
                        set ang2 = this.ang
                    else
                        set ang2 = Atan2((GetUnitY(prev) - temy),(GetUnitX(prev) - temx))
                    endif
                    
                    set newx = temx + this.speed * Cos(ang2)
                    set newy = temy + this.speed * Sin(ang2)
                    
                    if thistype.isPointInMap(newx,newy) == false then
                        set ang2 = ang2 * bj_RADTODEG
                        
                        if newy != thistype.safeY(newy) then
                            set ang2 = -ang2
                        else
                            set ang2 = 180.00 - ang2               
                        endif
                        
                        set ang2 = ang2 * bj_DEGTORAD
                        
                        if temp == this.head then
                            set this.ang = ang2
                            set this.cos = Cos(this.ang)
                            set this.sin = Sin(this.ang)
                        endif

                        set newx = temx + this.speed * Cos(ang2)
                        set newy = temy + this.speed * Sin(ang2)
                    endif
                    
                    call SetUnitX(temp,newx)
                    call SetUnitY(temp,newy)
                    call SetUnitFacing(temp,ang2 * bj_RADTODEG)
                    
                    set prev = temp
                    set linkx = linkx.next
                endloop
                
                call this.getTarget()
                
                call this.getAngle()
                
                set this.dist = this.dist + this.speed
            endif
            
            set prev = null
            set temp = null
            
            return false
        endmethod
        
        method retract takes nothing returns boolean
            local Link linkx = 0
            local unit temp  = null
            local unit prev  = null
            local real casx  = GetUnitX(this.cast)
            local real casy  = GetUnitY(this.cast)
            local real temx  = 0.00
            local real temy  = 0.00
            local real ang2  = 0.00
            local real dist  = 0.00
            
            if this.list.size == 0 then
                set this.done = true
                return true
            else
                set temp = this.list.last.link
                set temx = GetUnitX(temp)
                set temy = GetUnitY(temp)
                    
                set dist = SquareRoot((temx - casx) * (temx - casx) + (temy - casy) * (temy - casy))
                
                if dist > this.speed then    
                    loop
                        exitwhen dist <= this.speed
                        set ang2 = Atan2((casy - temy),(casx - temx))
                        set temx = temx + this.speed * Cos(ang2)
                        set temy = temy + this.speed * Sin(ang2)
                            
                        set linkx      = this.list.attach()
                        set linkx.link = CreateUnit(this.own,LINK_ID,temx,temy,(ang2 * bj_RADTODEG) + 180.00)
                            
                        call UnitAddAbility(linkx.link,CROW_ID)
                        call UnitRemoveAbility(linkx.link,CROW_ID)
                        call SetUnitFlyHeight(linkx.link,FLY_HEIGHT,0.00)
                        call SetUnitScale(linkx.link,LINK_SCALE,LINK_SCALE,LINK_SCALE)
                            
                        set temp = linkx.link
                        set temx = GetUnitX(temp)
                        set temy = GetUnitY(temp)
                        set dist = dist - this.speed
                    endloop
                endif
                
                if this.grap == true then
                    set temx = GetUnitX(this.list.last.link)
                    set temy = GetUnitY(this.list.last.link)
                    set ang2 = Atan2((temy - casy),(temx - casx))
                    
                    call SetUnitPosition(this.cast,casx + this.speed * Cos(ang2),casy + this.speed * Sin(ang2))
                    
                    set linkx = this.list.last
                    
                    loop
                        exitwhen linkx == 0
                        
                        set temp = linkx.link 
                        
                        set temy = GetUnitY(temp)
                        set temx = GetUnitX(temp)
                        
                        if ((temx - casx) * (temx - casx) + (temy - casy) * (temy - casy)) < this.speed * this.speed then
                            call this.list.detach(linkx)
                        endif
                        
                        set linkx = linkx.prev
                    endloop
                else
                    if this.targ != null then
                        call SetUnitX(this.targ,GetUnitX(this.head))
                        call SetUnitY(this.targ,GetUnitY(this.head))
                        
                        if GetWidgetLife(this.targ) < 0.406 then
                            set this.targ = null
                            call SetUnitTimeScale(this.head,TIME_SCALE)
                        endif
                    else
                        call this.getTarget()
                    endif
                    
                    set linkx = this.list.last
                    
                    loop
                        exitwhen linkx == 0
                        
                        set temp = linkx.link
                        
                        set temx = GetUnitX(temp)
                        set temy = GetUnitY(temp)
                        
                        if temp == this.list.last.link then
                            set ang2 = Atan2((casy - temy),(casx - temx))
                        else
                            set ang2 = Atan2((GetUnitY(prev) - temy),(GetUnitX(prev) - temx))
                        endif
                        
                        if linkx == this.list.last and ((temx - casx) * (temx - casx) + (temy - casy) * (temy - casy)) < this.speed * this.speed then
                            call this.list.detach(linkx)
                        endif
                        
                        call SetUnitX(temp,temx + this.speed * Cos(ang2))
                        call SetUnitY(temp,temy + this.speed * Sin(ang2))
                        call SetUnitFacing(temp,(ang2 * bj_RADTODEG) + 180.00)

                        set prev  = temp
                        set linkx = linkx.prev
                    endloop 
                endif 
            endif
            
            set temp = null
            set prev = null
            
            return false
        endmethod
        
        method getTarget takes nothing returns nothing
            local real headx = GetUnitX(this.head)
            local real heady = GetUnitY(this.head)
            local unit temp  = null
            
            call GroupEnumUnitsInRange(thistype.Group,headx,heady,UNIT_RADIUS,thistype.Unit)
        
            loop
                set temp = FirstOfGroup(thistype.Group)
                exitwhen temp == null
                call GroupRemoveUnit(thistype.Group,temp)
                if thistype.extraFilt(this.cast,temp) == true and FilterUnits(this.cast,temp) == true then
                    if ALLOW_HEADSHOT then
                        if this.hit == false then
                            if thistype.isTarget(temp) == true then
                                call StartSoundEx()
                                if ABOVE_CASTER then
                                    call thistype.textTag(GetUnitX(this.cast),GetUnitY(this.cast))
                                else
                                    call thistype.textTag(headx,heady)
                                endif
                                call UnitDamageTarget(this.cast,temp,1000000.00,true,false,A_TYPE,D_TYPE,W_TYPE)
                                set this.hit = true
                            else
                                exitwhen this.setTarget(temp)
                            endif
                        endif
                    else
                        if this.hit == false and thistype.isTarget(temp) == false then
                            exitwhen this.setTarget(temp)
                        endif
                    endif
                endif
            endloop
            
            set temp = null
        endmethod
        
        method setTarget takes unit target returns boolean
            set this.targ = target
            set this.hit  = true
            
            if IsUnitEnemy(this.targ,this.own) then
                call UnitDamageTarget(this.cast,this.targ,DAMAGE(this.level),true,false,A_TYPE,D_TYPE,W_TYPE)
            endif
                            
            call SetUnitTimeScale(this.head,0.00)
            
            return true
        endmethod
        
        method getAngle takes nothing returns nothing
            local unit temp = null
            
            call GroupEnumUnitsInRange(thistype.Group,GetUnitX(this.head),GetUnitY(this.head),BUILD_RADIUS,thistype.Build)
           
            loop
                set temp = FirstOfGroup(thistype.Group)
                exitwhen temp == null
                call GroupRemoveUnit(thistype.Group,temp)
                if ALLOW_BOUNCE then
                    exitwhen this.setAngle(GetUnitX(temp),GetUnitY(temp))
                else
                    if ALLOW_GRAPPLE then
                        if this.grap == false then
                            if thistype.isGrappling(this.cast) == false then
                                set this.hit  = true
                                set this.grap = true
                                call SetUnitTimeScale(this.head,0.00)
                            else
                                set this.hit = true
                            endif
                            exitwhen true
                        endif
                    endif
                endif
            endloop
            
            set temp = null
        endmethod
        
        method setAngle takes real tempx, real tempy returns boolean
            local real ang1 = this.ang * bj_RADTODEG
            local real ang2 = ModuloReal(Atan2((GetUnitY(this.head) - tempy),(GetUnitX(this.head) - tempx)) * bj_RADTODEG,360.00)
            local real diff = RAbsBJ(ang2 - ang1)
            
            if diff >= 90.00 and diff <= 270.00  then
                if ( ang2 > 45.00 and ang2 < 135.00 ) or ( ang2 > 225.00 and ang2 < 315.00 ) then
                    set this.ang = -ang1 * bj_DEGTORAD
                else
                    set this.ang = (180.00 - ang1) * bj_DEGTORAD
                endif
                            
                set this.sin = Sin(this.ang)
                set this.cos = Cos(this.ang)

                return true
            endif 
            
            return false
        endmethod
        
        method onDestroy takes nothing returns nothing          
            if this.list != 0 then
                call this.list.destroy()
            endif
            
            if this.hit then
                call SetUnitX(this.targ,GetUnitX(this.cast) + DISTANCE * Cos(GetUnitFacing(this.cast) * bj_DEGTORAD))
                call SetUnitY(this.targ,GetUnitY(this.cast) + DISTANCE * Sin(GetUnitFacing(this.cast) * bj_DEGTORAD))
            endif
            
            set this.cast = null
            set this.targ = null
            set this.head = null
            
            set this.ext  = true
            set this.hit  = false
            set this.done = false
            set this.grap = false

            call this.detachThis()
        endmethod
        
        static method actions takes nothing returns boolean
            call thistype.create()
            
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local unit dummy = null
            
            set thistype.Timer = CreateTimer()
            set thistype.Group = CreateGroup()
            set thistype.List  = thistype.createList()
            set thistype.Unit  = Filter(function thistype.unitFilt)
            set thistype.Build = Filter(function thistype.buildingFilt)
            
            set thistype.MaxX  = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
            set thistype.MaxY  = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
            set thistype.MinX  = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
            set thistype.MinY  = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
            
            call GT_AddStartsEffectAction(function thistype.actions,ABIL_ID)
            
            if ALLOW_PRELOAD then
                set dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),LINK_ID,0.00,0.00,0.00)
                call UnitAddAbility(dummy,CROW_ID)
                call RemoveUnit(dummy)
                
                set dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),HEAD_ID,0.00,0.00,0.00)
                call RemoveUnit(dummy)
                set dummy = null
            endif
        endmethod
        
    endstruct
    
endscope
 
idk why, but when I put this into a map or just test using the Meathook.w3x WC3 just goes to the main menu. any ideas as to what I need to do to get this to work right?
 
I have NewGen, and I always hit Save before I do Test Map. WC3 just doesn't like it

EDIT: Found the problem. For some reason I had Disable vJass Syntax enabled... works now
 
Ehm, great spell but I have some problems sometimes when I use it. Quite rarely but sometimes when I use the spell wc3 crashes completely, no warning, no errors, just an instant shutdown.

I have changed some things in the code, I removed the two headshot parts since I don't want that, and I added so if the caster is dead then the hook will drop it's current target alternatively not take up anyone on it's way to the caster.

Could any of those really cause it? Or is it something else..?

It has crashed both in singleplayer, when I shot at a building making it bounce, and at some time after about a 15 min game with 3 people using it quite often.

I added a "//changed!" before every change in the code, so you can easily find it by searching that.
Changed code:
JASS:
scope MeatHook

// ==================================MEAT HOOK BY TROLLVOTTEL==================================
// || How to import:                                                                         ||
// || *  Copy the systems TT and PUI and the library UsedFuncs into your map                 ||
// || *  Copy this trigger into your map, change it's name to MeatHook                       ||
// || *  Copy the chain dummy and the hookhead dummy into your map                           ||
// || *  Change the values below, the function have an argument (lvl) which refers to the    ||
// || *  level of the ability for the caster                                                 ||
// ============================================================================================

// WHAT IT DOES:
//    * It's a meathook quite similar to the one from pudge wars made by me
//    * It connects the hook and the caster with chains
//    * It is retracted before leaving the playable map area
//    * It bounces off buildings.
//    * It looks quite nice
// WHAT IS NEEDED:
//    * vJass
//    * TT (Timer Ticker)
//    * PUI (Perfect Unit Indexing)
//    * the UsedFuncs library, my personal code collection with useful functions


    globals
        private constant integer RAWID      = 'A014' // Ability id
        private constant integer CHAINID    = 'h003' // chain dummy id
        private constant integer HOOKHEADID = 'h005' // hook head id
        private constant real CHAINEVERY    = 30.    // every X wc3 units a chain link
        private constant integer MAXCHAINS  = 60     // max amount of chains
        private constant real RADIUS        = 80.    // radius to grab units
    endglobals

    private function Distance takes integer lvl returns real
        return 800. + 200. * lvl
    endfunction

    private function Damage takes integer lvl returns real
        return 55.  + 15.  * lvl // configure your damage
    endfunction
    
    private function Speed takes integer lvl returns real
        return 900. // configure your speed
    endfunction
    
    
    
    //==============================SPELL BEGINS HERE==============================
    //------DONT CHANGE ANYTHING BELOW OR ANGELS WILL COME DOWN AND KILL YOU------\\
    globals 
        public boolean array ISHOOKED
    endglobals
    
private struct Meathook 
    unit array  link            [MAXCHAINS]
    integer     len             = 0
    real        movement 
    unit        hooker
    unit        hookhead
    boolean     hooked          = false
    unit        hooktarg        = null
    real        directionangle
    real        angleCos
    real        angleSin
    real        dis             = 0.
    real        rangepassed     = 0.
    real        distance        = 0.
    
        method launch takes unit which, real direction returns nothing
            set .hooker         = which
            set .directionangle = direction
            set .movement       = Speed     ( GetUnitAbilityLevel( .hooker, RAWID ) ) * TT_PERIOD
            set .angleCos       = Cos       ( direction * DTR ) *.movement
            set .angleSin       = Sin       ( direction * DTR ) *.movement
            set .hookhead       = CreateUnit( GetOwningPlayer( which ), HOOKHEADID, GetUnitX( which ), GetUnitY( which ), direction)
            set .distance       = Distance  ( GetUnitAbilityLevel( .hooker, RAWID ) ) 
            call TT_Start(function Meathook.fly, this)
        endmethod
        
        static method fly takes nothing returns boolean
            local Meathook hook = TT_GetData(             )
            local real cx       = GetUnitX  ( hook.hooker )
            local real cy       = GetUnitY  ( hook.hooker )
            local real nx
            local real ny
            local real lx 
            local real ly 
            local real llx 
            local real lly
            local real angletonext
            local real distance
            local real angle
            local real angle2
            local real absmod
            
            local integer index = hook.len
            local group g
            local unit u
            local boolean flag = false
            

            
            // Chain moving
            
            loop
               
                
                set lx  = GetUnitX(hook.link[index])
                set ly  = GetUnitY(hook.link[index])
                
                set llx = GetUnitX(hook.link[index-1])
                set lly = GetUnitY(hook.link[index-1])
            
                set angletonext = Atan2(ly-lly,lx-llx)
                call SetSafeX(hook.link[index],llx+CHAINEVERY * Cos(angletonext))
                call SetSafeY(hook.link[index],lly+CHAINEVERY * Sin(angletonext))
                call SetUnitFacingTimed(hook.link[index], angletonext * RTD - 180,0)
                               
                
                
                set index = index - 1
                exitwhen index < 1
            endloop
            
            set lx  = GetUnitX(hook.link[0])
            set ly  = GetUnitY(hook.link[0])

        
            set llx = GetUnitX(hook.hookhead)
            set lly = GetUnitY(hook.hookhead)
            
            set angletonext = Atan2( lly - ly, llx - lx )
            
            set nx = lx + hook.movement * Cos(angletonext) 
            set ny = ly + hook.movement * Sin(angletonext) 
            
            call SetSafeX(hook.link[0],GetUnitX(hook.hookhead))
            call SetSafeY(hook.link[0],GetUnitY(hook.hookhead))
            call SetUnitFacingTimed(hook.link[0], angletonext, 0)
            
            // Hook forward and avoid leaving the map
            
            set lx = GetUnitX(hook.hookhead)
            set ly = GetUnitY(hook.hookhead)
            
            set nx = lx + hook.angleCos
            set ny = ly + hook.angleSin
            
            if not IsPointInMap(nx,ny) then
            
                if ny != SafeY(ny) then
                    set hook.directionangle = -hook.directionangle
                else
                    set hook.directionangle = 180-hook.directionangle               
                endif
                
                set hook.angleCos = hook.movement * Cos(hook.directionangle * DTR)
                set hook.angleSin = hook.movement * Sin(hook.directionangle * DTR)
                set nx = lx + hook.angleCos
                set ny = ly + hook.angleSin
            endif
   
            call SetSafeX(hook.hookhead,nx)
            call SetSafeY(hook.hookhead,ny)
            
            
            
   
   
   
            // new chain link
            set hook.dis = hook.dis + hook.movement
            if  hook.dis >= CHAINEVERY and hook.len < 2 then
                set llx = GetUnitX(hook.link[hook.len-1])
                set lly = GetUnitY(hook.link[hook.len-1])
                
                
                set angletonext = Atan2(cy-lly,cx-llx) * RTD
                
                if hook.len == 0 then
                    
                    set angletonext = hook.directionangle
                    
                    set hook.link[hook.len] = CreateUnit(GetOwningPlayer(hook.hooker), CHAINID, nx , ny ,angletonext)
                else
                    set hook.link[hook.len] = CreateUnit(GetOwningPlayer(hook.hooker), CHAINID, llx+CHAINEVERY*Cos(angletonext*DTR) , lly+CHAINEVERY*Sin(angletonext*DTR) ,angletonext-180)
                endif
                set hook.len = hook.len + 1                
                set hook.dis = 0.
                if hook.len > MAXCHAINS-2 then
                    set flag = true
                endif
            endif
            
            set lx = GetUnitX(hook.link[hook.len-1])
            set ly = GetUnitY(hook.link[hook.len-1])
            
            set distance = SquareRoot(SquareDistance(lx,ly,cx,cy))
            
            if distance >= CHAINEVERY and hook.len > 1 and flag == false then
                set hook.dis = 0.
                set angletonext = Atan2(cy-ly,cx-lx)
                set nx = lx + CHAINEVERY * Cos(angletonext)
                set ny = ly + CHAINEVERY * Sin(angletonext)
                set hook.link[hook.len] = CreateUnit(GetOwningPlayer(hook.hooker), CHAINID, nx , ny ,angletonext*RTD-180)
                set hook.len = hook.len + 1
                

            
                if hook.len > MAXCHAINS-2  then
                    set flag = true
                endif
            endif
            
            set hook.rangepassed = hook.rangepassed + hook.movement
            
            if hook.rangepassed >= hook.distance then
                set flag = true
            endif
            
            //  Getting Units
            set lx = GetUnitX(hook.hookhead)
            set ly = GetUnitY(hook.hookhead)
            
            set g = CreateGroup()
            call GroupEnumUnitsInRange(g,lx,ly,RADIUS,AliveGroundFilter)
            
            loop
                set u = FirstOfGroup(g)
                call GroupRemoveUnit(g,u)
                
                exitwhen u == null
                
                if  hook.hooker != u  then
                    
                    if IsUnitEnemy( u, GetOwningPlayer(hook.hookhead)) then
                        call UnitDamageTarget(hook.hooker,u, Damage(GetUnitAbilityLevel(hook.hooker, RAWID)), true, false, ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                    endif
                    if ISHOOKED[GetUnitIndex(u)] != true then
                        set ISHOOKED[GetUnitIndex(u)] = true
                        set hook.hooked = true
                        set hook.hooktarg = u
                        call SetUnitTimeScale(hook.hookhead,0)
                    else 
//CHANGED!
                        //call CreateTextTagDefault("HEADSHOT!!", "|cffff0000", GetUnitX(hook.hookhead), GetUnitX(hook.hookhead))
                        //call UnitDamageTarget(hook.hooker,u, 1000000000., true, false, ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                    endif
                    set flag = true
                    exitwhen true
                endif
            endloop
            call DestroyGroup(g)
            set g = null
            
            // Bounce off buildings
            if flag == false then
                set g = CreateGroup()
                set lx = GetUnitX(hook.hookhead)
                set ly = GetUnitY(hook.hookhead)
                set angle = hook.directionangle
                
                call GroupEnumUnitsInRange(g,lx,ly,RADIUS*1.55,AliveBuildingFilter)
                
                loop 
                    set u = FirstOfGroup(g)
                    
                    exitwhen u == null
                    
                    call GroupRemoveUnit(g,u)
                
                    set llx = GetUnitX(u)
                    set lly = GetUnitY(u)
                    
                    set angle2 = ModuloReal(Atan2(ly-lly, lx-llx) * bj_RADTODEG,360)
                    set absmod = RAbsBJ(angle2 - angle)
                    
                    if absmod >= 90 and absmod <= 270  then
                        if ( angle2 > 45 and angle2 < 135 ) or ( angle2 > 225 and angle2 < 315 ) then
                            set hook.directionangle = -angle
                        else
                            set hook.directionangle = 180-angle
                        endif
                                
                        set hook.angleSin = Sin( hook.directionangle * DTR ) * hook.movement
                        set hook.angleCos = Cos( hook.directionangle * DTR ) * hook.movement
                    endif
                    set u = null
            
                endloop
                call DestroyGroup(g)
                set g = null
            endif
            
           
            
            
            
            
            if flag == true then
                call TT_Start(function Meathook.reverse, hook)
            endif
            return flag
        endmethod
        
        static method reverse takes nothing returns boolean
            local Meathook hook = TT_GetData()
            local real cx = GetUnitX(hook.hooker)
            local real cy = GetUnitY(hook.hooker)
            local real nx
            local real ny
            local real lx 
            local real ly 
            local real llx 
            local real lly
            
            local integer index = 0
            local real angletonext
            local real distance
            local group g
            local unit u
            local boolean flag = false
 
            //link remove
            set lx = GetUnitX(hook.link[hook.len-1])
            set ly = GetUnitY(hook.link[hook.len-1])
            
            set nx = lx-cx
            set ny = ly-cy
            set distance = SquareRoot(nx*nx+ny*ny)
            
            if distance <= CHAINEVERY then
                call KillUnit(hook.link[hook.len-1])
                call ShowUnit(hook.link[hook.len-1],false)
                set hook.len = hook.len - 1
                if hook.len <= 1 then
                    set flag = true
                endif
            else
                set angletonext = Atan2(cy-ly,cx-lx)
                set nx = lx + hook.movement * Cos(angletonext)
                set ny = ly + hook.movement * Sin(angletonext)
                call SetSafeX(hook.link[hook.len-1],nx)
                call SetSafeY(hook.link[hook.len-1],ny)
                call SetUnitFacingTimed(hook.link[hook.len-1], (angletonext)*RTD+180,0)             
            endif
            
            if hook.len <= 1 then
                    set flag = true
            endif
            
            

            //chain move
            
            loop
                exitwhen index > hook.len - 2
                
                set lx = GetUnitX(hook.link[index])
                set ly = GetUnitY(hook.link[index])
                
                set llx = GetUnitX(hook.link[index+1])
                set lly = GetUnitY(hook.link[index+1])
                
                set distance = SquareRoot(SquareDistance(lx,ly,llx,lly))
                
                if distance <= CHAINEVERY then
                
                    set angletonext = Atan2(lly-ly,llx-lx)
                    
                    set nx = lx + hook.movement * Cos(angletonext)
                    set ny = ly + hook.movement * Sin(angletonext)
                    
                    call SetSafeX(hook.link[index],nx)
                    call SetSafeY(hook.link[index],ny)
                    call SetUnitFacing(hook.link[index], (angletonext)*RTD+180)
                
                else
                
                    set angletonext = Atan2(ly-lly,lx-llx)
                    
                    set nx = llx + CHAINEVERY * Cos(angletonext)
                    set ny = lly + CHAINEVERY * Sin(angletonext)
                    
                    call SetSafeX(hook.link[index],nx)
                    call SetSafeY(hook.link[index],ny)
                    call SetUnitFacing(hook.link[index], (angletonext)*RTD)
 
                endif
                
                set index = index + 1
            endloop
            
            //hook move
            
            set lx = GetUnitX(hook.hookhead)
            set ly = GetUnitY(hook.hookhead)
            set llx = GetUnitX(hook.link[0])
            set lly = GetUnitY(hook.link[0])
            

            

                set angletonext = Atan2(ly-lly,lx-llx)
                
                set nx = llx + CHAINEVERY * Cos(angletonext)
                set ny = lly + CHAINEVERY * Sin(angletonext)           

            call SetSafeX(hook.hookhead,nx)
            call SetSafeY(hook.hookhead,ny)
                
            if hook.hooked == true then
                call SetSafeX(hook.hooktarg,nx)
                call SetSafeY(hook.hooktarg,ny)
            else
                set g = CreateGroup()
                call GroupEnumUnitsInRange(g,nx,ny,RADIUS,AliveGroundFilter)
                loop
                    set u = FirstOfGroup(g)
                    call GroupRemoveUnit(g,u)
                
                    exitwhen u == null
                    
                    if  hook.hooker != u  then
                    
                        if IsUnitEnemy( u, GetOwningPlayer(hook.hookhead)) then
                            call UnitDamageTarget(hook.hooker,u, Damage(GetUnitAbilityLevel(hook.hooker, RAWID)), true, false, ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                        endif
                        if ISHOOKED[GetUnitIndex(u)] != true then
                            set ISHOOKED[GetUnitIndex(u)] = true
                            set hook.hooked = true
                            set hook.hooktarg = u
                            call SetUnitTimeScale(hook.hookhead,0)
                        else
//CHANGED!
                            //call CreateTextTagDefault("HEADSHOT!!", "|cffff0000", GetUnitX(hook.hookhead), GetUnitY(hook.hookhead))
                            //call UnitDamageTarget(hook.hooker,u, 1000000000., true, false, ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                        endif
                        exitwhen true
                        
                    endif                
                endloop
            
            endif
            
            //CHANGED!
            
            if (GetWidgetLife(hook.hooker) < 0.405) then
                set hook.hooked = true
                set hook.hooktarg = null
                call SetUnitTimeScale(hook.hookhead,0)
            endif
            
            if flag == true then
                call hook.destroy()
            endif
            
            return flag
        endmethod
        
        method onDestroy takes nothing returns nothing
            call KillUnit(.hookhead)
            call ShowUnit(.hookhead,false)
            call KillUnit(.link[0])
            call ShowUnit(.link[0],false)
            if .hooked == true then
                call SetUnitX(.hooktarg,GetUnitX(.hooker))
                call SetUnitY(.hooktarg,GetUnitY(.hooker))
                set ISHOOKED[GetUnitIndex(.hooktarg)] = false
            endif
        endmethod
endstruct

private function Actions takes nothing returns nothing
    local Meathook hook = Meathook.create()
    local unit cast = GetTriggerUnit()
    local real cx = GetUnitX(cast)
    local real cy = GetUnitY(cast)
    local location loc = GetSpellTargetLoc()
    local real tx = GetLocationX(loc)
    local real ty = GetLocationY(loc)
    local real angle = Atan2(ty-cy,tx-cx) * RTD
    call RemoveLocation(loc)
    set loc = null
    call hook.launch(cast,angle)
endfunction

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

//===========================================================================
public function InitTrig takes nothing returns nothing
    set gg_trg_MeatHook = CreateTrigger (                                                 )
    call TriggerRegisterAnyUnitEventBJ  ( gg_trg_MeatHook, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction               ( gg_trg_MeatHook,           function Actions     )
    call TriggerAddCondition            ( gg_trg_MeatHook, Condition(function Conditions) )
endfunction

endscope
 
if hook.hooked == true then
call SetSafeX(hook.hooktarg,nx)
call SetSafeY(hook.hooktarg,ny)
else


so you are always trying to move null here. i dont know wether it would cause problems but you should also check if hook.hooktarg != null. i dont know how this kind of error is called...
 
So, would this solve the problem, by puting my code change in front of the part you mentioned?

JASS:
            if (GetWidgetLife(hook.hooker) < 0.405) then
                set hook.hooked = false
                set hook.hooktarg = null
                call SetUnitTimeScale(hook.hookhead,0)
            endif
            
            if hook.hooked == true then
                call SetSafeX(hook.hooktarg,nx)
                call SetSafeY(hook.hooktarg,ny)
            else
 
uhm, i was talking about checking if hooktarg is null and dont move the hooktarg if it is null.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    +1
  • Varine Varine:
    I've been really into this
  • Varine Varine:
    It's a musical retelling of The Odyssey
  • Varine Varine:
    Also if you want mixed drinks without alcohol, there's a handful of non-alcoholic 'spirits'. Ritual Zero and Free Spirit are the two brands I've tried and they are alright. They don't have a TON of options, but they have some gin and whiskey alternatives that are fun to play with.
  • Varine Varine:
    I got a couple bottles to make some mixy drinks around holidays when I mostly want to drink, they aren't exact replacements but they are surprisingly close.
  • Varine Varine:
    I ended up with some hop water things that I really like instead for when I want to feel like I'm participating
  • Varine Varine:
    I just got moved to unsupervised probation, so I can get away with drinking a bit now. I technically am not supposed to, but I don't think I get checked anymore. I really want to smoke weed but it scares me
  • The Helper The Helper:
    Happy Wednesday! I am not feeling it today, my teeth are bothering me, or the fragments that were left behind are coming up and it is extra painful today. Don't ever let them tell you that getting full dental implants is easy. They have to pull all your teeth to do those and apparently they are not very good at getting all the teeth out all the time.
    +1
  • The Helper The Helper:
    That is true with Dentures too. I am taking the day off and working from home. The big difference is though, I am not drinking anymore so I have a nice tea and coffee regimen set up :)
  • Varine Varine:
    I wish i had a 3D scanner
  • Varine Varine:
    That would make what I'm doing SO much easier
  • Varine Varine:
    I guess I could just take a picture of it and then use that as a reference. I have a circuit board with some weird holes I can't get measured right
  • Varine Varine:
    Oh I got a Klipper mod for my big printer! It comes with a bunch of parts and I don't totally understand what it does but apparently it's way better than Marlin, which is I think what it currently runs
    +1
  • The Helper The Helper:
    Nice!
  • Varine Varine:
    You're people in Florida safe? The only couple I know are safe but I think they evacuated.
  • The Helper The Helper:
    They are without power but safe. Flood damage and stuff alot of tornados hit east Florida. No fatalities or injuries to any of my people that I know of
  • The Helper The Helper:
    They have been through all the Florida hurricanes they are like me I have been through every texas hurricane for the last 50 years.
  • The Helper The Helper:
    Never evacuated.
  • The Helper The Helper:
    Check out this bad ass Chicken Noodle soup recipe - https://www.thehelper.net/threads/crack-chicken-noodle-soup.196687/
  • The Helper The Helper:
    Happy Saturday!
    +1

      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