The fang's element, the core's new corners, and gem slots for limbs
Sea serpent fang was a fang anywhere: one of them, socketed, worn or held, and you breathed underwater. It is an armour material now, and worth something different in hand. Worn, it grants half a lung per module in each armour slot and the handler still only reads 1 as breathing, so it takes two worn fang modules - two sockets, two pieces built of it, or a socketed piece that is both. That is the trade the space set already makes, and a fang weapon no longer makes it at all. Held, it does what a serpent does in its element. Mining needs nothing of ours: vanilla models underwater digging as an attribute of its own, minecraft:player.submerged_mining_speed, base 0.2 for the fifth-speed penalty, and +0.6 a module puts a single fang module four times past a plain tool and two of them past dry land. Damage and swing speed have no such switch, so WaterCombat.java registers cmmodular:water_combat the way FireImmunity and WaterBreathing register theirs. Damage is answered on the blow. Attack speed cannot be - the game reads it off the attribute map whenever it likes - so a transient modifier under one id goes on and comes off as the bearer enters and leaves the water. The arcana core's recipe puts a trophy from each corner of the game in the corners of the grid - nether star, dragon's breath, heart of the sea and Iron's Spells' ancient knowledge fragment - with two cinder and two arcane essence on the edges and nothing in the middle. Armory sockets a helmet, a chestplate, a backplate and a belt and stops there, so an arm or a leg could never hold a gem. Four pieces here finish it: the heavy pauldron and the heavy pants, left and right, inheriting Armory's own whole and adding the same gem_armor_medium slot at the same price, 5% of the piece's armour. Where the gem sits is the other half - out on the shoulder in the pauldron, forward on the knee in the pants - and the numbers come from Armory's own art, which lies a sprite proud of the side of an arm at 2.15 and of the front of a body at 2.35. Last, a gem case, for anyone running Apotheosis. Its gems are not modules and never can be, since what makes one worth having is its own rarity and purity data, but Apotheosis counts sockets in a plain data component and MIAPI's component property sets any component a module likes. So the case is one line of data: put it in an armour piece's gem slot instead of a gemstone and the piece reports an Apotheosis socket, which the gem goes into at a smithing table. Modular armour extends vanilla ArmorItem, so Apotheosis' LootCategory already calls it a helmet or a chestplate, and gem bonuses arrive through ItemAttributeModifierEvent, where MIAPI is not looking. The pack is gated on Apotheosis and Armory both, which is why a pack can now name more than one mod. None of this is compiled or run - there is no JDK on the machine it was written on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>gem-slots-and-the-fangs-element
parent
3f3b4bdef6
commit
87a78136ec
106
README.md
106
README.md
|
|
@ -9,8 +9,10 @@ aerospace addons - so their metals, gems, scales and essences can all be
|
||||||
used to build modular tools and armour.
|
used to build modular tools and armour.
|
||||||
|
|
||||||
On top of the materials it adds a few modules of its own: a khopesh blade for
|
On top of the materials it adds a few modules of its own: a khopesh blade for
|
||||||
Arsenal, and a sealed helmet and life support pack for Armory that between them
|
Arsenal, a sealed helmet and life support pack for Armory that between them
|
||||||
let you breathe in the vacuum Create: Cosmonautics puts above the atmosphere.
|
let you breathe in the vacuum Create: Cosmonautics puts above the atmosphere,
|
||||||
|
socketed arms and legs for the armour pieces Armory leaves without a gem slot,
|
||||||
|
and a gem case that turns one of Armory's gem slots into an Apotheosis one.
|
||||||
It adds one item as well, the arcana core, which is an endgame gem that ends
|
It adds one item as well, the arcana core, which is an endgame gem that ends
|
||||||
every limit Iron's Spells puts on a caster.
|
every limit Iron's Spells puts on a caster.
|
||||||
|
|
||||||
|
|
@ -60,9 +62,21 @@ watches for it. Neither side knows about the other.
|
||||||
|
|
||||||
**Breathing in a vacuum.** The same trick, for the space set - see below.
|
**Breathing in a vacuum.** The same trick, for the space set - see below.
|
||||||
|
|
||||||
**Breathing underwater.** And again, for sea serpent fang: `WaterBreathing.java`
|
**Breathing underwater.** And again, for sea serpent fang armour:
|
||||||
answers the same `LivingBreatheEvent`, but only when the bearer's eyes are in
|
`WaterBreathing.java` answers the same `LivingBreatheEvent`, but only when the
|
||||||
water, so a fang is a fang rather than half a spacesuit.
|
wearer's eyes are in water, so a fang is a fang rather than half a spacesuit.
|
||||||
|
Half a lung per module, the same trade the space set makes, so it takes two
|
||||||
|
worn fang modules and a fang weapon does nothing at all - see below.
|
||||||
|
|
||||||
|
**Fighting underwater.** The other half of what a fang is for.
|
||||||
|
`WaterCombat.java` registers a `cmmodular:water_combat` attribute that a fang
|
||||||
|
grants in the main hand, and while the bearer is in water it adds that fraction
|
||||||
|
to the damage they deal and to how fast they swing. Damage is answered on the
|
||||||
|
blow. Attack speed cannot be - the game reads it off the attribute map whenever
|
||||||
|
it wants to know - so a transient modifier under one id goes on and comes off as
|
||||||
|
the bearer enters and leaves the water. Mining needs none of this: vanilla
|
||||||
|
already models underwater digging as an attribute,
|
||||||
|
`minecraft:player.submerged_mining_speed`, and the material simply adds to it.
|
||||||
|
|
||||||
**Moving through sand.** Deathworm chitin does what the worm does, and this is
|
**Moving through sand.** Deathworm chitin does what the worm does, and this is
|
||||||
the one thing an attribute and an event cannot carry on their own: whether a
|
the one thing an attribute and an event cannot carry on their own: whether a
|
||||||
|
|
@ -97,8 +111,9 @@ material or a module - see below.
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
Three of the packs hold modules rather than materials. They are hand-written
|
Four of the packs hold modules rather than materials. They are hand-written
|
||||||
rather than generated, and gated on the Truly Modular addon whose slots they use.
|
rather than generated, and gated on the Truly Modular addon whose slots they
|
||||||
|
use - and, for the gem case, on Apotheosis as well.
|
||||||
|
|
||||||
**Khopesh blade** (`packs/arsenal`) fits any `sword_blade` slot, so it is an
|
**Khopesh blade** (`packs/arsenal`) fits any `sword_blade` slot, so it is an
|
||||||
option wherever the plain sword blade is. It is the chopping sword it looks
|
option wherever the plain sword blade is. It is the chopping sword it looks
|
||||||
|
|
@ -119,6 +134,59 @@ Arsenal, Armory, Archery and MIAPI turns up no module anywhere that offers a
|
||||||
go. So there are two here: a medium socket, and a great socket for the large
|
go. So there are two here: a medium socket, and a great socket for the large
|
||||||
gem. Each is a heavier counterweight than the last, paid for in swing speed.
|
gem. Each is a heavier counterweight than the last, paid for in swing speed.
|
||||||
|
|
||||||
|
**Socketed arms and legs** (`packs/armory`) finish a set Armory started.
|
||||||
|
Armory sockets four pieces - helmet, chestplate, backplate and belt - and stops
|
||||||
|
there, so an arm or a leg is a piece of armour that can never hold a gem. These
|
||||||
|
four are the missing ones: the heavy pauldron and the heavy pants, left and
|
||||||
|
right, each with the same `gem_armor_medium` slot the rest of the set uses and
|
||||||
|
the same price, 5% of the piece's armour.
|
||||||
|
|
||||||
|
They inherit Armory's heavy pieces whole and change two things: the slot, and
|
||||||
|
where the gem sits in it. Armory's own gems are placed on the body - the chest's
|
||||||
|
sits on the sternum, from `origin: body` - so these do the same with the limb as
|
||||||
|
the origin. On an arm the gem is turned to face outwards and set at the top of
|
||||||
|
the shoulder, in the pauldron; on a leg it faces forward, halfway down, in the
|
||||||
|
knee. The numbers come from Armory's own: a sprite lies proud of the side of an
|
||||||
|
arm at 2.15 and of the front of a body at 2.35, which is what its banner patches
|
||||||
|
use, so the gems use it too. They are eyeball values on someone else's model
|
||||||
|
and the four transforms are the only thing to touch if a gem sits proud or
|
||||||
|
sinks in.
|
||||||
|
|
||||||
|
The pieces take any medium armour gemstone, which includes the Apothic gem case
|
||||||
|
below - so a pauldron can hold an Apotheosis gem as readily as a Truly Modular
|
||||||
|
one.
|
||||||
|
|
||||||
|
**The Apothic gem case** (`packs/apotheosis`) trades a Truly Modular gem slot
|
||||||
|
for an Apotheosis one. Apotheosis gems are not modules and never can be - what
|
||||||
|
makes one worth having is its own rarity and purity data, which a MIAPI
|
||||||
|
material cannot carry - so the two systems cannot share a slot. What they can
|
||||||
|
share is the item. Apotheosis stores how many gems a thing holds in a plain
|
||||||
|
data component, `apotheosis:sockets`, and MIAPI's `component` property sets any
|
||||||
|
component a module likes, so the case is one line of data: put it in an armour
|
||||||
|
piece's `gem_armor_medium` slot instead of a gemstone, and the piece reports one
|
||||||
|
Apotheosis socket. The gem itself goes in at a smithing table, through
|
||||||
|
Apotheosis' own socketing recipe, not at the workbench.
|
||||||
|
|
||||||
|
That leaves the two systems where they each work best. Apotheosis reads the
|
||||||
|
socket count through `SocketHelper`, which classifies the item with
|
||||||
|
`LootCategory` - and modular armour extends vanilla `ArmorItem`, so it is a
|
||||||
|
helmet or a chestplate to Apotheosis rather than nothing at all. Gem bonuses
|
||||||
|
arrive as attribute modifiers on the stack, through `ItemAttributeModifierEvent`,
|
||||||
|
which is where MIAPI is not looking; MIAPI writes its own numbers into the
|
||||||
|
`ATTRIBUTE_MODIFIERS` component, and the two add up rather than overwrite.
|
||||||
|
|
||||||
|
It wears Armory's own medium gemstone model rather than art of its own, which
|
||||||
|
is both the least work and the right picture: a case is a setting cut for a
|
||||||
|
gem, in whatever material it was built from, and it sits exactly where a
|
||||||
|
gemstone would. Nothing is copied to do it - the module points at Armory's
|
||||||
|
model path, and Armory is required for the pack to load at all.
|
||||||
|
|
||||||
|
One socket a case, which is the one-for-one trade the module is. Two things
|
||||||
|
worth knowing: the component is the module's while the module is in, so sockets
|
||||||
|
added with a Sigil of Socketing are overwritten by it and come back when the
|
||||||
|
case comes out, and a gem sitting in a case stops counting if the case is
|
||||||
|
removed - it is not destroyed, it is just in a socket that no longer exists.
|
||||||
|
|
||||||
**The space set** (`packs/armory`) is Create: Cosmonautics' answer to vacuum,
|
**The space set** (`packs/armory`) is Create: Cosmonautics' answer to vacuum,
|
||||||
rebuilt as modules. Cosmonautics suffocates you above the atmosphere by
|
rebuilt as modules. Cosmonautics suffocates you above the atmosphere by
|
||||||
answering NeoForge's `LivingBreatheEvent` with "no", and exempts its own space
|
answering NeoForge's `LivingBreatheEvent` with "no", and exempts its own space
|
||||||
|
|
@ -141,8 +209,10 @@ Textures below.
|
||||||
## The arcana core
|
## The arcana core
|
||||||
|
|
||||||
The one item this mod adds, and the only late-game thing in it that is not
|
The one item this mod adds, and the only late-game thing in it that is not
|
||||||
simply a good material. Four arcane essence at the corners, four cinder essence
|
simply a good material. One trophy from each corner of the game in the corners
|
||||||
on the edges, a nether star in the middle - all of which are already endgame -
|
of the grid - a nether star, a dragon's breath, a heart of the sea and Iron's
|
||||||
|
Spells' ancient knowledge fragment - with two cinder essence and two arcane
|
||||||
|
essence on the edges and nothing in the middle. All of it is already endgame,
|
||||||
and what comes out is a gem that fits any socket and ends Iron's Spells'
|
and what comes out is a gem that fits any socket and ends Iron's Spells'
|
||||||
limits: a mana pool of a million refilled a hundred times over, cooldowns and
|
limits: a mana pool of a million refilled a hundred times over, cooldowns and
|
||||||
cast times at zero, and ten times the spell power. It is deliberately absurd,
|
cast times at zero, and ten times the spell power. It is deliberately absurd,
|
||||||
|
|
@ -317,9 +387,21 @@ folders, so the hand-written packs survive it.
|
||||||
`gem_armor` as a hidden group as well, so a piece can be set into an armour
|
`gem_armor` as a hidden group as well, so a piece can be set into an armour
|
||||||
socket instead of only shaped into the armour. Sea serpent fang is new and
|
socket instead of only shaped into the armour. Sea serpent fang is new and
|
||||||
does both: Ice and Fire has no sea serpent bone, and the fang is what a
|
does both: Ice and Fire has no sea serpent bone, and the fang is what a
|
||||||
serpent leaves behind besides its scales. It grants water breathing, which is
|
serpent leaves behind besides its scales.
|
||||||
what Ice and Fire's own Tide Guardian armour does, from slot `any` - so one
|
- What a fang is worth depends on where it sits, and all of it is filed under
|
||||||
fang socketed, worn or held is enough, and a second neither adds nor dilutes.
|
`default` so a fang set into an armour socket counts as much as one shaped
|
||||||
|
into the piece. Worn, it grants 0.5 water breathing in each armour slot -
|
||||||
|
which is what Ice and Fire's own Tide Guardian armour does - and the handler
|
||||||
|
only reads 1 as breathing, so it takes two worn fang modules: two sockets,
|
||||||
|
two pieces built of it, or a socketed piece that is both. Held, it breathes
|
||||||
|
not at all and instead does what a serpent does in its element: `+0.6`
|
||||||
|
submerged mining speed on vanilla's base of `0.2`, so a single fang module
|
||||||
|
digs four times faster underwater than a plain tool and two of them beat
|
||||||
|
dry-land speed, and `+0.10` `cmmodular:water_combat`, so a weapon built of it
|
||||||
|
hits that much harder and swings that much faster while its bearer is in
|
||||||
|
water. Both stack per module; the combat half caps at `+0.5`. That half is
|
||||||
|
the bearer's rather than the weapon's, so a fang bow's arrows count as well,
|
||||||
|
as long as the fang is in hand and its bearer in water when they land.
|
||||||
- Scales are in the `scute` group, and no module in Truly Modular accepts that
|
- Scales are in the `scute` group, and no module in Truly Modular accepts that
|
||||||
group - not Armory's armour, not Arsenal's or Archery's parts, and MIAPI's own
|
group - not Armory's armour, not Arsenal's or Archery's parts, and MIAPI's own
|
||||||
turtle and armadillo scutes are stuck the same way. So each scale material
|
turtle and armadillo scutes are stuck the same way. So each scale material
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,15 @@ import net.neoforged.neoforge.event.AddPackFindersEvent;
|
||||||
public class CMModular {
|
public class CMModular {
|
||||||
public static final String MOD_ID = "cmmodular";
|
public static final String MOD_ID = "cmmodular";
|
||||||
|
|
||||||
/** A built-in datapack - materials or modules - and the mod it needs. */
|
/** A built-in datapack - materials or modules - and the mods it needs. */
|
||||||
private record MaterialPack(String id, String name, String requires) {
|
private record MaterialPack(String id, String name, String... requires) {
|
||||||
boolean present() {
|
boolean present() {
|
||||||
return ModList.get().isLoaded(requires);
|
for (String mod : requires) {
|
||||||
|
if (!ModList.get().isLoaded(mod)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,12 +73,17 @@ public class CMModular {
|
||||||
// Modules, not materials, and gated for the same reason: they name
|
// Modules, not materials, and gated for the same reason: they name
|
||||||
// slots - and inherit modules - that only exist with these installed.
|
// slots - and inherit modules - that only exist with these installed.
|
||||||
new MaterialPack("arsenal", "Arsenal Modules", "tm_arsenal"),
|
new MaterialPack("arsenal", "Arsenal Modules", "tm_arsenal"),
|
||||||
new MaterialPack("armory", "Armory Modules", "tm_armory"));
|
new MaterialPack("armory", "Armory Modules", "tm_armory"),
|
||||||
|
// Two mods, because the case sits in one of Armory's gem slots and
|
||||||
|
// sets a component only Apotheosis knows what to do with. Without
|
||||||
|
// either half it is a module that fits nowhere and does nothing.
|
||||||
|
new MaterialPack("apotheosis", "Apothic Gem Cases", "apotheosis", "tm_armory"));
|
||||||
|
|
||||||
public CMModular(IEventBus modBus) {
|
public CMModular(IEventBus modBus) {
|
||||||
FireImmunity.register(modBus);
|
FireImmunity.register(modBus);
|
||||||
VacuumSeal.register(modBus);
|
VacuumSeal.register(modBus);
|
||||||
WaterBreathing.register(modBus);
|
WaterBreathing.register(modBus);
|
||||||
|
WaterCombat.register(modBus);
|
||||||
SandPhasing.register(modBus);
|
SandPhasing.register(modBus);
|
||||||
EquipmentAttributes.register(modBus);
|
EquipmentAttributes.register(modBus);
|
||||||
ArcanaCore.register(modBus);
|
ArcanaCore.register(modBus);
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@ import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sea serpent fang lets its bearer breathe underwater, which is what Ice and
|
* Sea serpent fang armour lets its wearer breathe underwater, which is what Ice
|
||||||
* Fire's own Tide Guardian armour does and the only reason to hunt the thing.
|
* and Fire's own Tide Guardian armour does and the only reason to hunt the
|
||||||
|
* thing.
|
||||||
*
|
*
|
||||||
* <p>There is no attribute for it and MIAPI has no property for it either - the
|
* <p>There is no attribute for it and MIAPI has no property for it either - the
|
||||||
* closest data can get is a fake Respiration enchantment, which only postpones
|
* closest data can get is a fake Respiration enchantment, which only postpones
|
||||||
|
|
@ -22,6 +23,12 @@ import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
* {@link VacuumSeal} already set: an attribute of this mod's own that the
|
* {@link VacuumSeal} already set: an attribute of this mod's own that the
|
||||||
* material grants like any other, and a listener here that watches for it.
|
* material grants like any other, and a listener here that watches for it.
|
||||||
*
|
*
|
||||||
|
* <p>Half a lung per module, as with {@link VacuumSeal}: the fang grants 0.5 in
|
||||||
|
* each armour slot and nothing in hand, so it takes two worn pieces of it -
|
||||||
|
* two sockets, two pieces built of it, or a socketed piece that is both - to
|
||||||
|
* breathe, and a fang weapon breathes not at all. What a fang is worth in hand
|
||||||
|
* is {@link WaterCombat}'s.
|
||||||
|
*
|
||||||
* <p>It answers the same event {@link VacuumSeal} does but only underwater, so
|
* <p>It answers the same event {@link VacuumSeal} does but only underwater, so
|
||||||
* a fang is a fang and not half a spacesuit.
|
* a fang is a fang and not half a spacesuit.
|
||||||
*/
|
*/
|
||||||
|
|
@ -29,7 +36,7 @@ public final class WaterBreathing {
|
||||||
private static final DeferredRegister<Attribute> ATTRIBUTES =
|
private static final DeferredRegister<Attribute> ATTRIBUTES =
|
||||||
DeferredRegister.create(Registries.ATTRIBUTE, CMModular.MOD_ID);
|
DeferredRegister.create(Registries.ATTRIBUTE, CMModular.MOD_ID);
|
||||||
|
|
||||||
/** At or above 1 the bearer never drowns; the fang grants a whole one. */
|
/** At or above 1 the bearer never drowns; each worn fang module is 0.5. */
|
||||||
public static final DeferredHolder<Attribute, Attribute> WATER_BREATHING =
|
public static final DeferredHolder<Attribute, Attribute> WATER_BREATHING =
|
||||||
ATTRIBUTES.register("water_breathing", id -> new RangedAttribute(
|
ATTRIBUTES.register("water_breathing", id -> new RangedAttribute(
|
||||||
"attribute.cmmodular.water_breathing", 0.0D, 0.0D, 1.0D).setSyncable(true));
|
"attribute.cmmodular.water_breathing", 0.0D, 0.0D, 1.0D).setSyncable(true));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
package eu.abdelbaki.cmmodular;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.neoforged.bus.api.IEventBus;
|
||||||
|
import net.neoforged.neoforge.common.NeoForge;
|
||||||
|
import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent;
|
||||||
|
import net.neoforged.neoforge.event.tick.PlayerTickEvent;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sea serpent fang in hand is worth more in water than out of it: harder
|
||||||
|
* hits and a faster swing while the bearer is in it.
|
||||||
|
*
|
||||||
|
* <p>The mining half of the same idea needs nothing here - vanilla models
|
||||||
|
* underwater digging as an attribute of its own,
|
||||||
|
* {@code minecraft:player.submerged_mining_speed}, and the material simply
|
||||||
|
* adds to it. Damage and attack speed have no such switch, so this follows the
|
||||||
|
* pattern {@link FireImmunity} and {@link WaterBreathing} already set: an
|
||||||
|
* attribute of this mod's own that the material grants like any other, and a
|
||||||
|
* listener here that watches for it.
|
||||||
|
*
|
||||||
|
* <p>Damage is answered on the blow, which needs no state. Attack speed cannot
|
||||||
|
* be - it is read off the attribute map whenever the game wants to know how
|
||||||
|
* fast the bearer swings - so a modifier goes on and comes off as the bearer
|
||||||
|
* enters and leaves the water. It is transient and keyed by one id, so a
|
||||||
|
* player who never touches a fang never has one and a player who does never
|
||||||
|
* collects two.
|
||||||
|
*/
|
||||||
|
public final class WaterCombat {
|
||||||
|
private static final DeferredRegister<Attribute> ATTRIBUTES =
|
||||||
|
DeferredRegister.create(Registries.ATTRIBUTE, CMModular.MOD_ID);
|
||||||
|
|
||||||
|
/** No more than half again, however much fang the weapon is built from. */
|
||||||
|
private static final double MAX = 0.5D;
|
||||||
|
|
||||||
|
/** The fraction added to damage dealt and to swing speed, in water only. */
|
||||||
|
public static final DeferredHolder<Attribute, Attribute> WATER_COMBAT =
|
||||||
|
ATTRIBUTES.register("water_combat", id -> new RangedAttribute(
|
||||||
|
"attribute.cmmodular.water_combat", 0.0D, 0.0D, MAX).setSyncable(true));
|
||||||
|
|
||||||
|
/** The one attack speed modifier this puts on, so it can be taken off. */
|
||||||
|
private static final ResourceLocation SWING_ID =
|
||||||
|
ResourceLocation.fromNamespaceAndPath(CMModular.MOD_ID, "water_combat");
|
||||||
|
|
||||||
|
private WaterCombat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register(IEventBus modBus) {
|
||||||
|
ATTRIBUTES.register(modBus);
|
||||||
|
modBus.addListener(WaterCombat::attachToEntities);
|
||||||
|
NeoForge.EVENT_BUS.addListener(WaterCombat::onIncomingDamage);
|
||||||
|
NeoForge.EVENT_BUS.addListener(WaterCombat::onPlayerTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Attributes only exist on an entity if they were added to its type. */
|
||||||
|
private static void attachToEntities(EntityAttributeModificationEvent event) {
|
||||||
|
event.getTypes().forEach(type -> event.add(type, WATER_COMBAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void onIncomingDamage(LivingIncomingDamageEvent event) {
|
||||||
|
if (!(event.getSource().getEntity() instanceof LivingEntity attacker)
|
||||||
|
|| !attacker.isInWater()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double bonus = boost(attacker);
|
||||||
|
if (bonus > 0.0D) {
|
||||||
|
event.setAmount((float) (event.getAmount() * (1.0D + bonus)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the swing speed modifier in step with the water and the weapon.
|
||||||
|
*
|
||||||
|
* <p>Only players, because attack speed is only ever read for a player.
|
||||||
|
* The equipment is only consulted while the bearer is in water, so this
|
||||||
|
* costs nothing on dry land.
|
||||||
|
*/
|
||||||
|
private static void onPlayerTick(PlayerTickEvent.Post event) {
|
||||||
|
Player player = event.getEntity();
|
||||||
|
AttributeInstance speed = player.getAttribute(Attributes.ATTACK_SPEED);
|
||||||
|
if (speed == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double bonus = player.isInWater() ? boost(player) : 0.0D;
|
||||||
|
AttributeModifier applied = speed.getModifier(SWING_ID);
|
||||||
|
if (bonus <= 0.0D) {
|
||||||
|
if (applied != null) {
|
||||||
|
speed.removeModifier(SWING_ID);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (applied != null && applied.amount() == bonus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Weapon swapped for one with more fang in it, or the map was rebuilt
|
||||||
|
// out from under the modifier - either way, put the right one back.
|
||||||
|
speed.removeModifier(SWING_ID);
|
||||||
|
speed.addTransientModifier(new AttributeModifier(SWING_ID, bonus,
|
||||||
|
AttributeModifier.Operation.ADD_MULTIPLIED_BASE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** What the held modules are worth, clamped to what the attribute allows. */
|
||||||
|
private static double boost(LivingEntity entity) {
|
||||||
|
return Math.min(EquipmentAttributes.value(entity, WATER_COMBAT), MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"attribute.cmmodular.fire_immunity": "Fire Immunity",
|
"attribute.cmmodular.fire_immunity": "Fire Immunity",
|
||||||
"attribute.cmmodular.vacuum_seal": "Vacuum Seal",
|
"attribute.cmmodular.vacuum_seal": "Vacuum Seal",
|
||||||
"attribute.cmmodular.water_breathing": "Water Breathing",
|
"attribute.cmmodular.water_breathing": "Water Breathing",
|
||||||
|
"attribute.cmmodular.water_combat": "Underwater Fighting",
|
||||||
"attribute.cmmodular.sand_phasing": "Sand Phasing",
|
"attribute.cmmodular.sand_phasing": "Sand Phasing",
|
||||||
"item.cmmodular.arcana_core": "Arcana Core",
|
"item.cmmodular.arcana_core": "Arcana Core",
|
||||||
"miapi.module.cmmodular.blade.khopesh.name": "Khopesh Blade",
|
"miapi.module.cmmodular.blade.khopesh.name": "Khopesh Blade",
|
||||||
|
|
@ -11,6 +12,19 @@
|
||||||
"miapi.module.cmmodular.armor.space.helmet_socket.name": "Sealed Helmet with Socket",
|
"miapi.module.cmmodular.armor.space.helmet_socket.name": "Sealed Helmet with Socket",
|
||||||
"miapi.module.cmmodular.armor.space.life_support.name": "Life Support Pack",
|
"miapi.module.cmmodular.armor.space.life_support.name": "Life Support Pack",
|
||||||
"miapi.module.cmmodular.armor.space.life_support_socket.name": "Life Support Pack with Socket",
|
"miapi.module.cmmodular.armor.space.life_support_socket.name": "Life Support Pack with Socket",
|
||||||
|
"miapi.module.cmmodular.armor.socket.arm_left": "Left Heavy Socket Pauldron",
|
||||||
|
"miapi.module.cmmodular.armor.socket.arm_left.name": "%s Left Heavy Socket Pauldron",
|
||||||
|
"miapi.module.cmmodular.armor.socket.arm_left.description": "A Heavy Pauldron with a Gemstone set into it",
|
||||||
|
"miapi.module.cmmodular.armor.socket.arm_right": "Right Heavy Socket Pauldron",
|
||||||
|
"miapi.module.cmmodular.armor.socket.arm_right.name": "%s Right Heavy Socket Pauldron",
|
||||||
|
"miapi.module.cmmodular.armor.socket.arm_right.description": "A Heavy Pauldron with a Gemstone set into it",
|
||||||
|
"miapi.module.cmmodular.armor.socket.leg_left": "Heavy Socket Left Pants",
|
||||||
|
"miapi.module.cmmodular.armor.socket.leg_left.name": "%s Heavy Socket Left Pants",
|
||||||
|
"miapi.module.cmmodular.armor.socket.leg_left.description": "Heavy Pants with a Gemstone set into the Knee",
|
||||||
|
"miapi.module.cmmodular.armor.socket.leg_right": "Heavy Socket Right Pants",
|
||||||
|
"miapi.module.cmmodular.armor.socket.leg_right.name": "%s Heavy Socket Right Pants",
|
||||||
|
"miapi.module.cmmodular.armor.socket.leg_right.description": "Heavy Pants with a Gemstone set into the Knee",
|
||||||
|
"miapi.module.cmmodular.gem.apothic_case.name": "Apothic Gem Case",
|
||||||
"miapi.material.cmmodular.bone.dragonbone": "Dragonbone",
|
"miapi.material.cmmodular.bone.dragonbone": "Dragonbone",
|
||||||
"miapi.material.cmmodular.bone.hippogryph_talon": "Talon",
|
"miapi.material.cmmodular.bone.hippogryph_talon": "Talon",
|
||||||
"miapi.material.cmmodular.bone.sea_serpent_fang": "Sea Serpent Fang",
|
"miapi.material.cmmodular.bone.sea_serpent_fang": "Sea Serpent Fang",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"display_name": "miapi.module.cmmodular.gem.apothic_case.name",
|
||||||
|
"model": {
|
||||||
|
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||||
|
"translation": {"x": 0, "y": 0, "z": 0},
|
||||||
|
"scale": {"x": 1, "y": 1, "z": 1}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allowed_in_slots": [
|
||||||
|
"gem_armor_medium"
|
||||||
|
],
|
||||||
|
"tag": [
|
||||||
|
"gem_armor",
|
||||||
|
"gem_armor_medium",
|
||||||
|
"gem_armor_generic",
|
||||||
|
"gem_armor_medium_generic"
|
||||||
|
],
|
||||||
|
"component": {
|
||||||
|
"apotheosis:sockets": 1
|
||||||
|
},
|
||||||
|
"material_property": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"priority": 0,
|
||||||
|
"repair_priority": 0,
|
||||||
|
"rarity": "rare",
|
||||||
|
"allowed_material": {
|
||||||
|
"allowedMaterials": [
|
||||||
|
"metal",
|
||||||
|
"crystal",
|
||||||
|
"glass",
|
||||||
|
"bone",
|
||||||
|
"stone"
|
||||||
|
],
|
||||||
|
"cost": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"description": "Apothic Gem Cases for Truly Modular",
|
||||||
|
"pack_format": 48
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"parent": "tm_armory:armor/heavy/arm_left",
|
||||||
|
"data": {
|
||||||
|
"merge": {
|
||||||
|
"module_tag": [
|
||||||
|
"socket_armor"
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"attribute": "minecraft:generic.armor",
|
||||||
|
"value": "-0.05",
|
||||||
|
"operation": "*",
|
||||||
|
"slot": "chest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"slots": {
|
||||||
|
"banner-arm-left": {
|
||||||
|
"allowed": [
|
||||||
|
"cosmetic_2x4",
|
||||||
|
"banner_2x4"
|
||||||
|
],
|
||||||
|
"slotType": "cosmetic",
|
||||||
|
"translationKey": "miapi.slot.arm_patch.name",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": 90, "z": 0},
|
||||||
|
"translation": {"x": -2.15, "y": -1, "z": 0},
|
||||||
|
"scale": {"x": 1.1, "y": 1, "z": 1},
|
||||||
|
"origin": "left_arm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gem": {
|
||||||
|
"allowed": [
|
||||||
|
"gem_armor_medium"
|
||||||
|
],
|
||||||
|
"translationKey": "miapi.slot.gemstone.medium",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": 90, "z": 0},
|
||||||
|
"translation": {"x": -2.15, "y": -2, "z": 0},
|
||||||
|
"scale": {"x": 1, "y": 1, "z": 1},
|
||||||
|
"origin": "left_arm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"display_name": "miapi.module.cmmodular.armor.socket.arm_left.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"parent": "tm_armory:armor/heavy/arm_right",
|
||||||
|
"data": {
|
||||||
|
"merge": {
|
||||||
|
"module_tag": [
|
||||||
|
"socket_armor"
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"attribute": "minecraft:generic.armor",
|
||||||
|
"value": "-0.05",
|
||||||
|
"operation": "*",
|
||||||
|
"slot": "chest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"slots": {
|
||||||
|
"banner-arm-right": {
|
||||||
|
"allowed": [
|
||||||
|
"cosmetic_2x4",
|
||||||
|
"banner_2x4"
|
||||||
|
],
|
||||||
|
"slotType": "cosmetic",
|
||||||
|
"translationKey": "miapi.slot.arm_patch.name",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": -90, "z": 0},
|
||||||
|
"translation": {"x": 2.15, "y": -1, "z": 0},
|
||||||
|
"scale": {"x": 1.1, "y": 1, "z": 1},
|
||||||
|
"origin": "right_arm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gem": {
|
||||||
|
"allowed": [
|
||||||
|
"gem_armor_medium"
|
||||||
|
],
|
||||||
|
"translationKey": "miapi.slot.gemstone.medium",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": -90, "z": 0},
|
||||||
|
"translation": {"x": 2.15, "y": -2, "z": 0},
|
||||||
|
"scale": {"x": 1, "y": 1, "z": 1},
|
||||||
|
"origin": "right_arm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"display_name": "miapi.module.cmmodular.armor.socket.arm_right.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"parent": "tm_armory:armor/heavy/leg_left",
|
||||||
|
"data": {
|
||||||
|
"merge": {
|
||||||
|
"module_tag": [
|
||||||
|
"socket_armor"
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"attribute": "minecraft:generic.armor",
|
||||||
|
"value": "-0.05",
|
||||||
|
"operation": "*",
|
||||||
|
"slot": "legs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"slots": {
|
||||||
|
"gem": {
|
||||||
|
"allowed": [
|
||||||
|
"gem_armor_medium"
|
||||||
|
],
|
||||||
|
"translationKey": "miapi.slot.gemstone.medium",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||||
|
"translation": {"x": 0, "y": 5, "z": -2.35},
|
||||||
|
"scale": {"x": 1, "y": 1, "z": 1},
|
||||||
|
"origin": "left_leg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"display_name": "miapi.module.cmmodular.armor.socket.leg_left.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"parent": "tm_armory:armor/heavy/leg_right",
|
||||||
|
"data": {
|
||||||
|
"merge": {
|
||||||
|
"module_tag": [
|
||||||
|
"socket_armor"
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"attribute": "minecraft:generic.armor",
|
||||||
|
"value": "-0.05",
|
||||||
|
"operation": "*",
|
||||||
|
"slot": "legs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"slots": {
|
||||||
|
"gem": {
|
||||||
|
"allowed": [
|
||||||
|
"gem_armor_medium"
|
||||||
|
],
|
||||||
|
"translationKey": "miapi.slot.gemstone.medium",
|
||||||
|
"transform": {
|
||||||
|
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||||
|
"translation": {"x": 0, "y": 5, "z": -2.35},
|
||||||
|
"scale": {"x": 1, "y": 1, "z": 1},
|
||||||
|
"origin": "right_leg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"display_name": "miapi.module.cmmodular.armor.socket.leg_right.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,9 +42,39 @@
|
||||||
"attributes": [
|
"attributes": [
|
||||||
{
|
{
|
||||||
"attribute": "cmmodular:water_breathing",
|
"attribute": "cmmodular:water_breathing",
|
||||||
"value": "1",
|
"value": "0.5",
|
||||||
"operation": "+",
|
"operation": "+",
|
||||||
"slot": "any"
|
"slot": "head"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attribute": "cmmodular:water_breathing",
|
||||||
|
"value": "0.5",
|
||||||
|
"operation": "+",
|
||||||
|
"slot": "chest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attribute": "cmmodular:water_breathing",
|
||||||
|
"value": "0.5",
|
||||||
|
"operation": "+",
|
||||||
|
"slot": "legs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attribute": "cmmodular:water_breathing",
|
||||||
|
"value": "0.5",
|
||||||
|
"operation": "+",
|
||||||
|
"slot": "feet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attribute": "minecraft:player.submerged_mining_speed",
|
||||||
|
"value": "0.6",
|
||||||
|
"operation": "+",
|
||||||
|
"slot": "mainhand"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attribute": "cmmodular:water_combat",
|
||||||
|
"value": "0.1",
|
||||||
|
"operation": "+",
|
||||||
|
"slot": "mainhand"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,17 @@
|
||||||
"type": "minecraft:crafting_shaped",
|
"type": "minecraft:crafting_shaped",
|
||||||
"category": "misc",
|
"category": "misc",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
"ACA",
|
"NAB",
|
||||||
"CNC",
|
"C C",
|
||||||
"ACA"
|
"HAK"
|
||||||
],
|
],
|
||||||
"key": {
|
"key": {
|
||||||
|
"N": "minecraft:nether_star",
|
||||||
|
"B": "minecraft:dragon_breath",
|
||||||
|
"H": "minecraft:heart_of_the_sea",
|
||||||
|
"K": "irons_spellbooks:ancient_knowledge_fragment",
|
||||||
"A": "irons_spellbooks:arcane_essence",
|
"A": "irons_spellbooks:arcane_essence",
|
||||||
"C": "irons_spellbooks:cinder_essence",
|
"C": "irons_spellbooks:cinder_essence"
|
||||||
"N": "minecraft:nether_star"
|
|
||||||
},
|
},
|
||||||
"result": {
|
"result": {
|
||||||
"id": "cmmodular:arcana_core",
|
"id": "cmmodular:arcana_core",
|
||||||
|
|
|
||||||
|
|
@ -78,13 +78,23 @@ PACKS = {
|
||||||
# packs/armory, and are listed here so their pack.mcmeta is written.
|
# packs/armory, and are listed here so their pack.mcmeta is written.
|
||||||
"arsenal": {"mod": "tm_arsenal", "name": "Arsenal Modules"},
|
"arsenal": {"mod": "tm_arsenal", "name": "Arsenal Modules"},
|
||||||
"armory": {"mod": "tm_armory", "name": "Armory Modules"},
|
"armory": {"mod": "tm_armory", "name": "Armory Modules"},
|
||||||
|
# Same again, for the one module that needs a mod outside Truly Modular:
|
||||||
|
# the gem case wants Armory's slot and Apotheosis' component both, so
|
||||||
|
# CMModular.java gates it on the pair.
|
||||||
|
"apotheosis": {"mod": "apotheosis", "name": "Apothic Gem Cases"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Attributes this mod registers itself, so a material can grant something the
|
# Attributes this mod registers itself, so a material can grant something the
|
||||||
# game has no attribute for. See FireImmunity.java and WaterBreathing.java.
|
# game has no attribute for. See FireImmunity.java, WaterBreathing.java and
|
||||||
|
# WaterCombat.java.
|
||||||
FIRE_IMMUNITY = "cmmodular:fire_immunity"
|
FIRE_IMMUNITY = "cmmodular:fire_immunity"
|
||||||
WATER_BREATHING = "cmmodular:water_breathing"
|
WATER_BREATHING = "cmmodular:water_breathing"
|
||||||
SAND_PHASING = "cmmodular:sand_phasing"
|
SAND_PHASING = "cmmodular:sand_phasing"
|
||||||
|
WATER_COMBAT = "cmmodular:water_combat"
|
||||||
|
|
||||||
|
# Vanilla's own underwater mining multiplier, base 0.2 - the 5x penalty for
|
||||||
|
# swinging a pick with your eyes under water, and what Aqua Affinity undoes.
|
||||||
|
SUBMERGED_MINING_SPEED = "minecraft:player.submerged_mining_speed"
|
||||||
|
|
||||||
|
|
||||||
def attribute(attr, value, slot, operation="+"):
|
def attribute(attr, value, slot, operation="+"):
|
||||||
|
|
@ -444,16 +454,30 @@ MATERIALS = [
|
||||||
tier=4, hardness=6.0, density=3.0, flexibility=4, durability=760,
|
tier=4, hardness=6.0, density=3.0, flexibility=4, durability=760,
|
||||||
enchantability=16, mining_speed=7, toughness=2, armor_durability=26,
|
enchantability=16, mining_speed=7, toughness=2, armor_durability=26,
|
||||||
groups=("bone",), hidden_groups=("gem_armor",), textures=("rough", "shiny"),
|
groups=("bone",), hidden_groups=("gem_armor",), textures=("rough", "shiny"),
|
||||||
|
# All of it under `default`, so a fang set into an armour socket counts
|
||||||
|
# as much as one shaped into the piece - a gem module is not tagged
|
||||||
|
# `armor` or `handheld` and would never see anything filed there. What
|
||||||
|
# the fang is worth is decided by the slot instead: worn, it breathes;
|
||||||
|
# held, it fights and mines.
|
||||||
properties={
|
properties={
|
||||||
"default": {
|
"default": {
|
||||||
"attributes": [
|
"attributes": [
|
||||||
# Ice and Fire's own sea serpent armour grants water
|
# Ice and Fire's own sea serpent armour grants water
|
||||||
# breathing, and that is the whole reason to hunt one. Slot
|
# breathing, and that is the whole reason to hunt one. Half a
|
||||||
# `any`, capped at 1 and read as "at or above 1" by the
|
# lung per module and capped at 1, the trade the space set
|
||||||
# handler, so one fang anywhere - socketed, worn or held - is
|
# makes: two fangs worn - two sockets, two pieces, or a
|
||||||
# enough and a second neither adds nor dilutes, exactly as
|
# socketed piece built of it - and not one held.
|
||||||
# cinder essence does for fire.
|
*(attribute(WATER_BREATHING, 0.5, slot)
|
||||||
attribute(WATER_BREATHING, 1, "any"),
|
for slot in ("head", "chest", "legs", "feet")),
|
||||||
|
# Held rather than worn, and only under water. Vanilla mines
|
||||||
|
# at a fifth of the usual speed with your eyes submerged;
|
||||||
|
# 0.6 on a base of 0.2 is four times better than that off a
|
||||||
|
# single fang module and past dry-land speed off two.
|
||||||
|
attribute(SUBMERGED_MINING_SPEED, 0.6, "mainhand"),
|
||||||
|
# And the same water makes a serpent's tooth bite: a tenth
|
||||||
|
# more damage and a tenth faster a module, capped at half
|
||||||
|
# again - see WaterCombat.java.
|
||||||
|
attribute(WATER_COMBAT, 0.10, "mainhand"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"boot": {"fake_enchants": {"minecraft:depth_strider": 1}},
|
"boot": {"fake_enchants": {"minecraft:depth_strider": 1}},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue