Modding Guide

Introduction to the Craftics addon SDK: how the two extension paths work, how to set up a Fabric mod that depends on Craftics, and a walkthrough of the example addon.

Overview

Craftics exposes an addon SDK through two extension paths. You can use either one, or both together.

The Java API is centered on the CrafticsAPI class, a collection of static registration methods. An addon implements the CrafticsAddon entrypoint interface, declares it in fabric.mod.json, and Craftics calls onCrafticsInit() once at startup after all built-in content is ready.

The JSON datapack path requires no code at all. Place JSON files under data/<namespace>/craftics/ in your mod's resources and Craftics discovers them automatically at load time.

The table below lists every registry, the CrafticsAPI method that covers it, and where in this guide it is documented.

Registry CrafticsAPI method Purpose
Weapons registerWeapon() Register any item as a Craftics weapon with a damage type, stats, and an optional ability.
Armor Sets registerArmorSet() Define full-set bonuses for a custom armor material.
Hybrid Sets registerHybridSet() Define the bonus a player gets from wearing exactly two distinct armor materials.
Trim Patterns registerTrimPattern() Add per-piece stat bonuses and a set bonus for a custom trim pattern.
Trim Materials registerTrimMaterial() Add a stat bonus for a custom trim material.
Enchantments registerEnchantment() Map an enchantment to passive combat stat bonuses.
Equipment Scanners registerEquipmentScanner() Pull stat bonuses from non-standard inventory slots such as trinkets or curios.
Usable Items registerUsableItem() Make a consumable, throwable, or special item usable during a combat turn.
Custom Effects registerEffect() Add a custom combat status effect that ticks each round alongside built-in effects.
Enemies registerEnemy() Define a reusable enemy template that biome JSON can reference by id.
Enemy AI registerAI() Define an AI strategy for a custom or adopted mob type.
Allies registerAlly() Register a combat ally recruited from the player hub to fight alongside the player.
Biomes registerBiome() Add a biome programmatically. JSON datapacks also work for biomes.
Environments registerEnvironment() Define a custom arena environment theme: floor, post, and light blocks.
Campaigns registerCampaign() Define a full custom playthrough that replaces the vanilla progression.
Events registerEvent() Create a custom between-level event such as a trader, ambush, or gambling screen.
Village & Bartering Station scenes Datapack .schem Build the walk-around merchant hub scenes from a schematic using scene marker blocks.

Getting Started

A Craftics addon is a standard Fabric mod that declares Craftics as a dependency and implements the CrafticsAddon entrypoint. Follow the steps below to set one up from scratch.

Step 1: Get a GitHub Packages token

Craftics is published to GitHub Packages at https://maven.pkg.github.com/ChrisAtwell27/Craftics. GitHub Packages requires a Personal Access Token to resolve packages, even public ones. This is a GitHub requirement, not a Craftics one.

  1. Go to github.com/settings/tokens and create a classic token with the read:packages scope.
  2. Add the token to your user-level Gradle properties file at ~/.gradle/gradle.properties, creating the file if it does not exist:
gpr.user=your-github-username
gpr.key=ghp_yourtokenhere

Do not put the token inside your project's gradle.properties. It would be committed to source control.

Step 2: Add the Maven repository and dependency

In your addon's build.gradle, add the Craftics Maven repository and a modImplementation dependency. The version format is 0.2.0+<mc_version>, for example 0.2.0+1.21.1.

repositories {
    maven {
        name = 'Craftics'
        url = 'https://maven.pkg.github.com/ChrisAtwell27/Craftics'
        credentials {
            username = providers.gradleProperty('gpr.user')
                .orElse(providers.environmentVariable('GITHUB_ACTOR')).orNull
            password = providers.gradleProperty('gpr.key')
                .orElse(providers.environmentVariable('GITHUB_TOKEN')).orNull
        }
    }
}

dependencies {
    modImplementation "com.crackedgames:craftics:0.2.0+1.21.1"
}

Step 3: Declare the dependency in fabric.mod.json

Add Craftics to your mod's depends block so Fabric loads Craftics before your addon:

{
  "schemaVersion": 1,
  "id": "myaddon",
  "version": "1.0.0",
  "environment": "*",
  "depends": {
    "fabricloader": ">=0.16.0",
    "minecraft": "~1.21.1",
    "java": ">=21",
    "craftics": ">=0.2.0"
  }
}

Step 4: Implement CrafticsAddon and declare the entrypoint

Create a class that implements com.crackedgames.craftics.api.CrafticsAddon. Craftics calls onCrafticsInit() once at startup, after all built-in content is registered. This guarantees that addon registrations run deterministically after Craftics finishes, unlike plain ModInitializer where load order against Craftics is undefined.

import com.crackedgames.craftics.api.CrafticsAddon;
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.registry.WeaponEntry;
import com.crackedgames.craftics.combat.DamageType;

public final class MyCrafticsAddon implements CrafticsAddon {
    @Override
    public void onCrafticsInit() {
        CrafticsAPI.registerWeapon(MyItems.RUNE_BLADE, WeaponEntry.builder(MyItems.RUNE_BLADE)
            .damageType(DamageType.SLASHING)
            .attackPower(9)
            .apCost(1)
            .range(1)
            .build());
    }
}

Then declare the entrypoint in fabric.mod.json under the craftics key:

"entrypoints": {
  "craftics": [
    "com.example.myaddon.MyCrafticsAddon"
  ]
}

Exceptions thrown from onCrafticsInit() are caught and logged by Craftics. A failing addon will not crash the game or prevent other addons from loading.

No-code addons. If your addon only adds JSON content (biomes, environments, biome paths, enemies), you do not need a CrafticsAddon class at all. Place JSON files under data/<namespace>/craftics/ in your mod's resources and Craftics discovers them automatically.

Example Addon

The examples/addon-template/ directory in the Craftics repository is a fully working addon you can copy and rename. It is a compatibility addon for the Aether mod: it takes the Aether's content and makes it playable inside Craftics' turn-based combat. This is the most common type of Craftics addon.

File layout

The template uses both extension paths. The Java side registers a weapon and custom AI. The JSON side builds a complete arena region.

examples/addon-template/
  build.gradle
  src/main/resources/
    fabric.mod.json
    data/exampleaddon/craftics/
      environments/highlands.json   <-- arena environment theme
      biomes/highlands.json         <-- the Highlands biome
      paths/aether.json             <-- "The Aether" region path
      enemies/moa.json              <-- Moa enemy template
  src/main/java/com/example/exampleaddon/
    ExampleCrafticsAddon.java       <-- CrafticsAddon entrypoint
    MoaAI.java                      <-- custom combat AI

The entrypoint: ExampleCrafticsAddon

ExampleCrafticsAddon implements CrafticsAddon and is declared under "craftics" in fabric.mod.json. Its onCrafticsInit() does two things.

First, it registers the MoaAI immediately, because that class belongs to this addon and is always available:

CrafticsAPI.registerAI("exampleaddon:moa", new MoaAI());

Second, it defers the Zanite Sword weapon registration to server start. The Aether mod's items may not be in Minecraft's item registry yet when addons initialize, because mod load order is undefined. By registering a SERVER_STARTING listener, the lookup is postponed until every mod's items are ready. If the Aether mod is not installed the lookup finds nothing and skips silently.

ServerLifecycleEvents.SERVER_STARTING.register(server -> {
    Identifier id = Identifier.of("aether", "zanite_sword");
    if (!Registries.ITEM.containsId(id)) return; // Aether not installed
    Item zaniteSword = Registries.ITEM.get(id);
    CrafticsAPI.registerWeapon(zaniteSword, WeaponEntry.builder(zaniteSword)
        .damageType(DamageType.SLASHING)
        .attackPower(11)
        .apCost(1)
        .range(1)
        .ability(Abilities.applyEffect(CombatEffects.EffectType.SLOWNESS, 2, 0))
        .build());
});

The deferred cross-mod lookup pattern is the standard approach for compatibility addons. See ExampleCrafticsAddon.java for the full source.

Custom AI: MoaAI

MoaAI implements EnemyAI and gives the Moa two combat phases. Above half health it closes to melee range and strikes. At or below half health it backs away from the player and attacks at range instead. The phase is checked each turn by reading self.getCurrentHp() against self.getMaxHp().

decideAction returns an EnemyAction record. The available actions include Attack, Move, MoveAndAttack, and RangedAttack. The AIUtils helpers handle common pathfinding tasks such as finding an adjacent tile next to the player or computing a flee target. See MoaAI.java for the full implementation and the Enemies page for the full AI reference.

JSON datapacks: the Highlands region

Four JSON files under data/exampleaddon/craftics/ define a complete arena region with no Java code.

environments/highlands.json defines the arena's visual theme: moss block floors, bamboo fence posts, ochre froglight lamps, and a forest obstacle style.

{
  "id": "exampleaddon:highlands",
  "floor_block": "minecraft:moss_block",
  "post_block": "minecraft:bamboo_fence",
  "light_block": "minecraft:ochre_froglight",
  "decor_style": "forest"
}

biomes/highlands.json defines the Highlands biome. Its "order": 50 places it in the progression sequence. "environment" points at the environment above. The "enemies" block references the Moa enemy template by its id.

{
  "id": "exampleaddon:highlands",
  "name": "Highlands",
  "order": 50,
  "levels": 3,
  "environment": "exampleaddon:highlands",
  "enemies": {
    "hostile": [{ "enemy": "exampleaddon:moa", "weight": 10 }],
    "boss":    { "enemy": "exampleaddon:moa", "weight": 1 }
  }
}

enemies/moa.json adopts the Aether's aether:moa entity as a Craftics enemy. The "ai" field names the key registered in code, linking the JSON template to the Java MoaAI class.

{
  "id": "exampleaddon:moa",
  "entity": "aether:moa",
  "ai": "exampleaddon:moa",
  "hp": 12,
  "attack": 3,
  "defense": 0,
  "range": 4,
  "speed": 3
}

paths/aether.json groups the biome into a named region called "The Aether", which appears in the player's progression map alongside Craftics' built-in Overworld, Nether, and End regions.

{
  "id": "exampleaddon:aether",
  "display_name": "The Aether",
  "biomes": ["exampleaddon:highlands"]
}

To build and run the template, see the instructions in examples/addon-template/README.md. To adapt it for a different mod, rename the exampleaddon id everywhere and swap the Aether items and entities for those of the mod you want to integrate.