2002 lines
62 KiB
Lua
2002 lines
62 KiB
Lua
-- Backpack Version 5.1
|
|
-- OnlyTwentyCharacters, SolarCrane
|
|
|
|
local BackpackScript = {}
|
|
BackpackScript.OpenClose = nil -- Function to toggle open/close
|
|
BackpackScript.IsOpen = false
|
|
BackpackScript.StateChanged = Instance.new('BindableEvent') -- Fires after any open/close, passes IsNowOpen
|
|
|
|
BackpackScript.ModuleName = "Backpack"
|
|
BackpackScript.KeepVRTopbarOpen = true
|
|
BackpackScript.VRIsExclusive = true
|
|
BackpackScript.VRClosesNonExclusive = true
|
|
|
|
local ICON_SIZE = 60
|
|
local FONT_SIZE = Enum.FontSize.Size14
|
|
local ICON_BUFFER = 5
|
|
|
|
local BACKGROUND_FADE = 0.50
|
|
local BACKGROUND_COLOR = Color3.new(31/255, 31/255, 31/255)
|
|
|
|
local VR_FADE_TIME = 1
|
|
local VR_PANEL_RESOLUTION = 100
|
|
|
|
local SLOT_DRAGGABLE_COLOR = Color3.new(49/255, 49/255, 49/255)
|
|
local SLOT_EQUIP_COLOR = Color3.new(90/255, 142/255, 233/255)
|
|
local SLOT_EQUIP_THICKNESS = 0.1 -- Relative
|
|
local SLOT_FADE_LOCKED = 0.50 -- Locked means undraggable
|
|
local SLOT_BORDER_COLOR = Color3.new(1, 1, 1) -- Appears when dragging
|
|
|
|
local TOOLTIP_BUFFER = 6
|
|
local TOOLTIP_HEIGHT = 16
|
|
local TOOLTIP_OFFSET = -25 -- From top
|
|
|
|
local ARROW_IMAGE_OPEN = 'rbxasset://textures/ui/Backpack_Open.png'
|
|
local ARROW_IMAGE_CLOSE = 'rbxasset://textures/ui/Backpack_Close.png'
|
|
local ARROW_SIZE = UDim2.new(0, 14, 0, 9)
|
|
local ARROW_HOTKEY = Enum.KeyCode.Backquote.Value --TODO: Hookup '~' too?
|
|
local ARROW_HOTKEY_STRING = '`'
|
|
local ARROW_HOVER_COLOR = Color3.new(0,162/255,1)
|
|
|
|
local HOTBAR_SLOTS_FULL = 10
|
|
local HOTBAR_SLOTS_VR = 6
|
|
local HOTBAR_SLOTS_MINI = 3
|
|
local HOTBAR_SLOTS_WIDTH_CUTOFF = 1024 -- Anything smaller is MINI
|
|
local HOTBAR_OFFSET_FROMBOTTOM = -30 -- Offset to make room for the Health GUI
|
|
|
|
local INVENTORY_ROWS_FULL = 4
|
|
local INVENTORY_ROWS_VR = 3
|
|
local INVENTORY_ROWS_MINI = 2
|
|
local INVENTORY_HEADER_SIZE = 40
|
|
local INVENTORY_ARROWS_BUFFER_VR = 40
|
|
|
|
local SEARCH_BUFFER = 5
|
|
local SEARCH_WIDTH = 200
|
|
local SEARCH_TEXT = " Search"
|
|
|
|
local SEARCH_TEXT_OFFSET_FROMLEFT = 0
|
|
local SEARCH_BACKGROUND_COLOR = Color3.new(0.37, 0.37, 0.37)
|
|
local SEARCH_BACKGROUND_FADE = 0.15
|
|
|
|
local DOUBLE_CLICK_TIME = 0.5
|
|
|
|
local ZERO_KEY_VALUE = Enum.KeyCode.Zero.Value
|
|
local DROP_HOTKEY_VALUE = Enum.KeyCode.Backspace.Value
|
|
|
|
local GAMEPAD_INPUT_TYPES =
|
|
{
|
|
[Enum.UserInputType.Gamepad1] = true;
|
|
[Enum.UserInputType.Gamepad2] = true;
|
|
[Enum.UserInputType.Gamepad3] = true;
|
|
[Enum.UserInputType.Gamepad4] = true;
|
|
[Enum.UserInputType.Gamepad5] = true;
|
|
[Enum.UserInputType.Gamepad6] = true;
|
|
[Enum.UserInputType.Gamepad7] = true;
|
|
[Enum.UserInputType.Gamepad8] = true;
|
|
}
|
|
|
|
local PlayersService = game:GetService('Players')
|
|
local UserInputService = game:GetService('UserInputService')
|
|
local StarterGui = game:GetService('StarterGui')
|
|
local GuiService = game:GetService('GuiService')
|
|
local CoreGui = game:GetService('CoreGui')
|
|
local ContextActionService = game:GetService('ContextActionService')
|
|
local VRService = game:GetService("VRService")
|
|
local RobloxGui = CoreGui:WaitForChild('RobloxGui')
|
|
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
|
local IsTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
|
local Utility = require(RobloxGui.Modules.Settings.Utility)
|
|
local GameTranslator = require(RobloxGui.Modules.GameTranslator)
|
|
|
|
local FFlagBackpackScriptUseFormatByKey = settings():GetFFlag('BackpackScriptUseFormatByKey')
|
|
|
|
if FFlagBackpackScriptUseFormatByKey then
|
|
SEARCH_TEXT_OFFSET_FROMLEFT = 3
|
|
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
|
SEARCH_TEXT = RobloxTranslator:FormatByKey("BACKPACK_SEARCH")
|
|
else
|
|
pcall(function()
|
|
local LocalizationService = game:GetService("LocalizationService")
|
|
local CorescriptLocalization = LocalizationService:GetCorescriptLocalizations()[1]
|
|
SEARCH_TEXT = CorescriptLocalization:GetString(
|
|
LocalizationService.RobloxLocaleId,
|
|
"BACKPACK_SEARCH"
|
|
)
|
|
end)
|
|
end
|
|
|
|
local TopbarEnabled = true
|
|
|
|
if IsTenFootInterface then
|
|
ICON_SIZE = 100
|
|
FONT_SIZE = Enum.FontSize.Size24
|
|
end
|
|
|
|
local GamepadActionsBound = false
|
|
|
|
local IS_PHONE = UserInputService.TouchEnabled and GuiService:GetScreenResolution().X < HOTBAR_SLOTS_WIDTH_CUTOFF
|
|
|
|
local Player = PlayersService.LocalPlayer
|
|
|
|
local MainFrame = nil
|
|
local HotbarFrame = nil
|
|
local OpenInventoryButton = nil
|
|
local CloseInventoryButton = nil
|
|
local InventoryFrame = nil
|
|
local VRInventorySelector = nil
|
|
local ScrollingFrame = nil
|
|
local UIGridFrame = nil
|
|
local UIGridLayout = nil
|
|
local ScrollUpInventoryButton = nil
|
|
local ScrollDownInventoryButton = nil
|
|
|
|
local Character = nil
|
|
local Humanoid = nil
|
|
local Backpack = nil
|
|
|
|
local Slots = {} -- List of all Slots by index
|
|
local LowestEmptySlot = nil
|
|
local SlotsByTool = {} -- Map of Tools to their assigned Slots
|
|
local HotkeyFns = {} -- Map of KeyCode values to their assigned behaviors
|
|
local Dragging = {} -- Only used to check if anything is being dragged, to disable other input
|
|
local FullHotbarSlots = 0 -- Now being used to also determine whether or not LB and RB on the gamepad are enabled.
|
|
local ActiveHopper = nil -- NOTE: HopperBin
|
|
local StarterToolFound = false -- Special handling is required for the gear currently equipped on the site
|
|
local WholeThingEnabled = false
|
|
local TextBoxFocused = false -- ANY TextBox, not just the search box
|
|
local ViewingSearchResults = false -- If the results of a search are currently being viewed
|
|
local HotkeyStrings = {} -- Used for eating/releasing hotkeys
|
|
local CharConns = {} -- Holds character connections to be cleared later
|
|
local GamepadEnabled = false -- determines if our gui needs to be gamepad friendly
|
|
local TimeOfLastToolChange = 0
|
|
|
|
local IsVR = VRService.VREnabled -- Are we currently using a VR device?
|
|
local NumberOfHotbarSlots = IsVR and HOTBAR_SLOTS_VR or (IS_PHONE and HOTBAR_SLOTS_MINI or HOTBAR_SLOTS_FULL) -- Number of slots shown at the bottom
|
|
local NumberOfInventoryRows = IsVR and INVENTORY_ROWS_VR or (IS_PHONE and INVENTORY_ROWS_MINI or INVENTORY_ROWS_FULL) -- How many rows in the popped-up inventory
|
|
local BackpackPanel = nil
|
|
|
|
local lastEquippedSlot = nil
|
|
|
|
local function EvaluateBackpackPanelVisibility(enabled)
|
|
return enabled and StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack) and TopbarEnabled and VRService.VREnabled
|
|
end
|
|
|
|
local function ShowVRBackpackPopup()
|
|
if BackpackPanel and EvaluateBackpackPanelVisibility(true) then
|
|
BackpackPanel:ForceShowForSeconds(2)
|
|
end
|
|
end
|
|
|
|
local function NewGui(className, objectName)
|
|
local newGui = Instance.new(className)
|
|
newGui.Name = objectName
|
|
newGui.BackgroundColor3 = Color3.new(0, 0, 0)
|
|
newGui.BackgroundTransparency = 1
|
|
newGui.BorderColor3 = Color3.new(0, 0, 0)
|
|
newGui.BorderSizePixel = 0
|
|
newGui.Size = UDim2.new(1, 0, 1, 0)
|
|
if className:match('Text') then
|
|
newGui.TextColor3 = Color3.new(1, 1, 1)
|
|
newGui.Text = ''
|
|
newGui.Font = Enum.Font.SourceSans
|
|
newGui.FontSize = FONT_SIZE
|
|
newGui.TextWrapped = true
|
|
if className == 'TextButton' then
|
|
newGui.Font = Enum.Font.SourceSansBold
|
|
newGui.BorderSizePixel = 1
|
|
end
|
|
end
|
|
return newGui
|
|
end
|
|
|
|
local function FindLowestEmpty()
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local slot = Slots[i]
|
|
if not slot.Tool then
|
|
return slot
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function isInventoryEmpty()
|
|
for i = NumberOfHotbarSlots + 1, #Slots do
|
|
local slot = Slots[i]
|
|
if slot and slot.Tool then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function UseGazeSelection()
|
|
return UserInputService.VREnabled
|
|
end
|
|
|
|
local function AdjustHotbarFrames()
|
|
local inventoryOpen = InventoryFrame.Visible -- (Show all)
|
|
local visualTotal = (inventoryOpen) and NumberOfHotbarSlots or FullHotbarSlots
|
|
local visualIndex = 0
|
|
local hotbarIsVisible = (visualTotal >= 1)
|
|
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local slot = Slots[i]
|
|
if slot.Tool or inventoryOpen then
|
|
visualIndex = visualIndex + 1
|
|
slot:Readjust(visualIndex, visualTotal)
|
|
slot.Frame.Visible = true
|
|
else
|
|
slot.Frame.Visible = false
|
|
end
|
|
end
|
|
|
|
OpenInventoryButton.Visible = not inventoryOpen and (hotbarIsVisible or not isInventoryEmpty())
|
|
OpenInventoryButton.Position = UDim2.new(0.5, -15, 1, hotbarIsVisible and -110 or -50)
|
|
end
|
|
|
|
local function UpdateScrollingFrameCanvasSize()
|
|
local countX = math.floor(ScrollingFrame.AbsoluteSize.X/(ICON_SIZE + ICON_BUFFER))
|
|
local maxRow = math.ceil((#UIGridFrame:GetChildren() - 1)/countX)
|
|
local canvasSizeY = maxRow*(ICON_SIZE + ICON_BUFFER) + ICON_BUFFER
|
|
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, canvasSizeY)
|
|
end
|
|
|
|
local function AdjustInventoryFrames()
|
|
for i = NumberOfHotbarSlots + 1, #Slots do
|
|
local slot = Slots[i]
|
|
slot.Frame.LayoutOrder = slot.Index
|
|
slot.Frame.Visible = (slot.Tool ~= nil)
|
|
end
|
|
UpdateScrollingFrameCanvasSize()
|
|
end
|
|
|
|
local function UpdateBackpackLayout()
|
|
HotbarFrame.Size = UDim2.new(0, ICON_BUFFER + (NumberOfHotbarSlots * (ICON_SIZE + ICON_BUFFER)), 0, ICON_BUFFER + ICON_SIZE + ICON_BUFFER)
|
|
HotbarFrame.Position = UDim2.new(0.5, -HotbarFrame.Size.X.Offset / 2, 1, -HotbarFrame.Size.Y.Offset)
|
|
InventoryFrame.Size = UDim2.new(0, HotbarFrame.Size.X.Offset, 0, (HotbarFrame.Size.Y.Offset * NumberOfInventoryRows) + INVENTORY_HEADER_SIZE + (IsVR and 2*INVENTORY_ARROWS_BUFFER_VR or 0))
|
|
InventoryFrame.Position = UDim2.new(0.5, -InventoryFrame.Size.X.Offset / 2, 1, HotbarFrame.Position.Y.Offset - InventoryFrame.Size.Y.Offset)
|
|
|
|
ScrollingFrame.Size = UDim2.new(1, ScrollingFrame.ScrollBarThickness + 1, 1, -INVENTORY_HEADER_SIZE - (IsVR and 2*INVENTORY_ARROWS_BUFFER_VR or 0))
|
|
ScrollingFrame.Position = UDim2.new(0, 0, 0, INVENTORY_HEADER_SIZE + (IsVR and INVENTORY_ARROWS_BUFFER_VR or 0))
|
|
AdjustHotbarFrames()
|
|
AdjustInventoryFrames()
|
|
end
|
|
|
|
local function Clamp(low, high, num)
|
|
return math.min(high, math.max(low, num))
|
|
end
|
|
|
|
local function CheckBounds(guiObject, x, y)
|
|
local pos = guiObject.AbsolutePosition
|
|
local size = guiObject.AbsoluteSize
|
|
return (x > pos.X and x <= pos.X + size.X and y > pos.Y and y <= pos.Y + size.Y)
|
|
end
|
|
|
|
local function GetOffset(guiObject, point)
|
|
local centerPoint = guiObject.AbsolutePosition + (guiObject.AbsoluteSize / 2)
|
|
return (centerPoint - point).magnitude
|
|
end
|
|
|
|
local function DisableActiveHopper() --NOTE: HopperBin
|
|
ActiveHopper:ToggleSelect()
|
|
SlotsByTool[ActiveHopper]:UpdateEquipView()
|
|
ActiveHopper = nil
|
|
end
|
|
|
|
local function UnequipAllTools() --NOTE: HopperBin
|
|
if Humanoid then
|
|
Humanoid:UnequipTools()
|
|
if ActiveHopper then
|
|
DisableActiveHopper()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function EquipNewTool(tool) --NOTE: HopperBin
|
|
UnequipAllTools()
|
|
if tool:IsA('HopperBin') then
|
|
tool:ToggleSelect()
|
|
SlotsByTool[tool]:UpdateEquipView()
|
|
ActiveHopper = tool
|
|
else
|
|
--Humanoid:EquipTool(tool) --NOTE: This would also unequip current Tool
|
|
tool.Parent = Character --TODO: Switch back to above line after EquipTool is fixed!
|
|
end
|
|
end
|
|
|
|
local function IsEquipped(tool)
|
|
return tool and ((tool:IsA('HopperBin') and tool.Active) or tool.Parent == Character) --NOTE: HopperBin
|
|
end
|
|
|
|
local function MakeSlot(parent, index)
|
|
index = index or (#Slots + 1)
|
|
|
|
-- Slot Definition --
|
|
|
|
local slot = {}
|
|
slot.Tool = nil
|
|
slot.Index = index
|
|
slot.Frame = nil
|
|
|
|
local LocalizedName = nil
|
|
local LocalizedToolTip = nil
|
|
|
|
local SlotFrameParent = nil
|
|
local SlotFrame = nil
|
|
local FakeSlotFrame = nil
|
|
local ToolIcon = nil
|
|
local ToolName = nil
|
|
local ToolChangeConn = nil
|
|
local HighlightFrame = nil
|
|
local SelectionObj = nil
|
|
|
|
--NOTE: The following are only defined for Hotbar Slots
|
|
local ToolTip = nil
|
|
local SlotNumber = nil
|
|
|
|
-- Slot Functions --
|
|
|
|
local function UpdateSlotFading()
|
|
if VRService.VREnabled and BackpackPanel then
|
|
local panelTransparency = BackpackPanel.transparency
|
|
local slotTransparency = SLOT_FADE_LOCKED
|
|
|
|
-- This equation multiplies the two transparencies together.
|
|
local finalTransparency = panelTransparency + slotTransparency - panelTransparency * slotTransparency
|
|
|
|
SlotFrame.BackgroundTransparency = finalTransparency
|
|
SlotFrame.TextTransparency = finalTransparency
|
|
if ToolIcon then
|
|
ToolIcon.ImageTransparency = InventoryFrame.Visible and 0 or panelTransparency
|
|
end
|
|
if HighlightFrame then
|
|
for _, child in pairs(HighlightFrame:GetChildren()) do
|
|
child.BackgroundTransparency = finalTransparency
|
|
end
|
|
end
|
|
|
|
SlotFrame.SelectionImageObject = SelectionObj
|
|
else
|
|
SlotFrame.SelectionImageObject = nil
|
|
SlotFrame.BackgroundTransparency = (SlotFrame.Draggable) and 0 or SLOT_FADE_LOCKED
|
|
end
|
|
SlotFrame.BackgroundColor3 = (SlotFrame.Draggable) and SLOT_DRAGGABLE_COLOR or BACKGROUND_COLOR
|
|
end
|
|
|
|
function slot:Readjust(visualIndex, visualTotal) --NOTE: Only used for Hotbar slots
|
|
local centered = HotbarFrame.Size.X.Offset / 2
|
|
local sizePlus = ICON_BUFFER + ICON_SIZE
|
|
local midpointish = (visualTotal / 2) + 0.5
|
|
local factor = visualIndex - midpointish
|
|
SlotFrame.Position = UDim2.new(0, centered - (ICON_SIZE / 2) + (sizePlus * factor), 0, ICON_BUFFER)
|
|
end
|
|
|
|
function slot:Fill(tool)
|
|
if not tool then
|
|
return self:Clear()
|
|
end
|
|
|
|
self.Tool = tool
|
|
|
|
local function assignToolData()
|
|
LocalizedName = GameTranslator:TranslateGameText(tool, tool.Name)
|
|
LocalizedToolTip = nil
|
|
|
|
local icon = tool.TextureId
|
|
ToolIcon.Image = icon
|
|
ToolName.Text = (icon == '') and LocalizedName or '' -- (Only show name if no icon)
|
|
if ToolTip and tool:IsA('Tool') then --NOTE: HopperBin
|
|
LocalizedToolTip = GameTranslator:TranslateGameText(tool, tool.ToolTip)
|
|
ToolTip.Text = LocalizedToolTip
|
|
local width = ToolTip.TextBounds.X + TOOLTIP_BUFFER
|
|
ToolTip.Size = UDim2.new(0, width, 0, TOOLTIP_HEIGHT)
|
|
ToolTip.Position = UDim2.new(0.5, -width / 2, 0, TOOLTIP_OFFSET)
|
|
end
|
|
end
|
|
assignToolData()
|
|
|
|
if ToolChangeConn then
|
|
ToolChangeConn:disconnect()
|
|
ToolChangeConn = nil
|
|
end
|
|
|
|
ToolChangeConn = tool.Changed:connect(function(property)
|
|
if property == 'TextureId' or property == 'Name' or property == 'ToolTip' then
|
|
assignToolData()
|
|
end
|
|
end)
|
|
|
|
local hotbarSlot = (self.Index <= NumberOfHotbarSlots)
|
|
local inventoryOpen = InventoryFrame.Visible
|
|
|
|
if (not hotbarSlot or inventoryOpen) and not UserInputService.VREnabled then
|
|
SlotFrame.Draggable = true
|
|
end
|
|
|
|
self:UpdateEquipView()
|
|
|
|
if hotbarSlot then
|
|
FullHotbarSlots = FullHotbarSlots + 1
|
|
-- If using a controller, determine whether or not we can enable BindCoreAction("RBXHotbarEquip", etc)
|
|
if WholeThingEnabled then
|
|
if FullHotbarSlots >= 1 and not GamepadActionsBound then
|
|
-- Player added first item to a hotbar slot, enable BindCoreAction
|
|
GamepadActionsBound = true
|
|
ContextActionService:BindCoreAction("RBXHotbarEquip", changeToolFunc, false, Enum.KeyCode.ButtonL1, Enum.KeyCode.ButtonR1)
|
|
end
|
|
end
|
|
end
|
|
|
|
SlotsByTool[tool] = self
|
|
LowestEmptySlot = FindLowestEmpty()
|
|
end
|
|
|
|
function slot:Clear()
|
|
if not self.Tool then return end
|
|
|
|
if ToolChangeConn then
|
|
ToolChangeConn:disconnect()
|
|
ToolChangeConn = nil
|
|
end
|
|
|
|
ToolIcon.Image = ''
|
|
ToolName.Text = ''
|
|
if ToolTip then
|
|
ToolTip.Text = ''
|
|
ToolTip.Visible = false
|
|
end
|
|
SlotFrame.Draggable = false
|
|
|
|
self:UpdateEquipView(true) -- Show as unequipped
|
|
|
|
if self.Index <= NumberOfHotbarSlots then
|
|
FullHotbarSlots = FullHotbarSlots - 1
|
|
if FullHotbarSlots < 1 then
|
|
-- Player removed last item from hotbar; UnbindCoreAction("RBXHotbarEquip"), allowing the developer to use LB and RB.
|
|
GamepadActionsBound = false
|
|
ContextActionService:UnbindCoreAction("RBXHotbarEquip")
|
|
end
|
|
end
|
|
|
|
SlotsByTool[self.Tool] = nil
|
|
self.Tool = nil
|
|
LowestEmptySlot = FindLowestEmpty()
|
|
end
|
|
|
|
function slot:UpdateEquipView(unequippedOverride)
|
|
if not unequippedOverride and IsEquipped(self.Tool) then -- Equipped
|
|
lastEquippedSlot = slot
|
|
if not HighlightFrame then
|
|
HighlightFrame = NewGui('Frame', 'Equipped')
|
|
HighlightFrame.ZIndex = SlotFrame.ZIndex
|
|
local t = SLOT_EQUIP_THICKNESS
|
|
local dataTable = { -- Relative sizes and positions
|
|
{t, 1, 0, 0},
|
|
{1 - 2*t, t, t, 0},
|
|
{t, 1, 1 - t, 0},
|
|
{1 - 2*t, t, t, 1 - t},
|
|
}
|
|
for _, data in pairs(dataTable) do
|
|
local edgeFrame = NewGui('Frame', 'Edge')
|
|
edgeFrame.BackgroundTransparency = 0
|
|
edgeFrame.BackgroundColor3 = SLOT_EQUIP_COLOR
|
|
edgeFrame.Size = UDim2.new(data[1], 0, data[2], 0)
|
|
edgeFrame.Position = UDim2.new(data[3], 0, data[4], 0)
|
|
edgeFrame.ZIndex = HighlightFrame.ZIndex
|
|
edgeFrame.Parent = HighlightFrame
|
|
end
|
|
end
|
|
HighlightFrame.Parent = SlotFrame
|
|
else -- In the Backpack
|
|
if HighlightFrame then
|
|
HighlightFrame.Parent = nil
|
|
end
|
|
end
|
|
UpdateSlotFading()
|
|
end
|
|
|
|
function slot:IsEquipped()
|
|
return IsEquipped(self.Tool)
|
|
end
|
|
|
|
function slot:Delete()
|
|
SlotFrame:Destroy() --NOTE: Also clears connections
|
|
table.remove(Slots, self.Index)
|
|
local newSize = #Slots
|
|
|
|
-- Now adjust the rest (both visually and representationally)
|
|
for i = self.Index, newSize do
|
|
Slots[i]:SlideBack()
|
|
end
|
|
|
|
UpdateScrollingFrameCanvasSize()
|
|
end
|
|
|
|
function slot:Swap(targetSlot) --NOTE: This slot (self) must not be empty!
|
|
local myTool, otherTool = self.Tool, targetSlot.Tool
|
|
self:Clear()
|
|
if otherTool then -- (Target slot might be empty)
|
|
targetSlot:Clear()
|
|
self:Fill(otherTool)
|
|
end
|
|
if myTool then
|
|
targetSlot:Fill(myTool)
|
|
else
|
|
targetSlot:Clear()
|
|
end
|
|
end
|
|
|
|
function slot:SlideBack() -- For inventory slot shifting
|
|
self.Index = self.Index - 1
|
|
SlotFrame.Name = self.Index
|
|
SlotFrame.LayoutOrder = self.Index
|
|
end
|
|
|
|
function slot:TurnNumber(on)
|
|
if SlotNumber then
|
|
SlotNumber.Visible = on
|
|
end
|
|
end
|
|
|
|
function slot:SetClickability(on) -- (Happens on open/close arrow)
|
|
if self.Tool then
|
|
if UserInputService.VREnabled then
|
|
SlotFrame.Draggable = false
|
|
else
|
|
SlotFrame.Draggable = not on
|
|
end
|
|
UpdateSlotFading()
|
|
end
|
|
end
|
|
|
|
function slot:CheckTerms(terms)
|
|
local hits = 0
|
|
local function checkEm(str, term)
|
|
local _, n = str:lower():gsub(term, '')
|
|
hits = hits + n
|
|
end
|
|
local tool = self.Tool
|
|
if tool then
|
|
for term in pairs(terms) do
|
|
checkEm(LocalizedName, term)
|
|
if tool:IsA('Tool') then --NOTE: HopperBin
|
|
checkEm(LocalizedToolTip, term)
|
|
end
|
|
end
|
|
end
|
|
return hits
|
|
end
|
|
|
|
-- Slot select logic, activated by clicking or pressing hotkey
|
|
function slot:Select()
|
|
local tool = slot.Tool
|
|
if tool then
|
|
if IsEquipped(tool) then --NOTE: HopperBin
|
|
UnequipAllTools()
|
|
elseif tool.Parent == Backpack then
|
|
EquipNewTool(tool)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Slot Init Logic --
|
|
|
|
SlotFrame = NewGui('TextButton', index)
|
|
SlotFrame.BackgroundColor3 = BACKGROUND_COLOR
|
|
SlotFrame.BorderColor3 = SLOT_BORDER_COLOR
|
|
SlotFrame.Text = ""
|
|
SlotFrame.AutoButtonColor = false
|
|
SlotFrame.BorderSizePixel = 0
|
|
SlotFrame.Size = UDim2.new(0, ICON_SIZE, 0, ICON_SIZE)
|
|
SlotFrame.Active = true
|
|
SlotFrame.Draggable = false
|
|
SlotFrame.BackgroundTransparency = SLOT_FADE_LOCKED
|
|
SlotFrame.MouseButton1Click:connect(function() changeSlot(slot) end)
|
|
slot.Frame = SlotFrame
|
|
|
|
do
|
|
local selectionObjectClipper = NewGui('Frame', 'SelectionObjectClipper')
|
|
selectionObjectClipper.Visible = false
|
|
selectionObjectClipper.Parent = SlotFrame
|
|
|
|
SelectionObj = NewGui('ImageLabel', 'Selector')
|
|
SelectionObj.Size = UDim2.new(1, 0, 1, 0)
|
|
SelectionObj.Image = "rbxasset://textures/ui/Keyboard/key_selection_9slice.png"
|
|
SelectionObj.ScaleType = Enum.ScaleType.Slice
|
|
SelectionObj.SliceCenter = Rect.new(12,12,52,52)
|
|
SelectionObj.Parent = selectionObjectClipper
|
|
end
|
|
|
|
|
|
ToolIcon = NewGui('ImageLabel', 'Icon')
|
|
ToolIcon.Size = UDim2.new(0.8, 0, 0.8, 0)
|
|
ToolIcon.Position = UDim2.new(0.1, 0, 0.1, 0)
|
|
ToolIcon.Parent = SlotFrame
|
|
|
|
ToolName = NewGui('TextLabel', 'ToolName')
|
|
ToolName.Size = UDim2.new(1, -2, 1, -2)
|
|
ToolName.Position = UDim2.new(0, 1, 0, 1)
|
|
ToolName.Parent = SlotFrame
|
|
|
|
slot.Frame.LayoutOrder = slot.Index
|
|
|
|
if index <= NumberOfHotbarSlots then -- Hotbar-Specific Slot Stuff
|
|
-- ToolTip stuff
|
|
ToolTip = NewGui('TextLabel', 'ToolTip')
|
|
ToolTip.TextWrapped = false
|
|
ToolTip.TextYAlignment = Enum.TextYAlignment.Top
|
|
ToolTip.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
|
|
ToolTip.BackgroundTransparency = 0
|
|
ToolTip.Visible = false
|
|
ToolTip.Parent = SlotFrame
|
|
SlotFrame.MouseEnter:connect(function()
|
|
if ToolTip.Text ~= '' then
|
|
ToolTip.Visible = true
|
|
end
|
|
end)
|
|
SlotFrame.MouseLeave:connect(function() ToolTip.Visible = false end)
|
|
|
|
function slot:MoveToInventory()
|
|
if slot.Index <= NumberOfHotbarSlots then -- From a Hotbar slot
|
|
local tool = slot.Tool
|
|
self:Clear() --NOTE: Order matters here
|
|
local newSlot = MakeSlot(UIGridFrame)
|
|
newSlot:Fill(tool)
|
|
if IsEquipped(tool) then -- Also unequip it --NOTE: HopperBin
|
|
UnequipAllTools()
|
|
end
|
|
-- Also hide the inventory slot if we're showing results right now
|
|
if ViewingSearchResults then
|
|
newSlot.Frame.Visible = false
|
|
newSlot.Parent = InventoryFrame
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Show label and assign hotkeys for 1-9 and 0 (zero is always last slot when > 10 total)
|
|
if index < 10 or index == NumberOfHotbarSlots then -- NOTE: Hardcoded on purpose!
|
|
local slotNum = (index < 10) and index or 0
|
|
SlotNumber = NewGui('TextLabel', 'Number')
|
|
SlotNumber.Text = slotNum
|
|
SlotNumber.Size = UDim2.new(0.15, 0, 0.15, 0)
|
|
SlotNumber.Visible = false
|
|
SlotNumber.Parent = SlotFrame
|
|
HotkeyFns[ZERO_KEY_VALUE + slotNum] = slot.Select
|
|
end
|
|
end
|
|
|
|
do -- Dragging Logic
|
|
local startPoint = SlotFrame.Position
|
|
local lastUpTime = 0
|
|
local startParent = nil
|
|
|
|
SlotFrame.DragBegin:connect(function(dragPoint)
|
|
Dragging[SlotFrame] = true
|
|
startPoint = dragPoint
|
|
|
|
SlotFrame.BorderSizePixel = 2
|
|
|
|
-- Raise above other slots
|
|
SlotFrame.ZIndex = 2
|
|
ToolIcon.ZIndex = 2
|
|
ToolName.ZIndex = 2
|
|
if SlotNumber then
|
|
SlotNumber.ZIndex = 2
|
|
end
|
|
if HighlightFrame then
|
|
HighlightFrame.ZIndex = 2
|
|
for _, child in pairs(HighlightFrame:GetChildren()) do
|
|
child.ZIndex = 2
|
|
end
|
|
end
|
|
|
|
-- Circumvent the ScrollingFrame's ClipsDescendants property
|
|
startParent = SlotFrame.Parent
|
|
if startParent == UIGridFrame then
|
|
local oldAbsolutPos = SlotFrame.AbsolutePosition
|
|
local newPosition = UDim2.new(0, SlotFrame.AbsolutePosition.X - InventoryFrame.AbsolutePosition.X, 0, SlotFrame.AbsolutePosition.Y - InventoryFrame.AbsolutePosition.Y)
|
|
SlotFrame.Parent = InventoryFrame
|
|
SlotFrame.Position = newPosition
|
|
|
|
FakeSlotFrame = NewGui('Frame', 'FakeSlot')
|
|
FakeSlotFrame.LayoutOrder = SlotFrame.LayoutOrder
|
|
FakeSlotFrame.Size = SlotFrame.Size
|
|
FakeSlotFrame.BackgroundTransparency = 1
|
|
FakeSlotFrame.Parent = UIGridFrame
|
|
end
|
|
end)
|
|
|
|
SlotFrame.DragStopped:connect(function(x, y)
|
|
if FakeSlotFrame then
|
|
FakeSlotFrame:Destroy()
|
|
end
|
|
|
|
local now = tick()
|
|
SlotFrame.Position = startPoint
|
|
SlotFrame.Parent = startParent
|
|
|
|
SlotFrame.BorderSizePixel = 0
|
|
|
|
-- Restore height
|
|
SlotFrame.ZIndex = 1
|
|
ToolIcon.ZIndex = 1
|
|
ToolName.ZIndex = 1
|
|
if SlotNumber then
|
|
SlotNumber.ZIndex = 1
|
|
end
|
|
if HighlightFrame then
|
|
HighlightFrame.ZIndex = 1
|
|
for _, child in pairs(HighlightFrame:GetChildren()) do
|
|
child.ZIndex = 1
|
|
end
|
|
end
|
|
|
|
Dragging[SlotFrame] = nil
|
|
|
|
-- Make sure the tool wasn't dropped
|
|
if not slot.Tool then
|
|
return
|
|
end
|
|
|
|
-- Check where we were dropped
|
|
if CheckBounds(InventoryFrame, x, y) then
|
|
if slot.Index <= NumberOfHotbarSlots then
|
|
slot:MoveToInventory()
|
|
end
|
|
-- Check for double clicking on an inventory slot, to move into empty hotbar slot
|
|
if slot.Index > NumberOfHotbarSlots and now - lastUpTime < DOUBLE_CLICK_TIME then
|
|
if LowestEmptySlot then
|
|
local myTool = slot.Tool
|
|
slot:Clear()
|
|
LowestEmptySlot:Fill(myTool)
|
|
slot:Delete()
|
|
end
|
|
now = 0 -- Resets the timer
|
|
end
|
|
elseif CheckBounds(HotbarFrame, x, y) then
|
|
local closest = {math.huge, nil}
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local otherSlot = Slots[i]
|
|
local offset = GetOffset(otherSlot.Frame, Vector2.new(x, y))
|
|
if offset < closest[1] then
|
|
closest = {offset, otherSlot}
|
|
end
|
|
end
|
|
local closestSlot = closest[2]
|
|
if closestSlot ~= slot then
|
|
slot:Swap(closestSlot)
|
|
if slot.Index > NumberOfHotbarSlots then
|
|
local tool = slot.Tool
|
|
if not tool then -- Clean up after ourselves if we're an inventory slot that's now empty
|
|
slot:Delete()
|
|
else -- Moved inventory slot to hotbar slot, and gained a tool that needs to be unequipped
|
|
if IsEquipped(tool) then --NOTE: HopperBin
|
|
UnequipAllTools()
|
|
end
|
|
-- Also hide the inventory slot if we're showing results right now
|
|
if ViewingSearchResults then
|
|
slot.Frame.Visible = false
|
|
slot.Frame.Parent = InventoryFrame
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- local tool = slot.Tool
|
|
-- if tool.CanBeDropped then --TODO: HopperBins
|
|
-- tool.Parent = workspace
|
|
-- --TODO: Move away from character
|
|
-- end
|
|
if slot.Index <= NumberOfHotbarSlots then
|
|
slot:MoveToInventory() --NOTE: Temporary
|
|
end
|
|
end
|
|
|
|
lastUpTime = now
|
|
end)
|
|
end
|
|
|
|
-- All ready!
|
|
SlotFrame.Parent = parent
|
|
Slots[index] = slot
|
|
|
|
if index > NumberOfHotbarSlots then
|
|
UpdateScrollingFrameCanvasSize()
|
|
-- Scroll to new inventory slot, if we're open and not viewing search results
|
|
if InventoryFrame.Visible and not ViewingSearchResults then
|
|
local offset = ScrollingFrame.CanvasSize.Y.Offset - ScrollingFrame.AbsoluteSize.Y
|
|
ScrollingFrame.CanvasPosition = Vector2.new(0, math.max(0, offset))
|
|
end
|
|
end
|
|
|
|
return slot
|
|
end
|
|
|
|
-- NOTE: We should probably migrate to a 2 collection system:
|
|
-- One collection for the hotbar and another collection for the inventory
|
|
local function SetNumberOfHotbarSlots(numSlots)
|
|
if NumberOfHotbarSlots ~= numSlots then
|
|
local prevNumberOfSlots = NumberOfHotbarSlots
|
|
local newNumberOfSlots = numSlots
|
|
-- If we are shrinking the number of slots we need
|
|
-- to move around our tools to the right locations
|
|
if prevNumberOfSlots > newNumberOfSlots then
|
|
-- Delete the slots that are now no longer in the Hotbar
|
|
-- Iterate backwards as to not corrupt our iterator
|
|
for i = prevNumberOfSlots, newNumberOfSlots + 1, -1 do
|
|
local slot = Slots[i]
|
|
if slot then
|
|
slot:MoveToInventory()
|
|
slot:Delete()
|
|
end
|
|
end
|
|
-- Also need to slide-back the inventory slots now that they are indexed earlier
|
|
for i = prevNumberOfSlots, newNumberOfSlots + 1, -1 do
|
|
local slot = Slots[i]
|
|
if not slot.Tool then
|
|
slot:Delete()
|
|
end
|
|
end
|
|
else -- If we added more slots
|
|
for i = prevNumberOfSlots, newNumberOfSlots do
|
|
-- Incrementally add hotbar slots
|
|
NumberOfHotbarSlots = i
|
|
if Slots[i] then
|
|
-- Move old
|
|
local oldSlot = Slots[i]
|
|
local oldTool = Slots[i].Tool
|
|
|
|
local newSlot = MakeSlot(HotbarFrame, i)
|
|
|
|
if oldTool then
|
|
newSlot:Fill(oldTool)
|
|
elseif not LowestEmptySlot then
|
|
LowestEmptySlot = newSlot
|
|
end
|
|
else
|
|
local slot = MakeSlot(HotbarFrame, i)
|
|
|
|
slot.Frame.Visible = false
|
|
|
|
if not LowestEmptySlot then
|
|
LowestEmptySlot = slot
|
|
end
|
|
end
|
|
end
|
|
end
|
|
NumberOfHotbarSlots = numSlots
|
|
FullHotbarSlots = 0
|
|
for i = 1, NumberOfHotbarSlots do
|
|
if Slots[i] and Slots[i].Tool then
|
|
FullHotbarSlots = FullHotbarSlots + 1
|
|
end
|
|
end
|
|
|
|
UpdateBackpackLayout()
|
|
end
|
|
end
|
|
|
|
local function OnChildAdded(child) -- To Character or Backpack
|
|
if not child:IsA('Tool') and not child:IsA('HopperBin') then --NOTE: HopperBin
|
|
if child:IsA('Humanoid') and child.Parent == Character then
|
|
Humanoid = child
|
|
end
|
|
return
|
|
end
|
|
local tool = child
|
|
|
|
if tool.Parent == Character then
|
|
ShowVRBackpackPopup()
|
|
TimeOfLastToolChange = tick()
|
|
end
|
|
|
|
if ActiveHopper and tool.Parent == Character then --NOTE: HopperBin
|
|
DisableActiveHopper()
|
|
end
|
|
|
|
--TODO: Optimize / refactor / do something else
|
|
if not StarterToolFound and tool.Parent == Character and not SlotsByTool[tool] then
|
|
local starterGear = Player:FindFirstChild('StarterGear')
|
|
if starterGear then
|
|
if starterGear:FindFirstChild(tool.Name) then
|
|
StarterToolFound = true
|
|
local slot = LowestEmptySlot or MakeSlot(UIGridFrame)
|
|
for i = slot.Index, 1, -1 do
|
|
local curr = Slots[i] -- An empty slot, because above
|
|
local pIndex = i - 1
|
|
if pIndex > 0 then
|
|
local prev = Slots[pIndex] -- Guaranteed to be full, because above
|
|
prev:Swap(curr)
|
|
else
|
|
curr:Fill(tool)
|
|
end
|
|
end
|
|
-- Have to manually unequip a possibly equipped tool
|
|
for _, child in pairs(Character:GetChildren()) do
|
|
if child:IsA('Tool') and child ~= tool then
|
|
child.Parent = Backpack
|
|
end
|
|
end
|
|
AdjustHotbarFrames()
|
|
return -- We're done here
|
|
end
|
|
end
|
|
end
|
|
|
|
-- The tool is either moving or new
|
|
local slot = SlotsByTool[tool]
|
|
if slot then
|
|
slot:UpdateEquipView()
|
|
else -- New! Put into lowest hotbar slot or new inventory slot
|
|
slot = LowestEmptySlot or MakeSlot(UIGridFrame)
|
|
slot:Fill(tool)
|
|
if slot.Index <= NumberOfHotbarSlots and not InventoryFrame.Visible then
|
|
AdjustHotbarFrames()
|
|
end
|
|
if tool:IsA('HopperBin') then --NOTE: HopperBin
|
|
if tool.Active then
|
|
UnequipAllTools()
|
|
ActiveHopper = tool
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function OnChildRemoved(child) -- From Character or Backpack
|
|
if not child:IsA('Tool') and not child:IsA('HopperBin') then --NOTE: HopperBin
|
|
return
|
|
end
|
|
local tool = child
|
|
|
|
ShowVRBackpackPopup()
|
|
TimeOfLastToolChange = tick()
|
|
|
|
-- Ignore this event if we're just moving between the two
|
|
local newParent = tool.Parent
|
|
if newParent == Character or newParent == Backpack then
|
|
return
|
|
end
|
|
|
|
local slot = SlotsByTool[tool]
|
|
if slot then
|
|
slot:Clear()
|
|
if slot.Index > NumberOfHotbarSlots then -- Inventory slot
|
|
slot:Delete()
|
|
elseif not InventoryFrame.Visible then
|
|
AdjustHotbarFrames()
|
|
end
|
|
end
|
|
|
|
if tool == ActiveHopper then --NOTE: HopperBin
|
|
ActiveHopper = nil
|
|
end
|
|
end
|
|
|
|
local function OnCharacterAdded(character)
|
|
-- First, clean up any old slots
|
|
for i = #Slots, 1, -1 do
|
|
local slot = Slots[i]
|
|
if slot.Tool then
|
|
slot:Clear()
|
|
end
|
|
if i > NumberOfHotbarSlots then
|
|
slot:Delete()
|
|
end
|
|
end
|
|
ActiveHopper = nil --NOTE: HopperBin
|
|
|
|
-- And any old connections
|
|
for _, conn in pairs(CharConns) do
|
|
conn:disconnect()
|
|
end
|
|
CharConns = {}
|
|
|
|
-- Hook up the new character
|
|
Character = character
|
|
table.insert(CharConns, character.ChildRemoved:connect(OnChildRemoved))
|
|
table.insert(CharConns, character.ChildAdded:connect(OnChildAdded))
|
|
for _, child in pairs(character:GetChildren()) do
|
|
OnChildAdded(child)
|
|
end
|
|
--NOTE: Humanoid is set inside OnChildAdded
|
|
|
|
-- And the new backpack, when it gets here
|
|
Backpack = Player:WaitForChild('Backpack')
|
|
table.insert(CharConns, Backpack.ChildRemoved:connect(OnChildRemoved))
|
|
table.insert(CharConns, Backpack.ChildAdded:connect(OnChildAdded))
|
|
for _, child in pairs(Backpack:GetChildren()) do
|
|
OnChildAdded(child)
|
|
end
|
|
|
|
AdjustHotbarFrames()
|
|
end
|
|
|
|
local function OnInputBegan(input, isProcessed)
|
|
-- Pass through keyboard hotkeys when not typing into a TextBox and not disabled (except for the Drop key)
|
|
if input.UserInputType == Enum.UserInputType.Keyboard and not TextBoxFocused and (WholeThingEnabled or input.KeyCode.Value == DROP_HOTKEY_VALUE) then
|
|
local hotkeyBehavior = HotkeyFns[input.KeyCode.Value]
|
|
if hotkeyBehavior then
|
|
hotkeyBehavior(isProcessed)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function OnUISChanged(property)
|
|
if property == 'KeyboardEnabled' or property == "VREnabled" then
|
|
local on = UserInputService.KeyboardEnabled and not UserInputService.VREnabled
|
|
for i = 1, NumberOfHotbarSlots do
|
|
Slots[i]:TurnNumber(on)
|
|
end
|
|
end
|
|
end
|
|
|
|
local lastChangeToolInputObject = nil
|
|
local lastChangeToolInputTime = nil
|
|
local maxEquipDeltaTime = 0.06
|
|
local noOpFunc = function() end
|
|
local selectDirection = Vector2.new(0,0)
|
|
local hotbarVisible = false
|
|
|
|
function unbindAllGamepadEquipActions()
|
|
ContextActionService:UnbindCoreAction("RBXBackpackHasGamepadFocus")
|
|
ContextActionService:UnbindCoreAction("RBXCloseInventory")
|
|
end
|
|
|
|
local function setHotbarVisibility(visible, isInventoryScreen)
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local hotbarSlot = Slots[i]
|
|
if hotbarSlot and hotbarSlot.Frame and (isInventoryScreen or hotbarSlot.Tool) then
|
|
hotbarSlot.Frame.Visible = visible
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getInputDirection(inputObject)
|
|
local buttonModifier = 1
|
|
if inputObject.UserInputState == Enum.UserInputState.End then
|
|
buttonModifier = -1
|
|
end
|
|
|
|
if inputObject.KeyCode == Enum.KeyCode.Thumbstick1 then
|
|
|
|
local magnitude = inputObject.Position.magnitude
|
|
|
|
if magnitude > 0.98 then
|
|
local normalizedVector = Vector2.new(inputObject.Position.x / magnitude, -inputObject.Position.y / magnitude)
|
|
selectDirection = normalizedVector
|
|
else
|
|
selectDirection = Vector2.new(0,0)
|
|
end
|
|
elseif inputObject.KeyCode == Enum.KeyCode.DPadLeft then
|
|
selectDirection = Vector2.new(selectDirection.x - 1 * buttonModifier, selectDirection.y)
|
|
elseif inputObject.KeyCode == Enum.KeyCode.DPadRight then
|
|
selectDirection = Vector2.new(selectDirection.x + 1 * buttonModifier, selectDirection.y)
|
|
elseif inputObject.KeyCode == Enum.KeyCode.DPadUp then
|
|
selectDirection = Vector2.new(selectDirection.x, selectDirection.y - 1 * buttonModifier)
|
|
elseif inputObject.KeyCode == Enum.KeyCode.DPadDown then
|
|
selectDirection = Vector2.new(selectDirection.x, selectDirection.y + 1 * buttonModifier)
|
|
else
|
|
selectDirection = Vector2.new(0,0)
|
|
end
|
|
|
|
return selectDirection
|
|
end
|
|
|
|
local selectToolExperiment = function(actionName, inputState, inputObject)
|
|
local inputDirection = getInputDirection(inputObject)
|
|
|
|
if inputDirection == Vector2.new(0,0) then
|
|
return
|
|
end
|
|
|
|
local angle = math.atan2(inputDirection.y, inputDirection.x) - math.atan2(-1, 0)
|
|
if angle < 0 then
|
|
angle = angle + (math.pi * 2)
|
|
end
|
|
|
|
local quarterPi = (math.pi * 0.25)
|
|
|
|
local index = (angle/quarterPi) + 1
|
|
index = math.floor(index + 0.5) -- round index to whole number
|
|
if index > NumberOfHotbarSlots then
|
|
index = 1
|
|
end
|
|
|
|
if index > 0 then
|
|
local selectedSlot = Slots[index]
|
|
if selectedSlot and selectedSlot.Tool and not selectedSlot:IsEquipped() then
|
|
selectedSlot:Select()
|
|
end
|
|
else
|
|
UnequipAllTools()
|
|
end
|
|
end
|
|
|
|
changeToolFunc = function(actionName, inputState, inputObject)
|
|
if inputState ~= Enum.UserInputState.Begin then return end
|
|
|
|
if lastChangeToolInputObject then
|
|
if (lastChangeToolInputObject.KeyCode == Enum.KeyCode.ButtonR1 and
|
|
inputObject.KeyCode == Enum.KeyCode.ButtonL1) or
|
|
(lastChangeToolInputObject.KeyCode == Enum.KeyCode.ButtonL1 and
|
|
inputObject.KeyCode == Enum.KeyCode.ButtonR1) then
|
|
if (tick() - lastChangeToolInputTime) <= maxEquipDeltaTime then
|
|
UnequipAllTools()
|
|
lastChangeToolInputObject = inputObject
|
|
lastChangeToolInputTime = tick()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
lastChangeToolInputObject = inputObject
|
|
lastChangeToolInputTime = tick()
|
|
|
|
delay(maxEquipDeltaTime, function()
|
|
if lastChangeToolInputObject ~= inputObject then return end
|
|
|
|
local moveDirection = 0
|
|
if (inputObject.KeyCode == Enum.KeyCode.ButtonL1) then
|
|
moveDirection = -1
|
|
else
|
|
moveDirection = 1
|
|
end
|
|
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local hotbarSlot = Slots[i]
|
|
if hotbarSlot:IsEquipped() then
|
|
|
|
local newSlotPosition = moveDirection + i
|
|
local hitEdge = false
|
|
if newSlotPosition > NumberOfHotbarSlots then
|
|
newSlotPosition = 1
|
|
hitEdge = true
|
|
elseif newSlotPosition < 1 then
|
|
newSlotPosition = NumberOfHotbarSlots
|
|
hitEdge = true
|
|
end
|
|
|
|
local origNewSlotPos = newSlotPosition
|
|
while not Slots[newSlotPosition].Tool do
|
|
newSlotPosition = newSlotPosition + moveDirection
|
|
if newSlotPosition == origNewSlotPos then return end
|
|
|
|
if newSlotPosition > NumberOfHotbarSlots then
|
|
newSlotPosition = 1
|
|
hitEdge = true
|
|
elseif newSlotPosition < 1 then
|
|
newSlotPosition = NumberOfHotbarSlots
|
|
hitEdge = true
|
|
end
|
|
end
|
|
|
|
if hitEdge then
|
|
UnequipAllTools()
|
|
lastEquippedSlot = nil
|
|
else
|
|
Slots[newSlotPosition]:Select()
|
|
end
|
|
return
|
|
end
|
|
end
|
|
|
|
if lastEquippedSlot and lastEquippedSlot.Tool then
|
|
lastEquippedSlot:Select()
|
|
return
|
|
end
|
|
|
|
local startIndex = moveDirection == -1 and NumberOfHotbarSlots or 1
|
|
local endIndex = moveDirection == -1 and 1 or NumberOfHotbarSlots
|
|
for i = startIndex, endIndex, moveDirection do
|
|
if Slots[i].Tool then
|
|
Slots[i]:Select()
|
|
return
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function getGamepadSwapSlot()
|
|
for i = 1, #Slots do
|
|
if Slots[i].Frame.BorderSizePixel > 0 then
|
|
return Slots[i]
|
|
end
|
|
end
|
|
end
|
|
|
|
function changeSlot(slot)
|
|
local swapInVr = not VRService.VREnabled or InventoryFrame.Visible
|
|
|
|
if slot.Frame == GuiService.SelectedCoreObject and swapInVr then
|
|
local currentlySelectedSlot = getGamepadSwapSlot()
|
|
|
|
if currentlySelectedSlot then
|
|
currentlySelectedSlot.Frame.BorderSizePixel = 0
|
|
if currentlySelectedSlot ~= slot then
|
|
slot:Swap(currentlySelectedSlot)
|
|
VRInventorySelector.SelectionImageObject.Visible = false
|
|
|
|
if slot.Index > NumberOfHotbarSlots and not slot.Tool then
|
|
if GuiService.SelectedCoreObject == slot.Frame then
|
|
GuiService.SelectedCoreObject = currentlySelectedSlot.Frame
|
|
end
|
|
slot:Delete()
|
|
end
|
|
|
|
if currentlySelectedSlot.Index > NumberOfHotbarSlots and not currentlySelectedSlot.Tool then
|
|
if GuiService.SelectedCoreObject == currentlySelectedSlot.Frame then
|
|
GuiService.SelectedCoreObject = slot.Frame
|
|
end
|
|
currentlySelectedSlot:Delete()
|
|
end
|
|
end
|
|
else
|
|
local startSize = slot.Frame.Size
|
|
local startPosition = slot.Frame.Position
|
|
slot.Frame:TweenSizeAndPosition(startSize + UDim2.new(0, 10, 0, 10), startPosition - UDim2.new(0, 5, 0, 5), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, .1, true, function() slot.Frame:TweenSizeAndPosition(startSize, startPosition, Enum.EasingDirection.In, Enum.EasingStyle.Quad, .1, true) end)
|
|
slot.Frame.BorderSizePixel = 3
|
|
VRInventorySelector.SelectionImageObject.Visible = true
|
|
end
|
|
else
|
|
slot:Select()
|
|
VRInventorySelector.SelectionImageObject.Visible = false
|
|
end
|
|
end
|
|
|
|
function vrMoveSlotToInventory()
|
|
if not VRService.VREnabled then
|
|
return
|
|
end
|
|
|
|
local currentlySelectedSlot = getGamepadSwapSlot()
|
|
if currentlySelectedSlot and currentlySelectedSlot.Tool then
|
|
currentlySelectedSlot.Frame.BorderSizePixel = 0
|
|
currentlySelectedSlot:MoveToInventory()
|
|
VRInventorySelector.SelectionImageObject.Visible = false
|
|
end
|
|
end
|
|
|
|
function enableGamepadInventoryControl()
|
|
local goBackOneLevel = function(actionName, inputState, inputObject)
|
|
if inputState ~= Enum.UserInputState.Begin then return end
|
|
|
|
local selectedSlot = getGamepadSwapSlot()
|
|
if selectedSlot then
|
|
local selectedSlot = getGamepadSwapSlot()
|
|
if selectedSlot then
|
|
selectedSlot.Frame.BorderSizePixel = 0
|
|
return
|
|
end
|
|
elseif InventoryFrame.Visible then
|
|
BackpackScript.OpenClose()
|
|
spawn(function() GuiService:SetMenuIsOpen(false) end)
|
|
end
|
|
end
|
|
|
|
ContextActionService:BindCoreAction("RBXBackpackHasGamepadFocus", noOpFunc, false, Enum.UserInputType.Gamepad1)
|
|
ContextActionService:BindCoreAction("RBXCloseInventory", goBackOneLevel, false, Enum.KeyCode.ButtonB, Enum.KeyCode.ButtonStart)
|
|
|
|
-- Gaze select will automatically select the object for us!
|
|
if not UseGazeSelection() then
|
|
GuiService.SelectedCoreObject = HotbarFrame:FindFirstChild("1")
|
|
end
|
|
end
|
|
|
|
|
|
function disableGamepadInventoryControl()
|
|
unbindAllGamepadEquipActions()
|
|
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local hotbarSlot = Slots[i]
|
|
if hotbarSlot and hotbarSlot.Frame then
|
|
hotbarSlot.Frame.BorderSizePixel = 0
|
|
end
|
|
end
|
|
|
|
if GuiService.SelectedCoreObject and GuiService.SelectedCoreObject:IsDescendantOf(MainFrame) then
|
|
GuiService.SelectedCoreObject = nil
|
|
end
|
|
end
|
|
|
|
|
|
local function bindBackpackHotbarAction()
|
|
if WholeThingEnabled and not GamepadActionsBound then
|
|
GamepadActionsBound = true
|
|
ContextActionService:BindCoreAction("RBXHotbarEquip", changeToolFunc, false, Enum.KeyCode.ButtonL1, Enum.KeyCode.ButtonR1)
|
|
end
|
|
end
|
|
|
|
local function unbindBackpackHotbarAction()
|
|
disableGamepadInventoryControl()
|
|
GamepadActionsBound = false
|
|
ContextActionService:UnbindCoreAction("RBXHotbarEquip")
|
|
end
|
|
|
|
function gamepadDisconnected()
|
|
GamepadEnabled = false
|
|
disableGamepadInventoryControl()
|
|
end
|
|
|
|
function gamepadConnected()
|
|
GamepadEnabled = true
|
|
GuiService:AddSelectionParent("RBXBackpackSelection", MainFrame)
|
|
|
|
if FullHotbarSlots >= 1 then
|
|
bindBackpackHotbarAction()
|
|
end
|
|
|
|
if InventoryFrame.Visible then
|
|
enableGamepadInventoryControl()
|
|
end
|
|
end
|
|
|
|
local function OnCoreGuiChanged(coreGuiType, enabled)
|
|
-- Check for enabling/disabling the whole thing
|
|
if coreGuiType == Enum.CoreGuiType.Backpack or coreGuiType == Enum.CoreGuiType.All then
|
|
enabled = enabled and TopbarEnabled
|
|
WholeThingEnabled = enabled
|
|
MainFrame.Visible = enabled
|
|
|
|
-- Eat/Release hotkeys (Doesn't affect UserInputService)
|
|
for _, keyString in pairs(HotkeyStrings) do
|
|
if enabled then
|
|
GuiService:AddKey(keyString)
|
|
else
|
|
GuiService:RemoveKey(keyString)
|
|
end
|
|
end
|
|
|
|
if enabled then
|
|
if FullHotbarSlots >=1 then
|
|
bindBackpackHotbarAction()
|
|
end
|
|
else
|
|
unbindBackpackHotbarAction()
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
local function MakeVRRoundButton(name, image)
|
|
local newButton = NewGui('ImageButton', name)
|
|
newButton.Size = UDim2.new(0, 40, 0, 40)
|
|
newButton.Image = "rbxasset://textures/ui/Keyboard/close_button_background.png";
|
|
|
|
local buttonIcon = NewGui('ImageLabel', 'Icon')
|
|
buttonIcon.Size = UDim2.new(0.5,0,0.5,0);
|
|
buttonIcon.Position = UDim2.new(0.25,0,0.25,0);
|
|
buttonIcon.Image = image;
|
|
buttonIcon.Parent = newButton;
|
|
|
|
local buttonSelectionObject = NewGui('ImageLabel', 'Selection')
|
|
buttonSelectionObject.Size = UDim2.new(0.9,0,0.9,0);
|
|
buttonSelectionObject.Position = UDim2.new(0.05,0,0.05,0);
|
|
buttonSelectionObject.Image = "rbxasset://textures/ui/Keyboard/close_button_selection.png";
|
|
newButton.SelectionImageObject = buttonSelectionObject
|
|
|
|
return newButton, buttonIcon, buttonSelectionObject
|
|
end
|
|
|
|
|
|
-- Make the main frame, which (mostly) covers the screen
|
|
MainFrame = NewGui('Frame', 'Backpack')
|
|
MainFrame.Visible = false
|
|
MainFrame.Parent = RobloxGui
|
|
|
|
-- Make the HotbarFrame, which holds only the Hotbar Slots
|
|
HotbarFrame = NewGui('Frame', 'Hotbar')
|
|
HotbarFrame.Parent = MainFrame
|
|
|
|
-- Make all the Hotbar Slots
|
|
for i = 1, NumberOfHotbarSlots do
|
|
local slot = MakeSlot(HotbarFrame, i)
|
|
slot.Frame.Visible = false
|
|
|
|
if not LowestEmptySlot then
|
|
LowestEmptySlot = slot
|
|
end
|
|
end
|
|
|
|
-- Up arrow to open the inventory
|
|
OpenInventoryButton = NewGui('ImageButton', 'OpenInventory')
|
|
do
|
|
OpenInventoryButton.Size = UDim2.new(0, 30, 0, 30)
|
|
OpenInventoryButton.Image = "rbxasset://textures/ui/Backpack/ScrollUpArrow.png";
|
|
OpenInventoryButton.MouseButton1Click:connect(function()
|
|
BackpackScript.OpenClose()
|
|
end)
|
|
OpenInventoryButton.SelectionGained:connect(function()
|
|
OpenInventoryButton.ImageColor3 = ARROW_HOVER_COLOR
|
|
end)
|
|
OpenInventoryButton.SelectionLost:connect(function()
|
|
OpenInventoryButton.ImageColor3 = Color3.new(1,1,1)
|
|
end)
|
|
local openInventoryButtonSelectionObject = NewGui('Frame', 'Selection')
|
|
openInventoryButtonSelectionObject.Visible = false
|
|
OpenInventoryButton.SelectionImageObject = openInventoryButtonSelectionObject
|
|
end
|
|
|
|
CloseInventoryButton = MakeVRRoundButton('CloseInventory', 'rbxasset://textures/ui/Keyboard/close_button_icon.png')
|
|
CloseInventoryButton.Position = UDim2.new(0, 0, 0, -50)
|
|
CloseInventoryButton.MouseButton1Click:connect(function()
|
|
if InventoryFrame.Visible then
|
|
BackpackScript.OpenClose()
|
|
spawn(function() GuiService:SetMenuIsOpen(false) end)
|
|
end
|
|
end)
|
|
|
|
LeftBumperButton = NewGui('ImageLabel', 'LeftBumper')
|
|
LeftBumperButton.Size = UDim2.new(0, 40, 0, 40)
|
|
LeftBumperButton.Position = UDim2.new(0, -LeftBumperButton.Size.X.Offset, 0.5, -LeftBumperButton.Size.Y.Offset/2)
|
|
|
|
RightBumperButton = NewGui('ImageLabel', 'RightBumper')
|
|
RightBumperButton.Size = UDim2.new(0, 40, 0, 40)
|
|
RightBumperButton.Position = UDim2.new(1, 0, 0.5, -RightBumperButton.Size.Y.Offset/2)
|
|
|
|
-- Make the Inventory, which holds the ScrollingFrame, the header, and the search box
|
|
InventoryFrame = NewGui('Frame', 'Inventory')
|
|
InventoryFrame.BackgroundTransparency = BACKGROUND_FADE
|
|
InventoryFrame.BackgroundColor3 = BACKGROUND_COLOR
|
|
InventoryFrame.Active = true
|
|
InventoryFrame.Visible = false
|
|
InventoryFrame.Parent = MainFrame
|
|
|
|
VRInventorySelector = NewGui('TextButton', 'VRInventorySelector')
|
|
VRInventorySelector.Position = UDim2.new(0, 0, 0, 0)
|
|
VRInventorySelector.Size = UDim2.new(1, 0, 1, 0)
|
|
VRInventorySelector.BackgroundTransparency = 1
|
|
VRInventorySelector.Text = ""
|
|
VRInventorySelector.Parent = InventoryFrame
|
|
|
|
local selectorImage = NewGui('ImageLabel', 'Selector')
|
|
selectorImage.Size = UDim2.new(1, 0, 1, 0)
|
|
selectorImage.Image = "rbxasset://textures/ui/Keyboard/key_selection_9slice.png"
|
|
selectorImage.ScaleType = Enum.ScaleType.Slice
|
|
selectorImage.SliceCenter = Rect.new(12,12,52,52)
|
|
selectorImage.Visible = false
|
|
VRInventorySelector.SelectionImageObject = selectorImage
|
|
|
|
VRInventorySelector.MouseButton1Click:connect(function()
|
|
vrMoveSlotToInventory()
|
|
end)
|
|
|
|
-- Make the ScrollingFrame, which holds the rest of the Slots (however many)
|
|
ScrollingFrame = NewGui('ScrollingFrame', 'ScrollingFrame')
|
|
ScrollingFrame.Selectable = false
|
|
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
|
|
ScrollingFrame.Parent = InventoryFrame
|
|
|
|
UIGridFrame = NewGui('Frame', 'UIGridFrame')
|
|
UIGridFrame.Selectable = false
|
|
UIGridFrame.Size = UDim2.new(1, -(ICON_BUFFER*2), 1, 0)
|
|
UIGridFrame.Position = UDim2.new(0, ICON_BUFFER, 0, 0)
|
|
UIGridFrame.Parent = ScrollingFrame
|
|
|
|
UIGridLayout = Instance.new("UIGridLayout")
|
|
UIGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
|
|
UIGridLayout.CellSize = UDim2.new(0, ICON_SIZE, 0, ICON_SIZE)
|
|
UIGridLayout.CellPadding = UDim2.new(0, ICON_BUFFER, 0, ICON_BUFFER)
|
|
UIGridLayout.Parent = UIGridFrame
|
|
|
|
ScrollUpInventoryButton = MakeVRRoundButton('ScrollUpButton', 'rbxasset://textures/ui/Backpack/ScrollUpArrow.png')
|
|
ScrollUpInventoryButton.Size = UDim2.new(0, 34, 0, 34)
|
|
ScrollUpInventoryButton.Position = UDim2.new(0.5, -ScrollUpInventoryButton.Size.X.Offset/2, 0, INVENTORY_HEADER_SIZE + 3)
|
|
ScrollUpInventoryButton.Icon.Position = ScrollUpInventoryButton.Icon.Position - UDim2.new(0,0,0,2)
|
|
ScrollUpInventoryButton.MouseButton1Click:connect(function()
|
|
ScrollingFrame.CanvasPosition = Vector2.new(
|
|
ScrollingFrame.CanvasPosition.X,
|
|
Clamp(0, ScrollingFrame.CanvasSize.Y.Offset - ScrollingFrame.AbsoluteWindowSize.Y, ScrollingFrame.CanvasPosition.Y - (ICON_BUFFER + ICON_SIZE)))
|
|
end)
|
|
|
|
ScrollDownInventoryButton = MakeVRRoundButton('ScrollDownButton', 'rbxasset://textures/ui/Backpack/ScrollUpArrow.png')
|
|
ScrollDownInventoryButton.Rotation = 180
|
|
ScrollDownInventoryButton.Icon.Position = ScrollDownInventoryButton.Icon.Position - UDim2.new(0,0,0,2)
|
|
ScrollDownInventoryButton.Size = UDim2.new(0, 34, 0, 34)
|
|
ScrollDownInventoryButton.Position = UDim2.new(0.5, -ScrollDownInventoryButton.Size.X.Offset/2, 1, -ScrollDownInventoryButton.Size.Y.Offset - 3)
|
|
ScrollDownInventoryButton.MouseButton1Click:connect(function()
|
|
ScrollingFrame.CanvasPosition = Vector2.new(
|
|
ScrollingFrame.CanvasPosition.X,
|
|
Clamp(0, ScrollingFrame.CanvasSize.Y.Offset - ScrollingFrame.AbsoluteWindowSize.Y, ScrollingFrame.CanvasPosition.Y + (ICON_BUFFER + ICON_SIZE)))
|
|
end)
|
|
|
|
ScrollingFrame.Changed:connect(function(prop)
|
|
if prop == 'AbsoluteWindowSize' or prop == 'CanvasPosition' or prop == 'CanvasSize' then
|
|
local canScrollUp = ScrollingFrame.CanvasPosition.Y ~= 0
|
|
local canScrollDown = ScrollingFrame.CanvasPosition.Y < ScrollingFrame.CanvasSize.Y.Offset - ScrollingFrame.AbsoluteWindowSize.Y
|
|
|
|
ScrollUpInventoryButton.Visible = canScrollUp
|
|
ScrollDownInventoryButton.Visible = canScrollDown
|
|
end
|
|
end)
|
|
|
|
-- Position the frames and sizes for the Backpack GUI elements
|
|
UpdateBackpackLayout()
|
|
|
|
--Make the gamepad hint frame
|
|
local gamepadHintsFrame = Utility:Create'Frame'
|
|
{
|
|
Name = "GamepadHintsFrame",
|
|
Size = UDim2.new(0, HotbarFrame.Size.X.Offset, 0, (IsTenFootInterface and 95 or 60)),
|
|
BackgroundTransparency = 1,
|
|
Visible = false,
|
|
Parent = MainFrame
|
|
}
|
|
|
|
local function addGamepadHint(hintImage, hintImageLarge, hintText)
|
|
local hintFrame = Utility:Create'Frame'
|
|
{
|
|
Name = "HintFrame",
|
|
Size = UDim2.new(1, 0, 1, -5),
|
|
Position = UDim2.new(0, 0, 0, 0),
|
|
BackgroundTransparency = 1,
|
|
Parent = gamepadHintsFrame
|
|
}
|
|
|
|
local hintImage = Utility:Create'ImageLabel'
|
|
{
|
|
Name = "HintImage",
|
|
Size = (IsTenFootInterface and UDim2.new(0,90,0,90) or UDim2.new(0,60,0,60)),
|
|
BackgroundTransparency = 1,
|
|
Image = (IsTenFootInterface and hintImageLarge or hintImage),
|
|
Parent = hintFrame
|
|
}
|
|
|
|
local hintText = Utility:Create'TextLabel'
|
|
{
|
|
Name = "HintText",
|
|
Position = UDim2.new(0, (IsTenFootInterface and 100 or 70), 0, 0),
|
|
Size = UDim2.new(1, -(IsTenFootInterface and 100 or 70), 1, 0),
|
|
Font = Enum.Font.SourceSansBold,
|
|
FontSize = (IsTenFootInterface and Enum.FontSize.Size36 or Enum.FontSize.Size24),
|
|
BackgroundTransparency = 1,
|
|
Text = hintText,
|
|
TextColor3 = Color3.new(1,1,1),
|
|
TextXAlignment = Enum.TextXAlignment.Left,
|
|
Parent = hintFrame
|
|
}
|
|
local textSizeConstraint = Instance.new("UITextSizeConstraint", hintText)
|
|
textSizeConstraint.MaxTextSize = hintText.TextSize
|
|
end
|
|
|
|
local function resizeGamepadHintsFrame()
|
|
gamepadHintsFrame.Size = UDim2.new(HotbarFrame.Size.X.Scale, HotbarFrame.Size.X.Offset, 0, (IsTenFootInterface and 95 or 60))
|
|
gamepadHintsFrame.Position = UDim2.new(HotbarFrame.Position.X.Scale, HotbarFrame.Position.X.Offset, InventoryFrame.Position.Y.Scale, InventoryFrame.Position.Y.Offset - gamepadHintsFrame.Size.Y.Offset)
|
|
|
|
local spaceTaken = 0
|
|
|
|
local gamepadHints = gamepadHintsFrame:GetChildren()
|
|
--First get the total space taken by all the hints
|
|
for i = 1, #gamepadHints do
|
|
gamepadHints[i].Size = UDim2.new(1, 0, 1, -5)
|
|
gamepadHints[i].Position = UDim2.new(0, 0, 0, 0)
|
|
spaceTaken = spaceTaken + (gamepadHints[i].HintText.Position.X.Offset + gamepadHints[i].HintText.TextBounds.X)
|
|
end
|
|
|
|
--The space between all the frames should be equal
|
|
local spaceBetweenElements = (gamepadHintsFrame.AbsoluteSize.X - spaceTaken)/(#gamepadHints - 1)
|
|
for i = 1, #gamepadHints do
|
|
gamepadHints[i].Position = (i == 1 and UDim2.new(0, 0, 0, 0) or UDim2.new(0, gamepadHints[i-1].Position.X.Offset + gamepadHints[i-1].Size.X.Offset + spaceBetweenElements, 0, 0))
|
|
gamepadHints[i].Size = UDim2.new(0, (gamepadHints[i].HintText.Position.X.Offset + gamepadHints[i].HintText.TextBounds.X), 1, -5)
|
|
end
|
|
end
|
|
|
|
addGamepadHint("rbxasset://textures/ui/Settings/Help/XButtonDark.png", "rbxasset://textures/ui/Settings/Help/XButtonDark@2x.png", "Remove From Hotbar")
|
|
addGamepadHint("rbxasset://textures/ui/Settings/Help/AButtonDark.png", "rbxasset://textures/ui/Settings/Help/AButtonDark@2x.png", "Select/Swap")
|
|
addGamepadHint("rbxasset://textures/ui/Settings/Help/BButtonDark.png", "rbxasset://textures/ui/Settings/Help/BButtonDark@2x.png", "Close Backpack")
|
|
|
|
do -- Search stuff
|
|
local searchFrame = NewGui('Frame', 'Search')
|
|
searchFrame.BackgroundColor3 = SEARCH_BACKGROUND_COLOR
|
|
searchFrame.BackgroundTransparency = SEARCH_BACKGROUND_FADE
|
|
searchFrame.Size = UDim2.new(0, SEARCH_WIDTH - (SEARCH_BUFFER * 2), 0, INVENTORY_HEADER_SIZE - (SEARCH_BUFFER * 2))
|
|
searchFrame.Position = UDim2.new(1, -searchFrame.Size.X.Offset - SEARCH_BUFFER, 0, SEARCH_BUFFER)
|
|
searchFrame.Parent = InventoryFrame
|
|
|
|
local searchBox = NewGui('TextBox', 'TextBox')
|
|
searchBox.Text = SEARCH_TEXT
|
|
searchBox.ClearTextOnFocus = false
|
|
searchBox.FontSize = Enum.FontSize.Size24
|
|
searchBox.TextXAlignment = Enum.TextXAlignment.Left
|
|
searchBox.Size = searchFrame.Size - UDim2.new(0, SEARCH_TEXT_OFFSET_FROMLEFT, 0, 0)
|
|
searchBox.Position = UDim2.new(0, SEARCH_TEXT_OFFSET_FROMLEFT, 0, 0)
|
|
searchBox.Parent = searchFrame
|
|
|
|
local xButton = NewGui('TextButton', 'X')
|
|
xButton.Text = 'x'
|
|
xButton.TextColor3 = SLOT_EQUIP_COLOR
|
|
xButton.FontSize = Enum.FontSize.Size24
|
|
xButton.TextYAlignment = Enum.TextYAlignment.Bottom
|
|
xButton.BackgroundColor3 = SEARCH_BACKGROUND_COLOR
|
|
xButton.BackgroundTransparency = 0
|
|
xButton.Size = UDim2.new(0, searchFrame.Size.Y.Offset - (SEARCH_BUFFER * 2), 0, searchFrame.Size.Y.Offset - (SEARCH_BUFFER * 2))
|
|
xButton.Position = UDim2.new(1, -xButton.Size.X.Offset - (SEARCH_BUFFER * 2), 0.5, -xButton.Size.Y.Offset / 2)
|
|
xButton.ZIndex = 0
|
|
xButton.Visible = false
|
|
xButton.BorderSizePixel = 0
|
|
xButton.Parent = searchFrame
|
|
|
|
local function search()
|
|
local terms = {}
|
|
for word in searchBox.Text:gmatch('%S+') do
|
|
terms[word:lower()] = true
|
|
end
|
|
|
|
local hitTable = {}
|
|
for i = NumberOfHotbarSlots + 1, #Slots do -- Only search inventory slots
|
|
local slot = Slots[i]
|
|
local hits = slot:CheckTerms(terms)
|
|
table.insert(hitTable, {slot, hits})
|
|
slot.Frame.Visible = false
|
|
slot.Frame.Parent = InventoryFrame
|
|
end
|
|
|
|
table.sort(hitTable, function(left, right)
|
|
return left[2] > right[2]
|
|
end)
|
|
ViewingSearchResults = true
|
|
|
|
local hitCount = 0
|
|
for i, data in ipairs(hitTable) do
|
|
local slot, hits = data[1], data[2]
|
|
if hits > 0 then
|
|
slot.Frame.Visible = true
|
|
slot.Frame.Parent = UIGridFrame
|
|
slot.Frame.LayoutOrder = NumberOfHotbarSlots + hitCount
|
|
hitCount = hitCount + 1
|
|
end
|
|
end
|
|
|
|
ScrollingFrame.CanvasPosition = Vector2.new(0, 0)
|
|
UpdateScrollingFrameCanvasSize()
|
|
|
|
xButton.ZIndex = 3
|
|
end
|
|
|
|
local function clearResults()
|
|
if xButton.ZIndex > 0 then
|
|
ViewingSearchResults = false
|
|
for i = NumberOfHotbarSlots + 1, #Slots do
|
|
local slot = Slots[i]
|
|
slot.Frame.LayoutOrder = slot.Index
|
|
slot.Frame.Parent = UIGridFrame
|
|
slot.Frame.Visible = true
|
|
end
|
|
xButton.ZIndex = 0
|
|
end
|
|
UpdateScrollingFrameCanvasSize()
|
|
end
|
|
|
|
local function reset()
|
|
clearResults()
|
|
searchBox.Text = SEARCH_TEXT
|
|
end
|
|
|
|
local function onChanged(property)
|
|
if property == 'Text' then
|
|
local text = searchBox.Text
|
|
if text == '' then
|
|
clearResults()
|
|
elseif text ~= SEARCH_TEXT then
|
|
search()
|
|
end
|
|
xButton.Visible = (text ~= '' and text ~= SEARCH_TEXT)
|
|
end
|
|
end
|
|
|
|
local function onFocused()
|
|
if searchBox.Text == SEARCH_TEXT then
|
|
searchBox.Text = ''
|
|
end
|
|
end
|
|
|
|
local function focusLost(enterPressed)
|
|
if enterPressed then
|
|
--TODO: Could optimize
|
|
search()
|
|
elseif searchBox.Text == '' then
|
|
searchBox.Text = SEARCH_TEXT
|
|
end
|
|
end
|
|
|
|
searchBox.Focused:connect(onFocused)
|
|
xButton.MouseButton1Click:connect(reset)
|
|
searchBox.Changed:connect(onChanged)
|
|
searchBox.FocusLost:connect(focusLost)
|
|
|
|
BackpackScript.StateChanged.Event:connect(function(isNowOpen)
|
|
xButton.Modal = isNowOpen -- Allows free mouse movement even in first person
|
|
if not isNowOpen then
|
|
reset()
|
|
end
|
|
end)
|
|
|
|
HotkeyFns[Enum.KeyCode.Escape.Value] = function(isProcessed)
|
|
if isProcessed then -- Pressed from within a TextBox
|
|
reset()
|
|
elseif InventoryFrame.Visible then
|
|
BackpackScript.OpenClose()
|
|
end
|
|
end
|
|
|
|
local function detectGamepad(lastInputType)
|
|
if lastInputType == Enum.UserInputType.Gamepad1 and not UserInputService.VREnabled then
|
|
searchFrame.Visible = false
|
|
else
|
|
searchFrame.Visible = true
|
|
end
|
|
end
|
|
UserInputService.LastInputTypeChanged:connect(detectGamepad)
|
|
end
|
|
|
|
do -- Make the Inventory expand/collapse arrow (unless TopBar)
|
|
local removeHotBarSlot = function(name, state, input)
|
|
if state ~= Enum.UserInputState.Begin then return end
|
|
if not GuiService.SelectedCoreObject then return end
|
|
|
|
for i = 1, NumberOfHotbarSlots do
|
|
if Slots[i].Frame == GuiService.SelectedCoreObject and Slots[i].Tool then
|
|
Slots[i]:MoveToInventory()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local function openClose()
|
|
if not next(Dragging) then -- Only continue if nothing is being dragged
|
|
InventoryFrame.Visible = not InventoryFrame.Visible
|
|
local nowOpen = InventoryFrame.Visible
|
|
AdjustHotbarFrames()
|
|
HotbarFrame.Active = not HotbarFrame.Active
|
|
for i = 1, NumberOfHotbarSlots do
|
|
Slots[i]:SetClickability(not nowOpen)
|
|
end
|
|
end
|
|
|
|
if InventoryFrame.Visible then
|
|
if GamepadEnabled then
|
|
if GAMEPAD_INPUT_TYPES[UserInputService:GetLastInputType()] then
|
|
resizeGamepadHintsFrame()
|
|
gamepadHintsFrame.Visible = not UserInputService.VREnabled
|
|
end
|
|
enableGamepadInventoryControl()
|
|
end
|
|
if BackpackPanel and VRService.VREnabled then
|
|
BackpackPanel:SetVisible(true)
|
|
BackpackPanel:RequestPositionUpdate()
|
|
end
|
|
else
|
|
if GamepadEnabled then
|
|
gamepadHintsFrame.Visible = false
|
|
end
|
|
disableGamepadInventoryControl()
|
|
end
|
|
|
|
if InventoryFrame.Visible then
|
|
ContextActionService:BindCoreAction("RBXRemoveSlot", removeHotBarSlot, false, Enum.KeyCode.ButtonX)
|
|
else
|
|
ContextActionService:UnbindCoreAction("RBXRemoveSlot")
|
|
end
|
|
|
|
BackpackScript.IsOpen = InventoryFrame.Visible
|
|
BackpackScript.StateChanged:Fire(InventoryFrame.Visible)
|
|
end
|
|
HotkeyFns[ARROW_HOTKEY] = openClose
|
|
BackpackScript.OpenClose = openClose -- Exposed
|
|
end
|
|
|
|
-- Now that we're done building the GUI, we connect to all the major events
|
|
|
|
-- Wait for the player if LocalPlayer wasn't ready earlier
|
|
while not Player do
|
|
wait()
|
|
Player = PlayersService.LocalPlayer
|
|
end
|
|
|
|
-- Listen to current and all future characters of our player
|
|
Player.CharacterAdded:connect(OnCharacterAdded)
|
|
if Player.Character then
|
|
OnCharacterAdded(Player.Character)
|
|
end
|
|
|
|
do -- Hotkey stuff
|
|
-- Init HotkeyStrings, used for eating hotkeys
|
|
for i = 0, 9 do
|
|
table.insert(HotkeyStrings, tostring(i))
|
|
end
|
|
table.insert(HotkeyStrings, ARROW_HOTKEY_STRING)
|
|
|
|
-- Listen to key down
|
|
UserInputService.InputBegan:connect(OnInputBegan)
|
|
|
|
-- Listen to ANY TextBox gaining or losing focus, for disabling all hotkeys
|
|
UserInputService.TextBoxFocused:connect(function() TextBoxFocused = true end)
|
|
UserInputService.TextBoxFocusReleased:connect(function() TextBoxFocused = false end)
|
|
|
|
-- Manual unequip for HopperBins on drop button pressed
|
|
HotkeyFns[DROP_HOTKEY_VALUE] = function() --NOTE: HopperBin
|
|
if ActiveHopper then
|
|
UnequipAllTools()
|
|
end
|
|
end
|
|
|
|
-- Listen to keyboard status, for showing/hiding hotkey labels
|
|
UserInputService.Changed:connect(OnUISChanged)
|
|
OnUISChanged('KeyboardEnabled')
|
|
|
|
-- Listen to gamepad status, for allowing gamepad style selection/equip
|
|
if UserInputService:GetGamepadConnected(Enum.UserInputType.Gamepad1) then
|
|
gamepadConnected()
|
|
end
|
|
UserInputService.GamepadConnected:connect(function(gamepadEnum)
|
|
if gamepadEnum == Enum.UserInputType.Gamepad1 then
|
|
gamepadConnected()
|
|
end
|
|
end)
|
|
UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
|
|
if gamepadEnum == Enum.UserInputType.Gamepad1 then
|
|
gamepadDisconnected()
|
|
end
|
|
end)
|
|
end
|
|
|
|
function BackpackScript:TopbarEnabledChanged(enabled)
|
|
TopbarEnabled = enabled
|
|
-- Update coregui to reflect new topbar status
|
|
OnCoreGuiChanged(Enum.CoreGuiType.Backpack, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack))
|
|
end
|
|
|
|
-- Listen to enable/disable signals from the StarterGui
|
|
StarterGui.CoreGuiChangedSignal:connect(OnCoreGuiChanged)
|
|
local backpackType, healthType = Enum.CoreGuiType.Backpack, Enum.CoreGuiType.Health
|
|
OnCoreGuiChanged(backpackType, StarterGui:GetCoreGuiEnabled(backpackType))
|
|
OnCoreGuiChanged(healthType, StarterGui:GetCoreGuiEnabled(healthType))
|
|
|
|
|
|
local BackpackStateChangedInVRConn, VRModuleOpenedConn, VRModuleClosedConn = nil, nil, nil
|
|
local function OnVREnabled()
|
|
local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
|
|
|
|
IsVR = VRService.VREnabled
|
|
OnCoreGuiChanged(backpackType, StarterGui:GetCoreGuiEnabled(backpackType))
|
|
OnCoreGuiChanged(healthType, StarterGui:GetCoreGuiEnabled(healthType))
|
|
|
|
VRInventorySelector.Visible = IsVR
|
|
|
|
if IsVR then
|
|
local VRHub = require(RobloxGui.Modules.VR.VRHub)
|
|
|
|
local slotsToStuds = (ICON_SIZE + ICON_BUFFER) / VR_PANEL_RESOLUTION
|
|
local inventoryOpenStudSize = Vector2.new(6.25, 7.2) * slotsToStuds
|
|
local inventoryClosedStudSize = Vector2.new(6.25, 2) * slotsToStuds -- Closed size is computed as numberOfHotbarSlots + 0.25
|
|
local inventoryOpenPanelCF = CFrame.new(0, 0.6, 0)
|
|
local inventoryClosedPanelCF = CFrame.new(0, -1, 0)
|
|
local currentPanelLocalCF = inventoryClosedPanelCF
|
|
|
|
VRHub:RegisterModule(BackpackScript)
|
|
|
|
BackpackPanel = Panel3D.Get(BackpackScript.ModuleName)
|
|
BackpackPanel:ResizeStuds(inventoryClosedStudSize.x, inventoryClosedStudSize.y, VR_PANEL_RESOLUTION)
|
|
BackpackPanel:SetType(Panel3D.Type.Standard, { CFrame = currentPanelLocalCF })
|
|
BackpackPanel:RequestPositionUpdate()
|
|
local panelOriginCF = CFrame.new()
|
|
function BackpackPanel:CalculateTransparency()
|
|
if InventoryFrame.Visible then
|
|
return 0
|
|
end
|
|
|
|
local now = tick()
|
|
local timeSinceToolChange = now - TimeOfLastToolChange
|
|
local transparency = math.clamp(timeSinceToolChange / VR_FADE_TIME, 0, 1)
|
|
|
|
if transparency == 1 and BackpackPanel:IsVisible() and not InventoryFrame.Visible then
|
|
BackpackPanel:SetVisible(false)
|
|
end
|
|
|
|
return transparency
|
|
end
|
|
|
|
function BackpackPanel:PreUpdate()
|
|
local inventoryOpen = InventoryFrame.Visible
|
|
BackpackPanel.localCF = inventoryOpen and inventoryOpenPanelCF or inventoryClosedPanelCF
|
|
end
|
|
|
|
function BackpackPanel:OnUpdate()
|
|
local inventoryOpen = InventoryFrame.Visible
|
|
if not inventoryOpen then
|
|
BackpackPanel:ResizeStuds((FullHotbarSlots + 0.25) * slotsToStuds, inventoryClosedStudSize.y, VR_PANEL_RESOLUTION)
|
|
end
|
|
|
|
-- Update transparency
|
|
for i = 1, #Slots do
|
|
local slot = Slots[i]
|
|
if slot then
|
|
slot:UpdateEquipView()
|
|
end
|
|
end
|
|
OpenInventoryButton.ImageTransparency = BackpackPanel.transparency
|
|
CloseInventoryButton.ImageTransparency = BackpackPanel.transparency
|
|
end
|
|
|
|
MainFrame.Parent = BackpackPanel:GetGUI()
|
|
OpenInventoryButton.Parent = MainFrame
|
|
CloseInventoryButton.Parent = InventoryFrame
|
|
|
|
ScrollUpInventoryButton.Parent = InventoryFrame
|
|
ScrollDownInventoryButton.Parent = InventoryFrame
|
|
-- Stop the ScrollingFrame from automatically scrolling when you hover over items
|
|
ScrollingFrame.ScrollingEnabled = false
|
|
|
|
BackpackStateChangedInVRConn = BackpackScript.StateChanged.Event:connect(function(isNowOpen)
|
|
if isNowOpen then
|
|
VRHub:FireModuleOpened(BackpackScript.ModuleName)
|
|
BackpackPanel:ResizeStuds(inventoryOpenStudSize.x, inventoryOpenStudSize.y, VR_PANEL_RESOLUTION)
|
|
BackpackPanel:SetCanFade(false)
|
|
else
|
|
VRHub:FireModuleClosed(BackpackScript.ModuleName)
|
|
BackpackPanel:ResizeStuds(inventoryClosedStudSize.x, inventoryClosedStudSize.y, VR_PANEL_RESOLUTION)
|
|
BackpackPanel:SetCanFade(true)
|
|
end
|
|
end)
|
|
|
|
VRModuleOpenedConn = VRHub.ModuleOpened.Event:connect(function(moduleName)
|
|
local openedModule = VRHub:GetModule(moduleName)
|
|
if openedModule ~= BackpackScript and openedModule.VRIsExclusive then
|
|
BackpackPanel:SetVisible(EvaluateBackpackPanelVisibility(false))
|
|
if InventoryFrame.Visible then
|
|
BackpackScript.OpenClose()
|
|
end
|
|
end
|
|
end)
|
|
VRModuleClosedConn = VRHub.ModuleClosed.Event:connect(function(moduleName)
|
|
local openedModule = VRHub:GetModule(moduleName)
|
|
if openedModule ~= BackpackScript then
|
|
BackpackPanel:SetVisible(EvaluateBackpackPanelVisibility(true))
|
|
end
|
|
end)
|
|
|
|
|
|
-- Turn off dragging when in VR
|
|
for _, slot in pairs(Slots) do
|
|
slot:SetClickability(false)
|
|
end
|
|
else -- not IsVR (VR was turned off)
|
|
local BackpackPanel = Panel3D.Get(BackpackScript.ModuleName)
|
|
BackpackPanel:SetVisible(EvaluateBackpackPanelVisibility(false))
|
|
BackpackPanel:LinkTo(nil)
|
|
|
|
MainFrame.Parent = RobloxGui
|
|
OpenInventoryButton.Parent = nil
|
|
CloseInventoryButton.Parent = nil
|
|
|
|
ScrollUpInventoryButton.Parent = nil
|
|
ScrollDownInventoryButton.Parent = nil
|
|
ScrollingFrame.ScrollingEnabled = true
|
|
|
|
-- Turn draggin back on
|
|
for _, slot in pairs(Slots) do
|
|
slot:SetClickability(true)
|
|
end
|
|
|
|
if BackpackStateChangedInVRConn then
|
|
BackpackStateChangedInVRConn:disconnect()
|
|
BackpackStateChangedInVRConn = nil
|
|
end
|
|
if VRModuleOpenedConn then
|
|
VRModuleOpenedConn:disconnect()
|
|
VRModuleOpenedConn = nil
|
|
end
|
|
if VRModuleClosedConn then
|
|
VRModuleClosedConn:disconnect()
|
|
VRModuleClosedConn = nil
|
|
end
|
|
end
|
|
|
|
NumberOfInventoryRows = IsVR and INVENTORY_ROWS_VR or (IS_PHONE and INVENTORY_ROWS_MINI or INVENTORY_ROWS_FULL)
|
|
local newSlotTotal = IsVR and HOTBAR_SLOTS_VR or (IS_PHONE and HOTBAR_SLOTS_MINI or HOTBAR_SLOTS_FULL)
|
|
SetNumberOfHotbarSlots(newSlotTotal)
|
|
end
|
|
VRService:GetPropertyChangedSignal("VREnabled"):connect(OnVREnabled)
|
|
OnVREnabled()
|
|
|
|
return BackpackScript
|