| 11-30-2015, 08:16 PM | #1 |
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. 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 |
