Need Help with a Trip Wire Spell (and mysterious null variables)

Jonnycakes

New Member
Reaction score
6
Hi, I have been trying to make a trip wire ability-the premise is that the user targets two trees and then a trip wire is created between the two trees. If an enemy unit crosses the wire, it explodes. I am using Anitarf's spell here as a reference, but I am still running into a lot of problems.

The initial ability ('A00N') is based off of sentinel-when it is cast, the trigger stops the unit and forces the owner to hit the hotkey (T for trip wire) again, creating another tree selection cursor. Now the fun stuff happens-the unit gets a new instant cast dummy ability based on channel and the trigger spams its hotkey (L) for the player. If the second tree targeting is canceled, the dummy spell will be used, canceling the cast and triggering a clean-up of any variables involved.

This is where I am having issues. The function that is supposed to be spamming "L" is running, but the player variable that stores the owner of the casting unit (this.owner) is null for some reason. I can't figure out why this is, because when the struct (target) is created, .owner is NOT null. Shortly afterward, it is. Can anyone help me out on this one? (If you find other errors, that is appreciated as well!)

NOTE: I have only worked on the first part of this spell, which sets up the trip wire. It is fully functional as is-it just doesn't actually set up the trip wire. I will add the second half when I figure this out.

EDIT: Attached a test map. The unit is the mechanic, the spell is trip wire, and the current trigger is Trip Wire NEW. There are some BJDebugs that may get annoying as well-just a warning.


JASS:
scope tripwire initializer init

globals
    private hashtable h=InitHashtable()
endglobals

private function CheckTree takes destructable tree1, destructable tree2 returns boolean
    local real x=GetDestructableX(tree1)
    local real y=GetDestructableY(tree1)
    local real xx=GetDestructableX(tree2)
    local real yy=GetDestructableY(tree2)
    return true
    //return SquareRoot((xx-x)*(xx-x)+(yy-y)*(yy-y))<=600 and tree1!=tree2
endfunction

private struct trip
    unit caster
    player owner
    destructable tree1
    destructable tree2
    lightning l
    real duration
    
    private integer index
    private static trip array wire
    private static integer count=0
    private static timer time
endstruct


private struct target
    unit caster
    player owner
    destructable tree1
    
    private integer index=1
    static target array targ
    private static integer count=1
    private static timer time
    
    static method create takes unit u, destructable tree returns target
        local target t=target.allocate()
        set t.caster=u
        set t.owner=GetOwningPlayer(u)
        set t.tree1=tree
        set t.index=-1
        return t
    endmethod
    
    static method spam takes nothing returns nothing
        local integer i=1
        loop
            exitwhen i==target.count
            if GetLocalPlayer()==target.targ<i>.owner then
                call ForceUIKey(&quot;l&quot;)
            endif
            //if target.targ<i>.owner==null then
                //call BJDebugMsg(&quot;null &quot;+I2S(i))
            //endif
            set i=i+1
        endloop
    endmethod
    
    
    method start takes nothing returns nothing
        set .index=target.count
        call UnitAddAbility(.caster, &#039;A00C&#039;)
        call SaveInteger(h, GetHandleId(.caster), 0, .index)
        if GetLocalPlayer()==.owner then
            call ForceUIKey(&quot;t&quot;)
        endif
        if .owner==null then
            call BJDebugMsg(&quot;null owner&quot;)
        endif
        if target.count==1 then
            call TimerStart(target.time, .1, true, function target.spam)
        endif
        set target.count=target.count+1
        call BJDebugMsg(&quot;start  &quot;+I2S(target.count))
    endmethod
    
    method stop takes nothing returns nothing
        set target.count=target.count-1
        set target.targ[target.count].index=.index
        set target.targ[.index]=target.targ[target.count]
        set .index=-1
        if target.count==1 then
            call PauseTimer(target.time)
        endif
        call BJDebugMsg(&quot;stop  &quot;+I2S(target.count))
    endmethod
    
    static method onInit takes nothing returns nothing
        set target.time=CreateTimer()
    endmethod
        
endstruct

private function spellcast takes nothing returns boolean
    local unit u
    local target t
    local destructable tree
    
    if GetIssuedOrderId()==OrderId(&quot;sentinel&quot;) then
        set u=GetTriggerUnit()
        set tree=GetOrderTargetDestructable()
        if LoadInteger(h, GetHandleId(u), 0)==null then  //the unit has not cast the first target
            set t=target.create(u, tree)
            if t.caster==null or t.owner==null or t.tree1==null then
                call BJDebugMsg(&quot;null struct create&quot;)
            endif
            call DisableTrigger(GetTriggeringTrigger())
            call PauseUnit(u, true)
            call IssueImmediateOrder(u, &quot;stop&quot;)
            call PauseUnit(u, false)
            call EnableTrigger(GetTriggeringTrigger())
            call t.start()
        else    //the unit HAS cast the first target, validate second now...
            set t=target.targ[LoadInteger(h, GetHandleId(u), 0)]
            call t.stop()
            call FlushChildHashtable(h, GetHandleId(u))
            call UnitRemoveAbility(u, &#039;A00C&#039;)
            if CheckTree(t.tree1, tree) then
            else
                call DisableTrigger(GetTriggeringTrigger())
                call PauseUnit(u, true)
                call IssueImmediateOrder(u, &quot;stop&quot;)
                call PauseUnit(u, false)
                call EnableTrigger(GetTriggeringTrigger())
                call t.start()
            endif
        endif
        set u=null
        set tree=null
    endif
    return false
endfunction

private function spelleffect takes nothing returns boolean
    local unit u
    local target t
    
    if GetSpellAbilityId()==&#039;A00N&#039; then
        set u=GetTriggerUnit()
        set t=target.targ[LoadInteger(h, GetHandleId(u), 0)]
        call trip.create()
        //call t.destroy()
        call FlushChildHashtable(h, GetHandleId(u))
        call UnitRemoveAbility(u, &#039;A00C&#039;)
        set u=null
    endif
    
    return false
endfunction

private function cancel takes nothing returns boolean
    local unit u
    local target t
    if GetIssuedOrderId()==OrderId(&quot;channel&quot;) then
        set u=GetOrderedUnit()
        set t=target.targ[LoadInteger(h, GetHandleId(u), 0)]
        call t.stop()
        call FlushChildHashtable(h, GetHandleId(u))
        call UnitRemoveAbility(u, &#039;A00C&#039;)
        call t.destroy()
        set u=null
    endif
    return false
endfunction

private function herotype takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit())==&#039;H003&#039;
endfunction

//===========================================================================
private function init takes nothing returns nothing
    local trigger t=CreateTrigger()
    local integer x=0
    call TriggerAddCondition(t, Condition(function spellcast))
    loop
        exitwhen x==12
        call TriggerRegisterPlayerUnitEvent(t, Player(x), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, Filter(function herotype))
        set x=x+1
    endloop
    
    set x=0
    set t=CreateTrigger()
    call TriggerAddCondition(t, Condition(function spelleffect))
    loop
        exitwhen x==12
        call TriggerRegisterPlayerUnitEvent(t, Player(x), EVENT_PLAYER_UNIT_SPELL_EFFECT, Filter(function herotype))
        set x=x+1
    endloop
    
    set x=0
    set t=CreateTrigger()
    call TriggerAddCondition(t, Condition(function cancel))
    loop
        exitwhen x==12
        call TriggerRegisterPlayerUnitEvent(t, Player(x), EVENT_PLAYER_UNIT_ISSUED_ORDER, Filter(function herotype))
        set x=x+1
    endloop
    set t=null
endfunction
endscope</i></i>
 

Attachments

  • Trip Wire Test.w3x
    53 KB · Views: 132

Viikuna

No Marlo no game.
Reaction score
265
Not sure yet, you should however think if it is safe to use player variable there in the first place. Units owner might change and fuck the things up, which is probably why Anitarf did it like this: [lJASS]GetLocalPlayer()==GetOwningPlayer(trg.cancelList.caster)[/lJASS].

The other option would be to [lJASS]hook SetUnitOwner[/lJASS] native, if thats the only source of owner change in your map.



Anyways, dont really know what could change that owner variable like that... Lemme think this a little.

Providing a test map would be cool. Also these multi targetting scripts tend to be tricky. I remember Pyrogasm having some nasty problems with his Multi targetting system, and Im not even sure if that Tripwire works properly.
 

Jonnycakes

New Member
Reaction score
6
Well, the owner of the unit should never change. The map is a style which each player gets to choose one hero, and they will never change ownership, especially not in the middle of casting a (this) spell. I will attach a test map to the initial post once i figure out how :p

EDIT: done and done.
 

Viikuna

No Marlo no game.
Reaction score
265
Been testing it over and over again, with no luck. Cancel detection seems to be broken, and owner just is somehow null.


Anyways, Im trying something new now. I hope it works...


edit. Got frustrated to that shit and made a different kind of tripwire mechanic demo thinggy for you.

This method has its own downsides, though. I think its still worth of checking out.


edit2. Hrm. Silly me. If you wanted to do it this way, youd naturally use SpellChannelResponse first tree, which triggers before actually casting, and would save you from lot of trouble, cause you could simply abort the spell before Effect responses trigger... Not having to worry about making that cast not look like a cast...

Anyways. Damn.
 

Attachments

  • Trip Wire Test.w3x
    66.4 KB · Views: 133

Jonnycakes

New Member
Reaction score
6
Well, I think your method works a lot better than Anitarf's XD I looked through your test map and liked it a lot (especially how you add the lightning before the second cast). I may add in the spell channel event later, but I have a nice, working version now. Here is the new one that I created-there are a few new features:

-can't cast a tripwire that is too long or targets the same tree (it changes color if the caster moves out of range after targeting the first tree)

-the targeting sequence is aborted if the first tree is destroyed

-the cancel skill now gets added after the first tree is targeted so the player can abort the targeting manually (the icon is a cancel button)



Thank you for creating that demo map! It helped me a lot! +rep (nm, won't let me :()

JASS:
scope tripwire initializer init

globals
    private hashtable h=InitHashtable()
endglobals

private struct target
    unit caster
    real x1
    real y1
    real x2
    real y2
    destructable tree
    lightning l
    
    private integer index=1
    static target array targ
    private static integer count=1
    private static timer time
    
    static method render takes nothing returns nothing
        local real x
        local integer i=1
        local target t
        
        loop
            exitwhen i==target.count
            set t=target.targ<i>
            set t.x2=GetUnitX(t.caster)
            set t.y2=GetUnitY(t.caster)
            set x=1
            if SquareRoot((t.x2-t.x1)*(t.x2-t.x1)+(t.y2-t.y1)*(t.y2-t.y1))&gt;600 then
                set x=.5
            endif
            call MoveLightning(t.l, true, t.x1, t.y1, t.x2, t.y2)
            if GetLocalPlayer()==GetOwningPlayer(t.caster) then
                call SetLightningColor(t.l, x, x, x, 1)
            endif
            if GetDestructableLife(t.tree)&lt;=0 then
                call t.destroy()
                set i=i-1
            endif
            set i=i+1
        endloop
    endmethod
    
    static method create takes unit u, destructable tree returns target
        local target t=target.allocate()
        set t.caster=u
        call UnitAddAbility(u, &#039;A00C&#039;)
        set t.tree=tree
        set t.x1=GetDestructableX(tree)
        set t.y1=GetDestructableY(tree)
        set t.x2=GetUnitX(u)
        set t.y2=GetUnitY(u)
        set t.l=AddLightning(&quot;DRAM&quot;, true, t.x1, t.y1, t.x2, t.y2)
        call SetLightningColor(t.l, 1, 1, 1, 0)
        set t.index=target.count
        call SaveInteger(h, GetHandleId(u), 0, t.index)
        set target.targ[target.count]=t
        set target.count=target.count+1
        if target.count==2 then 
            call TimerStart(target.time, .04, true, function target.render)
        endif
        return t
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyLightning(.l)
        call UnitRemoveAbility(.caster, &#039;A00C&#039;)
        call FlushChildHashtable(h, GetHandleId(.caster))
        set target.count=target.count-1
        set target.targ[.index]=target.targ[target.count]
        set target.targ[.index].index=.index
        if target.count==1 then
            call PauseTimer(target.time)
        endif
    endmethod
    
    private static method onInit takes nothing returns nothing
        set target.time=CreateTimer()
    endmethod
        
endstruct

private struct tripwire
    unit caster
    player owner
    destructable tree1
    destructable tree2
    real x1
    real y1
    real x2
    real y2
    real duration
    unit dummy
    real angle
    real distance
    lightning l
    
    private integer index
    private static tripwire array trip
    private static integer count=1
    private static timer time
    
    private static method armed takes nothing returns nothing
        local integer i=1
        local tripwire t
    endmethod
    
    static method create takes target targ, destructable tree returns tripwire
        local tripwire t=tripwire.allocate()
        set t.caster=targ.caster
        set t.owner=GetOwningPlayer(t.caster)
        set t.tree1=targ.tree
        set t.tree2=tree
        set t.x1=targ.x1
        set t.y1=targ.y1
        set t.x2=GetDestructableX(tree)
        set t.y2=GetDestructableY(tree)
        set t.duration=0
        set t.angle=Atan2(t.y2-t.y1, t.x2-t.x1)
        set t.distance=SquareRoot((t.x2-t.x1)*(t.x2-t.x1)+(t.y2-t.y1)*(t.y2-t.y1))
        set t.l=AddLightning(&quot;DRAM&quot;, true, t.x1, t.y1, t.x2, t.y2)
        set t.dummy=CreateUnit(t.owner, &#039;h007&#039;, t.x1+t.distance*Cos(t.angle)/2, t.y1+t.distance*Sin(t.angle)/2, 0)
        set t.index=tripwire.count
        set tripwire.trip[t.index]=t
        set tripwire.count=tripwire.count+1
        if tripwire.count==2 then
            call TimerStart(tripwire.time, .04, true, function tripwire.armed)
        endif
        call targ.destroy()
        return t
    endmethod
    
    private static method onInit takes nothing returns nothing
        set tripwire.time=CreateTimer()
    endmethod
    
endstruct



private function actions takes nothing returns boolean
    local unit u
    local destructable tree
    local target t
    if GetSpellAbilityId()==&#039;A00C&#039; then
        set u=GetTriggerUnit()
        set t=target.targ[LoadInteger(h, GetHandleId(u), 0)]
        call t.destroy()
        set u=null
    elseif GetSpellAbilityId()==&#039;A00N&#039; then
        set u=GetTriggerUnit()
        set tree=GetSpellTargetDestructable()
        if LoadInteger(h, GetHandleId(u), 0)==null then
            call IssueImmediateOrder(u, &quot;stop&quot;)
            call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA)+10)
            set t=target.create(u, tree)
        else
            set t=target.targ[LoadInteger(h, GetHandleId(u), 0)]
            if SquareRoot((t.x2-t.x1)*(t.x2-t.x1)+(t.y2-t.y1)*(t.y2-t.y1))&lt;=600 and tree!=t.tree then
                call tripwire.create(t, tree)
            else
                call IssueImmediateOrder(u, &quot;stop&quot;)
                if GetLocalPlayer()==GetOwningPlayer(u) then
                    call StartSound(gg_snd_Error)
                endif
            endif
        endif
        set u=null
        set tree=null
    endif        
    return false
endfunction

private function herotype takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit())==&#039;H003&#039;
endfunction

//===========================================================================
private function init takes nothing returns nothing
    local trigger t=CreateTrigger()
    local integer x=0
    call TriggerAddCondition(t, Condition(function actions))
    loop
        exitwhen x==12
        call TriggerRegisterPlayerUnitEvent(t, Player(x), EVENT_PLAYER_UNIT_SPELL_EFFECT, Filter(function herotype))
        set x=x+1
    endloop
    set t=null
endfunction
endscope</i>


EDIT: finished the entire thing, splosions and all, and it works gloriously!
 
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