Paste text from hs.chooser in hammerspoon - lua

I am trying to create a shortcut where I store a set of text templates by using hs.chooser. And, the user can paste this by clicking on the drop down from hs.chooser.
I use the below code which displays my template but doesn't paste the text.
Can someone point me what I am doing wrong?
hs.hotkey.bind({"Q"}, "W", function()
local current = hs.application.frontmostApplication()
local chooser = hs.chooser.new(function(choice)
if not choice then focusLastFocused(); return end
hs.pasteboard.setContents(choice["chars"])
focusLastFocused()
hs.eventtap.keyStrokes(hs.pasteboard.getContents())
end)
chooser:queryChangedCallback(function(string)
local choices = {
{
["text"] = "Testing",
["subText"] = "Testing my text"
}
}
chooser:choices(choices)
end)
chooser:searchSubText(true)
chooser:show()
end)

I figured out the answer
-- Focus the last used window.
local function focusLastFocused()
local wf = hs.window.filter
local lastFocused = wf.defaultCurrentSpace:getWindows(wf.sortByFocusedLast)
if #lastFocused > 0 then lastFocused[1]:focus() end
end
-- On selection, copy the text and type it into the focused application.
local chooser = hs.chooser.new(function(choice)
if not choice then focusLastFocused(); return end
hs.pasteboard.setContents(choice["subText"])
focusLastFocused()
hs.eventtap.keyStrokes(hs.pasteboard.getContents())
end)
chooser:choices({
{
["text"] = "Browser\n",
["subText"] = "I used these browsers",
},
{
["text"] = "Device\n",
["subText"] = "I used these devices",
},
})
hs.hotkey.bind({"E"}, "E", function() chooser:show() end)

Related

Lua/Wow: UseInventoryItem() issue

My goal is to create a script that warns when you are disenchanting a piece you don't want. When disenchanting an item straight from inventory, I could use the UseInventoryItem() API. Since UseInventoryItem() seems to work only on inventory slot right click, I created this script, based on GetMouseFocus() API, that works with the original interface of the game: but if someone used an addon, would it still work?
Of course, better solutions are welcome
local mt = {
__index = {
isvalue = function(t, value)
local is = false
for k, entry in ipairs(t) do
if (entry == value) then
is = true
break
end
end
return is
end
}
};
local protected = { "item1", "item2", "item3", "etch." }; -- items I want to protect
setmetatable(protected, mt);
local disenchanting;
local antidisenchant = CreateFrame("Frame");
antidisenchant:RegisterEvent("UNIT_SPELLCAST_SENT");
antidisenchant:SetScript("OnEvent", function(self, event, ...)
if (event == "UNIT_SPELLCAST_SENT") then
if (arg2 == "Disenchant") then
disenchanting = true
end
end
end);
antidisenchant:SetScript("OnUpdate", function()
if GetMouseFocus() then -- GetMouseFocus() returns the frame that currently has mouse focus.
local TargetItemID = GetInventoryItemID("player",GetMouseFocus():GetID()) -- The IDs of each inventory slot frame have the same id as the slot (16 for main hand, 17 for off hand etc.).
if (TargetItemID) and (string.find(GetMouseFocus():GetName(),"Slot")) then -- Inventory slot frame are named like "CharacterMainHandSlot".
local name, link = GetItemInfo(TargetItemID)
if (disenchanting) and (protected:isvalue(name)) then
DEFAULT_CHAT_FRAME:AddMessage("WARNING! YOU'RE DISENCHANTING "..link,1,0,0)
end
end
end
end)

FiveM client lua. Problem with shops point

I have a problem with dots, when you enter a clothing store or a barbershop, the menu opens and works well. But after that, anywhere on the map, when you press E, the menu of a clothing store or a barbershop opens.
Here is the code itself, I myself do not know what the problem is there.
QBCore = exports['qb-core']:GetCoreObject()
local LastZone = nil
local CurrentAction = nil
local CurrentActionMsg = ''
local hasAlreadyEnteredMarker = false
local allMyOutfits = {}
local isPurchaseSuccessful = false
local PlayerData = {}
-- Net Events
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
QBCore.Functions.TriggerCallback('fivem-appearance:getPlayerSkin', function(appearance)
exports['fivem-appearance']:setPlayerAppearance(appearance)
PlayerData = QBCore.Functions.GetPlayerData()
if Config.Debug then -- This will detect if the player model is set as "player_zero" aka michael. Will then set the character as a freemode ped based on gender.
Wait(5000)
if GetEntityModel(PlayerPedId()) == `player_zero` then
print('Player detected as "player_zero", Starting CreateFirstCharacter event')
TriggerEvent('qb-clothes:client:CreateFirstCharacter')
end
end
end)
end)
RegisterNetEvent('qb-clothes:client:CreateFirstCharacter', function() -- Event renamed so you dont need to change anything for this to work... hopefully....
QBCore.Functions.GetPlayerData(function(PlayerData)
local skin = 'mp_m_freemode_01'
if PlayerData.charinfo.gender == 1 then
skin = "mp_f_freemode_01"
end
exports['fivem-appearance']:setPlayerModel(skin)
local config = {
ped = false,
headBlend = true,
faceFeatures = true,
headOverlays = true,
components = true,
props = true,
}
exports['fivem-appearance']:setPlayerAppearance(appearance)
exports['fivem-appearance']:startPlayerCustomization(function(appearance)
if (appearance) then
TriggerServerEvent('fivem-appearance:save', appearance)
print('Player Clothing Saved')
else
print('Canceled')
end
end, config)
end)
end, false)
AddEventHandler('fivem-appearance:hasExitedMarker', function(zone)
CurrentAction = nil
end)
RegisterNetEvent('fivem-appearance:clothingShop', function()
exports['qb-menu']:openMenu({
{
header = "👚 | Clothing Store Options",
isMenuHeader = true, -- Set to true to make a nonclickable title
},
{
header = "Buy Clothing - $"..Config.Money,
txt = "Pick from a wide range of items to wear",
params = {
event = "fivem-appearance:clothingMenu",
}
},
{
header = "Change Outfit",
txt = "Pick from any of your currently saved outfits",
params = {
event = "fivem-appearance:pickNewOutfit",
args = {
number = 1,
id = 2
}
}
},
{
header = "Save New Outfit",
txt = "Save a new outfit you can use later on",
params = {
event = "fivem-appearance:saveOutfit",
}
},
{
header = "Delete Outfit",
txt = "Yeah... We didnt like that one either",
params = {
event = "fivem-appearance:deleteOutfitMenu",
args = {
number = 1,
id = 2
}
}
},
})
end)
RegisterNetEvent('fivem-appearance:pickNewOutfit', function(data)
local id = data.id
local number = data.number
TriggerEvent('fivem-appearance:getOutfits')
Wait(150)
local outfitMenu = {
{
header = '< Go Back',
params = {
event = 'fivem-appearance:clothingShop'
}
}
}
for i=1, #allMyOutfits, 1 do
outfitMenu[#outfitMenu + 1] = {
header = allMyOutfits[i].name,
params = {
event = 'fivem-appearance:setOutfit',
args = {
-- number = (1 + i),
ped = allMyOutfits[i].pedModel,
components = allMyOutfits[i].pedComponents,
props = allMyOutfits[i].pedProps
}
}
}
end
exports['qb-menu']:openMenu(outfitMenu)
end)
RegisterNetEvent('fivem-appearance:getOutfits', function()
TriggerServerEvent('fivem-appearance:getOutfits')
end)
RegisterNetEvent('fivem-appearance:sendOutfits', function(myOutfits)
local Outfits = {}
for i=1, #myOutfits, 1 do
table.insert(Outfits, {id = myOutfits[i].id, name = myOutfits[i].name, pedModel = myOutfits[i].ped, pedComponents = myOutfits[i].components, pedProps = myOutfits[i].props})
end
allMyOutfits = Outfits
end)
RegisterNetEvent('fivem-appearance:setOutfit', function(data)
local pedModel = data.ped
local pedComponents = data.components
local pedProps = data.props
local playerPed = PlayerPedId()
local currentPedModel = exports['fivem-appearance']:getPedModel(playerPed)
if currentPedModel ~= pedModel then
exports['fivem-appearance']:setPlayerModel(pedModel)
Wait(500)
playerPed = PlayerPedId()
exports['fivem-appearance']:setPedComponents(playerPed, pedComponents)
exports['fivem-appearance']:setPedProps(playerPed, pedProps)
local appearance = exports['fivem-appearance']:getPedAppearance(playerPed)
TriggerServerEvent('fivem-appearance:save', appearance)
else
exports['fivem-appearance']:setPedComponents(playerPed, pedComponents)
exports['fivem-appearance']:setPedProps(playerPed, pedProps)
local appearance = exports['fivem-appearance']:getPedAppearance(playerPed)
TriggerServerEvent('fivem-appearance:save', appearance)
end
-- TriggerEvent('fivem-appearance:clothingShop')
end)
RegisterNetEvent('fivem-appearance:saveOutfit', function()
local keyboard = exports['qb-input']:ShowInput({
header = "Name your outfit",
submitText = "Create Outfit",
inputs = {
{
text = "Outfit Name",
name = "input",
type = "text",
isRequired = true
},
},
})
if keyboard ~= nil then
local playerPed = PlayerPedId()
local pedModel = exports['fivem-appearance']:getPedModel(playerPed)
local pedComponents = exports['fivem-appearance']:getPedComponents(playerPed)
local pedProps = exports['fivem-appearance']:getPedProps(playerPed)
Wait(500)
TriggerServerEvent('fivem-appearance:saveOutfit', keyboard.input, pedModel, pedComponents, pedProps)
QBCore.Functions.Notify('Outfit '..keyboard.input.. ' has been saved', 'success')
end
end)
RegisterNetEvent('fivem-appearance:deleteOutfitMenu', function(data)
local id = data.id
local number = data.number
TriggerEvent('fivem-appearance:getOutfits')
Wait(150)
local DeleteMenu = {
{
header = '< Go Back',
params = {
event = 'fivem-appearance:clothingShop'
}
}
}
for i=1, #allMyOutfits, 1 do
DeleteMenu[#DeleteMenu + 1] = {
header = 'Delete "'..allMyOutfits[i].name..'"',
txt = 'You will never be able to get this back!',
params = {
event = 'fivem-appearance:deleteOutfit',
args = allMyOutfits[i].id
}
}
end
exports['qb-menu']:openMenu(DeleteMenu)
end)
RegisterNetEvent('fivem-appearance:deleteOutfit', function(id)
TriggerServerEvent('fivem-appearance:deleteOutfit', id)
-- TriggerEvent('fivem-appearance:clothingShop')
QBCore.Functions.Notify('Outfit Deleted', 'error')
end)
RegisterNetEvent("fivem-appearance:purchase", function(bool)
isPurchaseSuccessful = bool
end)
RegisterNetEvent('fivem-appearance:clothingMenu', function()
TriggerServerEvent('fivem-appearances:buyclothing')
Wait(500)
if isPurchaseSuccessful then
local config = {
ped = false,
headBlend = false,
faceFeatures = false,
headOverlays = false,
components = true,
props = true
}
exports['fivem-appearance']:startPlayerCustomization(function(appearance)
if appearance then
TriggerServerEvent('fivem-appearance:save', appearance)
print('Player Clothing Saved')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
else
print('Canceled')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
end
end, config)
end
end)
RegisterNetEvent('fivem-appearance:barberMenu', function()
local config = {
ped = false,
headBlend = false,
faceFeatures = false,
headOverlays = true,
components = false,
props = false
}
exports['fivem-appearance']:startPlayerCustomization(function (appearance)
if appearance then
TriggerServerEvent('fivem-appearance:save', appearance)
print('Player Clothing Saved')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
else
print('Canceled')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
end
end, config)
end)
-- Backwords Events so you dont need to replace these
RegisterNetEvent('qb-clothing:client:openMenu', function() -- Admin Menu clothing event
Wait(500)
local config = {
ped = true,
headBlend = true,
faceFeatures = true,
headOverlays = true,
components = true,
props = true
}
exports['fivem-appearance']:startPlayerCustomization(function(appearance)
if appearance then
TriggerServerEvent('fivem-appearance:save', appearance)
print('Player Clothing Saved')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
else
print('Canceled')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
end
end, config)
end)
RegisterNetEvent('qb-clothing:client:openOutfitMenu', function() -- Name is so that you dont have to replace the event, Used in Appartments, Bossmenu, etc...
exports['qb-menu']:openMenu({
{
header = "👔 | Outfit Options",
isMenuHeader = true, -- Set to true to make a nonclickable title
},
{
header = "Change Outfit",
txt = "Pick from any of your currently saved outfits",
params = {
event = "fivem-appearance:pickNewOutfitApp",
args = {
number = 1,
id = 2
}
}
},
{
header = "Save New Outfit",
txt = "Save a new outfit you can use later on",
params = {
event = "fivem-appearance:saveOutfit",
}
},
{
header = "Delete Outfit",
txt = "Yeah... We didnt like that one either",
params = {
event = "fivem-appearance:deleteOutfitMenu",
args = {
number = 1,
id = 2
}
}
},
})
end)
RegisterNetEvent('fivem-appearance:pickNewOutfitApp', function(data)
local id = data.id
local number = data.number
TriggerEvent('fivem-appearance:getOutfits')
Wait(150)
local outfitMenu = {
{
header = '< Go Back',
params = {
event = 'qb-clothing:client:openOutfitMenu'
}
}
}
for i=1, #allMyOutfits, 1 do
outfitMenu[#outfitMenu + 1] = {
header = allMyOutfits[i].name,
params = {
event = 'fivem-appearance:setOutfit',
args = {
-- number = (1 + i),
ped = allMyOutfits[i].pedModel,
components = allMyOutfits[i].pedComponents,
props = allMyOutfits[i].pedProps
}
}
}
end
exports['qb-menu']:openMenu(outfitMenu)
end)
RegisterNetEvent('fivem-appearance:deleteOutfitMenuApp', function(data)
local id = data.id
local number = data.number
TriggerEvent('fivem-appearance:getOutfits')
Wait(150)
local DeleteMenu = {
{
header = '< Go Back',
params = {
event = 'fivem-appearance:clothingShop'
}
}
}
for i=1, #allMyOutfits, 1 do
DeleteMenu[#DeleteMenu + 1] = {
header = 'Delete "'..allMyOutfits[i].name..'"',
txt = 'You will never be able to get this back!',
params = {
event = 'fivem-appearance:deleteOutfit',
args = allMyOutfits[i].id
}
}
end
exports['qb-menu']:openMenu(DeleteMenu)
end)
-- Theads
CreateThread(function()
while true do
Wait(0)
if CurrentAction ~= nil then
if IsControlPressed(1, 38) then
Wait(500)
if CurrentAction == 'clothingMenu' then
TriggerEvent("fivem-appearance:clothingShop")
end
if CurrentAction == 'barberMenu' then
TriggerEvent("fivem-appearance:barberMenu")
end
end
end
end
end)
CreateThread(function()
for k,v in ipairs(Config.BarberShops) do
local blip = AddBlipForCoord(v)
SetBlipSprite (blip, 71)
-- SetBlipColour (blip, 47)
SetBlipScale (blip, 0.7)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName('STRING')
AddTextComponentSubstringPlayerName('Barber Shop')
EndTextCommandSetBlipName(blip)
end
for k,v in ipairs(Config.ClothingShops) do
local data = v
if data.blip == true then
local blip = AddBlipForCoord(data.coords)
SetBlipSprite (blip, 73)
-- SetBlipColour (blip, 47)
SetBlipScale (blip, 0.7)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName('STRING')
AddTextComponentSubstringPlayerName('Clothing Store')
EndTextCommandSetBlipName(blip)
end
end
end)
CreateThread(function()
while true do
local playerCoords, isInClothingShop, isInPDPresets, isInBarberShop, currentZone, letSleep = GetEntityCoords(PlayerPedId()), false, false, nil, true
local sleep = 2000
for k,v in pairs(Config.ClothingShops) do
local data = v
local distance = #(playerCoords - data.coords)
if distance < Config.DrawDistance then
sleep = 500
if distance < data.MarkerSize.x then
isInClothingShop, currentZone = true, k
end
end
end
for k,v in pairs(Config.BarberShops) do
local distance = #(playerCoords - v)
if distance < Config.DrawDistance then
sleep = 500
if distance < Config.MarkerSize.x then
isInBarberShop, currentZone = true, k
end
end
end
if (isInClothingShop and not hasAlreadyEnteredMarker) or (isInClothingShop and LastZone ~= currentZone) then
hasAlreadyEnteredMarker, LastZone = true, currentZone
CurrentAction = 'clothingMenu'
exports['qb-drawtext']:DrawText('[E] Clothing Menu','left')
end
if (isInBarberShop and not hasAlreadyEnteredMarker) or (isInBarberShop and LastZone ~= currentZone) then
hasAlreadyEnteredMarker, LastZone = true, currentZone
CurrentAction = 'barberMenu'
exports['qb-drawtext']:DrawText('[E] Barber Menu','left')
end
if not isInClothingShop and not isInBarberShop and hasAlreadyEnteredMarker then
hasAlreadyEnteredMarker = false
sleep = 0
TriggerEvent('fivem-appearance:hasExitedMarker', LastZone)
exports['qb-drawtext']:HideText()
end
Wait(sleep)
end
end)
-- Command(s)
RegisterCommand('reloadskin', function()
local playerPed = PlayerPedId()
local maxhealth = GetEntityMaxHealth(playerPed)
local health = GetEntityHealth(playerPed)
QBCore.Functions.TriggerCallback('fivem-appearance:getPlayerSkin', function(appearance)
exports['fivem-appearance']:setPlayerAppearance(appearance)
end)
for k, v in pairs(GetGamePool('CObject')) do
if IsEntityAttachedToEntity(PlayerPedId(), v) then
SetEntityAsMissionEntity(v, true, true)
DeleteObject(v)
DeleteEntity(v)
end
SetPedMaxHealth(PlayerId(), maxhealth)
Citizen.Wait(1000) -- Safety Delay
SetEntityHealth(PlayerPedId(), health)
end
end)
-- Testing Command
RegisterCommand('clothingmenu', function()
local config = {
ped = true,
headBlend = true,
faceFeatures = true,
headOverlays = true,
components = true,
props = true,
}
exports['fivem-appearance']:startPlayerCustomization(function (appearance)
if (appearance) then
TriggerServerEvent('fivem-appearance:save', appearance)
print('Player Clothing Saved')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
else
print('Canceled')
Wait(1000) -- Wait is needed to clothing menu dosent overwrite the tattoos
TriggerServerEvent('Select:Tattoos')
end
end, config)
end, false)
I tried to set the boundaries of the vectors, it did not help. Tried to find the error but couldn't...
The problem is that the CurrentAction variable is never being reset to nil, so the thread you started to monitor for that keypress continues to "react" to it with the last visited store.
The way the code is written, it appears as though it is meant to be reset to nil only when the when hasExitedMarker function is triggered, but you only trigger this in one place - and it is only conditionally run only if hasAlreadyEnteredMarker is set to true when a shop is detected as being exited. I have a feeling that variable is not being properly maintained.
You can see how this quickly turns into a daisy-chain of state that must be maintained "just so".
Without undergoing a massive debugging session on your behalf, I can only suggest you debug these variables by outputting them to the screen, and watching for when they are set and properly unset (or not).
Higher level advice might be to add whatever routine you use to detect entry/exit into barber/clothing shops to a standalone function you can call directly from the keypress event handler, and skip handling it if you are not in the expected location. This won't solve your original problem of your application state falling into disrepair, but it gives you a chance to "fix" your state on keypress if it turns out you are handling it when you shouldn't be.

Creating a Cursor in LOVE

I'm new to coding and recently started learning about having code located outside of main.lua, which I wanted to do for my custom cursor. The original correctly displayed "cursor.png", but the modified version simply displays the standard cursor. Could someone explain what I'm missing or doing wrong?
Original:
love.load = function()
cursor = love.graphics.newImage("assets/sprites/cursor.png")
love.mouse.setVisible(false)
end
love.draw = function()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(cursor, love.mouse.getX(), love.mouse.getY())
end
Modified:
local load = function(cursor)
love.mouse.setVisible(false)
local inst = {}
inst.cursor = love.graphics.newImage("assets/sprites/cursor.png")
return inst
end
local draw = function(color, image)
local inst = {}
inst.color = love.graphics.setColor(1, 1, 1, 1)
inst.image = love.graphics.draw(cursor, love.mouse.getX(), love.mouse.getY())
return inst
end
After several hours (and only a few minutes after posting this question), I found a solution that works. Please note that this was coded in a "working file" rather than my actual cursor file, hence the name.
local working_file = {}
local cusorSprite
working_file.load = function(self)
cursorSprite = love.graphics.newImage("assets/sprites/cursor.png")
love.mouse.setVisible(false)
end
working_file.update = function(self)
end
working_file.draw = function(self)
love.graphics.draw(cursorSprite, love.mouse.getX(), love.mouse.getY())
end
return working_file

dictionary help/DataStore

The Issue is I have a dictionary that holds all my data and its supposed to be able to turn into a directorys in replicated storage with all the values being strings then turn back into a dictionary with all the keys when the player leave. However, I cant figure out how to turn into a dictionary(With keys).
ive sat for a few hours testing things but after the first layer of values I cant get figure out a way to get the deeper values and keys into the table
local DataTable =
{
["DontSave_Values"] =
{
["Stamina"] = 100;
};
["DontSave_Debounces"] =
{
};
["TestData"] = 1;
["Ship"] =
{
["Hull"] = "Large_Ship";
["Mast"] = "Iron_Tall";
["Crew"] =
{
["Joe One"] =
{
["Shirt"] = "Blue";
["Pants"] = "Green"
};
["Joe Two"] =
{
["Shirt"] = "Silver";
["Pants"] = "Brown";
["Kids"] =
{
["Joe Mama1"] =
{
["Age"] = 5
};
["Joe Mama2"]=
{
["Age"] = 6
};
}
};
}
};
["Level"] =
{
};
["Exp"] =
{
};
}
------Test to see if its an array
function isArray(Variable)
local Test = pcall(function()
local VarBreak = (Variable.." ")
end)
if Test == false then
return true
else
return false
end
end
------TURNS INTO FOLDERS
function CreateGameDirectory(Player, Data)
local mainFolder = Instance.new("Folder")
mainFolder.Parent = game.ReplicatedStorage
mainFolder.Name = Player.UserId
local function IterateDictionary(Array, mainFolder)
local CurrentDirectory = mainFolder
for i,v in pairs(Array) do
if isArray(v) then
CurrentDirectory = Instance.new("Folder", mainFolder)
CurrentDirectory.Name = i
for o,p in pairs(v) do
if isArray(p) then
local TemporaryDir = Instance.new("Folder", CurrentDirectory)
TemporaryDir.Name = o
IterateDictionary(p, TemporaryDir)
else
local NewValue = Instance.new("StringValue", CurrentDirectory)
NewValue.Name = o
NewValue.Value = p
end
end
else
local value = Instance.new("StringValue", mainFolder)
value.Name = i
value.Value = v
end
end
end
IterateDictionary(Data, mainFolder)
end
------To turn it back into a table
function CreateTable(Player)
local NewDataTable = {}
local Data = RS:FindFirstChild(Player.UserId)
local function DigDeep(newData, pData, ...)
local CurrentDir = newData
for i,v in pairs(pData:GetChildren()) do
if string.sub(v.Name,1,8) ~= "DontSave" then
end
end
end
DigDeep(NewDataTable, Data)
return NewDataTable
end
I expected to when the player leaves run createtable function and turn all the instances in replicated storage back into a dictionary with keys.
Why not just store extra information in your data table to help make it easy to convert back and forth. As an example, why not have your data look like this :
local ExampleData = {
-- hold onto your special "DON'T SAVE" values as simple keys in the table.
DONTSAVE_Values = {
Stamina = 0,
},
-- but every element under ReplicatedStorage will be used to represent an actual Instance.
ReplicatedStorage = {
-- store an array of Child elements rather than another map.
-- This is because Roblox allows you to have multiple children with the same name.
Name = "ReplicatedStorage",
Class = "ReplicatedStorage",
Properties = {},
Children = {
{
Name = "Level",
Class = "NumberValue",
Properties = {
Value = 0,
},
Children = {},
},
{
Name = "Ship",
Class = "Model",
Properties = {},
Children = {
{
-- add all of the other instances following the same pattern :
-- Name, Class, Properties, Children
},
},
},
}, -- end list of Children
}, -- end ReplicatedStorage element
};
You can create this table with a simple recursive function :
-- when given a Roblox instance, generate the dataTable for that element
local function getElementData(targetInstance)
local element = {
Name = targetInstance.Name,
Class = targetInstance.ClassName,
Properties = {},
Children = {},
}
-- add special case logic to pull out specific properties for certain classes
local c = targetInstance.ClassName
if c == "StringValue" then
element.Properties = { Value = targetInstance.Value }
-- elseif c == "ADD MORE CASES HERE" then
else
warn(string.format("Not sure how to parse information for %s", c))
end
-- iterate over the children and populate their data
for i, childInstance in ipairs(targetInstance:GetChildren()) do
table.insert( element.Children, getElementData(childInstance))
end
-- give the data back to the caller
return element
end
-- populate the table
local Data = {
ReplicatedStorage = getElementData(game.ReplicatedStorage)
}
Now Data.ReplicatedStorage.Children should have a data representation of the entire folder. You could even save this entire table as a string by passing it to HttpService:JSONEncode() if you wanted to.
When you're ready to convert these back into instances, use the data you've stored to give you enough information on how to recreate the elements :
local function recreateElement(tableData, parent)
-- special case ReplicatedStorage
if tableData.Class == "ReplicatedStorage" then
-- skip right to the children
for i, child in ipairs(tableData.Children) do
recreateElement(child, parent)
end
-- quick escape from this node
return
end
-- otherwise, just create elements from their data
local element = Instance.new(tableData.Class)
element.Name = tableData.Name
-- set all the saved properties
for k, v in pairs(tableData.Properties) do
element[k] = v
end
-- recreate all of the children of this element
for i, child in ipairs(tableData.Children) do
recreateElement(child, element)
end
-- put the element into the workspace
element.Parent = parent
end
-- populate the ReplicatedStorage from the stored data
recreateElement( Data.ReplicatedStorage, game.ReplicatedStorage)
You should be careful about how and when you choose to save this data. If you are playing a multiplayer game, you should be careful that this kind of logic only updates ReplicatedStorage for the first player to join the server. Otherwise, you run the risk of a player joining and overwriting everything that everyone else has been doing.
Since there isn't a way to iterate over properties of Roblox Instances, you'll have to manually update the getElementData function to properly store the information you care about for each type of object. Hopefully this helps!
If anyone else has this problem the solution I used was just to convert the instances strait into JSON format(I used the names as keys). so that way I can save it, and then when the player rejoins I just used JSONDecode to turn it into the dictionary I needed.
function DirToJSON(Player)
local NewData = RS:FindFirstChild(Player.UserId)
local JSONstring="{"
local function Recurse(Data)
for i, v in pairs(Data:GetChildren()) do
if v:IsA("Folder") then
if #v:GetChildren() < 1 then
if i == #Data:GetChildren()then
JSONstring=JSONstring..'"'..v.Name..'":[]'
else
JSONstring=JSONstring..'"'..v.Name..'":[],'
end
else
JSONstring=JSONstring..'"'..v.Name..'":{'
Recurse(v)
if i == #Data:GetChildren()then
JSONstring=JSONstring..'}'
else
JSONstring=JSONstring..'},'
end
end
else
if i == #Data:GetChildren()then
JSONstring=JSONstring..'"'..v.Name..'":"'..v.Value..'"'
else
JSONstring=JSONstring..'"'..v.Name..'":"'..v.Value..'",'
end
end
end
end
Recurse(NewData)
JSONstring = JSONstring.."}"
return(JSONstring)
end

Corona SDK: Displaying Word from Table

OK my first question was too vague so I'm going to start simple here. I am trying to get a random word from a table in another lua file (content.lua). I have gotten the code to run without errors but cannot get a word to display on the screen or via print in the command console. What am I missing?
game.lua
--lua for game
--Loading the local variables
--creates the storyboard variable and calls the storyboard api
local storyboard = require ("storyboard")
--calls the mydata.lua module
local myData = require( "mydata" )
--calls the sfx.lua where sounds are stored
local sfx = require( "sfx" )
--calls the operations.lua
local operations = require("operations")
local content = require("content")
local playOrder
local wordGraphic
local currQuestion = 1
local homeButton
--tells storyboard to create a new scene
local scene = storyboard.newScene()
function scene:createScene(event)
local gameScreen = self.view
--creates a transparent background image centered on the display
local gameBackground = display.newImage("images/graphics/jungle1.jpg")
gameBackground.x = display.contentWidth/2
gameBackground.y = display.contentHeight/2
gameScreen:insert(gameBackground)
homeButton = display.newImage("images/buttons/home.png")
homeButton.alpha = .8
homeButton.y = 70
gameScreen:insert(homeButton)
playOrder = operations.getRandomOrder(#content)
end
local function onHomeTouch(event)
if event.phase == "began" then
storyboard.gotoScene("start")
end
end
function scene:enterScene(event)
homeButton:addEventListener("touch", onHomeTouch)
audio.play(sfx.Bkgd)
--uses the operations.lua to get words in a random order from the content.lua
--shows a random word from the content.lua table
function showWord()
local word = content[playOrder[currQuestion]].word
print(word)
wordGraphic = self.view
wordGraphic:insert(word)
wordGraphic.x = display.contentWidth/2
wordGraphic.y = display.contentHeight/2
end
end
--next question function which clears the screen and loads a new random word
function scene:exitScene(event)
homeButton:removeEventListener("touch", onHomeTouch)
end
function scene:destroyScene(event)
end
--the actual event listeners that make the functions work
scene:addEventListener("createScene", scene)
scene:addEventListener("enterScene", scene)
scene:addEventListener("exitScene", scene)
scene:addEventListener("destroyScene", scene)
return scene
Here is the operations.lua that gets the random order function
--operations.lua
module(..., package.seeall)
--function to get a random piece of data
function getRandomOrder(amount)
local order ={}
local i
local temp
local temp1
for n = 1,amount do
order[n] = n
end
for i=0,9 do
for temp = 1,amount do
n = math.random(1, amount)
temp1 = order[temp]
order[temp] = order[n]
order[n] = temp1
end
end
return order
end
This is where the words I am attempting to display are stored. I did not include all of them.
--content.lua
return {
{
id = "after",
word = "after"
},
{
id = "again",
word = "again"
},
{
id = "an",
word = "an"
},
{
id = "any",
word = "any"
},
{
id = "ask",
word = "ask"
},
{
id = "as",
word = "as"
},
{
id = "by",
word = "by"
}
}
You're not calling showWord in any of the code that you've shown so far. This is probably why it isn't even printing to the console. The function is just contained within scene:enterScene and exits to the outer scope, defining itself as a global variable when enterScene is called.

Resources