HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

FlyHeightGrid

11-30-2015, 08:16 PM#1
iNfraNe
Library GetUnitZ, the way to correctly obtain the z of a unit with movement type fly (and also the others). A demo map is included that shows the need for this library. Two lightning effects follow a flying unit. The chain lightning is calculated using the traditional GetLocationZ + GetUnitFlyHeight functions, the drain life with GetUnitZ.
Collapse JASS:
library GetUnitZ
    //******************************************************************************************************
    //                                                                                                      
    //      Library GetUnitZ, the way to correctly obtain the z of a unit with movement type fly.   
    //                                                                                                      
    //      Author: iNfraNe
    //
    //      API:
    //
    //      function GetUnitZ takes unit u returns real
    //          returns the correct absolute z value of a unit, regardless of its movement type. For flying
    //          units, a grid is made and the height which is appended naturally by the game engine
    //          is calculated.
    //
    //      function GetFlyHeightOffset takes real x, real y returns real
    //          calculates the absolute z of the point on the flyheight surface. This is the Z of the terrain
    //          plus the appended height for flying units.
    //
    //      Known issues:
    //
    //      - destructables influence the flyheight of flying units if their Fly-Over Height is set
    //        to anything but 0. Sadly, because their calculations are pathing-map based, it is not
    //        possible to correct for this, and we require users to make sure there are no destructables
    //        with a Fly-Over Height > 0.
    //      - The system will automatically unhide hidden destructables if the function is called on a
    //        nearby flying unit. Users are required to re-hide the destructables they have previously
    //        hidden after a function call to GetUnitNaturalFlyHeight.
    //
    //******************************************************************************************************
    globals
        private constant real RECT_SIZE = 1024. // the rect size at which destructables should be hidden who may alter the grid calculations. Don't change it.
        private location l = Location(0.,0.)
    endglobals
    private struct fh_grid256
        private static hashtable h = InitHashtable()
        private static rect r = null
        private static method hideDests takes nothing returns nothing
            // these destructables are in range of the current gridpoint, but their Z should not influence the terrain, hiding them means that they are ignored in GetLocationZ
            call ShowDestructable(GetEnumDestructable(), false)
        endmethod
        private static method showDests takes nothing returns nothing
            // show the destructables afterwards. CAREFUL: this means that destructables that were hidden initially will be shown!
            call ShowDestructable(GetEnumDestructable(), true)
        endmethod
        private static method calcGridPointZ takes real x, real y returns real
            local real array h
            local real r
            local real r1
            local real r2
            local real z
            // the function assumes that the x and y are on actual gridpoints!
            debug if I2R(R2I(x/128.))*128. != x or 2*(R2I(x/128.)/2) == R2I(x/128.) or I2R(R2I(y/128.))*128. != y or 2*(R2I(y/128.)/2) == R2I(y/128.) then
                call BJDebugMsg("Error in FlyHeightCalculator, non-grid x ("+R2S(x)+") or y ("+R2S(y)+") passed to calcGridPointZ!")
                return 0.
            endif
            // after this function has been run for a given gridpoint, its z is stored, so as to not run the length function anymore.
            if HaveSavedReal(.h,R2I(x),R2I(y)) then
                return LoadReal(.h,R2I(x),R2I(y))
            endif
            if .r == null then
                set .r = Rect(0.,0.,RECT_SIZE,RECT_SIZE)
            endif
            //
            // move the rect and hide the destructables, so that their walkability doesnt influence calculations
            call MoveRectTo(.r,x,y)
            call EnumDestructablesInRect(.r,null, function thistype.hideDests)
            //======================================================== 
            //=            CALCULATE THE TERRAIN HEIGHTS             =
            //========================================================
                // left bottom
                call MoveLocation(l, x-256., y-256.)
                set h[0] = GetLocationZ(l)
                // center bottom
                call MoveLocation(l, x-128., y-256.)
                set h[1] = GetLocationZ(l)
                call MoveLocation(l, x, y-256.)
                set r = GetLocationZ(l)
                if r > h[1] then
                    set h[1] = r
                endif
                call MoveLocation(l, x+128., y-256.)
                set r = GetLocationZ(l)
                if r > h[1] then
                    set h[1] = r
                endif
                // right bottom
                call MoveLocation(l, x+256, y-256.)
                set h[2] = GetLocationZ(l)
                // left center
                call MoveLocation(l, x-256., y-128.)
                set h[3] = GetLocationZ(l)
                call MoveLocation(l, x-256., y)
                set r = GetLocationZ(l)
                if r > h[3] then
                    set h[3] = r
                endif
                call MoveLocation(l, x-256., y+128.)
                set r = GetLocationZ(l)
                if r > h[3] then
                    set h[3] = r
                endif
                // center center
                call MoveLocation(l, x-128., y-128.)
                set h[4] = GetLocationZ(l)
                call MoveLocation(l, x, y-128.)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif
                call MoveLocation(l, x+128., y-128.)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif        
                
                call MoveLocation(l, x-128., y)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif   
                call MoveLocation(l, x, y)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif
                call MoveLocation(l, x+128., y)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif              
                
                call MoveLocation(l, x-128., y+128.)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif   
                call MoveLocation(l, x, y+128.)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif
                call MoveLocation(l, x+128., y+128.)
                set r = GetLocationZ(l)
                if r > h[4] then
                    set h[4] = r
                endif        
                // right center
                call MoveLocation(l, x+256., y-128.)
                set h[5] = GetLocationZ(l)
                call MoveLocation(l, x+256., y)
                set r = GetLocationZ(l)
                if r > h[5] then
                    set h[5] = r
                endif
                call MoveLocation(l, x+256., y+128.)
                set r = GetLocationZ(l)
                if r > h[5] then
                    set h[5] = r
                endif            
                // top left
                call MoveLocation(l, x-256., y+256.)
                set h[6] = GetLocationZ(l)
                // top center
                call MoveLocation(l, x-128., y+256.)
                set h[7] = GetLocationZ(l)
                call MoveLocation(l, x, y+256.)
                set r = GetLocationZ(l)
                if r > h[7] then
                    set h[7] = r
                endif
                call MoveLocation(l, x+128., y+256.)
                set r = GetLocationZ(l)
                if r > h[7] then
                    set h[7] = r
                endif
                // top right
                call MoveLocation(l, x+256., y+256.)
                set h[8] = GetLocationZ(l)
            //======================================================== 
            //=           CALCULATE THE ADDED Z BY SIDES             =
            //========================================================                
                set z = h[4]
                if h[1]>h[7] then
                    set r1 = h[1]
                else
                    set r1 = h[7]
                endif
                if h[3]>h[5] then
                    set r2 = h[3]
                else
                    set r2 = h[5]
                endif
                if r1 > r2 then
                    // lowest increase first (for some odd reason)
                    if h[3] > z then
                        set r1 = h[3]-z
                    else
                        set r1 = 0.
                    endif
                    if h[5] > z then
                        set r2 = h[5]-z
                    else
                        set r2 = 0.
                    endif
                    set z = z + (r1+r2)/2
                    
                    // then second
                    if h[1] > z then
                        set r1 = h[1]-z
                    else
                        set r1 = 0.
                    endif
                    if h[7] > z then
                        set r2 = h[7]-z
                    else
                        set r2 = 0.
                    endif
                    set z = z + (r1+r2)/2
                else
                    // lowest increase first (for some odd reason)
                    if h[1] > z then
                        set r1 = h[1]-z
                    else
                        set r1 = 0.
                    endif
                    if h[7] > z then
                        set r2 = h[7]-z
                    else
                        set r2 = 0.
                    endif
                    set z = z + (r1+r2)/2
                    // then second
                    if h[3] > z then
                        set r1 = h[3]-z
                    else
                        set r1 = 0.
                    endif
                    if h[5] > z then
                        set r2 = h[5]-z
                    else
                        set r2 = 0.
                    endif
                    set z = z + (r1+r2)/2
                endif
            //======================================================== 
            //=          CALCULATE THE ADDED Z BY CORNERS            =
            //========================================================        
                // left bottom
                if h[1] > h[3] then
                    if h[1] > h[4] then
                        set r = h[1]
                    else
                        set r = h[4]
                    endif
                else
                    if h[3] > h[4] then
                        set r = h[3]
                    else
                        set r = h[4]
                    endif
                endif
                if h[0] > r then
                    set z = z + ((h[0]-r)/4)
                endif
                // right bottom
                if h[1] > h[5] then
                    if h[1] > h[4] then
                        set r = h[1]
                    else
                        set r = h[4]
                    endif
                else
                    if h[5] > h[4] then
                        set r = h[5]
                    else
                        set r = h[4]
                    endif
                endif
                if h[2] > r then
                    set z = z + ((h[2]-r)/4)
                endif
                // left top
                if h[3] > h[7] then
                    if h[3] > h[4] then
                        set r = h[3]
                    else
                        set r = h[4]
                    endif
                else
                    if h[7] > h[4] then
                        set r = h[7]
                    else
                        set r = h[4]
                    endif
                endif
                if h[6] > r then
                    set z = z + ((h[6]-r)/4)
                endif
                // right top
                if h[5] > h[7] then
                    if h[5] > h[4] then
                        set r = h[5]
                    else
                        set r = h[4]
                    endif
                else
                    if h[7] > h[4] then
                        set r = h[7]
                    else
                        set r = h[4]
                    endif
                endif
                if h[8] > r then
                    set z = z + ((h[8]-r)/4)
                endif

            // show the surrounding destructables. Take into account that previously hidden destructables will also be shown.
            call EnumDestructablesInRect(.r,null, function thistype.showDests)
            //debug call CreateDestructableZ('B000', x, y, z, 0,1.,0)
            // save the real to the hashtable (later, only a read from the table is required to get the gridZ)            
            call SaveReal(.h,R2I(x),R2I(y),z)
            return z
        endmethod
        static method getGridZ takes real x, real y returns real
            local real minX = I2R(R2I((x+128.*RSignBJ(x))/256.))*256.-128.
            local real minY = I2R(R2I((y+128.*RSignBJ(y))/256.))*256.-128.
            local real maxX = minX+256.
            local real maxY = minY+256.
            local real dx = x-minX
            local real dy = y-minY
            local real dz1 = dx/256.*.calcGridPointZ(maxX,minY)+(1-dx/256.)*.calcGridPointZ(minX,minY) //if the calculation has been done for the point, it equals to a hastable call
            local real dz2 = dx/256.*.calcGridPointZ(maxX,maxY)+(1-dx/256.)*.calcGridPointZ(minX,maxY)
            return dy/256.*dz2+(1-dy/256.)*dz1
        endmethod
    endstruct
    function GetUnitZ takes unit u returns real
        if IsUnitType(u, UNIT_TYPE_FLYING) then
            return fh_grid256.getGridZ(GetUnitX(u),GetUnitY(u))+GetUnitFlyHeight(u)
        else
            call MoveLocation(l, GetUnitX(u), GetUnitY(u))
            return GetLocationZ(l)+GetUnitFlyHeight(u)
        endif
        return 0.
    endfunction
    function GetFlyHeightOffset takes real x, real y returns real
        return fh_grid256.getGridZ(x,y)
    endfunction
endlibrary
Attached Files
File type: w3xGetUnitZDemo.w3x (24.2 KB)