Saturday, 2 December 2017

Debugging your TTS mods with console++ : Odds & Ends

Let's wrap up this series (which started here) with some miscellaneous features of console++.

The console module, which console++ is built on top of, includes a handy command: the '=' command gives you access to Lua's dynamic.eval function; in other words it works as a simple expression calculator.  Type '= ' then any mathematical expression and it will display the resulting value.  You can refer to any of the variables in your program when you are a table admin, but all players can use the '=' command for simple maths functions.  Handy if your game is anything like Power Grid!



There are also a couple of commands for interacting with the chat window output:

'echo' will print its parameter into the chat output (just for you); useful to include some output in a script you wish to 'exec' with the -q or -s parameters.

'cls' will clear the chat window.


Finally, console++ gives all players a 'shout' command: this will broadcast its parameter as a message to all players, popping up at the top of their screen as well as in the chat log.  It will appear in your player colour.


I hope if you are using console++ that this series of posts has been helpful, or if you're not that it might have tempted you into trying it.  Any feedback is greatly appreciated, especially bug reports.

Monday, 27 November 2017

Debugging your TTS mods with console++ : Watch List

This will be the final installment of the tutorial series about console++ which began here.

In this enthralling episode we'll look at the 'watch' command; a very useful debugging tool.  'watch' allows you to monitor a variable or object in your game (or even a function: more on that later), displaying a message in the chat window whenever it changes.

In order to use this feature of console++ you must call 'console.update()' in an 'onUpdate' event.  The example module already includes this command:



If you've followed this series so far then after firing up the example module in Tabletop Simulator you'll already be in command mode (thanks to console.autoexec);  type 'slow' to enable the card display.  Now if we type 'ls' a few times, waiting a short delay between each, we'll see the next_check variable increasing:


This is a pretty awful way to check its progress however; lets use 'watch' instead:
Type 'watch /next_check'.  Every two seconds the next_check variable will change and we'll see it do so.  This is because we currently have check_delay set to 2 seconds; type 'fast' to change check_delay to 0.2, and you'll see the variable change every 0.2 seconds instead.  Switch it back to 2 seconds with 'slow'.  You wouldn't want this running after the point where you get the info you want; it's filling the chat window.  To clear the watch list type 'watch -c'.  The '-c' parameter clears the watch list if it's used on its own.  You may instead supply it with a variable name to only clear that variable from the watch list: here we could have typed 'watch -c /next_check'.


We can also watch objects: type 'watch /dice/d6' then roll the dice.


You can see it will track the objects rotation and position.  This is fine for an object which you have used in your program, but not all objects will be stored in a variable.  Happily we can also track objects via their GUID; right click the chess rook and in the scripting menu click on its GUID to copy it to the clipboard.  Now type 'watch -g ' and hit ctrl-v  to paste in the rook's GUID.  Pick up the rook and move it around to see it being watched.

You can see what variables you are watching by typing 'watch' without any parameters:


You can also disable the watch list temporarily by pausing it with the '-p' parameter; this lets you stop the watch list outputting to the chat window without clearing it (and losing everything you are tracking).  Try it now: type 'watch -p' and move the rook around; no updates! Do it again to unpause (use up-arrow to fetch the previous command).

Grab the rook and move it around; the watch list really spams the chat window, since every tick the position changes.  In a lot of cases this will be too much information, so the watch command allows you to throttle its output for each given item.  Use the up-arrow key to fetch the command that added the rook to the watch list ('watch -g b27933', unless your GUID is different) and then add to the end of it '-t2'.  This will add a 2 second throttle to the watch.  Now drag the rook around and notice the difference.  Notice also that this command has replaced the previous version of the item in the watch list, overwriting the old, unthrottled item; each variable or object may generally only appear once in the watch list.



Now clear the watch list  with 'watch -c'.

As well as tracking variables and objects, we can also watch their member functions.  Type 'watch /dice/d6/getValue', and then roll the dice:


Clear the list, and then try to do the same for the dice's position with 'watch /dice/d6/getPosition'.  Argh, what's happening here?


What is happening is that every tick the watch list is calling the getPosition function of the dice, and every tick it is getting a new table.  Even though the table's member values are identical, the table itself is changing, so the watch list is outputting it each frame. This is definitely not useful!  We can mitigate this by using the '-/' parameter; this parameter specifies a member of the returned table to watch, instead of the table itself.  Type 'watch /dice/d6/getPosition -/x'; phew, the spam has stopped.   Move the dice around to see the 'x' value being tracked, then clear the watch list.



As well as variables and objects, we can also watch functions. To test this out we'll need one so let's see what functions we have available with 'ls -f'.  Ah: 'dice_total' is nice and simple as it has no parameters; lets watch it with 'watch /dice_total'.  Roll the dice to see the function changing.

We can also use functions with parameters; let's do that, but lets make things a little more complex, more like a situation you might find yourself in while actually debugging your mod.

What does the near function do?  Here is its source:


Not too complicated a function; it checks if two objects are near each other on the x and z axis.  We're going to use it to check when the rook is near the d4.

Our first problem is that we do not have a variable for the rook in our program; remember when we were watching it before we were doing so via its GUID.  That's not an option here: the near function does not take a GUID as a parameter, it takes two object variables.  Therefor the first thing we have to do is make one up for the rook.  We'll do so using the 'call' command: type 'call getObjectFromGUID 'b27933'', and then 'add /rook ~'.  (the '~' special variable holds the last result).


Now we have a variable holding our rook we can use it as a parameter to the near function; type 'watch /near /dice/d4 /rook', and then pick up the rook and move it around, over the d4 and away from it.



The near function is fairly simplistic, but I hope you can see how we can write debug functions in our code, and then hook them up to problematic objects during a play session, using them to work out what is going wrong with the mod.  More persistently, if you know you want to track certain variables or objects you can add 'watch' commands to your console.autoexec, giving you feedback on events which you want to know happen (but which don't necessarily throw error messages) every time you play your mod.

Thursday, 23 November 2017

Debugging your TTS mods with console++ : Batching and Aliasing

This continues the series which started here.

The first thing we'll do in this session (after loading the example mod in TTS and loading it into Atom) is use console++'s autoexec feature: this allows you to set a series of commands which will be executed when your mod is loaded.  In order for this feature to work you need to call console.load() in your onLoad function; the example mod already does this as the last line:


With that line present all we need to do to make console++ execute a script on startup is set it in the console.autoexec variable.  Add this block of code above the onLoad function:



console.autoexec = [[
    cmd
    cd /console
]]



Hit Save And Play; now when we hit enter in TTS and type a command we don't need to prefix it with '>' and don't need to remember to switch to command mode - the 'cmd' command has already done that for us.  Type 'ls' to test this out:


You can see we are already in command mode (the command worked without a '>'), and are indeed inside the console table.

Now, the autoexec feature isn't groundbreaking: it's not doing anything we couldn't simply do with Lua commands.  Instead of making an autoexec script we could have written this code:


This accomplishes the same thing: sets you to command mode and changes your current location.  It was simpler to just type the console++ commands though, no?  Setting up the autoexec has an additional benefit; we can invoke it manually using the 'exec' command.  Type 'cd /' to return to the root table, then 'exec console/autoexec', then 'ls': you'll see we are back in the console table.  

Thus you can set up batches of instruction in strings in your program, and then run them with the 'exec' command; console.autoexec isn't special in this (it's only different because it is run automatically on loading) - any string variable can be executed.  

As with any command which takes a variable as an argument, we can make it work on a literal string too: thus we can use 'exec' to run a sequence of commands entered as one line.  To do this we need to know the use of these three punctuation marks in console++:
  • A string parameter prefixed with a back-tick (`) is treated as a literal (instead of a path)
  • Double- and single-quotes (" and ') can be used to surround a parameter containing spaces in order to stop the spaces delimiting the parameter.
  • The semi-colon (;) can be used to separate commands in a script instead of a new line.
So to duplicate the above autoexec script on a single line we type this:

exec "`cmd; cd /console"

The command line sees the surrounding double-quotes and treats everything inside them as one parameter. It then sees that that parameter starts with a back-tick and so knows it is a string literal, and then the 'exec' command splits that string up by its semi-colons.



We'll come back to 'exec' later; right now we're going to look at the 'alias' command.  'alias' allows you to create your own commands from any existing commands.  At its simplest this simply lets you rename commands to suit yourself: console++'s 'ls' and 'dir' commands are actually the same command: one is simply an alias of the other.  If you wanted to add your own name you can use 'alias' to do so.  Let's say you are especially verbose, and you want to type 'list' instead of 'ls': simply enter 'alias list ls'.

This is a pretty silly example; lets make it actually useful.  By default the 'ls' command only displays variables and tables - it does not display game objects or functions. To display everything in your current location you use the '-a' option: 'ls -a'.  Let's set up an alias for that: at the command prompt type 'cd /' to go to the root then 'alias list ls -a', and then 'list':


I typed 'ls' afterwards to compare the results: you can see our 'list' command displayed the functions as well as the rest, while 'ls' did not.  If you like this behaviour you can include this alias command in your autoexec to make it available whenever you run your mod.  You could even take it a step further, and alias the 'ls' command over the top of itself with 'alias ls ls -a', which would replace the default ls behaviour, allowing you to just type 'ls' to see everything (though a better alias for this would be 'alias ls ls -fov', as this will allow you to toggle them off again; '-a' overrides any other parameters)

You can check what a command does with the 'help' or '?' command (as with ls/dir, one of these is just an alias of the other). Type '? list' and you'll see:




Now we're going to combine these two commands, 'exec' and 'alias', to create some commands for controlling our mod.  Enter these commands:

alias slow exec -q "`set /update_cards true; set /check_delay 2.0"
alias fast exec -q "`set /update_cards true; set /check_delay 0.2"
alias off set /update_cards false

After entering these aliases we will have three new commands: 'slow', 'fast' and 'off'.  Typing 'slow' or 'fast' will enable the card display updating to match the dice total; one being more responsive than the other.  Typing 'off' will disable the card updating.  Try them out!

The '-q' (quiet) option tells the 'exec' command not to display the result of each command it is executing; just to display the final result.  If we used the '-s' (silent) option instead then the final result would also be suppressed.  Alternatively we could have used the '-v' (verbose) option to make 'exec' display all the commands it is executing as well as their results.  If you want to you can add your own custom output by using the 'echo' command, which prints its arguments in the chat window.

Note we don't need a back-tick before true and false - these are special cases.  If we were assigning a string we would need the back-tick, or it would try to look up the string as if it were a path.

These are nice, but no good if you have to type them in every time you open your mod in TTS.  That's OK though; we can add them to our autoexec!  Remove the 'cd /console' command and add them in like this:


Now whenever you load your mod you will start in command mode, and have these commands at your disposal.

Monday, 18 September 2017

Debugging your TTS mods with console++ : Functions

Don't just jump in here: start at the beginning!  Once you've loaded the module in Tabletop Simulator remember to switch to the Game tab and turn on command mode with '>'.

In the previous posts we've seen how we can display variables with 'ls' (or 'ls -v') and objects with 'ls -o'; the final option available is to display functions with 'ls -f'.


Here you can see the functions defined in the example mod.  We can do more than just display them, however; we can also call them.  Type 'call dice_total', then roll the dice and call it again.


What if we want to store the value a function outputs?  console++ defines a special path for the last returned value: the '~' character.  You can see this if you display it with 'ls ~', and all other commands can use it like they would any other path; this includes 'add' and 'set'.  Add a variable called total using the '~' path and check it's set to the right value:


The 'add' and 'set' commands can also be used to store a result directly, by calling them with no value argument before the 'call' command.  If you do so the the next 'call' command will store its result where you have specified.  Roll the dice again and then try it by typing 'set total' and then 'call dice_total':



Note that console++ also has a short-cut command to display the last result: simply type the result character on its own to display its contents: '~'


We can pass parameters to functions just as you would expect, by adding them to the 'call' command.  Let's use the is_face_up function on one of the cards.  Remembers, the card objects are stores in the cards table; you can see them by typing 'ls -o cards'.
Type 'call is_face_up cards/2' to call is_face_up on the 2 card, then flip the card and issue the command again (use up-arrow to get the last command typed instead of typing it again).



Note how when we 'ls -f' we see only the functions defined in the mod; we don't see all the built-in Lua functions.  That's not because they are not present; it's because console++ hides them under the 'sys' label.  If you do an 'ls' you will see two things at the bottom of the tables that look like tables, except they are in a salmon colour instead of yellow; these are labels used to hide globals to keep things tidy.  You can see what they are hiding by calling 'ls' on them;
try it now: 'ls sys -f'


At the point in the Lua code where you #include console++ it automatically scans all the globals and hides them under the `sys` label, thus keeping your mod easy to interact with in the console.  All these things still exist in the root table, though they may also be accessed via the label they are hidden under. i.e. 'ls /math' and 'ls /sys/math' are the same thing:



You can also create your own labels to hide things under by calling console.hide_globals in the mod's Lua code.  Type 'ls' and you'll see that as well as the 'sys' label there's also a 'guids' label; if you look at the end of the GUIDS block in the Lua code you'll see the command that created it; whenever you call console.hide_globals it will scan for global variables/functions etc and hide them under the label you specify (provided they haven't already been hidden under another label).



To come back to the point: all the built-in functions are available should you wish to call them.  For example, we can get a color using stringColorToRGB then display the RGB values:
'call stringColorToRGB Blue' then '~':



We can then store it in a variable and use it to highlight an object:
'add blue ~' then 'call /decks/b/highlightOn blue 100'


Thursday, 7 September 2017

Debugging your TTS mods with console++ : Manipulating Variables

If you haven't read it then you should check out the first installment of this series, which goes over how to get set up.

Last time we saw how to look at our program's structure.  This time we'll see how we can manipulate it in real-time by changing and adding variables.

First things first though; last time we were entering commands by typing the command symbol ('>') and then the command.  Let's shortcut past that by entering command mode.  You can type '>cmd' at the prompt to enter command mode, and when in command mode you can type 'exit' to leave it.  When you are in command mode the console will interpret all your inputs as commands, so you no longer need to prefix everything with '>'.
But we're not going to type those!  Instead, an even easier way to enter and exit command mode is to just type the command character on it's own; if you hit '>' then enter it will toggle you in and out of command mode.  Try it now:


After I entered command mode I typed 'cd cards' (just like last time), and you can see that while in command mode any commands you perform will end with a prompt which includes our current path.

OK, so now we can simply type commands without a prefix, let's try a new one out: the 'tgl' (or 'toggle') command flips a boolean.  Let's go back to the root table with 'cd /' then remind ourselves what is there with 'ls'.


At the bottom you can see the four global variables in the mod, and the bottom one is a boolean, currently set to false.  If we look at the code we can see what it does:


update_cards acts as a sentinel on the onUpdate event. Let's turn it on so the mod actually does what it's supposed to do.  Type: 'tgl update_cards' and see what happens.


Well, that isn't good.  The mod clearly doesn't work right, and worse, because the bug is in the onUpdate event it is spamming errors every time it engages.  Investigating will be impossible while the console is being filled with error messages, so turn it off again: in the console hit up-arrow to get the previous command and enter to execute it; this will toggle the update_cards boolean back off, disabling the faulty onUpdate event code.  Note this technique; it's always a good idea to wrap timer-event driven code inside a boolean like this so that if things go wrong you can easily disable the code without aborting from the faulty session.

So what's the problem?  The error is on line 1542, so go to the global script in Atom and hit ctrl-g and enter 1542 to go to the offending line number.  The Atom plugin will take into account any #includes in the code, and will take you to the correct line, which in this case is the one that reads 'cards[count].flip()'.

The error is suggesting that we're trying to index a nil value, so either cards is nil, or the individual location of cards[count] is nil.  Let's have a look: type 'ls cards' at the console.
Nothing? Oh, the cards are game objects; try 'ls -o cards'.


There they are; cards is clearly not nil.  Let's look again at the code - what is the cards table supposed to do?  You can see in the onUpdate event it is looping over the numbers 2 to 10 - the card values on the table.  The table has been set up such that to access the 2 of Spades we use cards[2], to get the 3 of Diamonds we use cards[3], etc.  In the onUpdate event we are looping over each of the cards from 2 to 10 and possibly flipping them.  However, look at the 'ls' output: the cards are not indexed from 2 to 10, they are indexed from 1 to 9.  Looks like the table has been set up wrong! There is no cards[10] so when it tries to access it on the last iteration of the loop it doesn't find anything, thus the error message.
Before we jump back into the code to try to track this down, let's confirm that that is the problem; we'll add a card at that index and see if the code works.  To add a variable we use the 'add' command.  We'll set it to the card that's not being looped over: cards[1].
Type 'add cards/10 cards/1', and then 'ls cards' again.


You can see it has a card at index 10, and it has the same GUID as the one in index 1.  Turn on the code by toggling the boolean: 'tgl update_cards'. No errors - nice!  The update code is working, but our table is still set up wrong; all the indexes are off by one (as you can tell by the card 3 being face up, but the total of the dice only being 2).  Select the dice and hit 'r' to roll them.  The cards will update, but always to the wrong value.

We now know what to fix in the code though, and on investigation you can see the part that sets up the table:



This looks pretty straight-forward; the table is created with a null value in index 1, so that when it loops over the GUIDs of the cards it will add them to slot 2, 3, 4... etc. all the way up to 10.  However, if you are more familiar with Lua than the programmer of this code you will know that nil does not behave just like null does in other languages; in Lua when you assign nil you are actually deleting that entry, so this code is no different from typing 'cards = {}'... it makes an empty table.  The fix should be simple: change the line to 'cards = {'dummy'}' and hit Save And Play.  Now we can go back into command mode with '>' and type 'ls -o cards' to see that they are correctly indexed, 2-10.  (An exercise to the reader: where is the 'dummy' card?)

Toggle the update_cards variable to turn on the event code.  No errors, and the card being displayed matches the total of the dice!  Select the dice and hit 'r' to roll them, and you can watch the cards flip to match.

It feels a bit clunky though; the cards are updating very slowly.  Type 'ls' again to see what variables we have to play with: sure enough, there's a 'check_delay' number set to 1.5 seconds.  Let's set it to a shorter delay using the 'set' command.  Type 'set check_delay 0.3' then select the dice and roll them.  Much more responsive!  If you were writing this code you could easily play with varying values for check_delay until you found the one you liked the best, and then amend the code afterwards to match.

That covers the basics of manipulating variables in the run-time environment.  You can see how we can use it to debug our code in a controlled manner, and also to help in refining control variables. We've now looked at simple variables and objects; next time we'll look at the third of the triad: functions.

Monday, 4 September 2017

Debugging your TTS mods with console++ : Introduction


The typical cycle for debugging a game in Tabletop Simulator is to play it, wait for an error message to emerge, use that error message to find the problem, write a potential fix, then start again.  This is less than ideal in a lot of ways: the error message can be a good clue for what the problem is, but sometimes it can be extremely vague (sometimes it doesn't even have a line number!).  Potentially more time-consuming is that when your game is getting fairly well developed the bugs become hidden further in; it may take a lot of setup to get to the specific circumstances that trigger the bug.  Typically this means that once you think you've written a fix you then need to play the game out exactly the same was as before, coordinating any playtesters to do what they did before, until the bug manifests again... or does not, and is potentially fixed - but how do you know for sure?

console++ gives you live access to your mod's structure, treating it as if it were a file system.  It lets you examine your variables, functions and objects, and also lets you update them, all without leaving the game.

To follow along to this tutorial you need to get console++ from the link above and subscribe to the example mod on the Steam Workshop.  Once you've loaded it into Tabletop Simulator, save it to your own hard disk so you can save your edits.

Source code is visible here.
This post will cover navigating and displaying your program using the two basic commands in console++: 'ls' and 'cd'.  As a quick aside, I'll be using the unix-like command names during this tutorial ('ls'), but console++ includes DOS-like alternatives, so if you prefer DOS you can use those instead.  In this case, anywhere I say 'ls' you can type 'dir' instead.

What does 'ls' do?  Well, we could ask for help on it, but lets do the impetuous thing and just type it into the console; make sure you are on the Game tab of the console, not the Global tab it defaults to (also it would be a good idea to hit the cog and disable autohide if you have it turned on).

So, hit enter, type 'ls' and hit enter again.  Congrats, you just said 'ls'; the console is just the chat window after all, and you didn't tell it you wanted to use a command!  To use a command you type the prefix '>' before it, so lets try again, typing '>ls'.  Much more interesting, and if you looked at the source file above it should look a little familiar:


You can see the variables we defined in the source are there; the 'ls' command lists all the tables and variables in your current location.  Right now we're in the root of our program ('/') which is the global level.  You can see at the top of the listing some labels in yellow: these are tables.  Let's see what's in them; type '>ls decks'. Nothing there?  That's because by default the 'ls' command only displays tables and basic variables (numbers, strings and booleans); to get it to show game objects we need to use the '-o' option.  Try '>ls decks -o' and you'll see:


There are our deck objects: table entries a, b, and c.  We can see their GUIDs listed next to them; right click a deck on the table and check the Scripting submenu to see it matches up.

We can refer to each of these decks by their path: if you type '>ls decks/a' you'll get the listing for only deck a.  Now, if we were debugging something to do with these decks, and were going to type their names a lot it would be tedious to have to keep specifying their location.  Instead we can change our current location to suit our needs, with the 'cd' command.  Type '>cd decks' and then '>ls -o'.  As you can see we have changed the table we are in; we can now refer to the decks simply by their letters: '>ls a'.  Note that when we specify an exact path we do not need the '-o' option.

Paths behave just as you would expect for a file-system; you can specify relative paths from your current location or absolute paths from the root table (by starting your path with a '/') You can refer to a parent table by using '..' and your current table with '.'

Let's go back to the root table: type either '>cd ..' to go up one level (we are only one level below the root) or '>cd /'.  This example mod is clearly very small and simple, but you can imagine in a big game you will have lots of variables and tables; simply typing '>ls' and scrolling up and down looking for things could be cumbersome.  In that situation it would be good to use a wildcard to trim down what you're looking at: try '>ls c*'


That's the basics of navigating and examining your program from within TTS with console++, except to return to what was said at the top of the post about asking for help: you can type '>help' or more simply '>?' to get help on any command, and the commands with more complicated options will also support a '-?' option to provide more detailed information.  If you type '>? ls' you'll see it has a few options we haven't tried yet.  To wrap up lets try '>ls -ar' ...



Next time we'll look at actually interacting with your program by modifying variables in real time.

Thursday, 24 August 2017

Atom Tabletop Simulator package, experimental #include system

A note about the experimental additions, specifically the #include:

You can #include other files into your source; when you hit Save And Play the plugin will go and grab those files and inline them in your code at the point you have placed them. The files are looked for inside the folder specified, or you can specify a complete path to their location.

For example, on the game I'm working my Global.-1.ttslua is now simply:

-- workshop id = 945458382

#include Shard/shard
When I hit Save And Play it will look for the shard file in the folder location specified in the settings: by default this is your USER_FOLDER/Documents/Tabletop Simulator. So in my case it will get the file:

C:\Users\onelivesleft\Documents\Tabletop Simulator\Shard\shard.ttslua
- Shard\shard.ttslua being specified by the #include 

My shard.ttslua file looks like this:
Code:
#include constants
#include guids
#include globals
#include levels
#include !/utils

#include admin
#include game
#include draft
#include rewards
#include dice
#include expansions

--NO_SAVE_OR_LOAD = true  -- To remove any state for clean upload to workshop
--DEBUG_DISPLAY   = true

game_started = false
loaded = false

function onload(saved_data)
    current_level = nil
    ...
When the plugin fetches it, it will then look through it inlining the includes it has, and so on. This time it won't look in the folder specified in the settings, it will look in the same folder as the file which included them. My file structure looks like this (and is a git repo!):



Almost all the files included by shard.ttslua are in the same folder as it, so I only need their name.

Note the line: #include !/utils - the !/ is an identifier that specifies the folder specified in the package settings.  In this way I can have a utils library sitting in the root of my TTS folders, accessible by all my games.  You may have a vector library you wish to include in all your objects' code; now, this is easy to do.  You may also use the symbol ~/ to start a path from your user home folder (even on Windows), or specify a full path on the #include line if you want to.

In practice how this works is it takes all the text in the files you are including and dumps them into the source at the point they are included.  This means if you have a bunch of functions you want your objects to have access to then you can, but they are in effect being repeated in every object. 

As it stands this means all your modules are sharing the same namespace; a global in one is accessible in all.  I'm OK with this (it's the Lua spirit), but if you want to make them behave like modules you can, you just have to do it explicitly.  

  1. Start the module by declaring itself as a table.
  2. Prefix all it's "globals" and function with that table.

Let's say you have a module to greet the players, saved as greetings.ttslua :

greetings = {}

greetings.default_greeting = "Hello!"

function greetings.greet(message)
    local message = message or greetings.default_greeting
    for p, player in pairs(getSeatedPlayers()) do
        printToColor(message, player, rgb)
    end
end

Then you can use it in your program by:

#include greetings

greetings.greet()
greetings.greet("Hi!")
greetings.default_greeting = "Yo!"
greetings.greet()

When you get the script from TTS the atom plugin will automatically strip out the inlined code and give you what you expect.  Note that the package commands like Go To Function will take into account all your includes, and when you use them will jump to the correct place.  If you are in your Global.-1.ttslua file and type a line number into the Go To Function dialog it is smart enough to know which line you are referring to and take you to it.  i.e. even though the program I listed at the top of this post only has 3 lines when I see it in the editor, when TTS says there's a bug on line 4345 I can hit ctrl-g and type 4345, and it will take me to the correct line (opening up the relevant included file if necessary).

EDIT: As of plugin version 11.0.2 you may optionally enclose your include in `<` and `>`; if you do so then the inserted text will be enclosed within a `do`...`end` block, keeping local variables contained. Example:

#include <Console/Console++>