Skip to content

Utilities and Debugging

vitellaryjr edited this page Apr 8, 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(string, sep, remove_empty): Splits string 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.
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.
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.
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.
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.
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.
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.

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. The indexes relevant to battles, 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

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.

Debugging

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

Common

M: Pauses or resumes music.
`: Opens a console that allows you to execute Lua code.
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.
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

H: Heals every party member to full health.
K: Sets current TP to 200%.
F: Ends the current wave if one is active.
B: Kills every party member.

Clone this wiki locally