Skip to content

Utilities and Debugging

vitellaryjr edited this page May 10, 2022 · 39 revisions

Relevant files: utils.lua, lib folder, vars.lua

External Libraries

Kristal implements the Timer, Class, and Vector-light libraries from hump libraries for use within mods. Kristal does make some slight alterations, however; this section will detail these changes.

As the Objects page said previously, Kristal makes heavy usage of the Class library. Kristal also adds functionality to this library; in addition to the special methods of extending other files already detailed in the Objects page, there are functions all Classes can use:

includes(other): Returns whether a class extends other. For example, Sprite:includes(Object) will return true. Classes that extend an extension will also be considered to extend their original class; if something were to extend Sprite, it will still return true for includes(Object).

Timer is implemented as an Object instead of a simple class; this means that you create new instances of timers through Timer() instead of Timer.new(), and you need to parent the Timer to an Object for it to update. This also means you can't use the global Timer variable as a timer instance itself, like the hump documentation says; instead, you'll likely want to use existing timers already parented to objects. Game.battle.timer Game.world.timer, and Game.stage.timer are examples of easily accessible Timer objects.

In addition to this change, Kristal's implementation of Timer adds and changes functions. These edits are:

  • Timer:everyInstant(delay, func, count) is a function added that behaves similarly to Timer:every(), except it also calls func() immediately upon being called, instead of waiting for delay to pass. This first call does not subtract from count if it is provided; for example, Timer:everyInstant(0.5, function() print("called") end, 2) will print called immediately, then again after 0.5 and 1 seconds, for a total of 3 times.
  • The implementation of the wait() function for Timer:script(func) is altered slightly; in addition to calling it with a number to wait a specified amount of seconds, you can call it with no arguments, which will cause the game to wait for a single frame, regardless of framerate.

This library's functions can be utilized through the global Vector table. Kristal does not change the library.

Kristal implements an Easing table containing functions to ease between 2 values. Each function can be referred to in 2 ways: with lower camel case, e.g. Ease.inOutQuad(), or snake case with dashes, e.g. Ease["in-out-quad"](). The values each function takes, in order, are:

  • 1: The elapsed time since starting the easing.
  • 2: The starting value of the easing.
  • 3: How much the value should change.
  • 4: The total duration of the easing.

For example, to find the eased value after 2 seconds of a 5 second long transformation from the value 10 to the value 16, you would call Ease.easeType(2, 10, 6, 5). 2 refers to how long the transformation has been occurring, 10 refers to the starting value of the transformation, 6 refers to how much the value 10 needs to change by to reach its final value, and 5 is how long the transformation will last in total.

A visual representation of all ease types is available here. For an alternative method of using easing, more consistent with other code in Kristal, see Utils.ease() below.

Utility Functions

Kristal contains a Utils library with many functions that can be used to assist coding. The rest of this page details functions defined by Kristal. All of the following functions in this section are contained within the global Utils table, and can be called universally.

copy(table, deep): Returns a copy of a table. If deep is true, all tables within the table will be copies of themselves as well.
dump(table): Returns a string detailing the contents of a table. Useful for debugging.
split(str, sep, remove_empty): Splits str with sep as a separator, and returns a table of the new strings. If remove_empty is true, empty strings ("") won't be added to the table.
join(tbl, sep, start, len): Joins a table of strings together into 1 string. tbl is the table of strings to combine, sep is an optional string that will be put between each string if defined, start is an optional number defining the index to start with, and len is an optional number defining how many values to combine before stopping.
titleCase(str): Returns string str converted to Title Case.
hook(target, name, func): Hooks a function, allowing you to add code to existing functions. target is the table that holds the function, name is the name of the function you're hooking, and func is the new function that you're replacing the old with, passing in the original function as the first argument. For example, if you want to hook Object.getRelativePos, you would do something like:

Utils.hook(Object, "getRelativePos", function(orig, object, x, y, other)
  -- the original arguments of the function are x, y, and other
  -- but since the function comes from a class, that means we need to pass in
  -- the object instance into the original function as well

  -- code here gets executed before the original

  -- since getRelativePos returns something, your code should also return something
  -- usually you'd want to return the results that the original function returns
  -- so you can keep the values the original returns as variables, then return them later
  -- getRelativePos returns 2 values, so we'll want to store both of them
  local orig_x, orig_y = orig(object, x, y, other)

  -- code here gets executed after the original

  -- return the result gotten earlier
  return orig_x, orig_y
end)

equal(a, b, deep): If a and b are tables, the function returns whether their contents are the same (and if deep is true, it checks this for all tables within the table as well). Otherwise, it simply returns whether a and b are equal.
hslToRgb(h, s, l): Returns 3 values between 0 and 1, converting HSL values between 0 and 1 to RGB.
rgbToHsl(r, g, b): Returns 3 values between 0 and 1, converting RGB values between 0 and 1 to HSL.
hexToRgb(hex, value): Returns a table of values between 0 and 1, converting a hex code ("#" required) to RGB. If value is provided, that will be passed in as the 4th value in the table; otherwise, the 4th value of the table will be 1.
rgbToHex(rgb): Returns a hex value string ("#" included), converting a table of RGB values to a hex code.
merge(tbl, other, deep): Merges 2 tables, returning a new one containing the values from both. If deep is true and it finds that both tables have a table value at the same index, it will merge those tables as well.
removeFromTable(tbl, value): Removes val from a numerically indexed table.
containsValue(tbl, value): Returns whether a numerically indexed table contains val.
round(value, to): Returns the value rounded to the nearest integer, or, if to is provided, to the nearest multiple of to.
clamp(value, min, max): Returns value if it is between min and max; otherwise, returns min if value is less than it, or max if value is greater than it.
absClamp(value, min, max): Returns value if its absolute value is between min and max; otherwise, return min with the sign of value if the absolute value of value is less than it, or max with the sign of value if the absolute value of value is greater than it. min and max must be positive numbers.
sign(value): Returns -1, 0, or 1, based on the polarity of the value.
approach(value, target, amount): If value is less than target, returns value + amount, otherwise it returns value - amount. amount should always be a positive number. If value ± amount goes past target, it returns target instead. In simple terms, it moves value towards target by amount, not allowing it to exceed it.
lerp(a, b, t, oob): Returns a value between a and b, by 100*t%. For example, if t is 0.5, the function will return the value halfway between a and b. If a and b are both tables with the same number of values, return a new table with each value being lerped between a and b. If oob is not true, then t will be clamped between 0 and 1.
lerpPoint(x1, y1, x2, y2, t, oob): Returns coordinates by lerping between the provided points.
ease(a, b, t, mode): Returns a value between a and b, eased by 100*t%. The easing methods available are specified above.
clampMap(value, min_a, max_a, min_b, max_b, mode): Gets the ratio value is at between min_a and max_a, and returns a new value with the same ratio between min_b and max_b. For example, Utils.clampMap(5, 0,10, 20,40) would give 30, since 5 is 50% between 0 and 10, so it returns the value 50% between 20 and 40. mode is an optional argument referring to an easing method; if not specified, the function assumes linear.
between(val, a, b): Returns whether val is between the numbers a and b.
mergeColor(start_color, end_color, amount): Lerps between start_color and end_color by amount, where start_color and end_color are tables with 3 or 4 values defining RGB(A), and amount is a number between 0 and 1.
random(): Returns a random decimal between 0 and 1.
random(val): Returns a random decimal between 0 and val.
random(a, b): Returns a random decimal between a and b.
random(a, b, round): Returns a random decimal between a and b, rounded to the nearest multiple of round.
randomSign(): Returns either -1 or 1 randomly.
randomAxis(): Returns a table of 2 values, representing a random polarity. The table can be either {1,0}, {0,1}, {-1,0}, or {0,-1}.
filter(tbl, filter): Takes a table and a function(value) that should return a bool. Returns a new table, containing the values from tbl that filter(value) returns true for.
pick(tbl, filter): Returns a random value from tbl. If filter is provided, returns a random value from tbl filtered through filter.
pickMultiple(tbl, amount, filter): Returns multiple random values from tbl, not returning the same value more than once. If filter is provided, it filters the table before choosing.
shuffle(tbl): Returns a new table with the values of tbl in a random order.
reverse(tbl): Returns a new table with the values of tbl in reversed order.
angle(x1, y1, x2, y2): Return the angle, in radians, between the points specified.
angle(obj1, obj2): Returns the angle, in radians, between two objects.
angleDiff(a, b): Returns the difference between 2 angles, accounting for wrap-around. Will be a value between -pi and pi.
approachAngle(value, target, amount): Similar to approach(), but always approaches accounting for wrapping around, opting to go for the shortest distance.
dist(x1, y1, x2, y2): Returns the distance between 2 points.
startsWith(value, prefix): If value and prefix are strings, it returns whether value starts with the string prefix, followed by the substring of value with that prefix removed. If value and prefix are numerically indexed tables, it returns whether the first values of value are equal to the values in prefix, followed by a copy of value with the values in prefix removed.
tableLength(tbl): Returns how many indexes are defined for a table, including non-numerical indexes.
facingFromAngle(angle): Returns right, down, left or up depending on the direction angle faces. angle is an angle measure, in radians.
isFacingAngle(facing, angle): Returns whether the specified facing direction matches the direction of the specified angle. facing is a string being either right, down, left or up, and angle is an angle measure, in radians.

Global Variables

Outside of the Utils library, Kristal also has several global variables that can be used to help with code. These variables always exist, and can be read from anywhere. These variables are:

SCREEN_WIDTH, SCREEN_HEIGHT: Refers to the current window size.
SCREEN_CANVAS: A reference to the canvas used to render the entire screen.
DT: The time that has elapsed since the last game update. Usually approximately equal to the framerate.
DTMULT: DT multiplied by 30 (the default FPS of Deltarune). At 30fps, this value will be approximately equal to 1. When changing values over the course of time, the change should be multiplied by DTMULT so that it's consistent across different framerates.
COLORS: A table of default colors available. Each index contains a table of 4 values, referring to R, G, B, and alpha of a color. The available colors are aqua, black, blue, dkgray, fuchsia, gray, green, lime, ltgray, maroon, navy, olive, orange, purple, red, silver, teal, white, and yellow.
BATTLE_LAYERS: A table of constants that refer to the layer values of objects in battles. The indexes, from bottom to top, are:

  • bottom: -1000
  • below_battlers: -200
  • battlers: -100
  • above_battlers, below_ui: 0
  • ui: 100
  • above_ui, below_arena: 200
  • arena: 300
  • above_arena, below_soul: 400
  • soul: 500
  • above_soul, below_bullets: 600
  • bullets: 700
  • above_bullets: 800
  • top: 1000

WORLD_LAYERS: A table of constants that refer to the layer values of objects in the overworld. The indexes, from bottom to top, are:

  • bottom: -100
  • below_soul: 100
  • soul: 200
  • above_soul, below_bullets: 300
  • bullets: 400
  • above_bullets, below_ui: 500
  • ui: 600
  • above_ui, below_textbox: 700
  • textbox: 800
  • above_textbox: 900
  • top: 1000

SHOP_LAYERS: A table of constants that refer to the layer values of objects in shops. The indexes, from bottom to top, are:

  • background: -100
  • below_shopkeeper: 100
  • shopkeeper: 200
  • above_shopkeeper, `below_boxes: 300
  • cover: 400
  • large_box: 450
  • left_box: 500
  • info_box: 550
  • right_box: 600
  • above_boxes, below_dialogue: 700
  • dialogue: 800
  • above_dialogue: 900
  • top: 1000

PALETTE: A table of colors used throughout the game. The colors defined are as follows:

  • action_strip: Used by the Action Bar during battles.

DEBUG_RENDER: A boolean toggled by a debug keybind that enables showing hitboxes of bullets, the player, and the arena. Can be used in code to render things for debug purposes.

Debug Shortcuts

Enabling Debug Hotkeys in the main menu enables several debug keybinds to use for debugging and coding purposes. These include:

Common

Ctrl+M: Pauses or resumes music.
Ctrl+R: Makes a quicksave and reloads the mod, allowing you to reload any changes to code or assets.
Ctrl+Shift+R: Reloads the mod without making a quicksave.
F1: Toggles an FPS display.
F2: Toggles VSync.
F3: Prints performance information to the console.
F5: Toggles speed-up for the game.
F6: Toggles DEBUG_RENDER, a global variable that can be checked in code. By default, DEBUG_RENDER is checked to render text describing the current gamestate and draw hitboxes for most objects.

Battle

Ctrl+H: Heals every party member to full health.
Ctrl+K: Sets current TP to 200%.
Ctrl+F: Ends the current wave if one is active.
Ctrl+B: Kills every party member.
Ctrl+J: Shatters the player's soul if a wave is active.

Debug Menus

Beyond the previously listed shortcuts, Kristal also implements some larger, more general debug menus that can be used anywhere in a mod. By default, the shortcuts to open these are: ` for the console; Shift+` for the debug command menu; and Ctrl+O for the object selection menu, though these can all be rebinded in the Controls menu.

Console

The console allows the user to input any Lua code they want and run it. Additionally, if the user prints a value, it will appear directly in the console. This can be used to quickly see the values of variables while debugging, or perform arbitrary code on the fly to test something.

Debug command menu

This menu contains a list of commands that can run code that can be helpful for testing mods, such as starting an arbitrary cutscene or entering an encounter, or blowing up the player (helpful). Additionally, it's possible for mods to define their own debug commands, by using functions within the Kristal.DebugSystem class. It's recommended to register debug commands in Mod:registerDebugOptions() (see mod.lua for more detail). The following are functions that can be used to register debug commands:

registerOption(menu, name, description, func, state): Registers a debug command. menu is the ID of the submenu the command should be in ("main" refers to the main debug command menu), name is a string defining the name of the command that is displayed, description is a string defining the description that appears at the bottom of the screen when hovering over the command, func is the function that will be run when the command is selected, and state is the Game state that the command is allowed to be used in (being either OVERWORLD, BATTLE, SHOP, GAMEOVER, or ALL). name and description can alternatively be functions that return strings; this can be used to make names or descriptions that change depending on certain conditions.
registerMenu(menu, name): Registers a submenu for debug commands. menu is the ID of the submenu to create, and name is either a string or a function that returns a string, defining the display name of the menu.
enterMenu(menu, index, skip_history): Enters a submenu. menu is a string referring to the ID of the submenu to enter, index is an optional number what option index the player should start on (defaulting to 1), and skip_history is an optional boolean, that, if true, will make it so exiting this menu puts the player at the menu that preceded the current submenu. This function should be used to set up debug commands that enter submenus so they can be accessed.
appendBool(text, bool): Returns a string, combining the given text string with (ON) or (OFF) depending on the state of bool.

Object selection

This mode allows the user to click on objects to select them, which can display certain information about objects. Clicking and dragging an object will also move its position. The information that is displayed for an object is customizable, as well; see Object:getDebugInfo() for more detail. Additionally, when selecting an object, the debug console gains more features; referencing the variables selected or _ will both refer to the currently selected object.

Clone this wiki locally