1
0

feat: initial commit

This commit is contained in:
Rokas Puzonas 2022-05-01 15:29:54 +03:00
commit 22166bf443
9 changed files with 926 additions and 0 deletions

9
config/config.lua Normal file
View File

@ -0,0 +1,9 @@
name = 'tic80'
words = {"TIC"}
configs = {
{
key = 'Lua.runtime.version',
action = 'set',
value = 'LuaJIT',
}
}

246
config/library/drawing.lua Normal file
View File

@ -0,0 +1,246 @@
---@meta
---
---This function draws a filled circle of the desired **radius** and **color** with its center at **x**, **y**. It uses the _Bresenham_ algorithm.
---
---@param x number # the `coordinates` of the circle's center
---@param y number # the `coordinates` of the circle's center
---@param radius number # the radius of the circle in pixels
---@param color number # the index of the desired color in the current `palette`
function circ(x, y, radius, color) end
---
---Draws the circumference of a circle with its center at **x**, **y** using the **radius** and **color** requested.
---It uses the _Bresenham_ algorithm.
---
---@param x number # the `coordinates` of the circle's center
---@param y number # the `coordinates` of the circle's center
---@param radius number # the radius of the circle in pixels
---@param color number # the index of the desired color in the current `palette`
function circb(x, y, radius, color) end
---
---This function draws a filled ellipse of the radiuses **a** **b** and **color** with its center at **x**, **y**. It uses the _Bresenham_ algorithm.
---
---See also:
---
---- `ellib` - draw only the boundary of the ellipse
---
---@param x number # the `coordinates` of the ellipse's center
---@param y number # the `coordinates` of the ellipse's center
---@param a number # the horizontal radius of the ellipse in pixels
---@param b number # the vertical radius of the ellipse in pixels
---@param color number # the index of the desired color in the current `palette`
function elli(x, y, a, b, color) end
---
---This function draws an ellipse border with the radiuses **a** **b** and **color** with its center at **x**, **y**. It uses the _Bresenham_ algorithm.
---
---See also:
---
---- `elli` - draw a filled ellipse
---
---@param x number # the `coordinates` of the ellipse's center
---@param y number # the `coordinates` of the ellipse's center
---@param a number # the horizontal radius of the ellipse in pixels
---@param b number # the vertical radius of the ellipse in pixels
---@param color number # the index of the desired color in the current `palette`
function ellib(x, y, a, b, color) end
---
---This function limits drawing to a clipping region or 'viewport' defined by `x`,`y`, `width`, and `height`.
---Any pixels falling outside of this area will not be drawn.
---
---Calling `clip()` with no parameters will reset the drawing area to the entire screen.
---
---@param x number # `coordinates` of the top left of the clipping region
---@param y number # `coordinates` of the top left of the clipping region
---@param width number # width of the clipping region in pixels
---@param height number # height of the clipping region in pixels
function clip(x, y, width, height) end
---
---This function clears/fills the entire screen using **color**. If no parameter is passed, index 0 of the palette is used.
---
---The function is often called inside TIC(), clearing the screen before each frame, but this is not mandatory. If you're drawing to the entire screen each frame (for example with sprites, the map or primitive shapes) there is no need to clear the screen beforehand.
---
---_Tip:_ You can create some interesting effects by not calling cls(), allowing frames to stack on top of each other - or using it repeatedly to "flash" the screen when some special event occurs.
---
---@param color? number # index (0..15) of a color in the current `palette` (defaults to 0)
function cls(color) end
---
---This function will draw text to the screen using the foreground spritesheet as the font. Sprite #256 is used for ASCII code 0, #257 for code 1 and so on. The character 'A' has the ASCII code 65 so will be drawn using the sprite with sprite #321 (256+65). See the example below or check out the [In-Browser Demo](https://tic80.com/play?cart=20)
---
---* To simply print text to the screen using the `system font`, please see `print`
---* To print to the console, please see `trace`
---
---@param text string # the string to be printed
---@param x number # `coordinates` of print position
---@param y number # `coordinates` of print position
---@param transcolor? number # the `palette` index to use for transparency
---@param char_width? number # distance between start of each character, in pixels
---@param char_height? number # distance vertically between start of each character, in pixels, when printing multi-line text.
---@param fixed? boolean # indicates whether the font is fixed width (defaults to false ie variable width)
---@param scale? number # font scaling (defaults to 1)
---@return number text_width # returns the width of the rendered text in pixels
function font(text, x, y, transcolor, char_width, char_height, fixed, scale) end
---
---Draws a straight line from point (x0,y0) to point (x1,y1) in the specified color.
---
---@param x0 number # the `coordinates` of the start of the line
---@param y0 number # the `coordinates` of the start of the line
---@param x1 number # the `coordinates` of the end of the line
---@param y1 number # the `coordinates` of the end of the line
---@param color number # the index of the color in the current `palette`
function line(x0, y0, x1, y1, color) end
---
---The map consists of cells of 8x8 pixels, each of which can be filled with a tile using the map editor.
---
---The map can be up to 240 cells wide by 136 deep. This function will draw the desired area of the map to a specified screen position. For example, `map(5,5,12,10,0,0)` will draw a 12x10 section of the map, starting from map co-ordinates (5,5) to screen position (0,0). `map()` without any parameters will draw a 30x17 map section (a full screen) to screen position (0,0).
---
---The map functions last parameter is a powerful callback function for changing how each cells is drawn. It can be used to rotate, flip or even replace tiles entirely. Unlike `mset`, which saves changes to the map, this special function can be used to create animated tiles or replace them completely. Some examples include changing tiles to open doorways, hiding tiles used only to spawn objects in your game and even to emit the objects themselves.
---
---The tilemap is laid out sequentially in RAM - writing 1 to 0x08000 will cause tile #1 to appear at top left when `map` is called. To set the tile immediately below this we need to write to 0x08000 + 240, ie 0x080F0
---
---@param x? number # The coordinates of the top left map cell to be drawn. (default: 0)
---@param y? number # The coordinates of the top left map cell to be drawn. (default: 0)
---@param w? number # The number of cells to draw horizontally and vertically. (default: 30)
---@param h? number # The number of cells to draw horizontally and vertically. (default: 17)
---@param sx? number # The screen coordinates where drawing of the map section will start. (default: 0)
---@param sy? number # The screen coordinates where drawing of the map section will start. (default: 0)
---@param colorkey? number # index (or array of indexes 0.80.0) of the color that will be used as transparent color. Not setting this parameter will make the map opaque. (default: -1)
---@param scale? number # Map scaling. (default: 1)
---@param remap? function # An optional function called before every tile is drawn. Using this callback function you can show or hide tiles, create tile animations or flip/rotate tiles during the map rendering stage: `callback [tile [x y] ] -> [tile [flip [rotate] ] ]`
function map(x, y, w, h, sx, sy, colorkey, scale, remap) end
---
---This function can read or write individual pixel color values. When called with a **color** argument , the pixel at the specified `coordinates` is set to that color. When called with only **x y** arguments, the color of the pixel at the specified coordinates is returned.
---
---@param x number # `coordinates` of the pixel
---@param y number # `coordinates` of the pixel
---@param color number # the index (0-15) of the `palette` color at the specified `coordinates`.
---@return number color # the index (0-15) of the `palette` color at the specified `coordinates`.
function pix(x, y, color) end
---
---This will simply print text to the screen using the font defined in config. When set to true, the fixed width option ensures that each character will be printed in a 'box' of the same size, so the character 'i' will occupy the same width as the character 'w' for example. When fixed width is false, there will be a single space between each character. Refer to the example for an illustration.
---
---* To use a custom rastered font, check out `font`.
---* To print to the console, check out `trace`.
---
---@param text string # any string to be printed to the screen
---@param x? number # `coordinates` for printing the text (default: 0)
---@param y? number # `coordinates` for printing the text (default: 0)
---@param color? number # the `color` to use to draw the text to the screen (default: 15)
---@param fixed? boolean # a flag indicating whether fixed width printing is required (default: false)
---@param scale? number # font scaling (default: 1)
---@param smallfont? boolean # use small font if true (default: false)
---@return number text_width # returns the width of the text in pixels.
function print(text, x, y, color, fixed, scale, smallfont) end
---
---This function draws a filled rectangle at the specified position.
---
---See also:
---
---- `rectb` - draw only the border of the rectangle
---
---@param x number # `coordinates` of the top left corner of the rectangle
---@param y number # `coordinates` of the top left corner of the rectangle
---@param width number # the width the rectangle in pixels
---@param height number # the height of the rectangle in pixels
---@param color number # the index of the color in the `palette` that will be used to fill the rectangle
function rect(x, y, width, height, color) end
---
---This function draws a one pixel thick rectangle border.
---
---See also:
---
---- `rect` - draws a filled rectangle
---
---@param x number # `coordinates` of the top left corner of the rectangle
---@param y number # `coordinates` of the top left corner of the rectangle
---@param width number # the width the rectangle in pixels
---@param height number # the height of the rectangle in pixels
---@param color number # the index of the color in the `palette` that will be used to color the rectangle's border.
function rectb(x, y, width, height, color) end
---
---Draws the `sprite` number **index** at the **x** and **y** `coordinate`.
---
---You can specify a **colorkey** in the `palette` which will be used as the transparent color or use a value of -1 for an opaque sprite.
---
---The sprite can be **scaled** up by a desired factor. For example, a scale factor of 2 means an 8x8 pixel sprite is drawn to a 16x16 area of the screen.
---
---You can **flip** the sprite where:
---* 0 = No Flip
---* 1 = Flip horizontally
---* 2 = Flip vertically
---* 3 = Flip both vertically and horizontally
---
---When you **rotate** the sprite, it's rotated clockwise in 90° steps:
---* 0 = No rotation
---* 1 = 90° rotation
---* 2 = 180° rotation
---* 3 = 270° rotation
---
---You can draw a **composite** sprite (consisting of a rectangular region of sprites from the sprite sheet) by specifying the **w** and **h** parameters (which default to 1).
---
---@param id number # index of the `sprite` (0..511)
---@param x number # screen `coordinates` of top left corner of sprite.
---@param y number # screen `coordinates` of top left corner of sprite.
---@param colorkey? number # index (or array of indexes) of the color in the `sprite` that will be used as transparent color. Use -1 if you want an opaque sprite. (default: -1)
---@param scale? number # scale factor applied to `sprite`. (default: 1)
---@param flip? number # flip the `sprite` vertically or horizontally or both. (default: 0)
---@param rotate? number # rotate the `sprite` by 0, 90, 180 or 270 degrees. (default: 0)
---@param w? number # width of composite sprite (default: 1)
---@param h? number # height of composite sprite (default: 1)
function spr(id, x, y, colorkey, scale, flip, rotate, w, h) end
---
---This function draws a triangle filled with **color**, using the supplied vertices.
---
---@param x1 number # the `coordinates` of the first triangle corner
---@param y1 number # the `coordinates` of the first triangle corner
---@param x2 number # the coordinates of the second corner
---@param y2 number # the coordinates of the second corner
---@param x3 number # the coordinates of the third corner
---@param y3 number # the coordinates of the third corner
---@param color number # the index of the desired color in the current `palette`
function tri(x1, y1, x2, y2, x3, y3, color) end
---
---This function draws a triangle border with **color**, using the supplied vertice
---
---@param x1 number # the `coordinates` of the first triangle corner
---@param y1 number # the `coordinates` of the first triangle corner
---@param x2 number # the coordinates of the second corner
---@param y2 number # the coordinates of the second corner
---@param x3 number # the coordinates of the third corner
---@param y3 number # the coordinates of the third corner
---@param color number # the index of the desired color in the current `palette`
function trib(x1, y1, x2, y2, x3, y3, color) end
---
---This function draws a triangle filled with texture from either `SPRITES` or `MAP` `RAM`.
---
---@param x1 number # the screen `coordinates` of the first corner
---@param y1 number # the screen `coordinates` of the first corner
---@param x2 number # the screen coordinates of the second corner
---@param y2 number # the screen coordinates of the second corner
---@param x3 number # the screen coordinates of the third corner
---@param y3 number # the screen coordinates of the third corner
---@param u1 number # the UV coordinates of the first corner
---@param v1 number # the UV coordinates of the first corner
---@param u2 number # the UV coordinates of the second corner
---@param v2 number # the UV coordinates of the second corner
---@param u3 number # the UV coordinates of the third corner
---@param v3 number # the UV coordinates of the third corner
---@param use_map? boolean # if false (default), the triangle's texture is read from `SPRITES` RAM. If true, the texture comes from the `MAP` RAM.
---@param trans? number # index (or array of indexes 0.80) of the color(s) that will be used as transparent (default: -1)
function textri(x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, use_map, trans) end

11
config/library/init.lua Normal file
View File

@ -0,0 +1,11 @@
---@meta
---
---This function allows you to write directly to RAM. The requested number of bits is written at the address requested. The address is typically specified in hexadecimal format.
---
---For in-depth detail on how addressing works with various bits parameters, please see `peek`.
---
---@param addr number # the address of RAM you desire to write (segmented based on bits)
---@param val number # the integer value write to RAM (range varies based on bits)
---@param bits number # the number of bits to write (1, 2, 4, or 8; default: 8)
function poke(addr, val, bits) end

48
config/library/input.lua Normal file
View File

@ -0,0 +1,48 @@
---@meta
---
---This function allows you to read the status of TIC's controller buttons. It returns `true` if the button with the supplied **id** is currently in the pressed state and remains `true` for as long as the button is held down. To see if a button was _just_ pressed, use `btnp` instead.
---
---@param id? number # id (0..31) of the key we want to interrogate (see the `key map` for reference)
---@return boolean pressed # button is pressed (true/false)
function btn(id) end
---
---This function allows you to read the status of one of TIC's buttons. It returns `true` only if the key has been pressed since the last frame.
---
---You can also use the optional **hold** and **period** parameters which allow you to check if a button is being held down. After the time specified by **hold** has elapsed, btnp will return *true* each time **period** is passed if the key is still down. For example, to re-examine the state of button '0' after 2 seconds and continue to check its state every 1/10th of a second, you would use btnp(0, 120, 6). Since time is expressed in ticks and TIC runs at 60 frames per second, we use the value of 120 to wait 2 seconds and 6 ticks (ie 60/10) as the interval for re-checking.
---
---@param id? number # the id (0..31) of the button we wish to interrogate - see the `key map` for reference
---@param hold? number # the time (in ticks) the button must be pressed before re-checking
---@param period? number # the amount of time (in ticks) after **hold** before this function will return `true` again.
---@return boolean pressed # button is pressed now but not in previous frame (true/false)
function btnp(id, hold, period) end
---
---The function returns *true* if the key denoted by *keycode* is presse
---
---@param code? number # the key code we want to check (1..65)
---@return boolean pressed # key is currently pressed (true/false)
function key(code) end
---
---This function returns `true` if the given key is pressed but wasn't pressed in the previous frame. Refer to `btnp` for an explanation of the optional **hold** and **period** parameters
---
---@param code? number # the key code we want to check (1..65) (see codes [here](https://github.com/nesbox/TIC-80/wiki/key#parameters))
---@param hold? number # time in ticks before autorepeat
---@param period? number # time in ticks for autorepeat interval
---@return boolean pressed # key is pressed (true/false)
function keyp(code, hold, period) end
---
---This function returns the mouse coordinates and a boolean value for the state of each mouse button, with true indicating that a button is pressed.
---
---@return number x # `coordinates` of the mouse pointer
---@return number y # `coordinates` of the mouse pointer
---@return boolean left # left button is down (true/false)
---@return boolean middle # middle button is down (true/false)
---@return boolean right # right button is down (true/false)
---@return number scrollx # x scroll delta since last frame (-31..32)
---@return number scrolly # y scroll delta since last frame (-31..32)
function mouse() end

160
config/library/memory.lua Normal file
View File

@ -0,0 +1,160 @@
---@meta
---
---This function copies a continuous block of `RAM` from one address to another.
---Addresses are specified in hexadecimal format, values are decimal.
---
---@param to number # the address you want to write to
---@param from number # the address you want to copy from
---@param length number # the length of the memory block you want to copy (in bytes)
function memcpy(to, from, length) end
---
---This function sets a continuous block of `RAM` to the same value.
---The address is specified in hexadecimal format, the value in decimal.
---
---@param addr number # the address of the first byte of `RAM` you want to write to
---@param value number # the value you want to write (0..255)
---@param length number # the length of the memory block you want to set
function memset(addr, value, length) end
---
---This function allows you to save and retrieve data in one of the 256 individual 32-bit slots available in the cartridge's persistent memory. This is useful for saving high-scores, level advancement or achievements. Data is stored as unsigned 32-bit integer (from 0 to 4294967295).
---
---When writing a new value the previous value is returned.
---
---@param index number # an index (0..255) into the persistent memory of a cartridge.
---@param val32 number # the current/prior value saved to the specified memory slot.
---@return number val32 # the current/prior value saved to the specified memory slot.
function pmem(index, val32) end
---
---This function allows you to read directly from RAM. It can be used to access resources created with the integrated tools, such as the sprite, map and sound editors, as well as cartridge data.
---
---The requested number of bits is read from the address requested. The address is typically specified in hexadecimal format.
---
---See also:
---
---- `poke` - Write to a memory address
---
---@param addr number # the address of `RAM` you desire to read (segmented based on `bits`)
---@param bits? number # the number of bits to read (1, 2, 4, or 8) from address (default: 8)
---@return number value # a number that depends on how many bits were read
function peek(addr, bits) end
---
---This function allows you to read directly from RAM. It can be used to access resources created with the integrated tools, such as the sprite, map and sound editors, as well as cartridge data.
---
---The requested number of bits is read from the address requested. The address is typically specified in hexadecimal format.
---
---See also:
---
---- `poke` - Write to a memory address
---
---@param bitaddr number # the address of [RAM](RAM) you desire to write
---@return number bitval # the integer value write to RAM
function peek1(bitaddr) end
---
---This function allows you to read directly from RAM. It can be used to access resources created with the integrated tools, such as the sprite, map and sound editors, as well as cartridge data.
---
---The requested number of bits is read from the address requested. The address is typically specified in hexadecimal format.
---
---See also:
---
---- `poke` - Write to a memory address
---
---@param addr2 number # the address of [RAM](RAM) you desire to write (segmented on 2)
---@return number val2 # the integer value write to RAM (segmented on 2)
function peek2(addr2) end
---
---This function allows you to read directly from RAM. It can be used to access resources created with the integrated tools, such as the sprite, map and sound editors, as well as cartridge data.
---
---The requested number of bits is read from the address requested. The address is typically specified in hexadecimal format.
---
---See also:
---
---- `poke` - Write to a memory address
---
---@param addr4 number # the address of [RAM](RAM) you desire to write (segmented on 4)
---@return number val4 # the integer value write to RAM (segmented on 4)
function peek4(addr4) end
---
---This function allows you to write directly to `RAM`. The requested number of bits is written at the address requested. The address is typically specified in hexadecimal format.
---
---For in-depth detail on how addressing works with various `bits` parameters, please see `peek`.
---
---See also:
---
---- `peek` - Read from a memory address
---
---@param addr number # the address of `RAM` you desire to write (segmented based on `bits`)
---@param val number # the integer value write to RAM (range varies based on `bits`)
---@param bits? number # the number of bits to write (1, 2, 4, or 8; default: 8)
function poke(addr, val, bits) end
---
---This function allows you to write directly to `RAM`. The requested number of bits is written at the address requested. The address is typically specified in hexadecimal format.
---
---For in-depth detail on how addressing works with various `bits` parameters, please see `peek`.
---
---See also:
---
---- `peek` - Read from a memory address
---
---@param bitaddr number # the address of [RAM](RAM) you desire to write
---@param bitval number # the integer value write to RAM
function poke1(bitaddr, bitval) end
---
---This function allows you to write directly to `RAM`. The requested number of bits is written at the address requested. The address is typically specified in hexadecimal format.
---
---For in-depth detail on how addressing works with various `bits` parameters, please see `peek`.
---
---See also:
---
---- `peek` - Read from a memory address
---
---@param addr2 number # the address of [RAM](RAM) you desire to write (segmented on 2)
---@param val2 number # the integer value write to RAM (segmented on 2)
function poke2(addr2, val2) end
---
---This function allows you to write directly to `RAM`. The requested number of bits is written at the address requested. The address is typically specified in hexadecimal format.
---
---For in-depth detail on how addressing works with various `bits` parameters, please see `peek`.
---
---See also:
---
---- `peek` - Read from a memory address
---
---@param addr4 number # the address of [RAM](RAM) you desire to write (segmented on 4)
---@param val4 number # the integer value write to RAM (segmented on 4)
function poke4(addr4, val4) end
---
---Use `sync()` to save data you modify during runtime and would like to persist, or to restore runtime data from the cartridge. For example, if you have manipulated the runtime memory (e.g. by using `mset`), you can reset the active state by calling `sync(0,0,false)`. This resets the whole of runtime memory to the contents of bank 0.
---
---Note that `sync` is never used to load _code_ from banks; this is done automatically. All data is restored from cartridge on every startup.
---
---_Note:_ In older versions of TIC-80, calling `sync` was not required to save runtime map and sprite data. Sync should be called any time changes to the sprites and map are made during runtime if you'd like the changes to be applied.
---
---
------
---
---@param mask? number # mask of sections you want to switch: (default: 0)
---@param bank? number # memory bank (0..7) (default: 0)
---@param tocart? boolean # `true` - save memory from runtime to bank/cartridge, `false` - load data from bank/cartridge to runtime. (default: false)
function sync(mask, bank, tocart) end
---
---VRAM is double-banked, such that the entire 16kb VRAM address space can be "swapped" at any time between banks 0 and 1. This is most commonly used for layering effects (background vs foreground layers, or a HUD that sits overtop of your main gameplay area, etc).
---
---Note: `vbank` should not be confused with `sync`. VRAM banks can be switched
---many times during a single `TIC` (though this isn't common) - this is not true for the other banked RAM.
---
---@param id number # the VRAM bank ID to switch to (0 or 1)
function vbank(id) end

35
config/library/sound.lua Normal file
View File

@ -0,0 +1,35 @@
---@meta
---
---This function starts playing a **track** created in the `Music Editor`.
---
---@param track? number # the id of the track to play (0..7) (default: -1)
---@param frame? number # the index of the frame to play from (0..15) (default: -1)
---@param row? number # the index of the row to play from (0..63) (default: -1)
---@param loop? boolean # loop music (true) or play it once (false) (default: true)
---@param sustain? boolean # sustain notes after the end of each frame or stop them (true/false) (default: false)
---@param tempo? number # play track with the specified tempo, _(added in version 0.90)_ (default: -1)
---@param speed? number # play track with the specified speed, _(added in version 0.90)_ (default: -1)
function music(track, frame, row, loop, sustain, tempo, speed) end
---
---This function will play the sound with **id** created in the sfx editor. Calling the function with an **id** of `-1` will stop playing the channel.
---
---The **note** can be supplied as an integer between 0 and 95 (representing 8 octaves of 12 notes each) or as a string giving the note name and octave. For example, a note value of '14' will play the note 'D' in the second octave. The same note could be specified by the string 'D-2'. Note names consist of two characters, the note itself (**in upper case**) followed by '-' to represent the natural note or '#' to represent a sharp. There is no option to indicate flat values. The available note names are therefore: C-, C#, D-, D#, E-, F-, F#, G-, G#, A-, A#, B-. The octave is specified using a single digit in the range 0 to 8.
---
---The **duration** specifies how many ticks to play the sound for; since TIC-80 runs at 60 frames per second, a value of 30 represents half a second. A value of -1 will play the sound continuously.
---
---The **channel** parameter indicates which of the four channels to use. Allowed values are 0 to 3.
---
---**Volume** can be between 0 and 15.
---
---**Speed** in the range -4 to 3 can be specified and means how many 'ticks+1' to play each step, so speed==0 means 1 tick per step.
---
---@param id number # the SFX id (0..63), or -1 to stop playing
---@param note? number # the note number (0..95) or name (ex: `C#3`) (-1 by default, which plays note assigned in the Sfx Editor)
---@param duration? number # the duration (number of frames) (-1 by default, which plays continuously)
---@param channel? number # the audio channel to use (0..3) (default: 0)
---@param volume? number # the volume (0..15) (defaults to 15)
---@param speed? number # the speed (-4..3) (defaults to 0)
function sfx(id, note, duration, channel, volume, speed) end

44
config/library/system.lua Normal file
View File

@ -0,0 +1,44 @@
---@meta
---
---This function causes program execution to be terminated **after** the current `TIC` function ends. The entire function is executed, including any code that follows `exit()`. When the program ends you are returned to the [console](console).
---
---See the example below for a demonstration of this behavior.
---
function exit() end
---
---Resets the TIC virtual "hardware" and immediately restarts the cartridge.
---
---To simply return to the console, please use `exit`.
---
---See also:
---
---- `exit`
---
function reset() end
---
---This function returns the number of _milliseconds_ elapsed since the cartridge began execution. Useful for keeping track of time, animating items and triggering events.
---
---@return number ticks # the number of _milliseconds_ elapsed since the game was started
function time() end
---
---This function returns the number of _seconds_ elapsed since January 1st, 1970. This can be quite useful for creating persistent games which evolve over time between plays.
---
---@return number timestamp # the current Unix timestamp in _seconds_
function tstamp() end
---
---This is a service function, useful for debugging your code. It prints the supplied string or variable to the console in the (optional) color specified.
---
---_Tips:_
---
---1. The Lua concatenation operator is `..` (two periods)
---2. Use the console `cls` command to clear the output from trace
---
---@param message string # the message to print in the `console`.
---@param color? number # the index of a color in the current `palette` (0..15) (default: 15)
function trace(message, color) end

View File

@ -0,0 +1,40 @@
---@meta
---
---Returns `true` if the specified flag of the sprite is set.
---
---See also:
---
---* `fset` (0.8
---
---@param sprite_id number # sprite index (0..511)
---@param flag number # flag index to check (0..7)
---@return boolean bool # a boolean
function fget(sprite_id, flag) end
---
---This function sets the sprite flag to a given boolean value. Each sprite has eight flags which can be used to store information or signal different conditions. For example, flag 0 might be used to indicate that the sprite is invisible, flag 6 might indicate that the sprite should be drawn scaled etc.
---
---To read the value of sprite flags, see `fget`.
---
---@param sprite_id number # sprite index (0..511)
---@param flag number # index of flag (0-7) to set
---@param bool boolean # a boolean
function fset(sprite_id, flag, bool) end
---
---This function returns the tile at the specified `MAP` coordinates, the top left cell of the map being (0, 0).
---
---@param x number # map coordinates
---@param y number # map coordinates
---@return number tile_id # returns the tile id at the given coordinates
function mget(x, y) end
---
---This function will change the tile at the specified `MAP` coordinates. By default, changes made are only kept while the current game is running. To make permanent changes to the map (persisting them back to the cartridge), see `sync`.
---
---@param x number # map coordinates
---@param y number # map coordinates
---@param tile_id number # The background tile (0-255) to place in map at specified coordinates.
function mset(x, y, tile_id) end

333
generate.py Executable file
View File

@ -0,0 +1,333 @@
#!/bin/python3
import requests
from os import path
import re
from dataclasses import dataclass
@dataclass
class MarkdownSection:
name: str
body: str
subsections: dict[str, "MarkdownSection"]
ROOT_RAW_URL = "https://raw.githubusercontent.com/wiki/nesbox/TIC-80"
ROOT_URL = "https://github.com/nesbox/TIC-80/wiki"
def get_url_for(item: str) -> str:
return path.join(ROOT_RAW_URL, item) + ".md"
def get_contents(url: str):
res = requests.get(url)
return res.content.decode()
def group_by_sections(markdown: str, min_level = 1) -> list[MarkdownSection]:
sections = []
# Find the lowest given section level from the text
level = min_level
header_prefix = ""
while True:
header_prefix = "#" * level
if re.search(f"^{header_prefix}\\s+(.+)\\s*\n", markdown, re.MULTILINE) != None:
break
level += 1
if level == 8:
return []
# Find all headers of the same level
header_prefix = "#" * level
headers = list(h for h in re.finditer(f"^{header_prefix}\\s+(.+)\\s*\n", markdown, re.MULTILINE))
for i in range(len(headers)):
header = headers[i]
header_start = header.end()
# The size of the whole section (including sub-sections)
section_end = -1
if i+1 < len(headers):
section_end = headers[i+1].start()
section_markdown = markdown[header_start:section_end]
# Find all subsections and save them
subsections = {}
for subsection in group_by_sections(section_markdown, level+1):
subsections[subsection.name] = subsection
# Determine body size (just text that isin't part of a subsection)
body_end = -1
next_neighbour_header = re.search(f"^{header_prefix}.+\n", markdown[header_start:], re.MULTILINE)
if next_neighbour_header != None:
body_end = header_start + next_neighbour_header.start()
body = markdown[header_start:body_end].strip()
# Save subsection
sections.append(MarkdownSection(
name = header.group(1),
body = body,
subsections = subsections
))
return sections
def extract_function_names(functions_section: MarkdownSection) -> dict[str, list[str]]:
function_names = {}
for subsection in functions_section.subsections.values():
names = []
for line in subsection.body.splitlines():
match = re.search(r"\[(.*?)\]", line)
if not match:
raise Exception(f"Couldn't extract function name from: '{line}'")
names.append(match.group(1))
function_names[subsection.name.lower()] = names
return function_names
def list_available_functions() -> dict[str, list[str]]:
api_url = get_url_for("API")
contents = get_contents(api_url)
top_section = group_by_sections(contents)[0]
return extract_function_names(top_section.subsections["Functions"])
def get_root_section_of_function(function_name) -> MarkdownSection:
contents = get_contents(get_url_for(function_name))
top_sections = group_by_sections(contents)
if len(top_sections) == 0:
match = re.search(r"^Please see: \[.+\]\((.+)\)", contents, re.MULTILINE)
if match == None:
raise Exception(f"Failed to fetch contents of function: '{function_name}'")
contents = get_contents(get_url_for(match.group(1)))
top_sections = group_by_sections(contents)
# TYPO: Because there is a type in `rectb`, where the top level header starts from "##"
# There needs to be some extra logic to see if that is the case
if len(top_sections) > 1:
# If it is the case, just skip the first "#" character
top_sections = group_by_sections(contents[1:])
return top_sections[0]
# Example line: `mouse() -> x, y, left, middle, right, scrollx, scrolly`
def get_function_signature(function_name: str, description: str) -> tuple[list[tuple[str, str|None]], list[str]]:
# TYPO: Because there is a typo in the signature for `keyp`.
# The simplest solution is to just hard-code the value for the time being.
if function_name == "keyp":
return ([("code", "nil"), ("hold", "nil"), ("period", "nil")], ["pressed"])
# TYPO: Because `vbank` is not documented in a strictly formatted way,
# it needs to be hard-coded here
if function_name == "vbank":
return ([("id", None)], [])
params: list[tuple[str, str|None]] = []
return_vars: list[str] = []
# There could exist multiple signatures, so its important too loop through
# all of them
for match in re.finditer(rf"`{function_name}\s*\((.*)\)( -> ([\w\, ]+))?`", description):
if match.group(3) != None and len(return_vars) == 0:
return_vars = list(v.strip().replace(" ", "_") for v in match.group(3).split(","))
if len(match.group(1)) > 0:
match_params = match.group(1).split(",")
for i in range(len(match_params)):
match_param = match_params[i].strip()
default = None
name = match_param
if name[0] == "[" or name[-1] == "]":
equal_sign = match_param.find("=")
# TYPO: Because there is a typo in `btnp` there needs to be some
# extra logic for stripping extra "[]" symbols
if equal_sign > 0:
name = match_param[1:equal_sign]
default = match_param[equal_sign+1:-1]
else:
name = match_param
default = "nil"
name = name.strip("[]")
if i == len(params):
params.append((name.replace(" ", "_"), default))
return (params, return_vars)
# Example line: * **text** : any string to be printed to the screen
# Example line: * **x, y** : the [coordinates](coordinate) of the circle's center
# Example line (from clip): * **x**, **y** : [coordinates](coordinate) of the top left of the clipping region
# Example line (from mouse): * **x y** : [coordinates](coordinate) of the mouse pointer
def parse_variable_description_line(line: str) -> tuple[list[str], str]:
match = re.match(r"[\*-] \*\*(.+)\*\*\s*:\s*(.+)", line)
if not match:
raise Exception(f"Failed to parse parameter descriptions: '{line}'")
description = match.group(2)
description = replace_relative_links(description)
names = list(name.strip(" *").replace(" ", "_") for name in match.group(1).split(","))
# TYPO: Adjust result, because there is a typo in `mouse`
if "x_y" in names:
names.remove("x_y")
names.append("x")
names.append("y")
return (names, description)
def get_variable_descriptions(root: MarkdownSection) -> dict[str, str]:
descriptions = {}
search_lines = []
if "Parameters" in root.subsections:
search_lines.extend(root.subsections["Parameters"].body.splitlines())
elif "**Parameters**" in root.body:
params_header_match = re.search(r"\*\*Parameters\*\*", root.body)
assert params_header_match
params_start = params_header_match.end()
search_lines.extend(root.body[params_start:].strip().splitlines())
if "Returns" in root.subsections:
search_lines.extend(root.subsections["Returns"].body.splitlines())
for line in search_lines:
if line.startswith("* **") or line.startswith("- **"):
names, description = parse_variable_description_line(line)
for name in names:
descriptions[name] = description
# TYPO: Because a line explaning the variable "value" in `peek` is missing,
# It is hard-coded here
if root.name == "peek*":
descriptions["value"] = "a number that depends on how many bits were read"
# TYPO: Because "bitaddr", "bitval", "addr2", "addr4", "val2", "val4" are
# not explicitly explained in `peek` and `poke`, it is hard-coded here
if root.name == "peek*" or root.name == "poke*":
descriptions["bitaddr"] = "the address of [RAM](RAM) you desire to write"
descriptions["bitval"] = "the integer value write to RAM"
descriptions["addr2"] = "the address of [RAM](RAM) you desire to write (segmented on 2)"
descriptions["val2"] = "the integer value write to RAM (segmented on 2)"
descriptions["addr4"] = "the address of [RAM](RAM) you desire to write (segmented on 4)"
descriptions["val4"] = "the integer value write to RAM (segmented on 4)"
# Because the return type in `fget` is "bool" and is not explicitly documented
# it is addded here
descriptions["bool"] = "a boolean"
return descriptions
def replace_relative_links(markdown: str) -> str:
return re.sub(
r"\[(.+?)\]\((.+?)\)",
lambda s: s.group(0) if s.group(2).startswith("http") else f"`{s.group(1)}`",
markdown
)
def remove_non_printable(text: str) -> str:
return ''.join(c for c in text if c.isprintable() or c == '\n')
def get_function_description(root: MarkdownSection) -> str:
# TYPO: Because `exit` is not strictly formatted, the description is hard-coded
if root.name == "exit":
return """This function causes program execution to be terminated **after** the current `TIC` function ends. The entire function is executed, including any code that follows `exit()`. When the program ends you are returned to the [console](console).
See the example below for a demonstration of this behavior."""
# TYPO: Because `reset` is not strictly formatted, the description is hard-coded
if root.name == "reset":
return """Resets the TIC virtual "hardware" and immediately restarts the cartridge.
To simply return to the console, please use `exit`.
See also:
- `exit`"""
description = root.subsections["Description"].body
return replace_relative_links(description)
def prefix_lines(text: str, prefix: str) -> str:
return "\n".join(prefix+line for line in text.splitlines())
def guess_variable_types(descriptions: dict[str, str], func_params: list[tuple[str, str|None]]) -> dict[str, str]:
type_names = {}
default_type_lookup = {}
for param in func_params:
if param[1] != None:
if param[1] == "false" or param[1] == "true":
default_type_lookup[param[0]] = "boolean"
elif param[1].isdigit():
default_type_lookup[param[0]] = "number"
number_variable_names = [
"id", "track", "tempo", "speed", "duration", "note",
"value", "val32",
"radius", "scale", "w", "h", "sx", "sy",
"period", "hold", "code", "length", "timestamp", "scrollx", "scrolly"
]
for var_name, desc in descriptions.items():
if var_name in default_type_lookup:
type_names[var_name] = default_type_lookup[var_name]
elif var_name in number_variable_names or \
"width" in var_name or \
"height" in var_name or \
"color" in var_name or \
"index" in desc or \
"number" in desc or \
"coordinate" in desc or \
"integer" in desc or \
"address" in desc or \
"radius" in desc:
type_names[var_name] = "number"
elif var_name in ["pressed", "bool"] or \
"true" in desc or \
"false" in desc:
type_names[var_name] = "boolean"
elif var_name == "text" or "message" in desc:
type_names[var_name] = "string"
elif "function" in desc:
type_names[var_name] = "function"
else:
type_names[var_name] = "MISSING_TYPE"
return type_names
def create_lua_function(function_name: str):
root = get_root_section_of_function(function_name)
param_vars, return_vars = get_function_signature(function_name, root.body)
func_signature = f"function {function_name}("
func_signature += ", ".join(list(p[0] for p in param_vars))
func_signature += ") end"
doc_comment = "---\n"
func_description = get_function_description(root)
func_description = remove_non_printable(func_description)
doc_comment += prefix_lines(func_description, "---")
doc_comment += "\n---"
var_descriptions = get_variable_descriptions(root)
var_types = guess_variable_types(var_descriptions, param_vars)
for param in param_vars:
param_name, default = param
is_optional = "?" if default != None else ""
param_description = var_descriptions[param_name]
type_name = var_types[param_name]
doc_comment += f"\n---@param {param_name}{is_optional} {type_name} # {param_description}"
if "default" not in param_description and default != None and default != "nil":
doc_comment += f" (default: {default})"
for return_var in return_vars:
return_description = var_descriptions[return_var]
type_name = var_types[return_var]
doc_comment += f"\n---@return {type_name} {return_var} # {return_description}"
return doc_comment + "\n" + func_signature
def main():
library_path = "config/library"
for section_name, funcs in list_available_functions().items():
with open(f"{library_path}/{section_name}.lua", "w") as f:
f.write("---@meta\n\n")
for func in funcs:
f.write(create_lua_function(func))
f.write("\n\n")
if __name__ == "__main__":
main()