Crumbs Engine allows you to easily manipulate and change canvas-based elements, including any that were present in vanilla. Traditionally, canvas elements are created through large functions that redraw the canvas every frame based on various factors, making them clunky to edit and difficult to mod. Crumbs Engine solves this by abstracting much of that complexity away, leaving you with core functionalities designed for creating more content.
- Integration
- Documentation
- Object
- Canvas Scopes & Custom Canvases
- Custom anchors
- Notable Components
- Particles
- Bridges
- Vanilla Implementation
- Source / Further Reading
Load the mod with Game.LoadMod('https://cursedsliver.github.io/Crumbs-engine/Crumbs.js');.
If you wish to have this as a strict prerequisite, consider forcing your users to load this mod before yours (in the case of Steam) or wrap the rest of your code in a function to wait until this mod loads completely before calling it. You'll know when it is loaded completely when the flag CrumbsEngineModObj?.ready becomes true.
To get a better understanding of the mod, it may be helpful to look at the examples provided in /examples alongside reading the documentation. Below is a list of roughly what they cover.
- MouseTrail.js: object basics
- StarCursor.js: children objects
- TinyWrinklers.js: modifying base game objects
- GlitchedCookie.js: settings and pointerInteractive components
- StarCursor2.js: canvasManipulator and text components
- GlowingCookieRain.js: cheap filters, particles
There are even more examples covering the vanilla objects in Implementation.js.
The core of the mod’s functionality; each object represents something to be drawn on the canvas, and has properties that can be manipulated and changed.
Instantiate a Crumbs.object with the function Crumbs.spawn(template, custom). Pass in an object into template, and (optionally) pass in another into custom.
- Every property of the passed-in object in
templatewill be copied over to the new object. - Then, every property of the passed-in object in
custom, if there is one, will be copied over to the new object. - Essentially, you can create a "template" for your Crumbs objects by creating an object and assigning it to another variable, then specify details for each object you want to create with that template.
- You can use
Crumbs.spawnVisible(template, custom)to have it only spawn the object when the game is being looked at. - You can also use
Crumbs.object.prototype.die()to remove the object from the game. - For any properties that aren’t specified in either object, defaults are taken from
Crumbs.objectDefaults.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false | If false, the object will not be drawn or updated, including its behaviors and components. |
x |
number | 0 | The x-coordinate of the object. |
y |
number | 0 | The y-coordinate of the object. |
rotation |
number | 0 | The rotation of the object, clockwise in radians. |
scaleX |
number | 1 | Scaling multiplier on the x-direction. |
scaleY |
number | 1 | Scaling multiplier on the y-direction. |
alpha |
number | 1 | The opacity of the image drawn (between 0 and 1 inclusive). |
imgs |
array/string | [0x0 empty image] | The images of the object. Can be a string or an array of them. If it is not one, it will be automatically converted to arrays. |
imgUsing |
number | 0 | The image currently displayed, out of all the images listed in imgs. |
noDraw |
boolean | false | Prevents the object from being drawn, but its behaviors and components still function. |
offsetX |
number | 0 | Offsets the image drawn by that amount on the x axis, which also rotates with its rotation. |
offsetY |
number | 0 | Like offsetX, but on the y axis. |
anchor |
string/Crumbs.anchor instance |
"center"/Crumbs.defaultAnchors.center |
The location of its position on the image. Values: “top-left”, “top”, “top-right”, “left”, “center”, “right”, “bottom-left”, “bottom”, “bottom-right”, or custom by creating custom Crumbs.anchor instances (instructions detailed below). |
scope |
string/Crumbs.canvas instance |
"foreground"/Crumbs.scopedCanvas.foreground |
Which canvas to draw it on, represented by its key in Crumbs.scopedCanvas. Defaults available: “left”, “middle”, “right”, “background”, “foreground” (but you can create your own canvases). |
order |
number | 0 | The order in which to draw the object (acts as the z-index). |
width |
number/null | null | If assigned, overrides the width from the image. |
height |
number/null | null | If assigned, overrides the height from the image. |
sx |
number | 0 | The x-coordinate of the top-left corner of the rectangle to draw the image provided. |
sy |
number | 0 | The y-coordinate of the top-left corner of the rectangle to draw the image provided. |
id |
any | null | An identifier of the object. Can be used to find objects via Crumbs.findObject, Crumbs.getObjects, Crumbs.object.prototype.findChild, and Crumbs.object.prototype.getChildren. |
init |
function | empty | A function to be called on object initialization, with this being the object. Does not support arrow functions. |
behaviors |
array/function/Crumbs.behavior/Crumbs.behaviorInstanceinstance(s) |
[] | Instances of Crumbs.behaviorInstance, containing a function called every logic frame. Non-arrays are converted to arrays. |
components |
array/component | [] | Instances of objects listed in Crumbs.component. Non-arrays are converted to arrays. |
children |
array | [] | Child objects to create upon initialization. Not stored in the resulting object; converted to an array of children Crumbs.object instead. |
t |
number | Cannot be set on object initialization. The moment when the object is created, relative to Crumbs.t. You can thus get the lifespan of the object in terms of frames by doing Crumbs.t - object.t. |
|
parent |
Crumbs.object/null | Cannot be set on object initialization. The parent object, or null if it is not a child. |
- Behaviors are instances of
Crumbs.behaviorInstance, containing a function called every logic frame withthisset to the object and an object passed in for local variables. Crumbs.behaviorInstance(behavior, parameters)behavior: An instance ofCrumbs.behavior, containing a function called every frame.- To create a
Crumbs.behavior, simply pass in a function fornew Crumbs.behavior. - DO NOT USE ARROW FUNCTIONS. Keyword
thiswill not work. Usefunction() { }.
- To create a
parameters(optional): An object passed into the function on every call, and is only accessible and modifiable to the function.- Note: if you don't need custom behavior parameters per object, place these in templates. Place as
Crumbs.behaviorifparametersis needed, otherwise put asCrumbs.behaviorInstance. - If you do need custom behavior parameters, override behavior in
customwith newCrumbs.behaviorInstancefrom behaviors assigned to variables. - Try not to put your functions directly in your object.
- If not an array, it will be converted into one.
- You can declare images in the
imgproperty. - Access and change them with the
imgUsingproperty. - If you place them in templates, MAKE SURE to ALWAYS wrap them in arrays, even if there's only one element!
- Child objects can be created upon initialization or for existing objects via
Crumbs.object.prototype.spawnChild(template, custom)orCrumbs.object.prototype.spawnChildVisible(template, custom). - Children inherit the coordinate plane and rotation of their parent, and also scale with them. You can nest children in children to create more complex effects.
--
Crumbs Engine contains many helper methods to help you find, manipulate, and do more stuff.
| Method | Description |
|---|---|
hasChildren() |
Returns true if the object has any children, otherwise false. |
removeChild(index) |
Removes the child at the specified index by setting it to null. Note: Children arrays are not automatically cleaned, so manage them carefully. |
addComponent(comp) |
Adds a component to the object, and calls the component's init method if it exists. |
removeComponent(type) |
Removes the first component of the specified type (string or the instance itself) from the object and returns it; returns null if not found. |
getComponent(type) |
Returns the first component of the specified type attached to the object, or null if not found. |
getAllComponents(type) |
Returns an array of all components of the specified type attached to the object. |
addBehavior(behavior) |
Adds a behavior to the object. Accepts a Crumbs.behavior, Crumbs.behaviorInstance, or a function. |
findChild(id) |
Recursively searches for a child (or descendant) with the specified id and returns it. Returns null if not found. |
getChildren(id) |
Returns an array of all direct children, or all children (and descendants) with the specified id if provided. |
die() |
Removes the object from the game. |
spawnChild(template, custom) |
Instantiates a child object using the given template and custom properties, and attaches it as a child of the current object. |
spawnChildVisible(template, custom) |
Instantiates a child object using the given template and custom properties, and attaches it as a child of the current object, if the game is currently being viewed. |
| Method | Description |
|---|---|
Crumbs.findObject(id, scope) |
Returns the first object with the specified id. If scope is provided, searches only within that canvas scope. Returns null if not found. |
Crumbs.getObjects(id, scopes) |
Returns an array of all objects with the specified id. If scopes is provided (string or array), searches only within those scopes. |
Crumbs.globalSearch(id) |
Returns an array of all objects and their descendants with the specified id across all scopes. Not recommended for frequent use due to performance. |
Crumbs.forceDrawObject(o, ctx) |
Forcibly draws the object o on that context, ignoring components, noDraw, and children. |
Crumbs.manipImage(old, newPropertyName, width, height, filters, drawCallback) |
Creates a new copy of an image at Game.Loader.assets[newPropertyName] with new width and height (scaling), optional css filters, and an optional callback with arguments ctx and Game.Loader.assets[old] that overrides the drawing step. Set old and newPropertyName to be the same to enable direct application. |
Available canvas scopes in Crumbs.scopedCanvas:
- left: The leftmost section of the game, containing the big cookie.
- middle: The rows of building displays, containing the minigames.
- right: The store.
- background: The background of the entire game.
- foreground: A canvas overlaid on top of the entire game.
You can create a new canvas by doing new Crumbs.canvas(parentEle, key, id, css).
parentEle: Parent element to attach the canvas to. The canvas will assume 100% of the parent's width and height.key: Its key inCrumbs.scopedCanvas.id: ID of the canvas itself (not the canvas container).css: Optional CSS.
Other properties of each scopedCanvas:
l: the element of the canvas.c:l.getContext('2d').objects: array of objects.sortedObjects: objects sorted by draw order; used by components such aspointerInteractive.mouseX: the x position of the mouse in its coordinate system.mouseY: the y position of the mouse in its coordinate system.redrawPerFrame: whether to redraw the canvas each frame, default:true. To manually redraw a canvas, doCrumbs.drawObjectsIn(scope).
You can create your own anchors by assigning something to a new instance of Crumbs.anchor. It takes two parameters:
- x: The amount of distance to the right compared to the width of the object of the anchor, starting from the top left.
- y: The amount of distance downward compared to the height of the object of the anchor, starting from the top left.
- For example, Crumbs.defaultAnchors.center has x and y set to 0.5 each.
- Note that you can set them to any value you like, including numbers bigger than 1 and smaller than 0.
- Helpful tip: You can see the anchors of objects by setting
Crumbs.prefs.anchorDisplayto 1.
- To create a new component, use
new Crumbs.component[component key](obj). - Attach the component to the component key either by itself or in an array with other components.
- Components can be used for more complex behaviors, such as cursor interaction, draw settings, pattern fill, text, and more.
- You can disable the warnings for having multiple of the same component on an object with
Crumbs.prefs.warnDuplicateComponents.
- Handles interaction with the mouse, allowing for hover detection and click detection.
- Always maximally fits the width and height of the object it's attached to.
- The
thiskeyword refers to the object that it's attached to.- DO NOT USE ARROW FUNCTIONS. Keyword
thiswill not work. Usefunction() { }.
- DO NOT USE ARROW FUNCTIONS. Keyword
- Helpful tip: You can see the collider boxes of objects with this component by setting
Crumbs.prefs.colliderDisplayto 1.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the component is active and can interact with pointer events. |
onClick |
function | empty | Function called when the object is clicked down. |
onRelease |
function | empty | Function called when the mouse button is released over the object. |
onMouseover |
function | empty | Function called when the mouse enters the object's area. |
onMouseout |
function | empty | Function called when the mouse leaves the object's area. |
alwaysInteractable |
boolean | false | If true, the object is interactable even if another object with a pointerInteractive component is drawn above it. |
boundingType |
string | 'rect' or Crumbs.colliderType |
The shape used for hit detection. |
hovered |
boolean | false | Whether the mouse is hovering over the object. Updates automatically. |
click |
boolean | false | Whether the mouse is holding down click over the object. Updates automatically. |
- You can create your own bounding boxes with
new Crumbs.colliderType(func). Pass in a function that determines whether or not a given position is in the box. - The function has the following arguments passed into it as individual arguments:
s: scope of the object it's attached tom: the objectpWidth: the width of the object in pixels, accounting for scaling (so you don't have to calculate that yourself)pHeight: the height of the object in pixels, accounting for scaling
- You can also pass in a string alongside the function to have it as its name. Once set, you will be able to have instantiations using the string as its bounding type convert the string into its corresponding collider.
- There are two default boundingTypes available:
rectandoval. You can pass in'rect'or'oval'to specify them.
- Gives raw control over the canvas, with drawing operations centered on the object itself (discounting offsetX and offsetY).
- The
thiskeyword refers to the component object itself, not the object it's attached to.- DO NOT USE ARROW FUNCTIONS. Keyword
thiswill not work. Usefunction() { }.
- DO NOT USE ARROW FUNCTIONS. Keyword
- Be careful! Make sure that you have an equal amount of
ctx.save();andctx.restore();in each canvasManipulator, otherwise things will break horribly.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the canvasManipulator component is active. |
function |
function | empty | Function called with the object (m) and the canvas context (ctx) for custom drawing. |
before |
function | empty | Function called with the object (m) and the canvas context (ctx) for custom drawing, with the difference that this is called right before the object is drawn. |
- Displays text on the origins of the object.
- Inherits most of its properties from canvas itself.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the text component is active and displayed. |
content |
string | '' | The text content to display. |
size |
number | 10 | Font size in pixels. |
font |
string | 'Merriweather' | Font family used for the text. |
align |
string | 'left' | Text alignment. Possible values: 'left', 'center', 'right'. |
direction |
string | 'inherit' | Text direction. Usually 'inherit', 'ltr', or 'rtl'. |
color |
string | '#fff' | Text color (CSS color value). |
outlineColor |
string | '#000' | Outline color for the text (CSS color value). |
outline |
number | 0 | Outline thickness in pixels. |
maxWidth |
number/null | null | Maximum width of the text box in pixels. If null, no limit is applied. |
- Adjusts global canvas drawing properties for this object.
- Inherits all of its properties from canvas itself.
- Note: to modify, you need to find the properties via the
.objobject first.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the setting changes are active. |
obj |
object | {} | Container for all the properties AFTER the setting is created using the properties below. Access them with this, but when creating them simply put the below properties in the constructor. |
globalCompositeOperation |
string | 'source-over' | Sets how new drawings are composited onto the existing canvas. See MDN docs for options. |
filter |
string | '' | Adds filters to the draw. See MDN docs for options. Will lag, ues in moderation. Not available in all browsers. If the shader parameters do not need to be changed often during runtime, consider using Crumbs.manipImage(old, newPropertyName, width, height, filters, drawCallback) instead. |
imageSmoothingEnabled |
boolean | true | Enables or disables image smoothing when scaling images. |
imageSmoothingQuality |
string | 'low' | Sets the quality of image smoothing. Possible values: 'low', 'medium', 'high'. |
- Fills the image of the object across some width and length in a grid pattern.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the pattern fill is active. |
width |
number | 2 | Number of minimum pixels to fill by repeating the image horizontally. |
height |
number | 2 | Number of minimum pixels to fill by repeating the image vertically. |
offX |
number | 0 | Horizontal offset (in pixels) for the pattern's starting position. |
offY |
number | 0 | Vertical offset (in pixels) for the pattern's starting position. |
dWidth |
number/null | null | Destination width for each pattern tile. If null, uses the image's width. |
dHeight |
number/null | null | Destination height for each pattern tile. If null, uses the image's height. |
sWidth |
number/null | null | Source width from the image to use for each tile. If null, uses the full image width. |
sHeight |
number/null | null | Source height from the image to use for each tile. If null, uses the full image height. |
sx |
number/null | null | X-coordinate of the top-left corner of the source rectangle. If null, starts at 0. |
sy |
number/null | null | Y-coordinate of the top-left corner of the source rectangle. If null, starts at 0. |
- Adds a gradual fading effect onto the object.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the linear fade effect is active. |
progress |
number | 1 | Midpoint of the fade effect (0 to 1). |
distance |
number | 30 | Total distance of the fade in pixels; scales with the object. |
sliceWidth |
number | 3 | Width in pixels between each redraw slice. |
horizontal |
boolean | false | If true, the fade is applied horizontally; otherwise, it is vertical. |
flip |
boolean | false | If true, reverses the fade direction. |
initialAlpha |
number/null | null | Starting opacity for the fade. If null, uses the object's base opacity. |
finalAlpha |
number/null | null | Ending opacity for the fade. If null, defaults to 0 (fully transparent). |
cutOff |
boolean | false | If true, will no longer draw the section before the gradient begins (will not affect the gradient itself) |
- Makes the object display a tooltip when it is hovered over.
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true | Whether the tooltip component is active and displayed. |
content |
string/function | '' | The tooltip text to display, or a function returning the text (for dynamic tooltips). The function will have the component itself as an argument, and use this to access the attached object. |
origin |
string | 'middle' | The origin point for the tooltip display. Valid values: left, middle, bottom-right, bottom, this, store. If the tooltip has crate enabled, all values other than left do the same thing. |
crate |
boolean | false | If true, tooltip is fixed on the object; if false, follows the mouse. |
alwaysInteractable |
boolean | false | If true, tooltip is interactable even if another object is above it. |
hideOnClick |
boolean | false | If true, hides the tooltip when the left mouse button is held down. |
boundingType |
string or Crumbs.colliderType |
'rect' | The collider type used for hit detection (default is rectangle). |
A particle is a simplified version of Crumbs.object that uses less memory and less computing power, but with reduced functionality. They are always drawn on top of all objects and does not support drawing order, components, or stacking images and behaviors. Declare a particle with Crumbs.spawnParticle(template, x, y, r, a, scope);, where:
- template: detailed in table below;
- x: x position;
- y: y position;
- r: rotation;
- a: alpha (opacity);
- scope: same as scope in regular objects.
| Property | Type | Default | Description |
|---|---|---|---|
width |
number | 1 | Width of the particle. |
height |
number | 1 | Height of the particle. |
img |
string | (empty) | Image used for the particle (optional). |
life |
number | 2 * Game.fps |
Lifespan of the particle in frames. |
init |
function | null | Function called on particle initialization. |
behavior |
function | null | Function called every frame for custom particle logic. |
reusePool |
array | null | Pool for reusing particle instances (optional, for optimization); see below. |
To use it, assign Crumbs.newReusePool() to it in the template (never place the object declaration directly in Crumbs.spawnParticle!), which will register it and automatically pull from the pool over creating new objects. The pool is cleaned once every 120 seconds. You can do Crumbs.prunePool(pool) to clean any pool of your choice.
Bridges are small integration scripts that adapt Crumbs engine to other mods. They let Crumbs load compatibility shims either before or after a target mod initializes, so both ordering-sensitive fixes and post-init hooks are possible. This feature is designed for mods that do not necessarily require Crumbs; if a mod requires Crumbs engine to function, one should simply set it as a prerequisite to load before loading the mod itself.
How to use
- To register a bridge, pull request to
bridgesList.jsonwith your bridge script. It can be within the/bridgesfolder, or it can be hosted elsewhere. - Crumbs engine watches Game.registerMod, so your mod must include that in order for the corresponding bridges to activate. Therefore, put your mod id as the key in the JSON, and URL as its value.
- You can have just one URL or an array of two. By default, the URL will be loaded after both your mod and Crumbs engine finish loading. If you use an array, the second URL will be load before your mod is ready, only in case that your mod is loaded after Crumbs engine.
Almost all vanilla elements that rely on canvas (with the exception of building displays) are converted into Crumbs Engine objects. You can access them with Crumbs.findObject(id, scope) or Crumbs.getObjects(id, scope) and modify them as you wish.
- You can also easily modify all instances of a behavior with
Crumbs.behavior.prototype.replace(original, newCode)orCrumbs.behavior.prototype.inject(line, code), and find their corresponding behavior from their behaviorInstance withCrumbs.behaviorInstance.prototype.getBehavior(). - For more details on how they are implemented and their IDs, refer to this folder. Find the js file corresponding to your game version of interest.
For more details, refer to the source code.