Retro Gadgets

Welcome to the Retro Gadgets documentation!

Use the chapters on the left to navigate the contents of this document. If you have any requests or suggestions, talk to us on our Discord server!

If this is your first time here, check out the Getting Started page.

See also

Retro Gadgets uses Luau extensions over Lua 5.1 for the programming portion. For more information on all the features the language provides, check the following external links:

Getting Started

Main screen

This is the initial view once you start Retro Gadgets. The rest of this document will explain to you the interface and the basics of how gadgets work, but if you want to learn in-depth programming techniques we recommend you check the gadgets under the Tutorial button to see how they were built.

The Gadget Archive button will take you to the folder in your computer where Retro Gadgets files are stored. This will be important for importing assets, creating new cutting mats, among other things.

Your desk

Once you close the Multitool you will see an overhead view of your workbench and your tools. The main drawers and tools at your disposal are highlighted with blue handles.

Desk

  1. Boards drawer - Inside this drawer are the parts with which you can build the base for your gadgets, giving them a shape.
  2. Inputs, Outputs and Misc - Each of these drawers contain their corresponding modules, which add functionality to your gadgets.
  3. Airbrush - Once you have a shell for your gadget, you can use this to paint it in a selection of colors.
  4. Multitool handle - This will open and close the Multitool, where you can edit your gadget, its assets and debug your code.
  5. Soldering iron - Using this tool you can join board parts together to form a bigger gadget, once you've placed them next to each other.
  6. Tweezers - This tool allows you to move stickers after they're placed down on your gadget.
  7. Archive - This drawer holds all the gadgets you've built or printed from the workshop, once you put them away from your desk. It also contains some example gadgets included with the game that you can play with, take apart and learn from.
  8. Desk lamp - No workbench is complete without a lamp to help you see your gadgets! You can use the handle to reposition it, turn it on and off with the switch, and even control it with the desk global methods.

Some of these drawers or tools will become inaccessible while a gadget is currently turned on, this will be indicated with a lock sign or a lid over them.

Once you've familiarized yourself with your tools, check out the FAQ and the Cookbook examples to get started building gadgets!

Minitool

Minitool

The Minitool is a pager-like device that sits atop your Boards drawer. It can give you information about the things you click in the game, such as the name of modules you select, and you can make it display your own messages from your gadget with the Minitool methods

Cutting Mats

Cutting mat

You start the game with a standard blue cutting mat with the Retro Gadgets logo. If you wish to change your cutting mat, you can go into Gadget Archive on the main menu and look into the CuttingMats folder. Inside that folder there is a README file which gives you instructions on creating your own mats and a Samples folder with some different mats ready to be used or modified.

Once you have some custom mats to put into the game, put them inside the Active folder in the cutting mats folder, restart your game, and click on the tab at the bottom-right corner of your mat, highlighted above in orange, to cycle between all active mats. Note this tab is only present if you have no gadgets currently placed onto your mat.

Another thing to note is how Steam Cloud backs up the Retro (Gadget Archive) folder, such that if you're trying to remove a file you must do it while the game is running so that the removed file syncs with Steam when you quit the game. Otherwise, if you remove a file while the game is not running, it will be restored by Steam Cloud when you launch the game again.

Recording Videos

Video recorder

Above your Archive drawer, a small board lets you record your gadget creations into MP4 video files to share with others online. The large red button will start and stop a recording, while the smaller green button below it will open the recording settings, where you can also open the folder where your videos are stored after recording.

Multitool

The Multitool sits to the left of your workbench. It's used to edit your gadget and its assets, as well as serving as the game's main menu and interface.

Gadget view

Gadget view

Once you start editing a gadget, this screen will appear.

  1. This is the ID of the gadget in your computer. Clicking it will open the folder where it's stored so you can access its rgadget file.
  2. The edit button brings you to the gadget's assets. Duplicate (or Jailbreak for gadgets downloaded from the Workshop) will make a new copy of the gadget and Delete will delete it.
  3. This button allows you to change the gadget's permissions.
  4. These triangle-shaped buttons allow you to edit the gadget's name and description.
  5. This section allows you to publish your finished gadget to the Workshop. The tags let you categorize your gadget and the "Showcase" flag will put it in the showcase list.

Permissions

To use Wi-Fi and Camera functions on each gadget, you need to grant it permissions. Otherwise, trying to make a web request or display a picture will result in an access denied error. To enable permissions, you need to edit the gadget and click on the Permissions button in the Multitool shown above.

Asset list

Asset list

On this screen you can manage the assets contained in your gadget. The three buttons at the top, respectively:

  1. Allows you to create new SpriteSheets with the Sprite Editor
  2. Create new Code with the Code Editor
  3. And import an external asset from your computer.

To import assets into your gadget, they must be in the Import folder in your computer, which can be found by clicking the Gadget Archive button in the main menu.

For assets to be used within your gadget's code, you need to include a ROM module in your gadget.

Bottom screen

Multitool bottom screen

In the bottom screen of the Multitool, you can change different properties of each module you click on your gadget while the Multitool is open. At the top of this screen, you can also see the name each one of your gadget's modules has, in order to use them in your code.

The properties labeled as Software can either be changed here, for your gadget's initial state at power on, or be changed with code your gadget runs. Properties labeled Hardware can only be changed here. One such property is Symbol, which changes what is printed physically on a button.

Airbrush

Airbrush

The Airbrush tool allows you to paint gadget shells and modules. Once you click on it in the bottom right corner of your desk, the camera pans to present you with all the options such as colors and different nozzles. Like the other tools and drawers, the Airbrush is only available while your gadget is powered off, this is noted by the toolbox being closed with a lid while it's on.

Using the Airbrush

different sizes, shapes and lines with the airbrush

When the Airbrush is active, it will follow your cursor with a red marker indicating where your are pointing. This marker will change in shape and size depending on the nozzle, size and mode you are currently using.

To change the size of your brush, scroll up and down with your mouse wheel. The size will be indicated both by the red marker and by a number on the handle of the Airbrush. To change the nozzle shape, simply click on them in the Airbrush's tool box to pick them up. Pressing X will toggle between locking to horizontal and vertical line modes, before returning to regular freehand mode. You can hold down the left shift key to zoom into your gadget for precision. You can also press Ctrl-Z to undo each paint stroke.

To select a color for your Airbrush, simply click on an ink bottle on the right. You can click with the right mouse button to select a secondary color. This color will be used if you paint with the right button instead of the left button.

You can also use stickers as masks for painting over. By placing a stencil sticker on your gadget, painting over it, then removing the sticker with the tweezers, you'll note the shell won't be painted where the sticker was sitting.

Certain modules can be painted with the Airbrush by clicking directly on them, and some modules also have a secondary color that can be set. For example, painting the Keypad with the left click will paint the bezel around the buttons, while painting it with the right click will paint the buttons themselves.

Once you are done using the Airbrush, simply click on its empty spot in the tool box to put it away.

Sprite Editor

Sprite Editor

The Sprite Editor is where you can create and modify SpriteSheets for your gadgets.

Each of its tools will have more information and settings in the bottom screen of the Multitool as you click them.

Brush The brush allows you to draw pixels and lines.

Fill bucket The fill bucket allows you to flood-fill or replace entire swaths of colors in your sprite or image.

Shapes The shapes tool allows you to draw circles and squares.

Select The selection tool allows you to select part of your images and move, flip or copy them.

Eyedropper The eyedropper allows you to select colors from the image.

Settings In the settings you can change colors and opacities for the UI elements of the Sprite Editor, such as grids and background.

Printer The printer lets you print out stickers for your gadget's shell.

Font The font tool allows you to create custom fonts to use when displaying text on video screens.

Position This will display your cursor's current position in the image. If the mouse icon is enabled, the position will be in pixels relative to the whole SpriteSheet. If the grid below it is enabled, the position will show you the X and Y values of each whole sprite in your sheet.

Sprite size These W and H values will set the size of every sprite in your sheet, that is, by how many pixels to slice your entire sprite sheet into individual sprites. Width and height can be different values, but values that aren't powers of 2 will disable the SNAP setting which lets your zoomed view snap to each sprite in the editor.

Palettes

Palettes

The palette will contain all the colors used in your sprite sheet. You can select colors to use with the editor's tools with the left mouse button for primary color and with the right mouse button for secondary or background color. The last entry in the palette, shown in the bottom right, is always transparent.

The down arrow in the top left allows you to load built-in palettes or palettes from other sprite sheets in your gadget's assets. The wrench next to it allows you to edit each color in your palette. Since palettes use indexed color, changing colors used in your sheet will change them across the whole image as well.

Fonts

Creating custom fonts

You can make a custom font by slicing your sprite sheet such that each sprite is one glyph (or character) of your desired font. Remember you can change the size of your sheet's sprites with the W and H values as explained above.

Once you have your sprites for every character you want to represent, use the "Font" tool. In the textbox that appears at the bottom, type the characters in the order they appear in your sheet, so that the game knows which sprite is what letter. Once all that is done, you can use this SpriteSheet in place of the default StandardFont when using DrawText.

Stickers

Printing stickers

Stickers can be placed on your gadget's shell as decoration, or to be used as masks for painting with the airbrush spray. To print a sticker from your SpriteSheet, first select the area you want to print out at the bottom. You can use the Invert Alpha button to turn the image into a negative mask instead, and once you're ready you can press Print to turn the image into a sticker.

You can then drag the sticker onto your gadget to place it down, and from there you must use the tweezers on your desk to move or remove it from your gadget. To get rid of a sticker, simply drag it back into the printer output. If you don't immediately drag a new sticker out of the printer, you can continue printing more images onto the same sticker sheet.

Code Editor

Code Editor

The Code Editor is where you can write the Lua code your gadget's CPU will run. If you're a beginner at programming and want to see some code examples, you can look in the Tutorial button on the main menu for a variety of built-in gadgets you can inspect and learn from.

At the bottom of the editor, there is a status bar that will tell the runtime state of your code while your gadget is on. It can show the performance of your gadget in "tps" (Ticks Per Second), or if your gadget has encountered an error. Errors are displayed in red and can be hovered over to show more information about what happened.

At the bottom-right there are 4 buttons, respectively:

  • Debugging will show line numbers so you can click them to set breakpoints in your code to inspect it more closely. Your code will pause at the breakpoint until told to continue, and clicking the current running line will show you a stacktrace.
  • Word wrap will wrap around lines that are too long horizontally to be displayed on the screen. When disabled, a horizontal scroll bar will be shown instead.
  • Minus button will decrease the resolution of the code editor, meaning letters are bigger and less code will fit on screen at once.
  • Plus button will increase the resolution of the code editor, meaning letters are smaller and more code will fit on screen at once.

Depending on your monitor's resolution, the minus and plus buttons may not have any effect.

To learn more about how to program in Retro Gadgets, see the Glossary and Code Structure pages.

Frequently Asked Questions

Here are some of the more frequently asked questions we've received, linking to different parts of this document where you can get more information. Be sure to also check out the cookbook examples if you haven't, as those can teach you some of the basic concepts of Retro Gadgets. If you still have any questions, feel free to ask in our Discord server!

How do I get started building a gadget?

We show how to build a gadget from scratch that takes button inputs and outputs sound in our Sound Board cookbook example. It also serves as teaching you the very basics to follow later cookbook examples.

Additionally, you can look at the built-in gadgets under the Tutorial button in the main menu to see how they were made.

The camera or the wi-fi modules don't work!

Check that your gadget permissions are set correctly. Additionally, there is currently no way to switch between camera sources in computers with multiple sources, so the game might not be detecting your webcam correctly.

I'm trying to use a function, but it seems to ask for the wrong number of arguments!

Check that you are using : (colon) for object methods, and . (period) for properties. Check out the glossary for more information on methods and properties.

How do I load assets to use in my code? GetAudioSample and GetSpriteSheet don't work!

Between the Next Fest demo and the Early Access release, some of the syntax was changed with new features and some supplemental material might not reflect that. To use assets currently, you must drag a ROM chip onto your gadget from the Misc drawer, and replace calls such as:

local sprites:SpriteSheet = GetSpriteSheet("filename.png")

with:

local sprites:SpriteSheet = gdt.ROM.User.SpriteSheets["filename.png"]

For built-in assets such as "StandardFont", replace User with System. For audio assets, replace SpriteSheets with AudioSamples. See the ROM page for more information.

How can I protect my gadget's assets or code from copying when I publish it into the Workshop?

You can include a SecurityChip in your gadget to lock its assets. Check out the module page for more information.

How do I tell what size a screen is?

A Screen module will have Width and Height properties that you can read to get the size of each individual screen. If you connect multiple screens to the same VideoChip, the drawing buffer grows in order to accommodate all the connected screens, so you can use the Width and Height properties of the VideoChip instead to get the full resolution of your combined screen. If you want to know what size each screen in the drawer is, that information is also in the Screen module's page in this document.

How do I make a code delay, or how do I make my code wait some time?

Since Retro Gadgets is aimed at being programmed like any other basic computer would need to be, you need to code everything from scratch. The CPU gives you a couple of time properties that allow you to do this.

First, understand your code runs in ticks. That means for each tick, your entire update() function will have run through in its entirety. Retro Gadgets aims to run at 60 ticks per second, that is your update() will run 60 times every second. This is fine for logic and for parsing inputs.

Second, we need a way to determine how much time has passed. We can do this by reading gdt.CPU0.Time to get the total time in seconds since the gadget has turned on, or gdt.CPU0.DeltaTime to get the time in seconds that passed since the last tick.

Say we want something to happen 2 seconds after a trigger, like a button. You could write it like so:

-- we start with an unset timer as nil, meaning null or unset
local timeToTrigger = nil
function update()
    if gdt.LedButton0.ButtonDown then
        -- we set the timer for 2 seconds from the current time
        timeToTrigger = gdt.CPU0.Time + 2
    end

    -- this if first checks if the timer is set, then if the time is right
    if timeToTrigger and gdt.CPU.Time >= timeToTrigger then
        timeToTrigger = nil
        -- this will execute once when the time is right
        -- and will not execute again since we unset the timer
        -- you can put your code to be triggered here
    end
end

There are other ways to keep track of time than individual timers however. If you want something to happen every half second, for example:

-- let's call our repeating timer "frames", meaning every half second we
-- advance one frame
local frameDuration = 0.5
-- this will keep track of DeltaTime
local deltaCounter = 0

function frame()
    -- this will get called every 0.5 seconds
end

function update()
    -- increase the counter by the CPU's DeltaTime
    deltaCounter += gdt.CPU0.DeltaTime

    -- we run a while loop for however many times the delta counter is greater
    -- than the duration of one frame, such that if for some reason the counter
    -- has gone past over one frame duration, we run our frame loop however many
    -- times necessary to keep the logic tied to the timer.
    while (deltaCounter >= frameDuration) do
        deltaCounter -= frameDuration
        frame()
    end
end

This last method is useful for repeating timers such as one to run a drum machine or a game's animation logic cycle.

How do I check collision between two sprites?

Like the delay question above, there is no built-in simple way to do this, and you have to make your own from scratch. The simplest type of collision checking you can write is called AABB collision, which checks if 2 rectangles are overlapping.

AABB collision between two boxes

To tell if the two boxes are overlapping, you need to know the position of each box (X and Y), and their sizes (Width and Height). However you store these values depends on your own code. Then, it's a matter of comparing those values like such:

function is_colliding(x1, y1, w1, h1, x2, y2, w2, h2)
    return x1 < x2 + w2 and x1 + w1 > x2 and
        y1 < y2 + h2 and y1 + h1 > y2
end

...where x1 and y1 are the top-left coordinates of the first box, w1 and h1 are the width and height of the first box, x2 and y2 are the top-left coordinates of the second box, and w2 and h2 are the width and height of the second box. The function will return true if there is a collision between those coordinates, or false otherwise.

Controls

Gadget Building

Grab a componentLeft Mouse Button
Rotate grabbed componentRight Mouse Button while grabbing
ZoomLeft Shift

Airbrush

Pick main colorLeft Mouse Button on a color jar
Pick secondary colorRight Mouse Button on a color jar
Spray with main colorLeft Mouse Button
Spray with secondary colorRight Mouse Button
Cycle drawing modes (free, horizontal, vertical)X
Alter the nozzle sizeMouse Wheel

Code Editor

Debug: next stepF1
Debug: continue runningF2

Specials

Enter Desktop widget modeF11
Exit Desktop widget modeShut down gadget

APIs

Glossary

Here is a basic understanding of some of the terms used in RetroGadgets for handling modules and other functions. For a more in-depth overview, check out the official Lua document on Object-Oriented programming.

Objects

An object is an instantiation of a class. Each module in your gadget is an instance of their type of module, therefore they're objects. For example, LcdDisplay is a type of module for text LCD displays, but to refer to the first LCD display in your gadget specifically you use gdt.Lcd0. The name of each individual object on your gadget can be checked using the Multitool. All of your gadget's modules can be found inside gdt as a table.

Note that modules which can only appear once in your gadget, such as ROM and PowerButton won't have a number at the end of their names in the gdt table, for example gdt.ROM and gdt.PowerButton.

Properties

A property is a value that can be read or set in an object. For example, it can be used to check the current condition of a module, or modify it.

It should be called with object.property.

Examples:

gdt.Led0.State = gdt.LedButton0.ButtonState

This sets the state of a LED Led0 to be the same as the incoming state of the button LedButton0. What will happen is that the LED will turn on as long as the button is pressed down.

gdt.Lcd0.Text = "Hello world!"

This will set the text property of an LCD screen Lcd0 to the string "Hello world!", displaying it.

In this document:

property type read only

description of the property

The name of the property is highlighted in bold. The type of that property is marked in red and links to an overview of the type. If the property can only be read, and not set, it will be marked as such to indicate it. If a type is surrounded by one of more {curly brackets}, that means that it's a table.

Methods

A method is a function which receives additional arguments to carry out a task.

Unless a method is determined to be a global method, it needs to be called in relation to a specific object. In order to do so, you must call it with object:method(argument). This way, with :, you are telling the function that what it expects to be the self argument is object.

Example:

gdt.AudioChip0:Play(sample, 1)

This will tell the AudioChip AudioChip0 to play a previously loaded sample sample on channel 1.

If a method is global, it can be called by itself, or if is static and part of a generic class, with a . like so: class.method(argument).

Example:

desk.SetLampState(true)

This will turn on the desk lamp.

In this document:

method( argument1 type, argument2 type ) type

description of the method

The name of the method is highlighted in bold. The arguments are shown between parentheses and each of them has a respective type they expect. If the method returns a value, the type it returns is marked at the end. If a type is surrounded by one of more {curly brackets}, that means that it's a table.

Events

An event is triggered by a module external to the CPU. For example the Wifi module triggers an event when a web request returns a response. To handle events, you must select the module that raises the event on your CPU's EventChannels and a specially named function eventChannelN, where N must be replaced by the number of your channel, will run when the event is triggered. The event channel function receives the module that triggered the event, and a table containing values pertaining to that event.

Example:

function eventChannel1(sender, event)
	log("got event " .. tostring(event) .. " from module " .. tostring(sender))
end

This function will handle incoming events on channel 1, once the module connected to that channel sends the CPU an event, it will print out to the Multitool debug console the string representation of the event table event and the name of the module sender that sent it.

In this document:

eventType : { value1 type, value2 type }

description of the event

The type associated with the event's table is highlighted in bold. The contents of the table are shown between curly brackets, each one with its associated type.

Code structure

Setting up your gadget

The CPU will run your entire Lua code from top to bottom, once. Statements will run in the order you write them in, and you can only reference what has already been declared. For example, if you have a method that uses a value stored in the variable player_x, the following example code in isolation will not work:

drawMyPlayer(player_x, player_y)

local player_x = 10
local player_y = 10

Before you can use those variables, their declaration needs to come first:

local player_x = 10
local player_y = 10

drawMyPlayer(player_x, player_y)

Note also that this is only a simplified code example and the method drawMyPlayer is illustrative and not a global that already exists in Retro Gadgets.

Since everything on the root of the Lua script is run through once, you can place all of your gadget's setup statements there. They will run on the first tick of the gadget only.

Update function

To make your code repeat for every tick that your gadget is powered on, Retro Gadgets expects a specially named update function like so:

local player_x = 10
local player_y = 10

function update()
	drawMyPlayer(player_x, player_y)
end

In this case, player_x and player_y are set up only at the power up of the gadget and then are not set again. Meanwhile at every single tick of the gadget, the method drawMyPlayer is called to run.

You can declare your update function anywhere in your script, as long as you follow the rule that anything it uses must be declared before it as explained above.

Other special functions

Just like how Retro Gadgets expects a function specifically named update to run it at every tick, it also expects specifically named functions to work with events. Your CPU will have a number of event channels it supports, and for each one of them you can define a function that will be run when each of those channels gets triggered. The function must be named eventChannelN where N is replaced by the number of the channel you're expecting events on.

For example, if you have a KeyboardChip connected to your CPU's event channel 2:

function eventChannel2(sender, event)
	if (event.ButtonDown) then
		log("user pressed " .. event.InputName)
	end
end

This will run, and print on the Multitool debug screen, only each time the user presses down a key (which is when the event is triggered).

Using require()

Retro Gadgets supports the require function built into Lua to make it easier to modularize your source code into separate files. To use the require function, the file you're trying to import must also be present in your gadget's assets, and it must return a table with values you want to use in your main file. For example:

utils.lua:

utils = {}

function utils.min(a, b)
	if (a < b) then return a end
	return b
end

function utils.max(a, b)
	if (a > b) then return a end
	return b
end

return utils

This file, named utils.lua returns a table which has been populated with two functions, minand max which return the smallest and the largest of two numbers respectively.

CPU0.lua:

local utils = require("utils.lua")

log(tostring(utils.min(2,4)))

The CPU0.lua file, which is what will run in our CPU, first imports the table returned by the file utils.lua into its own local variable named utils, then uses the min function from there to print out the smallest number between 2 and 4.

Multitool debug methods

Multitool debug screen

The Multitool debug screen, at the bottom of your Multitool, works like a terminal window. These global methods allow you to display information on it, providing an easy output to make sure your code is working right.

log( message string )

This will print a message on the screen using the current settings. The method print(message) is an alias to this method.
Keep in mind that to log numerical values, they must first be converted into string with tostring(value).

logWarning( message string )

This will print a message on the screen, automatically colored yellow.

logError( message string )

This will print a message on the screen, automatically colored red.

write( text string )

This will print a message on the screen without automatically adding a line break. Calling this method repeatedly will always print on the same line.

writeln( text string )

This will print a message on the screen adding a line break at the end. Functionally the same as log(message).

setFgColor( colorId number )

Sets the foreground color of the text to be printed. Uses the numbers on the ANSI colors table for Foreground.

setBgColor( colorId number )

Sets the background color of the text to be printed. Uses the numbers on the ANSI colors table for Background.

resetFgColor( )

Resets the text foreground color to the default bright white.

resetBgColor( )

Resets the text background color to the default black.

resetColors( )

Resets both background and foreground colors for the text to their defaults.

setCursorPos( column number, line number )

Sets the absolute position of the cursor, that is, where text is going to be printed. Takes both column and line at once.

setCursorX( column number )

Sets only the absolute horizontal position of the cursor to column.

setCursorY( line number )

Sets only the absolute vertical position of the cursor to line.

moveCursorX( deltaColumn number )

Moves the cursor relative to its current position, horizontally by an amount of deltaColumn characters.

moveCursorY( deltaLine number )

Moves the cursor relative to its current position, vertically by an amount of deltaLine characters.

saveCursorPos( )

Saves the current position of the cursor to memory.

restoreCursorPos( )

Moves the cursor to the position previously saved with saveCursorPos().

clear( )

Clears the entire screen.

clearToEndLine( )

Clears only the line where the cursor currently is.

ANSI colors

Foreground Background Name Color
30 40 Black 0, 0, 0
31 41 Red 255, 0, 0
32 42 Green 0, 255, 0
33 43 Yellow 255, 255, 0
34 44 Blue 0, 0, 255
35 45 Magenta 255, 0, 255
36 46 Cyan 0, 255, 255
37 47 White 176, 174, 165
90 100 Bright Black (Gray) 24, 22, 13
91 101 Bright Red 255, 158, 144
92 102 Bright Green 193, 255, 177
93 103 Bright Yellow 255, 255, 177
94 104 Bright Blue 0, 40, 255
95 105 Bright Magenta 255, 159, 255
96 106 Bright Cyan 194, 255, 255
97 107 Bright White 255, 255, 255

Desk methods

Lamp

These methods control the desk lamp on your workbench.

desk.GetLampState( ) boolean

Returns true if the lamp is on, false otherwise.

desk.SetLampState( state boolean )

Passing true to state will set the lamp on, and false will turn it off.

desk.SetLampColor( color color )

Sets the lamp's color to color.

Minitool

These methods control the Minitool status screen on your desk.

In all methods containing the argument persistent, this argument tells whether to keep the message forever or if it should disappear after a few seconds.

desk.ShowMessage( message string, persistent boolean )

Displays a message message with a blue background.

desk.ShowWarning( message string, persistent boolean )

Displays a message message with a flashing yellow background.

desk.ShowError( message string, persistent boolean )

Displays a message message with a flashing red background.

desk.HideMessage( )

If there is currently a persistent message being displayed, this will clear the Minitool.

Types

Structures

Built-in Lua types

For more information on how these types are handled, please check the official Lua 5.1 Reference Manual.

In the API reference, types surrounded by {curly brackets} means a table of said type.

boolean

A boolean is a logical value that can be either true or false. This can be used to evaluate logic, for example using comparators between two values returns a boolean. In module states, true means "on", "active" or "pressed", while false means the opposite.

local playerCanMove = false
-- sets a boolean called "playerCanMove" to false
gdt.Led0.State = true
-- sets the "State" of a LED to true, turning it on

number

A number refers to any sort of numerical value, integer or floating point.

local pi = 3.14159
-- sets a number called "pi" to 3.14159
local knobValue = gdt.Knob0.Value
-- sets a number called "knobValue" to the "Value" property of a knob

string

A string is a type that holds a text value (a string of characters). See the Lua documentation for various built-in functions to manipulate strings.

local message = "Hello world!"
-- sets a string called "message"
log(message)
-- displays the string "message"

table

In Lua, a table is a group of values of a certain type. Tables are described in typing with curly brackets such as {number} for a one-dimensional array, or {{number}} for a two-dimensional matrix.

Tables in Lua are 1-indexed meaning that the first element is numbered 1 and so forth. An example of a two-dimensional table can be manipulated like such:

variable[column][row]

where variable is your table, column and row are the indices you want to manipulate.

local primes = {1, 3, 5, 7, 9}
-- sets an array called "primes"
log(tostring(primes[2]))
-- displays the second entry of "primes"

You can get the size of a table by using # before the name of the table:

local mytable = {"a", "b", "c", "d"}
log(tostring(#mytable)) -- returns 4

If a table is two-dimensional, you need to use one of the inner tables to measure both dimensions:

local mytable = {
	{"a", "b"},
	{"c", "d"},
	{"e", "f"}
}

log(tostring(#mytable)) -- returns 3, number of rows
log(tostring(#mytable[1])) -- returns 2, length of row 1, which tells us the columns

Color

Color is a class that describes the values used to make up colors for screens and lights.

Global Methods

Color( r number, g number, b number ) color

Compose and returns a RGB Color object. Values for the 3 channels are always expressed in the range 0-255.

ColorRGBA( r number, g number, b number, a number ) color

Compose and returns a RGB Color object, with Alpha. Values for the 4 channels are always expressed in the range 0-255. Alpha 0 is transparent

ColorHSV( h number, s number, v number ) color

Compose and returns a RGB Color Object, expressing it in HSV values.
Hue [0-360]
Saturation [0-100]
Value [0-100]

Built-in colors

  • color.black
  • color.blue
  • color.clear
  • color.cyan
  • color.gray
  • color.green
  • color.magenta
  • color.red
  • color.white
  • color.yellow

Vectors

Retro Gadgets has vec2 and vec3 vector object types which are used for coordinate systems such as drawing images in a VideoChip.

The individual components of each of these types cannot be modified (e.g. setting X or Y individually), they can only be created or read. It can be more useful to store your values separately and only turn them into vector types when necessary.

However, it's useful to note you can apply certain matrix operations to these types like so:

vec2(1,2) * 2 -- returns vec2(2,4)
vec2(1,2) * vec2(2,1) -- returns vec2(2,2)

vec2(1,2) - vec2(1,0) -- returns vec2(0,2)
vec2(1,2) - 1 -- causes RuntimeException

vec2

Global methods

vec2( x number, y number ) vec2

Creates and returns a vec2 vector.

Properties

X number read only

The X component of this vec2 object.

Y number read only

The Y component of this vec2 object.

vec3

Global methods

vec3( x number, y number, z number ) vec3

Creates and returns a vec3 vector.

Properties

X number read only

The X component of this vec3 object.

Y number read only

The Y component of this vec3 object.

Z number read only

The Z component of this vec3 object.

InputSource

InputSource is a type in Retro Gadgets which describes a hardware real-world input that can be mapped onto a virtual input on your gadget.

For example, you can use the Keyboard chip to map a specific key to press a Button, or use the Gamepad chip to map an axis of a physical controller's analog stick to one of the axes of an Analog Stick.

Examples

The cookbook examples Sound Board and Collect the Dot show how to set up different types of input sources for single buttons and axes, respectively.

PixelData

Data structure containing video data.

This structure is used to provide lua with an optimized way to do GetPixel and SetPixel, the buffer is kept on the lua side minimizing read and write overhead.

Static Methods

PixelData.new( width number, height number, color color ) PixelData

Constructor used to create a PixelData.
width and height represent its size while color defines the color with which it is initialized.

Properties

Width number read only

Width in pixels of the buffer.

Height number read only

Height in pixels of the buffer.

Methods

GetPixel( x number, y number ) color

Returns the color of the pixel at the coordinate specified by x and y.

SetPixel( x number, y number, color color )

Sets the color of the pixel at the coordinate specified by x and y.

Cookbook

Retro Gadgets comes with a few example gadgets that you can take apart and learn how they work, just look in the Tutorial section of the main menu. However, if you want some ideas on where to begin making your own gadgets from scratch, we've prepared a few guides to follow along:

  1. Sound Board - Learn how to build a gadget from scratch, using button inputs to trigger sound samples
  2. Collect The Dot - Use a LED matrix to move around a dot to collect other dots
  3. Moving Mike - Move a sprite around a screen with the D-Pad

Useful snippets

Here are some functions that can help with frequent use cases in your gadgets:

Mapping knob and slider values

-- a function that maps "value"
-- from a scale of "src_from" to "src_to"
-- into a scale of "tgt_from" to "tgt_to"
function map(value:number, src_from:number, src_to:number, tgt_from:number, tgt_to:number)
    return ((value - src_from) / (src_to - src_from) * (tgt_to - tgt_from)) + tgt_from
end

For example:

log(tostring(map(50, 0, 100, 0, 360))) -- displays "180"

Turning modules into lists

function getCompList(prefix:string, start:number, stop:number)
	list = {}
	for i = 1, stop - start + 1 do
		list[i] = gdt[prefix .. tostring(i + start - 1)]
	end
	return list
end

For example, if you have Led3, Led4 and Led5:

local myLeds = getCompList("Led", 3, 5)

myLeds[1].State = true -- turns on Led3
myLeds[2].State = true -- turns on Led4
myLeds[3].State = true -- turns on Led5

Turning notes into pitch values

This will turn notes in semitones relative to 1 into pitch values for AudioChip's SetChannelPitch:

function noteToPitch(note:number)
	return 1.05946 ^ (note-1)
end

Wrapping a number back to 0 after it reaches a certain value

You can use the modulo function (%, AKA division remainder) to do this:

-- define variable outside update to retain value between ticks
local value = 0

function update()
	-- increase value by 1
	value += 1
	-- set value for the remainder by division of 100
	-- if value is 0-99 this will keep the value the same
	-- if value is 100 it will now be 0
	value = value % 100
end

In this case, every tick value will increase by one, and then be truncated by a modulo 100. If the value is 100, it will return to 0.

Generating random numbers

This is done with the standard Lua math library:

-- no arguments: floating point number between 0 and 1
log(tostring(math.random()))
-- one argument: integer number between 1 and argument, both inclusive
log(tostring(math.random(6)))
-- two arguments: integer number between lower and upper numbers, both inclusive
log(tostring(math.random(5,10)))

Delays and timers

Check the FAQ answer about delays.

Collision between sprites

Check the FAQ answer about collisions.

Sound board

In this example, we're going to show you how to make a gadget that can play sounds when you press buttons, or your computer's keys.

Building the gadget

First, drag boards onto the cutting mat to form the shape of your gadget. Note that when boards are next to each other they have a slight snapping action that places them exactly in the right place to be soldered together.

boards next to each other

You don't have to follow this exact shape, as long as you have enough space in your gadget for all the components. Once they're all snapped together, you can use the soldering iron to join them.

Now, click the little indentation in your gadget's front to open it. From the module drawers, place a Keypad and a Speaker on the front of your board.

keypad and speaker in gadget

Once you've done that, click on the arrow where the indentation was on your board to flip it. On the back of the board, place a ROM, a CPU, a KeyboardChip and an AudioChip. Make sure the AudioChip is at least the medium size.

chips in the back of the board

Setting up the modules

Now that you have all the modules in your gadget, click the shell of your gadget at the top of your screen to close it again. Open the Multitool and click the Keypad on your gadget. At the bottom of the Multitool you should see the module's properties.

In those properties, you can set the ButtonsInputSource of your Keypad, meaning we can give an external input to each one of its buttons. For this example, we are going to set each entry for the following KeyboardChip inputs by clicking on the bottom screen:

[1][1]Alpha1
[2][1]Alpha2
[3][1]Alpha3
[4][1]Alpha4
[1][2]Q
[2][2]W
[3][2]E
[4][2]R
[1][3]A
[2][3]S
[3][3]D
[4][3]F
[1][4]Z
[2][4]X
[3][4]C
[4][4]V

keypad properties in multitool

Now that those are set, you can try them out by clicking the PowerButton to power on your gadget and pressing the corresponding keys on your computer's keyboard. You should notice that each one of the keys on the left hand side of your keyboard correspond to the same positions on the Keypad.

Turn off your gadget, open it back up and flip the board over. With the Multitool still open, click the CPU to set its EventChannels. You want the first EventChannel to point to your Keypad module. Since the Keypad has its inputs also tied to KeyboardChip keys, this event can be triggered both with your keyboard and by clicking the buttons of your gadget's Keypad.

CPU properties in multitool

Writing our code

You're now ready to add some code to your gadget! Open the Code Editor by double-clicking the CPU0.lua file in your Asset List. The code is the part of the puzzle that will take inputs from the Keypad buttons, load corresponding samples, then play them through the speaker.

Since we'll be starting from scratch, you can erase the initial boilerplate provided to us to start with an empty code file.

First, we need a table to tell which sample we want to play at each button of the Keypad:

local sampleNames = {
	{"","","",""},
	{"","","",""},
	{"","","",""},
	{"","","",""}
}

This will make a two-dimensional table with the same size as the Keypad, 4 columns and 4 rows. Right now all our sample names are empty, but we'll change that soon. Then, we'll make a handler for EventChannel number 1, which will handle events incoming from the Keypad.

function eventChannel1(sender, event)

	if event.ButtonDown then -- was the event caused by a button being pressed?

		local sampleName = sampleNames[event.Y][event.X] -- get the name of the sample from our table
		local sample = gdt.ROM.User.AudioSamples[sampleName] -- load that sample from our assets

		if sample ~= nil then -- is the sample we tried to load anything but nil?
			gdt.AudioChip0:Play(sample, (event.X + event.Y * 4)) -- play the sample we loaded
		end

	end

end

In this handler, we are first checking if the event was caused by a button being pressed. The code inside that condition will only run if it's true.

Next, we look into the table we prepared earlier to find which sample name we're supposed to play for this button. We use the X and Y values of the event for that, since they correspond to the column and row of the button that was pressed.

Once we have the name of the sample, we can use the ROM module to load the actual sample.

Next, another condition checks if the sample we tried to load actually exists. If it doesn't, the ROM will return nil, so our condition asks for a value different from that with the ~= logic operator.

After all that, we can ask the AudioChip to play the sample we loaded. Notice the bit of math in the place of a channel: (event.X + event.Y * 4). This will calculate a channel number depending on the column and row of the button pressed, making sure that no two buttons try to use the same audio channel. For that to work, we need to use an AudioChip that supports at least 16 channels.

code in the code editor

Adding our assets

The last step we need to do is import some AudioSamples into our gadget so that our sound board has some sounds to play. You can choose any name for your samples when importing them into your gadget, just make sure to note the exact names you give them, including case sensitivity.

assets list

Once you've added those assets, go back to your gadget's CPU code to map them to the Keypad buttons using our table. Note how the table is structured in a way that the position of the sample names in it matches the position of the Keypad buttons. For this example, we used three samples like so:

local sampleNames = {
	{"kick","hat","",""},
	{"snare","","",""},
	{"","","",""},
	{"","","",""}
}

code with samples in place

With everything in place, you should be able to turn on your gadget and press those keys to make some noise. Huzzah!

Finishing touches

Once your gadget is complete, you can decorate it however you want, paint it with the Airbrush however you like, and even add stickers to it.

decorated gadget

Try adding some LEDs to make your gadget really shine. If you make something really cool, we'd love to see it in our Discord server!

Collect The Dot

This guide will show you how to move two single dots around a LED matrix, with some simple logic to increase a counter whenever the "player" dot reaches a "target" dot.

Building the gadget

Since we already covered in greater detail how to build a gadget in the previous example, we'll go over this part more succinctly. You'll need a LED matrix, a 2-digit segment display for our counter, and a D-Pad in the front of your gadget. For functionality, you'll need a KeyboardChip and a CPU somewhere in your gadget.

the bare gadget with matrix, dpad and segment display

The segment display will be easier to read if you paint it a darker color with the Airbrush.

Setting up the modules

Like we've done with the Sound Board before, we're going to set some InputSources for our D-Pad control in the Multitool. Since the D-Pad expects axes, not individual buttons, we need to tell it which keys of the KeyboardChip will control each axis.

Once you click to set InputSourceX and pick from KeyboardChip0, the input will look for an axis. Since the KeyboardChip does not have an axis (an analog input like a game pad has), your only option will be a Buttons axis, that is, a virtual axis that will be controlled by 2 keys instead of an actual analog input.

  • For InputSourceX, the horizontal axis, set Negative to LeftArrow and Positive to RightArrow
  • For InputSourceY, the vertical axis, set Negative to DownArrow and Positive to UpArrow

dpad inputsources

At this point you can turn on the gadget to see that your keyboard's arrow keys will move the D-Pad. After you've done that, set the CPU's EventChannel 1 to DPad0, this will make it so any movement on the D-Pad will trigger an event.

cpu eventchannels

Writing our code

Since we'll be starting from scratch, you can erase the initial boilerplate provided to us to start with an empty code file.

First, let's make some shorthands to make our life easier when referencing each one of our gadget's modules:

local pad:DPad = gdt.DPad0
local matrix:LedMatrix = gdt.LedMatrix0
local counter:SegmentDisplay = gdt.SegmentDisplay0

This means we can refer to our D-Pad by just pad, the LED matrix by just matrix and our segment display by just counter in the rest of our code. We are also setting each respective type after each variable's name with a :, so the code editor will know how to give us auto-complete suggestions.

Now, let's create some variables to store the information we need for our purposes:

local playerX = 1
local playerY = 1
local playerColor = color.yellow
local goalX = 0
local goalY = 0
local goalColor = color.red
local countValue = 0

playerX and playerY will store the player's starting position. You can change these, keeping in mind that the LED matrix has 8 columns and 8 rows, starting from 1. playerColor is the color the player's dot will light up in the matrix, which you can also change.

goalX and goalY will store the goal's position, and goalColor its color. We don't need to set this position right now since we'll make a function to do that for us.

countValue will be the number of times the player has reached the goal, to be displayed in the segment display.

With those values in mind, let's write a couple functions that will use them. The first is a function to randomize the goal's position:

function randomizeGoal()
	-- this code will repeat until we quit
	while true do
		-- place the goal in a random spot
		goalX = math.random(1, 8)
		goalY = math.random(1, 8)
		-- only quit if the goal is not where the player is
		if goalX ~= playerX or goalY ~= playerY then
			return
		end
	end
end

Every time this function is called, it will change goalX and goalY to a different position. We start with a forever while loop, so that the code only exits when it has reached a specific condition we want.

In that loop, we are using math.random to generate an integer number between 1 and 8, to fit inside the LED matrix. We generate one such random number for the horizontal and the vertical positions of the goal respectively.

Next, we check if the goal's position is different (~=) from the player's position. Since we don't want the goal to be generated where the player is standing, we only quit this function (with return) if that is the case. Otherwise, the function will not quit, and the loop will start over again, generating new numbers until the condition is met.

Now, we'll write a function to use the values we have to update our matrix and segment display:

function updateDisplays()
	-- clear all the matrix
	for x = 1, 8 do
		for y = 1,8 do
			matrix.States[x][y] = false
		end
	end
	
	-- set player dot
	matrix.States[playerX][playerY] = true
	matrix.Colors[playerX][playerY] = playerColor
	
	-- set goal dot
	matrix.States[goalX][goalY] = true
	matrix.Colors[goalX][goalY] = goalColor
	
	-- set counter
	counter:ShowDigit(2, countValue % 10)
	counter:ShowDigit(1, (countValue/10) % 10)
end

First, we are using a different type of loop, the for loop to run through the entire matrix and clear it. The two nested for functions will run each time with the values x and y iterating from 1 to 8. The nesting means that the inner loop will run 8 times for each of run the outer loop. Since both loops run 8 times, this means that the inner code will run 64 times, each single time with all permutations of x and y increasing from 1 to 8. The code inside the loop is using those values to set all of the LEDs in the matrix off, therefore we are clearing the entire matrix regardless of its current state.

Next, now that the matrix is cleared, we use playerX and playerY to set that particular LED in the matrix on, and then for it to be the player's color. Notice how in a LED matrix, we need to tell what column and row we want to set a State and a Color of.

The next part does the same thing, but for the goal dot.

Lastly, we make the counter segment display show our countValue using the ShowDigit method. We need to set each digit of the segment display individually, so we do that with some math. The second digit of the display shows the lower digit of our number, using modulo (%) to get the value's remainder from a division by 10. The first digit of the display shows the next higher digit, so we need to divide by 10 first, then take the same remainder from that number.

Once you have those two functions, call them at the end of your code so that they run once when your gadget is powered on:

randomizeGoal()
updateDisplays()

At this point you should be able to turn your gadget on and watch the red goal dot appear in a different position each time you power on your gadget. The segment display should read "00".

gadget showing lit up matrix and numbers

Now we'll write a handler for the D-Pad's events to move our player.

function eventChannel1(sender, event)
	-- i only want to run if the dpad changed to pressed, not released
	if event.X ~= 0 or event.Y ~= 0 then
		-- move horizontally
		if event.X < 0 and playerX > 1 then
			playerX -= 1
		elseif event.X > 0 and playerX < 8 then
			playerX += 1
		end
		
		-- move vertically
		if event.Y > 0 and playerY > 1 then
			playerY -= 1
		elseif event.Y < 0 and playerY < 8 then
			playerY += 1
		end
		
		-- if player overlaps goal, count up
		if playerX == goalX and playerY == goalY then
			countValue += 1
			randomizeGoal()
		end
		
		updateDisplays()
	end
end

We start by checking if the D-Pad was pressed, not released. Since when the D-Pad is released, both X and Y axes will read 0, we are checking if either one of them are different from 0.

To move the player horizontally, we check if the X axis is negative, and if the player is to the right of the leftmost edge (1). If both conditions pass, we decrease the player's X value to move it left. We also check if the X axis is positive and if the player is to the left of the rightmost edge (8). If those conditions pass instead, we move the player to the right.

The next part does the same for the vertical axis, however you must note that the D-Pad's Y axis is positive when pressed up and negative when pressed down, so those conditions are "reversed" when compared to the horizontal movement.

One last condition checks if the players X and Y positions match the goal's X and Y positions. This means that the player is overlapping the goal, and therefore has reached it. If that is true, then we increase the countValue and tell our program to randomize the goal again.

Finally, we call for the displays to be updated again. This means that each time we press a direction in the D-Pad we'll be able to see the LED matrix change.

You should be able to turn on your gadget, move your yellow player dot around and collect red dots to increase the number in the counter display.

Conclusion

Now that you know how to take D-Pad inputs and set LEDs in a matrix, you should have enough knowledge to see how the Mighty Cobra game works! If you can't find this game in your Archive drawer, check the Tutorial button in the main menu for the built-in gadgets to print a copy.

mighty cobra

Moving Mike

This example will show you how to make a D-Pad control the position of a sprite being drawn on a Screen with a VideoChip.

Building the gadget

We are going to place screens and a D-Pad on the front of the gadget. For this example, we're using one large 64x64 screen to the left, joined with two 32x32 screens to make it wider.

Note that with precise positioning you can slightly overlap the edges of several screens together to form a bigger screen. If done correctly, you will see one big screen without the edges in between. The example below shows the smaller screen at the top right correctly positioned, and the bottom right one needing adjustment to the left to join properly. You can hold the left shift key to zoom in on your desk for higher precision.

joining screens together in the gadget

Once you have those modules ready, go ahead and add a CPU, a KeyboardChip, a VideoChip and a ROM to your gadget as well.

Setting up the modules

Despite being visually joined together, each screen still needs to be connected to a VideoChip. Since we want all of our screens to be part of the same display, we need to connect each one of them to the same VideoChip with the Multitool.

connecting screens to video chip

Once you have those done, you should set up the D-Pad to use KeyboardChip InputSources and the CPU to take events from the D-Pad in channel one, exactly the same way we've done previously in the Collect the Dot example.

Making our assets

At the Asset List, we can click the first button to create a new SpriteSheet with the built-in Sprite Editor. For this example we'll be naming the asset mike exactly.

By default the editor will be set up to draw 16x16 sprites, we can use that as it is. Draw 4 sprites for our main character Mike, facing left, right, up and down, respectively. Remember you can use the selection tool to copy and flip sprites to make your job easier.

drawing sprites for mike

Writing our code

The code part of this gadget will be very similar to the one in the previous example, Collect the Dot. First, our module shorthands:

local pad:DPad = gdt.DPad0
local video:VideoChip = gdt.VideoChip0
local mike:SpriteSheet = gdt.ROM.User.SpriteSheets["mike"]

Here we are loading the mike SpriteSheet into a variable called mike so we can easily use it later.

Then, we'll store some values for the player:

local playerX = 0
local playerY = 0
local direction = 0
local stepSize = 5

The playerX and playerY variables work the same as before, but since the VideoChip buffer starts at 0, we can use those values instead of 1.

The direction value will remember which way we want Mike to face, that is, which sprite in his SpriteSheet we need to display.

The stepSize value will be used as how many pixels we want our sprite to move each time we press a direction.

The next part of the code will update our display:

function updateDisplays()
	-- clear the screen with white
	video:Clear(color.white)
	
	-- display mike's sprite
	video:DrawSprite(vec2(playerX,playerY), mike, direction, 0, color.white, color.clear)
end

First thing when calling the display update, we are clearing the video display so we don't keep whatever was on it previously. We are clearing with color.white which means the entire screen will be filled with that color.

The DrawSprite method might look daunting, but it can be easily broken down into each of its arguments:

  • First, we are telling it we want the sprite to be put into the vec2 coordinate of playerX and playerY
  • Then, we are telling that the SpriteSheet we want to use is mike
  • The next two ones, direction and 0 tell which sprite within the SpriteSheet we want to display. These two values are the horizontal and vertical position in the grid we can see while in the Sprite Editor. In the starting values, this means to pick the very first sprite, which is Mike facing left.
  • The next value is which color to tint the sprite with. We don't want to affect its color, so we use color.white.
  • Last, this value is which color to replace the sprite's transparency with. We want it to be transparent, so we use color.clear.

Once you have that function in, add a call at the end of your script so that it runs once when your gadget is turned on:

updateDisplays()

At this point, you can turn on your gadget and see the display shows Mike on the top left corner, facing left.

Now, we'll be controlling the movement of the sprite with a D-Pad event handler very similar to the one used in Collect the Dot:

function eventChannel1(sender, event)
	-- i only want to run if the dpad changed to pressed, not released
	if event.X ~= 0 or event.Y ~= 0 then
		if event.X < 0 and playerX > 0 then
			-- moving left
			playerX -= stepSize
			direction = 0
		elseif event.X > 0 and playerX < video.Width - 16 then
			-- moving right
			playerX += stepSize
			direction = 1
		end
		
		-- move vertically
		if event.Y > 0 and playerY > 0 then
			-- moving up
			playerY -= stepSize
			direction = 2
		elseif event.Y < 0 and playerY < video.Height - 16 then
			-- moving down
			playerY += stepSize
			direction = 3
		end
		
		updateDisplays()
	end
end

The main differences in this code is that now for up and left movement we are checking if the player is above coordinate 0 in either way instead of 1, and for the other edges moving right and down we are checking against the Width and Height properties of the VideoChip. Those properties will return the size of the screen taking into account all of the screens connected to the same VideoChip, so we don't need to keep track of how big our screen is by hand. Also, we are subtracting 16 from those values since that's the width and the height of our sprite, otherwise the sprite would be allowed to travel beyond the bottom right edge of the screen.

On each movement, we are increasing or decreasing the player's coordinates by a value of stepSize. This way if we decide to change by how much the sprite should move at each press of the button, we can change a single variable instead of having to change it each time it appears in our code.

And lastly, when moving our character, we are changing the direction value to correspond to each different sprite in our SpriteSheet, so that when the screen gets updated Mike will be facing the direction we are moving him in.

gadget on with mike on display

Now you know the basics of how to move a sprite on the screen!

Conclusion

A very similar built-in gadget you can look at is Screen Coordinates, which you can find in the Tutorial section in the main menu of the game. The method used to move the sprite is different, reading the D-Pad inside the update() function instead of events, so you might want to read the code and see how it works!

screen coordinates gadget

Modules

Input

Output

Misc

AnalogStick

AnalogStick

A little joystick-like input device for your gadget, which has smooth movement in all directions.

Similar to the D-Pad in functionality.

Properties

X number read only

The position of the stick along the horizontal axis, ranging from -100 to 100.

Y number read only

The position of the stick along the vertical axis, ranging from -100 to 100.

InputSourceX InputSource

The module associated with the stick's horizontal axis. See InputSource for more info.

InputSourceY InputSource

The module associated with the stick's vertical axis. See InputSource for more info.

Events

StickValueChangeEvent : { X number, Y number, Type string }

Triggered when the stick is moved.
* X and Y are the respective values of the stick
* Type is "StickValueChangeEvent"

In the Multitool

You can set the InputSource properties from the Multitool while editing your gadget to set these values without using code.

DPad

DPad

A digital directional input device for your gadget, which returns what directions are being pressed.

Similar to the Analog Stick in functionality.

Properties

X number read only

The position of the pad along the horizontal axis, only returns -100 or 100 when pressed, 0 when not.

Y number read only

The position of the pad along the vertical axis, only returns -100 or 100 when pressed, 0 when not.

InputSourceX InputSource

The module associated with the stick's horizontal axis. See InputSource for more info.

InputSourceY InputSource

The module associated with the stick's vertical axis. See InputSource for more info.

Events

DPadValueChangeEvent : { X number, Y number, Type string }

Triggered when the D-Pad is pressed.
* X and Y are the respective values of the pad
* Type is "DPadValueChangeEvent"

In the Multitool

You can set the InputSource properties from the Multitool while editing your gadget to set these values without using code.

Examples

See the Collect the Dot cookbook example for an example on how to address individual LEDs in the matrix with D-Pad input.

Keypad

Keypad

A grid of buttons that can be used in a group.

Properties

A Keypad works similarly to a grid of Buttons, its properties are all multi-dimensional tables that correspond to each button in the Keypad. The tables should be addressed with [column][row].

ButtonsState {{boolean}} read only

A multi-dimensional table mapping the state of each button to a boolean value.
Returns true if the corresponding button is currently pressed, false otherwise.

ButtonsDown {{boolean}} read only

A multi-dimensional table mapping boolean flags which will be true only in the time tick the corresponding button changes its state to pressed.

ButtonsUp {{boolean}} read only

A multi-dimensional table mapping boolean flags which will be true only in the time tick the corresponding button changes its state to released.

ButtonsInputSource {{InputSource}}

A multi-dimensional table mapping InputSources to each of the buttons in the Keypad.

Events

KeypadButtonEvent : { X number, Y number, ButtonDown boolean, ButtonUp boolean, Type string }

Triggered when a button is pressed or released.
* X is the column of the button that triggered the event.
* Y is the row of the button that triggered the event.
* ButtonDown is true if the button was just pressed.
* ButtonUp is true is the button was just released.
* Type is "KeypadButtonEvent".

In the Multitool

You can select different symbols to be printed on each one of the buttons of the keypad in the bottom screen of your Multitool.

Examples

See the Sound Board cookbook example for an example on how to play samples with keypad input.

Knob

Knob

A circular component that rotates to set numerical values. In Default mode the value goes from -100 on the left to 100 on the right. When set to Infinite mode the rotation is unclamped and the value is the absolute angle in degrees.

Similar to the Slider in functionality.

Properties

Mode KnobMode

The mode of the Knob:
In Default mode the value goes from -100 on the left to 100 on the right.
When set to Infinite mode the rotation is unclamped and the value is the absolute angle in degrees.

Value number

The value returned by the position of the knob.
In Default mode the value goes from -100 on the left to 100 on the right.
When set to Infinite mode the rotation is unclamped and the value is the absolute angle in degrees.

DeltaValue number

The rotation delta from the previous update.

IsMoving boolean read only

Returns true if the user is moving the knob.

Events

KnobValueChangeEvent : { Value number, Type string }

Triggered when the knob is moved.
* Value is the respective value given by the knob's position
* DeltaValue is the the rotation delta from the previous update
* Type is "KnobValueChangeEvent"

Examples

A simple code using some math to map knob's value into the hue of a LED's color:

-- a function that maps "value"
-- from a scale of "src_from" to "src_to"
-- into a scale of "tgt_from" to "tgt_to"
function map(value, src_from, src_to, tgt_from, tgt_to)
	return ((value - src_from) / (src_to - src_from) * (tgt_to - tgt_from)) + tgt_from
end

-- turn LED on at startup
gdt.Led0.State = true

function update()
	-- map slider -100,100 to hue 0,360
	local hue = map(gdt.Knob0.Value, -100, 100, 0, 360)
	
	-- set LED color to hue
	gdt.Led0.Color = ColorHSV(hue, 100, 100)
end

LedButton

LedButton

A button that can tell when it's pressed or released, and can glow with a LED embedded in it.

Properties

ButtonState boolean read only

Returns true if the button is currently pressed, false otherwise.

ButtonDown boolean read only

A boolean flag which will be true only in the time tick the corresponding button changes its state to pressed.

ButtonUp boolean read only

A boolean flag which will be true only in the time tick the corresponding button changes its state to released.

InputSource InputSource

An InputSource to be used to trigger the button state.

LedState boolean

The lit/unlit state of the Led of this button.

LedColor color

The color of the Led of this button.

Events

LedButtonEvent : { ButtonDown boolean, ButtonUp boolean, Type string }

Triggered when the LedButton is pressed or released.
* ButtonDown is true if the button was just pressed.
* ButtonUp is true is the button was just released.
* Type is "LedButtonEvent".

In the Multitool

You can select different symbols to be printed on some sizes of buttons in the bottom screen of your Multitool.

Examples

You can tie the state of the button and the state of the LED together to make a button light up while it's pressed:

function update()
	-- LED state always being set to Button state every tick
	gdt.LedButton0.LedState = gdt.LedButton0.ButtonState
end

See also the Intro Tutorial video in the official Retro Gadgets YouTube channel for an example to control LED strips with Buttons.

ScreenButton

ScreenButton

A button that can display images on itself, meaning that it works like a Screen and a Button at the same time.

The resolution of the screen inside the button is 16x16.

Properties

ButtonState boolean read only

Returns true if the button is currently pressed, false otherwise.

ButtonDown boolean read only

A boolean flag which will be true only in the time tick the corresponding button changes its state to pressed.

ButtonUp boolean read only

A boolean flag which will be true only in the time tick the corresponding button changes its state to released.

InputSource InputSource

An InputSource to be used to trigger the button state.

VideoChip VideoChip

The VideoChip the screen part of this button is bound to.

Offset vec2 read only

The offset of the screen's top-left position in the corresponding VideoChip's overall rendering buffer.

Width number read only

The width of the screen in pixels.

Height number read only

The height of the screen in pixels.

Events

ScreenButtonEvent : { ButtonDown boolean, ButtonUp boolean, Type string }

Triggered when the ScreenButton is pressed or released.
* ButtonDown is true if the button was just pressed.
* ButtonUp is true is the button was just released.
* Type is "ScreenButtonEvent".

Slider

Slider

A linear component that slides from one end to the other to set numerical values.

Similar to the Knob in functionality.

Properties

Value number

The value returned by the position of the slider, ranging from 0 to 100.

IsMoving boolean read only

Returns true if the user is moving the slider.

Events

SliderValueChangeEvent : { Value number, Type string }

Triggered when the slider is moved.
* Value is the respective value given by the slider's position
* Type is "SliderValueChangeEvent"

Examples

A simple code using some math to map the slider's value into the hue of a LED's color:

-- a function that maps "value"
-- from a scale of "src_from" to "src_to"
-- into a scale of "tgt_from" to "tgt_to"
function map(value, src_from, src_to, tgt_from, tgt_to)
	return ((value - src_from) / (src_to - src_from) * (tgt_to - tgt_from)) + tgt_from
end

-- turn LED on at startup
gdt.Led0.State = true

function update()
	-- map slider 0-100 to hue 0-360
	local hue = map(gdt.Slider0.Value, 0, 100, 0, 360)
	
	-- set LED color to hue
	gdt.Led0.Color = ColorHSV(hue, 100, 100)
end

Switch

Switch

Works similar to a Button, but holds its state as flipped or not flipped instead of being a momentary push button.

Properties

State boolean

The state of this switch.

InputSource InputSource

An InputSource to be used to trigger the switch's state.

Events

SwitchStateChangeEvent : { State boolean, Type string }

Triggered when the Switch is flipped.
* State is the new state of this switch.
* Type is "SwitchStateChangeEvent".

Webcam

Webcam

Properties

RenderTarget VideoChip

The VideoChip this camera is streaming contents to.

AccessDenied boolean read only

Will return true if Camera access is denied. See Permissions for more information.

IsActive boolean read only

Returns true if the Webcam is currently being used.

IsAvailable boolean read only

Returns true if the Webcam is available for being used.

Methods

GetRenderBuffer( ) RenderBuffer

Gets the camera RenderBuffer. The render buffer obtained can then be fed to the DrawRenderBuffer method of the VideoChip module.

Events

WebcamIsActiveEvent : { IsActive boolean, IsAvailable boolean, AccessDenied boolean, Type string }

Triggered when the Webcam's active state changes.
* IsActive is true if the Webcam is being used.
* IsAvailable is true if the Webcam is available for use.
* AccessDenied is true if permissions are disabled for Webcams.
* Type is "WebcamIsActiveEvent".

Examples

See the Camera Component Tutorial video in the official Retro Gadgets YouTube channel.

AnalogGauge

AnalogGauge

A physical device for visualizing a numerical value with a moving needle.

Properties

Value number

Setting this to a number between 0 and 100 will move the needle from left to right relative to that number.

Examples

Here's some code that sets an AnalogGauge to 100 if a LedButton is pressed down:

function update()
	-- if the button is pressed down
	if gdt.LedButton0.ButtonState then
		-- then the gauge gets set to 100
		gdt.Gauge0.Value = 100
	-- otherwise
	else
		-- the gauge will be set to 0
		gdt.Gauge0.Value = 0
	end
end

Note that even though the setting of the value is instantaneous, since the physical needle of the gauge can't move instantly, you can see it bounce between 0 and 100 in this example.

LcdDisplay

LcdDisplay

The LcdDisplay is an alphanumerical display consisting of 2 lines, 16 characters per line. It cannot display graphics and doesn't need a VideoChip to work.

Note: Unlike the other modules, the LcdDisplay does not follow its class' name for its objects, using Lcd instead, like so:

gdt.Lcd0

Properties

Text string

The text to be visualized on the LCD. To display text on the second line, your string must be formatted starting from the 17th character and beyond.

BgColor color

Background color for the LCD.

TextColor color

Foreground color for the LCD's text.

Examples

A small piece of code to change the LCD Display background color, displaying the hue value in text:

-- store a value for hue
local hue = 0

function update()
	-- put the hue value converted to text on display
	gdt.Lcd0.Text = "Hue: " .. tostring(hue)
	
	-- change display background color with hue
	gdt.Lcd0.BgColor = ColorHSV(hue, 100, 75)
	
	-- increase hue and wrap back to 0 once it reaches 360
	hue = (hue + 1) % 360
end

Led

Led

A small light that can change colors.

Properties

State boolean

LED on/off state.

Color color

LED color.

Examples

A small piece of code for a color-changing LED:

-- turn the LED on once at startup
gdt.Led0.State = true

-- store a value for hue
local hue = 0

function update()
	-- set LED color with hue
	gdt.Led0.Color = ColorHSV(hue, 100, 100)
	
	-- increase hue and wrap back to 0 once it reaches 360
	hue = (hue + 1) % 360
end

LedMatrix

LedMatrix

A two-dimensional cluster of LEDs that can be addressed individually.

Properties

A LedMatrix works similarly to a grid of LEDs, its properties are all multi-dimensional tables that correspond to each LED in the LedMatrix. The tables should be addressed with [column][row].

States {{boolean}}

A multi-dimensional table that maps each of the LED's lit/unlit status.

Colors {{color}}

A multi-dimensional table that maps each of the LED's colors.

Examples

See the Collect the Dot cookbook example for an example on how to address individual LEDs in the matrix with D-Pad input.

LedStrip

LedStrip

LedStrips are several LEDs joined together into one group.

Properties

A LedStrip works similarly to an array of LEDs, its properties are tables that correspond to each LED in the LedStrip. The tables should be addressed with [index].

States {boolean}

A table that maps each of the LED's lit/unlit status.

Colors {color}

A table that maps each of the LED's colors.

Examples

This example uses the CPU's Time to select a LED to turn on, such that a light moves on the strip each second. Since the lights will stay on until we tell them not to, the code also gets the previous light to turn off, otherwise all lights on the strip would come on and stay on forever.

function update()
	-- round down cpu time and then truncate to the number of leds in our strip
	local ledToTurnOff = math.floor(gdt.CPU0.Time) % #gdt.LedStrip0.States
	-- we do the same with the next number over to get the adjacent light
	local ledToTurnOn = math.floor(gdt.CPU0.Time+1) % #gdt.LedStrip0.States

	-- since the modulo truncation starts at 0 and the states table starts at 1, we need to add 1 to both numbers
	gdt.LedStrip0.States[ledToTurnOff+1] = false
	gdt.LedStrip0.States[ledToTurnOn+1] = true	
end

See also the Intro Tutorial video in the official Retro Gadgets YouTube channel for an example to control LED strips with Buttons.

Screen

Screen

A screen is what displays the images you generate with a VideoChip.

Sizes

Screens come in five different sizes with the following resolutions each:

16x16 screen 16x16

32x32 screen 32x32

40x40 screen 40x40

64x36 screen 64x36

64x64 screen 64x64

By carefully positioning two or more screens next to each other, you're able to join them together to make screens of different sizes and shapes. Remember you still need to individually connect each separate screen to your VideoChip in order to display images on them.

Properties

VideoChip VideoChip

The VideoChip this screen is bound to.

Offset vec2 read only

The offset of the screen's top-left position in the corresponding VideoChip's overall rendering buffer.

Width number read only

The width of the screen in pixels.

Height number read only

The height of the screen in pixels.

SegmentDisplay

SegmentDisplay

A collection of different seven-segment displays for showing numbers.

Segments

Each digit of a SegmentDisplay has its segments enumerated as such:

segment 1 1

segment 2 2

segment 3 3

segment 4 4

segment 5 5

segment 6 6

segment 7 7

segment 8 8

Each digit of a multi-digit display has all these segments, except for the display with two dots in the middle. In the case of that display, the two dots are instead enumerated as their own "digit" in which each segment is each dot, making the total of "digits" for it 5. Additionally, the 8th segment (the dot) is absent.

4 digits 4 digits

5 digits 5 "digits"

Properties

The displays are enumerated in tables of tables. The outer table is each display digit, the inner table is each segment in that digit. Therefore the tables must be accessed by [digit][segment].

States {{boolean}}

A table that maps the lit/unlit state of each of the segments in the display.

Colors {{color}}

A table that maps the color of each of the segments in the display.

Methods

ShowDigit( groupIndex number, digit number )

A helper function to show a numerical digit in a whole display. groupIndex is the digit that you want the number to show on, digit is the number you want to show.

SetDigitColor( groupIndex number, color color )

A helper function to set the color of a whole numerical digit at once. groupIndex is the digit that you want to change the color of, color is the color you want to set it to.

Examples

Here's how to display the CPU time (measured in seconds) on a 4-digit SegmentDisplay:

-- shorthand display module to "disp"
local disp = gdt.SegmentDisplay0

function update()
	-- store the CPU time rounded down
	local time = math.floor(gdt.CPU0.Time)
	
	-- each digit gets remainder of division by 10
	-- each next digit gets divided by 10 to get next decimal place
	disp:ShowDigit(4, time % 10)
	disp:ShowDigit(3, (time/10) % 10)
	disp:ShowDigit(2, (time/100) % 10)
	disp:ShowDigit(1, (time/1000) % 10)
end

The Collect the Dot cookbook example also makes use of a SegmentDisplay to show an increasing counter.

Speaker

Speaker

The Speaker outputs audio played with an AudioChip.

Sizes

Currently the different speaker sizes are purely cosmetic for your gadget's look, they all function the same.

Properties

State boolean

Sets whether the speaker is enabled.

AudioChip

AudioChip

The AudioChip can be used together with a Speaker to make your gadgets produce sounds through the usage of AudioSamples.

Sizes

The different sizes of AudioChips determine how many concurrent channels each of them supports:

small AudioChip The small AudioChip supports 4 channels.

medium AudioChip The medium AudioChip supports 16 channels.

large AudioChip The large AudioChip supports 32 channels.

Properties

ChannelsCount number read only

Number of available channels for this AudioChip. Each channel can independently play an audio sample.

Volume number

The global AudioChip volume.

Methods

GetSpectrumData( channel number, samplesCount number ) {number}

Returns the audio spectrum values of a channel as a table of number values, each one expressing the value of a different frequency.
samplesCount must be a power of 2 (ie 128/256/512 etc) Min = 64 Max = 8192.

GetDspTime( ) number

Returns the current internal AudioChip's DSP time. It is meant to be used in combination with the PlayScheduled method. The returned number is expressed in seconds.

Play( audioSample AudioSample, channel number )

Immediately plays an AudioSample on a specific channel.

PlayScheduled( audioSample AudioSample, channel number, dspTime number )

Schedule the play of and AudioSample at a specific DSP time, expressed in seconds, on the specific channel.

PlayLoop( audioSample AudioSample, channel number )

Immediately plays an AudioSample on a specific channel, looping it.

PlayLoopScheduled( audioSample AudioSample, channel number, dspTime number )

Schedule the play of and AudioSample at a specific DSP time, expressed in seconds, on the specific channel, looping it.

Stop( channel number )

Stops any audio playing on a specific channel

Pause( channel number )

Pause the audio on a specific channel

UnPause( channel number )

Resumes the audio on a specific channel

IsPlaying( channel number ) boolean

Returns true if a channel is currently playing audio

IsPaused( channel number ) boolean

Returns true if a channel is currently paused.

GetPlayTime( channel number ) number

Returns the current play time of a channel, expressed in seconds.

SeekPlayTime( time number, channel number )

Sets the current position of the play head, for the specific channel, expressed in seconds.

SetChannelVolume( volume number, channel number )

Sets the current volume for a channel, 0-100 range.

GetChannelVolume( channel number ) number

Gets the current volume for a channel, 0-100 range.

SetChannelPitch( pitch number, channel number )

Sets the pitch for a channel. Acts as a multiplier. A value of 1 means the default pitch for a sample, a value of 2 plays the sample one octave higher.

GetChannelPitch( channel number ) number

Gets the current pitch of a channel.

Events

AudioChipChannelEvent : { Channel number, Type string }

Event sent when a channel has finished playing.
* Channel is the index of the audio channel.
* Type is "AudioChipChannelEvent".

Examples

See the Sound Board cookbook example for an example on how to play samples with keypad input.

See also the Audio Tutorial video in the official Retro Gadgets YouTube channel.

CPU

CPU

The CPU is what runs the Code assets in your gadget to control all your gadget's modules.

Sizes

The different CPU sizes support different amounts of EventChannels at once.

small CPU The small CPU can have up to 4 EventChannels.

medium CPU The medium CPU can have up to 16 EventChannels.

large CPU The large CPU can have up to 64 EventChannels.

Aside from that, the different CPU sizes do not operate differently.

Properties

Source Code read only

The code asset uploaded to the CPU.

Time number read only

The time since the gadget is turned on, expressed in seconds.

DeltaTime number read only

The time elapsed since the last tick, expressed in seconds.

EventChannels {Module}

A table which is used to connect each Event Channel with a module in your gadget that can trigger that channel with its events.

In the Multitool

Multitool

You can set the Source and EventChannels properties from the Multitool while editing your gadget to set these values without using code.

Decoration

Decoration

The Decoration modules can be used to give your gadget a little bit of flourish. They have no function other than cosmetics.

Note that the grill decorations need to be picked up from the edges, if you're having trouble moving them.

FlashMemory

FlashMemory

The FlashMemory module allows you to store persistent data while your gadget is powered off or even put away while you're not playing Retro Gadgets.

Sizes

FlashMemory modules have a capacity for how much data they can store, and each different chip size has a different capacity:

small FlashMemory The small FlashMemory can store 4096 bytes.

medium FlashMemory The medium FlashMemory can store 32768 bytes.

large FlashMemory The large FlashMemory can store 256000 bytes.

Properties

Size number read only

Returns the overall capacity of the memory in bytes.

Usage number read only

Returns the amount of data in bytes currently stored.

Methods

Save( table table ) boolean

Saves table into the flash memory. Returns true if saving was successful, false otherwise.

Load( ) table

Returns the table stored in the memory.

Examples

To store data in a FlashMemory, your data must be in the form of a table.

If you want to store a default number value of 100, for example:

-- first we check if the flash is empty, meaning there is no data in it yet
-- this way we can store something default only if there isn't already saved data
local value = 100

if gdt.FlashMemory0.Usage == 0 then
	-- we put the value we want to store into a table
	-- constructing the table like so gives it a "value" key
	local data = {
		value = value
	}

	gdt.FlashMemory0:Save(data)
end

Once the gadget starts, it will check if there is already data saved in the FlashMemory. If there isn't it'll create a data table with a key value containing our value of 100, and save it.

We can then retrieve the same table that is in the FlashMemory again:

local data = gdt.FlashMemory0:Load()

-- this assumes your data table was given a "value" key
-- otherwise you can index it like any other table by numerical index: data[1]
log(tostring(data.value))

GamepadChip

GamepadChip

The GamepadChip allows you to control your gadgets using your computer's real game controller.

Similar in functionality to the Keyboard chip.

Properties

GamepadIndex number

Setting this property allows you to select which one of the gamepads currently connected to your computer the chip will use.

IsActive boolean read only

Returns whether or not the selected gamepad is currently active (plugged in and available) on your computer.

Methods

GetButton( name InputName ) InputSource

Returns an InputSource for a given button.

GetAxis( name InputName ) InputSource

Returns an InputSource axis for a given axis.

GetButtonAxis( negativeName InputName, positiveName InputName ) InputSource

Returns an InputSource axis controlled by two given buttons, one for negative and one for positive values.

Events

GamepadChipIsActiveEvent : { IsActive boolean, Type string }

Triggered when a gamepad becomes active or inactive.
* IsActive returns whether or not the selected gamepad currently became active.
* Type is "GamepadChipIsActiveEvent".

GamepadChipButtonEvent : { ButtonDown boolean, ButtonUp boolean, IsAxis boolean, InputName InputName, Type string }

Triggered when a gamepad control is pressed or released.
* ButtonDown is true if a button or axis was pressed at this event.
* ButtonUp is true if a button or axis was released at this event.
* IsAxis is true if the input that triggered this event is an axis.
* InputName returns the input name of the key that triggered this event.
* Type is "GamepadChipButtonEvent".

Input names

The following inputs can be used as axis, that is, they return analog values ranging from -100 to 100.

  • GamepadChip.LeftStickX
  • GamepadChip.LeftStickY
  • GamepadChip.RightStickX
  • GamepadChip.RightStickY

The following inputs can be used as buttons, that is, they can be pressed or released.

  • GamepadChip.ActionBottomRow1
  • GamepadChip.ActionBottomRow2
  • GamepadChip.ActionBottomRow3
  • GamepadChip.ActionTopRow1
  • GamepadChip.ActionTopRow2
  • GamepadChip.ActionTopRow3
  • GamepadChip.LeftShoulder1
  • GamepadChip.LeftShoulder2
  • GamepadChip.RightShoulder1
  • GamepadChip.RightShoulder2
  • GamepadChip.Center1
  • GamepadChip.Center2
  • GamepadChip.Center3
  • GamepadChip.LeftStickButton
  • GamepadChip.RightStickButton
  • GamepadChip.DPadUp
  • GamepadChip.DPadRight
  • GamepadChip.DPadDown
  • GamepadChip.DPadLeft

KeyboardChip

KeyboardChip

The KeyboardChip allows you to control your gadgets using your computer's real keyboard.

Similar in functionality to the Gamepad chip.

Methods

GetButton( name InputName ) InputSource

Returns an InputSource for a given key.

GetButtonAxis( negativeName InputName, positiveName InputName ) InputSource

Returns an InputSource axis controlled by two given keys, one for negative and one for positive values.

Events

KeyboardChipEvent : { ButtonDown boolean, ButtonUp boolean, InputName InputName, Type string }

Triggered when a key is pressed or released.
* ButtonDown is true if a key was pressed at this event.
* ButtonUp is true if a key was released at this event.
* InputName returns the input name of the key that triggered this event.
* Type is "KeyboardChipEvent".

Input names

  • KeyboardChip.Return
  • KeyboardChip.Space
  • KeyboardChip.LeftArrow
  • KeyboardChip.RightArrow
  • KeyboardChip.DownArrow
  • KeyboardChip.UpArrow
  • KeyboardChip.Backspace
  • KeyboardChip.Escape
  • KeyboardChip.Tab
  • KeyboardChip.Clear
  • KeyboardChip.Pause
  • KeyboardChip.Exclaim
  • KeyboardChip.DoubleQuote
  • KeyboardChip.Hash
  • KeyboardChip.Dollar
  • KeyboardChip.Percent
  • KeyboardChip.Ampersand
  • KeyboardChip.Quote
  • KeyboardChip.LeftParen
  • KeyboardChip.RightParen
  • KeyboardChip.Asterisk
  • KeyboardChip.Plus
  • KeyboardChip.Comma
  • KeyboardChip.Minus
  • KeyboardChip.Period
  • KeyboardChip.Slash
  • KeyboardChip.Alpha0
  • KeyboardChip.Alpha1
  • KeyboardChip.Alpha2
  • KeyboardChip.Alpha3
  • KeyboardChip.Alpha4
  • KeyboardChip.Alpha5
  • KeyboardChip.Alpha6
  • KeyboardChip.Alpha7
  • KeyboardChip.Alpha8
  • KeyboardChip.Alpha9
  • KeyboardChip.Colon
  • KeyboardChip.Semicolon
  • KeyboardChip.Less
  • KeyboardChip.Equals
  • KeyboardChip.Greater
  • KeyboardChip.Question
  • KeyboardChip.At
  • KeyboardChip.LeftBracket
  • KeyboardChip.Backslash
  • KeyboardChip.RightBracket
  • KeyboardChip.Caret
  • KeyboardChip.Underscore
  • KeyboardChip.BackQuote
  • KeyboardChip.A
  • KeyboardChip.B
  • KeyboardChip.C
  • KeyboardChip.D
  • KeyboardChip.E
  • KeyboardChip.F
  • KeyboardChip.G
  • KeyboardChip.H
  • KeyboardChip.I
  • KeyboardChip.J
  • KeyboardChip.K
  • KeyboardChip.L
  • KeyboardChip.M
  • KeyboardChip.N
  • KeyboardChip.O
  • KeyboardChip.P
  • KeyboardChip.Q
  • KeyboardChip.R
  • KeyboardChip.S
  • KeyboardChip.T
  • KeyboardChip.U
  • KeyboardChip.V
  • KeyboardChip.W
  • KeyboardChip.X
  • KeyboardChip.Y
  • KeyboardChip.Z
  • KeyboardChip.LeftCurlyBracket
  • KeyboardChip.Pipe
  • KeyboardChip.RightCurlyBracket
  • KeyboardChip.Tilde
  • KeyboardChip.Delete
  • KeyboardChip.Keypad0
  • KeyboardChip.Keypad1
  • KeyboardChip.Keypad2
  • KeyboardChip.Keypad3
  • KeyboardChip.Keypad4
  • KeyboardChip.Keypad5
  • KeyboardChip.Keypad6
  • KeyboardChip.Keypad7
  • KeyboardChip.Keypad8
  • KeyboardChip.Keypad9
  • KeyboardChip.KeypadPeriod
  • KeyboardChip.KeypadDivide
  • KeyboardChip.KeypadMultiply
  • KeyboardChip.KeypadMinus
  • KeyboardChip.KeypadPlus
  • KeyboardChip.KeypadEnter
  • KeyboardChip.KeypadEquals
  • KeyboardChip.Insert
  • KeyboardChip.Home
  • KeyboardChip.End
  • KeyboardChip.PageUp
  • KeyboardChip.PageDown
  • KeyboardChip.F1
  • KeyboardChip.F2
  • KeyboardChip.F3
  • KeyboardChip.F4
  • KeyboardChip.F5
  • KeyboardChip.F6
  • KeyboardChip.F7
  • KeyboardChip.F8
  • KeyboardChip.F9
  • KeyboardChip.F10
  • KeyboardChip.F11
  • KeyboardChip.F12
  • KeyboardChip.F13
  • KeyboardChip.F14
  • KeyboardChip.F15
  • KeyboardChip.Numlock
  • KeyboardChip.CapsLock
  • KeyboardChip.ScrollLock
  • KeyboardChip.RightShift
  • KeyboardChip.LeftShift
  • KeyboardChip.RightControl
  • KeyboardChip.LeftControl
  • KeyboardChip.RightAlt
  • KeyboardChip.LeftAlt
  • KeyboardChip.RightCommand
  • KeyboardChip.LeftCommand
  • KeyboardChip.AltGr
  • KeyboardChip.Help
  • KeyboardChip.Print
  • KeyboardChip.SysReq
  • KeyboardChip.Break
  • KeyboardChip.Menu

MagneticConnector

MagneticConnector

The MagneticConnector can be used to attach different parts of your gadget together. Once two connectors are attached, you can press the button in the middle of them to detach them.

Properties

ButtonState boolean read only

Reflect the pressed/released state of the connector's button.

IsConnected boolean read only

Returns true if the connector is connected to another one, false otherwise.

AttachedConnector MagneticConnector read only

Returns the other MagneticConnector module that this one is attached to, if any.

Events

MagneticConnectorEvent : { IsConnected boolean, Type string }

Triggered once another MagneticConnector attaches or detaches from this one.
* IsConnected is true if another connector is attached.
* Type is "MagneticConnectorEvent".

PowerButton

PowerButton

The PowerButton turns your gadget on or off. You must have a PowerButton in your gadget for it to work, and you can only have one at a time. There are different designs of Power buttons, so you need to move it (using the striped handle) into the modules drawers to pick a different one. PowerButtons with blue handles can only go in the middle of your gadgets, while ones with red handles can only go on the edges of your gadget.

Note: Since you can only have one PowerButton per gadget, it will not have a number after its name in your gadget's list of modules:

gdt.PowerButton

Properties

ButtonState boolean read only

Currently only returns false, pressing down the PowerButton will always toggle the running status of your Gadget.

RealityChip

RealityChip

The RealityChip lets you read some values about real-world usage of your computer from inside Retro Gadgets.

Properties

Cpu.TotalUsage number read only

The total CPU usage of the system, from 0 to 100.

Cpu.CoresUsage {number} read only

An array that contains the CPU usage of each logical CPU core, from 0 to 100.

Ram.Available number read only

Available RAM expressed in MB.

Ram.Used number read only

Used RAM expressed in MB.

Network.TotalSent number read only

Total sent by network interfaces expressed in Mbps.

Network.TotalReceived number read only

Total received from network interfaces expressed in Mbps.

LoadedAssets Asset read only

Array containing all the assets currently loaded from the Documents/My Games/Retro/UserData folder.

Methods

GetDateTime( ) {}

Returns a table containing the current local date and time.
The values in the table are:
* year
* month
* day
* yday (year day)
* wday (week day)
* hour
* min
* sec
* isdst (is daylight saving time)

GetDateTimeUTC( ) {}

Returns a table containing the current local date and time expressed as the Coordinated Universal Time (UTC).
The values in the table are:
* year
* month
* day
* yday (year day)
* wday (week day)
* hour
* min
* sec
* isdst (is daylight saving time)

LoadAudioSample( filename string ) AudioSample

Load an AudioSample asset from an audio file.
The file must be in Documents/My Games/Retro/UserData, filename represents the relative path.

LoadSpriteSheet( filename string, spritesWidth number, spritesHeight number ) SpriteSheet

Load a SpriteSheet asset from an image file.
The file must be in Documents/My Games/Retro/UserData, filename represents the relative path.

UnloadAsset( filename string )

Unload a previously loaded asset.
Remove it from LoadedAssets and make it invalid.

ListDirectory( directory string ) string[]

Returns the list of files and folders contained in the specified folder.
The directory must be in Documents/My Games/Retro/UserData, the directory parameter represents the relative path.

GetFileMetadata( filename string ) {}

Returns a table containing information about the file without the need to load it.
The file must be in Documents/My Games/Retro/UserData, filename represents the relative path.

ROM

ROM

The ROM chip gives you access to assets through code. These include some built-in assets such as the StandardFont.

Note: Since you can only have one ROM per gadget, it will not have a number after its name in your gadget's list of modules:

gdt.ROM

Properties

ROM contains two main properties, User and System. User gives you access to your own assets added to your gadget through the Multitool, and System accesses the built-in assets. Each one of those two properties have their own tables as such:

Assets {Asset} read only

Returns a table of all assets regardless of type.

SpriteSheets {SpriteSheet} read only

Returns a table of only SpriteSheets.

Codes {Code} read only

Returns a table of only Code assets.

AudioSamples {AudioSample} read only

Returns a table of only AudioSamples.

Examples

Say you have a sprite sheet in your gadget's assets named "Player.png" which contains sprites for a player character. To use it with the VideoChip's DrawSprite method, you would load it like so:

local sheet = gdt.ROM.User.SpriteSheets["Player.png"]

gdt.VideoChip0:DrawSprite(vec2(10,10), sheet, 0, 0, color.white, color.clear)

In this case we are drawing the sprite at the requested SpriteSheet sheet's position 0, 0 onto the Screen position 10, 10, without affecting its tint or transparency.

If you want to print text using the default font, you need to load it like so:

local font = gdt.ROM.System.SpriteSheets["StandardFont"]

gdt.VideoChip0:DrawSprite(vec2(10,10), sheet, "Hello world!", color.white, color.clear)

This will use the StandardFont to print the string "Hello world!" at the screen's position 10, 10, in white color and no background. If you want to make a custom font to use with this function instead, check the font function of the Sprite Editor.

SecurityChip

SecurityChip

The security chip makes it so your gadget's assets and code cannot be inspected or exported by other players when you publish your gadget on the Workshop. Once you include it in your gadget, each one of your assets will have an icon next to it to deny (red lock) or allow (green eye) access by people inspecting your gadget.

asset list with security options

Note: Since you can only have one SecurityChip per gadget, it will not have a number after its name in your gadget's list of modules:

gdt.SecurityChip

However, there is currently no code that uses the SecurityChip.

VideoChip

VideoChip

The VideoChip controls Screens and ScreenButtons to display graphics on them using SpriteSheets.

Sizes

There are three different VideoChip sizes, but currently their behavior is identical.

small VideoChip

medium VideoChip

large VideoChip

Properties

Mode VideoChipMode

The buffering mode for this VideoChip. Defaults to DoubleBuffer.

Width number read only

Width in pixels of the rendering buffer. The area takes into account all the displays connected to this VideoChip.

Height number read only

Height in pixels of the rendering buffer. The area takes into account all the displays connected to this VideoChip.

RenderBuffers {RenderBuffer} read only

List of RenderBuffers on which the VideoChip can render.

TouchState boolean read only

The pressed/released state of the screen touch interaction.

TouchDown boolean read only

A boolean flag that will only be set to true during the time tick in which the TouchState changes from released to pressed.

TouchUp boolean read only

A boolean flag that will only be set to true during the time tick in which the TouchState changes from pressed to released.

TouchPosition vec2 read only

The position of the touch interaction on the screen area.

Methods

RenderOnScreen( )

This method sets the VideoChip to use screens as render targets.

RenderOnBuffer( index number )

This method sets the VideoChip to use one of its RenderBuffers as a render target.
The index parameter identifies the RenderBuffer to use.

SetRenderBufferSize( index number, width number, height number )

Resize one of the VideoChip's RenderBuffers.

Clear( color color )

Clears all the render area with the specified color

SetPixel( position vec2, color color )

Sets the pixel at the specified position to the specified color

DrawPointGrid( gridOffset vec2, dotsDistance number, color color )

Draws a dotted grid on the entire display area, with an offset. The dotsDistance parameter express the distance in pixels, on both axis, between dots.

DrawLine( start vec2, end vec2, color color )

Draws a line from position start to position end, using the specified color

DrawCircle( position vec2, radius number, color color )

Draws an empty circle at the specified position, with the specified radius, in the specified color.

FillCircle( position vec2, radius number, color color )

Draws a filled circle at the specified position, with the specified radius, in the specified color.

DrawRect( position1 vec2, position2 vec2, color color )

Draws an empty rect from position1 to position2, in the specified color.

FillRect( position1 vec2, position2 vec2, color color )

Draws a filled rect from position1 to position2, in the specified color.

DrawTriangle( position1 vec2, position2 vec2, position3 vec2, color color )

Draws an empty triangle with vertexes in position1,position2 and position3, in the specified color.

FillTriangle( position1 vec2, position2 vec2, position3 vec2, color color )

Draws a filled triangle with vertexes in position1,position2 and position3, in the specified color.

DrawSprite( position vec2, spriteSheet SpriteSheet, spriteX number, spriteY number, tintColor color, backgroundColor color )

Draws a specific sprite frame from the spriteSheet.
Position is the on-screen sprite desired position starting from the top left corner,
spriteSheet is the SpriteSheet asset containing the sprite frame to draw,
spriteX and spriteY are the coordinates to identify the desired sprite frame starting from the, top left corner, expressed in grid units.
spriteX=0 and spriteY=0 are the coordinates of the first sprite, top left.
tintColor is the color multiplier used to draw the sprite frame. Color(255,255,255) or color.white will leave the sprite frame unaffected.
backgroundColor is the color used to replace the transparent areas of the spriteFrame. Using ColorRGBA(0,0,0,0) or color.clear will leave the transparency as it is.

DrawCustomSprite( position vec2, spriteSheet SpriteSheet, spriteOffset vec2, spriteSize vec2, tintColor color, backgroundColor color )

Draw a portion of a SpriteSheet (defined by spriteOffset, spriteSize) without taking into account the grid

DrawText( position vec2, fontSprite SpriteSheet, text string, textColor color, backgroundColor color )

Draws the string contained in the text parameter, at the desired position, using textColor and backgroundColor.
The parameter fontSprite expect a spriteSheet asset labeled as font.
By placing a ROM you can access the standard font: gdt.ROM.System.SpriteSheets["StandardFont"]

RasterSprite( position1 vec2, position2 vec2, position3 vec2, position4 vec2, spriteSheet SpriteSheet, spriteX number, spriteY number, tintColor color, backgroundColor color )

Draws a specific sprite frame from the spriteSheet mapping it on a quad identified by position1, position2, position3, position4

RasterCustomSprite( position1 vec2, position2 vec2, position3 vec2, position4 vec2, spriteSheet SpriteSheet, spriteOffset vec2, spriteSize vec2, tintColor color, backgroundColor color )

Draws a portion of a SpriteSheet (defined by spriteOffset, spriteSize) without taking into account the grid mapping it on a quad identified by position1, position2, position3, position4

DrawRenderBuffer( position vec2, renderBuffer RenderBuffer, width number, height number )

Draws a render buffer (supposedly coming from Webcam component) at the desired position, width and height

RasterRenderBuffer( position1 vec2, position2 vec2, position3 vec2, position4 vec2, renderBuffer RenderBuffer )

Draws a RenderBuffer mapping it on a quad identified by position1, position2, position3, position4.

SetPixelData( pixelData PixelData )

Apply pixelData content to VideoChip's video buffer, width and height must have the same value as VideoChip's

BlitPixelData( position vec2, pixelData PixelData )

Draw a pixelData content to VideoChip's video buffer at the offset of position. This way PixelData width and height don't need to match the VideoChip's

Events

VideoChipTouchEvent : { TouchDown boolean, TouchUp boolean, Value vec2, Type string }

Sent when the touch interaction is pressed or released.

Buffering modes

There are two valid values for a VideoChipMode:

  • SingleBuffer
  • DoubleBuffer

Double buffering means the video chip will flip between two buffers instead of constantly refreshing the same one. This is a technique to prevent image tearing, since with two buffers you don't see the current active one being drawn to. In practice, use whichever mode seems to work best in your gadget.

Wifi

Wifi

Properties

AccessDenied boolean read only

Will return true if network access is denied. See Permissions for more information.

Methods

WebGet( url string ) number

Send a web HTTP GET request, return a numeric handle to identify the request

WebPutData( url string, data string ) number

Send a web HTTP PUT request, return a numeric handle to identify the request

WebPostData( url string, data string ) number

Send a web HTTP POST request, return a numeric handle to identify the request

WebPostForm( url string, form table ) number

Send a web HTTP POST request, return a numeric handle to identify the request

WebCustomRequest( url string, method string, customHeaderFields table, contentType string, contentData string ) number

Send a web request, return a numeric handle to identify the request

WebAbort( handle number ) boolean

Abort a web request

GetWebUploadProgress( handle number ) number

Returns a percentage (0-100) representing the progress of the current upload associated with the given handle

GetWebDownloadProgress( handle number ) number

Returns a percentage (0-100) representing the progress of the current download associated with the given handle

ClearCookieCache( )

Clear stored cookies

ClearUrlCookieCache( url string )

Only cookies that apply to this url will be removed from the cache

Events

WifiWebResponseEvent : { RequestHandle number, ResponseCode number, IsError boolean, ErrorType string, ErrorMessage string, ContentType string, Text string, Data string, Type string }

Sent when a web request is completed by the remote server.
* RequestHandle is the handle of the request, which matches the number returned by the function that initiated it.
* ResponseCode is the HTTP code returned by the server.
* IsError is a boolean value which indicates if the response wasn't successful.
* ErrorType and ErrorMessage elaborate on the error encountered by the request.
* ContentType is the MIME type of the content the server returned.
* Text is the UTF8 encoded content of the response.
* Data is the binary content of the response.
* Type is "WifiWebResponseEvent".

Examples

When making a web request to a URL, the request function will give you only a handle to keep track of said request. To get the actual response from the server, since it's an asynchronous process, we need to use an event. Here is an example which uses World Time API to get information about the current time in the London timezone:

gdt.Wifi0:WebGet("http://worldtimeapi.org/api/timezone/Europe/London.txt")

function eventChannel1(sender, response)
	if response.IsError == false then
		log(response.Text)
	end
end

The call to WebGet, outside of the update function, will run only once when the gadget is powered on. Then, once the server is done replying to us, the eventChannel1 function will trigger. If our response is not an error code, the text content of the response will be shown in the Multitool debug screen.

Note: In order for this example to work, your WiFi module must be set on your CPU's event channel 1, and you must enable network permissions for your gadget.

Serial

Serial

Module that allows communication through the serial port.

Properties

ReceiveMode SerialReceiveMode

Determines the type of data received by the SerialReceiveEvent event.
The BinaryData option passes binary data into the event's Data property.
The Lines option splits the received data into lines using the \n character as a separator, writing the result inside the event's Lines property.

Port number

Serial COM port number.

IsActive boolean read only

Indicates if the serial connection on the specified port is active.

BaudRate number

The serial connection baud rate.

DataBits number

The standard length of data bits per byte.
The value must be between 5 and 8 inclusive.

Parity SerialParity

The serial connection parity-checking protocol.

StopBits SerialStopBits

The number of stopbits per byte.

Methods

WriteInt8( value number )

Writes an 8-bit integer to the serial port.

WriteUInt8( value number )

Writes an 8-bit unsigned integer to the serial port.

WriteInt16( value number )

Writes a 16-bit integer to the serial port.

WriteUInt16( value number )

Writes a 16-bit unsigned integer to the serial port.

WriteInt32( value number )

Writes a 32-bit integer to the serial port.

WriteUInt32( value number )

Writes a 32-bit unsigned integer to the serial port.

WriteFloat32( value number )

Writes a 32-bit float to the serial port.

WriteFloat64( value number )

Writes a 64-bit float to the serial port.

Write( data string )

Writes a byte array to the serial port.

Print( text string )

Write a utf8 string to the serial port.

Println( text string )

Writes a string with a \n (new line) added at the end to the serial port.

GetAvailablePorts( ) number[]

Returns an array containing the available serial COM port numbers.

Events

SerialIsActiveEvent : { IsActive boolean, Type string }

Sent when the serial IsActive state change.
* IsActive indicates if the serial connection on the specified port is active.
* Type is "SerialIsActiveEvent".

SerialReceiveEvent : { Lines {}, Data string, Type string }

Sent when data is received from a Serial module.
The module's ReceiveMode property determines the type of data received by the event.
* The SerialReceiveMode.BinaryData option passes binary data into the Data property.
* The SerialReceiveMode.Lines option splits the received data into lines using the \n character as a separator, writing the result inside the Lines property.
* Type is "SerialReceiveEvent".

Receive modes

There are two valid values for a SerialReceiveMode:

  • BinaryData
  • Lines

The BinaryData option passes binary data into the event's Data property, the Lines option splits the received data into lines using the \n character as a separator, writing the result inside the event's Lines property.

Parity

SerialParity specifies the parity bit mode:

  • None No parity check occurs.
  • Odd Sets the parity bit so that the count of bits set is an odd number.
  • Even Sets the parity bit so that the count of bits set is an even number.
  • Mark Leaves the parity bit set to 1.
  • Space Leaves the parity bit set to 0.

Stop bits

SerialStopBits Specifies the number of stop bits.

  • One One stop bit is used.
  • OnePointFive 1.5 stop bits are used.
  • Two Two stop bits are used.

Assets

SpriteSheet

A SpriteSheet asset contains images to be used in a gadget, which can be displayed with the VideoChip and Screen, or printed as stickers to decorate the shell of the gadget. You can import PNG images as SpriteSheets in the Multitool or edit them in-game with the Sprite Editor.

To import a SpriteSheet from your computer, the image must be at most 256 x 64 pixels in size, with best results when using "PNG-8" or "Indexed PNG" modes.

Properties

Name string read only

The name of the asset.

Type string read only

String containing "SpriteSheet"

Palette Palette read only

Returns the palette used in the sprite sheet, which can be seen in the Sprite Editor. Currently, there's no code that can be used with this.

Methods

IsValid( ) boolean

Returns true if the asset reference is still valid.
References can become invalid if the asset is unloaded.

GetPixelData( ) PixelData

Returns a PixelData object containing the SpriteSheet data.

GetSpritePixelData( spriteX number, spriteY number ) PixelData

Returns a PixelData object containing the data of a specific sprite. The sprite is identified by spriteX and spriteY which represent its coordinate on the grid.

Code

A Code asset contains the Lua script that will be run by your gadget's CPU. It can be created through the Code Editor in-game, or imported into the assets list.

While you can retrieve a Code asset with the ROM chip, there's currently no methods that use this asset type that way.

AudioSample

An AudioSample is a piece of audio that can be played back from your gadget with the AudioChip module.

To import an AudioSample, you must use OGG, WAV or MP3 files, and for best results they must be 44.1kHz and 16bit.

Currently, there's no way to create an AudioSample purely in-game, but audio generation is planned for the future.

Properties

Name string read only

The name of the asset.

Type string read only

String containing "AudioSample"

SamplesCount number read only

Channels number read only

Frequency number read only

Length number read only

Length of the AudioSample in seconds

Metadata {} read only

Table that contains the AudioSample's metadata, it can contain the following values:
* LongDescription
* Date
* Year
* TrackNumber
* TrackTotal
* DiscNumber
* DiscTotal
* Popularity
* SeriesPart
* Bitrate
* BitDepth
* SampleRate
* IsVariableBitRate
* Duration
* SeriesTitle
* SortTitle
* Title
* Artist
* Composer
* Comment
* Genre
* Album
* Group
* OriginalArtist
* Copyright
* OriginalAlbum
* Publisher
* PublishingDate
* AlbumArtist
* Conductor
* ProductId
* SortAlbum
* SortAlbumArtist
* SortArtist
* Description

Methods

IsValid( ) boolean

Returns true if the asset reference is still valid.
References can become invalid if the asset is unloaded.

RenderBuffer

A RenderBuffer is similar to a PixelData, however it can't be modified directly from code.

VideoChips have an array of child RenderBuffers, with the VideoChip.RenderOnBuffer method you can set one as render target allowing you to use draw methods on the selected RenderBuffer.

A RenderBuffer can also be obtained with the Webcam module, to be displayed with VideoChip methods.

Properties

Name string read only

The name of the asset.

Type string read only

String containing "RenderBuffer"

Width number read only

Width in pixels.

Height number read only

Height in pixels.

Methods

IsValid( ) boolean

Returns true if the asset reference is still valid.
References can become invalid if the asset is unloaded.

GetPixelData( ) PixelData

Returns a PixelData object containing the RenderBuffer data.