A mod is a folder containing files. This folder must be placed in the mods folder of the mod loader. The location of this folder depends on your operating system:
%APPDATA%\animdustry\mods~/.config/animdustry/mods/storage/emulated/0/Android/data/io.anuke.animdustry/files/modsIf the folder does not exist, you can create it manually, otherwise it will be created automatically on first startup of the mod loader.
The folder structure of a mod is as follows:
modfolder
├── credits.txt
├── mod.json
├── maps
│ └── exampleMap.json
├── music
│ └── exampleMusic.ogg
├── sprites
│ └── exampleSprite.png
├── scripts
│ ├── __api.js
│ ├── exampleScript.js
│ ├── init.js
│ └── main.js
├── units
│ └── exampleUnit.json
├── unitSplashes
│ └── exampleUnit.png
└── unitSprites
├── exampleUnit-angery.png
├── exampleUnit-happy.png
├── exampleUnit-hit.png
└── exampleUnit.png
An example of a mod can be found here.
A mod.json or mod.hjson file must be placed in the root folder of the mod. It is what tells the mod loader that this folder contains a mod. The content of the file is as follows:
{
"name": "The name of your mod",
"namespace": "exampleNamespace",
"author": "You",
"description": "Description of your mod",
"version": "1.0.0",
"dependencies": [
"exampleDependency"
],
"tags": ["template", "example"],
"enabled": true,
"debug": false,
"legacy": false
}
true.false.false if you are making a JavaScript mod. Optional, defaults to false.All JavaScript files inside the scripts folder (not recursive) are executed by the mod loader when loading your mod.
If it contains a file named init.js, that file is executed first.
If it contains a file named main.js, it is executed once all non-main files of all other mods have been executed.
All other files are executed in arbitrary order.
Additionally, if a file is named __api.js, it is ignored by the mod loader, this is useful for highlighting in code editors.
You can download the __api.js file from the latest release of the mod loader.
Since all mods run in the same context, it is recommended that you use your namespace for function definitions. Otherwise, variables and functions defined in the global context might be overridden or cause errors.
Since mods are loaded in an arbitrary order, you should not rely upon functions defined by other mods to be available in init.js.
Unit and map functions can rely on this, as all mods are loaded by the time they are called.
Unit scripts describe how a unit is drawn and how it interacts with the game. To define a unit, first place a JSON or Hjson file with the same name as your unit in the units folder. Its contents should look like this:
{
"name": "exampleUnit",
"title": "-EXAMPLE-",
"subtitle": "lorem ipsum",
"abilityDesc": "dolor sit amet",
"abilityReload": 4,
"unmoving": false
}
To add a splash image to your unit, place an image file with the same name as your unit into the unitSplashes folder. To add in-game sprites of your unit, place the files example.png and example-hit.png in the unitSprites folder (replace “example” with the name of your unit). Those two files must exist for the unit to display properly. Additionally, an example-angery.png (not a typo) and example-happy.png file can be placed in the folder as well. The -angery sprite is displayed when the player misses a beat, and the -happy sprite is displayed one second before a level ends.
The mod loader expects two functions in your namespace object, a _draw and an _ability function.
The name of these functions must be in the form {unit name}_draw and {unit name}_ability respectively.
The _draw function draws the unit’s splash image.
This is used when you click on a unit in the main menu or roll it with the gacha system.
It takes a single Vec2 argument, which is the base position of the unit’s splash image on the screen.
The hoverOffset() function should be used here to make the unit hover, and the getScl() function should be used to make the unit ‘pop out’ when it is rolled with the gacha system.
exampleNamespace.exampleUnit_draw = function(basePos) {
pos = Vec2.add(Vec2.sub(basePos, new Vec2(0, 0.5)), Vec2.scale(hoverOffset(), 0.5));
drawUnit(Vec2.sub(pos, shadowOffset), getScl(0.165), Color.shadow);
drawUnit(pos, getScl(0.165));
};
The _ability function is used to activate the unit’s ability during the game.
It is called every time the unit moves to a new tile on the playing field.
The game does not check automatically whether the ability has recharged, this is checked using the moves variable.
The function takes three arguments: The number of successful moves the player has made, the current position of the player as an integer vector, and the last move direction of the player as an integer vector.
exampleNamespace.exampleUnit_ability = function(moves, gridPosition, lastMove) {
if(moves % 4 == 0) {
for(i = 0; i < d8.length; i++) {
effectExplode(Vec2.add(gridPosition, Vec2.scale(d8[i], 2)));
damageBlocks(Vec2.add(gridPosition, Vec2.scale(d8[i], 2)));
}
}
}
Map scripts describe a playable level in the game. To add a custom map to the game, place a JSON or Hjson file into the maps folder. The contents of the file should look like this:
{
"name": "exampleMap",
"songName": "Anuke - Boss 1",
"music": "boss1",
"bpm": 100.0,
"beatOffset": 0.0,
"maxHits": 10,
"copperAmount": 8,
"fadeColor": "fa874c",
"alwaysUnlocked": true
}
PNG files in the sprites directory of your mod can be used in functions like makeBullet to make bullets, enemies, etc. with custom sprites.
To do this, use the path of the file from the sprites directory without the file extension.
For example, the file /sprites/exampleSprite.png would be used with "exampleSprite".
To use sprites from another mod, you have to use the importSprite function.
For ease of use, a mod can provide a method to do this automatically, for example:
exampleNamespace.importSprites() {
// Import `/sprites/exampleSprite.png` into the calling namespace
importSprite("exampleSprite", "exampleNamespace");
}
Important: importSprite always imports into the currently active mod’s namespace. That means that if you call a function from a different mod that uses sprites, you must import all sprites used in that function into your mod’s namespace.
Some sprites are included with the base game. These are:
For each map, three functions must be defined in the mod’s namespace, the _drawPixel function, the _draw function and the _update function. Like the unit functions, these must be in the format {map name}_drawPixel, {map name}_draw and {map name}_update.
The _drawPixel function is used to draw a map’s background.
exampleNamespace.exampleMap_drawPixel = function() {
drawStripes(Color.parse("#19191c"), Color.parse("#ab8711"));
drawBeatSquare(Color.parse("#f25555"));
}
The _draw function is used to draw the playing field.
exampleNamespace.exampleMap_draw = function() {
drawTiles();
}
The _update function is used for spawning enemies / obstacles on the playing field.
Since it is called every frame, state.newTurn should be used to check for the start of a beat.
exampleNamespace.exampleMap_update = function() {
if(state.newTurn && state.turn >= 7 && state.turn < 23 && state.turn % 4 == 3) {
for(i = 0; i < d4edge.length; i++) {
makeDelayBulletWarn(Vec2.add(playerPos, Vec2.scale(d4edge[i], 2)), Vec2.neg(d4edge[i]));
}
}
}
You can create a custom entity using the makeCustomEntity function.
An entity takes a function as a script.
This script is then called every frame while the entity is alive.
The function’s this binding is of type CustomEntity.
Any function (with no parameters and return type) can be an entity script, and unlike the other special functions, it does not require a specific naming convention.
You can also add addititional properties to the object.
The properties pos, rot, scl and sprite can be changed at any point during the entity’s lifetime, this can be used for movement of sprite animations.
// The entity script
exampleNamespace.exampleEntity = function() {
if(!this.initDone) {
this.initDone = true;
this.sprite = Sprites.arc;
this.rot = rad(270);
this.startTurn = state.turn;
}
var lifeTurn = state.turn - this.startTurn;
if(lifeTurn % 2 == 0) {
// Shoot a bullet from the entity's position
makeBullet(this.pos, this.dir, Sprites.bullet);
}
}
// Wrapper function for spawning
exampleNamespace.makeExampleEntity = function(pos, dir) {
var entity = makeCustomEntity(new Vec2(-3, 6), exampleNamespace.exampleEntity, 10, false, true, false);
entity.dir = dir; // Add custom data
return entity;
}
A credits.txt file should be placed at the root of your mod folder.
It can be used to give credit for music, art and other mentions.
The contents of this file will be added to the in-game credits.
If no credits.txt file is found, the following will be added instead (name and author are replaced with the respective entries in mod.json):
- {name} -
Made by: {author}
(Auto-generated credits)
The mod loader’s JavaScript engine is based on Duktape. This comes with a few limitations:
new).let keyword is not supported in Duktape.for...of loops are not supported in Duktape. Either use a for...in or for loop.To publish a mod, it needs to be in a public GitHub repository and have the topic animdustry-mod.
The namespace of your mod must be unique across all mods. If two mods have the same namespace, the mod that was created first has priority, the other mod will not be added.
For now, the only way to download mods is manually downloading it from its repository on GitHub, unzipping it, and placing it in the mods folder.
For convenience, you can find a list of mods with download links here.
Once the mod browser is implemented, it will be possible to install mods directly
Note: Tags will be used for the mod browser, which is not implemented yet.
Tags are keywords that make it easier to find your mod in the mod browser. Valid tags are: