Enemies & Allies
Register reusable enemy templates, write custom AI strategies, and define combat allies that fight alongside the player.
Enemies
An enemy template is a reusable set of combat stats tied to a mob entity type. Once registered, biome JSON can reference it by id with "enemy": "<id>" instead of spelling out the same stats in every biome that uses that mob. Templates are registered with CrafticsAPI.registerEnemy().
Enemy templates can also be loaded from JSON datapacks with no Java code required. Both approaches produce the same result: a named entry in the enemy registry that biomes can reference.
Java API
Build an EnemyEntry with its fluent builder and pass it to CrafticsAPI.registerEnemy():
EnemyEntry entry = EnemyEntry.builder("mymod:desert_husk", "minecraft:husk")
.ai("minecraft:skeleton") // optional: use a different AI key
.hp(10)
.attack(3)
.defense(1)
.range(1)
.speed(2)
.build();
CrafticsAPI.registerEnemy(entry);
| Builder Method | Type | Default | Description |
|---|---|---|---|
builder(id, entityTypeId) |
String, String | required | Registry key (e.g. "mymod:desert_husk") and the Minecraft entity type to render (e.g. "minecraft:husk"). Both are required. |
ai(String) |
String | entityTypeId | Key used to look up the AI strategy in AIRegistry. Defaults to the entityTypeId, so most enemies need no explicit AI key unless you want to reuse another mob's strategy. |
hp(int) |
int | 6 | Base health. |
attack(int) |
int | 2 | Base attack. |
defense(int) |
int | 0 | Base defense. |
range(int) |
int | 1 | Attack range in tiles. |
speed(int) |
int | 0 | Combat move speed in tiles per turn. 0 means use the entity type's built-in default speed. Set a positive integer to override it with a fixed value. |
JSON Datapack Schema
Place JSON files at data/<namespace>/craftics/enemies/<name>.json. The id and entity fields are required. All stat fields are optional and fall back to the same defaults as the Java builder. The entity value must be a valid, loaded entity type or the file is skipped with a warning.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id |
string | yes | Registry key for this template, e.g. "mymod:desert_husk". |
|
entity |
string | yes | Minecraft entity type to render, e.g. "minecraft:husk". Must be a loaded entity type. |
|
ai |
string | no | entity |
AI registry key. Resolved at runtime; unknown keys fall back to the entity type's default strategy. |
hp |
int | no | 6 | Base health. |
attack |
int | no | 2 | Base attack. |
defense |
int | no | 0 | Base defense. |
range |
int | no | 1 | Attack range in tiles. |
speed |
int | no | 0 | Move speed in tiles per turn. 0 means use the entity type default. |
Example JSON
// data/mymod/craftics/enemies/desert_husk.json
{
"id": "mymod:desert_husk",
"entity": "minecraft:husk",
"ai": "minecraft:skeleton",
"hp": 10,
"attack": 3,
"defense": 1,
"range": 1,
"speed": 2
}
Once loaded, a biome JSON can reference this template with "enemy": "mymod:desert_husk" instead of repeating all the stat fields inline.
entity field controls only how the mob looks in the arena. You can render a husk but give it skeleton AI, or render any modded mob entity as long as it is loaded by the time the datapack is applied. The stat block is completely independent of the entity's vanilla behavior.
Enemy AI
Craftics uses a strategy interface, EnemyAI, to decide what each enemy does on its turn. Built-in strategies cover all vanilla hostile mobs. Addon mods can register custom strategies for new mob types (or override behavior for existing ones) with CrafticsAPI.registerAI().
Registration
CrafticsAPI.registerAI("mymod:custom_zombie", (self, arena, playerPos) -> {
// Decide what this enemy does this turn and return an EnemyAction.
return new EnemyAction.MoveAndAttack(path, self.getAttackPower());
});
The first argument is the entity type id (or any custom string you used as the ai key in an enemy template). The second argument is an EnemyAI instance, which is a functional interface.
The EnemyAI Interface
public interface EnemyAI {
EnemyAction decideAction(CombatEntity self, GridArena arena, GridPos playerPos);
default Set<GridPos> computeThreatTiles(CombatEntity self, GridArena arena) {
return null; // null = use the generic speed+range danger diamond
}
}
decideAction is called once per enemy turn. It receives the enemy's own CombatEntity, the full GridArena (tile data, occupancy, obstacle positions), and the player's current GridPos. Return any EnemyAction record to describe what the enemy does.
computeThreatTiles is optional. Override it when the enemy's real attack reach does not match the generic danger diamond computed from speed + range. Return a set of GridPos values that Craftics will highlight red as the danger indicator for the player. Return null (the default) to use the generic formula.
AIUtils Helpers
The AIUtils class provides pathfinding and geometry helpers so you do not have to implement common patterns from scratch.
| Method | Description |
|---|---|
seekOrWander(self, arena, playerPos) |
Universal fallback: move toward the player using pathfinding, or wander to a random adjacent tile if no path exists. Returns a Move or MoveAndAttack action, never Idle unless the enemy is completely boxed in. |
wander(self, arena) |
Move to a random adjacent walkable tile. Use for neutral or passive mobs that are not targeting anyone. |
getAdjacentTiles(arena, pos) |
Returns all walkable, unoccupied tiles cardinally adjacent to pos. |
findBestAdjacentTarget(arena, self, playerPos, maxSteps) |
Finds the closest tile adjacent to the player that this entity can path to within maxSteps. |
findBestAdjacentTarget(arena, self, playerPos, maxSteps, entitySize) |
Size-aware variant: checks that the full entity footprint fits at each candidate tile. |
canPlaceFootprint(arena, anchor, entitySize) |
Returns true if a sized entity can occupy all footprint tiles at anchor (in-bounds, walkable, not enemy-occupied). |
hasCardinalLOS(arena, from, to, maxRange) |
Returns true if from and to share an axis and every tile between them is clear and walkable. |
getFleeTarget(arena, self, threat, maxSteps) |
Finds a tile 1-2 steps away from threat in the primary flee direction. Returns null if the enemy is stuck. |
EnemyAction Types
EnemyAction is a sealed interface. Return one of these records from decideAction. The records marked with an asterisk carry additional fields described below the table.
| Record | Fields | Description |
|---|---|---|
Move | path | Move along a list of grid positions. The enemy does not attack. |
Attack | damage | Attack the player from the current position. |
MoveAndAttack | path, damage | Move, then attack. |
MoveAttackMove | approachPath, damage, retreatPath | Move in, attack, then reposition in the same turn using remaining movement. |
Flee | path | Move away from the player. Does not attack. |
Idle | Do nothing this turn. | |
Teleport | target | Instantly relocate to a tile with no movement animation. |
TeleportAndAttack | target, damage | Teleport, then immediately attack. |
Pounce | landingPos, damage | Leap over one tile gap to land adjacent and attack. |
Explode | damage, radius, blastEffects | AoE explosion centered on self. blastEffects is a list of BlastEffect(effectType, turns, amplifier) applied to the player on hit. Use the two-arg constructor for no status effects. |
StartFuse | Begin a fuse countdown (creeper-style). Pair with Detonate on the next turn. | |
Detonate | Resolve a pending fuse explosion. | |
RangedAttack | damage, effectName | Attack from range without moving (potion throw, arrow, etc.). |
Swoop | path, damage | Sweep along a line; deal damage if the player is in the path. |
AttackMob | targetEntityId, damage | Attack another mob (predator-prey behavior) instead of the player. |
MoveAndAttackMob | path, targetEntityId, damage | Move, then attack another mob. |
AttackWithKnockback | damage, knockbackTiles | Attack and push the player N tiles away from the attacker. |
MoveAndAttackWithKnockback | path, damage, knockbackTiles | Move, then attack with knockback. |
MimicDash | dirX, dirZ, damage | Charge in a cardinal direction until blocked; shove players or allies in the path sideways. |
MimicTantrum | path, damage | Hop through an ordered list of tiles; stop and deal damage if any hop lands on the player. |
SummonMinions | entityTypeId, count, positions, hp, atk, def | Boss action: spawn minions at the given positions. |
AreaAttack | center, radius, damage, effectName | Hit all entities within radius tiles of center. |
CreateTerrain | tiles, terrainType, duration | Place or transform terrain tiles. duration of 0 is permanent. |
LineAttack | start, dx, dz, length, damage | Hit every tile along a line from start in direction (dx, dz). |
ModifySelf | stat, amount, duration | Boss action: temporarily change the enemy's own stat. duration of 0 is permanent. |
ForcedMovement | targetEntityId, dx, dz, tiles | Push a target entity in direction (dx, dz). Use targetEntityId = -1 to target the player. |
BossAbility | abilityName, resolvedAction, warningTiles | Telegraph a warning this turn; the resolvedAction executes next turn. |
CeilingAscend | Rise off the grid for one turn (spider ceiling mechanic). | |
CeilingDrop | landingPos, damage | Drop from ceiling onto a tile near the player and attack. |
SpawnProjectile | entityTypeId, positions, directions, hp, atk, def, projectileType | Boss action: create traveling projectile entities. |
ProjectileMove | path, impacts, impactPos | Advance a projectile; impacts = true means it collides at path end. |
CompositeAction | actions | Execute multiple actions in sequence in a single turn. |
Supported Mob Types (Built-in)
These mobs already have registered AI strategies. You can use them in enemy templates and biome JSON without writing any code.
- Passive:
minecraft:cow,minecraft:pig,minecraft:sheep,minecraft:chicken,minecraft:horse,minecraft:donkey,minecraft:panda,minecraft:parrot,minecraft:rabbit,minecraft:bat,minecraft:cod,minecraft:salmon,minecraft:axolotl,minecraft:cat,minecraft:ocelot,minecraft:fox,minecraft:bee,minecraft:wolf,minecraft:goat,minecraft:polar_bear,minecraft:llama - Hostile:
minecraft:zombie,minecraft:zombie_villager,minecraft:husk,minecraft:drowned,minecraft:zombified_piglin,minecraft:skeleton,minecraft:stray,minecraft:bogged,minecraft:spider,minecraft:cave_spider,minecraft:creeper,minecraft:vindicator,minecraft:evoker,minecraft:pillager,minecraft:ravager,minecraft:witch,minecraft:enderman,minecraft:endermite,minecraft:phantom,minecraft:silverfish,minecraft:slime,minecraft:hoglin,minecraft:piglin,minecraft:blaze,minecraft:ghast,minecraft:shulker,minecraft:breeze,minecraft:magma_cube,minecraft:wither_skeleton - Boss:
minecraft:warden,minecraft:ender_dragon
Example: Custom Ranged AI
import com.crackedgames.craftics.combat.ai.EnemyAI;
import com.crackedgames.craftics.combat.ai.EnemyAction;
import com.crackedgames.craftics.combat.ai.AIUtils;
// A simple ranged AI: attack in place if in range, else close the distance.
EnemyAI archerAI = (self, arena, playerPos) -> {
int dist = self.getGridPos().manhattanDistance(playerPos);
int range = self.getRange();
if (dist <= range && AIUtils.hasCardinalLOS(arena, self.getGridPos(), playerPos, range)) {
// Already in range with a clear line: shoot.
return new EnemyAction.RangedAttack(self.getAttackPower(), "arrow");
}
// Out of range or blocked: move toward the player.
return AIUtils.seekOrWander(self, arena, playerPos);
};
CrafticsAPI.registerAI("mymod:dungeon_archer", archerAI);
Allies
Combat allies are mobs recruited from the player's hub that fight alongside them in the arena. Allies have their own combat stats and can optionally scale with the owner's gear, accept a heal item during combat, or run a custom per-round effect. Allies are registered with CrafticsAPI.registerAlly().
Like enemies, allies can be defined in JSON datapacks. Datapack allies always use the default melee AI. Allies that need a custom AI strategy, a per-round hook, or programmatic stat logic must be registered through the Java API.
Java API
AllyEntry entry = AllyEntry.builder("minecraft:wolf")
.hp(12)
.attack(4)
.defense(1)
.range(1)
.speed(3)
.recruitMode(AllyEntry.RecruitMode.TAMED)
.scalesWithOwnerGear(true)
.healItem(Items.BONE, 6)
.build();
CrafticsAPI.registerAlly(entry);
| Builder Method | Type | Default | Description |
|---|---|---|---|
builder(entityTypeId) |
String | required | The Minecraft entity type this entry describes, e.g. "minecraft:wolf". |
hp(int) |
int | 6 | Base health. |
attack(int) |
int | 1 | Base attack. |
defense(int) |
int | 0 | Base defense. |
range(int) |
int | 1 | Attack range in tiles. |
speed(int) |
int | 2 | Movement tiles per turn. |
recruitMode(RecruitMode) |
enum | TAMED | How the ally is collected from the hub. See the RecruitMode table below. |
ai(AllyAI) |
AllyAI | MeleeAllyAI | Combat behavior. Defaults to AllyEntry.DEFAULT_AI, which is a standard melee strategy. |
scalesWithOwnerGear(boolean) |
boolean | true | When true, the ally's attack gains bonuses from the owner's armor and trim. |
roundHook(AllyRoundHook) |
AllyRoundHook | null | Optional callback invoked at the start of each combat round. Signature: (CombatEntity self, EnvironmentDef environment). Use for aura effects, per-round stat changes, or environment-reactive triggers. |
healItem(Item, int) |
Item, int | null, 0 | Binds a heal item: using this item on the ally during combat restores the given amount of HP. |
RecruitMode Values
| Value | Description |
|---|---|
TAMED |
The mob must be tamed and owned by the hub's player (wolves, cats, horses, and similar). Default. |
BUILT |
Any mob of this type present in the hub yard qualifies, with no taming or ownership requirement. Use for golems and constructed mobs. |
IN_COMBAT_ONLY |
Never recruited from the hub. The entry exists only to define combat stats for a mob tamed mid-battle. |
JSON Datapack Schema
Place JSON files at data/<namespace>/craftics/allies/<name>.json. The entity field is required. Datapack allies always use the default melee AI and cannot have a per-round hook.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
entity |
string | yes | Minecraft entity type, e.g. "minecraft:wolf". Must be a loaded entity type. |
|
hp |
int | no | 6 | Base health. |
attack |
int | no | 1 | Base attack. |
defense |
int | no | 0 | Base defense. |
range |
int | no | 1 | Attack range in tiles. |
speed |
int | no | 2 | Movement tiles per turn. |
recruit_mode |
string | no | "TAMED" |
Recruitment requirement. Accepts "TAMED", "BUILT", or "IN_COMBAT_ONLY" (case-insensitive). Unknown values fall back to TAMED with a warning. |
scales_with_owner_gear |
boolean | no | true | Whether the ally's attack gains bonuses from the owner's armor and trim. |
heal_item |
string | no | Item id that heals this ally when used on it in combat, e.g. "minecraft:bone". Ignored if the item is not loaded. |
|
heal_amount |
int | no | 0 | HP restored by heal_item. Only meaningful when heal_item is set. |
Example JSON
// data/mymod/craftics/allies/iron_golem.json
{
"entity": "minecraft:iron_golem",
"hp": 20,
"attack": 6,
"defense": 3,
"range": 1,
"speed": 1,
"recruit_mode": "BUILT",
"scales_with_owner_gear": false
}
Example: Custom Ally with a Round Hook
Round hooks require the Java API. The hook receives the living ally as a CombatEntity and the arena's EnvironmentDef. It is called once per round for each living ally of this type.
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.registry.AllyEntry;
CrafticsAPI.registerAlly(AllyEntry.builder("minecraft:cat")
.hp(8)
.attack(2)
.defense(0)
.range(1)
.speed(3)
.recruitMode(AllyEntry.RecruitMode.TAMED)
.scalesWithOwnerGear(false)
.roundHook((self, environment) -> {
// Round hook receives the ally itself and the arena environment.
// Use self to read or modify the ally's own combat state each round.
})
.build());
MeleeAllyAI) and cannot have a custom roundHook. If your ally needs custom combat behavior or per-round effects, register it through CrafticsAPI.registerAlly() in your onCrafticsInit() method instead.