I originally came up with this idea for the purpose of understanding the interaction between a rendering engine, game logic, and a scripting language along with learning how to use the C-Lua interface, learning the Unix sockets API, and experiencing firsthand manual memory management on a decently-sized project. Additionally, this project fills a gap I perceive in the open source gaming community; that is, a way to play tabletop games with friends on basically any *NIX system with a CPU. Indeed, this project runs only in the terminal; as of yet there are no graphical capabilities. It should be noted that the project has only been compiled on Linux, once the game is complete I will add in the necessary code for building on MacOS, OpenBSD, and FreeBSD. Earlier, when I ran OpenBSD on my laptop the game was maintaining compatibility with OpenBSD but I have since quit using that computer and OpenBSD compatibility has not been tested in a long time - it will almost certainly not build on OpenBSD.
Currently, the only target is Linux. After the game is finished, it will target Mac OSX, OpenBSD, and FreeBSD. Other targets may be added after that. Windows support is not planned, however it *may* be possible to run this in WSL, I have no idea how well WSL works with the Unix sockets API.
### Dependencies
Currently, the only dependencies are [termbox2](https://github.com/termbox/termbox2) and Lua 5.3. An older version of termbox2 is included in the repository for convenience. I used termbox2 instead of ncurses because ncurses leaks memory, and one of my goals for this project is to not leak memory at all. Also, termbox2 is much, much simpler than ncurses in my opinion. I plan on upgrading to Lua 5.4 eventually, when I began this project Lua 5.4 was already out but a tutorial I was following was using Lua 5.3.
### License
I have not chosen a license for this project yet. Will definitely have one by the time version 1.0 comes out.
-`-ll LOG_LEVEL`: Sets the log level for output: 0=error, 1=warning, 2=information, 3=debug
-`-lf LOG_FILE`: Sets the output file for the log, `stderr` by default
#### Provided Lua scripts
The lua scripts in the repository are provided as examples and sanity checks
- demo2.lua - I don't actually remember what this was for... some testing in the early stages of development
- incorrect_oct_init.lua - A script that has a runtime error in the `oct_init` function, used for testing lua errors and memory leaks
- incorrect_oct_loop.lua - A script that has a runtime error in the `oct_loop` function, used for testing lua errors and memory leaks
- incorrect_script.lua - A script that will fail to parse successfully, used for testing lua errors and memory leaks
- oct_utils.lua - Defines useful constants and functions specific to Open Card Table
- pong.lua - Implements 2 player pong, with both players using the same keyboard on the same computer. Does not work very well because of n-key rollover
- termbox_defs.lua - Contains termbox2-specific stuff needed in Lua, mainly key constants defined by termbox2
- test_client/test_server.lua - Scripts used for testing networking. Server should listen on port `1234` and client can listen on any port; both should be on same computer. Client sends message to server every second or so and the server echoes what it received
### Developing Lua scripts
The best way to learn scripting for Open Card Table is to look at the included Lua scripts, especially pong.lua as that's the only complete project as of right now. Unfortunately, pong.lua does not demonstrate networking at all but I am currently working on some projects to do this.
#### Includes
It is generally a good idea to include `oct_utils.lua` and `termbox_defs.lua` at the top of every script. This can be accomplished with the following lines
```
require("oct_utils");
require("termbox_defs");
```
Of course, this requires the script under development be in the same directory as these files.
#### Required functions
Every Open Card Table lua script must have 2 functions defined: `oct_init` and `oct_loop`. `oct_init` is called once at the beginning of program execution and should be used for initializing variables, initializing the environment, defining sprites, etc. `oct_loop` is called once per iteration of the main loop in the C part of the code, and is where all game logic should reside. `open_card_table` will exit with an error if either one of these is missing.
##### `oct_init`
`oct_init` needs to return two variables in order: a variable defining whether or not we need networking, and a variable defining whether or not we need termbox. `oct_utils.lua` defines some handy, self-explanatory constants for this: `OCT_NOT_NEEDS_NETWORKING`, `OCT_NEEDS_NETWORKING`, `OCT_NOT_NEEDS_TERMBOX`, and `OCT_NEEDS_TERMBOX`. Singleplayer games obviously do not need networking, and generally the only time you will not want termbox is when you are running a server program as opposed to a client.
##### `oct_loop`
This function is where the game logic resides. It generally consists of reading in keyboard input and adjusting sprites accordingly. It does not return anything.
#### Sprites
Sprites are the basic unit or what's drawn on screen. They should be declared in global scope, e.g. like
```
paddle_left = oct_tb_sprite_new();
```
Sprites are managed on the C side of things, and `oct_tb_sprite_new` allocates memory for each sprite; all sprite references are stored in a dynamically growing array so the limit of how many sprites you may have is determined only by your hardware. Sprites can only be created, not deleted. Every sprite has 3 required attributes: `x`, `y`, and `shape`; other attributes may be defined for game logic but these three are reserved. `x` and `y` are just the location of the top-left corner of the sprite on screen. `shape` is the representation of the sprite. This can be manually specified as a string, or sprites can be defined in a file and read in via the function `load_termbox_sprite`. Here is an example:
**Note that at this time, networking is incomplete and only basic functionality is working.** Networking consists of the `oct_recv` and `oct_send` functions. Note that in order to use networking, `oct_init()` had to return `OCT_NEEDS_NETWORKING` as the first argument, or else nothing will happen. These usually are used in the `oct_loop` function. I still need to add in support for broadcasting, this will come in a while. **Important**: Message sending is *synchronous*, i.e. under the hood a message and destination are placed in buffers, and at the next invocation of the main loop the message is sent to the destination. I believe that tabletop games are simple enough that this is sufficient to express games even with real-time components as the main loop is executed at a high frequency. If this turns out not to be the case, I will look into adding asynchronous message sending.
Receiving a message: `msg,addr,port = oct_recv();`
Sending a message: `oct_send("MESSAGE", "IP_ADDRESS", "PORT")`;
#### Logging
I have written a convenient logging interface for Lua, it uses the logging macros I implemented in C. It can be invoked in Lua via the `OCT_LOG_ERROR`, `OCT_LOG_INFO`, `OCT_LOG_WARNING`, and `OCT_LOG_DEBUG` functions, passing the desired string as an argument. Remember that termbox2 takes over the console, so it is a good idea to use the `-lf` switch to specify an output file if you are running a termbox-enabled program. Also remember that the log level needs to be set to a sufficient strength via `-ll` in order to see the messages.