Gear
Register custom weapons, armor sets, hybrid subclasses, trim patterns, trim materials, enchantments, and equipment scanners.
Weapons
Any Item can be registered as a Craftics weapon. The registration gives it a damage type, base stats, and an optional on-hit ability. Call CrafticsAPI.registerWeapon(item, entry) during onCrafticsInit().
API Method
CrafticsAPI.registerWeapon(Item item, WeaponEntry entry);
WeaponEntry Builder
WeaponEntry entry = WeaponEntry.builder(myItem)
.damageType(DamageType.SLASHING) // damage category
.attackPower(8) // flat attack value
.apCost(1) // action points per attack
.range(1) // attack range in tiles
.ranged(false) // true for projectile weapons
.breakChance(0.0) // probability of breaking on use (0.0 to 1.0)
.ability(myAbilityHandler) // optional on-hit ability
.build();
| Builder Method | Type | Default | Description |
|---|---|---|---|
damageType(DamageType) | enum | PHYSICAL | Weapon's damage category |
attackPower(int) | int | 1 | Base attack power |
attackPower(IntSupplier) | supplier | Dynamic attack power computed each time the weapon attacks | |
apCost(int) | int | 1 | Action point cost per attack |
range(int) | int | 1 | Attack range in tiles |
ranged(boolean) | bool | false | Whether this is a projectile weapon |
breakChance(double) | double | 0.0 | Chance to break on use (0.0 to 1.0) |
ability(WeaponAbilityHandler) | handler | null | On-hit ability (see below) |
DamageType Values
Every weapon belongs to one of eight damage types. Armor sets, trims, and player affinity points grant bonuses to specific types.
| Value | Display Name | Typical Weapons |
|---|---|---|
SLASHING | Slashing | Swords |
CLEAVING | Cleaving | Axes |
BLUNT | Blunt | Maces, shovels |
WATER | Water | Tridents, corals |
SPECIAL | Special | Blaze rods, end rods, breeze rods |
PET | Pet | Pet and ally attacks |
RANGED | Ranged | Bows, crossbows |
PHYSICAL | Physical | Default / untyped weapons |
Built-in Ability Factories (Abilities class)
The Abilities class provides static factory methods that return WeaponAbilityHandler instances. Compose multiple abilities with .and().
WeaponAbilityHandler handler = Abilities.bleed()
.and(Abilities.sweepAdjacent(0.10, 0.05));
| Factory Method | Description |
|---|---|
bleed() | Applies bleed stacks equal to the weapon's Sharpness enchant level. |
sweepAdjacent(baseChance, bonusPerPoint) | Chance to hit one adjacent enemy for half damage. Scales with SLASHING affinity. Chance = baseChance + (affinity * bonusPerPoint) + (luck * 0.02). |
armorIgnore(baseChance, bonusPerPoint) | Chance to permanently destroy a portion of the target's defense and deal that amount as bonus damage. Scales with CLEAVING affinity. |
stun(baseChance, bonusPerPoint) | Chance to stun the target for one turn. Scales with BLUNT affinity. |
knockbackDirection(distance) | Pushes the target away from the player by up to N tiles, checking bounds and walkability each step. |
aoe(radius, damageMultiplier) | Hits all non-ally enemies within radius (Manhattan distance) of the target for (int)(baseDamage * damageMultiplier). |
applyEffect(type, turns, amplifier) | Applies a status effect to the target. Supported types: POISON, BURNING, SOAKED, SLOWNESS, CONFUSION. |
pierce() | Hits the first enemy directly behind the target in the attack direction for full base damage. |
fireDamage(bonusDmg) | Sets the target on fire and deals flat bonus fire damage. |
Custom Ability Handler
WeaponAbilityHandler is a @FunctionalInterface. Implement it directly for fully custom on-hit logic.
@FunctionalInterface
public interface WeaponAbilityHandler {
WeaponAbility.AttackResult apply(
ServerPlayerEntity player,
CombatEntity target,
GridArena arena,
int baseDamage,
PlayerProgression.PlayerStats stats,
int luckPoints
);
// Chain with another handler:
default WeaponAbilityHandler and(WeaponAbilityHandler next);
}
Return a WeaponAbility.AttackResult(totalDamage, messages, extraTargets). The messages list holds combat log strings and extraTargets lists any additional entities your ability hit.
JSON Datapack Schema
Place weapon JSON files at data/<namespace>/craftics/weapons/*.json. The ability array is an ordered list of built-in ability blocks chained together.
| Field | Type | Required | Description |
|---|---|---|---|
item | string | yes | Full item registry ID, e.g. minecraft:diamond_sword |
damage_type | string | no | DamageType name (case-insensitive). Defaults to PHYSICAL. |
attack_power | int | no | Base attack value. Defaults to 1. |
ap_cost | int | no | Action point cost per attack. Defaults to 1. |
range | int | no | Attack range in tiles. Defaults to 1. |
ranged | bool | no | Whether this is a projectile weapon. Defaults to false. |
break_chance | float | no | Probability of breaking on use (0.0 to 1.0). Defaults to 0.0. |
ability | array | no | Ordered list of ability objects (see ability kinds below). |
Ability object kind values and their fields:
| kind | Extra Fields |
|---|---|
bleed | (none) |
pierce | (none) |
sweep | base_chance (float, default 0.10), bonus_per_point (float, default 0.05) |
armor_ignore | base_chance (float, default 0.10), bonus_per_point (float, default 0.05) |
stun | base_chance (float, default 0.10), bonus_per_point (float, default 0.05) |
knockback | distance (int, default 1) |
aoe | radius (int, default 1), damage_multiplier (float, default 0.5) |
fire_damage | bonus_damage (int, default 2) |
apply_effect | effect (string, e.g. POISON), turns (int, default 3), amplifier (int, default 0) |
{
"item": "minecraft:diamond_sword",
"damage_type": "slashing",
"attack_power": 7,
"ap_cost": 1,
"range": 1,
"ranged": false,
"break_chance": 0.0,
"ability": [
{ "kind": "bleed" },
{ "kind": "sweep", "base_chance": 0.1, "bonus_per_point": 0.05 }
]
}
Code Example
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.Abilities;
import com.crackedgames.craftics.api.registry.WeaponEntry;
import com.crackedgames.craftics.combat.DamageType;
// Heavy Claymore: cleaving type, armor ignore + knockback
Item claymore = Registries.ITEM.get(Identifier.of("mymod", "heavy_claymore"));
CrafticsAPI.registerWeapon(claymore, WeaponEntry.builder(claymore)
.damageType(DamageType.CLEAVING)
.attackPower(7)
.apCost(2)
.range(1)
.ability(Abilities.armorIgnore(0.15, 0.03)
.and(Abilities.knockbackDirection(2)))
.build());
Armor Sets
Armor sets grant per-piece damage affinity bonuses and full-set stat bonuses when all four matching armor pieces are worn. The set ID must match the material key that PlayerCombatStats.getArmorSet() derives from the player's equipped armor. Call CrafticsAPI.registerArmorSet(entry) during onCrafticsInit().
Damage affinity is per-piece: each worn piece of a material contributes half the damageBonus value for that type. A full four-piece set contributes twice the value. The flat stat bonuses (speedBonus, defenseBonus, etc.) apply only when all four pieces are worn.
API Method
CrafticsAPI.registerArmorSet(ArmorSetEntry entry);
ArmorSetEntry Builder
ArmorSetEntry entry = ArmorSetEntry.builder("mymod:mythril")
.damageBonus(DamageType.SLASHING, 2) // affinity value per 2 pieces (per-piece = 1)
.damageBonus(DamageType.SPECIAL, 1)
.allDamageBonus(1) // applied first; per-type entries override it
.speedBonus(1)
.apBonus(0)
.defenseBonus(3)
.attackBonus(1)
.apCostReduction(0)
.description("Mythril Armor: light and sharp")
.build();
CrafticsAPI.registerArmorSet(entry);
| Builder Method | Type | Default | Description |
|---|---|---|---|
damageBonus(DamageType, int) | per-type | 0 | Affinity value per 2 pieces for this damage type. A single piece grants half this amount. |
allDamageBonus(int) | all types | Sets the same affinity value for every damage type. Per-type entries override it for that specific type. | |
speedBonus(int) | int | 0 | Movement speed bonus (full set only) |
apBonus(int) | int | 0 | Extra action points per turn (full set only) |
defenseBonus(int) | int | 0 | Defense stat bonus (full set only) |
attackBonus(int) | int | 0 | Base attack bonus (full set only) |
apCostReduction(int) | int | 0 | Reduction in AP cost per attack (full set only) |
description(String) | string | "" | Tooltip description shown in the combat HUD |
JSON Datapack Schema
Place armor set JSON files at data/<namespace>/craftics/armor_sets/*.json. The id must match the armor-set name your material produces.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Armor-set material key, e.g. mythril |
description | string | no | Tooltip description |
all_damage_bonus | int | no | Affinity value applied to every damage type first |
damage_bonuses | array | no | Per-type affinity overrides: each object has type (DamageType name) and amount (int) |
speed_bonus | int | no | Speed bonus (full set only) |
ap_bonus | int | no | Extra action points (full set only) |
defense_bonus | int | no | Defense bonus (full set only) |
attack_bonus | int | no | Attack bonus (full set only) |
ap_cost_reduction | int | no | AP cost reduction per attack (full set only) |
{
"id": "mythril",
"description": "Mythril Set: light and sharp",
"all_damage_bonus": 0,
"damage_bonuses": [
{ "type": "SLASHING", "amount": 2 },
{ "type": "SPECIAL", "amount": 1 }
],
"speed_bonus": 1,
"ap_bonus": 0,
"defense_bonus": 3,
"attack_bonus": 1,
"ap_cost_reduction": 0
}
Code Example
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.registry.ArmorSetEntry;
import com.crackedgames.craftics.combat.DamageType;
CrafticsAPI.registerArmorSet(ArmorSetEntry.builder("mythril")
.damageBonus(DamageType.SLASHING, 2)
.damageBonus(DamageType.SPECIAL, 1)
.speedBonus(1)
.defenseBonus(3)
.attackBonus(1)
.description("Mythril Armor: light and sharp")
.build());
Hybrid Sets
A hybrid set is a subclass bonus a player earns by wearing exactly two distinct armor materials at once. The pair is unordered: {iron, diamond} and {diamond, iron} resolve to the same hybrid. Call CrafticsAPI.registerHybridSet(entry) during onCrafticsInit().
API Method
CrafticsAPI.registerHybridSet(HybridSetEntry entry);
HybridSetEntry Builder
HybridSetEntry entry = HybridSetEntry.builder("iron", "diamond")
.className("Warlord")
.description("Build stacks on kill; spend them to empower your next attack.")
.effect(HybridEffect.WARLORD)
.build();
CrafticsAPI.registerHybridSet(entry);
| Builder Method / Field | Type | Required | Description |
|---|---|---|---|
builder(materialA, materialB) | yes | The two armor-set material keys. The builder normalizes the pair alphabetically so order does not matter. | |
className(String) | string | no | Subclass display name shown on the armor tooltip |
description(String) | string | no | One-line mechanic description shown on the armor tooltip |
effect(HybridEffect) | enum | yes | The combat mechanic applied during Phase 2 (see HybridEffect values below) |
HybridEffect Values
Pass one of these enum constants to .effect(). Each constant maps to a distinct combat mechanic implemented internally.
| HybridEffect | HybridEffect | HybridEffect |
|---|---|---|
SKIRMISHER |
COUNTERPUNCHER |
LUCKY_STREAK |
BREAKER |
RAMPAGE |
RUN_AND_GUN |
SENTINEL |
CUTPURSE |
DUELIST |
AMBUSH |
DEADEYE |
GILDED_GUARD |
WARLORD |
IMMOVABLE |
AEGIS |
GLADIATOR |
BERSERKER |
CONTAGION |
STONEWALL |
SIEGE |
STORMBRINGER |
JSON Datapack Schema
Place hybrid set JSON files at data/<namespace>/craftics/hybrid_sets/*.json. The three required fields are material_a, material_b, and effect.
| Field | Type | Required | Description |
|---|---|---|---|
material_a | string | yes | First armor material key (e.g. iron) |
material_b | string | yes | Second armor material key (e.g. diamond) |
effect | string | yes | HybridEffect name (case-insensitive) |
class_name | string | no | Subclass display name |
description | string | no | One-line mechanic description |
{
"material_a": "iron",
"material_b": "diamond",
"class_name": "Warlord",
"description": "Build stacks on kill; spend them to empower your next attack.",
"effect": "WARLORD"
}
Code Example
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.registry.HybridSetEntry;
import com.crackedgames.craftics.combat.HybridEffect;
CrafticsAPI.registerHybridSet(HybridSetEntry.builder("iron", "diamond")
.className("Warlord")
.description("Build stacks on kill; spend them to empower your next attack.")
.effect(HybridEffect.WARLORD)
.build());
Trim Patterns
Each trim pattern grants a per-piece stat bonus and a full-set bonus activated when all four armor pieces carry the same pattern. Register custom patterns for modded trim templates. Call CrafticsAPI.registerTrimPattern(entry) during onCrafticsInit().
The pattern id must match the registry path of the vanilla trim pattern, not the full namespaced ID. For example, minecraft:sentry uses "sentry".
API Method
CrafticsAPI.registerTrimPattern(TrimPatternEntry entry);
TrimPatternEntry Record
TrimPatternEntry is a record with no builder. Construct it directly.
CrafticsAPI.registerTrimPattern(new TrimPatternEntry(
"mymod:dragon", // patternId
TrimEffects.Bonus.MELEE_POWER, // perPieceStat
"+1 Melee Power per piece", // perPieceDescription
TrimEffects.SetBonus.FERAL, // setBonus
"Dragon's Fury", // setBonusName
"First attack each turn costs 0 AP" // setBonusDescription
));
| Field | Type | Description |
|---|---|---|
patternId | String | Registry path of the trim pattern (e.g. "mymod:dragon") |
perPieceStat | TrimEffects.Bonus | Stat bonus added once per armor piece carrying this pattern. Null means no per-piece bonus. |
perPieceDescription | String | Human-readable text for the per-piece bonus |
setBonus | TrimEffects.SetBonus | Bonus activated when all four pieces use this pattern. Use NONE for no set bonus. |
setBonusName | String | Display name for the set bonus |
setBonusDescription | String | Human-readable text for the set bonus |
JSON Datapack Schema
Place trim pattern JSON files at data/<namespace>/craftics/trim_patterns/*.json.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Registry path of the trim pattern |
per_piece_stat | string | no | TrimEffects.Bonus name (case-insensitive). Omit for no per-piece bonus. |
per_piece_description | string | no | Description of the per-piece bonus |
set_bonus | string | no | TrimEffects.SetBonus name (case-insensitive). Defaults to NONE. |
set_bonus_name | string | no | Display name for the set bonus |
set_bonus_description | string | no | Description of the set bonus |
{
"id": "sentry",
"per_piece_stat": "RANGED_POWER",
"per_piece_description": "+1 Ranged Power per trimmed piece",
"set_bonus": "OVERWATCH",
"set_bonus_name": "Overwatch",
"set_bonus_description": "Counter-attack ranged enemies that hit you"
}
TrimEffects.Bonus Values
| Bonus | Effect |
|---|---|
RANGED_POWER | Bonus ranged attack damage |
MELEE_POWER | Bonus melee damage (all melee types) |
SPEED | Movement speed (tiles per turn) |
AP | Extra action points per turn |
DEFENSE | Damage reduction |
LUCK | Affects loot rolls and ability proc chances |
ATTACK_RANGE | Extra attack range in tiles |
MAX_HP | Bonus maximum hit points |
ARMOR_PEN | Ignore a portion of enemy defense |
REGEN | HP regeneration per turn |
ALLY_DAMAGE | Bonus damage for pet and ally attacks |
STEALTH_RANGE | Reduced enemy detection range |
SWORD_POWER | Bonus SLASHING damage specifically |
CLEAVING_POWER | Bonus CLEAVING damage specifically |
BLUNT_POWER | Bonus BLUNT damage specifically |
WATER_POWER | Bonus WATER damage specifically |
SPECIAL_POWER | Bonus SPECIAL damage specifically |
TrimEffects.SetBonus Values
| SetBonus | Name | Effect |
|---|---|---|
NONE | No set bonus | |
OVERWATCH | Sentry | Counter-attack ranged enemies that hit you |
SANDSTORM | Dune | Enemies within 2 tiles lose 1 Speed |
TIDAL | Coast | Water tiles heal 1 HP per turn |
FERAL | Wild | Kill streak: 1.3x damage per streak level (resets if no kills on your turn) |
FORTRESS | Ward | 50% less damage when you did not move this turn |
ALL_SEEING | Eye | Ranged attacks have +30% crit chance |
ETHEREAL | Vex | 20% chance to dodge incoming attacks |
OCEAN_BLESSING | Tide | Full heal when dropping below 25% HP (once per combat) |
BRUTE_FORCE | Snout | Melee attacks splash to adjacent enemies |
INFERNAL | Rib | Fire attacks deal +3 bonus damage |
FORTUNE_PEAK | Spire | Double emerald rewards |
PATHFINDER | Wayfinder | Movement ignores obstacle tiles |
TERRAFORMER | Shaper | Moving 3 or more tiles deals 2 damage to all enemies adjacent to your destination |
PHANTOM | Silence | Invisible for the first 2 turns (enemies do not act) |
RALLY | Raiser | Tamed allies get +2 Speed and +1 Attack |
SYMBIOTE | Host | Heal 1 HP for each enemy killed |
CURRENT | Flow | Killing an enemy refunds 1 AP |
THUNDERSTRIKE | Bolt | Critical hits stun the target for 1 turn |
Trim Materials
Each trim material grants a per-piece stat bonus stacking once for each armor piece that carries a trim using that material. Register custom materials for modded ingots. Call CrafticsAPI.registerTrimMaterial(entry) during onCrafticsInit().
The material id must match the registry path of the trim material, not the full namespaced ID. For example, minecraft:iron uses "iron".
API Method
CrafticsAPI.registerTrimMaterial(TrimMaterialEntry entry);
TrimMaterialEntry Record
TrimMaterialEntry is a record with no builder. Construct it directly.
CrafticsAPI.registerTrimMaterial(new TrimMaterialEntry(
"mymod:orichalcum", // materialId
TrimEffects.Bonus.ARMOR_PEN, // stat
2, // valuePerPiece
"+2 Armor Penetration per piece"
));
| Field | Type | Description |
|---|---|---|
materialId | String | Registry path of the trim material |
stat | TrimEffects.Bonus | Stat bonus type |
valuePerPiece | int | Amount of the stat bonus added per trimmed armor piece |
description | String | Human-readable description shown in the armor tooltip |
JSON Datapack Schema
Place trim material JSON files at data/<namespace>/craftics/trim_materials/*.json.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Registry path of the trim material |
stat | string | yes | TrimEffects.Bonus name (case-insensitive). An unknown value causes the file to be skipped. |
value_per_piece | int | no | Bonus amount per trimmed piece. Defaults to 1. |
description | string | no | Human-readable description |
{
"id": "iron",
"stat": "DEFENSE",
"value_per_piece": 1,
"description": "+1 Defense per trimmed piece"
}
Code Example
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.registry.TrimMaterialEntry;
import com.crackedgames.craftics.combat.TrimEffects;
// Orichalcum trim: +2 armor pen per trimmed piece
CrafticsAPI.registerTrimMaterial(new TrimMaterialEntry(
"mymod:orichalcum",
TrimEffects.Bonus.ARMOR_PEN,
2,
"+2 Armor Penetration per trimmed piece"
));
Enchantments
Register enchantments that contribute passive stat bonuses during Craftics combat. The handler receives an EnchantmentContext holding the enchantment level, the player, and a StatModifiers accumulator. Call CrafticsAPI.registerEnchantment(enchantmentId, handler) during onCrafticsInit().
Enchantments that modify weapon abilities, such as Sharpness or Smite, are handled by weapon ability handlers rather than this registry.
API Method
CrafticsAPI.registerEnchantment(String enchantmentId, EnchantmentEffectHandler handler);
EnchantmentEffectHandler and EnchantmentContext
@FunctionalInterface
public interface EnchantmentEffectHandler {
void apply(EnchantmentContext ctx);
}
public class EnchantmentContext {
public int getLevel(); // enchantment level (1, 2, 3, ...)
public ServerPlayerEntity getPlayer(); // the player
public StatModifiers getModifiers(); // accumulator to add bonuses to
}
Add bonuses to the accumulator using ctx.getModifiers().add(TrimEffects.Bonus, amount). The full list of available TrimEffects.Bonus values is in the Trim Patterns section above.
JSON Datapack Schema
Place enchantment JSON files at data/<namespace>/craftics/enchantments/*.json. Each bonus entry grants (enchantLevel / per_levels) * amount of the specified stat.
| Field | Type | Required | Description |
|---|---|---|---|
enchantment | string | yes | Full enchantment registry ID, e.g. minecraft:protection |
bonuses | array | no | List of stat bonus objects |
bonuses[].stat | string | yes | TrimEffects.Bonus name (case-insensitive) |
bonuses[].amount | int | no | Bonus amount per qualifying level. Defaults to 1. |
bonuses[].per_levels | int | no | How many enchantment levels are needed per amount. Defaults to 1 (one bonus per level). |
{
"enchantment": "minecraft:protection",
"bonuses": [
{ "stat": "DEFENSE", "amount": 1, "per_levels": 2 }
]
}
With per_levels: 2 and amount: 1, Protection IV (level 4) grants (4 / 2) * 1 = 2 Defense.
Code Example
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.combat.TrimEffects;
// Holy Blessing: +1 Defense and +1 Regen per enchantment level
CrafticsAPI.registerEnchantment("mymod:holy_blessing", (ctx) -> {
ctx.getModifiers().add(TrimEffects.Bonus.DEFENSE, ctx.getLevel());
ctx.getModifiers().add(TrimEffects.Bonus.REGEN, ctx.getLevel());
});
// Swiftness: +1 Speed regardless of level
CrafticsAPI.registerEnchantment("mymod:swiftness", (ctx) -> {
ctx.getModifiers().add(TrimEffects.Bonus.SPEED, 1);
});
Equipment Scanners
Equipment scanners let addon mods contribute stat bonuses from non-standard inventory slots, such as trinkets, baubles, or curio slots, into Craftics combat stats. The scanner is called during combat stat calculation and its results are merged with trim and armor bonuses. Call CrafticsAPI.registerEquipmentScanner(id, scanner) during onCrafticsInit().
This registry is code-only; no JSON datapack loader exists for scanners. The scanner function must be provided in Java.
API Method
CrafticsAPI.registerEquipmentScanner(String id, EquipmentScanner scanner);
EquipmentScanner Interface
@FunctionalInterface
public interface EquipmentScanner {
StatModifiers scan(ServerPlayerEntity player);
}
StatModifiers Class
StatModifiers accumulates bonuses using the TrimEffects.Bonus enum as keys. It can also carry an optional set bonus and custom combat effect handlers.
StatModifiers mods = new StatModifiers();
mods.add(TrimEffects.Bonus.DEFENSE, 2); // +2 Defense
mods.add(TrimEffects.Bonus.SPEED, 1); // +1 Speed
mods.add(TrimEffects.Bonus.MAX_HP, 4); // +4 Max HP
mods.addSetBonus(TrimEffects.SetBonus.FERAL, "Feral Ring Set"); // optional set bonus
Call mods.addCombatEffect(name, handler) to register a dynamic CombatEffectHandler alongside the stat bonuses. Combat effect handlers are covered on the Items & Effects page.
Code Example
import com.crackedgames.craftics.api.CrafticsAPI;
import com.crackedgames.craftics.api.EquipmentScanner;
import com.crackedgames.craftics.api.StatModifiers;
import com.crackedgames.craftics.combat.TrimEffects;
import net.minecraft.item.ItemStack;
CrafticsAPI.registerEquipmentScanner("mymod", (player) -> {
StatModifiers mods = new StatModifiers();
// Read items from your addon's custom equipment slots
ItemStack belt = getCustomSlot(player, "belt");
if (!belt.isEmpty()) {
if (belt.isOf(MY_SWIFT_BELT)) {
mods.add(TrimEffects.Bonus.SPEED, 1);
} else if (belt.isOf(MY_IRON_BELT)) {
mods.add(TrimEffects.Bonus.DEFENSE, 2);
}
}
ItemStack amulet = getCustomSlot(player, "amulet");
if (!amulet.isEmpty() && amulet.isOf(MY_LUCKY_CHARM)) {
mods.add(TrimEffects.Bonus.LUCK, 3);
}
return mods;
});