This page is intended to document Ved's internals as well as possible. The page is still being worked on, and more things are being added over time. If you'd like to know more about anything specific in Ved that I haven't explained yet here, or not well enough (except for the easter eggs :P), feel free to ask! If you'd like more information on how to make plugins, check this page. The repository is here.
Last updated: Monday 18 March 2024 03:14 (UTC) (this is the last edit date of the file)
The following source files are used in Ved. Note that this table may not be entirely accurate - the "Added" column may not always be filled in even if a file didn't exist since the early days of Ved for example, some files may have been removed from the list altogether when they were removed, some changes may not yet have been documented here, and some files may have had more complicated overhauls. Version numbers in parentheses indicate that basically, the file had either been renamed, or something else is the matter that should be explained in the description.
Filename | Added | Rem'd | Description |
---|---|---|---|
callback_*.lua | 1.8.4 | Contain all love.* callbacks. For example, callback_load.lua loads most other source files and assets. | |
clargs.lua | Stores and formats the command line help output when requested. | ||
conf.lua | LÖVE's configuration file, controlling default window settings and loaded LÖVE modules. | ||
const.lua | Constants - Contains known scripting commands, music names, and other lookup tables. Also contained tileset data before 1.8.4. | ||
coordsdialog.lua | 1.4.0 | Contains code related to the little room coordinates input after hitting Q in the main editor. Before 1.4.0, this was part of dialog.lua. | |
corefunc.lua | Contains a few functions that are used so early in loading (and/or are used on the crash screen), they must exist before things like plugins and the error handler are loaded. | ||
coretext.lua | Contains functions for loading fonts, language files, and printing text. | ||
devstrings.lua | Used for defining new text strings during development of a new version, before putting them in all the language files. | ||
dialog.lua | Contains code related to dialog boxes. Before 1.4.0, this also contained code for right click menus, scrollbars and VVVVVV-style text boxes, which have each been moved to their own separate files as of 1.4.0. | ||
dialog_uses.lua | Contains callback functions and definitions of fields for dialogs, which are used as arguments for dialog.create(...) | ||
drawhelp.lua | 1.8.4 | Holds drawhelp(), called by love.draw() in state 15 (the help state). The help system is also used for level notes and the plugins list. As of 1.8.4, this code was moved to uis/help/draw.lua. | |
drawlevelslist.lua | 1.8.4 | Holds drawlevelslist(), called by love.draw() in state 6 (the loading screen state). As of 1.8.4, this code was moved to uis/levelslist/draw.lua. | |
drawmaineditor.lua | 1.8.4 | Holds drawmaineditor(), called by love.draw() in state 1 (the main editor state). As of 1.8.4, this code was moved to uis/maineditor/draw.lua. | |
drawmap.lua | 1.8.4 | Holds drawmap(), called by love.draw() in state 12 (the map state). As of 1.8.4, this code was moved to uis/map/draw.lua. | |
drawscripteditor.lua | 1.8.4 | Holds drawscripteditor(), called by love.draw() in state 3 (the script editor state). As of 1.8.4, this code was moved to uis/scripteditor/draw.lua. | |
drawsearch.lua | 1.8.4 | Holds drawsearch(), called by love.draw() in state 11 (the search state). As of 1.8.4, this code was moved to uis/search/draw.lua. | |
entity_mousedown.lua | 1.9.0 | Contains handle_entity_mousedown(). Handles (right) clicking on entities and creating right click menus for them. | |
errorhandler.lua | Contains code for both the crash screen and the plugin error screen. | ||
filefunc_linmac.lua | 1.5.0 | Since Ved 1.5.0, this contains functions necessary for accessing the VVVVVV levels and graphics folders on Linux and macOS. This uses the vedlib_filefunc_* library found in the libs folder via LuaJIT FFI. Before Ved 1.5.0, this was split in filefunc_lin.lua and filefunc_mac.lua and used terminal utilities to list level files. Before Ved 1.10.0, on Linux this library was only ever compiled locally. If unsuccessful (due to missing gcc) we'd fallback to filefunc_lin_fallback.lua instead. Now a compiled Linux version is provided (just like for macOS), with local compilation as a fallback. | |
filefunc_lin_fallback.lua | (1.5.0) | 1.10.0 | Contained functions necessary for accessing the VVVVVV levels and graphics folders on Linux, if compiling the filefunc library was not successful (due to missing gcc). This used command line utilities like ls to list level files and some other file-related things. Before 1.5.0, this file was called filefunc_lin.lua, because this was the only method that existed. |
filefunc_luv.lua | Contains fallback love.filesystem functions for accessing fallback levels and graphics folders if the operating system is something other than Windows, macOS or Linux. | ||
filefunc_mac.lua | 1.5.0 | Contained functions necessary for accessing the VVVVVV levels and graphics folders on macOS. Used command line utilities like ls to list level files and some other file-related things. | |
filefunc_win.lua | Contains functions necessary for accessing the VVVVVV levels and graphics folders on Windows. As of Ved 1.5.0, this uses the Windows API for everything (including reading and writing level files, due to io.open being non-Unicode on Windows), before 1.5.0, it used command line utilities like dir. | ||
func.lua | Contains many functions, especially general-purpose ones and core Ved functions. | ||
helpfunc.lua | Contains certain functions related to (editing) level notes, and the rest of the help system. | ||
https_*.lua | 1.8.3 1.8.4 | Contains platform-specific code for making HTTPS requests. These files were added in Ved 1.8.3 and 1.8.4. | |
imagefont.lua | 1.4.0 | 1.10.0 | Loads and readies font.png for use inside Ved. In Ved 1.10.0, this was superseded by vedfont.lua. |
incompatmain8.lua | (1.4.5) | If LÖVE 0.8 or lower is used, this is loaded from main.lua. It displays a message that outdated LÖVE is being used in all available languages. Before Ved 1.4.5, this file was called incompatmain.lua. | |
incompatmain9.lua | 1.4.5 | If LÖVE 0.9.0 is used, this is loaded from main.lua. It displays a message that LÖVE 0.9.0 is no longer supported in all available languages. | |
input.lua | 1.8.0 | Contains the new input system. | |
konami.lua | (1.8.4) | Handles the shortcut that can be used in the help screen to make text editable. Before Ved 1.8.4, this file was called keyfunc.lua. | |
librarian.lua | 1.10.0 | Provides prepare_library and load_library, which make sure necessary libraries are ready for use, and can load them. | |
libs/ | Folder containing some C and Objective-C support libraries for Linux and macOS, and C header files for those libraries and parts of the Windows API, for use with LuaJIT FFI. | ||
loadallmetadata.lua | Returns level metadata for the levels list from a different thread. | ||
loadconfig.lua | Handles anything related to the settings. | ||
love10compat.lua | Loaded only when LÖVE 0.10.0 or higher is used, and provides compatibility with those versions. Contains the new love.wheelmoved callback. | ||
love11compat.lua | Loaded only when LÖVE 11.0 or higher is used, and provides compatibility with those versions. For example, this hijacks color functions so they work with 0-255 instead of 0-1. | ||
main.lua | The first file that is loaded. Loads the fonts, sets a few basic variables, and loads plugins.lua, errorhandler.lua and, most importantly, all the callback_*.lua files (or main2.lua before 1.8.4). | ||
main2.lua | 1.8.4 | Contained all the LÖVE callbacks that are now split into callback_*.lua files. | |
mapfunc.lua | 1.4.2 | Contains functions related to rendering and updating the map overview screen. | |
music.lua | 1.6.0 | Handles reading and writing vvvvvvmusic.vvv, mmmmmm.vvv, and other custom music files. | |
ogg_vorbis_metadata.lua | 1.9.0 | Decodes some metadata from Ogg Vorbis files, like sample rate, and Vorbis comments (for loop points). | |
playtesting.lua | 1.8.0 | Contains code relevant to playtesting in VVVVVV. | |
playtestthread.lua | 1.8.0 | The thread that starts up VVVVVV (dependent on OS of course) and waits for it to be closed. | |
plugins.lua | Makes sure plugins and their file edits and hooks are loaded | ||
resizablebox.lua | Has a system for a box that can be resized by dragging borders with the mouse. Was formerly used for resizing script boxes, but it was glitchy so it's now unused. | ||
rightclickmenu.lua | 1.4.0 | Contains code related to right click menus. Before 1.4.0, this was part of dialog.lua. | |
roomfunc.lua | Contains functions related to rooms in levels, tiles and such. | ||
scaling.lua | Hijacks/Decorates a couple of LÖVE functions to make scaling work perfectly | ||
scriptfunc.lua | Contains functions related to scripts. | ||
scrollbar.lua | 1.4.0 | Contains code related to scrollbars. Before 1.4.0, this was part of dialog.lua. | |
searchfunc.lua | Contains functions related to searching levels. | ||
slider.lua | Used for the number controls like in the options screen | ||
tileset_data.lua | Contains tile numbers for all tilesets. | ||
tool_mousedown.lua | 1.9.0 | Contains handle_tool_mousedown(). Handles general clicking on the canvas in the main editor, including all the tools, and placing down moved entities. Excludes (right) clicking on entities, see handle_entity_mousedown() (entity_mousedown.lua) for that. | |
ui_elements.lua | 1.7.0 | Contains all the GUI elements | |
uis/ | 1.7.0 | Folder with UI files for each state (see below). Note that in 1.8.4, each state was changed from a single file to a folder with each callback in its own file. | |
updatecheck.lua | (1.8.4) | Contains functionality for the update check. This file was added in Ved 1.8.4-pre14. Before, this filename was used for the actual update checking thread (see updatecheckthread.lua) | |
updatecheckthread.lua | (1.8.4) | Checks what the latest version of Ved is via HTTPS, and reports back. This is run inside a separate thread. Before Ved 1.8.4-pre14, this file was called updatecheck.lua. | |
utf8lib_*.lua | Implements or supplements necessary parts of the Lua utf8 module, depending on LÖVE version | ||
vedfont.lua | 1.10.0 | Contains the font class implementing text rendering (instead of using love.graphics Font objects.) | |
vvvvvvfunc.lua | Implements some code from VVVVVV in Lua, mostly for displaying accurate colors. | ||
vvvvvv_textbox.lua | 1.4.0 | Contains code related to VVVVVV-style text boxes. Before 1.4.0, this was part of dialog.lua. | |
vvvvvvxml.lua | Loads and parses levels from .vvvvvv level files, and creates and saves them. Also has a function for "loading" a blank level. | ||
ziplevel.lua | 1.11.0 | Contains the function that collects levels and their assets and uses the zipwriter to put them in a ZIP. | |
zipwriter.lua | 1.11.0 | Is a library for creating ZIP files |
Ved uses state numbers to represent different screens, menus and interfaces. Blue state numbers are not normally used anymore, and/or are not normally accessible, and many of them are leftover testing states. Red, struck-through state numbers have been removed from Ved altogether (and won't be reused).
As of 1.8.2, most of the code specific to each state can be found in the uis/ directory. States have their own versions of LÖVE callbacks (such as ui.update(dt), ui.keypressed(key), ui.mousepressed(x, y, button), etc). Furthermore, user interfaces can be built up of "Elements" which may automatically implement their own callbacks based on their parameters and position. For example, buttons can be defined to automatically be drawn at the correct position, and to execute the same action when it is clicked and when a given shortcut is pressed. For more information, see the GUI elements section. It should be noted that states can also implement ui.draw(), which is called before the Elements are drawn.
# | UI name | Description |
---|---|---|
-3 | Black screen | |
-2 | init | tostate 6 |
-1 | Display error (expected: errormsg) | |
0 | state0 | Jump to any state number you want. Can be accessed in debug mode by pressing F12. |
1 | The editor (will expect things to have been loaded) | |
2 | Syntax highlighting test | |
3 | scripteditor | Scripting editor |
4 | Some XML testing | |
5 | fsinfo | Filesystem info |
6 | levelslist | Listing of all files in the levels folder, and load a level from here (loading screen) |
7 | spriteview | Display all sprites from sprites.png where you can get the number of the sprite you're hovering over |
8 | Ancient save screen (you can type in a name and press enter) | |
9 | dialogtest | Dialog test, and right click menu test |
10 | scriptlist | List of scripts, and enter one to load |
11 | search | Search |
12 | map | Map |
13 | options | Options screen |
14 | enemypickertest | Enemy picker preview |
15 | help | Help/Level notes/Plugins list |
16 | Reserved for scroll bar test, never used | |
17 | Reserved for folderopendialog utility, never used | |
18 | unreinfo | Show main editor undo/redo stacks |
19 | scriptflags | Flags list |
20 | resizableboxtest | Resizable box test |
21 | overlapentinfo | Display overlapping entities (may be a visible function later) (maybe doesn't work properly) |
22 | Load a script file in the 3DS format (lines separated by dollars) | |
23 | Load a script file NOT in the 3DS format (lines separated by \r\n or \n) | |
24 | Simple plugins list (already never used) | |
25 | syntaxoptions | Syntax highlighting color settings |
26 | fonttest | Font test |
27 | displayoptions | Display/Scale settings |
28 | levelstats | Level stats |
29 | pluralformstest | Plural forms test |
30 | assetsmenu | Assets viewer main menu |
31 | audioplayer | Music player/editor, sound player |
32 | graphicsviewer | Graphics viewer |
33 | language | Language screen |
34 | inputtest | New input system test |
35 | vvvvvvsetupoptions | "VVVVVV setup" options |
36 | textboxcolors | Textbox colors |
100 and further can be allocated by plugins (next paragraph) |
In Ved 1.11.1 and higher, the best way for plugins to add their own states (or "screens") is by including them in a uis folder. This will automatically allocate a state for each of your UIs, which you can jump to with the following function:
to_astate(name [, new=0 [, dontinitialize=false [, ...]]])
Here, name is the name of your UI, which is the name of the folder. If new is specified (because you want to specify dontinitialize=true), then new must be 0.
Jumping to a state calls the hook func_loadstate and the UIs load callback, unless dontinitialize is true.
For more information about UI folders, see the plugins page.
In Ved 1.1.4 and higher, plugins can allocate an amount of states for their own use, without using hardcoded state numbers, making it unnecessary to think of unique state numbers that won't interfere with any other plugins or future Ved updates. The following functions can be used:
For example, take a plugin called My First Plugin, which uses three states. Upon startup, like in hook love_load_start or love_load_end, the plugin calls allocate_states("my_1st_plug", 3). If this is the only plugin, or the first plugin to call allocate_states(), the allocated states will now, internally, be 100, 101 and 102. Let's say My First Plugin has three buttons to go to each of the allocated states. The first button, when clicked, would call to_astate("my_1st_plug", 0), the second would call to_astate("my_1st_plug", 1) and the third would call to_astate("my_1st_plug", 2). Hook love_draw_state, would contain something like this:
if in_astate("my_1st_plug", 0) then -- Insert drawing code for first state! statecaught = true -- <- only necessary in 1.8.1 and older! elseif in_astate("my_1st_plug", 1) then -- Insert drawing code for second state! statecaught = true elseif in_astate("my_1st_plug", 2) then -- Insert drawing code for third state! statecaught = true end
The hook func_loadstate could contain something similar for initialization code for all the states (but without statecaught = true). Speaking about statecaught = true, this variable was used to prevent an "Unknown state" screen from showing, but this screen has been removed in 1.8.2, and thus setting the variable is no longer necessary.
The identifying name can be anything, but this name should be unique to one plugin. It's also possible to allocate multiple blocks of state numbers within the same plugin, if you use different names. If your plugin only has one state, you can leave out the number (allocate_states("my_1st_plug"), in_astate("my_1st_plug"), to_astate("my_1st_plug")). And of course, this means you can have multiple states that are only referred to by string names (I can see how in_astate("my_1st_plug_menu") and in_astate("my_1st_plug_display") can be more pleasing than in_astate("my_1st_plug", 0) and in_astate("my_1st_plug", 1)). It's up to you to choose whatever you like most, or whatever works best for your plugin.
Ved is compatible with all revisions of LÖVE 0.9.x, 0.10.x and 11.x (except LÖVE 0.9.0 as of Ved 1.4.2), but its code is written for 0.9.x. Compatibility with newer versions is mostly achieved by causing update changes to be undone; for example, LÖVE functions that were renamed or expect different arguments are redefined/hijacked and then called by those redefinitions if arguments or return values need to be passed differently, and callbacks that get "new-style" data from LÖVE get a bit of conversion code at the top. There are a few instances of conditionals depending on the version number in regular code, but that is not very common.
In summary: (the unsupported features per version are more detailed below)
LÖVE | Ved support |
---|---|
12.0 | Hasn't been released, but should work without problems since 1.11.1. |
11.5 | Supported since 1.3.3 |
11.4 | |
11.3 | |
11.2 | |
11.1 | |
11.0 | |
0.10.2 |
Supported since a42, with the following restriction:
|
0.10.1 | |
0.10.0 | |
0.9.2 |
Supported, with the following restrictions:
|
0.9.1 | |
0.9.0 | Support dropped in 1.4.5 (broken since 1.4.2) |
0.8.0- | Has never been supported, but a message is shown |
Ved has a dedicated function to check if the current LÖVE version is at least a certain version or later, love_version_meets(). E.g. love_version_meets(10) means "LÖVE version is 0.10.0 or later", love_version_meets(9, 2) means "LÖVE version is 0.9.2 or later". It automatically takes care of the difference between 0.x and 11.x, too, so love_version_meets(10) means "LÖVE version is 0.10.0 or later" while love_version_meets(11) means "LÖVE version is 11.0 or later".
Nevertheless, there are simply some features or improved behavior added in later LÖVE versions, which Ved takes advantage of, that simply can't be backported to previous LÖVE versions. None of these are particularly important features for Ved's main purpose of editing levels, but it is still good to document them.
The restrictions in this block only apply to Ved 1.9.1 and older. Due to different reasons, it has been possible to remove them in Ved 1.10.0.
Being able to use font.png from the VVVVVV graphics folder as the main font
This feature is only supported in LÖVE versions 0.10.0 and up.
This is because in LÖVE versions previous to 0.10.0, the font returned by love.graphics.newImageFont() automatically had 1 pixel of extra horizontal spacing, and there was no way to change this. If you used a custom font.png with 1 pixel of extra spacing for each glyph, it would look really ugly, partly because you wouldn't be used to it being rendered that way, but mostly because Ved prints text assuming there's no 1 pixel of extra space for each glyph.
This problem is fixed in LÖVE 0.10.0+ because it added an optional third argument to love.graphics.newImageFont() to specify the spacing, which also lets you use negative values.
On a side note, tinynumbers, Ved's F9 hotkey font, doesn't have this problem. This is because of a semi-hacky workaround: the font image is intentionally made with 1 less pixel of space per glyph, and then when it gets passed to love.graphics.newImageFont(), it gets 1 pixel of extra spacing either because it's below LÖVE 0.10.0 and it's forced or because we've specified 1 pixel of spacing in LÖVE 0.10.0+.
Ved 1.10.0 added its own text renderer, which supersedes TTF and ImageFont, so we no longer have any forced spacing and this now works in all LÖVE versions.
Having the F9 hotkey font change depending on operating system, language, etc.
This feature is only supported in LÖVE versions 0.10.0 and up.
This is referring to the feature where the characters on the hotkeys that show up when you hold down F9 will change to match your operating system and language. This means that, for example, Ctrl will change to ⌘ (Cmd) on macOS, and Ctrl will change to Strg if your language is German. (If you're a German macOS user then it will still be ⌘.)
This feature depends on Font:setFallbacks(), which was only added in LÖVE 0.10.0. Not much we can do without it.
Ved 1.10.0 added its own text renderer, which supersedes TTF and ImageFont, so we no longer need to rely on Font:setFallbacks() and this now works in all LÖVE versions.
Basically anything to do with jumping around the track of the currently playing audio in the music and sound effect viewers
This feature is only supported in LÖVE versions 0.10.0 and up.
In LÖVE versions before 0.10.0, you can't jump around the track of the currently playing music or sound effect. That means you cannot click on the track to go to a certain position, nor can you use (Shift)+(kp)Left/Right to move 5 or 10 seconds forwards or backwards.
The reason is simple: we need to know the duration of the currently playing audio. The only function that does this is Source:getDuration(), and it only exists starting in LÖVE 0.10.0.
Without knowing the duration of the audio, clicking on the track becomes meaningless, because one end is supposed to be the start of the audio (time t=0) and the other end is supposed to be the end of the audio (time t=<duration of audio>). Without the duration, we don't know what timecode the other end should be. So if one song is, let's say, 2:30 long and the other is 5:00 long, then in the 5:00-long song the middle of the track is 2:30, and in the 2:30-long song the middle of the track is 1:15 - but without knowing the duration of each we don't know where each timecode is supposed to be placed on the track for each song.
Another consequence of not knowing the duration is that we can't make sure that you don't go past the end of the track when you use (Shift)+(kp)Left/Right to jump around. The end of the track is determined by its duration, which we don't know. So we wouldn't know if you went past the end or not without knowing the duration of the audio.
Ved 1.10.0 creates a temporary SoundData object when playing a song in LÖVE 0.9.x, which does have a :getDuration() method in 0.9.x. This means now the only restrictions in the music player/editor in LÖVE 0.9.x are that you can't see the duration of songs before playing them for the first time, and the songs take a little longer to start playing.
Loop points in the music player/editor
This feature is only supported in LÖVE versions 11.0 and up.
Ogg/Vorbis audio can have loop points, which work in VVVVVV. Ved 1.10.0 added support for playing the audio with loop points correctly as well, but this relies on QueueableSource, which was added in LÖVE 11.0. So on older versions, the audio instead just loops from start to finish (including the intro and the possibly otherwise inaccessible part beyond the end point).
Creating ZIP files of levels and their assets
This feature is only supported in LÖVE versions 0.10.0 and up.
LÖVE 0.10 added love.math.compress – renamed to love.data.compress in LÖVE 11.0. We rely on this function for compressing files and calculating a checksum, so they can be put into ZIP files.
Debug mode is a special mode used to access certain features and information that can be useful for debugging and developing Ved. Enabling debug mode has the following effects:
Debug mode is enabled by the boolean variable allowdebug. It is possible to enable it in-app by going from the load screen to the Ved options, clicking and holding the OK button, and "dragging" over the Send Feedback button holding the right mouse button as well.
The number of the currently selected tile is stored in selectedtile.
To edit roomtext and (re)name scripts in script boxes and terminals, Ved uses editingroomtext. The entity ID of the entity data attribute currently being edited is stored in editingroomtext. You can get the entity being edited by simply doing entitydata[editingroomtext]. Since tables in Lua are 1-indexed, editingroomtext cannot be 0, so to check if we are currently editing the roomtext, just do editingroomtext > 0; to check if we are not, just do editingroomtext == 0.
editingroomname is a boolean that is true when the current room's roomname is being edited, and false when it isn't. However, you should use toggleeditroomname() to start and stop editing the roomname.
When editing enemy and platform boundaries, Ved uses editingbounds. It is 0 when no boundaries are being edited. Its magnitude (i.e. its absolute value, i.e. ignore the negative sign if there is one) will be 1 for platform bounds, and 2 for enemy bounds. Its sign (i.e. whether it's positive or negative) will be negative when placing the first corner (the top-left corner), and will be positive when placing the second corner (the bottom-right corner). So to reiterate: when editing platform bounds, editingbounds will go from 0, to -1, to 1; and when editing enemy bounds, editingbounds will go from 0, to -2, to 2.
You can start a boundary edit by calling either changeplatformbounds() or changeenemybounds().
The variable that controls the eraser (i.e. whether right-clicking will erase tiles if holding a tile brush) is eraserlocked. When it is true (by default), you can erase tiles using right-click. When false, you cannot.
Whether or not enemy and platform bounds are rendered is controlled by showepbounds.
The tiles picker (e.g. what pops up when you click on "Show all", or press/hold Ctrl+Shift) being open or not is controlled by tilespicker. tilespicker_shortcut controls whether or not you are holding the shortcut, so Ved knows to close it when you release Ctrl+Shift. But using RCtrl+RShift will keep the tiles picker open (and mixing Left/Right Ctrl+Shift will be the same as LCtrl+LShift - that is, it won't "stick" and you have to keep holding the key combo).
metadata["Creator"] metadata["Title"] metadata["Created"] metadata["Modified"] metadata["Modifiers"] metadata["Desc1"] metadata["Desc2"] metadata["Desc3"] metadata["website"] metadata["mapwidth"] metadata["mapheight"] metadata["levmusic"]
To get a tile in a room, use roomdata_get(x, y, tx, ty). To get all of a room's tiles, use roomdata_get(x, y).
To set a tile, use roomdata_set(x, y, tx, ty, value). To set all of a room's tiles, use roomdata_set(x, y, values).
From 1.8.0-pre22 until 1.9.0-pre05, these functions had an altstate argument for VVVVVV-CE. See the Changelog of breaking codebase changes for 1.9.0 for more info.
{ x = 12, y = 34, t = 17, p1 = 0, p2 = 0, p3 = 0, p4 = 0, p5 = 320, p6 = 240, data = "Roomtext or script name" }
{ tileset = 0, tilecol = 0, platx1 = 0, platy1 = 0, platx2 = 320, platy2 = 240, platv = 4, enemyx1 = 0, enemyy1 = 0, enemyx2 = 320, enemyy2 = 240, enemytype = 0, directmode = 0, warpdir = 0, roomname = "Roomname", auto2mode = 0, }directmode is always present, even after a VVVVVV 2.0 level is loaded. If auto2mode == 1 then multi-tileset mode is used for that room, and in that case directmode should be 0. However, when saving, directmode is set to 1 in the level file because auto2mode is not saved to it.
To get a room's metadata, use levelmetadata_get(x, y).
To set an attribute of metadata, use levelmetadata_set(x, y, attribute, value). It's also possible to use levelmetadata_set(x, y, attribute_table) to replace the entire room's metadata table.
scriptnames = { [1] = "mynewscript", [2] = "mynewscript_load" } scripts = { mynewscript_load = { [1] = "ifflag(2,stop)", [2] = "flag(2,on)", [3] = "iftrinkets(0,mynewscript)" }, mynewscript = { [1] = "reply(3)", [2] = "I probably don't really need it,", [3] = "but it might be nice to take it", [4] = "back to the ship to study..." } }If you're wondering how Ved stores internal scripts: for both loadscript and say(-1) internal script modes, text(1,0,0,3) #v and say(x) #v are put in between each block if the script has to be split. The loadscript mode starts with squeak(off) #v and say(x) #v, and ends with loadscript(stop) #v and text(1,0,0,3) #v. The say(-1) mode starts with squeak(off) #v, say(-1) #v, text(1,0,0,3) #v, and say(x) #v, and ends with loadscript(stop) #v (with no extra text line like the loadscript internal script mode has). If you want to check, hold down the shift key while opening a script. The same goes for checking checking flag names - Ved converts them to numbers when leaving the script editor and converts them back into names when opening it, unless you hold shift while opening.
extra.textboxcolors_order = { [1] = "red", [2] = "magenta" } extra.textboxcolors = { red = { [1] = 255, [2] = 0, [3] = 0 }, magenta = { [1] = 255, [2] = 0, [3] = 255 } }As you can see in this example: despite "red" already being a built-in color, it can still be customized in the level file, which overrides the built-in color.
local mycount = {trinkets = 0, crewmates = 0, entities = 0, startpoint = nil, FC = 0} -- FC = Failed Checks(mycount is local to that function, and will be returned and then stored to count. So this whole system of creating tables locally and then returning references to them works? Yeah, it does, funny eh? I should probably rewrite that a little bit though.)
The metadata entity is used by Ved to store data that is specific to a certain level. It is a roomtext entity at x=800 y=600, which means it's in room 21,21 (1-indexed). The data is formatted with several levels of separation characters:
| | primary separator (see this as separating different "tables" of data) |
$ | secondary separator (see this as separating different "rows" or "records" inside a "table") |
@ | tertiary separator (see this as separating different "columns" or "properties" inside a "rows"/"records") |
For example, one of the "tables" contains level notes. Each level note is separated by $, and inside a level note, the name and the contents of the note are separated by @.
Accent grave (`) is the escape character; if the real versions of certain characters need to be represented, they are escaped as follows:
` | `g |
| | `p |
$ | `d |
@ | `a |
(newline) | `n |
(2 spaces) | `_ |
The function despecialchars(text) encodes the raw characters to escaped format, undespecialchars(text) decodes escaped characters back to raw characters.
As said, | is the primary separator, which separates the following items:
Description | ≥V | |
---|---|---|
1 | Metadata entity version number | 0? |
2 | Flag names | 2 |
3 | (Reserved) | - |
4 | Level variables, for use by Ved or plugins (Why haven't I documented this out of commit messages) | 3 |
5 | Level notes | 0? |
(more coming soon)
Currently, when you copy a room to the clipboard, it's stored in a comma-separated format. It consists of 1215 values separated by commas, and it's structured as follows (indices start at 1, as in Lua):
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 — 1215 |
Tileset | Tilecol | Platf. bounds x1,y1,x2,y2 | Platv | Enemy bounds x1,y1,x2,y2 | Enemy type | Direct mode | Warp dir | Room name* | 1200 tile numbers |
In the room name, commas are replaced by the character ´ (U+00B4 ACUTE ACCENT).
In this format, entities are not copied. I'm planning to replace this CSV system by an XML format, which will also contain entities. Data in the old format will still be pasteable, and I'll probably also leave in a way to copy as CSV.
Strictly speaking, the buttons parameter accepts a list/table of buttons. For each element, if that element is a string, that string will be displayed as-is. But if possible, it should be an integer, representing one of the built-in buttons. A list of buttons is available as DB, so for example, DB.YES is a "Yes" button. The list is as follows:
Constant | Value | Button label |
---|---|---|
DB.OK | 1 | OK |
DB.CANCEL | 2 | Cancel |
DB.YES | 3 | Yes |
DB.NO | 4 | No |
DB.APPLY | 5 | Apply |
DB.QUIT | 6 | Quit |
DB.DISCARD | 7 | Discard |
DB.SAVE | 8 | Save |
DB.CLOSE | 9 | Close |
DB.LOAD | 10 | Load |
DB.ADVANCED | 11 | Advanced |
DB.LOAD was added in Ved 1.6.0. DB.ADVANCED was added in Ved 1.10.0.
There's also built-in lists of buttons available as DBS, like DBS.YESNO, which stands for {DB.YES, DB.NO}, meaning a Yes and No button.
Constant | Buttons |
---|---|
DBS.OK | OK |
DBS.QUIT | Quit |
DBS.YESNO | Yes, No |
DBS.OKCANCEL | OK, Cancel |
DBS.OKCANCELAPPLY | OK, Cancel, Apply |
DBS.OKCANCELADVANCED | OK, Cancel, Advanced |
DBS.SAVEDISCARDCANCEL | Save, Discard, Cancel |
DBS.YESNOCANCEL | Yes, No, Cancel |
DBS.SAVECANCEL | Save, Cancel |
DBS.LOADCANCEL | Load, Cancel |
DBS.SAVECANCEL and DBS.LOADCANCEL were added in Ved 1.6.0. DBS.OKCANCELADVANCED was added in Ved 1.10.0.
The purpose of the handler function is to take action after closing a dialog. For example, if a question is asked whether the user wants to destroy something, then that should be done if (and only if) the user chooses DB.YES.
The handler is a function that can take up to five arguments:
Dialog handlers as used in Ved's code can be found in the file dialog_uses.lua, starting with dialog.callback. An example handler is the following. Assume the buttons for this dialog are DBS.YESNOCANCEL. In this example, users press Yes if they want a new dialog to be created showing what they entered in a field with the key name, press No if they still want a dialog but don't want to know their input, and press Cancel if they want no new dialog.
function(button, fields) if button == DB.YES then dialog.create("You pressed Yes, and the input with key \"name\" is " .. fields.name) elseif button == DB.NO then dialog.create("You pressed No! Apparently you don't want to know what you entered, but you still want a dialog.") end endDB.CANCEL is not checked, therefore the handler does nothing if that button is pressed.
No-close checkers are similar to handlers, but they are called before the dialog closes. This function can stop the dialog from being closed by returning true, and thus are useful for creating error messages if the user puts in invalid input. They can also be used to not close the dialog if an Apply button is pressed, for example.
The arguments for the no-close checker are the same as for the main handler, except the fourth argument (closing prevented by no-close checker) doesn't exist, which means the fourth argument is the dialog object. If the no-close checker returns true (and thus stops the dialog from closing) the handler will still be called, and its fourth argument will be set to true as well. Here's an example of how a no-close checker can be used to give an error message in case someone enters a value for the field with key inp that is empty or above 20 characters:
function(button, fields) if button == DB.OK and (fields.inp == "" or fields.inp:len() > 20) then dialog.create("Your input must not be empty or longer than 20 characters.") return true end endNow the handler might be defined as follows:
function(button, fields, identifier, notclosed) if notclosed then return end if button == DB.OK then -- Save your fields.inp here. end endYou can find more examples in dialog_uses.lua in Ved, no-close checkers are generally prefixed _validate.
The X and Y positions for the field both start at 0, which is the position the regular dialog text also starts.
These are the different types of input fields:
0 | DF.TEXT | Text input |
1 | DF.DROPDOWN | Dropdown |
2 | DF.LABEL | Plain text label (does not take input) |
3 | DF.CHECKBOX | Checkbox |
4 | DF.RADIOS | Radio button list |
5 | DF.FILES | Files list and directory navigation |
6 | DF.HIDDEN | Hidden field |
The DF. constants were added in Ved 1.5.0.
dialog_uses.lua has some complete forms (but mostly functions that generate forms) under dialog.form. One general-purpose example is dialog.form.simplename, which has a single text field at position 0,1 to be used to fill in a name (for example a name for a new script or note), and dialog.form.simplename_make(default) to generate a similar form but with a value pre-filled.
More information about how the different types work:
Basically, there's two forms: first the simpler one. In the simpler form, you only need a list of items that will appear in the dropdown, and whenever the user selects an item, the value of the input field is set to the text of the option that the user selected. This means what's readable as an option will be passed. You may want to set the default value to an option in the list.
An example: {"drop", 0, 0, 30, "Option A", DF.DROPDOWN, {"Option A", "Option B", "Option C"}}
The width is set to 30 because that's how wide dropdown menus are (currently). If you want a function to be called every time an option is selected in this case, there'd be two more arguments: false (as a filler for the second table) and then the function.
For the second form, let's take the example of a user selecting between percentages, let's say 50%, 100% and 200%. You want to pass this as a number instead, so if the user selects 50%, you want the actual value to be 0.5. When the user does select 50%, the "onchange" function is called, and converts the "50%" into 0.5, and returns that. The second table that was mentioned (the one that converts a value to a displayable "current selection") has this 0.5 background value as a key, and that maps to a value of "50%".
So this is that example:
{ "drop2", 0, 2, 30, 0.5, DF.DROPDOWN, {"50%", "100%", "200%"}, {[0.5] = "50%", [1] = "100%", [2] = "200%"}, function(picked) if picked == "50%" then return 0.5 elseif picked == "100%" then return 1 elseif picked == "200%" then return 2 end end }
As of version 1.5.0, you can use the function generate_dropdown_tables(tuples) to generate these last three arguments. The function takes a table as an argument with key-value tuples as elements (not keys as keys and values as values). It returns the three required arguments for the second form (list of displayed items, converter key-value table and "onchange" function). For example, the previous example could be written more elegantly as follows:
{ "drop2", 0, 2, 30, 0.5, DF.DROPDOWN, generate_dropdown_tables({{0.5, "50%"}, {1, "100%"}, {2, "200%"}}) }
This is just a bit of text that can be displayed anywhere in the dialog you want. It can therefore be used to label other input fields without having to include those labels in the dialog contents.
The "default value" will be used as text, but it can also be a function that returns the text dynamically.
An example for a plain-text label is as follows: {"", 0, 5, 10, "Label", DF.LABEL}
The key is left empty, because it has not much use. But we can't set it to nil, otherwise Lua might think the table ends there. It is positioned on the start of the 6th line. The width is 10 characters, which means it will merely wrap beyond that point. Then the label is just a string, and will be displayed. 2 is the type.
Here's an example of a label that keeps changing: {"", 0, 5, 40, function() return love.math.random() end, DF.LABEL}
This will continuously display a different random number between 0 and 1. Note that the key and the table of fields are passed to the function, but it's not used here.
{"option", 0, 5, 2+font8:getWidth(OPTIONLABEL)/8, true, DF.CHECKBOX}, {"", 2, 5, 40, OPTIONLABEL, DF.LABEL}The default state of this checkbox is checked, since the default value is set to true here. It is followed by a plain text label (type 2), and the label can be clicked as well to toggle the checkbox.
Radio buttons were added in Ved 1.5.0. These function exactly like dropdowns do, the only differences are the type and the way they behave in the GUI. The width argument is not used. You can use the same generate_dropdown_tables function for radio buttons that you can use for dropdowns.
For example, the time format picker in the language dialog works like this:
{ "timeformat", 23, 8, 0, s.new_timeformat, DF.RADIOS, generate_dropdown_tables( {{24, "23:59"}, {12, "11:59pm"}} ) },
The initial value is s.new_timeformat, which is the setting for the time format, which is either 24 or 12. Selecting "23:59" sets the value to 24, selecting "11:59pm" sets the value to 12. (Note that the dialog handler should apply the change, the value that you fill in as 5th argument is merely the initial state of the 'field'.)
The files list type was added in Ved 1.6.0. The default value is the full path to the current directory. It takes 7 more arguments (note that despite me giving each of these arguments names, they don't actually have keys by those names, and this is only to make it easier to understand):
Note that you need a field with the key of "name" to select a file, and you need a checkbox to toggle showing only directories, showing only files that are filtered, or showing hidden files.
For this reason, it is recommended to make a full file selection dialog with dialog.form.files_make() instead. In fact, all code in Ved currently uses that function instead of making the files list manually.
Hidden fields were added in Ved 1.8.5. A hidden field can be used to carry data from dialog creation to dialog submission, without accepting user changes. For example, if you have a confirmation dialog to delete a certain script, you want to still know what script it was when the user presses "Yes". The type of the value can be anything you need, as long as it is not nil (again, Lua limitation, unless we changed dialog fields to have string keys for named arguments). nil values could instead be encoded as having the field be missing altogether, which will have the same effect, because for the handler, fields.the_missing_field would yield nil anyway. Position and width has no meaning for this field, so for example: {"key", 0, 0, 0, "value", DF.HIDDEN}
There is a convenience function to make a form with hidden fields from a table where the keys and values correspond to the field keys and values: dialog.form.hidden_make(values, existing_form). existing_form can be filled in if you already have a form, and simply want to add one or more hidden fields to it.
For example:
dialog.form.hidden_make({script="applebapple"}) or
dialog.form.hidden_make({script="applebapple"}, dialog.form.simplename)
The fourth argument of dialog.new (called buttons above) can be set to one of the following values V:
V | Button 3 | Button 2 | Button 1 |
---|---|---|---|
0 | |||
1 | OK | ||
2 | Quit | ||
3 | Yes | No | |
4 | OK | Cancel | |
5 | OK | Cancel | Apply |
6 | Save | Discard | Cancel |
(6 has been added in Ved 1.3.0)
TODO: Update this for the new input system, for now see input.lua (using newinputsys until the old input system is fully removed)
In short: a simple one-line input can be:
General text input can be started by a single call to startinput(). There's also a function startinputonce(), which can be used if you decide to do it in update/drawing code, but expect me to remove that sooner or later. Input can be stopped (or locked) by calling stopinput(). The text input to the left of the cursor can be found in input, and text to the right of the cursor can be found in input_r. The variable __ (two underscores) contains the text cursor and the text to the right of it. So, to display the input field, you can concatenate input and __ (input .. __).
It's also possible to include text boxes in dialogs, see the above part on fields in dialogs.
If you didn't know already, you can hold F9 to reveal hotkeys.
Since Ved 1.6.1, Ved has had a dedicated function to display any hotkey on the screen if F9 is held down, showhotkey:The following function can be used in button UI elements, to easily add a functioning hotkey to a button. This function will return an anonymous function which returns true if the given hotkey, in combination with a modifier key if specified, is pressed.
Each symbol in the hotkey font, tinynumbersfont, is actually mapped to one specific character, and is case-sensitive.
What this means is that, for example, a is Alt, but A is just the letter A. In fact, 0-9 and A-Z (uppercase) are all just themselves, along with a lot of other characters. You can also easily combine symbols togetter like so: aS would simply be Alt+S, and show up as such accordingly.
In the following list of usable symbols, a character is simply itself if it has no text saying otherwise:
Ved 1.8.4 introduced some pretty major changes in the code, which probably broke a lot of plugins. I thought it might be a good idea to finally start documenting breaking "API" changes at this point, instead of having everyone figure them out as commits go by and new versions get released. So this log starts at 1.8.4 and is intended to give an overview of changes that are likely to break plugins.
Depending on how likely I think changes are to break plugins and depending on the weather, changes may or may not get listed here. I'll probably list a lot that isn't necessary, and on the other hand there will probably be some changes I didn't imagine would affect anyone but turn out they do (especially when plugins find-and-replace too large blocks of code or something).
Numbers at the start of list items indicate relevant pre-versions in which the changes were introduced.