• 🏆 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] GetLocationZ

Status
Not open for further replies.
Level 6
Joined
Jul 24, 2008
Messages
180
Is there a way to take the lowest Z value in a group of Z values without a ton of equations?

EX: Take 8 Z values in each cardinal direction (N,NE,E,SE,S,SW,W,NW) in 100 radius and teleport to the lowest point?
 
Level 6
Joined
Jul 24, 2008
Messages
180
In what way? This is what I'm doing. It works perfectly, except the unit slides up the hill instead of down.

  • Slide
    • Events
      • Time - Every 0.01 seconds of game time
    • Conditions
    • Actions
      • Set temploc1 = (Position of Peasant 0000 <gen>)
        • Do Multiple ActionsFor each (Integer A) from 1 to 8, do (Actions)
          • Loop - Actions
            • Custom script: call GetLocationZ(udg_temploc1)
            • Custom script: set udg_Z1 = GetLocationZ(udg_temploc1)
            • Custom script: call GetLocationX(udg_temploc1)
            • Custom script: set udg_X1 = GetLocationX(udg_temploc1)
            • Set temploc2 = (temploc1 offset by 1.00 towards (45.00 x (Real((Integer A)))) degrees)
            • Custom script: call GetLocationZ(udg_temploc2)
            • Custom script: set udg_Z2 = GetLocationZ(udg_temploc2)
            • Custom script: call GetLocationX(udg_temploc2)
            • Custom script: set udg_X2 = GetLocationX(udg_temploc2)
              • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Abs(((Z1 - Z2) / (X1 - X2)))) Greater than or equal to (>=) (Square root(3.00))
                • Then - Actions
                  • Special Effect - Create a special effect at temploc1 using Objects\Spawnmodels\Undead\ImpaleTargetDust\ImpaleTargetDust.mdl
                  • Unit - Move Peasant 0000 <gen> instantly to temploc2
                  • Special Effect - Destroy (Last created special effect)
                • Else - Actions
            • Custom script: call RemoveLocation(udg_temploc2)
      • Custom script: call RemoveLocation(udg_temploc1)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
In what way? This is what I'm doing. It works perfectly, except the unit slides up the hill instead of down.
You already have 90% of this GUI code in JASS, so just convert it to custom script at this point.

Post the custom script here on this forum in
JASS:
 tags and we'll fix it up for you.
 
We're a lot keener to fix a JASS code than a GUI code because we don't have to load up WorldEdit (good if we're not in front of our normal pc) and JASS is 10x easier to read.
 
Level 6
Joined
Jul 24, 2008
Messages
180
Thanks, I did that already, and I don't really now the commands and format for most of it. The intent is to "slide" units down when they are on a slope greater than 60 degrees. It works, but units slide up, so I wanted to find lowest point, but am not sure how the "Min - " function works.

JASS:
function Trig_Slide_Func003C takes nothing returns boolean
    if ( not ( RAbsBJ(( ( udg_Z1 - udg_Z2 ) / ( udg_X1 - udg_X2 ) )) >= SquareRoot(3.00) ) ) then
        return false
    endif
    return true
endfunction

function Trig_Slide_Actions takes nothing returns nothing
    set udg_temploc1 = GetUnitLoc(gg_unit_hpea_0000)
    set bj_forLoopAIndex = 1
    set bj_forLoopAIndexEnd = 8
    loop
        exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
        call GetLocationZ(udg_temploc1)
        set udg_Z1 = GetLocationZ(udg_temploc1)
        call GetLocationX(udg_temploc1)
        set udg_X1 = GetLocationX(udg_temploc1)
        set udg_temploc2 = PolarProjectionBJ(udg_temploc1, 1.00, ( 45.00 * I2R(GetForLoopIndexA()) ))
        call GetLocationZ(udg_temploc2)
        set udg_Z2 = GetLocationZ(udg_temploc2)
        call GetLocationX(udg_temploc2)
        set udg_X2 = GetLocationX(udg_temploc2)
        set udg_temploc3[GetForLoopIndexB()] = udg_temploc2
        set bj_forLoopAIndex = bj_forLoopAIndex + 1
    endloop
    if ( Trig_Slide_Func003C() ) then
        call AddSpecialEffectLocBJ( udg_temploc1, "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" )
        call SetUnitPositionLoc( gg_unit_hpea_0000, udg_temploc2 )
        call DestroyEffect( GetLastCreatedEffectBJ() )
    else
    endif
    call RemoveLocation(udg_temploc1)
    call RemoveLocation(udg_temploc2)
    set bj_forLoopAIndex = 1
    set bj_forLoopAIndexEnd = 8
    loop
        exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
        call RemoveLocation(udg_temploc3[(bj_forLoopAIndex)])
        set bj_forLoopAIndex = bj_forLoopAIndex + 1
    endloop
    call RemoveLocation(udg_temploc4)
endfunction

//===========================================================================
function InitTrig_Slide takes nothing returns nothing
    set gg_trg_Slide = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Slide, 0.01 )
    call TriggerAddAction( gg_trg_Slide, function Trig_Slide_Actions )
endfunction
 
Level 6
Joined
Jul 24, 2008
Messages
180
That sounds unnecessary... but thanks. :)

Edit: Below is an alternate form which doesn't work, but I'm not sure why. The dirt FX appears, but no movement.

JASS:
function Trig_Slide_Func006Func006Func001C takes nothing returns boolean
    if ( not ( udg_Z1 > udg_Z2 ) ) then
        return false
    endif
    return true
endfunction

function Trig_Slide_Func007C takes nothing returns boolean
    if ( not ( RAbsBJ(( ( udg_Z1 - udg_Z2 ) / ( udg_X1 - udg_X2 ) )) >= SquareRoot(3.00) ) ) then
        return false
    endif
    return true
endfunction

function Trig_Slide_Actions takes nothing returns nothing
    set udg_temploc1 = GetUnitLoc(gg_unit_hpea_0000)
    call GetLocationZ(udg_temploc1)
    set udg_Z1 = GetLocationZ(udg_temploc1)
    call GetLocationX(udg_temploc1)
    set udg_X1 = GetLocationX(udg_temploc1)
    set bj_forLoopAIndex = 1
    set bj_forLoopAIndexEnd = 8
    loop
        exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
        set udg_temploc2 = PolarProjectionBJ(udg_temploc1, 10.00, ( 45.00 * I2R(GetForLoopIndexA()) ))
        call GetLocationZ(udg_temploc2)
        set udg_Z2 = GetLocationZ(udg_temploc2)
        call GetLocationX(udg_temploc2)
        set udg_X2 = GetLocationX(udg_temploc2)
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = 8
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            if ( Trig_Slide_Func006Func006Func001C() ) then
                set udg_temploc3 = udg_temploc2
                call RemoveLocation(udg_temploc2)
            else
                call RemoveLocation(udg_temploc2)
            endif
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        set bj_forLoopAIndex = bj_forLoopAIndex + 1
    endloop
    if ( Trig_Slide_Func007C() ) then
        call AddSpecialEffectLocBJ( udg_temploc1, "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" )
        call SetUnitPositionLoc( gg_unit_hpea_0000, udg_temploc3 )
        call DestroyEffect( GetLastCreatedEffectBJ() )
    else
    endif
    call RemoveLocation(udg_temploc1)
    call RemoveLocation(udg_temploc3)
endfunction

//===========================================================================
function InitTrig_Slide takes nothing returns nothing
    set gg_trg_Slide = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Slide, 0.01 )
    call TriggerAddAction( gg_trg_Slide, function Trig_Slide_Actions )
endfunction
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Here is your fully-optimized, fully-functional trigger, have fun :p

I also added a bit where you can press Esc to enable/disable the sliding mechanism. It's initially on; Esc turns it off; Esc turns it back on.

JASS:
constant function SlideDust takes nothing returns string
return "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
endfunction
function SlideActions takes nothing returns nothing
 local boolean     move=    false
 local unit         u=      gg_unit_hpea_0000
 local location    loc=     GetUnitLoc( u )
 local location    eloc=    loc
 // above variables need deallocation
 local integer      i=      1
 local real        proj=    45 * bj_DEGTORAD
 local real         x=      GetUnitX( u ) + 1
 local real         y=      GetUnitY( u ) + 1
 local real         c
 local real         d
 
    loop
        set c= x * Cos( proj * i )
        set d= y * Sin( proj * i )
        call MoveLocation( eloc,c,d )
        if GetLocationZ( loc ) > GetLocationZ( eloc ) then
            call MoveLocation( loc,c,d )
            set move= true  // this is key
        endif
        set i= i + 1
        exitwhen i > 8
    endloop
 
    if move then
        call DestroyEffect( AddSpecialEffect( SlideDust() , x-1 , y-1 ) )
        call SetUnitPositionLoc( u , loc )
    endif
 
 // deallocate variables to prevent lag
 call RemoveLocation( eloc )
 call RemoveLocation( loc )
 set move = null
 set    u = null
 set  loc = null
 set eloc = null
endfunction
function OnAndOff takes nothing returns nothing
    if IsTriggerEnabled( gg_trg_Slide ) then 
        call DisableTrigger( gg_trg_Slide )
    else
        call EnableTrigger( gg_trg_Slide )
    endif
endfunction
function InitTrig_Slide takes nothing returns nothing
 local trigger t = CreateTrigger(  )
    call TriggerRegisterPlayerEvent( t , Player(0) , EVENT_PLAYER_END_CINEMATIC )
    call TriggerAddAction( t , function OnAndOff )
 set t = null
    set gg_trg_Slide = CreateTrigger()
    call TriggerRegisterTimerEvent( gg_trg_Slide , 0.01 , true )
    call TriggerAddAction( gg_trg_Slide , function SlideActions )
endfunction
You can delete all the variables you made from the Variable Editor as I haven't used any of them.
 
Last edited:
Level 6
Joined
Jul 24, 2008
Messages
180
Thanks so much! I'll have to test and compare to original script right away! :grin:


Edit: The WE seems to have a major issue with this section of code. Can you fix?

JASS:
    If IsTriggerEnabled( gg_trg_Slide ) then 
        call DisableTrigger( gg_trg_Slide )
    else
        call EnableTrigger( gg_trg_Slide )
    endif
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I can optimize this further, but WorldEditor doesn't have the features I would need to make those changes. You would need to install a program called Jass NewGen Pack.

I don't expect you to download it, but NewGen significantly increases what you can create in the editor. It allows you to create constant variables, which are faster than normal variables.

This is how the code would look if I optimized it with NewGen (delete the peasant if you use this):

JASS:
scope Slide initializer Init
globals
    private constant unit       u = CreateUnit(Player(0),'hpea',0,0)
    private constant trigger    t = CreateTrigger()
    private constant trigger    s = CreateTrigger()
    private constant string  path = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
    private constant real    proj = 45*bj_DEGTORAD
    private boolean          move
    private location         eloc = Location(0,0)
    private location          loc = eloc
    private real array        cos
    private real array        sin
    private real               ex
    private real               ey
    private real                x
    private real                y
    private real                z
    private integer             i
endglobals
private function Actions takes nothing returns nothing
    set move = false
    set    i = 0
    set    x = GetUnitX(u)
    set    y = GetUnitY(u)
        call   MoveLocation(loc,x,y)
    set    z = GetLocationZ(loc)
    loop
        set ex= x + 3 * cos[ i ]
        set ey= y + 3 * sin[ i ]
        call MoveLocation(eloc,ex,ey)
        if GetLocationZ(eloc) < z then
            call MoveLocation(loc,ex,ey)
            set z = GetLocationZ(loc)
            set move= true
        endif
        set i= i + 1
        exitwhen i > 7
    endloop
    if move then
        call DestroyEffect(AddSpecialEffect(path,x,y))
        call SetUnitPositionLoc(u,loc)
    endif
endfunction
private function Toggle takes nothing returns nothing
    if IsTriggerEnabled(s) then 
        call DisableTrigger(s)
    else
        call EnableTrigger(s)
    endif
endfunction
private function Init takes nothing returns nothing
    set i = 1
    loop
        set cos[i - 1] = Cos( proj * i )
        set sin[i - 1] = Sin( proj * i )
        set i = i + 1
        exitwhen i > 8
    endloop
    call TriggerAddAction(s,function Actions)
    call TriggerAddAction(t,function Toggle)
    call TriggerRegisterTimerEvent(s,0.03,true)
    call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
endfunction
endscope

This way, there is no need to keep creating a new "loc" and a new "eloc", there is no need to destroy them, there is no need to keep allocating/unallocating unit u and boolean move every 0.01 seconds. There might be a visual difference; there might not, but I assure you this way puts much less stress on your computer and on the game.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Then just insert this into the custom script, instead of the trigger, and it should work wonders (delete the peasant; this creates a new one in the middle of the map):

JASS:
scope Slide initializer Init
globals
// prefix e for 'Enumeration' in loop
    private constant unit       u = CreateUnit(Player(0),'hpea',0,0)
    private constant trigger    t = CreateTrigger()
    private constant trigger    s = CreateTrigger()
    private constant string  path = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
    private constant real    proj = 45*bj_DEGTORAD
    private boolean          move
    private location         eloc = Location(0,0)
    private location          loc = eloc
    private real array        cos
    private real array        sin
    private real               ex
    private real               ey
    private real                x
    private real                y
    private real                z
    private integer             e
endglobals
private function Actions takes nothing returns nothing
    set move = false
    set    e = 0
    set    x = GetUnitX(u)
    set    y = GetUnitY(u)
        call   MoveLocation(loc,x,y)
    set    z = GetLocationZ(loc)
    loop
        set ex= x + 3 * cos[e]
        set ey= y + 3 * sin[e]
        call MoveLocation(eloc,ex,ey)
        if GetLocationZ(eloc) < z then
            call MoveLocation(loc,ex,ey)
            set z = GetLocationZ(loc)
            set move= true
        endif
        set e= e + 1
        exitwhen e > 7
    endloop
    if move then
        call DestroyEffect(AddSpecialEffect(path,x,y))
        call SetUnitPositionLoc(u,loc)
    endif
endfunction
private function Toggle takes nothing returns nothing
    if IsTriggerEnabled(s) then 
        call DisableTrigger(s)
    else
        call EnableTrigger(s)
    endif
endfunction
private function Init takes nothing returns nothing
    set e = 1
    loop
        set cos[ e - 1 ] = Cos( proj * e )
        set sin[ e - 1 ] = Sin( proj * e )
        set e = e + 1
        exitwhen e > 8
    endloop
    call TriggerAddAction(s,function Actions)
    call TriggerAddAction(t,function Toggle)
    call TriggerRegisterTimerEvent(s,0.03,true)
    call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
endfunction
endscope
The Python reference doesn't have to do with this code; he was saying he prefers Python to JASS. The capital I was the interference that wouldn't let the code parse for you. It's fixed now.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
How to do it for every unit on the map (type add to create an additional peasant or remove to kill one):

JASS:
scope Slide initializer Init
globals
    private constant string  path = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
    private constant trigger    T = CreateTrigger() //• T    == Main Trigger               •|
    private constant group      g = CreateGroup()   //• g    == Unit Group; do not destroy •|
    private boolean          move                   //• move == Will move unit "u" if true •|
    private location         eloc = Location(0,0)   //• eloc == Test Location              •|
    private location          loc = eloc            //• loc  == True Location              •|
    private real array        cos                   //• cos & sin are pre-calculated       •|
    private real array        sin                   //••••••••••••••••••••••••••••••••••••••|
    private real               ex
    private real               ey
    private real                x
    private real                y
    private real                z
    private integer             e
    private unit                u
endglobals
private function Slide takes nothing returns nothing
    call GroupEnumUnitsInRect( g , bj_PLAYABLE_MAP_RECT , null )
//* Init Loop 1 ********************** Loop 1 Rdy
    loop
    set      u  =  FirstOfGroup( g )
    exitwhen u  == null
    call GroupRemoveUnit( g , u )
    set   move  =  false
    set      x  =  GetUnitX(u)
    set      y  =  GetUnitY(u)
    call MoveLocation(loc,x,y)
    set      z  =  GetLocationZ(loc)
    set      e  =  0
//* Init Loop 2 ********************** Loop 2 Rdy
    loop
    set     ex  =  x + 3 * cos[e]
    set     ey  =  y + 3 * sin[e]
    call MoveLocation(eloc,ex,ey)
//* Evaluate Height ******************* MOVE? Rdy
    if GetLocationZ(eloc) < z then
    call MoveLocation(loc,ex,ey)
    set      z  =  GetLocationZ(loc)
    set   move  =  true
    endif
//* End Evaluation ******************** MOVE? End
        set  e  =  e + 1
    exitwhen e  == 16
    endloop
//* End Loop 2 *********************** Loop 2 End
    if move then
        call DestroyEffect(AddSpecialEffect(path,x,y))
        call SetUnitPositionLoc(u,loc)
    endif
    endloop
//* End Loop 1 *********************** Loop 1 End
endfunction
private function Toggle takes nothing returns nothing
    if IsTriggerEnabled(T) then 
        call DisableTrigger(T)
    else
        call EnableTrigger(T)
    endif
endfunction
private function Remove takes nothing returns nothing
    local unit v
    call GroupEnumUnitsInRect( g , bj_PLAYABLE_MAP_RECT , null )
    set        e  =  1
//* Loop - Pick Random Unit to Remove ********//*
    loop
    set        v  =  FirstOfGroup( g )
    exitwhen   v  == null
    call GroupRemoveUnit( g , v )
    if GetRandomInt( 1 , e ) == 1 then
        set    u  =  v
        set    e  =  e + 1
    endif
    endloop
//* End Loop *********************************//*
    call KillUnit( u )
    set        v  =  null
endfunction
private function Add takes nothing returns nothing
    call CreateUnit(GetTriggerPlayer(),'hpea',0,0)
endfunction
 
//* The following initializes the four triggers/events/actions...
private function Maketrg takes string msg, code func returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddAction( t , func )
    set          e = 0
    loop  //* Init Player 0-11 (Players 1-12) *//
        call TriggerRegisterPlayerChatEvent( t , Player( e ) , msg , true)
        set      e = e + 1
        exitwhen e == 11
    endloop
    set t = null
endfunction
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()           //* Toggle on/off Trig
    call TriggerAddAction(t,function Toggle)    //* Toggle Trig actions
    call TriggerAddAction(T,function Slide)     //* Main Trig actions
    call TriggerRegisterTimerEvent(T,0.03,true) //* Main Trig events
    call Maketrg( "remove" , function Remove )  //* Init Remove Unit Trig
    call Maketrg( "add" , function Add )        //* Init Add Unit Trig
    set          e = 0
    loop
        if       e < 12 then
            call TriggerRegisterPlayerEvent(t,Player(e),EVENT_PLAYER_END_CINEMATIC)
        endif
        set  z = e + 1 * 22.5 * bj_DEGTORAD
        set cos[ e ] = Cos( z )
        set sin[ e ] = Sin( z )
        set      e = e + 1
        exitwhen e == 16
    endloop
    set t = null
endfunction
endscope

This will also work for up to 12 players.
 
Last edited:
Level 6
Joined
Jul 24, 2008
Messages
180
I figured .01/sec was excessive, but I hadn't tested different increments to determine the most efficient.

JASS:
//* Init Loop 2 ********************** Loop 2 Rdy
    loop
    set     ex  =  x + 3 * cos[e]
    set     ey  =  y + 3 * sin[e]
    call MoveLocation(eloc,ex,ey)

This is the section to determine the second point? If so, how far to you have it set to check and how would I modify it? I don't remember at all how sin, cosine, and tangent work.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Here's what can change:

That '3' in those two equations can be any number (just make sure you change both values to the new number). '3' is the offset in the polar projection (I changed it from 1 to 3 when I changed the timer from 0.01 to 0.03).

You can change the iterations in the loop to a higher value if you want a more specific location. 12 would be fine; I wouldn't push it higher than 16. If you want to change this part, let me know, and I'll fix the loops/arrays.
 
Level 9
Joined
Nov 28, 2008
Messages
704
Yeah, there is also no reason to enumerate this system 100 times per second as the human eye does not notice that much. 30 frames per second will look the same to normal human eyes as 100 frames per second.

Not true at all. I notice the difference when my WC3 is going at 30 and 60 FPS, and there's no way eyes are consistently always at 30 fps.

I would say 30 is more than enough, but the modern computer is ridiculously fast, so doing 0.01 second intervals is unlikely to lag at all. Just avoid hundreds of units!
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I've heard it that way, too, but considering videos play at 24 fps, you're really talking about a difference you would have to study intently to notice.

Edit: I think Mooglian has a point with computer processing speed. I updated the "all units slide" system to process 16 polar projections instead of 8 - that will add more dynamics to the slide feature.
 
Last edited:
Level 6
Joined
Jul 24, 2008
Messages
180
Okay, thanks a lot guys. You're really helpful. :D

EDIT: I've tested it and some problems arose.

1) Units slide even when the slope isn't steep enough to slide.

2) Units only slide west (left), whether moving up or down hills.
 
Last edited:
Status
Not open for further replies.
Top