Skip to content

Instantly share code, notes, and snippets.

@DolenzSong
Last active May 15, 2016 22:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DolenzSong/ef91fd371fd13fe1c00a to your computer and use it in GitHub Desktop.
Save DolenzSong/ef91fd371fd13fe1c00a to your computer and use it in GitHub Desktop.
ChoiceMaker doesn't make anything right now, but is an initial implementation of a method for specifying screens and UIs as simple functions
--# Main
-- ChoiceMaker
--80 columns:
--3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
displayMode(OVERLAY)
supportedOrientations(LANDSCAPE_ANY)
function setup()
--monitor performance
profiler.init()
--toggle for reporting debug messages
setReporting(true)
--set global styles
rectMode(CENTER)
font("HelveticaNeue-Light")
fontSize(24)
stroke(255, 255, 255, 255)
strokeWidth(3)
fill(255, 255, 255, 255)
--set starting state
showFirstScreen()
showFirstUI()
--create win and lose screens
winScreen = image(WIDTH,HEIGHT)
setContext(winScreen)
background(255, 231, 0, 255)
sprite("SpaceCute:Star", WIDTH / 2, HEIGHT/2,WIDTH,HEIGHT)
setContext()
loseScreen = image(WIDTH,HEIGHT)
setContext(loseScreen)
background(73, 30, 33, 255)
sprite("Tyrian Remastered:Flame Wave", WIDTH / 2, HEIGHT/2,WIDTH,HEIGHT)
setContext()
--tests()
startScreen = imageResizedToScreen(readImage("Cargo Bot:Game Area"))
uiPieceHandler.backgroundImage = startScreen
uiPieceHandler.shouldUpdateScreenBlur = true
end
function draw()
profiler.draw()
currentScreen()
currentUI()
end
--measure performance:
profiler={}
function profiler.init(quiet)
profiler.del=0
profiler.c=0
profiler.fps=0
profiler.mem=0
if not quiet then
parameter.watch("profiler.fps")
parameter.watch("profiler.mem")
end
end
function profiler.draw()
profiler.del = profiler.del + DeltaTime
profiler.c = profiler.c + 1
if profiler.c==10 then
profiler.fps=profiler.c/profiler.del
profiler.del=0
profiler.c=0
profiler.mem=collectgarbage("count", 2)
end
end
--# setup
--showFirstScreen sets the currentScreen (will be loaded from a saved value in the future)
function showFirstScreen()
if currentScreen == nil then
currentScreen = sampleScreenA
end
end
--showFirstUI sets the currentUI (will be loaded from a saved value in the future)
function showFirstUI()
report("showing UI")
if currentUI == nil then
choicesActive = false
currentUI = introductionUI
end
end
--defineMainButtons sets up the main buttons for sample screens and UI.
--this means changes made to these in editable mode will not persist between sessions
function defineMainButtons()
--button for introductionUI: activates editable mode
uiPieceHandler.buttons['welcome'] =
{x = 502.5, y = 384.5, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = function()
showPlayModeUI()
choicesActive = true
end}
--button for toggling editableMode
uiPieceHandler.buttons['to editable mode'] =
{x = 928.5, y = 729.0, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = showEditModeUI}
--button for toggling fixed mode
uiPieceHandler.buttons['to fixed mode'] =
{x = 932.5, y = 727.5, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = showPlayModeUI}
--button for resetting sample game, used by both end screens
uiPieceHandler.buttons['reset sample game'] =
{x = 865.0, y = 248.5, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = uiPieceHandler.defaultButtonAction}
--buttonTable definition for "you lose" text area
uiPieceHandler.buttons['you lost sample game'] =
{x = 865.0, y = 315.5, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = uiPieceHandler.defaultButtonAction}
--buttonTable definition for "you win" text area
uiPieceHandler.buttons['you won sample game'] =
{x = 861.5, y = 319.0, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = uiPieceHandler.defaultButtonAction}
--button for losing sample game, used on the start screen of the sample game
uiPieceHandler.buttons['lose sample game'] =
{x = 860.0, y = 167.5, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = uiPieceHandler.defaultButtonAction}
--buttonTable definition for start screen text area
uiPieceHandler.buttons['sample game start screen'] =
{x = 860.5, y = 321.0, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = uiPieceHandler.defaultButtonAction}
--button for winning sample game, used on the start screen of the sample game
uiPieceHandler.buttons['win sample game'] =
{x = 860.0, y = 244.0, width=uiPieceHandler.defaultWidth,
height=uiPieceHandler.defaultHeight,
action = uiPieceHandler.defaultButtonAction}
end
--# screens
--screen defaults
choiceWidth = 400
narrationWidth = choiceWidth
narrationHeight = 300
--draws backgroundImage and updates blur if needed
drawBackground = function(backgroundImage)
sprite(backgroundImage, WIDTH / 2, HEIGHT/2,WIDTH,HEIGHT)
if uiPieceHandler.shouldUpdateScreenBlur then
uiPieceHandler.screenBlur = imageWithGaussian2PassBlur(backgroundImage)
uiPieceHandler.shouldUpdateScreenBlur = false
end
end
--all the screens that can be used as currentScreen
function sampleScreenA()
drawBackground(startScreen)
textArea("sample game start screen",narrationWidth,narrationHeight)
choice("win sample game", sampleScreenB,choiceWidth)
choice("lose sample game", sampleScreenC,choiceWidth)
end
function sampleScreenB()
drawBackground(winScreen)
textArea("you won sample game",narrationWidth,narrationHeight)
choice("reset sample game", sampleScreenA,choiceWidth)
end
function sampleScreenC()
drawBackground(loseScreen)
textArea("you lost sample game",narrationWidth,narrationHeight)
choice("reset sample game", sampleScreenA,choiceWidth)
end
--# uiModes
--UI defaults
uiModeButtonWidth = 300
uiModeButtonFontColor = color(229, 223, 94, 255)
--all the UIs that can be used as currentUI
function introductionUI()
tintScreen(color(255,128,128))
button("welcome", function()
showPlayModeUI()
choicesActive = true
end)
end
function editModeUI()
tintScreen(color(0,128,128,95))
button("to fixed mode", showPlayModeUI,uiModeButtonWidth,nil,uiModeButtonFontColor)
end
function playModeUI()
button("to editable mode", showEditModeUI,uiModeButtonWidth,nil,uiModeButtonFontColor)
end
--these are the routines that manage UI displays
function showUI()
if currentUI == nil then
currentUI = introductionUI
end
end
function showEditModeUI()
currentUI = editModeUI
buttons_are_draggable = true
end
function showPlayModeUI()
currentUI = playModeUI
buttons_are_draggable = false
end
--# uiPieces
--pieces are interface elements like buttons, text areas, etc
--because I began with the button class, all pieces are called 'buttons' in many places
--thess should all be refactored so that there's a generic 'piece' function
--button only actually needs a name to work, the rest have defaults
function button(name, action, width, height, fontColor)
--create a default button if none exists under this name
if uiPieceHandler.buttons[name] == nil then
uiPieceHandler.defaultButton(name)
end
--set button drawing values, using saved values if none passed in
local buttonTable = uiPieceHandler.buttons[name]
local x,y = buttonTable.x, buttonTable.y
width = width or buttonTable.width
height = height or buttonTable.height
fontColor = fontColor or buttonTable.fontColor
--update the stored values if necessary
if width ~= buttonTable.width then
uiPieceHandler.buttons[name].width = width
end
if height ~= buttonTable.height then
uiPieceHandler.buttons[name].height = height
end
if fontColor ~= buttonTable.fontColor then
uiPieceHandler.buttons[name].fontColor = fontColor
end
--'action' must be stored, not retrieved; it's called outside of this function
if action ~= nil or buttonTable.action == nil then
buttonTable.action = action
end
--draw the button
roundedRectangle{
x=x,y=y,w=width,h=height,
tex=uiPieceHandler.screenBlur,
texCoord=vec4(x,y,width,height)}
styleSafe(function()
fill(fontColor)
text(name, x, y)
end)
--handle touches (wherein action gets called or not)
uiPieceHandler.evaluateTouchFor(name)
--set the flag that shows we rendered
uiPieceHandler.buttons[name].didRenderAlready = true
end
--textArea is a button that has no action and defaults to black text
function textArea(textToShow, width, height, fontColor)
--a placeholder action value
local action
--if first draw, set temporary empty action (in case tapped in first draw)
if uiPieceHandler.buttons[name] == nil then
action = function() end
end
--pass all the values to button()--by default setting border transparent (as above)
button(textToShow, action, width, height, fontColor)
--if button action isn't nil yet, nil it--this will only be needed first time drawn
if uiPieceHandler.buttons[textToShow].action ~= nil then
uiPieceHandler.buttons[textToShow].action = nil
end
end
--choice creates a button whose action changes currentScreen to the specified screen
function choice(choiceText, resultScreenAsFunction, width, height, fontColor)
--define the change screen action, depending on choicesActive setting
local choiceAction
local actionNotSet = uiPieceHandler.buttons[choiceText].action == uiPieceHandler.defaultButtonAction
if actionNotSet and resultScreenFunction ~= nil then
choiceAction = function ()
currentScreen = resultScreenAsFunction
uiPieceHandler.shouldUpdateScreenBlur = true
end
end
--set custom font color and pass all values to button
fontColor = fontColor or color(160, 173, 223, 255)
button(choiceText, choiceAction, width, height, fontColor)
end
--[[
true mesh rounded rectangle. Original by @LoopSpace
with anti-aliasing, optional fill and stroke components, optional texture that preserves aspect ratio of original image, automatic mesh caching
usage: RoundedRectangle{key = arg, key2 = arg2}
required: x;y;w;h: dimensions of the rectangle
optional: radius: corner rounding radius, defaults to 6;
corners: bitwise flag indicating which corners to round, defaults to 15 (all corners).
Corners are numbered 1,2,4,8 starting in lower-left corner proceeding clockwise
eg to round the two bottom corners use: 1 | 8
to round all the corners except the top-left use: ~ 2
tex: texture image
texCoord: vec4 specifying x,y,width,and height to use as texture coordinates
scale: size of rect (using scale)
use standard fill(), stroke(), strokeWidth() to set body fill color, outline stroke color and stroke width
]]
local __RRects = {}
function roundedRectangle(t)
local s = t.radius or 8
local c = t.corners or 15
local w = math.max(t.w+1,2*s)+1
local h = math.max(t.h,2*s)+2
local hasTexture = 0
local texCoord = t.texCoord or vec4(0,0,1,1) --default to bottom-left-most corner, full with and height
if t.tex then hasTexture = 1 end
local label = table.concat({w,h,s,c,hasTexture,texCoord.x,texCoord.y},",")
if not __RRects[label] then
local rr = mesh()
rr.shader = shader(rrectshad.vert, rrectshad.frag)
local v = {}
local no = {}
local n = math.max(3, s//2)
local o,dx,dy
local edge, cent = vec3(0,0,1), vec3(0,0,0)
for j = 1,4 do
dx = 1 - 2*(((j+1)//2)%2)
dy = -1 + 2*((j//2)%2)
o = vec2(dx * (w * 0.5 - s), dy * (h * 0.5 - s))
-- if math.floor(c/2^(j-1))%2 == 0 then
local bit = 2^(j-1)
if c & bit == bit then
for i = 1,n do
v[#v+1] = o
v[#v+1] = o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n)))
v[#v+1] = o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n)))
no[#no+1] = cent
no[#no+1] = edge
no[#no+1] = edge
end
else
v[#v+1] = o
v[#v+1] = o + vec2(dx * s,0)
v[#v+1] = o + vec2(dx * s,dy * s)
v[#v+1] = o
v[#v+1] = o + vec2(0,dy * s)
v[#v+1] = o + vec2(dx * s,dy * s)
local new = {cent, edge, edge, cent, edge, edge}
for i=1,#new do
no[#no+1] = new[i]
end
end
end
-- print("vertices", #v)
-- r = (#v/6)+1
rr.vertices = v
rr:addRect(0,0,w-2*s,h-2*s)
rr:addRect(0,(h-s)/2,w-2*s,s)
rr:addRect(0,-(h-s)/2,w-2*s,s)
rr:addRect(-(w-s)/2, 0, s, h - 2*s)
rr:addRect((w-s)/2, 0, s, h - 2*s)
--mark edges
local new = {cent,cent,cent, cent,cent,cent,
edge,cent,cent, edge,cent,edge,
cent,edge,edge, cent,edge,cent,
edge,edge,cent, edge,cent,cent,
cent,cent,edge, cent,edge,edge}
for i=1,#new do
no[#no+1] = new[i]
end
rr.normals = no
--texture
if t.tex then
rr.shader.fragmentProgram = rrectshad.fragTex
rr.texture = t.tex
local w,h = t.tex.width,t.tex.height
local textureOffsetX,textureOffsetY = texCoord.x,texCoord.y
local coordTable = {}
for i,v in ipairs(rr.vertices) do
coordTable[i] = vec2((v.x + textureOffsetX)/w, (v.y + textureOffsetY)/h)
end
rr.texCoords = coordTable
end
local sc = 1/math.max(2, s)
rr.shader.scale = sc --set the scale, so that we get consistent one pixel anti-aliasing, regardless of size of corners
__RRects[label] = rr
end
__RRects[label].shader.fillColor = color(fill())
if strokeWidth() == 0 then
__RRects[label].shader.strokeColor = color(fill())
else
__RRects[label].shader.strokeColor = color(stroke())
end
if t.resetTex then
__RRects[label].texture = t.resetTex
t.resetTex = nil
end
local sc = 0.25/math.max(2, s)
__RRects[label].shader.strokeWidth = math.min( 1 - sc*3, strokeWidth() * sc)
pushMatrix()
translate(t.x,t.y)
scale(t.scale or 1)
__RRects[label]:draw()
popMatrix()
end
rrectshad ={
vert=[[
uniform mat4 modelViewProjection;
attribute vec4 position;
//attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normal;
//varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying vec3 vNormal;
void main()
{
// vColor = color;
vTexCoord = texCoord;
vNormal = normal;
gl_Position = modelViewProjection * position;
}
]],
frag=[[
precision highp float;
uniform lowp vec4 fillColor;
uniform lowp vec4 strokeColor;
uniform float scale;
uniform float strokeWidth;
//varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying vec3 vNormal;
void main()
{
lowp vec4 col = mix(strokeColor, fillColor, smoothstep((1. - strokeWidth) - scale * 0.5, (1. - strokeWidth) - scale * 1.5 , vNormal.z)); //0.95, 0.92,
col = mix(vec4(col.rgb, 0.), col, smoothstep(1., 1.-scale, vNormal.z) );
// col *= smoothstep(1., 1.-scale, vNormal.z);
gl_FragColor = col;
}
]],
fragTex=[[
precision highp float;
uniform lowp sampler2D texture;
uniform lowp vec4 fillColor;
uniform lowp vec4 strokeColor;
uniform float scale;
uniform float strokeWidth;
//varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying vec3 vNormal;
void main()
{
vec4 pixel = texture2D(texture, vTexCoord) * fillColor;
lowp vec4 col = mix(strokeColor, pixel, smoothstep(1. - strokeWidth - scale * 0.5, 1. - strokeWidth - scale * 1.5, vNormal.z)); //0.95, 0.92,
// col = mix(vec4(0.), col, smoothstep(1., 1.-scale, vNormal.z) );
col *= smoothstep(1., 1.-scale, vNormal.z);
gl_FragColor = col;
}
]]
}
--# uiPieceHandler
-- uiPieceHandler: provides various functions for UI pieces:
-- enables pieces to be initialized with defaults
-- manages how pieces look and behave
uiPieceHandler = {}
uiPieceHandler.defaultWidth = 160
uiPieceHandler.defaultHeight = 55
uiPieceHandler.defaultFontColor = color(89, 75, 75, 255)
uiPieceHandler.buttons = {}
uiPieceHandler.shouldUpdateScreenBlur = true
uiPieceHandler.backgroundImage = 0 --not sure how this will be set irl
uiPieceHandler.screenBlur = 0 --0 means "none drawn yet"; will normally be an image
parameter.boolean("buttons are draggable", false)
uiPieceHandler.defaultButton = function(name)
uiPieceHandler.buttons[name] = {x=math.random(WIDTH),y=math.random(HEIGHT),
width = uiPieceHandler.defaultWidth, height = uiPieceHandler.defaultHeight,
fontColor = uiPieceHandler.defaultFontColor,
didRenderAlready = false}
end
uiPieceHandler.defaultButtonAction = function()
report("in 'setup()', use 'buttonAction(name, action)' to define an action for"..
"this button")
end
uiPieceHandler.doAction = function(name)
if uiPieceHandler.buttons[name].action == nil then
return
else
uiPieceHandler.buttons[name].action()
end
end
uiPieceHandler.clearRenderFlags = function()
for name, buttonTable in pairs(uiPieceHandler.buttons) do
buttonTable.didRenderAlready = false --now see if rendering is triggered at the right time
end
end
--evaluateTouchFor: called by each button inside the button() function
--precondition: to use CurrentTouch, pass nothing to the touch value
--postcondition: one of these:
-- a new activatedButton is set (if touch began on this piece)
-- activatedButton has been cleared (touch ended)
-- a button tap has occurred (for detecting button presses in editable mode)
-- a button has been moved (activatedButton was dragged in editable mode)
-- nothing (this piece did not interact with the touch)
uiPieceHandler.evaluateTouchFor = function(name, touch)
if touch == nil then
touch = CurrentTouch
end
if uiPieceHandler.thisButtonIsActivated(name, touch) then
uiPieceHandler.makeActivatedButtonRespond(name, touch)
end
end
--thisButtonIsActivated: called to decide if this button should respond to this touch
--precondition: name and touch cannot be nil
--postconditions:
-- activatedButton has been set or unchanged (note that it is never cleared here)
-- boolean returned true if the given button is the activatedButton, false if not
uiPieceHandler.thisButtonIsActivated = function(name, touch)
--if there is already an activatedButton and this isn't it, return false
if activatedButton ~= nil and activatedButton ~= name then
return false
end
--if there is no activatedButton, see if this should become activatedButton
if activatedButton == nil then
--if touch state is BEGAN and touch is inside button, set it to activatedButton
if touch.state == BEGAN and uiPieceHandler.touchIsInside(name, touch) then
activatedButton = name
else
--otherwise return false
return false
end
end
--here only reached if this is activated button (or has become it), so return true
return true
end
--uiPieceHandler.touchIsInside: calculated using touch's distance from this piece
--preconditions: name and touch cannot be nil, and touched object is basically rectangular
uiPieceHandler.touchIsInside = function(name, touch)
local xDistance = math.abs(touch.x-uiPieceHandler.buttons[name].x)
local yDistance = math.abs(touch.y-uiPieceHandler.buttons[name].y)
insideX = xDistance < uiPieceHandler.buttons[name].width /2
insideY = yDistance < uiPieceHandler.buttons[name].height /2
if insideX and insideY then
return true
else
return false
end
end
--makeActivatedButtonRespond: decide how the given button should react to given touch
--precondition: button and touch cannot be nil, button must be activatedButton
uiPieceHandler.makeActivatedButtonRespond = function(name, touch)
--move button if it should be moved
if buttons_are_draggable then
uiPieceHandler.evaluateDrag(name, touch)
end
--if this is an end touch, do a button action, or save new position, or do nothing
if touch.state == ENDED then
if buttons_are_draggable then
if touch.tapCount == 1 then
uiPieceHandler.doAction(name)
else
report("saving buttons positions")
uiPieceHandler.savePositions()
end
elseif uiPieceHandler.touchIsInside(name, touch) then
uiPieceHandler.doAction(name)
report(name, uiPieceHandler.buttons[name].action)
end
activatedButton = nil
end
end
uiPieceHandler.evaluateDrag = function (name, touch)
if touch.state == MOVING then
uiPieceHandler.buttons[name].x = touch.x
uiPieceHandler.buttons[name].y = touch.y
end
end
uiPieceHandler.savePositions = function (name, position)
dataString = ""
for name, buttonValues in pairs(uiPieceHandler.buttons) do
dataString = dataString.."uiPieceHandler.buttons['"..name.."'] = \n"
dataString = dataString.." {x = "..buttonValues.x
dataString = dataString..", y = "..buttonValues.y..",\n"
dataString = dataString.." width="..buttonValues.width..", "
dataString = dataString.."height="..buttonValues.height..",\n"
dataString = dataString.." fontColor=color("..buttonValues.fontColor.r..","
dataString = dataString..buttonValues.fontColor.g..","
dataString = dataString..buttonValues.fontColor.b..","
dataString = dataString..buttonValues.fontColor.a.."),\n"
dataString = dataString.." action = uiPieceHandler.defaultButtonAction}\n\n"
end
saveProjectTab("uiPieceTables",dataString)
end
--# fullScreenBlur
function imageResizedToScreen(imageToResize)
local screenSizedImage = image(WIDTH,HEIGHT)
setContext(screenSizedImage)
sprite(imageToResize,WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
setContext()
return screenSizedImage
end
function blurClipFromImage(sourceImage,x,y,width,height)
local clippedRect = image(width,height)
local clippedRectPosition = vec2(x,y)
local clippedRectHalved = vec2(width/2,height/2)
setContext(clippedRect)
pushMatrix()
translate(-clippedRectPosition.x+clippedRectHalved.x,-clippedRectPosition.y+clippedRectHalved.y)
sprite(sourceImage,sourceImage.width/2,sourceImage.height/2)
popMatrix()
setContext()
return imageWithGaussian2PassBlur(clippedRect)
end
function imageWithGaussian2PassBlur(imageToBlur)
--aspect ratio for blurring with:
local largestSide = math.max(imageToBlur.width,imageToBlur.height)
local aspect = vec2(largestSide/imageToBlur.width, largestSide/imageToBlur.height) --should be inverse ratio?
--aspect = vec2(0.45,0.45) --********* clean up
--dimensions for fullSized and downsampled images
local downsampleAmount = 0.5 -- going down to 0.25 actually looks pretty good, but, weirdly, slower than 0.5
local fullDimensions = vec2(imageToBlur.width,imageToBlur.height)
local downsampleDimensions = vec2(imageToBlur.width*downsampleAmount,imageToBlur.height*downsampleAmount)
--images
local blurImages = {}
blurImages.fullSized = imageToBlur
blurImages.downsampled = image(downsampleDimensions.x,downsampleDimensions.y)
setContext(blurImages.downsampled)
sprite(imageToBlur,downsampleDimensions.x/2,downsampleDimensions.y/2,downsampleDimensions.x,downsampleDimensions.y)
setContext()
--meshes
local blurMeshes = {}
blurMeshes.horizontal = mesh()
blurMeshes.vertical = mesh()
--horizontal mesh settings
blurMeshes.horizontal.texture = blurImages.fullSized
blurMeshes.horizontal:addRect(downsampleDimensions.x/2,downsampleDimensions.y/2,
downsampleDimensions.x,downsampleDimensions.y) --fullSized image uses downsampled rect
blurMeshes.horizontal.shader = shader(gaussianShader.vert[1],gaussianShader.frag)
blurMeshes.horizontal.shader.am = aspect
--vertical mesh settings
blurMeshes.vertical.texture = blurImages.downsampled
blurMeshes.vertical:addRect(fullDimensions.x/2,fullDimensions.y/2,
fullDimensions.x,fullDimensions.y) --downsampled image uses fullSized rect
blurMeshes.vertical.shader = shader(gaussianShader.vert[2],gaussianShader.frag)
blurMeshes.vertical.shader.am = aspect
--draw the blurred horizontal mesh to the vertical mesh texture
setContext(blurMeshes.vertical.texture)
blurMeshes.horizontal:draw() --pass one
setContext()
--draw the double-blurred vertical mesh to a new image
local renderTarget = image(imageToBlur.width,imageToBlur.height)
setContext(renderTarget)
blurMeshes.vertical:draw() --pass two
setContext()
--send back the blurred image
return renderTarget
end
gaussianShader = {
vert = { -- horizontal pass vertex shader
[[
uniform mat4 modelViewProjection;
uniform vec2 am; // ammount of blur, inverse aspect ratio (so that oblong shapes still produce round blur)
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_Position = modelViewProjection * position;
vTexCoord = texCoord;
v_blurTexCoords[ 0] = vTexCoord + vec2(-0.028 * am.x, 0.0);
v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024 * am.x, 0.0);
v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020 * am.x, 0.0);
v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016 * am.x, 0.0);
v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012 * am.x, 0.0);
v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008 * am.x, 0.0);
v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004 * am.x, 0.0);
v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004 * am.x, 0.0);
v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008 * am.x, 0.0);
v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012 * am.x, 0.0);
v_blurTexCoords[10] = vTexCoord + vec2( 0.016 * am.x, 0.0);
v_blurTexCoords[11] = vTexCoord + vec2( 0.020 * am.x, 0.0);
v_blurTexCoords[12] = vTexCoord + vec2( 0.024 * am.x, 0.0);
v_blurTexCoords[13] = vTexCoord + vec2( 0.028 * am.x, 0.0);
}]],
-- vertical pass vertex shader
[[
uniform mat4 modelViewProjection;
uniform vec2 am; // ammount of blur
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_Position = modelViewProjection * position;
vTexCoord = texCoord;
v_blurTexCoords[ 0] = vTexCoord + vec2(0.0, -0.028 * am.y);
v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024 * am.y);
v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020 * am.y);
v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016 * am.y);
v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012 * am.y);
v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008 * am.y);
v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004 * am.y);
v_blurTexCoords[ 7] = vTexCoord + vec2(0.0, 0.004 * am.y);
v_blurTexCoords[ 8] = vTexCoord + vec2(0.0, 0.008 * am.y);
v_blurTexCoords[ 9] = vTexCoord + vec2(0.0, 0.012 * am.y);
v_blurTexCoords[10] = vTexCoord + vec2(0.0, 0.016 * am.y);
v_blurTexCoords[11] = vTexCoord + vec2(0.0, 0.020 * am.y);
v_blurTexCoords[12] = vTexCoord + vec2(0.0, 0.024 * am.y);
v_blurTexCoords[13] = vTexCoord + vec2(0.0, 0.028 * am.y);
}]]},
--fragment shader
frag = [[precision mediump float;
uniform lowp sampler2D texture;
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_FragColor = vec4(0.0);
gl_FragColor += texture2D(texture, v_blurTexCoords[ 0])*0.0044299121055113265;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 1])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 2])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 3])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 4])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 5])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])*0.147308056121;
gl_FragColor += texture2D(texture, vTexCoord )*0.159576912161;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])*0.147308056121;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 9])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[10])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[11])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[12])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[13])*0.0044299121055113265;
}]]
}
--# blur
--[[
Gaussian = {} --a component for nice effects like shadows and blur
--Gaussian blur
--adapted by Yojimbo2000 from http://xissburg.com/faster-gaussian-blur-in-glsl/
function smoothstep(t,a,b)
local a,b = a or 0,b or 1
local t = math.min(1,math.max(0,(t-a)/(b-a)))
return t * t * (3 - 2 * t)
end
function Gaussian.setImage(givenImage)
local p = givenImage
local hardCodedFalloff = 1
local ww,hh = p.width * hardCodedFalloff, p.height * hardCodedFalloff -- shadow image needs to be larger than the element casting the shadow, in order to capture the blurry shadow falloff
-- self.ww, self.hh = ww,hh
local d = math.max(ww, hh)
local blurRad = smoothstep(d, math.max(WIDTH, HEIGHT)*1.5, 60) * 1.5
local aspect = vec2(d/ww, d/hh) * blurRad --work out the inverse aspect ratio
-- print(p.title, "aspect", aspect)
local downSample = 0.25
local dimensions = vec2(ww, hh) * downSample --down sampled
local blurTex = {} --images
local blurMesh = {} --meshes
for i=1,2 do --2 passes, one for horizontal, one vertical
blurTex[i]=image(dimensions.x, dimensions.y)
local m = mesh()
m.texture=blurTex[i]
m:addRect(dimensions.x/2, dimensions.y/2,dimensions.x, dimensions.y)
m.shader=shader(Gaussian.shader.vert[i], Gaussian.shader.frag)
-- blurred[i].shader.am = falloff
m.shader.am = aspect
blurMesh[i] = m
end
local imgOut = image(dimensions.x, dimensions.y)
pushStyle()
pushMatrix()
setContext(blurTex[1])
scale(downSample)
--self:drawImage()
--from blur
--function Blur:drawImage()
pushMatrix()
--translate(-self.parent:left(), -self.parent:bottom())
translate(-200,-10)--???
--Soda.drawing(self.parent) --draw all elements to the blur image, with the parent set as the breakpoint (so that the parent window itself does not show up in the blurred image)
popMatrix()
--end
popMatrix()
popStyle()
setContext(blurTex[2])
blurMesh[1]:draw() --pass one
setContext(imgOut)
blurMesh[2]:draw() --pass two, to output
setContext()
return imgOut
end
function Gaussian:draw()
local p = self.parent
self.mesh:setRect(1, p.x + self.off, p.y - self.off, self.ww, self.hh)
self.mesh:draw()
end
---------------------------------------------------------------------------
Blur = class(Gaussian)
function Blur:init(t)
self.parent = t.parent
self.falloff = 1
self.off = 0
self:setMesh()
-- self.image = image(self.parent.w * 0.25, self.parent.h * 0.25)
-- self.draw = self.setMesh --
end
function Blur:draw() end
function Blur:setMesh()
-- self.draw = null
self.image = self:setImage()
self.parent.shapeArgs.tex = self.image
self.parent.shapeArgs.resetTex = self.image
end
function Blur:drawImage()
pushMatrix()
translate(-self.parent:left(), -self.parent:bottom())
Soda.drawing(self.parent) --draw all elements to the blur image, with the parent set as the breakpoint (so that the parent window itself does not show up in the blurred image)
popMatrix()
end
---------------------------------------------------------------------------
Shadow = class(Gaussian)
function Shadow:init(t)
self.parent = t.parent
self.falloff = 1.3
self.off = math.max(2, self.parent.w * 0.015, self.parent.h * 0.015)
-- print(self.parent.title, "offset", self.off)
self.mesh = mesh()
self.mesh:addRect(0,0,0,0)
self:setMesh()
end
function Shadow:setMesh()
self.mesh.texture = self:setImage()
-- self.mesh:setRect(1, self.parent.x + self.off,self.parent.y - self.off,self.ww, self.hh) --nb, rect is set in draw function, for animation purposes
end
function Shadow:drawImage()
pushStyle()
pushMatrix()
translate((self.ww-self.parent.w)*0.45, (self.hh-self.parent.h)*0.45)
self.parent:drawShape({Soda.style.shadow})
popMatrix()
popStyle()
end
Gaussian.shader = {
vert = { -- horizontal pass vertex shader
[[
uniform mat4 modelViewProjection;
uniform vec2 am; // ammount of blur, inverse aspect ratio (so that oblong shapes still produce round blur)
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_Position = modelViewProjection * position;
vTexCoord = texCoord;
v_blurTexCoords[ 0] = vTexCoord + vec2(-0.028 * am.x, 0.0);
v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024 * am.x, 0.0);
v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020 * am.x, 0.0);
v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016 * am.x, 0.0);
v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012 * am.x, 0.0);
v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008 * am.x, 0.0);
v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004 * am.x, 0.0);
v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004 * am.x, 0.0);
v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008 * am.x, 0.0);
v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012 * am.x, 0.0);
v_blurTexCoords[10] = vTexCoord + vec2( 0.016 * am.x, 0.0);
v_blurTexCoords[11] = vTexCoord + vec2( 0.020 * am.x, 0.0);
v_blurTexCoords[12] = vTexCoord + vec2( 0.024 * am.x, 0.0);
v_blurTexCoords[13] = vTexCoord + vec2( 0.028 * am.x, 0.0);
}]]
--[[
,
-- vertical pass vertex shader
[[
uniform mat4 modelViewProjection;
uniform vec2 am; // ammount of blur
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_Position = modelViewProjection * position;
vTexCoord = texCoord;
v_blurTexCoords[ 0] = vTexCoord + vec2(0.0, -0.028 * am.y);
v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024 * am.y);
v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020 * am.y);
v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016 * am.y);
v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012 * am.y);
v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008 * am.y);
v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004 * am.y);
v_blurTexCoords[ 7] = vTexCoord + vec2(0.0, 0.004 * am.y);
v_blurTexCoords[ 8] = vTexCoord + vec2(0.0, 0.008 * am.y);
v_blurTexCoords[ 9] = vTexCoord + vec2(0.0, 0.012 * am.y);
v_blurTexCoords[10] = vTexCoord + vec2(0.0, 0.016 * am.y);
v_blurTexCoords[11] = vTexCoord + vec2(0.0, 0.020 * am.y);
v_blurTexCoords[12] = vTexCoord + vec2(0.0, 0.024 * am.y);
v_blurTexCoords[13] = vTexCoord + vec2(0.0, 0.028 * am.y);
}]]
--[[
},
--fragment shader
frag = [[precision mediump float;
uniform lowp sampler2D texture;
varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_FragColor = vec4(0.0);
gl_FragColor += texture2D(texture, v_blurTexCoords[ 0])*0.0044299121055113265;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 1])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 2])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 3])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 4])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 5])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])*0.147308056121;
gl_FragColor += texture2D(texture, vTexCoord )*0.159576912161;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])*0.147308056121;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 9])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[10])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[11])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[12])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[13])*0.0044299121055113265;
}]]
--[[
}
]]
--# Utilities
-- setReporting &c handles control whether or not report(string) commands get printed
function setReporting(shouldReport)
if shouldReport then
reporting = true
else
reporting = false
end
end
function report(x)
if reporting == nil then
print("reporting not set")
elseif reporting == false then
return
else
print(x)
end
end
--lets just consolidate pushStyle() and popStyle() here
function styleSafe(functionToCall)
pushStyle()
functionToCall()
popStyle()
end
--need an easy boolean-to-string thingy
function boolToString(boolean)
if boolean == true then
return "TRUE"
elseif boolean == false then
return "FALSE"
else
return "NOT A BOOLEAN"
end
end
--tintScreen applies given color as semitransparent tint to a CargoBot full-screen sprite
function tintScreen(tintColor)
--enforce a minimum opacity of 191
if tintColor.a > 191 then
tintColor.a = 191
end
tint(tintColor)
sprite("Cargo Bot:Game Lower BG", WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
noTint()
end
--# uiPieceTables
uiPieceHandler.buttons['to editable mode'] =
{x = 811.5, y = 702.5,
width=300, height=55,
fontColor=color(109.0,223.0,112.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['sample game start screen'] =
{x = 811.5, y = 429.0,
width=400, height=300,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['you won sample game'] =
{x = 811.5, y = 429.0,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['welcome'] =
{x = 502.5, y = 384.5,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['lose sample game'] =
{x = 811.5, y = 157.0,
width=400, height=55,
fontColor=color(160.0,173.0,223.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['to fixed mode'] =
{x = 811.5, y = 702.5,
width=300, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['reset sample game'] =
{x = 811.5, y = 232.0,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['you lost sample game'] =
{x = 811.5, y = 429.0,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['win sample game'] =
{x = 811.5, y = 232.0,
width=400, height=55,
fontColor=color(160.0,173.0,223.0,255.0),
action = uiPieceHandler.defaultButtonAction}
--# defaults
--[[ paste imto uiPieceTables to reset buttons if you've messed with em
uiPieceHandler.buttons['to editable mode'] =
{x = 811.5, y = 702.5,
width=300, height=55,
fontColor=color(109.0,223.0,112.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['sample game start screen'] =
{x = 811.5, y = 429.0,
width=400, height=300,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['you won sample game'] =
{x = 811.5, y = 429.0,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['welcome'] =
{x = 502.5, y = 384.5,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['lose sample game'] =
{x = 811.5, y = 157.0,
width=400, height=55,
fontColor=color(160.0,173.0,223.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['to fixed mode'] =
{x = 811.5, y = 702.5,
width=300, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['reset sample game'] =
{x = 811.5, y = 232.0,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['you lost sample game'] =
{x = 811.5, y = 429.0,
width=160, height=55,
fontColor=color(253.0,253.0,253.0,255.0),
action = uiPieceHandler.defaultButtonAction}
uiPieceHandler.buttons['win sample game'] =
{x = 811.5, y = 232.0,
width=400, height=55,
fontColor=color(160.0,173.0,223.0,255.0),
action = uiPieceHandler.defaultButtonAction}
]]
--# Tests
function tests()
--testTouch and a function to reset it
local testTouch = {}
local resetTestTouch = function()
testTouch = {}
end
--when in playModeUI, and a touch on the editable mode button is of state BEGIN
showPlayModeUI()
local detectedTestTouch, correctButtonActivated = false, false
local editModeButtonName = "to editable mode"
local buttonTable = uiPieceHandler.buttons["to editable mode"]
local testTouch = {x=buttonTable.x, y=buttonTable.y}
testTouch.state = BEGAN
--then if it is passed to touchIsInside
detectedTestTouch = uiPieceHandler.touchIsInside(editModeButtonName, testTouch)
--result is a true value for detectedTestTouch
print("detectedTestTouch: "..boolToString(detectedTestTouch))
--and then if it is passed to evaluateTouchFor
uiPieceHandler.evaluateTouchFor(editModeButtonName, testTouch)
--result is the correct activatedButton
correctButtonActivated = activatedButton == editModeButtonName
print("correctButtonActivated: "..boolToString(correctButtonActivated))
--and when the touch moves over a different object
local playModeButtonName = "to fixed mode"
local playModeButton = uiPieceHandler.buttons[playModeButtonName]
testTouch.x, testTouch.y = playModeButton.x, playModeButton.y
testTouch.state = MOVING
--then if it is passed to evaluateTouchFor
uiPieceHandler.evaluateTouchFor(playModeButtonName, testTouch)
--result is the first object remaining the activatedButton
correctButtonActivated = activatedButton == editModeButtonName
print("touching second object preserves activatedButton: "..
boolToString(correctButtonActivated))
--and when the touch ends outside the button
local outsideOffsetX, outsideOffsetY = buttonTable.width, buttonTable.height
local startingX, startingY = buttonTable.x, buttonTable.y
testTouch.x = buttonTable.x+outsideOffsetX
testTouch.y = playModeButton.y+outsideOffsetY
testTouch.state = ENDED
--then if it is passed to evaluateTouchFor
uiPieceHandler.evaluateTouchFor(editModeButtonName, testTouch)
--result is that activatedButton is now nil
local activatedButtonIsNil = activatedButton == nil
print("touch ending outside button erases activatedButton: "..
boolToString(activatedButtonIsNil))
--and result is that the button has NOT moved
local buttonInRightPlace = buttonTable.x == startingX and buttonTable.y == startingY
print("button did not move when it shouldn't: "..boolToString(buttonInRightPlace))
--and result is button action NOT performed (action = setting editable mode)
local actionPerformed = currentUI == editModeUI
print("action not performed when it shouldn't: "..
boolToString(actionPerformed == false))
--when playModeUI & NOT buttons_are_draggable & touch starts on editable mode button
showPlayModeUI()
resetTestTouch()
buttons_are_draggable = false
testTouch.state = BEGAN
testTouch.x, testTouch.y = buttonTable.x, buttonTable.y
uiPieceHandler.evaluateTouchFor(editModeButtonName, testTouch)
--then if it also ends on that button
testTouch.state = ENDED
uiPieceHandler.evaluateTouchFor(editModeButtonName, testTouch)
--result is that activatedButton is now nil
activatedButtonIsNil = activatedButton == nil
print("touch ending inside button erases activatedButton: "..
boolToString(activatedButtonIsNil))
--and result is UI is in editable mode
actionPerformed = currentUI == editModeUI
print("action performed when it should in fixed mode: "..
boolToString(actionPerformed))
--and when buttons are draggable and a touch starts on the fixed mode button
resetTestTouch()
showEditModeUI()
testTouch = {x=buttonTable.x, y=buttonTable.y}
testTouch.state = BEGAN
uiPieceHandler.evaluateTouchFor(playModeButtonName, testTouch)
--then if the touch moves
testTouch.x = playModeButton.x-outsideOffsetX
testTouch.y = playModeButton.y-outsideOffsetY
testTouch.state = MOVING
uiPieceHandler.evaluateTouchFor(playModeButtonName, testTouch)
--result is that button has moved along with it
local rightX = playModeButton.x == testTouch.x
local rightY = playModeButton.y == testTouch.y
buttonInRightPlace = rightX and rightY
print("button moved when it should for MOVING state: "..
boolToString(buttonInRightPlace))
--then if the touch moves more and ends
startingX, startingY = playModeButton.x, playModeButton.y
testTouch.x, testTouch.y = testTouch.x+outsideOffsetX, testTouch.y+outsideOffsetY
testTouch.state = ENDED
uiPieceHandler.evaluateTouchFor(playModeButtonName, testTouch)
--result is that button has moved along with it
buttonInRightPlace = playModeButton.x == startingX and playModeButton.y == startingY
print("button didn't move for ENDED state: "..boolToString(buttonInRightPlace))
--at the end of tests, reset all the default button values
defineMainButtons()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment