Difference between revisions of "Ftf libs"

From GiderosMobile
 
(6 intermediate revisions by the same user not shown)
Line 4: Line 4:
  
 
'''All samples are ready to use. Enjoy!'''
 
'''All samples are ready to use. Enjoy!'''
 +
 +
== AUDIO ==
 +
=== Producing Sound Without Resources '''@PaulH''' ===
 +
'''Creates files on the fly using the .wav format for playback'''
 +
<syntaxhighlight lang="lua">
 +
-- https://forum.gideros.rocks/discussion/comment/68095/#Comment_68095
 +
function make_wave(file_name, length, wave, freq, vol)
 +
-- We'll need to write some 32 bit values in binary. Helper function for that:
 +
local function to4Bytes(n) return n & 0xff, (n>>8) & 0xff, (n>>16) & 0xff, (n>>24) & 0xff end
 +
 +
-- For a given point (sample number) in a wav file, return the 16 bit value for the specified sound wave and frequency
 +
local function sound_wave(wave,p,freq,vol)
 +
local sample_rate = 6000
 +
local n = 0 -- Default to silence
 +
if wave == "noise" then n = math.random(-255*128*vol, 255*128*vol)
 +
elseif wave == "sine" then n = math.floor(math.sin(p/sample_rate*freq*math.rad(180))*256*vol*127)
 +
elseif wave == "sawtooth" then n = math.floor((freq*p/sample_rate)*256*vol*256)
 +
end
 +
local c2, c1 = (n>>8) & 0xff, n & 0xff
 +
return string.char(c1, c2)
 +
end
 +
 +
-- We're just dealing with a fixed format wav file:
 +
local sample_rate = 6000
 +
local samples = length*sample_rate
 +
local data_size = samples*2
 +
local total_file_length = 44+data_size
 +
local fl1, fl2, fl3, fl4 = to4Bytes(total_file_length)
 +
local ds1, ds2, ds3, ds4 = to4Bytes(data_size)
 +
local f = io.open(file_name, "wb")
 +
-- Write the WAV file header:
 +
f:write(
 +
string.char(
 +
82,73,70,70,--RIFF
 +
fl1, fl2, fl3, fl4,-- File size overall
 +
87,65,86,69,-- WAVE header
 +
102,109,116,32,--fmt chunk marker w/ trailing null
 +
16,0,0,0, -- Length of format data (16 bytes before this)
 +
1,0, -- Wave type. 1 == PCM
 +
1,0, -- Channels. 1 == mono
 +
112,23,0,0,-- Sample rate (Hertz)
 +
224,46,0,0, -- Bits per sample * channels * sample rate / 8
 +
2,0,
 +
16,0, -- Bytes per sample - this is 16
 +
100,97,116,97, -- "data" - chunk header
 +
ds1,ds2,ds3,ds4-- size of data section
 +
)
 +
)
 +
-- Write the data:
 +
for l = 1, samples do
 +
f:write(sound_wave(wave,l,freq, vol or 1))
 +
end
 +
f:close()
 +
end
 +
 +
-- Demo some white noise:
 +
--make_wave("|D|noise.wav", 1, "noise") -- 0.1 second sine wave at 256 Herz (middle C) at full volume
 +
--s=Sound.new("|D|noise.wav")
 +
--s:play()
 +
 +
-- one note with a sawtooth wave:
 +
--make_wave("|D|note.wav", 2, "sawtooth", 262, 1) -- 0.1 second sine wave at 256 Herz (middle C) at full volume
 +
--s=Sound.new("|D|note.wav")
 +
--s:play()
 +
 +
-- Make wave files for the chromatic scale.
 +
-- The frequencies in Hertz of the notes in octave zero:
 +
notes = { 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.5, 25.96, 27.50, 29.14, 30.87 }
 +
-- 3 letter names for those notes:
 +
names = { "C0n", "C0s", "D0n", "D0s", "E0n", "F0n", "F0s", "G0n", "G0s", "A0n", "A0s", "B0n" }
 +
-- Compute the ratio of each note to the C:
 +
local note_multipliers = {}
 +
for i = 1, #notes do
 +
note_multipliers[i] = notes[i]/notes[1]
 +
end
 +
 +
-- Make a sound for each note in several octaves:
 +
local sounds = {}
 +
for octave = 3, 7 do
 +
for i = 1, #names do
 +
local original_note = names[i]
 +
local octave_note = string.gsub(original_note, "0", octave)
 +
local file_name = "|D|note_" .. octave_note .. ".wav"
 +
-- Compute the frequency for the note by applying the ratio of that note over C
 +
-- and then apply the octave multiplier. Each octave is double the frequency
 +
-- of the one below it:
 +
local fr = math.floor(notes[1] * note_multipliers[i] * math.pow(2, octave))
 +
-- We'll make each note 0.2 seconds, using a sine wave
 +
--function make_wave(file_name, length, wave, freq, vol)
 +
make_wave(file_name, 0.2, "sine", fr, 1)
 +
sounds[octave_note] = Sound.new(file_name)
 +
end
 +
end
 +
 +
-- Given a string of 3 letter note names, play those notes in order:
 +
function play_sequence(seq)
 +
for i = 1, string.len(seq), 3 do
 +
local note = string.sub(seq, i, i+2)
 +
if sounds[note] then
 +
Timer.delayedCall(60 * (i-1), function()
 +
sounds[note]:play()
 +
end)
 +
end
 +
end
 +
end
 +
 +
local fur_elise =
 +
"E4nD4sE4nD4sE4nB3nD4nC4nA3n      "..
 +
"C3nE3nA3nB3n      "..
 +
"E3nG3sB3nC4n      "..
 +
"E3nE4nD4sE4nD4sE4nB3nD4nC4nA3n      "..
 +
"C3nE3nA3nB3n      "..
 +
"E3nC4nB3nA3n"
 +
play_sequence(fur_elise)
 +
-- To play an octave higher:
 +
Timer.delayedCall(9000, function()
 +
play_sequence(string.gsub(string.gsub(fur_elise,"4","5"),"3","4"))
 +
end)
 +
</syntaxhighlight>
 +
 +
== STRING ==
 +
=== Score Counting TextField '''@errorpi''' ===
 +
<syntaxhighlight lang="lua">
 +
--[[
 +
Score Counting TextField
 +
by: @errorpi
 +
]]
 +
 +
Score = Core.class(Sprite)
 +
 +
function Score:init(xscore, xincrease)
 +
self.score = xscore or 0
 +
self.countTime = (xincrease or 5) * 100
 +
-- a textfield
 +
self.scoreText = TextField.new(nil, tostring(self.score))
 +
self.scoreText:setLayout({wid = 80, hei = 40, flags=FontBase.TLF_CENTER})
 +
self.scoreText:setTextColor(math.random(0xffffff))
 +
self.scoreText:setScale(5)
 +
self.scoreText:setPosition(64, 64)
 +
self:addChild(self.scoreText)
 +
-- a timer
 +
self.t = Timer.new(1) -- timer init
 +
self.t:addEventListener(Event.TIMER, self.updateText, self)
 +
end
 +
 +
function Score:updateText()
 +
local displayScore = tonumber(self.scoreText:getText())
 +
if displayScore < self.score then
 +
self.scoreText:setText(tostring( displayScore + 1) )
 +
end
 +
end
 +
 +
function Score:addScore(add)
 +
self.score += add
 +
local displayScore = tonumber(self.scoreText:getText())
 +
-- set timer properties (bigger add score means faster steps)
 +
local tRepeat = self.score - displayScore
 +
local tDelay =  self.countTime / tRepeat
 +
self.t:stop()
 +
self.t:reset()
 +
self.t:setDelay(tDelay)
 +
self.t:setRepeatCount(tRepeat)
 +
self.t:start()
 +
 +
return self.score
 +
end
 +
 +
--------------------
 +
application:setBackgroundColor(0x4d4d4d)
 +
-- demo
 +
local initialscore = 500
 +
local score = Score.new(initialscore, 6) -- initial score value, increase speed
 +
stage:addChild(score)
 +
local earnedpoints = 300
 +
 +
Timer.delayedCall(3000, function()
 +
score:addScore(earnedpoints) -- target score value
 +
end)
 +
</syntaxhighlight>
  
 
== GFX ==
 
== GFX ==
=== Hex to Number / Number to Hex ===
+
=== Hex to Number / Number to Hex '''@hanzhao''' ===
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
 
--[[
 
--[[
Line 168: Line 347:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== KEY REMAPPING WITH SAVE DATA ==
+
=== Xml Parser '''@Alexander Makeev''' ===
[[File:keyremapping.png||256px]]
 
 
 
In this ''Settings'' scene we can remap any predefined keys. You can navigate using the arrows on your keyboard and press '''ENTER''' to remap a key.
 
 
 
Once you have remapped the keys, the new settings are saved to a file which will be read everytime you restart the game.
 
 
 
'''Code using [[ButtonMonster class|ButtonMonster]]'''
 
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
-- *** SAVING AND LOADING DATA IN JSON FORMAT ***
+
-----------------------------------------------------------------------------------------
require "json" -- don't forget to add the JSON plugin in your project
+
-- LUA only XmlParser from Alexander Makeev
 +
-----------------------------------------------------------------------------------------
 +
XmlParser = {}
  
function saveData(filepath, value)
+
function XmlParser:ToXmlString(value)
local contents = json.encode(value)
+
value = string.gsub(value, "&", "&amp;") -- '&' -> "&amp;"
local file = io.open(filepath, "w") -- create file
+
value = string.gsub(value, "<", "&lt;") -- '<' -> "&lt;"
file:write(contents) -- save json string in file
+
value = string.gsub(value, ">", "&gt;") -- '>' -> "&gt;"
io.close(file)
+
--value = string.gsub(value, "'", "&apos;") -- '\'' -> "&apos;"
 +
value = string.gsub(value, "\"", "&quot;") -- '"' -> "&quot;"
 +
-- replace non printable char -> "&#xD;"
 +
value = string.gsub(value, "([^%w%&%;%p%\t% ])", function(c)
 +
return string.format("&#x%X;", string.byte(c))
 +
--return string.format("&#x%02X;", string.byte(c))
 +
--return string.format("&#%02d;", string.byte(c))
 +
end)
 +
return value
 
end
 
end
  
function getData(filepath)
+
function XmlParser:FromXmlString(value)
local value
+
value = string.gsub(value, "&#x([%x]+)%;", function(h)
local file = io.open(filepath, "r")
+
return string.char(tonumber(h,16))
if file then
+
end)
local contents = file:read("*a") -- read contents
+
value = string.gsub(value, "&#([0-9]+)%;", function(h)
value = json.decode(contents) -- decode json
+
return string.char(tonumber(h,10))
io.close(file)
+
end)
end
+
value = string.gsub(value, "&quot;", "\"")
 +
value = string.gsub(value, "&apos;", "'")
 +
value = string.gsub(value, "&gt;", ">")
 +
value = string.gsub(value, "&lt;", "<")
 +
value = string.gsub(value, "&amp;", "&")
 
return value
 
return value
 +
end
 +
 
 +
function XmlParser:ParseArgs(s)
 +
local arg = {}
 +
string.gsub(s, "(%w+)=([\"'])(.-)%2", function(w, _, a)
 +
arg[w] = self:FromXmlString(a)
 +
end)
 +
return arg
 
end
 
end
  
-- *** LOADING AND SAVING USER PREFS DATA ***
+
function XmlParser:ParseXmlText(xmlText)
-- global prefs functions
+
local stack = {}
function myLoadPrefs(xconfigfilepath)
+
local top = { Name=nil, Value=nil, Attributes={}, ChildNodes={} }
local mydata = getData(xconfigfilepath) -- try to read information from file
+
table.insert(stack, top)
if not mydata then -- if no prefs file, create it
+
local ni, c, label, xarg, empty
mydata = {}
+
local i, j = 1, 1
-- set prefs
+
while true do
mydata.g_language = g_language
+
ni, j, c, label, xarg, empty = string.find(xmlText, "<(%/?)([%w:]+)(.-)(%/?)>", i)
-- controls
+
if not ni then break end
mydata.g_keyleft = g_keyleft
+
local text = string.sub(xmlText, i, ni-1)
mydata.g_keyright = g_keyright
+
if not string.find(text, "^%s*$") then
mydata.g_keyup = g_keyup
+
top.Value=(top.Value or "")..self:FromXmlString(text)
mydata.g_keydown = g_keydown
+
end
mydata.g_keyaction1 = g_keyaction1
+
if empty == "/" then -- empty element tag
mydata.g_keyaction2 = g_keyaction2
+
table.insert(top.ChildNodes,
mydata.g_keyaction3 = g_keyaction3
+
{
-- save prefs
+
Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={}
saveData(xconfigfilepath, mydata) -- create file and save data
+
}
else -- prefs file exists, use it!
+
)
-- set prefs
+
elseif c == "" then -- start tag
g_language = mydata.g_language
+
top = { Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={} }
-- controls
+
table.insert(stack, top) -- new level
g_keyleft = mydata.g_keyleft
+
--log("openTag ="..top.Name)
g_keyright = mydata.g_keyright
+
else -- end tag
g_keyup = mydata.g_keyup
+
local toclose = table.remove(stack) -- remove top
g_keydown = mydata.g_keydown
+
--log("closeTag="..toclose.Name)
g_keyaction1 = mydata.g_keyaction1
+
top = stack[#stack]
g_keyaction2 = mydata.g_keyaction2
+
if #stack < 1 then
g_keyaction3 = mydata.g_keyaction3
+
error("XmlParser: nothing to close with "..label)
 +
end
 +
if toclose.Name ~= label then
 +
error("XmlParser: trying to close "..toclose.Name.." with "..label)
 +
end
 +
table.insert(top.ChildNodes, toclose)
 +
end
 +
i = j + 1
 
end
 
end
end
+
local text = string.sub(xmlText, i)
 
+
if not string.find(text, "^%s*$") then
function mySavePrefs(xconfigfilepath)
+
stack[#stack].Value=(stack[#stack].Value or "")..self:FromXmlString(text)
local mydata = {} -- clear the table
 
-- set prefs
 
mydata.g_language = g_language
 
-- controls
 
mydata.g_keyleft = g_keyleft
 
mydata.g_keyright = g_keyright
 
mydata.g_keyup = g_keyup
 
mydata.g_keydown = g_keydown
 
mydata.g_keyaction1 = g_keyaction1
 
mydata.g_keyaction2 = g_keyaction2
 
mydata.g_keyaction3 = g_keyaction3
 
-- save prefs
 
saveData(xconfigfilepath, mydata) -- save new data
 
end
 
 
 
-- initial default global prefs values
 
g_configfilepath = "|D|configfile.txt" -- data are saved in this file (in the Document app folder)
 
g_language = application:getLanguage()
 
g_keyleft = KeyCode.LEFT
 
g_keyright = KeyCode.RIGHT
 
g_keyup = KeyCode.UP
 
g_keydown = KeyCode.DOWN
 
g_keyaction1 = KeyCode.D -- punch1
 
g_keyaction2 = KeyCode.S -- punch1
 
g_keyaction3 = KeyCode.Q -- jump/jump punch1
 
 
 
-- load saved prefs from file (|D|configfile.txt)
 
myLoadPrefs(g_configfilepath) -- override default values by saved values if any
 
 
 
-- *** GLOBAL FONT AND UI THEME FOR BUTTONS ***
 
myttf = TTFont.new("fonts/Cabin-Regular-TTF.ttf", 2.8*8) -- UI
 
g_ui_theme = {}
 
g_ui_theme.pixelcolorup = 0x8d595d
 
g_ui_theme.pixelcolordown = 0xc09c98
 
g_ui_theme.textcolorup = 0xd9d2cb
 
g_ui_theme.textcolordown = 0xd9d2cb
 
g_ui_theme._textcolordisabled = 0xd9d2cb
 
g_ui_theme.tooltiptextcolor = 0x3a1d20
 
g_ui_theme.tooltipoffsetx = -7*8
 
g_ui_theme.tooltipoffsety = 1*4
 
 
 
-- *** THE OPTIONS SCENE (Class) ***
 
Options = Core.class(Sprite)
 
 
 
local keyNames = { -- display a friendly key name
 
  [KeyCode.LEFT] = "LEFT",
 
  [KeyCode.RIGHT] = "RIGHT",
 
  [KeyCode.UP] = "UP",
 
  [KeyCode.DOWN] = "DOWN",
 
  [KeyCode.NUM0] = "NUMPAD 0",
 
  [KeyCode.NUM1] = "NUMPAD 1",
 
  [KeyCode.NUM2] = "NUMPAD 2",
 
  [KeyCode.NUM3] = "NUMPAD 3",
 
  [KeyCode.NUM4] = "NUMPAD 4",
 
  [KeyCode.NUM5] = "NUMPAD 5",
 
  [KeyCode.NUM6] = "NUMPAD 6",
 
  [KeyCode.NUM7] = "NUMPAD 7",
 
  [KeyCode.NUM8] = "NUMPAD 8",
 
  [KeyCode.NUM9] = "NUMPAD 9",
 
  [KeyCode.SPACE] = "SPACE",
 
  [KeyCode.SHIFT] = "SHIFT",
 
  [KeyCode.CTRL] = "CONTROL",
 
  [KeyCode.ALT] = "ALT",
 
  [KeyCode.TAB] = "TAB",
 
}
 
 
 
function Options:init()
 
-- bg
 
application:setBackgroundColor(0x232a35)
 
-- tooltip layer
 
local tooltiplayer = Sprite.new()
 
-- buttons
 
local key
 
-- movement
 
if (keyNames[g_keyleft] or 0) == 0 then key = utf8.char(g_keyleft)
 
else key = keyNames[g_keyleft]
 
end -- display a friendly key name
 
self.btnLEFT = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="left", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 2, tooltiplayer)
 
if (keyNames[g_keyright] or 0) == 0 then key = utf8.char(g_keyright)
 
else key = keyNames[g_keyright]
 
end -- display a friendly key name
 
self.btnRIGHT = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="right", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 3, tooltiplayer)
 
if (keyNames[g_keyup] or 0) == 0 then key = utf8.char(g_keyup)
 
else key = keyNames[g_keyup]
 
end -- display a friendly key name
 
self.btnUP = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="up", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 1, tooltiplayer)
 
if (keyNames[g_keydown] or 0) == 0 then key = utf8.char(g_keydown)
 
else key = keyNames[g_keydown]
 
end -- display a friendly key name
 
self.btnDOWN = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="down", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 4, tooltiplayer)
 
-- actions
 
if (keyNames[g_keyaction1] or 0) == 0 then key = utf8.char(g_keyaction1)
 
else key = keyNames[g_keyaction1]
 
end -- display a friendly key name
 
self.btnACTION1 = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="punch1", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 5, tooltiplayer)
 
if (keyNames[g_keyaction2] or 0) == 0 then key = utf8.char(g_keyaction2)
 
else key = keyNames[g_keyaction2]
 
end -- display a friendly key name
 
self.btnACTION2 = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="punch2", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 6, tooltiplayer)
 
if (keyNames[g_keyaction3] or 0) == 0 then key = utf8.char(g_keyaction3)
 
else key = keyNames[g_keyaction3]
 
end -- display a friendly key name
 
self.btnACTION3 = ButtonMonster.new({
 
autoscale=false, pixelwidth=17*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text=key, ttf=myttf, textcolorup=g_ui_theme.textcolorup, textcolordown=g_ui_theme.textcolordown,
 
tooltiptext="jump punch", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 7, tooltiplayer)
 
-- btn menu
 
self.btnMENU = ButtonMonster.new({
 
autoscale=false, pixelwidth=12*8, pixelheight=6*8,
 
pixelscalexup=0.8, pixelscalexdown=0.9,
 
pixelcolorup=g_ui_theme.pixelcolorup, pixelcolordown=g_ui_theme.pixelcolordown,
 
text="MENU", ttf=myttf, textcolorup=0x0009B3, textcolordown=0x45d1ff,
 
tooltiptext="Enter", tooltipttf=myttf, tooltiptextcolor=g_ui_theme.tooltiptextcolor,
 
tooltipoffsetx=g_ui_theme.tooltipoffsetx, tooltipoffsety=g_ui_theme.tooltipoffsety,
 
}, 8, tooltiplayer)
 
-- btns table
 
self.btns = {}
 
self.btns[#self.btns + 1] = self.btnUP -- 1
 
self.btns[#self.btns + 1] = self.btnLEFT -- 2
 
self.btns[#self.btns + 1] = self.btnRIGHT -- 3
 
self.btns[#self.btns + 1] = self.btnDOWN -- 4
 
self.btns[#self.btns + 1] = self.btnACTION1 -- 5
 
self.btns[#self.btns + 1] = self.btnACTION2 -- 6
 
self.btns[#self.btns + 1] = self.btnACTION3 -- 7
 
self.btns[#self.btns + 1] = self.btnMENU -- 8
 
self.selector = 1 -- starting button
 
-- position
 
self.btnLEFT:setPosition(1.5*myappwidth/10, 6*8+6*myappheight/10) -- 2
 
self.btnRIGHT:setPosition(3.5*myappwidth/10, 6*8+6*myappheight/10) -- 3
 
self.btnUP:setPosition(2.5*myappwidth/10, 6*8+4.6*myappheight/10) -- 1
 
self.btnDOWN:setPosition(2.5*myappwidth/10, 6*8+7.4*myappheight/10) -- 4
 
for i = 5, 7 do -- action buttons
 
self.btns[i]:setPosition(5.6*myappwidth/10, (i-5)*6*8+6*myappheight/10)
 
 
end
 
end
self.btnMENU:setPosition(myappwidth - self.btnMENU:getWidth()/2, myappheight - self.btnMENU:getHeight()/2)
+
if #stack > 1 then
-- order
+
error("XmlParser: unclosed "..stack[stack.n].Name)
for k, v in ipairs(self.btns) do self:addChild(v) end
 
self:addChild(tooltiplayer)
 
-- buttons listeners
 
for k, v in ipairs(self.btns) do
 
-- v:addEventListener("pressed", function() self.selector = k self:goto() end)
 
v:addEventListener("clicked", function() self.selector = k self:goto() end)
 
v:addEventListener("hovered", function(e) self.selector = e.currselector end)
 
v.btns = self.btns -- FOR KEYBOARD NAVIGATION!
 
 
end
 
end
-- let's go!
+
return stack[1].ChildNodes[1]
self.isremapping = false
 
self:myKeysPressed()
 
self:updateButton()
 
 
end
 
end
  
-- key remapping
+
function XmlParser:ParseXmlFile(xmlFileName)
function Options:remapKey(xbool)
+
local hFile, err = io.open(xmlFileName,"r")
local btn = self.btns[self.selector]
+
if (not err) then
if xbool then
+
local xmlText=hFile:read("*a") -- read file content
self.isremapping = true
+
io.close(hFile)
btn:setColorTransform(255/255, 255/255, 0/255, 255/255)
+
return self:ParseXmlText(xmlText),nil
 
else
 
else
btn:setColorTransform(255/255, 255/255, 255/255, 255/255)
+
return nil, err
 
end
 
end
 
end
 
end
  
-- update button state
+
--[[
function Options:updateButton()
+
-- Usage:
for k, v in ipairs(self.btns) do
+
-- parse xml
v.currselector = self.selector
+
local xml = XmlParser:ParseXmlText(myxmlfile)
v:updateVisualState()
+
]]
 +
</syntaxhighlight>
 +
 
 +
== TABLE ==
 +
=== Var Dump '''@xxx''' ===
 +
<syntaxhighlight lang="lua">
 +
function vardump(value, depth, key)
 +
local linePrefix = ""
 +
local spaces = ""
 +
 
 +
if key ~= nil then
 +
linePrefix = "["..key.."] = "
 
end
 
end
end
 
  
-- KEYS HANDLER
+
if depth == nil then
function Options:myKeysPressed()
+
depth = 0
self:addEventListener(Event.KEY_DOWN, function(e)
+
else
if self.isremapping then -- KEY REMAPPING
+
depth = depth + 1
if e.keyCode == KeyCode.ESC then
+
for i = 1, depth do
self.isremapping = false
+
spaces = spaces .. "  "
self:remapKey(self.isremapping)
 
self:updateButton()
 
return
 
end
 
local keycode = e.keyCode
 
local key = keyNames[keycode]
 
if (keyNames[keycode] or 0) == 0 then key = utf8.char(keycode) end
 
self.btns[self.selector]:changeText(key)
 
if self.selector == 1 then g_keyup = keycode -- follows self.btns order
 
elseif self.selector == 2 then g_keyleft = keycode -- follows self.btns order
 
elseif self.selector == 3 then g_keyright = keycode -- follows self.btns order
 
elseif self.selector == 4 then g_keydown = keycode -- follows self.btns order
 
elseif self.selector == 5 then g_keyaction1 = keycode -- follows self.btns order
 
elseif self.selector == 6 then g_keyaction2 = keycode -- follows self.btns order
 
elseif self.selector == 7 then g_keyaction3 = keycode -- follows self.btns order
 
end
 
self.isremapping = false
 
self:remapKey(self.isremapping)
 
self:updateButton()
 
mySavePrefs(g_configfilepath)
 
else -- NOT KEY REMAPPING
 
if e.keyCode == KeyCode.ESC then self:back() end
 
-- keyboard
 
if e.keyCode == KeyCode.UP or e.keyCode == g_keyup or e.keyCode == KeyCode.LEFT or e.keyCode == g_keyleft then
 
self.selector -= 1 if self.selector < 1 then self.selector = #self.btns end
 
self:updateButton()
 
elseif e.keyCode == KeyCode.DOWN or e.keyCode == g_keydown or e.keyCode == KeyCode.RIGHT or e.keyCode == g_keyright then
 
self.selector += 1 if self.selector > #self.btns then self.selector = 1 end
 
self:updateButton()
 
end
 
-- modifier
 
local modifier = application:getKeyboardModifiers()
 
local alt = (modifier & KeyCode.MODIFIER_ALT) > 0
 
if not alt and (e.keyCode == KeyCode.ENTER or e.keyCode == KeyCode.SPACE or e.keyCode == g_keyaction1) then -- validate
 
self:goto()
 
end
 
 
end
 
end
end)
+
end
end
 
  
-- scenes navigation
+
if type(value) == 'table' then
function Options:goto()
+
local mTable = getmetatable(value)
for k, v in ipairs(self.btns) do
+
if mTable == nil then
if k == self.selector then
+
print(spaces ..linePrefix.."(table) ")
if v.isdisabled then print("btn disabled!", k)
+
else
elseif k >= 1 and k <= #self.btns-1 then self:remapKey(true) -- player1 controls/actions
+
print(spaces .."(metatable) ")
elseif k == #self.btns then self:back() -- last button is go back to menu scene
+
value = mTable
else print("nothing here!", k)
+
end
end
+
for tableKey, tableValue in pairs(value) do
 +
vardump(tableValue, depth, tableKey)
 
end
 
end
 +
elseif type(value) == 'function' or
 +
type(value) == 'thread' or
 +
type(value) == 'userdata' or
 +
value == nil
 +
then
 +
print(spaces..tostring(value))
 +
else
 +
print(spaces..linePrefix.."("..type(value)..") "..tostring(value))
 
end
 
end
 
end
 
end
function Options:back()
 
print("SWITCH TO MENU SCENE")
 
-- scenemanager:changeScene("menu", 1, transitions[1], easings[3])
 
end
 
  
-- *** START THE SCENE ***
+
--[[
stage:addChild(Options.new())
+
Usage:
 +
foo="Hello world"
 +
vardump(foo)
 +
vardump( { print, 12, nil, io.stdin, math } )
 +
 
 +
foo = {
 +
"zero",1,2,3,{1,{1,2,3,4,{1,2,{1,"cool",2},4},6},3,vardump,5,6},          5,{Mary=10, Paul="10"},"last value"
 +
}
 +
vardump(foo)
 +
--]]
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
 +
  
  

Latest revision as of 04:06, 31 July 2025

Here are some longer code (classes) from the Gideros forum and some other places :-)

All samples are ready to use. Enjoy!

AUDIO

Producing Sound Without Resources @PaulH

Creates files on the fly using the .wav format for playback

-- https://forum.gideros.rocks/discussion/comment/68095/#Comment_68095
function make_wave(file_name, length, wave, freq, vol)
	-- We'll need to write some 32 bit values in binary. Helper function for that:
	local function to4Bytes(n) return n & 0xff, (n>>8) & 0xff, (n>>16) & 0xff, (n>>24) & 0xff end

	-- For a given point (sample number) in a wav file, return the 16 bit value for the specified sound wave and frequency
	local function sound_wave(wave,p,freq,vol)
		local sample_rate = 6000
		local n = 0 -- Default to silence
		if wave == "noise" then n = math.random(-255*128*vol, 255*128*vol)
		elseif wave == "sine" then n = math.floor(math.sin(p/sample_rate*freq*math.rad(180))*256*vol*127)
		elseif wave == "sawtooth" then n = math.floor((freq*p/sample_rate)*256*vol*256)
		end
		local c2, c1 = (n>>8) & 0xff, n & 0xff
		return string.char(c1, c2)
	end

	-- We're just dealing with a fixed format wav file:
	local sample_rate = 6000
	local samples = length*sample_rate
	local data_size = samples*2
	local total_file_length = 44+data_size
	local fl1, fl2, fl3, fl4 = to4Bytes(total_file_length)
	local ds1, ds2, ds3, ds4 = to4Bytes(data_size)
	local f = io.open(file_name, "wb")
	-- Write the WAV file header:
	f:write(
		string.char(
			82,73,70,70,--RIFF
			fl1, fl2, fl3, fl4,-- File size overall
			87,65,86,69,-- WAVE header
			102,109,116,32,--fmt chunk marker w/ trailing null
			16,0,0,0, -- Length of format data (16 bytes before this)
			1,0, -- Wave type. 1 == PCM
			1,0, -- Channels. 1 == mono
			112,23,0,0,-- Sample rate (Hertz)
			224,46,0,0, -- Bits per sample * channels * sample rate / 8
			2,0,
			16,0, -- Bytes per sample - this is 16 
			100,97,116,97, -- "data" - chunk header
			ds1,ds2,ds3,ds4-- size of data section
		)
	)
	-- Write the data:
	for l = 1, samples do
		f:write(sound_wave(wave,l,freq, vol or 1))
	end
	f:close()
end

-- Demo some white noise:
--make_wave("|D|noise.wav", 1, "noise") -- 0.1 second sine wave at 256 Herz (middle C) at full volume
--s=Sound.new("|D|noise.wav")
--s:play()

-- one note with a sawtooth wave:
--make_wave("|D|note.wav", 2, "sawtooth", 262, 1) -- 0.1 second sine wave at 256 Herz (middle C) at full volume
--s=Sound.new("|D|note.wav")
--s:play()

-- Make wave files for the chromatic scale.
-- The frequencies in Hertz of the notes in octave zero:
notes = { 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.5, 25.96, 27.50, 29.14, 30.87 }
-- 3 letter names for those notes:
names = { "C0n", "C0s", "D0n", "D0s", "E0n", "F0n", "F0s", "G0n", "G0s", "A0n", "A0s", "B0n" }
-- Compute the ratio of each note to the C:
local note_multipliers = {}
for i = 1, #notes do
	note_multipliers[i] = notes[i]/notes[1]
end

-- Make a sound for each note in several octaves:
local sounds = {}
for octave = 3, 7 do
	for i = 1, #names do
		local original_note = names[i]
		local octave_note = string.gsub(original_note, "0", octave)
		local file_name = "|D|note_" .. octave_note .. ".wav"
		-- Compute the frequency for the note by applying the ratio of that note over C
		-- and then apply the octave multiplier. Each octave is double the frequency
		-- of the one below it:
		local fr = math.floor(notes[1] * note_multipliers[i] * math.pow(2, octave))
		-- We'll make each note 0.2 seconds, using a sine wave
		--function make_wave(file_name, length, wave, freq, vol)
		make_wave(file_name, 0.2, "sine", fr, 1)
		sounds[octave_note] = Sound.new(file_name)
	end
end

-- Given a string of 3 letter note names, play those notes in order:
function play_sequence(seq)
	for i = 1, string.len(seq), 3 do
		local note = string.sub(seq, i, i+2)
		if sounds[note] then
			Timer.delayedCall(60 * (i-1), function()
				sounds[note]:play()
			end)
		end
	end
end

local fur_elise =
	"E4nD4sE4nD4sE4nB3nD4nC4nA3n      "..
	"C3nE3nA3nB3n      "..
	"E3nG3sB3nC4n      "..
	"E3nE4nD4sE4nD4sE4nB3nD4nC4nA3n      "..
	"C3nE3nA3nB3n      "..
	"E3nC4nB3nA3n"
play_sequence(fur_elise)
-- To play an octave higher:
Timer.delayedCall(9000, function()
	play_sequence(string.gsub(string.gsub(fur_elise,"4","5"),"3","4"))
end)

STRING

Score Counting TextField @errorpi

--[[
	Score Counting TextField
	by: @errorpi
]]

Score = Core.class(Sprite)
 
function Score:init(xscore, xincrease)
	self.score = xscore or 0
	self.countTime = (xincrease or 5) * 100
	-- a textfield
	self.scoreText = TextField.new(nil, tostring(self.score))
	self.scoreText:setLayout({wid = 80, hei = 40, flags=FontBase.TLF_CENTER})
	self.scoreText:setTextColor(math.random(0xffffff))
	self.scoreText:setScale(5)
	self.scoreText:setPosition(64, 64)
	self:addChild(self.scoreText)
	-- a timer
	self.t = Timer.new(1) -- timer init
	self.t:addEventListener(Event.TIMER, self.updateText, self)
end
 
function Score:updateText()
	local displayScore = tonumber(self.scoreText:getText())
	if displayScore < self.score then
		self.scoreText:setText(tostring( displayScore + 1) )
	end
end

function Score:addScore(add)
	self.score += add
	local displayScore = tonumber(self.scoreText:getText())
	-- set timer properties (bigger add score means faster steps)
	local tRepeat = self.score - displayScore
	local tDelay =  self.countTime / tRepeat
	self.t:stop()
	self.t:reset()
	self.t:setDelay(tDelay)
	self.t:setRepeatCount(tRepeat)
	self.t:start()

	return self.score
end

--------------------
application:setBackgroundColor(0x4d4d4d)
-- demo
local initialscore = 500
local score = Score.new(initialscore, 6) -- initial score value, increase speed
stage:addChild(score)
local earnedpoints = 300

Timer.delayedCall(3000, function()
	score:addScore(earnedpoints) -- target score value
end)

GFX

Hex to Number / Number to Hex @hanzhao

--[[
Lua Hex v0.4
-------------------
Hex conversion lib for Lua.

How to use:
 lhex.to_hex(n) -- convert a number to a hex string
 lhex.to_dec(hex) -- convert a hex "string" (prefix with "0x..." or "0X...") to number

Part of LuaBit(http://luaforge.net/projects/bit/).

Under the MIT license.

copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com)
--]]

local function to_bits(n)
	-- checking not float
	if(n - (n//1) > 0) then error("trying to apply bitwise operation on non-integer!") end
	if(n < 0) then return to_bits(~(n//1) + 1) end -- negative
	-- to bits table
	local tbl = {}
	local cnt = 1
	while (n > 0) do
		local last = n%2
		if(last == 1) then tbl[cnt] = 1
		else tbl[cnt] = 0
		end
		n = (n-last)/2
		cnt += 1
	end
	return tbl
end

local function tbl_to_number(tbl)
	local n = #tbl
	local rslt = 0
	local power = 1
	for i = 1, n do
		rslt += tbl[i]*power
		power *= 2
	end
	return rslt
end

local function to_hex(n)
	if(type(n) ~= "number") then error("non-number type passed in.") end
	-- checking not float
	if(n - (n//1) > 0) then error("trying to apply bitwise operation on non-integer!") end
	if(n < 0) then -- negative
		n = to_bits(~(-n<>n) + 1)
		n = tbl_to_number(n)
	end
	local hex_tbl = { "A", "B", "C", "D", "E", "F" }
	local hex_str = ""
	while(n ~= 0) do
		local last = n%16
		if(last < 10) then hex_str = tostring(last) .. hex_str
		else hex_str = hex_tbl[last-10+1] .. hex_str
		end
		n = (n/16)//1 -- floor
	end
	if(hex_str == "") then hex_str = "0" end
	return "0x"..hex_str
end

local function to_dec(hexstring)
	if(type(hexstring) ~= "string") then error("non-string type passed in.") end
	local head = string.sub(hexstring, 1, 2)
	if(head ~= "0x" and head ~= "0X") then error("wrong hex format, should lead by 0x or 0X.") end
	return tonumber(hexstring:sub(3), 16) -- base 16
end

--------------------
-- lua hex lib interface
lhex = {
	to_hex=to_hex,
	to_dec=to_dec,
}

--------------------
-- examples
local dec = 4341688
local hex = lhex.to_hex(dec) -- number to hex
print(hex) -- 0x423FB8
local revdec = lhex.to_dec(hex) -- hex string (prefix with "0x" or "0X") to number
print(revdec) -- 4341688

print(lhex.to_dec("0x0")) -- 0, hex string (prefix with "0x" or "0X") to number
print(lhex.to_dec("0x00ff00")) -- 65280, hex string (prefix with "0x" or "0X") to number
print(lhex.to_hex(16777215)) -- 0xFFFFFF, number to hex

local pix = Pixel.new(lhex.to_hex(56816), 1, 32, 32)
stage:addChild(pix)

print("hex is", hex)

FILES

Get Files in Folder @xxx

note: requires the Lfs plugin

Gets all the files in a folder matching a file extension and store them in a list.

local myos = application:getDeviceInfo()
local iswin32 = if myos == "win32" then true else false
--print(iswin32)

function getFilesInFolder(xpath, xlist)
	local lfs = require "lfs"
	for file in lfs.dir(xpath) do
		if file ~= "." and file ~= ".." then
			if file:match(".png$") then -- choose your file extension here
				if iswin32 then
					local f = xpath.."\\"..file
--					print(f)
					xlist[#xlist + 1] = f:gsub("/", "\\")
				else
					local f = xpath.."/"..file
--					print(f)
					xlist[#xlist + 1] = f:gsub("\\", "/")
				end
			end
		end
	end
	lfs = nil
end

Demo code using ButtonMonster

local myos = application:getDeviceInfo()
local iswin32 = if myos == "win32" then true else false
--print(iswin32)

local folderimages = {} -- list all images in user selected folder
local images = {} -- list all images with valid extension
local btnimagefolder = ButtonMonster.new({
	pixelcolorup=0xffaa00, text="import images", textcolorup=0xaa0000, textscalex=3,
	isautoscale=true,
})
btnimagefolder:setPosition(16, 16)
stage:addChild(btnimagefolder)
btnimagefolder:addEventListener("clicked", function()
	local path
	if iswin32 then path = application:get("openDirectoryDialog", "Select Folder", "C:\\tmp\\")
	else path = application:get("openDirectoryDialog", "Select Folder", "C:/tmp/")
	end -- print(path)
	if path then getFilesInFolder(path, folderimages) end
	if folderimages and #folderimages > 0 then
		for i = 1, #folderimages do
			images[i] = Bitmap.new(Texture.new(folderimages[i]), true)
			images[i]:setAnchorPoint(0.5, 1)
			images[i]:setPosition(8, 8)
			-- ...
		end
	end
end)

Xml Parser @Alexander Makeev

-----------------------------------------------------------------------------------------
-- LUA only XmlParser from Alexander Makeev
-----------------------------------------------------------------------------------------
XmlParser = {}

function XmlParser:ToXmlString(value)
	value = string.gsub(value, "&", "&amp;")		-- '&' -> "&amp;"
	value = string.gsub(value, "<", "&lt;")		-- '<' -> "&lt;"
	value = string.gsub(value, ">", "&gt;")		-- '>' -> "&gt;"
	--value = string.gsub(value, "'", "&apos;")	-- '\'' -> "&apos;"
	value = string.gsub(value, "\"", "&quot;")	-- '"' -> "&quot;"
	-- replace non printable char -> "&#xD;"
	value = string.gsub(value, "([^%w%&%;%p%\t% ])", function(c)
		return string.format("&#x%X;", string.byte(c))
		--return string.format("&#x%02X;", string.byte(c))
		--return string.format("&#%02d;", string.byte(c))
	end)
	return value
end

function XmlParser:FromXmlString(value)
	value = string.gsub(value, "&#x([%x]+)%;", function(h)
		return string.char(tonumber(h,16))
	end)
	value = string.gsub(value, "&#([0-9]+)%;", function(h)
		return string.char(tonumber(h,10))
	end)
	value = string.gsub(value, "&quot;", "\"")
	value = string.gsub(value, "&apos;", "'")
	value = string.gsub(value, "&gt;", ">")
	value = string.gsub(value, "&lt;", "<")
	value = string.gsub(value, "&amp;", "&")
	return value
end
   
function XmlParser:ParseArgs(s)
	local arg = {}
	string.gsub(s, "(%w+)=([\"'])(.-)%2", function(w, _, a)
		arg[w] = self:FromXmlString(a)
	end)
	return arg
end

function XmlParser:ParseXmlText(xmlText)
	local stack = {}
	local top = { Name=nil, Value=nil, Attributes={}, ChildNodes={} }
	table.insert(stack, top)
	local ni, c, label, xarg, empty
	local i, j = 1, 1
	while true do
		ni, j, c, label, xarg, empty = string.find(xmlText, "<(%/?)([%w:]+)(.-)(%/?)>", i)
		if not ni then break end
		local text = string.sub(xmlText, i, ni-1)
		if not string.find(text, "^%s*$") then
			top.Value=(top.Value or "")..self:FromXmlString(text)
		end
		if empty == "/" then -- empty element tag
			table.insert(top.ChildNodes,
				{
					Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={}
				}
			)
		elseif c == "" then -- start tag
			top = { Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={} }
			table.insert(stack, top) -- new level
			--log("openTag ="..top.Name)
		else -- end tag
			local toclose = table.remove(stack) -- remove top
			--log("closeTag="..toclose.Name)
			top = stack[#stack]
			if #stack < 1 then
				error("XmlParser: nothing to close with "..label)
			end
			if toclose.Name ~= label then
				error("XmlParser: trying to close "..toclose.Name.." with "..label)
			end
			table.insert(top.ChildNodes, toclose)
		end
		i = j + 1
	end
	local text = string.sub(xmlText, i)
	if not string.find(text, "^%s*$") then
		stack[#stack].Value=(stack[#stack].Value or "")..self:FromXmlString(text)
	end
	if #stack > 1 then
		error("XmlParser: unclosed "..stack[stack.n].Name)
	end
	return stack[1].ChildNodes[1]
end

function XmlParser:ParseXmlFile(xmlFileName)
	local hFile, err = io.open(xmlFileName,"r")
	if (not err) then
		local xmlText=hFile:read("*a") -- read file content
		io.close(hFile)
		return self:ParseXmlText(xmlText),nil
	else
		return nil, err
	end
end

--[[
-- Usage:
-- parse xml
local xml = XmlParser:ParseXmlText(myxmlfile)
]]

TABLE

Var Dump @xxx

function vardump(value, depth, key)
	local linePrefix = ""
	local spaces = ""

	if key ~= nil then
		linePrefix = "["..key.."] = "
	end

	if depth == nil then
		depth = 0
	else
		depth = depth + 1
		for i = 1, depth do
			spaces = spaces .. "  "
		end
	end

	if type(value) == 'table' then
		local mTable = getmetatable(value)
		if mTable == nil then
			print(spaces ..linePrefix.."(table) ")
		else
			print(spaces .."(metatable) ")
			value = mTable
		end		
		for tableKey, tableValue in pairs(value) do
			vardump(tableValue, depth, tableKey)
		end
	elseif type(value) == 'function' or
		type(value)	== 'thread' or
		type(value)	== 'userdata' or
		value == nil
		then
		print(spaces..tostring(value))
	else
		print(spaces..linePrefix.."("..type(value)..") "..tostring(value))
	end
end

--[[
Usage:
foo="Hello world"
vardump(foo)
vardump( { print, 12, nil, io.stdin, math } )

foo = {
	"zero",1,2,3,{1,{1,2,3,4,{1,2,{1,"cool",2},4},6},3,vardump,5,6},          5,{Mary=10, Paul="10"},"last value"
}
vardump(foo)
--]]




More to come God's willing...