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

Full Screen Window

Status
Not open for further replies.
Level 24
Joined
Aug 1, 2013
Messages
4,657
Hi all.

Something like 5-6 months ago, I created a small system to create a generic full screen window.
Ofcourse with my JASS coding skills back then, it wasnt really good (even though it worked) so I decided to recreate it.
JASS:
globals
    integer         udg_FSW_Index                       = -1
    
    integer array   udg_FSW_Array_LocationX
    integer array   udg_FSW_Array_LocationY
    integer array   udg_FSW_Array_LocationCenterX
    integer array   udg_FSW_Array_LocationCenterY
    integer array   udg_FSW_Array_SizeX
    integer array   udg_FSW_Array_SizeY
    unit array      udg_FSW_Array_DummyUnit
    integer array   udg_FSW_Array_TerrainType
    real array      udg_FSW_Array_CameraDistance
    
    integer         udg_FSW_ObjectType_Destructable     = 1
    integer         udg_FSW_ObjectType_Texttag          = 2
    integer         udg_FSW_ObjectType_Unit             = 3
    
    trigger array   udg_FSW_Trigger_WhileOpen
    integer         udg_FSW_Black_Terrain               = 'Oaby'
    trigger         udg_FSW_Trigger_TrackableClick      = CreateTrigger()
    trigger         udg_FSW_Trigger_TrackableHover      = CreateTrigger()
    string          udg_FSW_Trackable_Model             = "AutoImport\\Models\\Custom Window\\Trackable.mdx"
    hashtable       udg_FSW_Hashtable                   = InitHashtable()
endglobals

library fswSystem
    function FSW_System_SaveStandardDestructable takes integer whichFSW, destructable d returns nothing
        local integer i = 20
        
        loop
            exitwhen LoadInteger(udg_FSW_Hashtable, whichFSW, i) == 0
            set i = i + 2
        endloop
        
        call SaveInteger(udg_FSW_Hashtable, whichFSW, i, udg_FSW_ObjectType_Destructable)
        call SaveDestructableHandle(udg_FSW_Hashtable, whichFSW, i+1, d)
    endfunction

    function FSW_System_SaveStandardTexttag takes integer whichFSW, texttag t returns nothing
        local integer i = 20
        
        loop
            exitwhen LoadInteger(udg_FSW_Hashtable, whichFSW, i) == 0
            set i = i + 2
        endloop
        
        call SaveInteger(udg_FSW_Hashtable, whichFSW, i, udg_FSW_ObjectType_Texttag)
        call SaveTextTagHandle(udg_FSW_Hashtable, whichFSW, i+1, t)
    endfunction

    function FSW_System_SaveStandardUnit takes integer whichFSW, unit u returns nothing
        local integer i = 20
        
        loop
            exitwhen LoadInteger(udg_FSW_Hashtable, whichFSW, i) == 0
            set i = i + 2
        endloop
        
        call SaveInteger(udg_FSW_Hashtable, whichFSW, i, udg_FSW_ObjectType_Unit)
        call SaveUnitHandle(udg_FSW_Hashtable, whichFSW, i+1, u)
    endfunction

    function FSW_System_ShowStandardObjects takes integer whichFSW, boolean show returns nothing
        local integer i = 20
        local integer objectType
        
        loop
            set objectType = LoadInteger(udg_FSW_Hashtable, whichFSW, i)
            exitwhen objectType == 0
            if objectType == udg_FSW_ObjectType_Destructable then
                call ShowDestructable(LoadDestructableHandle(udg_FSW_Hashtable, whichFSW, i+1), show)
            elseif objectType == udg_FSW_ObjectType_Texttag then
                call SetTextTagVisibility(LoadTextTagHandle(udg_FSW_Hashtable, whichFSW, i+1), show)
            elseif objectType == udg_FSW_ObjectType_Unit then
                call ShowUnit(LoadUnitHandle(udg_FSW_Hashtable, whichFSW, i+1), show)
            endif
            set i = i + 2
        endloop
    endfunction

    function FSW_System_SaveTempDestructable takes integer whichFSW, integer pId, destructable d returns nothing
        local integer i = 2000
        
        loop
            exitwhen i > 3000
            if LoadInteger(udg_FSW_Hashtable, whichFSW, i) == 0 then
                call SaveInteger(udg_FSW_Hashtable, whichFSW, i, udg_FSW_ObjectType_Destructable)
                call SaveDestructableHandle(udg_FSW_Hashtable, whichFSW, i+1, d)
                call SaveInteger(udg_FSW_Hashtable, whichFSW, i+2, pId)
                return
            endif
            set i = i + 3
        endloop
    endfunction

    function FSW_System_SaveTempTexttag takes integer whichFSW, integer pId, texttag t returns nothing
        local integer i = 2000
        
        loop
            exitwhen i > 3000
            if LoadInteger(udg_FSW_Hashtable, whichFSW, i) == 0 then
                call SaveInteger(udg_FSW_Hashtable, whichFSW, i, udg_FSW_ObjectType_Texttag)
                call SaveTextTagHandle(udg_FSW_Hashtable, whichFSW, i+1, t)
                call SaveInteger(udg_FSW_Hashtable, whichFSW, i+2, pId)
                return
            endif
            set i = i + 3
        endloop
    endfunction

    function FSW_Remove_Temp_Objects takes integer whichFSW, integer pId returns nothing
        local integer i = 2000
        local integer objectType
        
        loop
            exitwhen i > 3000
            if LoadInteger(udg_FSW_Hashtable, whichFSW, i+2) == pId then
                set objectType = LoadInteger(udg_FSW_Hashtable, whichFSW, i)
                if objectType == udg_FSW_ObjectType_Destructable then
                    call RemoveDestructable(LoadDestructableHandle(udg_FSW_Hashtable, whichFSW, i+1))
                    call SaveInteger(udg_FSW_Hashtable, whichFSW, i, 0)
                elseif objectType == udg_FSW_ObjectType_Texttag then
                    call DestroyTextTag(LoadTextTagHandle(udg_FSW_Hashtable, whichFSW, i+1))
                    call SaveInteger(udg_FSW_Hashtable, whichFSW, i, 0)
                elseif objectType == udg_FSW_ObjectType_Unit then
                    call RemoveUnit(LoadUnitHandle(udg_FSW_Hashtable, whichFSW, i+1))
                    call SaveInteger(udg_FSW_Hashtable, whichFSW, i, 0)
                endif
            endif
            set i = i + 3
        endloop
    endfunction



    function FSW_System_Create_FSW takes integer x, integer y, integer sizeX, integer sizeY, real cameraDistance returns integer
        set udg_FSW_Index = udg_FSW_Index + 1
        set udg_FSW_Array_LocationX[udg_FSW_Index] = x
        set udg_FSW_Array_LocationY[udg_FSW_Index] = y
        set udg_FSW_Array_LocationCenterX[udg_FSW_Index] = x*64 + sizeX*32
        set udg_FSW_Array_LocationCenterY[udg_FSW_Index] = y*64 - sizeY*32
        set udg_FSW_Array_SizeX[udg_FSW_Index] = sizeX
        set udg_FSW_Array_SizeY[udg_FSW_Index] = sizeY
        set udg_FSW_Array_DummyUnit[udg_FSW_Index] = CreateUnit(Player(12), udg_Dummy_Unit_Type, udg_FSW_Array_LocationCenterX[udg_FSW_Index], udg_FSW_Array_LocationCenterY[udg_FSW_Index], 0)
        set udg_FSW_Array_TerrainType[udg_FSW_Index] = GetTerrainType(udg_FSW_Array_LocationCenterX[udg_FSW_Index], udg_FSW_Array_LocationCenterY[udg_FSW_Index])
        set udg_FSW_Array_CameraDistance[udg_FSW_Index] = cameraDistance
        
        return udg_FSW_Index
    endfunction

    function FSW_System_Create_Trackable takes integer whichFSW, integer x, integer y returns trackable
        local integer fswX = udg_FSW_Array_LocationX[whichFSW]
        local integer fswY = udg_FSW_Array_LocationY[whichFSW]
        local trackable t
        local integer id
        local integer i = 0
        local player p
        local string s
        
        loop
            exitwhen i > 12
            set p = Player(i)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then
                if GetLocalPlayer() == p then
                    set s = udg_FSW_Trackable_Model
                else
                    set s = ""
                endif
                set t = CreateTrackable(s, (fswX+x) * 64, (fswY-y) * 64, 0)
                set id = GetHandleId(t)
                
                //Register the events for the trackable.
                call TriggerRegisterTrackableHitEvent(udg_FSW_Trigger_TrackableClick, t)
                call TriggerRegisterTrackableTrackEvent(udg_FSW_Trigger_TrackableHover, t)
                
                //Save the data to the hashtable.
                call SaveInteger(udg_FSW_Hashtable, id, 0, i)
                call SaveInteger(udg_FSW_Hashtable, id, 1, fswX + x)
                call SaveInteger(udg_FSW_Hashtable, id, 2, fswY + y)
            endif
            set i = i + 1
        endloop
        return t
    endfunction

    function FSW_System_Create_Destructable takes integer whichFSW, integer destId, integer x, integer y returns destructable
        local real fswX = udg_FSW_Array_LocationX[whichFSW]
        local real fswY = udg_FSW_Array_LocationY[whichFSW]
        local destructable d = CreateDestructable(destId, (fswX+x) * 64, (fswY-y) * 64, 0, 0.7, 0)
        //local destructable d = CreateDeadDestructableZ(destId, (fswX+x) * 64, (fswY-y) * 64, 250, 0, 0.7, 0)
        
        call FSW_System_SaveStandardDestructable(whichFSW, d)
        
        call ShowDestructable(d, false)
        
        return d
    endfunction

    function FSW_System_Create_Texttag takes integer whichFSW, string text, real size, real x, real y, integer RGBA_R, integer RGBA_G, integer RGBA_B, integer RGBA_A returns texttag
        local real fswX = udg_FSW_Array_LocationX[whichFSW]
        local real fswY = udg_FSW_Array_LocationY[whichFSW]
        local texttag t = CreateTextTag()
        call SetTextTagText(t, text, size * 0.0023)
        call SetTextTagPos(t, fswX*64 + x, fswY*64 - y, 0)
        call SetTextTagColor(t, RGBA_R, RGBA_G, RGBA_B, RGBA_A)
        call SetTextTagPermanent(t, true)
        
        call FSW_System_SaveStandardTexttag(whichFSW, t)
        
        call SetTextTagVisibility(t, false)
        
        return t
    endfunction

    function FSW_System_Create_Unit takes integer whichFSW, integer unitType, real x, real y, player whichPlayer, real facing returns unit
        local real fswX = udg_FSW_Array_LocationX[whichFSW]
        local real fswY = udg_FSW_Array_LocationY[whichFSW]
        local unit u = CreateUnit(whichPlayer, unitType, fswX*64 + x, fswY*64 - y, facing)
        
        call FSW_System_SaveStandardUnit(whichFSW, u)
        
        call ShowUnit(u, false)
        
        return u
    endfunction

    function FSW_System_Create_Temp_Destructable takes integer whichFSW, player whichPlayer, integer destId, integer x, integer y returns destructable
        local real fswX = udg_FSW_Array_LocationX[whichFSW]
        local real fswY = udg_FSW_Array_LocationY[whichFSW]
        local destructable d = CreateDestructable(destId, (fswX+x) * 64, (fswY-y) * 64, 0, 0.7, 0)
        local timer Timer
        //local destructable d = CreateDeadDestructableZ(destId, (fswX+x) * 64, (fswY-y) * 64, 5, 0, 0.7, 0)
        
        call FSW_System_SaveTempDestructable(whichFSW, GetPlayerId(whichPlayer), d)
        
        if GetLocalPlayer() == whichPlayer then
            call ShowDestructable(d, true)
        else
            call ShowDestructable(d, false)
        endif
        
        return d
    endfunction

    function FSW_System_Create_Temp_Texttag takes integer whichFSW, player whichPlayer, string text, real size, real x, real y, integer RGBA_R, integer RGBA_G, integer RGBA_B, integer RGBA_A returns texttag
        local real fswX = udg_FSW_Array_LocationX[whichFSW]
        local real fswY = udg_FSW_Array_LocationY[whichFSW]
        local texttag t = CreateTextTag()
        call SetTextTagText(t, text, size * 0.0023)
        call SetTextTagPos(t, fswX*64 + x, fswY*64 - y, 0)
        call SetTextTagColor(t, RGBA_R, RGBA_G, RGBA_B, RGBA_A)
        call SetTextTagPermanent(t, true)
        
        call FSW_System_SaveTempTexttag(whichFSW, GetPlayerId(whichPlayer), t)
        
        if GetLocalPlayer() == whichPlayer then
            call SetTextTagVisibility(t, true)
        else
            call SetTextTagVisibility(t, false)
        endif
        
        return t
    endfunction


    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  
    //  Handle Functions
    //  
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    function FSW_System_Close_FSW takes integer whichFSW, player whichPlayer returns nothing
        local integer pId = GetPlayerId(whichPlayer)
        
        if (GetLocalPlayer() == whichPlayer) then
            call ResetToGameCamera(0)
            call PanCameraToTimed(GetUnitX(udg_Heroes[pId]), GetUnitY(udg_Heroes[pId]), 0)
        endif
        
        call SetTerrainType(udg_FSW_Array_LocationCenterX[udg_FSW_Index], udg_FSW_Array_LocationCenterY[udg_FSW_Index], udg_FSW_Array_TerrainType[udg_FSW_Index], -1, 8, 1)
        
        call FSW_System_ShowStandardObjects(whichFSW, false)
        call FSW_Remove_Temp_Objects(whichFSW, pId)
        
        call SaveBoolean(udg_FSW_Hashtable, whichFSW, pId, false)
        
        set udg_FSW_Selected_X[pId] = -1
        set udg_FSW_Selected_Y[pId] = -1
        
        set udg_FSW_Event_Window_Closed = 1
        set udg_FSW_Event_Window_Closed = 0
    endfunction

    function FSW_System_Open_FSW takes integer whichFSW, player whichPlayer returns nothing
        local integer pId = GetPlayerId(whichPlayer)
        local integer i = 0
        
        if LoadBoolean(udg_FSW_Hashtable, whichFSW, pId) then
            return
        endif
        
        loop
            exitwhen i > udg_FSW_Index
            if LoadBoolean(udg_FSW_Hashtable, i, pId) then
                call FSW_System_Close_FSW(i, whichPlayer)
                exitwhen true
            endif
            set i = i + 1
        endloop
        
        if (GetLocalPlayer() == whichPlayer) then
            call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, -90, 0)
            call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, udg_FSW_Array_CameraDistance[whichFSW], 0)
            call SetCameraTargetController(udg_FSW_Array_DummyUnit[whichFSW], 0, 0, false)
            
            call SetTerrainType(udg_FSW_Array_LocationCenterX[whichFSW], udg_FSW_Array_LocationCenterY[whichFSW], udg_FSW_Black_Terrain, -1, 8, 1)
            
            call FSW_System_ShowStandardObjects(whichFSW, true)
            
            call SaveBoolean(udg_FSW_Hashtable, whichFSW, pId, true)
            
            set udg_FSW_Selected_X[pId] = -1
            set udg_FSW_Selected_Y[pId] = -1
            
            set udg_FSW_Param_Player = whichPlayer
            set udg_FSW_Param_FSW = whichFSW
            call TriggerExecute(udg_FSW_Trigger_WhileOpen[whichFSW])
            
            set udg_FSW_Event_Window_Opened = 1
            set udg_FSW_Event_Window_Opened = 0
        endif
    endfunction


    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  
    //  Create Trackable Events
    //  
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //Trackable click event.
    function FSW_System_Trackable_Clicked takes nothing returns boolean
        local integer id = GetHandleId(GetTriggeringTrackable())
        local integer whichFSW
        local integer pId = LoadInteger(udg_FSW_Hashtable, id, 0)
        local integer i = 0
        
        loop
            if i > udg_FSW_Index then
                return false
            endif
            
            if LoadBoolean(udg_FSW_Hashtable, i, pId) then
                set whichFSW = i
                exitwhen true
            endif
            set i = i + 1
        endloop
        
        set udg_FSW_Param_LocationX = -udg_FSW_Array_LocationX[whichFSW] + LoadInteger(udg_FSW_Hashtable, id, 1)
        set udg_FSW_Param_LocationY = -udg_FSW_Array_LocationY[whichFSW] + LoadInteger(udg_FSW_Hashtable, id, 2)
        set udg_FSW_Param_FSW = whichFSW
        set udg_FSW_Param_Player = Player(pId)
        
        if LoadBoolean(udg_FSW_Hashtable, whichFSW, pId) then
            set udg_FSW_Event_Trackable_Clicked = 1
            set udg_FSW_Event_Trackable_Clicked = 0
        endif
        
        return false
    endfunction

    //Trackable hover event.
    function FSW_System_Trackable_Hovered takes nothing returns boolean
        local integer id = GetHandleId(GetTriggeringTrackable())
        local integer whichFSW
        local integer pId = LoadInteger(udg_FSW_Hashtable, id, 0)
        local integer i = 0
        
        loop
            if i > udg_FSW_Index then
                return false
            endif
            
            if LoadBoolean(udg_FSW_Hashtable, i, pId) then
                set whichFSW = i
                exitwhen true
            endif
            set i = i + 1
        endloop
        
        set udg_FSW_Param_LocationX = -udg_FSW_Array_LocationX[whichFSW] + LoadInteger(udg_FSW_Hashtable, id, 1)
        set udg_FSW_Param_LocationY = -udg_FSW_Array_LocationY[whichFSW] + LoadInteger(udg_FSW_Hashtable, id, 2)
        set udg_FSW_Param_FSW = whichFSW
        set udg_FSW_Param_Player = Player(pId)
        
        if LoadBoolean(udg_FSW_Hashtable, whichFSW, pId) then
            set udg_FSW_Event_Trackable_Hovered = 1
            set udg_FSW_Event_Trackable_Hovered = 0
        endif
        
        return false
    endfunction

    function FSW_System_While_Open takes nothing returns boolean
        local integer pId = 0
        local player p
        local integer i = 0
        
        loop
            exitwhen pId > 3
            set p = Player(pId)
            
            loop
                exitwhen i > udg_FSW_Index
                if LoadBoolean(udg_FSW_Hashtable, i, pId) then
                    call FSW_Remove_Temp_Objects(i, pId)
                    set udg_FSW_Param_Player = p
                    set udg_FSW_Param_FSW = i
                    call TriggerEvaluate(udg_FSW_Trigger_WhileOpen[i])
                    exitwhen true
                endif
                set i = i + 1
            endloop
            
            set pId = pId + 1
        endloop
        
        return false
    endfunction
endlibrary

function InitTrig_FSW_System takes nothing returns nothing
    call TimerStart(CreateTimer(), 0.05, true, function FSW_System_While_Open)
    call TriggerAddCondition(udg_FSW_Trigger_TrackableClick, Filter(function FSW_System_Trackable_Clicked))
    call TriggerAddCondition(udg_FSW_Trigger_TrackableHover, Filter(function FSW_System_Trackable_Hovered))
endfunction

The new system is very similar but I did not found a nice way to create the objects such as a unit, destructable or texttag easily.
This is how far I got the new system:
JASS:
library fswSystem uses basicFunctions
    
    globals
        
        //Object Data
        integer         udg_FSW_Index                       = 0
        integer array   udg_FSW_Location_X
        integer array   udg_FSW_Location_Y
        real array      udg_FSW_Camera_X
        real array      udg_FSW_Camera_Y
        real array      udg_FSW_CameraDistance
        integer array   udg_FSW_Size
        integer array   udg_FSW_TerrainType
        unit array      udg_FSW_DummyUnit
        
        real array      udg_FSW_PlayerCamera_X
        real array      udg_FSW_PlayerCamera_Y
        integer array   udg_FSW_OpenedWindow
        
        //Constants
        integer         udg_FSW_TERRAIN_BLACK               = 'Oaby'
        trigger         udg_FSW_TRIGGER_TRACKABLE_CLICK     = CreateTrigger()
        trigger         udg_FSW_TRIGGER_TRACKABLE_HOVER     = CreateTrigger()
        string          udg_FSW_TRACKABLE_MODEL             = "FSW\\Trackable.mdx"
        hashtable       udg_FSW_HASHTABLE                   = InitHashtable()
        integer         udg_FSW_GRID                        = 64
        player          udg_FSW_PLAYER_NEUTRAL              = Player(0)     //Player(12)
        integer         udg_FSW_DUMMY_UNIT                  = 'hfoo'        //<dummy-unit>
        timer           udg_FSW_TIMER                       = CreateTimer()
        real            udg_FSW_INTERVAL                    = 0.05
        //integer         udg_FSW_STANDARD_OBJECTS_LIMIT      = 10000
        
        //Events
        real            udg_FSW_Event_Trackable_Clicked     = 0
        real            udg_FSW_Event_Trackable_Hovered     = 0
        real            udg_FSW_Event_Window_Opened         = 0
        real            udg_FSW_Event_Window_Closed         = 0
        integer         udg_FSW_EventResponse_LocX
        integer         udg_FSW_EventResponse_LocY
        integer         udg_FSW_EventResponse_Window
        player          udg_FSW_EventResponse_Player
        
    endglobals
    
    function FSW_CloseWindow takes integer windowIndex, player whichPlayer returns nothing
        local real centerX = udg_FSW_Location_X[windowIndex]*udg_FSW_GRID + udg_FSW_Camera_X[windowIndex]
        local real centerY = udg_FSW_Location_Y[windowIndex]*udg_FSW_GRID + udg_FSW_Camera_Y[windowIndex]
        local integer pId = GetPlayerId(whichPlayer)
        
        if udg_LOCAL_PLAYER == whichPlayer then
            call ResetToGameCamera(0)
            call PanCameraToTimed(udg_FSW_PlayerCamera_X[pId], udg_FSW_PlayerCamera_Y[pId], 0)
            call SetTerrainType(centerX, centerY, udg_FSW_TerrainType[windowIndex], -1, udg_FSW_Size[windowIndex], 1)
        endif
        
        //call FSW_System_ShowStandardObjects(whichFSW, false)
        //call FSW_Remove_Temp_Objects(whichFSW, pId)
        
        call SaveBoolean(udg_FSW_HASHTABLE, windowIndex, pId, false)
        set udg_FSW_OpenedWindow[pId] = 0
        
        set udg_FSW_Event_Window_Closed = 1
        set udg_FSW_Event_Window_Closed = 0
    endfunction
    
    function FSW_OpenWindow takes integer windowIndex, player whichPlayer returns nothing
        local real centerX = udg_FSW_Location_X[windowIndex]*udg_FSW_GRID + udg_FSW_Camera_X[windowIndex]
        local real centerY = udg_FSW_Location_Y[windowIndex]*udg_FSW_GRID + udg_FSW_Camera_Y[windowIndex]
        local integer pId = GetPlayerId(whichPlayer)
        
        if udg_FSW_OpenedWindow[pId] > 0 then
            call FSW_CloseWindow(udg_FSW_OpenedWindow[pId], whichPlayer)
        endif
        
        if udg_LOCAL_PLAYER == whichPlayer then
            
            set udg_FSW_PlayerCamera_X[pId] = GetCameraTargetPositionX()
            set udg_FSW_PlayerCamera_Y[pId] = GetCameraTargetPositionY()
            
            call SetCameraTargetController(udg_FSW_DummyUnit[windowIndex], 0, 0, false)
            call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, -90, 0)
            call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, udg_FSW_CameraDistance[windowIndex], 0)
            call SetTerrainType(centerX, centerY, udg_FSW_TERRAIN_BLACK, 0, udg_FSW_Size[windowIndex], 1)
            
            call SaveBoolean(udg_FSW_HASHTABLE, windowIndex, pId, true)
            set udg_FSW_OpenedWindow[pId] = windowIndex
            
            //call FSW_System_ShowStandardObjects(whichFSW, true)
            
            set udg_FSW_EventResponse_Window = windowIndex
            set udg_FSW_EventResponse_Player = whichPlayer
            
            set udg_FSW_Event_Window_Opened = 1
            set udg_FSW_Event_Window_Opened = 0
            
            //call TriggerExecute(udg_FSW_Trigger_WhileOpen[whichFSW])
        endif
    endfunction
    
    function FSW_CreateWindow takes integer x, integer y, real cameraX, real cameraY, real cameraDistance returns integer
        set udg_FSW_Index = udg_FSW_Index +1
        
        set udg_FSW_Location_X[udg_FSW_Index] = x
        set udg_FSW_Location_Y[udg_FSW_Index] = y
        set udg_FSW_Camera_X[udg_FSW_Index] = cameraX
        set udg_FSW_Camera_X[udg_FSW_Index] = cameraY
        set udg_FSW_CameraDistance[udg_FSW_Index] = cameraDistance
        set udg_FSW_Size[udg_FSW_Index] = 9
        set udg_FSW_TerrainType[udg_FSW_Index] = GetTerrainType(x*udg_FSW_GRID + cameraX, y*udg_FSW_GRID + cameraY)
        set udg_FSW_DummyUnit[udg_FSW_Index] = CreateUnit(udg_FSW_PLAYER_NEUTRAL, udg_FSW_DUMMY_UNIT, cameraX, cameraY, 0)
        
        return udg_FSW_Index
    endfunction
    
    function FSW_CreateTrackable takes integer windowIndex, integer x, integer y returns nothing
        local integer fswX = udg_FSW_Location_X[windowIndex]+x
        local integer fswY = udg_FSW_Location_Y[windowIndex]-y
        local real locX = fswX * udg_FSW_GRID
        local real locY = fswY * udg_FSW_GRID
        local trackable t
        local integer id
        local integer i = 0
        local player p
        local string s
        
        loop
            exitwhen i > 12
            set p = Player(i)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then
                if udg_LOCAL_PLAYER == p then
                    set s = udg_FSW_TRACKABLE_MODEL
                else
                    set s = ""
                endif
                set t = CreateTrackable(s, locX, locY, 0)
                set id = GetHandleId(t)
                
                //Register the events for the trackable.
                call TriggerRegisterTrackableHitEvent(udg_FSW_TRIGGER_TRACKABLE_CLICK, t)
                call TriggerRegisterTrackableTrackEvent(udg_FSW_TRIGGER_TRACKABLE_HOVER, t)
                
                //Save the data to the hashtable.
                call SaveInteger(udg_FSW_HASHTABLE, id, 0, i)
                call SaveInteger(udg_FSW_HASHTABLE, id, 1, fswX)
                call SaveInteger(udg_FSW_HASHTABLE, id, 2, fswY)
            endif
            set i = i + 1
        endloop
        
        set t = null
        set p = null
    endfunction
    
    function FSW_TrackableClick takes nothing returns boolean
        local integer id = GetHandleId(GetTriggeringTrackable())
        local integer windowIndex
        local integer pId = LoadInteger(udg_FSW_HASHTABLE, id, 0)
        local integer i = 0
        
        loop
            if i > udg_FSW_Index then
                return false
            endif
            
            if LoadBoolean(udg_FSW_HASHTABLE, i, pId) then
                set windowIndex = i
                exitwhen true
            endif
            set i = i + 1
        endloop
        
        set udg_FSW_EventResponse_LocX = -udg_FSW_Location_X[windowIndex] + LoadInteger(udg_FSW_HASHTABLE, id, 1)
        set udg_FSW_EventResponse_LocY = -udg_FSW_Location_Y[windowIndex] + LoadInteger(udg_FSW_HASHTABLE, id, 2)
        set udg_FSW_EventResponse_Window = windowIndex
        set udg_FSW_EventResponse_Player = Player(pId)
        
        set udg_FSW_Event_Trackable_Clicked = 1
        set udg_FSW_Event_Trackable_Clicked = 0
        
        return false
    endfunction
    
    function FSW_TrackableHover takes nothing returns boolean
        local integer id = GetHandleId(GetTriggeringTrackable())
        local integer windowIndex
        local integer pId = LoadInteger(udg_FSW_HASHTABLE, id, 0)
        local integer i = 0
        
        loop
            if i > udg_FSW_Index then
                return false
            endif
            
            if LoadBoolean(udg_FSW_HASHTABLE, i, pId) then
                set windowIndex = i
                exitwhen true
            endif
            set i = i + 1
        endloop
        
        set udg_FSW_EventResponse_LocX = -udg_FSW_Location_X[windowIndex] + LoadInteger(udg_FSW_HASHTABLE, id, 1)
        set udg_FSW_EventResponse_LocY = -udg_FSW_Location_Y[windowIndex] + LoadInteger(udg_FSW_HASHTABLE, id, 2)
        set udg_FSW_EventResponse_Window = windowIndex
        set udg_FSW_EventResponse_Player = Player(pId)
        
        set udg_FSW_Event_Trackable_Hovered = 1
        set udg_FSW_Event_Trackable_Hovered = 0
        
        return false
    endfunction
    
    function FSW_Interval takes nothing returns nothing
        local integer pId = 0
        
        loop
            exitwhen pId > 12
            if udg_FSW_OpenedWindow[pId] > 0 then
                call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, -90, 0)
                call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, udg_FSW_CameraDistance[udg_FSW_OpenedWindow[pId]], 0)
                
                
                
            endif
            set pId = pId +1
        endloop
    endfunction
    
endlibrary

function InitTrig_FSW_System takes nothing returns nothing
    call TimerStart(udg_FSW_TIMER, udg_FSW_INTERVAL, true, function FSW_Interval)
    call TriggerAddCondition(udg_FSW_TRIGGER_TRACKABLE_CLICK, Filter(function FSW_TrackableClick))
    call TriggerAddCondition(udg_FSW_TRIGGER_TRACKABLE_HOVER, Filter(function FSW_TrackableHover))
endfunction
(Dont mind udg_LOCAL_PLAYER, that is just GetLocalPlayer())
 
Level 7
Joined
Oct 11, 2008
Messages
304
JASS:
library fswSystem uses basicFunctions

Huh?

What basicFunctions is at all?

To be honest I hate your way to write it, Nestharus's resources was more readable than this (blame me, but I hate those prefix in everything, udg, fsw, no private and etc, also camelCase for me [and probably most of the current resources] are for structs). Sorry, I don't mean to sound rude.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
BasicFunctions is a little library that sums up a lot of usefull functions and stuff.
In this one, I wrote a few of them down at the top which are in the original library but as I removed that use of the functions in the system itself, I could have removed the requirement of it as well.

Except that I never write structs... I just dont... dont ask.

Also... the discussion about we needing such a system is basically because we wanted to have a full screen inventory...
Noone mentioned such a system until I did.
Nestharus way of uploading resources is also not really my favorite.
I dont check GitHub to see if WC3 resources are there or not.
Can you link his system?
 
Level 7
Joined
Oct 11, 2008
Messages
304
I think I expressed myself poorly :p

I was talking about the API (not only the API, but all the way you code, with the udg_, no private function, camelCase, etc).

Nestharus didn't write a Full Screen Interface System, I was talking about the way he write his codes, with poorly names and noone understand that shit :O
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
This has nothing to do with inventory or anything else.
The idea is that you can create your full screen stuff easily with a system like this.
Noone said there was one, so I uploaded one that I used 5-6 months ago and uploaded a WIP rework of it.

If you can link a system that does this already then link it.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Neither do I think that I am able to make it perfect actually.
I have no idea how I can store all objects properly so it will not take massive proc time to do and keeping it MPI.
It worked in that earlier version but it is very very heavy.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Structs dont help.
A struct can have a total of 8192 objects separated by an equal amount over a total of the supported number of windows.
This means that if I want to have 5 different windows, I will have a total of (lets say) 1600 objects.
That amount is all objects that are situational.
For example, there are different items shown on your screen than on mine if we both have the inventory opened.
So those 1600 objects are divided by 12 which leaves us with 133 objects.
That amount might not be enough.

I can make things that do the same as structs but are more powerfull... just because I can make the size of the arrays more generic.
but that wont help either.

The two ways that do work are
1, A hashtable (but I dont know how I would save the things).
2, Textmacros (creating new arrays for each window, thus allowing much more objects)
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I'm now also starting to write a full screen system, as I need it for a map of mine.
At first i will scan already existing systems, so I don't have to figure out every tricks&hints
from the scratch.

I'll gonna make an own thread here in the Lab as soon as I wrote some lines down.
Seems to be a big project and I hope I don't get stuck somewhere on the way.

I'll give you input on your system once I'm more familiar with the matter. Good luck :)
 
Level 7
Joined
Oct 11, 2008
Messages
304
Structs dont help.
A struct can have a total of 8192 objects separated by an equal amount over a total of the supported number of windows.
This means that if I want to have 5 different windows, I will have a total of (lets say) 1600 objects.
That amount is all objects that are situational.
For example, there are different items shown on your screen than on mine if we both have the inventory opened.
So those 1600 objects are divided by 12 which leaves us with 133 objects.
That amount might not be enough.

You're not supposed to do ONE struct for all the windows and players.

IMO, it should be something like this:

JASS:
local Window fullScreen = Window.Create()
local Window panelLeft = Window.Create()

set panelLeft.PositionX = 30
set panelLeft.PositionX = 50

set fullScreen.Tittle = "Giant Screen of The Death!"


Or worst case should be something like this:
JASS:
local Interface inv1 = Interface.create()
local Interface inv2 = Interface.create()

set inv1.Size = 100
set inv1.Owner = GetPlayerId(0) //Only player 1 will interact with it

set inv2.Size = 200
set inv2.Owner = GetPlayerId(1) //Only player 2 will interact with it
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I was wondering if trackables are created on the coordinates you want
or centered to the tile they are on.
When messing around with Anarchons FSE I experienced that
the onHover event is fired even when I'm not on the placed icon,
but also when I'm little bit left/down from the coordinate.

Edit: Somehow trackables also seem to overlap in his system.
click and hover perform quite buggy, but maybe it's the trackable model he is using.
Assuming the name 75x75 represent the size, it is too big for his demo code.
 
Last edited:
I was wondering if trackables are created in the coordinates you want
or centered to the tile they are on.
When messing around with Anarchons FSE I experienced that
the onHover event is fired even when I'm not on the places icon,
but also when I'm little bit left/down from the coordinate.

That might have to do with the Z of the destructables or the trackable/trackable model.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
"You're not supposed to do ONE struct for all the windows and players."
I am supposed to write ONE struct which will act like its own window.
Every window is made for 12 players.
Every player needs an amount of objects.

Unless I use Textmacros, ALL windows come from that ONE struct.
Example:
Window window1 = Window.create()
Window window2 = Window.create()
Window window3 = Window.create()
== ONE struct

64x64 is a very basic one.
It is just the grid on which you can place your destructables (if you have a pathing map).

My trackables overlap... but only if you told it to overlap.
You can place your trackables on that 64x64 grid.
Each point only needs one trackable to work. All windows are able to use that single trackable properly.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
An instance is something different than an object (struct).

However, it doesnt matter.
When you create a struct with local variables, you create those variables with a very fcked up prefix (like mine already have), global as an array.
The size of those arrays (8192) have to fit for every window (as they use the same struct, so the same global variables) and every player.

So your arrays are very small.
So, structs are a no-go.
Or I will have to do it with structs inside textmacros... in which case, I do it without structs inside textmacros.
 
Level 6
Joined
Jul 30, 2013
Messages
282
In common usage struct or class usualyl refers to the fromal description of a (usualyl user defined) type. and object or instance refers to an artefact created based on that description.
So if you say "instance of struct" and really mean "copy of the entire struct definition to actualyl stamp out several identical structs with the same name" that would be confusing..

Also vjass allows for 400k indicies on (sized)arrays tho im not sure how evil the backend looks like. you can definitely use them with structs tho.
 
Level 6
Joined
Jul 30, 2013
Messages
282
Just when I created a struct with an array variable with 400k size, it said it cannot do it.

I didnt try making the struct an array though.

well theres your problem. you made the index space of a struct member bigger than the index space for the entire struct.
struct S
integer array mymember[40000]
enstruct

means

struct S[8192]
integer array mymember[40000]
enstruct

which means you have a maximum of 8192/40000 ~= 0.2 struct instances. its quite reasonable it will error. and it is unreasonable to try and use a feature without takin gthe time to understand what it really does. (and yes its not a very noob-friendly notation but its no rocket science..)
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
JASSHelper supports some feature to "chain" arrays if I recall. This can allow for more than 8192 objects or support for more objects with private arrays. This is not free as it chains them together at best with a binary tree to resolve specific arrays and so introduces a large lookup cost for such member variables.

A 40k array would need to chain together 5 arrays to form an array of maximum size 40960. This would allow only a single such object instance to exist. This would have a lookup complexity of O(3).

A 400k array would need to chain together 49 arrays to form an array of maximum size 401408. This would allow only a single such object instance to exist. This would have a lookup complexity of O(6) which means it will take twice the time of the 40k array to resolve an element.

You can get around such limits using hashtables as a form of multi-dimensional array. Use parent key as the object reference and child key as the array. They have practically unlimited capacity so should have no problem with array sizes like 400k. However like all hashtables, storing large numbers of entries in them is prone to degrading performance from O(1) to O(n).
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
1) Use half-assed features that limit your code and make it spaghetti.
2) Use a proper hash table that is slightly slower, but makes perfect sense CONSIDERING THE CONTEXT, and makes your code easy to maintain.

But we all know that you people will rather die than be sensible, so go and put every function in a separate library, and make short variable names that you won't remember in two days, and most definitely don't use a hash table for offline code (offline in the sense that it isn't real-time) even though it actually makes sense.
So what if your code is terrible, unreadable, and is constrained, it runs 0.2 milliseconds faster, that's the important part.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
An instance is something different than an object (struct).

Im not sure about that one.

Regarding size:

JASS:
struct S
    Table t1
    private Table t2
endstruct

This is 4 lines of code, compared to what you would have to write to replicate in Jass(like 50+ considering allocators).

And also, afaik you already use hashtable so no deal here.

I would say evaluating if 2x is still faster than calling hashtable, considering you are performing the conditional on integers.

This.

Also it's already unreadable and terrible, so I don't see it becoming worst :ogre_kawaii:

200 microseconds? You crazy Wolfy? more like, 50.
 
1) Use half-assed features that limit your code and make it spaghetti.
2) Use a proper hash table that is slightly slower, but makes perfect sense CONSIDERING THE CONTEXT, and makes your code easy to maintain.

But we all know that you people will rather die than be sensible, so go and put every function in a separate library, and make short variable names that you won't remember in two days, and most definitely don't use a hash table for offline code (offline in the sense that it isn't real-time) even though it actually makes sense.
So what if your code is terrible, unreadable, and is constrained, it runs 0.2 milliseconds faster, that's the important part.
Little bitter, eh? I understand your reasoning, though.
A modular fullscreen system is a monumental task that you need careful planning for.

But I trust in BPower's skills to make it reasonable in flexibility and readability. It certainly won't be as hacky as Nest's resources.


I agree with the hashtable thing. While I don't think the struct instance or member limit will ever be a problem (if the system is structured well), I think you will eventually end up using a hashtable anyway, simply because hashtables are the only sane solution for fast lookup tables indexed by rawcodes or handle IDs.
So you might aswell just go and embrace it right from the start.

Good thing hashtables and structs are not mutual exclusive. ;)


About the functionality of the system, here are my suggestions:

- Use the http://www.hiveworkshop.com/forums/jass-resources-412/system-track-205760/ resource as a requirement.
- Here is a window library that you can use for inspiration; I think it is very clean and well structured; maybe you can actually use it as a basis and extend it with extra drawing functionality: http://www.hiveworkshop.com/forums/spells-569/customwindow-v1-1-1-2-a-207526/
- This is how you should display tooltips and texts in general, as texttags tend to have unpredictable length depending on the resolution setting of the player: http://www.hiveworkshop.com/forums/lab-715/wurst-textsplat-247943/ (you have to translate it to vJass, though)
 
Level 11
Joined
Dec 3, 2011
Messages
366
Little bitter, eh? I understand your reasoning, though.
A modular fullscreen system is a monumental task that you need careful planning for.

But I trust in BPower's skills to make it reasonable in flexibility and readability. It certainly won't be as hacky as Nest's resources.


I agree with the hashtable thing. While I don't think the struct instance or member limit will ever be a problem (if the system is structured well), I think you will eventually end up using a hashtable anyway, simply because hashtables are the only sane solution for fast lookup tables indexed by rawcodes or handle IDs.
So you might aswell just go and embrace it right from the start.

Good thing hashtables and structs are not mutual exclusive. ;)


About the functionality of the system, here are my suggestions:

- Use the http://www.hiveworkshop.com/forums/jass-resources-412/system-track-205760/ resource as a requirement.
- Here is a window library that you can use for inspiration; I think it is very clean and well structured; maybe you can actually use it as a basis and extend it with extra drawing functionality: http://www.hiveworkshop.com/forums/spells-569/customwindow-v1-1-1-2-a-207526/
- This is how you should display tooltips and texts in general, as texttags tend to have unpredictable length depending on the resolution setting of the player: http://www.hiveworkshop.com/forums/lab-715/wurst-textsplat-247943/ (you have to translate it to vJass, though)

I remember that I've seen textsplat of Deaod

http://www.hiveworkshop.com/forums/...2-a-147358/?prev=search=textsplat&d=list&r=20
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I started, but it's going slowly as I don't have to much time at the moment.

- Purge's Track is a requirement, because it fits perfectly.

I already used Maker's Custom Window in the past. It's really nice.
Yet, I still wonder if it's better to draw borders with images or use destructables.
I guess images are more effecient, but hard to connect seamless. Any opinions on that?

Icons which should be move-able are made from destructables, that's for sure.
For buttons I'm also unsure, but destructable would offer a possibility to use animation.
 
I started, but it's going slowly as I don't have to much time at the moment.

- Purge's Track is a requirement, because it fits perfectly.

I already used Maker's Custom Window in the past. It's really nice.
Yet, I still wonder if it's better to draw borders with images or use destructables.
I guess images are more effecient, but hard to connect seamless. Any opinions on that?

Icons which should be move-able are made from destructables, that's for sure.
For buttons I'm also unsure, but destructable would offer a possibility to use animation.
Border choice depends on how you want the workflow for users, as borders based on destructables allow you to preplace the borders directly in the terrain --> more artistic freedom for the user. In that case, your window draw commands would only create dynamic elements; all static elements like borders could be hand-crafted by the user.
This would also allow defining window and panel sizes by using rects; which creates a neat artistic intuitive workflow, especially for unexperienced users:
-> Place the destructables for the borders
-> draw the rects
-> register the rects into the system


If you want auto-generated borders or want dynamic windows (and resizable windows), images are far better, as they aren't synced and use up way less CPU.

Icons imho shouldn't be destructables, as then you would need one destructable per icon and can not simply change icons at runtime. Images are more flexible as you can just create/destroy them on the fly and they don't require any object data.

On a side note you can also use units for the icons and detect select events or order events (right click) instead of trackable events. The cool thing about this is that it allows showing tooltips directly and lag-free over the icon, by abusing SetPlayerName on a computer player owner for that icon.
I think this is also slightly faster than trackables. However, it also looks ugly because of ... well, you select the icons, which means you will see the command card pop up.
Units allow changing textures via Warclub ability; however, you would - again - need one destructable per icon.

I'd go with images for icons aswell.

Buttons can be destructables so that they can be animated.

Selection effects (like a glowing border for an icon you just clicked) can be special effects; no reason to use destructables for that.


So, tl:dr:

Borders: Images/Destructables, depending on workflow
Icons: Images
Buttons: Destructables
Effects: Special Effects (obviously)


PS: You can also create a neat "drag and drop" effect by using a complete trackmap. I think there was a library here called TrackMap which does that. What you can do is basicly "attach" the icon to the mouse position after clicking by periodically moving it to the cursor.
The trackmap has a delay, you'd need to interpolate the position between each track event to make it run smooth.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
A 40k array would need to chain together 5 arrays to form an array of maximum size 40960. This would allow only a single such object instance to exist. This would have a lookup complexity of O(3).

A 400k array would need to chain together 49 arrays to form an array of maximum size 401408. This would allow only a single such object instance to exist. This would have a lookup complexity of O(6) which means it will take twice the time of the 40k array to resolve an element.

You can get around such limits using hashtables as a form of multi-dimensional array. Use parent key as the object reference and child key as the array. They have practically unlimited capacity so should have no problem with array sizes like 400k. However like all hashtables, storing large numbers of entries in them is prone to degrading performance from O(1) to O(n).

O(3), O(6), ...

What is that supposed to mean? O(6) takes twice as long as O(3)? Thats not how this works...
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
O has nothing to do with "how long does something take" in general.

It describes time/space complexity of the algorithm, and O(3) algorithm takes 3 operations, and O(6) takes 6 operations. Yes, both could be shortened to O(1), but then you would lose the idea of "this approach takes more operations that the other" information from it. Thats why O(n) != O(2n), even when O(n) ~~ O(2n) for sufficiently large n
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
What is that supposed to mean? O(6) takes twice as long as O(3)? Thats not how this works...
Well The algorithm has a complexity (or should have) of O(log2(n)) where n is the number of arrays used. If you substitute for n to provide those spaces you get 3 and 6 appropriately. As such the 400k array will take twice as long to interact with than the 40k array.

It scales pretty well, but due to the nature of JASS anything that is not a direct array access will be a lot slower (the conditional resolution time becomes larger than the array look up time). As such it generally is not recommended unless you absolutely need the larger arrays.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Compare an O(n) loop and an O(log n) binary search over different n values in different languages, you might be surprised by the results.
Point is, you should benchmark real use cases.

This isn't related to this thread, and nor are any of your performance replies (even though I mentioned performance myself, it was actually a criticism of the OP).

I am not sure why performance is even in this topic, seeing how it's offline code.
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
You cannot directly access the right data in the arrays.
So when you use a value form such an array, you will have to check if the index would be in the first array.
If not, then check if it is inside the second array.
If not, then check if it is inside the third array.
If not, then check if it is inside the fourth array.
If not, then check if it is inside the fifth array.
So you are pretty much doomed wen you have to check for each array because your index is inside the last one (lets say 49th).

To speed things up, it is better to just split up the arrays in two.
So you half the arrays by checking if it is in the bigger half or in the lower half.
So in the first check, you check if it is between 1 and 32 or between 33 and 64.
In the second check between 1 and 16, 17 and 32, 33 and 48 or 49 and 64.
And you do that until you only have one array left.
That way, you only need (1 (2), 2 (4), 3 (8), 4 (16), 5 (32), 6 (64)) 6 checks to get to the right array in every single case of array groups larger than 32 but smaller or equal to 64.

This is the most simple option to do and I assume it is pretty much done like that.
I dont really think you can use other numbers in JASS because you cannot link a variable to an integer so...

But that means that an array of size 8192*5 (bigger than 4, smaller than 8 = 3 checks) is double as fast as an array of size 8192*49 (bigger than 32, smaller than 64 = 6 checks).
I assume O is in this case equal to one check.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
a simple modulo will not help to get to the right array without linking the array to a number.

and this is linked to the point where this can be done in arrays.
which doesnt really matter yet cause noone has mentioned a good way to save the stuff in any case.
 
Status
Not open for further replies.
Top