v0.4.0
What's new
0.4.0
Addon API: A Bench, and Switching Off It
- A field ally provider can now declare reserves alongside the allies it fields: creatures carried into the fight with no tile and no mob in the world, fielded only when the player swaps one in. A party larger than the field is the point of having a party - six creatures where three fight is a different game from six all swinging at once, and choosing which three is the interesting part
- A switch costs 1 AP and puts the incoming creature on the tile the outgoing one vacated. It is refused, with no AP spent, when it is not your turn, when the ally is not yours, when someone is riding it, or when the incoming creature is too large for the tile being freed
- A benched creature keeps everything it was carrying. Bench a wounded, poisoned ally and it comes back wounded and poisoned with its summon timer still running - the combatant itself goes to the bench rather than being rebuilt from its definition, so every scrap of per-fight state rides along, including state added later that nobody remembers to copy. A bench that healed would be the cheapest heal in the game
- No menu. Craftics owns what a switch means and the addon owns the screen the player picked from, the same split the combat tools use: read the bench, draw it however you like, ask for the swap. A party-selection UI is your design
- Reserves are addressed by index rather than entity id, because a benched ally has no entity - no mob, no tile, nothing to be found by. That absence is what being benched is
- Only provider allies get a bench, and Craftics' own hub pets deliberately do not. A hub pet is a real animal that was standing in your yard and is owed back to it; one that is neither in the yard nor in the fight is an animal in no place at all. Every end-of-fight path filters on exactly the flag that excludes it, and each would have to learn about a bench before one could be safe - where a miss duplicates the animal and a false positive destroys it, since the hub copy was discarded when the party was collected
Accuracy
- Attacks can now miss. Accuracy is a per-action multiplier living beside the attack type, on the same slot with the same lifecycle: set for the one action an AI is about to name, cleared before the next decision. A creature whose movepool holds a wild haymaker and a reliable jab needs the haymaker to land less often, and nothing about the defender expresses that
- Distinct from the armor-class dodge, which is the defender's property and answers "did I get out of the way". Accuracy is the attack's own
- Inert until something asks for it. Every attack defaults to always landing, and a certain hit deliberately draws no randomness at all - so adding the roll to the damage paths does not shift any other roll in the fight. Had it drawn and discarded, every dodge and crit downstream would have started landing differently on the day this shipped, which reads as a balance change nobody made
- A miss skips the hit outright rather than dealing zero. Zero already means three other things, and a hit that "lands for 0" still fires every on-hit rider behind it - knockback, thorns, weapon debuffs, counters
- The player's existing ranged miss now runs through the same roll, and no longer cancels an empty-tile cone or sweep: that swing was aimed at a tile, the anchor enemy is only there to orient the shape, and there was never anything to miss
onMissfires. The addon hook has been declared since the effect API existed and was invoked from nowhere
Blinded Pets
- Fixed blindness doing nothing whatsoever to an ally. Enemies could apply it, the HUD showed it ticking down, and no ally code path ever read it - the debuff was applied to a wolf and then simply ignored
- A blinded ally now swings at half accuracy for the turn instead of losing it. Enemies fumble their whole turn to blindness at a check allies never reach, and copying that would have a pet silently forfeit turns, which reads as the game being broken; one that visibly swings and misses reads as blinded
Stale Per-Action Overrides
- Fixed an attack-type override outliving the action it was set for. The clear sat inside the branch that consults the AI, while the two branches that return an action without consulting it - a burning mob running for water, one that has lost sight of its target - skipped it, leaving the override to silently retype every later attack that never asked for one
Addon API: Spawning What You Are, Not What Your Entity Type Says
- Enemy and ally entries can now carry spawn NBT, merged onto the mob the moment it appears. A datapack can author it as an SNBT string, the same syntax /summon takes, so variant mobs need no Java at all
- For what NBT cannot express - an entity that has to be initialised through its own mod's API - there is a spawn customizer hook that runs on the live mob before its first turn
- Both are looked up by the combatant's AI key first and its entity type second. That ordering is the point: a mod that ships ONE entity type for hundreds of creatures can give each its own initialisation while they all share a type. Without it, every one of them spawns blank and identical
- The arena's own flags are re-applied after any NBT merge. Loading NBT onto a live entity restores its whole serialized state, so an authored tag would happily switch AI and gravity back on and let a mob wander off its tile. Your NBT decides what the mob IS; the arena decides how it is held
Addon API: Attack Types
- A new third idea alongside damage types and affinities. An attack type is a trait of the attack itself - nobody levels it - and it exists only to be compared against what the defender is, producing a multiplier
- All three are orthogonal. A weapon can be Slashing damage, so it scales from the Slashing affinity, while being typed Fire, so it lands hard on a grass defender and poorly on a water one. Retyping it changes nothing about what the player levels to improve it
- Effectiveness is authored as a chart per attacking type - what it is strong and weak against - and defenders simply declare what they ARE. That is the only shape that scales: a thousand creatures across eighteen types needs eighteen chart entries and one line per creature, where per-creature resistance tables would need eighteen thousand cells
- Dual types multiply through, so strong against one and weak against the other cancels out. Immunity is absorbing - nothing later brings it back above zero
- Typing applies in every direction: player on enemy, enemy on ally, ally on enemy, and enemy on player. A mob types its own attacks once, or an AI overrides it per action for a creature with a movepool. The player's defending types come from a provider, since a player's typing usually derives from something that changes mid-run
- A mount that intercepts a hit aimed at its rider is judged as the mount, because the animal is the thing being hit
- Players are told what happened - "It's super effective!", "It's not very effective...", "It has no effect..." - so matchups are legible without a wiki
- Inert until something opts in. An untyped weapon, an unregistered type or an untyped defender all return a plain 1x, so nothing about existing combat changes
Addon API: Custom Enemy Actions
- An addon can define an enemy action whose resolution is genuinely new, rather than composing the forty shapes Craftics already has
- Deliberately one extra member of a sealed set rather than an open interface. The turn machine dispatches with pattern-matching switches the compiler checks for exhaustiveness; unsealing would surrender that across dozens of sites, and an unhandled action would look like an enemy that just stands there
- Handlers are given a context whose damage and movement methods route through Craftics' own pipeline, so an addon action cannot accidentally skip resistances, typings, shields, death handling or the pit-fall check
- Wrap one in a boss ability and it inherits the whole telegraph system: warning tiles, the wind-up VFX and the one-turn delay, with the handler firing when it resolves
- An unregistered action id costs that enemy its turn and logs once. An addon can be uninstalled while a save still holds an AI that names its actions, and a missing handler should not wedge the fight waiting for something that will never resolve
Addon API: Custom Actions in a Player's Hands
- Fixed an ally handed a custom action doing nothing at all with it. The ally turn resolved only move, attack, flee and idle, and dispatched custom actions from the enemy path alone - so an ordered custom action fell through to idle and quietly burned the turn. An addon's move worked completely in an enemy's hands and landed as a single plain hit in yours, which made the same move strictly better on the side you were fighting
- A handler's damage now routes by whose action it is rather than assuming an enemy threw it. An ally's action hits creatures and never falls through to the player; previously anything an ally targeted that was not itself was read as "the player", so a pet's own move hit its owner
- Handlers can name the player outright with
damagePlayer. Before this the player was whatever was left after ruling out allies and the actor, so an action that meant the player had to pass a throwaway combatant as a token, and an ally's action had no way to say it at all - Attack-type effectiveness is applied on the handler's behalf for creature targets, so an addon move deals the same damage from either side. It changes nothing for an action that never declared a typing
Commanded Moves Losing Their Typing
- Fixed the ally turn clearing the pending attack type and accuracy before reading the standing order. An addon sets both when it issues a command and the order is obeyed a turn later, so every commanded move arrived untyped and at default accuracy - leaving the ally AI as the only way to give an ally a typed move, which is the wrong seam for a player-issued order
- Blindness now scales an accuracy the attack already brought instead of replacing it. Overwriting meant a wild haymaker was more likely to land while blinded than while sighted, so the debuff read as a buff on exactly the attacks it should punish hardest. Two sources of blindness now compound, and no stack of them can grind a combatant down to unable-to-act
Modded Weapons and Armor Now Work on Install
- Modded weapons that nothing registered used to hit for a bare fist, and modded armor was worth no Armor Class at all. Both failed silently: a whole weapon pack equipped normally, showed a tooltip, and left every blade in it identical and useless with nothing on screen to say why. Craftics now works stats out from the item itself, the same way it has always read a modded food's heal value off its own nutrition
- Damage is not a formula. Craftics' own numbers are hand-tuned and do not track vanilla - a netherite axe hits for 27 where vanilla gives it 10 - so a modded weapon is placed on Craftics' existing ladder by interpolation instead. A sword sitting between iron and diamond in vanilla terms gets a Craftics number between iron and diamond
- The ladder is read from the live vanilla items at runtime rather than hardcoded, so it follows the Minecraft version and the server's own damage config instead of drifting from them. A weapon far above netherite is clamped rather than extrapolated: a joke sword with 400 attack damage does not get 400 damage here
- Weapon shape is read from the item's name and decides damage type, AP cost, reach and signature trick, using the families Craftics already supports: daggers and sai, chakrams and other thrown blades, warglaives, spears, halberds and glaives, scythes, greatswords, greataxes, warhammers, bows. A modded halberd reaches a tile the way a Craftics halberd does
- Compound names beat the words inside them, so a greataxe is not an axe and a warglaive is not a glaive. Tools are excluded outright - a modded pickaxe contains "axe" and does not become a battleaxe - and anything that reads as no weapon at all is left alone rather than guessed at
- Armor gets an affinity too, chosen from its material's name, because every Craftics set grants one and a set granting none reads as broken next to the rest. A modded variant of a vanilla material lands where the player already expects: "reinforced iron" boosts Cleaving exactly like iron. Armor Class comes from the piece's own rating and toughness, measured against the ladder for its own slot, since a chestplate carries three times a helmet's points and one shared ladder would call every helmet leather
- Toughness counts on its own, because diamond and netherite carry identical armor points and differ only in it - a score built on points alone would hand every modded end-tier armor diamond's number
- An explicit registration always wins. Inference only ever fills a gap, so a compat module or a datapack correcting a guess is never fighting it. Off entirely with
autoIntegrateModdedGearin the config
Addon API: Clicking an Ally
- An addon can now handle a player clicking one of their own allies. Craftics did exactly one thing with that click - heal, if the player held the ally's registered heal item - and refused everything else, which is a closed set and the wrong shape for an addon whose allies are the point of the mod
- There was no way to reach it from outside: grid clicks arrive on Craftics' own packet and go straight into the attack path, so no Fabric event ever sees them. This is the general form of the heal-item hook that was already sitting there
- Handlers get first refusal, before the heal-item check, and decline by returning false so anything they do not recognise still heals as it always did. Craftics charges no AP for an ally click, so a handler that should cost something spends it itself
Diagnostics: Unclaimed Spawn Keys
- Craftics now says once, per key, when a combatant was given an AI key that no spawn customizer is registered for, and lists the keys that are registered. An undressed combatant looks exactly like a generic one, so the only way to tell "my customizer ran and did nothing" from "my customizer was never called" was to add logging on the addon side and guess between them
- Only for a combatant whose AI key differs from its entity type, since that difference means somebody set the key deliberately. An ordinary mob nobody intended to customise stays silent, so a vanilla install logs nothing
Addon API: Combat Portraits
- An addon can now draw the combatant icons in the combat HUD itself. Craftics picks a head texture by entity type, which fails completely for a mod whose single entity type stands in for hundreds of creatures: no icon registered for that type could be right for more than one of them, so every combatant fell through to a coloured square with a letter in it
- The renderer is handed the entity id, not just the type, because the roster is already keyed by it and the creature is standing in the client world - so an addon can reach the live entity and draw whatever its own screens draw, rather than being limited to a flat texture it would have had to invent
- One registration covers all four places a combatant icon appears: both rosters, the turn-order strip and the hover inspect panel. A portrait in one panel and a blank square in the next reads worse than blank squares everywhere
- Renderers get first refusal and do not replace the fallback, so anything unclaimed still gets its head texture or coloured square. The damage tint is passed through, so a portrait can redden by health the way the enemy column already does
- A renderer that throws is logged once and then never asked again for the session. This runs once per combatant per frame, and something that fails once fails sixty times a second
Addon API: Hiding HUD Panels
- A combat HUD panel can now be turned off by an addon that draws its own version of the same thing. A compat mod whose party screen already lists your creatures had no way to stop Craftics stacking an ally roster underneath it, which is two lists of the same information competing for one corner of the screen
- Four panels are addressable rather than one switch for the ally list, because the redundancy argument is never about one panel for long: a mod that replaces the party list usually replaces the opposing side's list too
- Hiding the enemy roster keeps hover inspection. Pointing at an enemy, an ally or a party member still opens its stat panel - that is a different feature, and replacing a list is not a reason to lose it
- Suppression is visual only. Craftics keeps tracking and syncing everything the panel would have shown, so a panel turned back on mid-fight shows the truth immediately instead of catching up
- Players get their own toggles for the ally and enemy rosters in the config's Visual section. Neither side overrules the other: an addon cannot force back a panel the player turned off, and a player who never touched the setting does not undo an addon that replaced it
Title Screen Crash on Addon Biomes
- Fixed the menu crashing every frame once any addon campaign was installed. A biome id was concatenated straight into a texture path, and a resource location rejects the
:in a namespaced id, so building the path threw instead of returning something that merely fails to resolve. The "no card art, draw a flat backdrop" fallback was on the very next line and could never be reached - Craftics' own biomes are all bare - plains, cave, deep_dark - so nothing in the base game ever produced an id that could trip it. The documented convention for addon biomes is
namespace:path, which the addon template's own example uses, so this hit every code-registered addon campaign immediately and no vanilla install ever - The same concatenation existed at five places, not one: the title screen backdrop and its save-card thumbnail, two in level select, and the world-icon writer. All five now go through one resolver that cannot throw whatever the id looks like
- Addon card art is now looked up in the addon's own namespace:
mymod:cavernreadsassets/mymod/textures/gui/biomes/cavern.png. Two addons naming a biome the same thing no longer overwrite each other's cards
Addon API: An Event Can Become a Fight
- Fixed a forced event being unable to turn into a level. The non-choice path built its arena from the level it had queued before the handler ran, and cleared the pending level first, so a handler that asked to run its own fight was silently overruled and the event could only hand out rewards. That rules out the entire shape a trainer battle, a gym or a scripted ambush needs, and left choice events as the only way to reach it
EventManager.setPendingNextLevelis now read back. It was public, it was on the object every handler is handed, and nothing anywhere called it - so the setter an author reaches for first did nothing at all on either path- Both paths now resolve the same way: whatever the handler set on its EventManager, else whatever it set on the combat manager, else the level already queued. A handler that starts its own fight instead of queuing one is left alone rather than having a second arena built on top of it
Raid Softlock After the First Round
- Fixed a raid locking up the moment the opening round ended, with the last player shown as still taking their turn and nobody able to act or pass. The raid's prep turn skips the first enemy phase so the party can reposition before any pillager fires, and that shortcut jumped straight back to the player phase without handing the round to the first player. The turn counter had already wound back, so every client was told it was the first player's turn while the server still believed it belonged to whoever ended the round: the first player's input was rejected as out of turn, and the last player's client never offered the button
- The AFK timer could not rescue it either, because it only counts down during a turn it believes someone is holding. That is why the fight sat there indefinitely instead of timing out
- The Phantom armor bonus skips the enemy phase the same way and had the same latent bug in any party fight, not just raids. Both now share one hand-off, so a third skip cannot reintroduce it
Compat Item Stats Missing for Everyone but the Host
- Fixed instrument, paladin and simply-swords items showing no Craftics stats for anyone except the world's host. Compat gear is registered late, after every other mod's items exist, and the three places that trigger it each kept their own hand-written list which had drifted apart. Instruments and paladins were only on the list the server runs, and a multiplayer client never reaches that one
- The host was the one player who could not see the problem, since their client shares a process with the server and picks the registration up for free. From every other seat it looked like a permissions bug
- All three now call one shared list, so a newly added compat cannot be registered in some sessions and not others
Pets Not Coming Home After a Fight
- Fixed a party member's pet failing to return to their island. The restore worked out which island the animal belonged to and then spawned it into whichever world the restore was running for, so a guest's wolf was placed at its own island's coordinates inside the host's dimension. From the owner's side the animal simply never came back
- Solo play was never affected, which is why it survived this long: with one player the two worlds are the same world
- A pet restored without stored NBT is also re-tamed to its actual owner rather than to whoever the fight was resolved for, and everyone who got an animal back now has their party list resynced instead of only the player the restore was called for
Allies With Nowhere to Stand
- A fielded ally that cannot be placed now says so in chat instead of only in the server log. With a handful of tamed wolves a full arena was near-impossible to hit, so a log line was enough; an addon fielding a party of six on a cramped floor reaches it easily, and a creature that silently never turns up reads as the game having lost it
- Reported once with a count rather than once per ally, so a tight arena reads as a tight arena instead of a stack of errors
Addon API: Sending Out From an Empty Field
- A trainer can now field a benched creature onto an empty tile with nothing withdrawn. Switching captures the outgoing creature's live state onto the bench, so it needs one on the field to capture; when a trainer's last creature was knocked out there was nothing to withdraw, the bench was stranded, and the trainer conceded with reserves left - the opposite of how a gym battle ends
- The reserve is removed from the bench rather than swapped, since nothing is coming back to take its place. Swapping would leave the same creature sittable twice
- It is fielded through the same path a switch uses, so its NBT, spawn customizer, AI key and typing land exactly as if it had started the fight on the grid. Refused, with the bench untouched, when the tile is out of bounds, occupied, not standable, or too small for the creature's footprint
Addon API: Allies From Outside the Hub
- Craftics' battle party is built from real mobs standing in the hub: you tag a wolf, it is snapshotted and put back afterwards. A mod whose party is DATA ON THE PLAYER has no wolf to tag, so there is now a field ally provider hook for it
- Provider allies may each declare their own AI key, spawn NBT and display name, which is what lets a single entity type field a whole party of visibly different creatures. Without the name, six creatures sharing an entity type all read as six copies of the same thing
- They are fielded as temporary: they fight the battle and are gone, never carried between levels and never materialised into the hub. An ally that was never a hub entity must not be "returned" to one, or the player ends up with a second copy of a creature the owning mod is still tracking
- The party cap is passed through as advisory and the result is deliberately not truncated to it. The cap is written for tamed wolves; a mod with a six-creature party owns its own rules, and silently cutting that to one would look like a Craftics bug
Addon API: Affinity Reskins
- The eight affinities can be renamed and re-iconed. Their number stays fixed because they are the axes level-up points are spent on and the screens are laid out for exactly eight; what a total-conversion mod needs is not more axes but different ones
- One call renames the affinity everywhere it is shown: the level-up screen, the respec screen, the Infinite Mode class picker, the damage-type panel, weapon tooltips including the Simply Swords and Simply Bows compat ones, the combat damage feedback line with its Resisted and Weak notes, and both chat messages
- The damage type that scales from the affinity is renamed with it. A player who saw "Fire affinity" next to "Slashing damage" would read it as a bug rather than a theme
- Nothing mechanical changes, and points are saved by internal name, so a reskin can be added to or removed from a live world without touching player progress
Allies Leaking Into the Hub
- Fixed temporary allies - spawn-egg summons, and now provider-supplied party creatures - being materialised into the hub world as real mobs when combat ended abnormally
- The normal end-of-fight path already excluded them correctly. The abnormal-exit rescue path, which recovers allies when a fight is torn down unexpectedly, rescued every living ally without checking, so a summon that should have evaporated turned into a permanent animal standing in your hub
All Relations
- All Relations
- Embedded Library
- Optional Dependency
- Required Dependency
- Tool
- Incompatible
- Include

