• 🏆 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!

User Interface v1.0.6

Advanced Example: Inventory & Equipment UI System v1.3.0

This library is a re-structure of DGUI which works in multiplayer.

JASS:
library UserInterface initializer Init requires optional UnitDex/*or any unit indexer*/, Camera
/***************************************************************
*
*   v1.0.6, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*
*   - Create UI elements based on dummy units. 
*
*   - Elements are overlay onto the current camera keeping you in gameplay. 
*
*   - The camera can be modified to any view including third person, however
*     it must be locked.
*
*   - Dynamic textures are applied to dummies through an ability based off
*     off War Clib, using destructables with their replaceable texture field 
*     set to the desired texture. (more: http://www.wc3c.net/showthread.php?p=1043980)
*
*   - Detect left and right clicks.
*
*   - Works in multiplayer (and single) without lag.
*   _________________________________________________________________________
*   1. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Copy the script to your map and save it (requires JassHelper *or* JNGP)
*       2. Copy the object editor data and paste the correct raw codes into the configuration.
*   _________________________________________________________________________
*   2. API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       struct UIButton
*
*           static method create takes real minx, real maxy, real W, real H, real z, integer texture returns thistype
*           static method updateAll takes nothing returns nothing
*           static method click takes unit u, boolean isLeft returns boolean
*           static method clickEx takes real x, real y, boolean isLeft returns boolean
*           static method clickPeriodicSelect takes player p, boolean isLeft returns boolean
*
*           method destroy takes nothing returns nothing
*           method update takes nothing returns nothing
*           method show takes boolean flag, Camera Cam returns nothing
*           method showPlayer takes player p, boolean flag, Camera cam returns nothing
*           method isClicked takes real x, real y returns boolean
*           method clickL takes nothing returns nothing
*           method clickR takes nothing returns nothing
*           method setPosition takes real minx, real maxy returns nothing
*           method setTexture takes integer texture returns nothing
*
*           -----------------------
*
*           integer customValue
*           integer animIndex
*           trigger triggerL
*           trigger triggerR
*           filterfunc onLeftClick
*           filterfunc onRightClick
*
*
*       struct UIPicture
*
*           static method create takes real minx, real maxy, real w, real h, real z, integer texture returns thistype
*           static method createEx takes real minx, real maxy, real z, real scale, integer unitId, real modelWidth, real modelHeight, integer texture returns thistype
*           static method updateAll takes nothing returns nothing
*           method destroy takes nothing returns nothing
*           method update takes nothing returns nothing
*           method show takes boolean show, Camera Cam returns nothing
*           method showPlayer takes player p, boolean flag, Camera cam returns nothing
*           method setPosition takes real minx, real maxy returns nothing
*           method setTexture takes integer texture returns nothing
*
*           -----------------------
*
*           integer customValue
*           integer animIndex
*
*       struct UIText
*   
*           static method create takes real minx, real maxy, real z returns thistype
*           static method updateAll takes nothing returns nothing
*
*           method destroy takes nothing returns nothing
*           method update takes nothing returns nothing
*           method setPosition takes real minx, real maxy returns nothing
*           method show takes boolean show, Camera Cam returns nothing
*
*           -----------------------
*
*           integer customValue
*
*       function GetTriggerButton takes nothing returns UIButton
*       function GetClickingPlayer takes nothing returns player
*       function GetCameraDeltaZ takes nothing returns real
*       function CreateWindow takes real x, real y, real size, real width, real height, race playerRace returns UIPicture
*
*       public function ApplySkin takes unit u, integer texture returns boolean
*
***************************************************************/
    // configuration
    private module InterfaceConfig
   
        // dummy unit with pitch angle animations
        static constant integer DUMMY_TYPE          = 'uidm'
       
        // model used in CreateWindow function
        static constant integer WINDOW_DUMMY        = 'uiwn'
       
        // destructable ID of the human border texture. the
        // rest of the races should have consecutive ids.
        static constant integer RACE_BORDERS_START  = 'D201'
       
        // run a timer to check for unit selections.
        // faster but more cpu intensive.
        static constant boolean LEFT_CLICK_TIMER    = true
            // really low timer intervals remove the flicker
            // effect, most of the time.
            static constant real LEFT_CLICK_TIMER_RATE  = 0.01
            // in multiplayer, IsUnitSelected is synchronous
            // so it doesn't instantly return false when you use
            // SelectUnit(u, false). So we must disable the button
            // for the clicking player for a brief period or else
            // the event will be spammed.
            static constant real LEFT_CLICK_DELAY   = 0.6
       
        // ability id's
        static constant integer DESTROYER_FORM_ID   = 'Aave'
        static constant integer LOCUST_ID           = 'Aloc'
       
        // ability used to change dummy unit textures
        static constant integer SKIN_CHANGE_ABILITY = 'Skin'
       
        // removes pathing and possibly other things from the unit.
        // similar to locust, but allows clicking.
        static constant integer GHOST_ABILITY       = 'Aeth'
        // dummy player
        static constant player PLAYER               = CAMERA_DUMMY_PLAYER // from Camera library
    endmodule
    // end configuration
    globals
        public destructable DummyTree = null
        private trigger Trig = CreateTrigger()
        private UIButton ClickedButton = 0
        private player ClickingPlayer = null
        private location DummyLoc = Location(0,0)
    endglobals
    function GetTriggerButton takes nothing returns UIButton
        return ClickedButton
    endfunction
   
    function GetClickingPlayer takes nothing returns player
        return ClickingPlayer
    endfunction
    public function Eval takes filterfunc func returns boolean
        call TriggerClearConditions(Trig)
        call TriggerAddCondition(Trig, func)
        return TriggerEvaluate(Trig)
    endfunction
    public function GetTerrainZ takes real x, real y returns real
        call MoveLocation(DummyLoc, x, y)
        return GetLocationZ(DummyLoc)
    endfunction
   
    // adjusts 
    public function FindModelAnimIndex takes real w, real h, real z returns integer
        local real W = w*SCREEN_WIDTH*z
        local real H = h*SCREEN_HEIGHT*z
        local real anim
        if (H<W) then
            set anim = 10*(W/H-1)
            return UIMath_R2I_N(anim)
        else
            set anim = 10*(H/W-1)
            if anim >= 0.5 then
                return 100+UIMath_R2I_N(anim)
            endif
        endif
        return 0
    endfunction
    public function FindModelSize takes real w, real h, real z returns real
        local real W = w*SCREEN_WIDTH*z
        local real H = h*SCREEN_HEIGHT*z
        if (H<W) then
            return 0.5*H
        endif
        return 0.5*W
    endfunction
    private function LocalReal takes player p, real forPlayer, real default returns real
        if (User.Local == p) then
            return forPlayer
        endif
        return default
    endfunction
   
    public function ApplySkin takes unit u, integer texture returns boolean
        local boolean out = false
       
        if (texture != 0) then
           
            call UnitAddAbility(u, Interface.SKIN_CHANGE_ABILITY)
            set DummyTree = CreateDestructable(texture,GetUnitX(u),GetUnitY(u),0,0,1)
            set out = IssueTargetOrder(u, "grabtree", DummyTree)
            call RemoveDestructable(DummyTree)
            set DummyTree = null
            call UnitRemoveAbility(u, Interface.SKIN_CHANGE_ABILITY)
        endif
       
        return out
    endfunction
    struct UIButton
        integer customValue
        integer animIndex
        trigger triggerL
        trigger triggerR
        filterfunc onLeftClick
        filterfunc onRightClick
        boolean disabled
        unit selectUnit
        static boolean useDelay = false
        readonly static thistype array AllShow
        readonly static integer CountShow = 0
        readonly integer index
        readonly Camera camera
       
        readonly real scale
        readonly real centerx
        readonly real centery
        readonly real minx
        readonly real maxx
        readonly real miny
        readonly real maxy
        readonly real width
        readonly real height
        readonly real z
        readonly unit picture
        readonly effect model
       
        readonly boolean displayed
        readonly integer texture 
       
        private integer unitId
        private static thistype array Unit2Button
       
        static method create takes real minx, real maxy, real W, real H, real z, integer texture returns thistype
            local thistype this = thistype.allocate()
            set .texture        = texture
            set .customValue    = 0
            set .camera         = 0
            set .width          = W
            set .height         = H
            set .minx           = minx
            set .maxx           = minx+W
            set .maxy           = maxy
            set .miny           = maxy-H
            set .z              = 100.2+z
            set .centerx        = minx+W/2.0
            set .centery        = maxy-H/2.0
            set .displayed      = false
            set .scale          = FindModelSize(W, H, .z)
            set .triggerL       = null
            set .triggerR       = null
            set .onLeftClick    = null
            set .onRightClick   = null
            set .selectUnit     = null
            set .picture        = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
           
            // apply texture
            call ApplySkin(.picture, texture)
           
            set .animIndex = FindModelAnimIndex(W, H, .z)
            call SetUnitAnimationByIndex(.picture, .animIndex)
            call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitAddAbility(.picture, Interface.SKIN_CHANGE_ABILITY)
            call UnitAddAbility(.picture, Interface.GHOST_ABILITY)
           
            set this.unitId = GetUnitUserData(.picture)
            set Unit2Button[this.unitId] = this
           
            call SetUnitScale(.picture, 0, 0, 0)
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call RemoveUnit(.picture)
           
            if .displayed then
                set .AllShow[.index] = .AllShow[.CountShow]
                set .AllShow[.index].index = .index
                set .CountShow = .CountShow - 1
            endif
        endmethod
        method update takes nothing returns nothing
            local VECTOR3 Pos
           
            if (.displayed) then
                set Pos = .camera.win2World(.centerx, .centery, .z)
                call SetUnitX(.picture, Pos.x)
                call SetUnitY(.picture, Pos.y)
                call SetUnitFlyHeight(.picture, Pos.z-GetTerrainZ(Pos.x, Pos.y), 0)
                call Pos.destroy()
            endif
        endmethod
       
        method show takes boolean flag, Camera Cam returns nothing
            if Cam != -1 then
                set .camera = Cam
            endif
           
            if flag != .displayed then
                if flag then
                    set .AllShow[.CountShow] = this
                    set .index = .CountShow
                    set .CountShow = .CountShow + 1
                    call .update()
                    call SetUnitAnimationByIndex(.picture, .animIndex)
                   
                    call SetUnitScale(.picture, .scale, 0, 0)
                else
                    set .CountShow = .CountShow - 1
                    set .AllShow[.index] = .AllShow[.CountShow]
                    set .AllShow[.index].index = .index
                   
                    call SetUnitX(.picture, 0)
                    call SetUnitY(.picture, 0)
                   
                    call SetUnitScale(.picture, 0, 0, 0)
                endif
            endif
           
            set .displayed = flag
        endmethod
       
        method showPlayer takes player p, boolean flag, Camera cam returns nothing
            call .show(flag, cam)
           
            if (flag) then
                call SetUnitScale(.picture, LocalReal(p, .scale, 0), 0, 0)
            endif
        endmethod
        method isClicked takes real x, real y returns boolean
            return .minx < x and x < .maxx and .miny < y  and y < .maxy
        endmethod
       
        method clickL takes nothing returns nothing
            set ClickedButton = this
            if (.triggerL != null and TriggerEvaluate(.triggerL)) then
                call TriggerExecute(.triggerL)
            endif
            if (.onLeftClick != null) then
                 call Eval(.onLeftClick)
            endif
        endmethod
       
        method clickR takes nothing returns nothing
            set ClickedButton = this
            if (.triggerR != null and TriggerEvaluate(.triggerR)) then
                call TriggerExecute(.triggerR)
            endif
           
            if (.onRightClick != null) then
                 call Eval(.onRightClick)
            endif
        endmethod
       
        method setPosition takes real minx, real maxy returns nothing
            set .minx = minx
            set .maxx = minx+.width
            set .maxy = maxy
            set .miny = maxy-.height
            set .centerx = minx+.width/2.0
            set .centery = maxy-.height/2.0
           
            if .displayed then
                call .update()
            endif
        endmethod
       
        method setTexture takes integer texture returns nothing
            call SetUnitX(.picture, 0)
            call SetUnitY(.picture, 0)
            call ApplySkin(.picture, texture)
           
            if .displayed then
                call .update()
            endif
           
            set .texture = texture
        endmethod
       
        static method updateAll takes nothing returns nothing
            local integer i = .CountShow
            loop
                exitwhen i < 0
                call .AllShow[i].update()
                set i = i - 1
            endloop
        endmethod
       
        static method click takes unit u, boolean isLeft returns boolean
            local thistype b
           
            if GetUnitTypeId(u) == Interface.DUMMY_TYPE then
                set b = Unit2Button[GetUnitUserData(u)]
                if b > 0 and b.displayed then
                    call SelectUnit(u, false)
                    if isLeft then
                        call b.clickL()
                    else
                        call b.clickR()
                    endif
                   
                    return true
                endif
            endif
           
            return false
        endmethod
       
        static method clickEx takes real x, real y, boolean isLeft returns boolean
            local integer i = .CountShow
           
            loop
                exitwhen i < 0
                if .AllShow[i].isClicked(x, y) then
                    if isLeft then
                        call .AllShow[i].clickL()
                    else
                        call .AllShow[i].clickR()
                    endif
                   
                    return true
                endif
               
                set i = i - 1
            endloop
           
            return false
        endmethod
       
        private static method enableButtonCallback takes nothing returns nothing
            local timer t = GetExpiredTimer()
            static if (LIBRARY_TimerUtils) then
                local thistype btn = thistype(GetTimerData(t))
            else
                local thistype btn = thistype(R2I(TimerGetRemaining(t) + 0.5))
            endif
            set btn.disabled = false
            set t = null
        endmethod
        static method clickPeriodicSelect takes player p, boolean isLeft returns boolean
            local integer i = .CountShow
            local timer t
           
            loop
                exitwhen i < 0
                if (IsUnitSelected(.AllShow[i].picture, p)) then
                   
                    if (not .AllShow[i].disabled) then
                        if isLeft then
                            call .AllShow[i].clickL()
                        else
                            call .AllShow[i].clickR()
                        endif
                       
                        if (thistype.useDelay) then // static if doesn't work..
                            set .AllShow[i].disabled = true
                            static if (LIBRARY_TimerUtils) then
                                call TimerStart(NewTimerEx(.AllShow[i]), Interface.LEFT_CLICK_DELAY, false, function thistype.enableButtonCallback)
                            else
                                set t = CreateTimer()
                                call TimerStart(t, .AllShow[i], false, null)
                                call PauseTimer(t)
                                call TimerStart(t, Interface.LEFT_CLICK_DELAY, false, function thistype.enableButtonCallback)
                                set t = null
                            endif
                        endif
                       
                        return true
                    endif
                    call SelectUnit(.AllShow[i].picture, false)
                    if (.AllShow[i].selectUnit != null and User.Local == p) then
                        call SelectUnit(.AllShow[i].selectUnit, true)
                    endif
                endif
               
                set i = i - 1
            endloop
           
            return false
        endmethod
    endstruct
    struct UIPicture
        integer customValue
        integer animIndex
       
        readonly static thistype array AllShow
        readonly static integer CountShow = 0
       
        readonly integer index
        readonly Camera camera
       
        readonly real scale
        readonly real centerx
        readonly real centery
        readonly real width
        readonly real height
        readonly real z
        readonly unit picture
        readonly boolean displayed
        readonly integer texture 
        static method create takes real minx, real maxy, real w, real h, real z, integer texture returns thistype
            local thistype this = thistype.allocate()
            set .texture        = texture
            set .customValue    = 0
            set .camera         = 0
            set .width          = w
            set .height         = h
            set .centerx        = minx+w/2.0
            set .centery        = maxy-h/2.0
            set .z              = 100.2+z
            set .displayed      = false
            set .picture        = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
            set .animIndex      = FindModelAnimIndex(w, h, .z)
            set .scale          = FindModelSize(w, h, .z)
           
            call ApplySkin(.picture, texture)
           
            call SetUnitAnimationByIndex(.picture, .animIndex)
           
            call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitAddAbility(.picture, Interface.LOCUST_ID)
            call UnitRemoveAbility(.picture, Interface.LOCUST_ID)
           
            // hide unit
            call SetUnitScale(.picture, 0, 0, 0)
           
            return this
        endmethod
       
        static method createEx takes real minx, real maxy, real z, real scale, integer unitId, real modelWidth, real modelHeight, integer texture returns thistype
            local thistype this = thistype.allocate()
            set .customValue    = 0
            set .camera         = 0
            set .z              = 100.2 + z
            set .width          = (modelWidth*scale) / (SCREEN_WIDTH*.z)
            set .height         = (modelHeight*scale) / (SCREEN_HEIGHT*.z)
            set .scale          = scale
            set .centerx        = minx+.width/2.0
            set .centery        = maxy-.height/2.0
            set .displayed      = false
            set .picture        = CreateUnit(Interface.PLAYER, unitId, 0, 0, 270)
           
            call ApplySkin(.picture, texture)
           
            call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
            call UnitAddAbility(.picture, Interface.LOCUST_ID)
            call UnitRemoveAbility(.picture, Interface.LOCUST_ID)
           
            // hide unit
            call SetUnitScale(.picture, 0, 0, 0)
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call RemoveUnit(.picture)
           
            if .displayed then
                set .AllShow[.index] = .AllShow[.CountShow]
                set .AllShow[.index].index = .index
                set .CountShow = .CountShow - 1
            endif
        endmethod
        method update takes nothing returns nothing
            local VECTOR3 Pos
            if (this.displayed) then
                set Pos = this.camera.win2World(.centerx, .centery, .z)
                call SetUnitX(.picture, Pos.x)
                call SetUnitY(.picture, Pos.y)
                call SetUnitFlyHeight(.picture, Pos.z - GetTerrainZ(Pos.x, Pos.y), 0)
                call Pos.destroy()
            endif
        endmethod
       
        method show takes boolean show, Camera Cam returns nothing
            if Cam != -1 then
                set this.camera = Cam
            endif
            if show != .displayed then
                if show then
                    set .AllShow[.CountShow] = this
                    set .index = .CountShow
                    set .CountShow = .CountShow + 1
                    call .update()
                    call SetUnitAnimationByIndex(.picture, .animIndex)
                    call SetUnitScale(.picture, .scale, 0, 0)
                else
                    set .CountShow = .CountShow - 1
                    set .AllShow[.index] = .AllShow[.CountShow]
                    set .AllShow[.index].index = .index
                    call SetUnitX(.picture, 0)
                    call SetUnitY(.picture, 0)
                    call SetUnitScale(.picture, 0, 0, 0)
                endif
            endif
           
            set .displayed = show
        endmethod
       
        method showPlayer takes player p, boolean flag, Camera cam returns nothing
            call .show(flag, cam)
           
            if (flag) then
                call SetUnitScale(.picture, LocalReal(p, .scale, 0), 0, 0)
            endif
        endmethod
       
        method setPosition takes real minx, real maxy returns nothing
            set .centerx = minx+.width/2.0
            set .centery = maxy-.height/2.0
            if .displayed then
                call .update()
            endif
        endmethod
       
        method setTexture takes integer texture returns nothing
            call SetUnitX(.picture, 0)
            call SetUnitY(.picture, 0)
            call ApplySkin(.picture, texture)
           
            if .displayed then
                call .update()
            endif
           
            set .texture = texture
        endmethod
       
        static method updateAll takes nothing returns nothing
            local integer i = .CountShow
            loop
                exitwhen i < 0
                call .AllShow[i].update()
                set i = i - 1
            endloop
        endmethod
    endstruct
    struct UIText
        integer customValue
       
        readonly unit dummy
        readonly static thistype array AllShow
        readonly static integer CountShow = 0
        readonly integer index
        readonly Camera camera
        readonly real minx
        readonly real maxy
        readonly real z
        readonly texttag text
        readonly boolean displayed
       
        static method create takes real minx, real maxy, real z returns thistype
            local thistype this = thistype.allocate()
           
            set .customValue    = 0
            set .camera         = 0
            set .minx           = minx
            set .maxy           = maxy
            set .z              = 100 + z
            set .displayed      = false
            set .text           = CreateTextTag()
            set .dummy          = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
            call SetUnitScale(.dummy, 0, 0, 0)
            call UnitAddAbility(.dummy, Interface.LOCUST_ID)
            call UnitRemoveAbility(.dummy, Interface.LOCUST_ID)
           
            call SetTextTagVisibility(.text, false)
            call SetTextTagPosUnit(.text, .dummy, 0)
           
            return this
        endmethod
       
        static method createEx takes player p, real minx, real maxy, real z returns thistype
            local thistype this = thistype.allocate()
           
            set .customValue    = 0
            set .camera         = 0
            set .minx           = minx
            set .maxy           = maxy
            set .z              = 100 + z
            set .displayed      = false
            set .dummy          = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
            if (User.Local == p) then
                set .text = CreateTextTag()
            endif
           
            call SetUnitScale(.dummy, 0, 0, 0)
            call UnitAddAbility(.dummy, Interface.LOCUST_ID)
            call UnitRemoveAbility(.dummy, Interface.LOCUST_ID)
           
            call SetTextTagVisibility(.text, false)
            call SetTextTagPosUnit(.text, .dummy, 0)
           
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call DestroyTextTag(.text)
           
            if .displayed then
                set .AllShow[.index] = .AllShow[.CountShow]
                set .AllShow[.index].index = .index
                set .CountShow = .CountShow - 1
            endif
        endmethod
       
        method update takes nothing returns nothing
            local VECTOR3 Pos = .camera.win2World(.minx, .maxy, .z)
            call SetUnitX(.dummy, Pos.x)
            call SetUnitY(.dummy, Pos.y)
            call SetTextTagPosUnit(.text, .dummy, (Pos.z-GetTerrainZ(Pos.x,Pos.y))-14.8)
            call Pos.destroy()
        endmethod
       
        method setPosition takes real minx, real maxy returns nothing
            set .minx = minx
            set .maxy = maxy
           
            if .displayed then
                call .update()
            endif
        endmethod
       
        method show takes boolean show, Camera Cam returns nothing
            if Cam != -1 then
                set .camera = Cam
            endif
           
            call SetTextTagVisibility(.text, show)
           
            if show != .displayed then
                if show then
                    set .AllShow[.CountShow] = this
                    set .index = .CountShow
                    set .CountShow = .CountShow + 1
                    call .update()
                else
                    set .CountShow = .CountShow - 1
                    set .AllShow[.index] = .AllShow[.CountShow]
                    set .AllShow[.index].index = .index
                endif
            endif
           
            set .displayed = show
        endmethod
       
        static method updateAll takes nothing returns nothing
            local integer i = .CountShow
            loop
                exitwhen i < 0
                call .AllShow[i].update()
                set i = i - 1
            endloop
        endmethod
    endstruct
    struct Interface extends array
       
        implement InterfaceConfig
       
       static method getRaceBorders takes race r returns integer
            return RACE_BORDERS_START + (GetHandleId(r)-1)
        endmethod
       
        static method updateAll takes boolean but, boolean pic, boolean tex returns nothing
            if but then
                call UIButton.updateAll()
            endif
            if pic then
                call UIPicture.updateAll()
            endif
            if tex then
                call UIText.updateAll()
            endif
        endmethod
    endstruct
    function CreateWindow takes real x, real y, real size, real width, real height, race playerRace returns UIPicture
        return UIPicture.createEx(x, y, /*z=*/2, size, Interface.WINDOW_DUMMY, width, height, Interface.getRaceBorders(playerRace))
    endfunction
   
    private function InterfaceClickR takes nothing returns nothing
        local unit clicker = GetTriggerUnit()
        local unit btnUnit = GetOrderTargetUnit()
       
        set ClickingPlayer = GetOwningPlayer(clicker)
       
        if (UIButton.click(btnUnit, false)) then
            call PauseUnit(clicker, true)
            call IssueImmediateOrderById(clicker, 851973)
            call PauseUnit(clicker, false)
        endif
       
        set clicker = null
    endfunction
   
    private function InterfaceClickL takes nothing returns nothing
        set ClickingPlayer = GetTriggerPlayer()
        call UIButton.click(GetTriggerUnit(), true)
    endfunction
    private function InterfaceClickL_Timer takes nothing returns nothing
        local integer i = 0
        local integer c = UIButton.CountShow
        local User user = User.first
       
        loop
            exitwhen user == User.NULL
 
            set ClickingPlayer = user.handle
           
            call UIButton.clickPeriodicSelect(user.handle, true)
            set user = user.next
        endloop
    endfunction
   
    private function Init takes nothing returns nothing
         local trigger t = CreateTrigger()
         local trigger t2 = CreateTrigger()
         local integer i = 0
         local User user = User.first
       
         loop
            exitwhen user == User.NULL
           
            call TriggerRegisterPlayerUnitEvent(t, user.handle, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, null)
           
            static if (not Interface.LEFT_CLICK_TIMER) then
                call TriggerRegisterPlayerUnitEvent(t2, user.handle, EVENT_PLAYER_UNIT_SELECTED, null)
            endif
           
            set user = user.next
        endloop
        call TriggerAddAction(t, function InterfaceClickR)
       
        static if (Interface.LEFT_CLICK_TIMER) then
            call TimerStart(CreateTimer(), Interface.LEFT_CLICK_TIMER_RATE, true, function InterfaceClickL_Timer)
        else
            call TriggerAddAction(t2, function InterfaceClickL)
        endif
        set UIButton.useDelay = Interface.LEFT_CLICK_TIMER
    endfunction
   
endlibrary
Contents

User Interface v1.0.6 (Map)

Reviews
KILLCIDE
A system like this is going to require some usage from other users to improve, so I will approve for now to get users to see this easier. Unfortunately, the logic behind all of this is sort of beyond me, but I do know that this will be an extremely...

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I still can't compile the map with message "Functions passed to Filter or Condition must return a boolean". I wonder what jasshelper are you using that you can compile it just fine?

Btw, I'm not sure if we need both UIButton and UIPicture since basically UIPicture is unclickable UIButton. So merging them by adding "allowClick" as boolean parameter for UIButton creator method is good enough, no?

Also,
JASS:
            // in multiplayer, IsUnitSelected is synchronous
            // so it doesn't instantly return false when you use
            // SelectUnit(u, false). So we must disable the button
            // for the clicking player for a brief period or else
            // the event will be spammed.
            static constant real LEFT_CLICK_DELAY       = 0.6
Instead of using predicted delay, how about waiting for the button unit deselected event before re-enabling it? The delay would be as accurate as it can get and no need to be pre-defined. Tested myself and it doesn't work. The deselect event won't fire somehow.

And lastly, what does this do with your dummy model if I may ask?
JASS:
call SetUnitAnimationByIndex(.picture, .animIndex)
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Do you still need to change the pitch angle? The model is billboarded already, changing pitch doesn't work.

What? Each animation index corresponds to a different pitch.

Have you tried to use your setTexture method? I suppose it won't work now.

If you look at the code you can see I un-pause the unit before changing textures.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
What? Each animation index corresponds to a different pitch.
I know, but your dummy (platform) models are already billboarded: it will face toward the screen no matter what. So you don't need to adjust pitch angle at all. It's even okay to use 1KB dummy unit as long as it's billboarded.

If you look at the code you can see I un-pause the unit before changing textures.
I know, but it seems the dummy don't get unpaused instantly, as it won't cast the war club spell afterward.
 
Updated, v1.0.4.
  • Fixed a bug in the demo where your hero would not get reselected.
  • 851973 (stun) is now issued instead of "stop" to units who are issued a target order on dummies
  • Reverted pausing dummy units.
I know, but your dummy (platform) models are already billboarded: it will face toward the screen no matter what. So you don't need to adjust pitch angle at all. It's even okay to use 1KB dummy unit as long as it's billboarded.

For the character model it changes the pitch, for the UI\dummy.mdl model it changes the units scale (width/height).

I know, but it seems the dummy don't get unpaused instantly, as it won't cast the war club spell afterward.

For some reason I thought I had it working in my personal map, but it was commented out there too. I probably should have tested it further.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
For the character model it changes the pitch
One last thing, I'm not sure which function/method you are using for displaying hero unit on screen (haven't checked your inventory code), but I suppose it will break if you change the camera rotation, as you don't adjust the unit's yaw angle based on current camera state. Again, except if you use a fully billboarded dummy and attach hero's model on it, or if somehow you managed to billboard unit's yaw angle only (which is impossible at the moment).

EDIT:
Updating unit's yaw angle instantly is another problem, as you can't set unit's facing angle instantly. Thus you have to use the bone locking trick. But sadly, using that trick will break the pitch angle calculation. If you are trying to adjust pitch angle by using bone locking, it will break the yaw angle adjustment at some occasions. Everything just goes against it.

So the only solution is to use fully billboarded dummy model. To display a hero, just attach the model on an invisible billboarded dummy. But then it will take the model path, not the unit raw code.
 
Last edited:
Updated, v1.0.5.
  • Added support for water-level terrains. Now the UI won't be off-position when the camera is over water.
  • TextTags are now positioned relative to a dummy unit. This allows texttags to be shown over water as well.
  • Implemented basic example of double-click detection.
  • Reorganized imports.
  • Updated PlayerUtils to 1.2.8.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
A system like this is going to require some usage from other users to improve, so I will approve for now to get users to see this easier. Unfortunately, the logic behind all of this is sort of beyond me, but I do know that this will be an extremely powerful library for the future. One can already see that from the Inventory & Equipment System you made as an extension to this library.

Needs Fixed

  • Nothing

Suggestions

  • Nothing

Status

Approved
 

eejin

Tool Moderator
Level 12
Joined
Mar 6, 2017
Messages
221
Say, is the camera being attached to an unit a hard requirement for this system? I would like to have an extra inventory slot/button dedicated to potions, but it has to be on a free camera. If the attached camera is required, then do you have any recommendations on how to achieve my goal?
 
Say, is the camera being attached to an unit a hard requirement for this system? I would like to have an extra inventory slot/button dedicated to potions, but it has to be on a free camera. If the attached camera is required, then do you have any recommendations on how to achieve my goal?

Free camera is not possible without flickering / jumping UI. The camera must be locked something, or moving slowly.
 
Level 6
Joined
Oct 25, 2010
Messages
203
can this be used in the same way as a simple dialogue button?

would be nice to have icons on a dialogue button, but I dont get how to set this stuff up :(
 
Top