Luka's Scripting Utilities
by Luka S.J.A pack full of various scripting utilities to ease your development!
Overview
Luka's Scripting Utilities (LUTS) is a foundation library of small,
reusable utilities that the rest of Luka's plugins build on top of. It extends
the core RGSS/mkxp-z classes (Sprite, Viewport, Bitmap, Color,
Tone, …) and the Ruby built-ins (Array, Hash, String, Numeric, …)
with convenience helpers, and it ships a handful of standalone systems:
animation, shaders, a sprite manager, mouse input, a PBS compiler, and barcode
/ QR-code generators.
Most code lives under the LUTS namespace. The reusable building blocks are
mixin concerns under LUTS::Concerns:
LUTS::Concerns::Animatable — tween any property towards a target valueLUTS::Concerns::Floatable — track sub-pixel float values for smooth motionLUTS::Concerns::Shaderable — attach and manage GLSL shadersLUTS::Concerns::BlockConstructor — accept a configuration block innewLUTS::QuickAnimatable — declarative, array-driven sprite animations
These are already mixed into the standard classes for you (Sprite,
Viewport, Color, Tone, Rect), so the helpers documented in the rest of
these pages are available out of the box.
How the documentation is organised
- Core Extensions — the helpers added to Ruby and RGSS base classes.
- Components — the animation, float, shader and helper concerns.
- Sprites — the
Sprites::*family and theSpriteHashmanager. - Drawing & Bitmaps — bitmap drawing helpers and the polygon renderer.
- Mouse Input — the
Mousemodule and its object extensions. - Code Generators — UPC-A barcodes and QR codes rendered to bitmaps.
- Utilities — logging, math helpers, error messages and the 9-slice window.
- System — the PBS compiler and plugin-manager helpers.
another plugin requires it, but every utility documented here is free to use in
your own scripts.
Ruby Object Extensions
LUTS adds a set of convenience methods to the Ruby built-in classes. The most
widely used are the blank? / present? predicates, which are defined
consistently across Array, Hash, String, Numeric, Symbol and
NilClass so you can test "emptiness" uniformly regardless of type.
Array
array.swap_at(index1, index2) swaps the values at two indexesarray.to_last(val) moves a value to the end of the array (removing it from its current position first)array.last?(index) checks whether the given index is the last indexarray.string_include?(val) checks whether any string element is contained within the given stringarray.value(index) fetches the element at the given indexarray.blank? true when the array has no elementsarray.present? true when the array has any elements
Hash
hash.value(key) returns the value for the given keyhash.merge_many(*hashes) merges multiple hashes into selfhash.blank? true when the hash has no keyshash.present? true when the hash has any keys
String
Constants & casing
string.constantize resolves the string to a Ruby constant (object)string.safe_constantize resolves to a constant, ornilif it does not existstring.capitalize capitalises the first letterstring.camelize converts anunder_scoredstring toCamelCasestring.underscore converts aCamelCasedstring tosnake_casestring.demodulize strips leading module namespaces from a constant namestring.preposition returns the indefinite article ("a"or"an") for the stringstring.blank? true when the string is empty or only whitespacestring.present? true when the string has non-whitespace content
Console colour tags
These wrap the string in colour markup for console output:
Numeric
number.lerp(inverse: false) scales the number for frame-rate-independent interpolationnumber.blank? true when the value is zeronumber.present? true when the value is non-zero
Frame-count helpers convert a count into the equivalent number of 60-FPS frames
(singular forms are aliases of the plural):
number.minute /number.minutes number.hour /number.hours number.day /number.days
# wait two in-game minutes (in frames)
2.minutes # => 7200
Symbol & NilClass
For interface consistency with the other types:
symbol.blank? alwaysfalse,symbol.present? alwaystruenil.blank? alwaystrue,nil.present? alwaysfalse
Graphics Object Extensions
LUTS reopens the core graphics classes — Color, Tone, Rect, Sprite,
Viewport and Graphics — to add helpers and to mix in the animation, float
and shader concerns. Because the concerns are already included, every object of
these types can be animated, given a shader, or queried for its on-screen
position without any extra setup.
Color
Includes
Color.blank a fully transparent black (all components zero)Color.dark_gray a dark gray preset (64, 64, 64)color.darken(amt = 0.2) returns a darkened copy of the colourcolor.blank? true when every component (R, G, B, A) is zerocolor.present? true when any component is non-zerocolor.to_vec3 normalised[r, g, b]floats (0.0–1.0), e.g. for shader uniformscolor.to_vec4 normalised[r, g, b, a]floats (0.0–1.0)
Tone
Includes
tone.lookup_table a memoisedTone::LookupTablefor fast per-channel pixel transformstone.all the average of the red, green and blue channelstone.all = val sets all three colour channels at once
The nested
grayscale mix for a tone (using the standard YUV luma weights) so it can
transform(r, g, b) pixels efficiently — this is what Bitmap#apply_tone
uses.
Rect
Includes
and tracked as floats.
Sprite
Includes
sprite.in_viewport? true when the sprite is visible within its viewport (64-pixel margin)sprite.apparent_x /sprite.apparent_y on-screen position accounting for origin and zoomsprite.apparent_width /sprite.apparent_height on-screen size accounting for zoom
(See the Sprites section for the much larger Sprites::Base subclass.)
Viewport
Includes
and
geometry accessors.
Tagging
Viewport.get_by_tag(tag) all viewports carrying the given tagviewport.tag = value adds a tag to the viewportviewport.tags the list of assigned tagsviewport.tag?(tag) whether the viewport carries a tag
Sprites & compositing
viewport.sprites all non-disposed sprites in the viewport, sorted by zviewport.flatten renders all visible sprites into a singleBitmapviewport.reset_color clears any applied colour (sets it to blank)
Geometry & colour
Direct passthroughs to the underlying rect / colour so you don't have to reach
through .rect:
= writer.
Graphics
Adds a global animation driver used by the Animatable concern:
Graphics.animate(duration, &block) runs the block, then plays the registered animations of every cached object acrossdurationframes, cleaning the cache up afterwardsGraphics.target_object_cache the list of objects queued for animation
Graphics.animate(20) do
@sprite.animate(x: 320, opacity: 255)
@other.animate(zoom_x: 2.0)
end
Filesystem & Environment
Helpers for working with files, directories and well-known system paths.
File
File.safe_data?(file) safely checks whether a.rxdatafile exists and is loadable (it attempts the load and returns a boolean rather than raising)
Dir
Dir.create(path) recursively creates every directory needed for a pathDir.all_dirs(path) walks a directory tree and returns every directory found within itDir.delete_all(path) deletes all files in a directory and its subdirectories (works on non-empty directories)
Env
Env resolves well-known Windows directories by GUID and escapes reserved
filesystem characters.
Env.path(type) resolves a known directory path from its key (e.g.'DESKTOP','DOCUMENTS','DOWNLOADS')Env.directory the current working directory of the gameEnv.guid_to_hex(string) converts a GUID string to the hex array format the OS expectsEnv.char_esc(string) escapes reserved filesystem characters in placeEnv.char_dsc(string) restores previously escaped characters in place
Known path keys (COMMON_PATHS)
CAMERA_ROLL, START_MENU, DESKTOP, DOCUMENTS, DOWNLOADS, HOME,
MUSIC, PICTURES, SAVED_GAMES, SCREENSHOTS, VIDEOS, LOCAL,
LOCALLOW, ROAMING, PROGRAM_DATA, PROGRAM_FILES_X64,
PROGRAM_FILES_X86, COMMON_FILES, PUBLIC.
desktop = Env.path('DESKTOP')
Dir.create(desktop + '/MyGame/exports/')
The reserved characters handled by char_esc / char_dsc are
\ / : * ? ' < > |.
Animatable
LUTS::Concerns::Animatable lets you tween any numeric property of an object
towards a target value. It is already included in Sprite, Viewport,
Color, Tone and Rect, so you can animate those directly. To make your own
class animatable, include LUTS::Concerns::Animatable.
How it works
You register the target values you want with
object is queued in Graphics.target_object_cache and each registered property
is advanced every frame (using frame-rate-independent lerp interpolation)
until the duration elapses.
Graphics.animate(30) do # tween over 30 frames
@sprite.animate(x: 400, y: 120, opacity: 0)
end
API
object.animate(options) registers properties → target values to tween (a Hash like{ x: 400, opacity: 0 }). Ignored if the object is already animating.object.animating? whether any animation targets are currently registeredobject.anim_target the registered targets (each stored with its interpolation state)object.play_target_animation(duration) advances every registered property one step towards its target overdurationframes — normally called for you byGraphics.animateobject.clear_anim_target clears all registered animation values
QuickAnimatable
LUTS::QuickAnimatable is a higher-level, declarative layer for sprite-group
animations. Include it in a class that exposes sprites, viewport and
update, then describe the animation as an array of steps.
Each step is [duration, { sprite_key => { attribute => value } }]:
class MyScene
include LUTS::QuickAnimatable
def quick_animation_array
[
[20, { logo: { opacity: 255, y: 100 } }],
[10, { logo: { zoom_x: 1.2, zoom_y: 1.2 } }]
]
end
end
play_quick_animation plays every step inquick_animation_arrayin sequence, callinganimateon the named spritesquick_animation_array (you implement this) returns the list of animation steps; raisesNotImplementedErrorif missing
The concern validates at runtime that the host class provides sprites,
viewport and update before playing.
Floatable & Shaderable
Floatable
LUTS::Concerns::Floatable adds sub-pixel float tracking to an object. RGSS
properties like x, y and opacity are integers, which makes slow or
fractional movement stutter. Floatable keeps a parallel set of float values
and writes the rounded result back to the object.
It is already included in Sprite, Viewport and Rect. To add it yourself,
include LUTS::Concerns::Floatable.
object.float returns the object'sFloatValueswrapper (created on first use)
FloatValues
FloatValues tracks these properties as floats and pushes each change back to
the wrapped object when it responds to the matching setter:
Each has a getter and a setter (e.g. float.x / float.x = 1.5).
@sprite.float.x += 0.4 # accumulates; the sprite's integer x updates when it crosses a pixel
Shaderable
LUTS::Concerns::Shaderable manages a stack of GLSL shaders on an object. It is
already included in Sprite and Viewport (both of which expose a writable
shaders array).
object.add_shader(key, properties = {}) adds a shader — pass aSymbolkey to load a newShader(with optional uniform properties), or pass an existingShaderinstanceobject.get_shader(key) fetches an applied shader by its key symbol or by array indexobject.remove_shader(key) removes a shader by key symbol, index, or instanceobject.dispose_shaders disposes every applied shader and clears the stack
@sprite.add_shader(:wave, intensity: 0.5)
@sprite.get_shader(:wave)
@sprite.remove_shader(:wave)
The Shader class
Shader.new(key, properties = {}) loads the GLSL file at
Data/Shaders/{key}.glsl and applies the given uniform properties. Uniform
values may be bitmaps, strings, or numeric vectors of 1–4 components (so a
Color#to_vec4 works directly). It raises Shader::ShaderError if the shader
file does not exist.
shader.key the shader's identifier symbolshader.dispose /shader.disposed? standard disposal lifecycle
BlockConstructor
LUTS::Concerns::BlockConstructor lets a class accept a configuration block in
its constructor — a built-in alternative to .tap. After the normal
initialisation runs, the new instance is yielded to the block.
Viewport.new(0, 0, 640, 480) do |vp|
vp.z = 9999
vp.tag = :overlay
end
Viewport already includes this concern; include
LUTS::Concerns::BlockConstructor in your own class to gain the behaviour.
Helper Classes
Two small base classes used throughout LUTS for declaring data and deferring
callbacks.
AttributeContainer
AttributeContainer is a base class for objects that declare a fixed set of
attributes with default values. Subclass it and register attributes; each one
gets a reader, a writer, and — when the default is a boolean — a ? predicate.
class Settings < AttributeContainer
with_attribute :volume, default: 100
with_attribute :muted, default: false
end
s = Settings.new
s.volume # => 100
s.muted? # => false (predicate, because the default is boolean)
s.volume = 50
SubClass.with_attribute(key, default: nil) declares an attribute (and its predicate, for booleans)SubClass.attribute_list the registered attributes mapped to their defaultsSubClass.new initialises every attribute to its default value
Attribute definitions are inherited by further subclasses.
CallbackWrapper
CallbackWrapper stores a block and executes it later in an isolated context,
with keyword params exposed inside the block as instance variables.
cb = CallbackWrapper.with_params(name: "Pikachu") { puts @name }
cb.execute # prints "Pikachu"
# equivalent long form:
cb = CallbackWrapper.new { puts @name }
cb.set_params(name: "Pikachu")
cb.execute
CallbackWrapper.with_params(**kwargs, &block) builds a wrapper and immediately sets its paramswrapper.set_params(**kwargs) exposes the given keywords as instance variables in the block context (chainable — returns self)wrapper.execute runs the block in the isolated context and returns its result (ornilif no block was given)
Sprites::Base
Sprites::Base is the workhorse sprite class for LUTS-based plugins. It
subclasses the core Sprite (so it inherits the Animatable, Floatable and
Shaderable behaviour) and layers on a large toolkit of bitmap, colour and
positioning helpers.
sprite = Sprites::Base.new(viewport) do |s|
s.set_bitmap("Graphics/Pictures/logo")
s.center!(snap: true)
end
Sprites::Base.new(viewport, &block) creates the sprite, seeds its default
attributes, and yields itself to an optional configuration block.
Extra attributes
Beyond the standard sprite properties, Sprites::Base carries a set of
free-use accessors that the various plugins lean on: direction, speed,
toggle, end_x, end_y, param, skew_d, ex, ey, zx, zy, dx,
dy, finished, plus the read-only stored_bitmap. Call
to reset them.
Dimensions
sprite.width /sprite.width = val source-rect widthsprite.height /sprite.height = val source-rect heightsprite.zoom /sprite.zoom = val uniform zoom on both axes
Bitmaps
sprite.set_bitmap(bmp) set the bitmap from a path or a bitmap objectsprite.create_rect(width, height, color) set the bitmap to a solid colour rectsprite.full_rect(color) fill the whole screen with a coloursprite.blank_screen set a blank bitmap the size of the viewportsprite.snap_screen capture a screenshot as the bitmapsprite.stretch_screen(path) draw a bitmap stretched across the whole screensprite.memorize_bitmap(bmp = nil) remember the current (or given) bitmapsprite.restore_bitmap restore the memorised bitmapsprite.online_bitmap(url) download and apply a bitmap from a URLsprite.mask(mask = nil, ox: 0, oy: 0) mask the bitmap with another bitmapsprite.text_sprite(width = viewport.width, height = viewport.height) create a bitmap with the system font applied, ready for text
Colour effects
sprite.alpha /sprite.alpha = val the colour's alpha channelsprite.swap_colors(map) swap the colour palette using a colour-map bitmapsprite.avg_color(freq: 2) the average colour of the bitmap (sampling everyfreqpixels)sprite.blur blur the spritesprite.outline(color) draw an outlinesprite.colorize(color, amount: 255) tint the solid pixels byamount(0–255)sprite.glow(color, keep: true) draw a glow;keep: falsedrops the original bitmap
Positioning
sprite.center the[x, y]of the sprite's centre pointsprite.center!(snap: false) centre the sprite on itself, or on the viewport withsnap: truesprite.bottom the[x, y]of the bottom-centre pointsprite.bottom! anchor the sprite to its bottom centresprite.anchor(type) set the origin to a named anchor
Anchor types: :top_left, :top_middle, :top_right, :middle_left,
:middle, :middle_right, :bottom_left, :bottom_middle, :bottom_right.
Special Sprites
Several ready-made Sprites::Base subclasses cover common animated-sprite
needs. Each one renders itself when you call update every frame.
Sprites::Scrolling
A sprite whose bitmap scrolls continuously — useful for backgrounds, conveyor
belts and parallax. Optionally pulses its opacity while scrolling.
scrolling.set_bitmap(path, vertical: false, pulse: false, speed: @speed) set the scrolling bitmap (returns self for chaining)scrolling.update advance the scroll one frame
Accessors: speed, direction, vertical, pulse, min_o, max_o,
current_x, current_y.
bg = Sprites::Scrolling.new(viewport).set_bitmap("clouds", speed: 1.5)
# in the update loop:
bg.update
Sprites::Rainbow
A sprite that cycles its hue over time for a shifting rainbow effect.
rainbow.set_bitmap(path, speed: 1) set the bitmap and the per-frame hue-shift speedrainbow.update advance the hue one frame
Sprites::Sheet
A spritesheet animator that steps through evenly divided frames of a sheet.
sheet.set_bitmap(file, frames: 1, vertical: false, speed: @speed) load a sheet offramesframes (laid out horizontally, or vertically withvertical: true);speedis the number of update ticks per framesheet.update advance the animation one ticksheet.cur_frame the current frame index (read-only);sheet.speed ticks per frame
AnimatedPlane
AnimatedPlane is a Plane subclass that adds end_x / end_y accessors so a
scrolling plane's target offset can be tracked by the animation system.
SpriteHash
SpriteHash is a manager that owns a keyed collection of sprites sharing one
viewport. It handles construction, iteration, bulk updates and disposal, so a
scene can keep all its sprites in a single object.
@sprites = SpriteHash.new(viewport)
@sprites.add(:logo, bitmap: "Graphics/Pictures/logo")
@sprites.add(:bar, type: :Scrolling) do |s|
s.set_bitmap("loadbar", speed: 2)
end
# every frame:
@sprites.update
# when done:
@sprites.dispose
Adding sprites
hash.add(key, options = {}, &block) build and store a sprite underkey, yielding it to the block. Recognised options::type aSprites::*type symbol (e.g.:Scrolling,:Rainbow,:Sheet):class an explicit sprite class (overrides:type):object a pre-built sprite object to adopt:bitmap a path string, or a hash with a:filekey plus extra options
hash.add_raw(key, object, &block) store a sprite instantiated elsewhere
Access & iteration
hash[key] fetch a sprite by keyhash.keys all keys ·hash.key?(key) whether a key existshash.each { |key, sprite| } iterate key/sprite pairshash.each_sprite { |sprite| } iterate spriteshash.each_key { |key| } iterate keyshash.select { |key, sprite| } /hash.reject { |key, sprite| } filter
Bulk operations
hash.update update every sprite in the collectionhash.set(options = {}) assign an attribute on every sprite (e.g.set(opacity: 0))hash.viewport = vp reassign the viewport across all spriteshash.dispose(options = {}) dispose all sprites; supports:only/:exceptkey filtershash.disposed? whether the collection is empty
Bitmap helpers
Class methods for resolving bitmaps used internally and available directly:
SpriteHash.bitmap(path) resolve a bitmap from a path or any bitmap-able objectSpriteHash.online_bitmap(url) download and return a bitmap (nilif the downloaded file is unsafe)
Bitmap Drawing
Sprites::Bitmap is a Bitmap subclass with higher-level drawing helpers for
text, shapes, masking and colour work. It can be created with the usual bitmap
arguments (a size or a file path) and accepts a configuration block.
bmp = Sprites::Bitmap.new("Graphics/Pictures/panel")
bmp.render_text("Hello", x: 8, y: 8, base: Color.white, outline: true)
It tracks the path it was loaded from (
Text
bitmap.set_font(name:, size:, bold: false) set the font for subsequent text renderingbitmap.render_text(text, x:, y:, align: :left, base: Color.white, shadow: Color.dark_gray, outline: false) draw a single line of text with shadow / optional outline;alignis:left,:centeror:rightbitmap.render_text_ex(text, x:, y:, width:, lines:, line_height: 32, base: Color.white, shadow: Color.dark_gray) draw word-wrapped text within a bounded box oflineslines
Shapes
bitmap.draw_circle(color, radius:, hollow: false) draw a filled (or, withhollow: true, outlined) circle
Colour & masking
bitmap.mask!(mask = nil, offset_x: 0, offset_y: 0) apply a mask using the alpha channel of another image — the mask may be aBitmap, aSprite, or a file-pathStringbitmap.swap_colors(bmp) remap colours using a map bitmap whose first row lists source colours and second row lists target coloursbitmap.apply_tone(tone) apply aTonedirectly to the pixel data (via the tone's lookup table)bitmap.tolerance?(pixel, target) whether a pixel matches a target colour within tolerancebitmap.finished? alwaystrue(bitmaps have no inherent animation; provided for interface parity)
Polygon rendering
Bitmaps::Polygon is a Bitmap subclass that draws a filled polygon from a
list of vertices using an optimised scanline fill. Create it with a size, add
vertices, then render.
poly = Bitmaps::Polygon.new(128, 128)
5.times { |i| poly.calc_vertex(percent: 1.0, angle: i * 72) } # a pentagon
poly.render(Color.new(255, 80, 80))
polygon.add_vertex(x:, y:) add a vertex at an absolute pointpolygon.calc_vertex(percent:, angle:) add a vertex by polar coordinates —percentof the bitmap radius atangledegrees from centrepolygon.render(color = Color.black) fill the polygon (needs at least 3 vertices)
Mouse Input
The Mouse module wraps mkxp-z mouse handling into a friendly API, and extends
Sprite, Viewport and Rect with hit-testing and dragging helpers. A press
counts as a click when it is released within Mouse::CLICK_TIMEOUT (0.5 s).
All button arguments accept :left (default), :right or :middle.
Cursor state
Mouse.active? whether the cursor is inside the game windowMouse.show /Mouse.hide show or hide the system cursor
Buttons
Mouse.click?(button = :left) pressed and released within the click timeoutMouse.press?(button = :left) currently pressedMouse.release?(button = :left) just releasedMouse.repeat?(button = :left) input is repeatingMouse.hold?(button) held longer than the click timeout
Wheel
Mouse.scroll_up?(rect = nil) wheel scrolled up (optionally only while overrect)Mouse.scroll_down?(rect = nil) wheel scrolled down (optionally only while overrect)
Hit testing
Mouse.over?(object) whether the cursor is over an object (the object must respond tomouse_params)Mouse.over_area?(x, y, w, h) whether the cursor is within an arbitrary rectangleMouse.create_rect(button = :left) build aRectfrom a click-and-drag selection (empty when not dragging)
Dragging
Mouse.dragging?(object, button = :left) whether the object is currently being draggedMouse.drag_object(object, button = :left, rect = nil, lock = nil) drag the object to follow the cursor, optionally bounded byrectand locked to:horizontalor:verticalMouse.drag_object_x(object, ...) /Mouse.drag_object_y(object, ...) drag on one axis only
Object extensions
Sprite, Viewport and Rect gain the following (each computes its own
on-screen mouse_params):
object.over? cursor is over the objectobject.click? object was clickedobject.press? mouse is pressed over the objectobject.mouse_drag(rect = nil) drag with the left button (alsomouse_drag_x/mouse_drag_y)object.overlap?(target) this object's area overlaps the target'sobject.released_in?(target) mouse released while overlapping the targetobject.released_in_rect?(target) mouse released while this object's position falls inside the target rect
Sprites additionally provide:
sprite.over_pixel? cursor is over a non-transparent pixel (alpha-aware hit testing)
if @button.click?
do_thing
elsif @icon.over_pixel?
@icon.colorize(Color.yellow, amount: 80)
end
Barcode Generator
BarcodeGenerator renders a UPC-A barcode to a Bitmap from a numeric
value of up to 11 digits. The 12th check digit is calculated for you, and the
input is zero-padded to the full width.
bmp = BarcodeGenerator.new(12345).generate
sprite.bitmap = bmp
Usage
BarcodeGenerator.new(number, height: 96, unit: 4, color_white: ..., color_black: ...)
number the value to encode (Integer, ≤ 11 digits)height bar height in pixels (default 96)unit bar unit width in pixels (default 4)color_white /color_black the two bar colours (default white / black)
Then:
generator.generate the full UPC-A barcode bitmap — guards, both digit halves and the check digitgenerator.generate_trimmed a trimmed bitmap containing only the right-hand 5 digits, for tight layouts
Errors
BarcodeGenerator::TypeError— the input was not anIntegerBarcodeGenerator::SizeError— the input had more than 11 digits
Both generate and generate_trimmed return a standard Bitmap you can
assign to any sprite.
QR Code Generator
QRCodeGenerator encodes an arbitrary string into a scannable QR code Bitmap
using byte-mode encoding with full Reed-Solomon error correction.
bmp = QRCodeGenerator.new("https://luka-sj.com").generate
sprite.bitmap = bmp
Usage
QRCodeGenerator.new(data, version: nil, unit_size: 4, color_white: ..., color_black: ...)
data the string to encodeversion the QR version (size). Leavenilto auto-select the smallest version that fitsunit_size pixel size of each module (default 4)color_white /color_black the module colours (default white / black)
Then:
generator.generate renders the final QR bitmap, including the 4-module quiet-zone border
Choosing a version
QRCodeGenerator.recommend_version(data, mode = :byte) the smallest supported version that fits the data (ornil)QRCodeGenerator.version_capacity(version, mode = :byte) the approximate byte capacity of a version
Supported range
- Versions 2–4 only (version 1 is not supported).
- Byte mode encoding (any string data).
- Error correction level L (≈ 7% recovery).
If the data exceeds the capacity of all supported versions — or an explicit
version: is out of range — construction raises ArgumentError.
Internals
The generator is built from focused subcomponents, exposed in case you want to
work at a lower level:
QRCodeGenerator::Config — version/error-level capacity, matrix size and alignment-pattern positionsQRCodeGenerator::Encoder — turns the input string into the encoded bit streamQRCodeGenerator::ReedSolomon — error-correction codewords (with aGF256Galois-field helper)QRCodeGenerator::Matrix — the module grid with finder and alignment patterns
Logger & Error Messages
Logger
LUTS::Logger writes timestamped, levelled messages to both the console and a
log file (luts_log.txt). Each level has its own console colour.
LUTS::Logger.info("Plugin loaded")
LUTS::Logger.warn("Missing optional asset")
LUTS::Logger.error("Failed to read data file")
LUTS::Logger.debug("verbose detail") # only when $DEBUG
LUTS::Logger.critical("Unrecoverable!") # logs, then raises
LUTS::Logger.info(msg, options = {}) INFO level (console colour: cyan)LUTS::Logger.warn(msg, options = {}) WARN level (brown)LUTS::Logger.error(msg, options = {}) ERROR level (red)LUTS::Logger.debug(msg, options = {}) DEBUG level — only emits when$DEBUGis truthyLUTS::Logger.critical(msg, options = {}) logs at ERROR level and raisesLUTS::ScriptErrorto halt the gameLUTS::Logger.log_msg(msg, options = {}) the underlying logger that all levels call (writes timestamp + level to console and file)
The options hash controls formatting (colour, headers, indentation, and
:skip_console to suppress console output). Helpers:
Error Messages
LUTS::ErrorMessages defines a family of ready-made error classes. Each builds
a descriptive message and reports it at an appropriate log level when you call
raise on the instance (routing through LUTS::Logger).
ImageNotFound.new(path) — a bitmap could not be found (level: error)SpriteError.new(name) — no suchSprites::*class to instantiate (level: warn)ComponentError.new(name) — a component could not be loaded (level: warn)MissingFunctionError.new(klass, function) — an undefined method was called (level: warn)VertexError.new(vertices = 3) — too few polygon vertices (level: error)
LUTS::ErrorMessages::ImageNotFound.new("Graphics/Pictures/foo").raise
All of these derive from BaseError; LUTS::ScriptError is the
StandardError subclass that Logger.critical raises to crash the game.
Math Helpers
LUTS::Math adds geometry helpers on top of Ruby's Math module — handy for
laying out shapes and scattering points.
LUTS::Math.polygon_points(n, radius:, width:, height:, angle: 0) the[x, y]coordinates of allnvertices of a regular polygon centred in awidth×heightarea, rotated byangledegrees. Returns one pair per vertex.LUTS::Math.rand_circle_coord(radius, x:) a random[x, y]point on the circumference of a circle of the givenradius. Passx: nilto also randomise the x-coordinate within the diameter.
# vertices of a hexagon inside a 128x128 box
points = LUTS::Math.polygon_points(6, radius: 60, width: 128, height: 128)
points.each { |x, y| bmp.set_pixel(x, y, Color.white) }
These pair naturally with Bitmaps::Polygon for drawing custom shapes (see
Bitmap Drawing).
UI Window (9-slice)
LUTS::UI::Window is a Sprites::Base subclass that renders a windowskin to a
bitmap using 9-slice scaling — the corners stay fixed while the edges and
centre stretch to fill the requested size. This lets one small skin graphic
produce a crisp window box at any dimension.
window = LUTS::UI::Window.new(viewport)
window.set_bitmap(
width: 240,
height: 120,
path: "Graphics/Windowskins/box",
slice: 16
)
window.set_bitmap(width:, height:, path:, slice:) build the windowskin bitmap atwidth×heightfrom the source image atpath, usingsliceas the corner/edge boundary that defines the stretchable centre region
Because it inherits Sprites::Base, you also get all of its positioning,
colour and animation helpers on the resulting window sprite.
PBS Compiler
LUTS::Compiler is a schema-driven compiler that turns PBS text files into
serialized .dat game data at load time — the same pattern Essentials uses for
its own data, but reusable for a plugin's custom PBS. It only runs in debug
mode (when there is no packaged Game.rgssad archive), and it recompiles
incrementally.
LUTS::Compiler.compile(
path: "PBS/my_data",
schema: MyData,
file_ext: "myextension"
)
API
LUTS::Compiler.compile(path:, schema:, file_ext:, force: false) compile every PBS.txtfile inpathagainstschemainto serialized data files.force: trueforces a full recompile.LUTS::Compiler.compile_files(path) the unique base filenames in a PBS directory (collapsingname.txtandname_packN.txtinto a single entry)LUTS::Compiler.compile_data(path, schema:, const:) compile a single PBS file against a schema constant and return the parsed hash
When it recompiles
Compilation is skipped entirely unless $DEBUG is on and the path exists, and
no game archive is present. Within that, it recompiles when:
- the compiled data file does not yet exist, or
- you hold
Input::CTRL during load (or passforce: true), or - any PBS source file is newer than the compiled data file.
PBS format it understands
- Section headers:
[TYPE],[TYPE, NAME], or[TYPE, NAME, VERSION](CSV-parsed) - Properties:
KEY = value, type-cast through the schema - Subsections: an all-caps
SUBSECTION_NAMEblock terminated byEND - Coordinate shorthand: a property ending in
XY/XYZexpands into separate_x,_y,_zentries - "Pack" files (
name_packN.txt) merge into their base file
The schema is a module/class carrying a LABEL constant (for log output) and a
nested ClassMethods::SCHEMA describing each property as [type, …] (or
:any for free parsing). POKEMON and TRAINERS ids are auto-filled when
omitted. Progress and failures are reported through LUTS::Logger.
Plugin Manager Helpers
LUTS extends Essentials' PluginManager and Console modules and ships a
mixin for safely composing plugins.
PluginManager
PluginManager.find_dir(plugin) returns the directory of an installed plugin by matching its name against themeta.txtfiles in thePluginsfolder (ornilif not found)
dir = PluginManager.find_dir("Luka's Scripting Utilities")
Console echo helpers
Markup-aware echo helpers added to the Console module:
Console.echo_str(msg, options = {}) echo without a line break, applying markup / stylingConsole.echo_p(msg, options = {}) echo with a line break (paragraph)
These pair with the String colour tags ("text".red, .green, …) from the
core extensions.
Aliaser
PluginManager::Aliaser is a mixin that bulk-aliases overlapping methods when
you prepend a module — letting a plugin override base methods while keeping the
originals callable. Include it in a class, then:
alias_with_module(alias_module, extension = 'old') prependalias_moduleand auto-alias every overlapping instance, private and class method. Each original method is preserved under{name}_{extension}(e.g.update→update_old).map_methods_for_alias(alias_module) the instance/private methods present in both the base class and the modulemap_class_methods_for_alias(alias_module) the class methods present in both
class MyScene
extend PluginManager::Aliaser
alias_with_module(MyScenePatch, 'legacy')
# MyScene#update is preserved as #update_legacy; MyScenePatch#update takes over
end
Class methods are matched against the module's nested ClassMethods, and an
existing alias is never overwritten. This is the mechanism that lets multiple
LUTS-based plugins layer behaviour on the same class without clobbering one
another.