Login | Signup

Documentation

Geolua is not only about playing adventures. It is also about creating them. Here you will find all the information you need to get started.

Once you have created an adventure, users can create instances of this adventure, called games. Each user entering a game is called a player within the game. Each game can have multiple players. It is therefore possible to write multiplayer adventures.

All adventures are written in Lua. Lua is a powerful scripting language. Lua is easy to learn. You can get started in minutes. See https://lua.org/ for more information about Lua or open up the reference manual or the book Programming in Lua.

Overview

Once a user creates a new game, a new Lua instance is started and the adventure code is loaded. This code is responsible for controlling the actions the player can take. Here is a very simple "Hello World" example.

-- hello world
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text("Hello World"))
end)

The geo namespace within each Lua instance is provided by the runtime environment. You can react to events (like a player joining a game) using functions provided in the geo.event namespace.

Each player can interact with the game using a user interface. The runtime environment provides functions to modify the user interface using functions in the geo.ui namespace.

Each user interface consists of widgets. Currently the game provides 6 different widgets to build the user interface for a player. Widgets are created using functions in the geo.widget namespace. Once a widget is constructed, it can be added to one or many user interfaces.

In the example above the user interface for each joining player consists of a single text widget showing the text "Hello World".

Here is another example showing a slightly more complex interaction:

-- button interaction
geo.event.join(function(event)
    local player_id = event.player_id 

    geo.ui.append(player_id, geo.widget.button{
        text = "Click me";
        onClick = function(event)
            geo.ui.clear(player_id)
            geo.ui.append(player_id, geo.widget.text("You clicked the button"))
        end
    })
end)

Each player that joins the game is shown a button. Once the player clicks the button, the callback specified by onClick is fired. It clears the interface displayed to the player (thereby removing the button) and adds a new text widget showing the message.

If you run the example above, try reloading the game page. You'll notice, that the game won't restart. Since all state of the game is saved on the server side, players can reload their games or even reopen a game at a later time and always see the state the game had when they left.

Multiplayer

All "games" shown above don't have any multiplayer interaction. Lets try a very simple multiplayer "game":

-- multiplayer interaction
players = {}

function update_ui_for_all_players()
    for _, player_id in ipairs(players) do
        geo.ui.clear(player_id)
        geo.ui.append(player_id, geo.widget.text(
            string.format("Players in the game: %d", #players)
        ))
    end
end

geo.event.join(function(event)
    table.insert(players, event.player_id)
    update_ui_for_all_players()
end)

Click Try it on your desktop. Once the game is loaded, click on the Invite other players link at the bottom of the screen. Scan the resulting qr code on your mobile. You should see Players in the game: 2 on both your desktop and mobile now.

If you do multiplayer code, be sure to prefix local variables with the local keyword. Otherwise strange effects might happen if users overwrite each others variables. See https://www.lua.org/pil/4.2.html for more information on scoping in Lua.

Debugging

Everyone makes mistakes. You code can contain syntax errors or have runtime errors. Once the environment detects an error in your game, the players are redirected to a crash page. As developer of an adventure, you can see a detailed explanation the the crash.

-- syntax error example
huh?
-- boot error
error("fnord")
-- runtime error example
geo.event.join(function(event)
    error("Happy crashing")
end)

Lua API Documentation

Widgets

Every player in a game has a user interface consisting of widgets. Widgets allow the player to interact with the game.

geo.widget.text(markup)

Creates a widget adding containing markup. The markup is expected to be formatted in the creole wiki syntax (For more information, see below). This widget must then be added to a player user interface using one of the geo.ui functions.

The markup supports macros. They are used for example to display badges. Read more about it in the creole markup help below.

-- text widget example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text("Hello World"))
end)

geo.widget.button(spec)

Creates a clickable button.

spec is a table containing the following values

text required The caption of the button
width optional: 1, 2 or 3 (default 1) Numeric width of the button. 1 lets the button take 100% of the available horizontal space. 2 allows 2 adjacent buttons, 3 allows 3 adjacent buttons.
onClick required, function(event) Function called, once the button is clicked. event is an empty table
-- button widget example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.button{
        text = "click me";
        onClick = function(event)
            -- some action
        end
    })
    for i = 1, 2 do 
        geo.ui.append(event.player_id, geo.widget.button{
            text = "click me";
            width = 2;
            onClick = function(event)
                -- some action
            end
        })
    end
    for i = 1, 3 do 
        geo.ui.append(event.player_id, geo.widget.button{
            text = "click me";
            width = 3;
            onClick = function(event)
                -- some action
            end
        })
    end
end)

geo.widget.input(spec)

Creates a one-line text input box with a submit button.

spec is a table containing the following values

value optional, default '' (the empty string) Preset value of the input field
text optional, default 'Ok' Caption of the submit button
onSubmit required, function(event) Function called, once the input field is submitted. event is a table. See below

The onSubmit callback is given a table containing the following values

value String value of the text in the input box
-- input widget example
geo.event.join(function(event)
    local player_id = event.player_id
    geo.ui.append(player_id, geo.widget.input{
        text = "That's my name";
        onSubmit = function(event)
            geo.ui.append(player_id, geo.widget.text(
                "Hello, " .. event.value
            ))
        end
    })
end)

geo.widget.compass(spec)

Creates a compass widget. It provides the player with the ability to find a target. The target location is displayed on a map. The relative direction as well as the distance to the target is displayed.

You must enable GPS on a players device using the geo.feature.gps function. Otherwise the compass won't show direction and distance.

spec is a table containing the following values

target required, table A table containing lat and lon. The compass guides the player to this location
radius optional, default 10 The onInRange callback is triggered if the player is within the specified distance (in meters) to the target
onInRange required, function(event) Function called, once the player is within range (as specified by radius) of the target. event is an empty table.
showControls optional, default false Whether or not to show zoom controls
-- compass widget example
geo.event.join(function(event)
    local player_id = event.player_id
    geo.feature.gps(player_id, true) -- activate GPS
    geo.ui.append(player_id, geo.widget.compass{
        target = { lat = 37.826526, lon = -122.422512 };
        radius = 300;
        onInRange = function(event)
            geo.ui.append(player_id, geo.widget.text(
                "Welcome to Alcatraz"
            ))
        end
    })
end)

geo.widget.qrcode(spec)

Creates a QR code widget. It shows a QR code to be scanned by other players. Once another player scans the QR code, a callback is triggered.

spec is a table containing the following values

onScanned required, function(event) Function called, once the QR code was scanned by another player who then (re)joined the game. event is a table. See below

Once another player scanned the QR code and accepted to join the game, the onScanned callback is fired. It is given a table containing the following values

scan_player_id The player_id of the player that scanned the QR code.
medium A string containing "qr" or "click". The value "qr" is provided for normal QR based scans. "click" is used, if the player clicked on the displayed QR code
-- qr code widget example
geo.event.join(function(event)
    local player_id = event.player_id
    geo.ui.append(player_id, geo.widget.text "Scan me!")
    geo.ui.append(player_id, geo.widget.qrcode{
        onScanned = function(event)
            geo.ui.append(player_id, geo.widget.text(
                "Yay. It was scanned"
            ))
            geo.ui.append(event.scan_player_id, geo.widget.text(
                "Thanks for scanning"
            ))
        end
    })
end)

geo.widget.places(spec)

Creates a widget describing places. It displays a list of places, sorted by player distance. Each place is described using an image and text. The player can see the distance as well as direction to each place.

You must enable GPS on a players device using the geo.feature.gps function. Otherwise the distance and sorting won't be active.

spec is a table containing the following values

places required, table A table (used as an array) containing places.

Each place is described using a table containing the following values

id required A string value giving the place an id, which is then used in the onSelected callback
image required The url of an image describing the place
text required Markup in creole wiki sytnax describing the place.
location required A table containing lat and lon values. It should specify the location of the place
onSelected required, function(event) The callback fired, if this place is selected by the user. event is a table. See below

Once a player selects a place, the onSelected callback is fired. It is given a table containing the following values

id The string value specified when defining the place

If you use individual callbacks for each location, the id value might be ignored. Its primary use is for a places widget where each place shares the same onSelected handler.

-- places widget example
geo.event.join(function(event)
    local player_id = event.player_id
    geo.feature.gps(player_id, true) -- activate GPS

    local function goThere(event)
        geo.ui.clear(player_id)
        geo.ui.append(player_id, geo.widget.text(
            "Going to " .. event.id
        ))
    end

    geo.ui.append(player_id, geo.widget.places{
        places = {
            {
                id = 'Baikonur';
                image = 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Baikonuriss.jpg/320px-Baikonuriss.jpg';
                text = 'Baikonur';
                location = { lat = 45.616667, lon = 63.316667 };
                onSelected = goThere;
            },
            {
                id = 'Cape Canaveral';
                image = 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Cape_canaveral.jpg/200px-Cape_canaveral.jpg';
                text = 'Cape Canaveral';
                location = { lat = 28.455556, lon = -80.527778 };
                onSelected = goThere;
            },
            {
                id = 'Kourou';
                image = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/CSG_Ariane_4_Launch_Site.JPG/320px-CSG_Ariane_4_Launch_Site.JPG';
                text = 'Kourou';
                location = { lat = 5.237222, lon = -52.760556 };
                onSelected = goThere;
            }
        }
    })
end)

Interfaces functions

Every player in an adventure has one user interface. You can add widgets to this user interface. Widgets can only be added or removed from the player user interface. You cannot change a widget.

geo.ui.clear(player_id)

Clears all widgets currently displayed to player player_id.

geo.ui.append(player_id, widget)

Appends the given widget to the user interface shown to the player player_id. Widgets can be created using the geo.widget functions.

-- appending widgets example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text "Hello World")
end)

Returns a widget_id. You can use it to delete the widget using geo.ui.remove.

geo.ui.insert(player_id, before_idx, widget)

Inserts the widget to the user interface at position before_idx. Note that Lua uses 1-based tables. The first widget is at index 1.

You can use negative indices to insert widgets relative to the last inserted widget.

-- inserting widgets example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text "Appended")
    geo.ui.insert(event.player_id,  1, geo.widget.text "Inserted at index 1")
    geo.ui.insert(event.player_id, -1, geo.widget.text "Inserted at index -1")
end)

Returns a widget_id. You can use it to delete the widget using geo.ui.remove.

geo.ui.remove(player_id, widget_id)

Removes a widget from a player interface.

-- remove widget example
geo.event.join(function(event)
    local widget_id = geo.ui.append(event.player_id, geo.widget.text("First text"))
    geo.ui.append(event.player_id, geo.widget.text("Second text"))
    geo.ui.remove(event.player_id, widget_id)
end)

Returns two values. The first value indicates success: true if the widget was removed, false otherwise. The second value is the index of the deleted widget.

geo.ui.replace(player_id, widget_id, widget)

Replaces the widget widget_id using the given new widget.

-- replace widget example
geo.event.join(function(event)
    local player_id = event.player_id

    geo.ui.append(player_id, geo.widget.text "First text")
    local widget_id = geo.ui.append(player_id, geo.widget.text "This is a counter")
    geo.ui.append(player_id, geo.widget.text "Second text")

    local counter = 0
    geo.ui.append(player_id, geo.widget.button{
        text = "Increment Counter";
        onClick = function()
            counter = counter + 1
            widget_id = geo.ui.replace(player_id, widget_id, geo.widget.text("counter " .. counter))
        end
    })
end)

Returns the widget_id of the new widget. geo.ui.replace is implemented like this:

function geo.ui.replace(player_id, widget_id, widget)
    local ok, old_idx = geo.ui.remove(player_id, widget_id)
    assert(ok, "old widget not found")
    return geo.ui.insert(player_id, old_idx, widget)
end

Player device features

Some device features for a player must be activated before using them.

geo.feature.gps(player_id, status)

Activates the GPS device for a player. If the player device has GPS support, if will ask the player for permission to receive location updates.

If the player denies location updates, the runtime will invoke callbacks registered by the geo.event.gps_error function.

If GPS is started, the runtime will send location updates using callbacks registered by the geo.event.location function.

If your game uses GPS and later stops using location updates, you should deactivate the GPS for your players. This will reduce power consumption.

-- activate GPS
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "This examples ask for GPS permissions"
    ))
    geo.feature.gps(event.player_id, true) -- activate GPS
end)

Game events

Your adventure can react to events. Use the following functions to register event handlers. Each call adds (not replaces) an event handler.

geo.event.join(function(event) ... end)

Registers a new event handler for player join events. This event is called once for every new player that enters the game.

event is a table containing the following values

player_id The numeric player_id of the joining player
source How the player entered the game

source can have different values depending on how the player entered the game.

"create" The player created the game using the website
"join" The player joined the game using the website
"qr_join" The player joined the game by scanning a QR code displayed using the QR code widget

geo.event.open(function(event) ... end)

Registers a new event handler that fires every time a player opens a game. Players open a game either by creating a new game, by (re)joining a running game or by reloading the game page.

The open event is guaranteed to fire after the initial geo.event.join event.

event is a table containing the following values

player_id The numeric player_id of the player opening the game
-- open event handler example
geo.event.open(function(event)
    local player_id = event.player_id
    geo.ui.append(player_id, geo.widget.text(
        "You (re)opened this game (try reloading or rejoining)"
    ))
end)

geo.event.location(function(event) ... end)

Registers a new event handler that is fired for each location update of a player. A player starts sending location updates, if the gps feature is enabled for a player using the geo.feature.gps function.

event is a table containing the following values

player_id The numeric player_id of the player sending the update
pos A table containing lat and lon values
accuracy The estimated accuracy in meters
-- location event handler example
geo.event.join(function(event)
    geo.feature.gps(event.player_id, true) -- Activate GPS
    geo.ui.append(event.player_id, geo.widget.text(
        "Waiting for location..."
    ))
end)

geo.event.location(function(event)
    geo.ui.clear(event.player_id)
    geo.ui.append(event.player_id, geo.widget.text(
        "You are now at " .. event.pos.lat .. ", " .. event.pos.lon
    ))
end)

geo.event.gps_error(function(event) ... end)

Registers a new event handler that is fired if the game detects a problem related to GPS. Each players GPS device must be enabled using geo.feature.gps. If the activation fails, this event handler is fired.

event is a table containing the following values

player_id The numeric player_id of the player having gps problems
reason A string describing the problem

Reason can have one of the following values:

"no gps support" The player has no GPS capable device
"unknown error" Well. Some unknown error happend.
"permission denied" The user refused to activate GPS support.
"position unavailable" The position of the player device could not be determined. For instance, one or more of the location providers used in the location acquisition process reported an internal error that caused the process to fail entirely.
"timeout" The player device could not acquire a position within 30 seconds. This error might fire repeatedly, if it detects the same condition again.

If a player refused to activate GPS, the player must reload the game using browser controls, before the GPS support can be activated again. Trying to activate the GPS using geo.feature.gps will fail without reloading the page.

-- gps error example

geo.event.open(function(event)
    -- Try to activate GPS every time the player opens the game.
    geo.feature.gps(event.player_id, true) -- Active GPS
    geo.ui.clear(event.player_id)
    geo.ui.append(event.player_id, geo.widget.text(
        "Waiting for GPS permissions"
    ))
end)

geo.event.gps_error(function(event)
    geo.ui.clear(event.player_id)
    geo.ui.append(event.player_id, geo.widget.text(
        "gps error. reason: " .. event.reason
    ))
end)

geo.event.location(function(event)
    geo.ui.clear(event.player_id)
    geo.ui.append(event.player_id, geo.widget.text(
        "Location update received. Please revoke GPS permissions and reload this page"
    ))
end)

geo.event.qr_scan(function(event) ... end)

Registers a new event handler that is fired every time a user scan a QR code related to the game.

event is a table containing the following values

player_id The numeric player_id of the player that scanned a QR code
source String value specifying the source of the scan

There are two kind of QR code sources available:

QR Codes generated by a player

They a created by the qrcode widget. Once a player scans the QR code displayed by this widget and joins the game, the qr_scan event is fired. The event table contains the following additional values.

source "player"
qr_player_id player_id of the player that displayed the QR code widget
QR Codes generated by the adventure

They are created outside of the game and are meant to be physical QR codes tied to an adventure. If a user scans one of them, three things can happen:

  • The user didn't play the adventure referenced in the QR code before. Geolua creates a new game for this adventure. The user then enters this new game.
  • The user played the adventure before and there is only one open game. In that case, the user reopens the previously created game.
  • The user played the adventure before and has multiple games running. In that case, a page is displayed, asking the user to choose one of the previous games.

In all cases, the qr_scan event is fired. The event table contains the following additional fields.

source "adventure"
via_id The numerical id used when creating the QR code

via_id is specified when creating an adventure QR code. You can use it to encode different physical locations to a QR code. To create your own QR codes, edit your adventure and select the Adventure QR codes tab.

-- qr_scan event handler example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.qrcode{
        onScanned = function(event) end -- ignore
    })
end)

geo.event.qr_scan(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "QR code scanned. Source is " .. event.source
    ))
end)

geo.event.link(function(event) ... end)

Registers a new event handler that is fired if the player clicks on a link created by the link macro.

event is a table containing the following values

player_id The numeric player_id of the player clicking the link
link_id The link id uses in the macro
-- link example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "This is <<link(test)|a link>>!"
    ))
end)

geo.event.link(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "Link clicked: " .. event.link_id
    ))
end)

geo.event.external(function(event) ... end)

Registers a new event handler that is fired if external data is pushed into geolua using the HTTP API. The pushed JSON data is available in the event table. See the HTTP API documentation below for more information about this event.

Game functions

Some functions that control your game. You can give badges to players.

geo.game.badge(player_id, badge_id)

You can reward your players by giving them badges. They are meant to be archievments like "finished game" or "found all places".

You create badges in the adventure editor. Use the Badge Tab to create badges. Every badge has a unique badge_id and is tied to your adventure.

Once you have created a badge, the editor will display sample code that shows you how to reward players with the badge you created.

You can display a badge using the badge macro within markup.

geo.game.close()

Closes the game. All players will be redirected to a "game over" page where they can choose to play other games. Once a game is closed, its players cannot return into the game. The game disappears from the "Your recent games" information. Be sure to call this function for games that have an end condition. Otherwise finished games pile up within the "Your recent game" information of your players.

-- close example
geo.event.join(function(event)
    geo.game.close()
end)

geo.game.ping(info)

Makes this game visible on the adventures selection page. Players searching for adventures will see your game and can join it.

info is a table and can contains these values:

location Optional. A table containing lat and lon. Your game will be displayed near this coordinate on the world map. If you don't give a location, no marker will be placed on the map.
message Required. A short message (max 40 characters) to display. Use this for example to inform other players on why they should join this game.
expire Optional. How long should this ping be active in seconds. Maximum and default value is 1800 (30 minutes), minimum value is 30 seconds.

Only one ping can be active for each game. If you ping again, the old ping will be replaced. If you are not prepared to handle any number of players, you should remove the ping using geo.game.unping once enough players have joined.

-- ping example
geo.event.join(function(event)
    geo.game.ping{
        message = "Testing the ping";
        expire = 30;
    }
    geo.ui.append(event.player_id, geo.widget.text[=[
        This game will be visible on the
        [[https://geolua.com/adventures|adventure page]] 
        for 30 seconds
    ]=])
end)

geo.game.unping()

Removes the current ping.

Miscellaneous functions

geo.sleep(delay, function () ... end)

Schedules the provided function for execution after sleeping for delay seconds.

-- sleep example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "Waiting for 5 seconds..."
    ))
    geo.sleep(5, function()
        geo.ui.append(event.player_id, geo.widget.text(
            "... here we go"
        ))
    end)
end)

geo.http.get(options)

Executes an HTTP GET request. This can be used to fetch external data into your game. Or to submit scores to your own server.

options is a table with the following keys:

url required A string containing the url
data optional, default empty A table containing additional url parameters
data_type optional, default "json" A string indicating the expected response type. Valid values: "json" or "raw"
onCompleted optional, function(event) Function called on completion/failure

The request is scheduled and executed. On success/failure, the callback onCompleted is given a table containing the following values:

status Status/Error code for the request
data Further information about the request

Depending on status, data contains this information:

statusdata
200 data for a successful response. If data_type is "json", it will contain the parsed json data as a table. Otherwise it will contain the raw response
Numerical status codes If the request failed, status will contain the HTTP error code, while data will contain further information about the error
"json" Parsing the json response failed. data contains further information. This can only happen if data_type is "json"
"http" HTTP Protocol error, data contains further information
"timeout" The remote server failed to deliver a response within a reasonable time
"url" Invalid url. data contains further information
"error" Unspecified error. data contains further information

While executing the request, the following HTTP headers are sent to the remote server:

HTTP header keyHTTP header value
X-game-id contains the game_id of the game that sent this event. This can be used to send events back into the game.
X-timeout time limit in seconds. The remote server can use this value to determine the deadline for this request. If your server takes more than the specified seconds to respond, the request will fail with a timeout

This example fetches the json data from https://geolua.com/doc/example.json

-- http get request example
geo.event.join(function(event)
    local player_id = event.player_id

    geo.http.get{
        url = "https://geolua.com/doc/example.json";
        onCompleted = function(event)
            geo.ui.append(player_id, geo.widget.text(
                table.show(event)
            ))
        end
    }
end)

The request might be cached by geolua. Minimum cache time is set to 30 seconds. You might use a cache-busting random value within data if this is a problem for your usecase. Or you can use geo.http.post which will use a HTTP POST request which will not be cached.

geo.http.post(options)

Similar to geo.http.get but will do an HTTP POST request.

POST requests will not be cached by geolua.

geo.time()

Returns the current time in epoch seconds.

-- geo.time example
geo.event.join(function(event)
    -- Note, that reloading doesn't change the time, since
    -- the widget is created at the moment, the player joins.
    geo.ui.append(event.player_id, geo.widget.text(
        "You joined at " .. geo.time()
    ))
end)

geo.distance(location1, location2)

Calculates the distance between the 2 given locations in meters. Each location is a table containing lat and lon.

HTTP API Documentation

Geolua provides an HTTP api you can use to control running games or upload/download adventure code. All request must include the api_key parameter. Your API Key is available on your API settings page.

All API endpoints return JSON data on success. On error, the response status code will tell you about the reason of the failure:

HTTP response status
403 (Forbidden) api_key missing or invalid
413 (Request Entity To Large) To much data in your request. Maximum size is 8kb
400 (Bad Request) Invalid content within your request. For example invalid utf8-encoded content

/api/v1/ping

Test API availability.

$ curl https://geolua.com/api/v1/ping?api_key=<api key>
{"pong":"pong"}

/api/v1/game/<game_id>/event

Sends an event into one of your games.

Must be a PUT request containing utf8 encoded JSON data. This will trigger a geo.event.external event in your game. This example will wait for an external event:

-- external data example
geo.event.join(function(event)
    local player_id = event.player_id
    geo.ui.append(player_id, geo.widget.text(
        "game_id = " .. GAME_ID
    ))

    geo.event.external(function(event)
        geo.ui.append(player_id, geo.widget.text(
            event.example
        ))
    end)
end)

Use the displayed game_id to send a value into the game:

$ cat data
{"example": "external value"}

$ curl -Tdata https://geolua.com/api/v1/game/<game_id>/event?api_key=<api key>
{"status":"queued"}

Creole wiki examples

All markup is formatted in creole wiki syntax. Here is an example.

-- creole syntax example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text[=[
        = About this site

        {{https://dividuum.de/res/fw.jpg|Itse me!}}

        That's me. See also [[https://dividuum.de|my homepage]].
    ]=])
end)

Note that images are always resized to 300x200 and then scaled to the full width of the device. This limits the download size for images on mobile devices.

External links are rewritten to use a redirector. This avoids leaking the game id to external sites.

Macros

The creole markup supports macros. You can use them, for example, to display badges in your game.

<<badge(badge_id)>>

Displays a badge. Clicking it will bring the player to the badge page.

-- creole badge example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text[[
        = Badge Number 1

        <<badge(1)>>
    ]])
end)

<<link(link_id) | text>>

Creates a clickable link. Clicking it will create a link event. You can handle them using geo.event.link.

link_id can be a number or string. Valid characters are a-z, 0-9 and the dot.

-- link example
geo.event.join(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "This is <<link(test)|a link>>!"
    ))
end)

geo.event.link(function(event)
    geo.ui.append(event.player_id, geo.widget.text(
        "Link clicked: " .. event.link_id
    ))
end)

<<tan(tanlist_id)>>

Displays a one-time number also known as a TAN (Transaction authentication number). This number can be used to verify that a player has indeed played a game upto a certain point and is actually in the game and not showing you some screenshot of an earlier game. This can be used for example to give out free trinks to players in your bar if they played through your adventure. To do so, take these simple steps:

  • Goto your adventure edit page and click on the "Tan Lists" tab.
  • Create a new Tan List and give it a name, like "Mos Eisley Cantina".
  • Use the provided sample code to include the TAN generator macro in your text widget.
  • Use the "Current list" link to print out a piece of paper you can use to verify a displayed TAN.

Here is an example of a TAN generated by tanlist macro for your Mos Eisley adventure:

To validate this TAN, follow these steps:

  • Look for row #36 on your printed piece of paper. If it is crossed out, the code was used earlier: No drink for the player.
  • Make sure that the piece of paper confirms the code 5476. If it does not, the TAN is invalid: No drink for the player.
  • Give the player the free trink and cross out row #36.

Tan Lists are not bound to one adventure. You can use the same tan list in any of your adventures.

The Lua environment

Geolua uses Lua 5.1.5. You code is running in a sandboxed environment.

All adventures currently have a memory limit of 2MB. Execution of code is terminated, if you use too much processing power.

If you write normal code, these limitations should be no problem.

The runtime environment contains most of the normal Lua functions. The following functions are available.

Namespace Function Modifications for Geolua
assert
error
getmetatable
ipairs
next
pairs
pcall
print Modified to write to the debug output. Currently there is no way to read this. Stay tuned
rawequal
rawget
rawset
select
setmetatable
tonumber
tostring
type
unpack
xpcall
GAME_ID The game_id of the current game.
coroutine create
coroutine resume
coroutine running
coroutine status
coroutine wrap
coroutine yield
debug traceback level is limited from 0 to 256
math abs
math acos
math asin
math atan
math atan2
math ceil
math cos
math cosh
math deg
math exp
math floor
math fmod
math frexp
math ldexp
math log
math log10
math max
math min
math modf
math pi
math pow
math rad
math random modified to deliver repeatable values. Takes no arguments and returns values in the range between [0, 1)
math randomseed modified to deliver repeatable values. Same arguments as the Lua version
math sin
math sinh
math sqrt
math tan
math tanh
string byte modified to handle unicode
string char modified to handle unicode
string find modified to handle unicode
string format modified to handle unicode. No support for %q. String %s and character %c formats do not support the length specification.
string gmatch modified to handle unicode
string gsub modified to handle unicode
string len modified to handle unicode. Always use this function to get the length of a string. If you use #string_value you'll get the byte length which can be different if the string contains characters outside the ascii range (like umlauts).
string lower modified to handle unicode
string match modified to handle unicode
string rep n limited to 8192
string reverse modified to handle unicode
string sub modified to handle unicode
string upper modified to handle unicode
table concat
table insert
table maxn
table remove
table show Added. table pretty printer. Usage: table.show(value)
table sort
About Geolua | Privacy Policy | Documentation | Follow @geolua on Twitter | Share on