Compare commits
No commits in common. "main" and "modules-and-missing-materials" have entirely different histories.
main
...
modules-an
|
|
@ -5,6 +5,3 @@ runs/
|
|||
repo/
|
||||
*.class
|
||||
__pycache__/
|
||||
|
||||
# Python virtualenv for the tools in tools/
|
||||
tools/.venv/
|
||||
|
|
|
|||
380
README.md
|
|
@ -1,7 +1,7 @@
|
|||
# Create/Mekanism Modular
|
||||
|
||||
A [Truly Modular](https://modrinth.com/mod/modular-item-api) addon for Minecraft
|
||||
1.21.1 / NeoForge. It adds 128 materials drawn from 22 mods - Create, Mekanism, Ice
|
||||
1.21.1 / NeoForge. It adds 126 materials drawn from 22 mods - Create, Mekanism, Ice
|
||||
and Fire, Immersive Engineering, Ender IO, Applied Energistics, Occultism,
|
||||
Ars Nouveau, Iron's Spells, Pastel, Crystal Chronicles, NauTec, Superb Warfare,
|
||||
Industrial Foregoing, RFTools, Create Deco, Steampunk Dimension and the Create
|
||||
|
|
@ -9,12 +9,8 @@ aerospace addons - so their metals, gems, scales and essences can all be
|
|||
used to build modular tools and armour.
|
||||
|
||||
On top of the materials it adds a few modules of its own: a khopesh blade for
|
||||
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,
|
||||
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
|
||||
every limit Iron's Spells puts on a caster.
|
||||
Arsenal, and a sealed helmet and life support pack for Armory that between them
|
||||
let you breathe in the vacuum Create: Cosmonautics puts above the atmosphere.
|
||||
|
||||
Every source mod is optional. Install any subset - the materials for mods you do
|
||||
not have are never registered.
|
||||
|
|
@ -24,7 +20,7 @@ not have are never registered.
|
|||
Almost all of this mod is data: MIAPI material JSON under
|
||||
`src/main/resources/packs/<mod>/data/cmmodular/miapi/materials/`.
|
||||
|
||||
The small amount of Java exists for the handful of things data cannot do.
|
||||
The small amount of Java exists for two reasons.
|
||||
|
||||
**Conditional loading.** MIAPI reads materials with its own loader, which does
|
||||
not honour `neoforge:conditions` or anything else that could switch a file off,
|
||||
|
|
@ -62,65 +58,10 @@ watches for it. Neither side knows about the other.
|
|||
|
||||
**Breathing in a vacuum.** The same trick, for the space set - see below.
|
||||
|
||||
**Breathing underwater.** And again, for sea serpent fang armour:
|
||||
`WaterBreathing.java` answers the same `LivingBreatheEvent`, but only when the
|
||||
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
|
||||
the one thing an attribute and an event cannot carry on their own: whether a
|
||||
block collides is asked of the block, with the entity along for the ride in the
|
||||
collision context and nothing in between. So there is one mixin,
|
||||
`BlockStateBaseMixin`, and it is two lines - `SandPhasing.java` owns the
|
||||
decision. A block in `cmmodular:sand_like` returns no collision to a bearer of
|
||||
`cmmodular:sand_phasing`, and sand you are standing inside of cannot suffocate
|
||||
you.
|
||||
|
||||
It does not drop you through the desert. A block wholly below the feet still
|
||||
holds the bearer up, so sand stays something to walk on and a dune becomes
|
||||
something to walk into: step in and you keep your level, jump and the sand under
|
||||
you turns solid and puts you back on top. Without that rule the first sand block
|
||||
you stood on would swallow you down to the sandstone and leave you there, which
|
||||
is a trap rather than an ability.
|
||||
|
||||
**Attributes that go missing.** Modifiers granted by worn and held items are
|
||||
transient - vanilla reads them when equipment changes and the entity's attribute
|
||||
map is the only place they live. Go through a portal and they can be gone, with
|
||||
no equipment change to follow, so nothing puts them back until the piece is
|
||||
taken off and put on again; cinder essence's fire immunity is the one that gets
|
||||
noticed, because the Nether is exactly where it was wanted.
|
||||
`EquipmentAttributes.java` rebuilds the map from the equipment after a dimension
|
||||
change or a respawn, which is what re-equipping would have done and which fixes
|
||||
every attribute rather than only this mod's, and reads this mod's own attributes
|
||||
back off the equipment as well, so the answer is the same whether or not the map
|
||||
ever lost them.
|
||||
|
||||
**Counting gem cases.** `ApothicSockets.java` makes each Apothic gem case worth
|
||||
an Apotheosis socket, which data cannot do at all - every route through MIAPI's
|
||||
component properties is either dead or crashes the crafting screen, see below.
|
||||
It is the one place this mod reaches into another's internals, and it does it
|
||||
reflectively so that neither Apotheosis nor Truly Modular becomes something to
|
||||
build against.
|
||||
|
||||
**One item.** The arcana core, which is the only thing here that is not a
|
||||
material or a module - see below.
|
||||
|
||||
## Modules
|
||||
|
||||
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 - and, for the gem case, on Apotheosis as well.
|
||||
Three 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.
|
||||
|
||||
**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
|
||||
|
|
@ -140,137 +81,6 @@ Arsenal, Armory, Archery and MIAPI turns up no module anywhere that offers a
|
|||
`gem_melee_large` slot - the large gemstone is defined and then has nowhere to
|
||||
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.
|
||||
Either socket takes an Apothic gem case as readily as a gemstone - see below.
|
||||
|
||||
**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.
|
||||
|
||||
Where the gem sits is the unfinished part of these four, and the honest summary
|
||||
is that it is placed by trial rather than by arithmetic. MIAPI draws a worn piece
|
||||
once per body part; which part a module is drawn under comes from its own model's
|
||||
`origin`, and where it sits comes from the matching entry of its slot's transform
|
||||
stack. Armory's gemstone declares no origin, so a gem is drawn once, in the
|
||||
`body` pass, from the `body` entry - which means a gem slot is placed in body
|
||||
space no matter which limb it is cut into, and is pinned to the torso rather than
|
||||
to the limb it belongs to.
|
||||
|
||||
The numbers are body-space pixels from the neck: +x to the wearer's left, +y
|
||||
down, -z forward. A pauldron gem is turned a quarter turn about y to face out of
|
||||
the shoulder; a knee gem faces forward already. Right-hand pieces are the same
|
||||
numbers with x and the turn negated.
|
||||
|
||||
They are not right yet, and two attempts to make them right are worth recording
|
||||
so that they are not repeated. Naming the limb as the gem slot's `origin` - so
|
||||
the offset lands in the entry the limb pass reads and the gem follows the arm -
|
||||
composes correctly on paper and in game puts the gems several blocks away from
|
||||
the wearer, floating. And `tools/preview_armour.py`, which applies the same chain
|
||||
MIAPI does and draws the result from any angle, agrees with the paper rather than
|
||||
with the game: given the configuration that in practice left gems at the neck, it
|
||||
puts them on the pauldron. The tool is worth keeping for reading Armory's own
|
||||
geometry - where the plate bands are, which face is which, how far the chain
|
||||
scales a limb - and it should not be trusted to predict a gem's final position
|
||||
until it can reproduce a symptom somebody has actually seen.
|
||||
|
||||
One thing does not survive any of this and is not fixable from here. A slot
|
||||
transform that names an origin has nothing left for the inventory icon, which
|
||||
reads a different entry of the same stack, so the gem sits at the centre of the
|
||||
icon - the collar of a chestplate, which is where Armory leaves its own, and the
|
||||
crotch of a pair of pants, which is where this one leaves ours. The two
|
||||
placements come from one transform and cannot both be aimed.
|
||||
|
||||
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 can set any component a module
|
||||
likes: put a case in an armour piece's `gem_armor_medium` slot instead of a
|
||||
gemstone, and the piece reports an Apotheosis socket. The gem itself goes in at
|
||||
a smithing table, through Apotheosis' own socketing recipe, not at the workbench.
|
||||
|
||||
A weapon takes one too, and there is a case per gem size to say so. A slot
|
||||
accepts what its `allowed` list names: the socketed pommels above ask for
|
||||
`gem_melee_medium` and `gem_melee_large`, and the baseline sockets - Arsenal's
|
||||
own socket pommel, and the dual socket guard that carries two of them - ask for
|
||||
`gem_melee_small`. So the case is allowed in the medium slots, a **great case**
|
||||
answers for the large, and a **small case** for the small, which is the same
|
||||
trade in a sword that it is in a pauldron. The small one names the armour size
|
||||
key as well, because a small slot is a small slot wherever it was cut and a key
|
||||
nothing asks for costs nothing.
|
||||
|
||||
Nothing else changes. Sockets are counted per case rather than per item, so a
|
||||
sword with a case in its pommel reports one the same way a chestplate does and
|
||||
a guard holding two small cases reports two; Apotheosis sockets gems into
|
||||
weapons already. All three are filed under `packs/apotheosis` rather than with
|
||||
the pommels, because a case without Apotheosis is an item that grants nothing,
|
||||
while a case without Arsenal is one that simply never fits anywhere.
|
||||
|
||||
One socket a case, and none of it is in the module JSON, because every route
|
||||
through data is closed. MIAPI's `components` property sets a component rather
|
||||
than adding to it, so two cases both writing `1` leave the piece with one socket
|
||||
between them. The property that could resolve a count instead of a constant,
|
||||
`advanced_components`, is written but never registered in MIAPI 2.3.8 -
|
||||
`PropertyRegistry` wires up `components` and `material_component_property` and
|
||||
never mentions it - so that key resolves to nothing. And the one that does work
|
||||
turned out to be worse than useless: `ComponentProperty.preview` dereferences the
|
||||
workbench without a null check, MIAPI passes it null while hovering an option in
|
||||
the replace list, and `shouldExecuteOnCraft` only calls it for a module that
|
||||
carries the property - so a gem case with a `components` line crashed the
|
||||
crafting screen, and only a gem case did.
|
||||
|
||||
So `ApothicSockets.java` answers it instead, and Apotheosis makes that easy by
|
||||
asking the question out loud: `SocketHelper` reads the component and then fires
|
||||
`GetItemSocketsEvent` so anything may revise the answer. This counts the cases on
|
||||
the stack and adds one for each. Nothing writes the component now, which is
|
||||
better than the data version managed: a socket cut by a Sigil of Socketing
|
||||
survives and the cases stack on top, where the component used to overwrite it.
|
||||
|
||||
It reaches both mods reflectively rather than compiling against them. Neither is
|
||||
a build dependency here and one socket is not worth making them one, so the seven
|
||||
members it needs - three off MIAPI's module tree, three off the event, and the
|
||||
event class itself - are resolved once at startup and cached. Missing classes
|
||||
mean the mods are not installed, which is ordinary and silent; a missing method
|
||||
means something was renamed, which is logged, because it is a thing to fix rather
|
||||
than a thing to expect. Either way the count falls back to what the component
|
||||
says on its own.
|
||||
|
||||
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 is drawn here rather than borrowed. It used to wear Armory's medium gemstone
|
||||
model, which was the least work and the wrong picture: a case is the setting,
|
||||
not the stone, and one that looks like a gemstone is indistinguishable from the
|
||||
gemstone it is an alternative to. So `items/gem_case.png` is a four-by-four
|
||||
bezel around an empty two-by-two socket - a gemstone's own footprint, with the
|
||||
mount drawn around it - lit from the top left like the pommel sockets, and on
|
||||
the same greys the materials' palettes map, so it comes out in whatever it was
|
||||
built from. The socket stays empty-looking whatever is in it, which is honest:
|
||||
an Apotheosis gem lives in a component and MIAPI has nothing to render for it.
|
||||
|
||||
The bezel is twice a gemstone across, which would put a case at twice a
|
||||
gemstone's size in a slot sized for one, so the module's own model transform
|
||||
carries a scale of 0.5 and hands the slot back a gemstone's footprint. That is
|
||||
the module's business rather than the slot's: a socket that holds either should
|
||||
not have to be scaled differently depending on which went in.
|
||||
|
||||
One thing worth knowing: 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,
|
||||
rebuilt as modules. Cosmonautics suffocates you above the atmosphere by
|
||||
|
|
@ -291,37 +101,6 @@ drawn in, rather than wearing the plain piece's art.
|
|||
All of the art here is placeholder-grade and meant to be repainted - see
|
||||
Textures below.
|
||||
|
||||
## The arcana core
|
||||
|
||||
The one item this mod adds, and the only late-game thing in it that is not
|
||||
simply a good material. One trophy from each corner of the game in the corners
|
||||
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'
|
||||
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,
|
||||
which is the point of the recipe.
|
||||
|
||||
Cooldown and cast time are read by Iron's Spells as a factor on a base of 1 and
|
||||
spent as `time * (2 - value)`, so a `*` modifier of 1 puts the value at 2 and
|
||||
the time at nothing; spell power is the same reading, where `1 + 9` is ten
|
||||
times. There is no "infinite mana" to ask for, so a pool nothing can empty and a
|
||||
regeneration that refills it instantly is what that means here.
|
||||
|
||||
The item is always registered, because an item cannot be added conditionally the
|
||||
way a datapack can. Nothing that makes it worth having is: the material and the
|
||||
recipe both live in the Iron's Spells pack, so without that mod the core cannot
|
||||
be crafted, is not a material, and grants nothing.
|
||||
|
||||
Its texture is Minecraft's own heart of the sea with its hue rotated into
|
||||
violet - the only art here that is not drawn for this mod, and Mojang's rather
|
||||
than ours, which is worth knowing given what the khopesh section above says
|
||||
about shipping other people's. Nothing but the hue is touched, so the shading
|
||||
is the vanilla item's, including how dark its underside is: the palette the
|
||||
material hands out is therefore a deep one with little highlight, which is what
|
||||
the item actually looks like rather than what a gem usually does.
|
||||
|
||||
## Stats
|
||||
|
||||
Tinkers' Construct 3 is the reference, re-scaled onto MIAPI's vanilla-derived
|
||||
|
|
@ -393,26 +172,11 @@ new piece is wanted rather than a repaint of an existing one.
|
|||
## Building
|
||||
|
||||
```sh
|
||||
./build.sh
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
The jar lands in `build/libs/`. Needs nothing but NeoForge - Truly Modular is a
|
||||
runtime dependency only, since this mod talks to it purely through datapack
|
||||
JSON.
|
||||
|
||||
`./gradlew build` does the same thing, and is fine if the java on your path is
|
||||
one Gradle can run on. Gradle 8.12 cannot run on a JDK newer than 23, which a
|
||||
distro's default java may well be by now, and the failure when it is says
|
||||
`Could not create task ':test'` and `Type T not present` without ever mentioning
|
||||
Java. `build.sh` exists to pick a JDK between 17 and 23 - honouring `JAVA_HOME`
|
||||
if it already points at one - and to say what is wrong in as many words when
|
||||
there is none to pick. Arguments are passed through, with any task named
|
||||
replacing the default `build` rather than adding to it, so `./build.sh clean
|
||||
build` cleans and then builds; a bare flag such as `./build.sh --info` still
|
||||
gets the default task.
|
||||
|
||||
None of this touches what the mod is compiled against: the toolchain in
|
||||
`build.gradle` is Java 21 either way, and Gradle provisions it if it is missing.
|
||||
Needs nothing but NeoForge - Truly Modular is a runtime dependency only, since
|
||||
this mod talks to it purely through datapack JSON.
|
||||
|
||||
## Regenerating materials
|
||||
|
||||
|
|
@ -423,10 +187,8 @@ textures. The generated JSON is committed, so a plain build does not need this.
|
|||
python3 tools/generate_materials.py --jars ~/path/to/mod/jars
|
||||
```
|
||||
|
||||
Point it at the pack's `mods` folder. This mod's own namespace is served out of
|
||||
`src/main/resources/assets/cmmodular/` rather than a jar, so the arcana core is
|
||||
checked and sampled like everything else. Every item id in `materials.py` is
|
||||
checked against those jars and the run fails on a typo, rather than shipping a material
|
||||
Point it at the pack's `mods` folder. Every item id in `materials.py` is checked
|
||||
against those jars and the run fails on a typo, rather than shipping a material
|
||||
that silently refuses to load.
|
||||
|
||||
A mod whose jar is not there is skipped instead, with a line saying so, and its
|
||||
|
|
@ -443,58 +205,6 @@ Requires `pillow` and `numpy`.
|
|||
Module JSON and textures are not generated. A run only clears the material
|
||||
folders, so the hand-written packs survive it.
|
||||
|
||||
## Looking at the armour
|
||||
|
||||
```sh
|
||||
python3 tools/ARMOUR_QUICKSTART.py # every armour model, one at a time
|
||||
python3 tools/ARMOUR_GUI.py --jar <armory> # draw and unwrap socket geometry
|
||||
python3 tools/ARMOUR_EDITOR.py --jar <armory> # pose a scene and nudge placements
|
||||
```
|
||||
|
||||
The quickstart is the way in and needs no arguments: it reads every model in
|
||||
`src/main/resources` - worn armour, icons, sword parts, the loose item models,
|
||||
the files in the tree rather than a built jar - and lists them with the first
|
||||
one showing. A jar passed with `--jar` adds its models to the list too, after
|
||||
yours and marked with their namespace, which is the only way to see a socket
|
||||
against the plate it is cut into: the geometry in this repo is the socket, and
|
||||
`arm_left/heavy` is Armory's. Click a row to show or hide it and to point the camera at it;
|
||||
`Reload from disk` picks up a model saved in another window.
|
||||
|
||||
The left panel is the selected model's MIAPI transform. Three arrows on the
|
||||
model's origin move it along an axis and the rotate toggle swaps them for three
|
||||
rings; the position and rotation boxes read what the drag did, in the units a
|
||||
module writes, and take typed numbers back. `Copy as JSON` puts the `transform`
|
||||
block on the clipboard, and `Write to source` puts it back in the module entry
|
||||
it was read from - scale and origin untouched. It writes every model that has
|
||||
moved rather than just the selected one, and marks a moved row with a `*` until
|
||||
it has; a model no module names has nowhere to write to and is named in the
|
||||
report instead. `Unwrap UVs` re-cuts the selected model's texture so every
|
||||
face has a patch of its own, writes the atlas size beside the boxes, and paints
|
||||
a template to match where there is no texture yet - never over one there is.
|
||||
|
||||
Where a model goes comes from the mod's own module data: MIAPI draws a model
|
||||
under the body part its `origin` names, and those are declared in
|
||||
`packs/*/data/*/miapi/modules/`. What is *not* in this tree is the offset
|
||||
Armory's slot transforms carry, so without `--jar` a worn model is flipped onto
|
||||
its part but not moved along it - right limb, roughly right place. Pass the
|
||||
Armory jar for exact placement, and for its own plates to line new geometry up
|
||||
against.
|
||||
|
||||
These need pyvista and pyvistaqt, which hardly any distribution packages, so
|
||||
the tools keep a virtualenv at `tools/.venv` and re-run themselves inside it.
|
||||
Build it once with
|
||||
|
||||
```sh
|
||||
python3 -m venv --system-site-packages tools/.venv
|
||||
tools/.venv/bin/pip install pyvista pyvistaqt
|
||||
```
|
||||
|
||||
`--system-site-packages` is what keeps the distribution's Qt in charge; a
|
||||
PySide6 from pip alongside the system Qt is a partial upgrade waiting to
|
||||
happen. On a Wayland session the viewport runs through XWayland, because VTK's
|
||||
Python wheels have no Wayland window backend - the tools say which display they
|
||||
picked, and why, when they start.
|
||||
|
||||
## Notes
|
||||
|
||||
- Titanium's palette comes from Cosmonautics' ingot, which is violet. Air War's
|
||||
|
|
@ -503,51 +213,6 @@ picked, and why, when they start.
|
|||
emissive, so it still reads as fire in game.
|
||||
- Dragon scales are one material per colour (12 dragon, 7 sea serpent, 3 death
|
||||
worm chitin). Colours within an element share stats and differ only in palette.
|
||||
- Sea serpent scales add `miapi:generic.swim_speed` per worn slot, 0.10 a
|
||||
module. It stacks per module like everything else here, so a full suit of
|
||||
them lands near +1 - about what Armory's own scuba set is worth, which is the
|
||||
company a sea serpent belongs in.
|
||||
- Deathworm chitin's sand phasing is gated on a tag, `cmmodular:sand_like`, so
|
||||
a pack can widen it: the common `#c:sands`, `#c:gravels` and
|
||||
`#c:concrete_powders` (the `c:` namespace being where Forge's tags went),
|
||||
each optional in case a set is not registered, plus the vanilla loose blocks
|
||||
by name so it works with none of them.
|
||||
- Dragon bone burns what it hits: a fake Fire Aspect I on handheld modules, the
|
||||
way Ice and Fire's own flamed dragonbone sword does. MIAPI's `immolate` is
|
||||
not that - despite the name it is bonus damage against something that is
|
||||
already alight, and MIAPI's own wiki marks it unimplemented. Ice and Fire
|
||||
ships one dragon bone item for all three kinds of dragon, so this cannot be
|
||||
the fire dragon's alone; the fire dragonscales are the only element-specific
|
||||
dragon material there is.
|
||||
- Bones are the other half of that. Dragon bone already built grips, handles
|
||||
and hafts - `bone` is on every tool and bow whitelist - and now carries
|
||||
`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
|
||||
does both: Ice and Fire has no sea serpent bone, and the fang is what a
|
||||
serpent leaves behind besides its scales.
|
||||
- What a fang is worth depends on where it sits, and all of it is filed under
|
||||
`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
|
||||
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
|
||||
carries `bone` as a hidden group: matched when a module decides what it will
|
||||
accept, ignored when the workbench decides what heading to file the material
|
||||
under, which is how Armory hands its own gem slots vanilla materials. Bone is
|
||||
on every armour whitelist except the scuba set - that one takes
|
||||
fabric/metal/glass, being a sealed suit rather than a plated one - so a scale
|
||||
builds every other piece of armour, and tool and bow parts besides.
|
||||
- Interstellar Expansion is mostly machinery. Only three of its items are a
|
||||
material rather than a component; solid E-710 is left out because it is rocket
|
||||
fuel.
|
||||
|
|
@ -574,34 +239,25 @@ picked, and why, when they start.
|
|||
- Cinder essence uses slot `any`, so one gem grants fire immunity wherever it
|
||||
sits, worn or held. The attribute caps at 1 and the handler treats anything at
|
||||
or above 1 as immune, so extra pieces neither add nor dilute.
|
||||
- A material's name is a lang key, not a field on the material. MIAPI reads
|
||||
`miapi.material.<namespace>.<group>.<name>` - its own id with the separators
|
||||
turned into dots - so `cmmodular:metal/arcane_metal` is named by
|
||||
`miapi.material.cmmodular.metal.arcane_metal` in `assets/cmmodular/lang/`,
|
||||
which the generator writes. The value is the bare name: MIAPI puts in the
|
||||
space before the module's own name. There is a `translation` field, but it is
|
||||
only read alongside `fake_translation`, as a pair registering a name for a key
|
||||
at runtime; on its own it names nothing, and the key shows raw in game.
|
||||
- Material properties belong under the `default` key, not `armor` or `handheld`.
|
||||
A gem socketed into armour is not tagged `armor`: gem modules declare which
|
||||
material properties they accept and list only `default` plus their own gem
|
||||
tags. Anything filed under `armor`/`handheld` silently never applies to a
|
||||
socketed gem. The attribute's own slot is what decides where it counts.
|
||||
- Arcane metal shortens Iron's Spells cooldowns, and that is the point of it:
|
||||
- Arcane alloy shortens Iron's Spells cooldowns, and that is the point of it:
|
||||
0.10 held, 0.05 on a helmet, 0.03 on the lower armour, filed under `default`
|
||||
so a suit plated in it counts as well as a staff made of it. Iron's Spells
|
||||
reads the attribute as a factor on a base of 1 and spends it as
|
||||
`cooldown * (2 - value)`, so the operation is `*` - a flat `+` would be added
|
||||
to a base of zero and do nothing - and 0.10 means 10% off the wait. It stacks
|
||||
per module, so a weapon built entirely of arcane metal is around 40%, against
|
||||
per module, so a weapon built entirely of arcane alloy is around 40%, against
|
||||
25% for Iron's Spells' own best staff; a full set adds roughly another 45%,
|
||||
which keeps even an all-arcane player under the 100% where the formula
|
||||
bottoms out. The material is called Arcane Metal because the item is an Arcane
|
||||
Ingot and a material is named for the substance, not the shape it is traded
|
||||
in; plain Arcane, which is what Iron's Spells calls the material itself, is
|
||||
taken here by the essence. Create: Wizardry's arcane sheet and block are the
|
||||
same metal - they arrive as a material extension in that mod's own pack, since
|
||||
naming them directly would break the material for anyone without it.
|
||||
bottoms out. The material
|
||||
is called Arcane Alloy rather than Arcane Steel because Create: Wizardry
|
||||
renames the ingot that way, and Create: Wizardry's arcane sheet and block are
|
||||
the same metal - they arrive as a material extension in that mod's own pack,
|
||||
since naming them directly would break the material for anyone without it.
|
||||
- Platinum comes from Create Propulsion: Simulated, the only platinum in the
|
||||
pack. It is soft for its density, so it swings slowly and hits like steel; it
|
||||
is worth mining for gold-grade enchantability that does not fall apart, and it
|
||||
|
|
|
|||
76
build.sh
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Builds the mod jar into build/libs/.
|
||||
#
|
||||
# Gradle 8.12 cannot run on a JDK newer than 23, and a distro's default java is
|
||||
# often newer than that, so this picks a JDK Gradle can run on rather than
|
||||
# leaving you to read the "Type T not present" failure that comes of not doing
|
||||
# so. The build itself still compiles against Java 21 either way - that is the
|
||||
# toolchain in build.gradle, and Gradle provisions it if it is missing.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Major version of the java at $1/bin/java, or nothing if it will not run.
|
||||
java_major() {
|
||||
local out
|
||||
out=$("$1/bin/java" -version 2>&1) || return 0
|
||||
sed -n 's/^[^"]*"\([0-9]*\).*/\1/p' <<<"$out" | head -1
|
||||
}
|
||||
|
||||
usable() {
|
||||
local major
|
||||
[ -x "$1/bin/java" ] || return 1
|
||||
major=$(java_major "$1")
|
||||
[ -n "$major" ] && [ "$major" -ge 17 ] && [ "$major" -le 23 ]
|
||||
}
|
||||
|
||||
if [ -z "${JAVA_HOME:-}" ] || ! usable "$JAVA_HOME"; then
|
||||
for candidate in /usr/lib/jvm/java-{21,17}-openjdk /usr/lib/jvm/java-{21,17}-openjdk-amd64; do
|
||||
if usable "$candidate"; then
|
||||
export JAVA_HOME="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "${JAVA_HOME:-}" ] || ! usable "$JAVA_HOME"; then
|
||||
echo "build.sh: no JDK between 17 and 23 found - Gradle 8.12 cannot run on anything newer." >&2
|
||||
echo "Install one (e.g. pacman -S jdk17-openjdk) or point JAVA_HOME at it." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Named tasks replace the default rather than being appended to it. Appending
|
||||
# would turn `build.sh clean build` into `gradlew build clean build`, which
|
||||
# Gradle folds back into build and clean, in that order - it cleans away the jar
|
||||
# it just built. Bare flags still get the default task, so `build.sh --info`
|
||||
# means an informative build rather than a no-op.
|
||||
tasks=()
|
||||
for arg in "$@"; do
|
||||
[ "${arg#-}" = "$arg" ] && tasks+=("$arg")
|
||||
done
|
||||
[ ${#tasks[@]} -eq 0 ] && set -- build "$@"
|
||||
|
||||
echo "build.sh: using JDK $(java_major "$JAVA_HOME") at $JAVA_HOME"
|
||||
./gradlew "$@"
|
||||
|
||||
# Name the jar this run produced. Old versions linger in build/libs/, so listing
|
||||
# the whole directory would be ambiguous about which one is current. Only worth
|
||||
# saying when a task that builds one actually ran: `build.sh clean` legitimately
|
||||
# ends with no jar at all.
|
||||
builds_jar=false
|
||||
for arg in "$@"; do
|
||||
case "$arg" in build | assemble | jar) builds_jar=true ;; esac
|
||||
done
|
||||
|
||||
if [ "$builds_jar" = true ]; then
|
||||
prop() { sed -n "s/^$1=//p" gradle.properties; }
|
||||
jar="build/libs/$(prop mod_id)-$(prop mod_version).jar"
|
||||
|
||||
echo
|
||||
if [ -f "$jar" ]; then
|
||||
echo "Built: $jar"
|
||||
else
|
||||
echo "build.sh: expected $jar, but it is not there." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
@ -7,7 +7,7 @@ neo_version=21.1.248
|
|||
|
||||
mod_id=cmmodular
|
||||
mod_name=Create/Mekanism Modular
|
||||
mod_version=1.4.0
|
||||
mod_version=1.2.0
|
||||
mod_group_id=eu.abdelbaki.cmmodular
|
||||
mod_authors=themiro
|
||||
mod_description=Materials from Create, Mekanism, Ice and Fire, Iron's Spells and the Create aerospace addons, for Truly Modular - plus a khopesh blade and a sealed space suit.
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
package eu.abdelbaki.cmmodular;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.bus.api.Event;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Makes each Apothic gem case worth one Apotheosis socket instead of the piece
|
||||
* as a whole being worth one.
|
||||
*
|
||||
* <p>None of this is in the module JSON, and it cannot be. MIAPI's
|
||||
* {@code components} property sets a component rather than adding to it, so two
|
||||
* cases both writing 1 would leave the piece with one socket between them; the
|
||||
* property that could resolve a count instead, {@code advanced_components}, is
|
||||
* written but never registered in MIAPI 2.3.8, so that key is dead. And the one
|
||||
* that does work is worse than useless here: {@code ComponentProperty.preview}
|
||||
* dereferences the workbench without a null check, and MIAPI hands it null while
|
||||
* hovering an option in the replace list, so merely carrying a {@code components}
|
||||
* property crashes the crafting screen on a server.
|
||||
*
|
||||
* <p>Apotheosis asks the question itself. {@code SocketHelper.getSockets} reads
|
||||
* the component and then fires {@code GetItemSocketsEvent} so anything can
|
||||
* revise the answer, which is exactly what is wanted, so this counts the cases
|
||||
* and revises it.
|
||||
*
|
||||
* <p>It does that without compiling against either mod. This mod deliberately
|
||||
* has no dependencies to build on - Truly Modular and Apotheosis are runtime
|
||||
* mods it talks to through data - and one socket is not worth giving that up.
|
||||
* So both sides are reached reflectively and resolved once at startup. Failing
|
||||
* costs nothing: the count reverts to what the component alone says, which is
|
||||
* the behaviour without this class at all. Absence is silent, since a pack
|
||||
* without those mods is the ordinary case, but a rename is logged, because
|
||||
* that one is a thing to fix rather than a thing to expect.
|
||||
*/
|
||||
public final class ApothicSockets {
|
||||
/** The one place this mod logs: a silent reflective failure is unfindable. */
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CMModular.MOD_ID);
|
||||
|
||||
private static final String EVENT = "dev.shadowsoffire.apotheosis.event.GetItemSocketsEvent";
|
||||
private static final String MIAPI_MODULE = "smartin.miapi.modules.ItemModule";
|
||||
|
||||
/**
|
||||
* Module ids that are a gem case: one per gem size, one per limb socket.
|
||||
* A case is not listed twice for being allowed in a pommel as well as in
|
||||
* armour - it is one module either way, and this counts modules, so a
|
||||
* guard holding two small cases is worth two sockets by the same rule
|
||||
* that makes a chestplate holding one worth one.
|
||||
*/
|
||||
private static final List<String> CASES = List.of(
|
||||
"cmmodular:gem/apothic_case",
|
||||
"cmmodular:gem/case_small",
|
||||
"cmmodular:gem/case_great",
|
||||
"cmmodular:gem/limb/case_arm_left",
|
||||
"cmmodular:gem/limb/case_arm_right",
|
||||
"cmmodular:gem/limb/case_leg_left",
|
||||
"cmmodular:gem/limb/case_leg_right");
|
||||
|
||||
/** MIAPI's module tree, reached without naming its types at compile time. */
|
||||
private static Method getModules;
|
||||
private static Method getFlatList;
|
||||
private static Method getModule;
|
||||
private static Method moduleId;
|
||||
|
||||
/** Apotheosis' event, likewise. */
|
||||
private static Method getStack;
|
||||
private static Method getSockets;
|
||||
private static Method setSockets;
|
||||
|
||||
private ApothicSockets() {
|
||||
}
|
||||
|
||||
static void register() {
|
||||
Class<?> itemModule;
|
||||
Class<?> moduleInstance;
|
||||
Class<?> event;
|
||||
try {
|
||||
itemModule = Class.forName(MIAPI_MODULE);
|
||||
moduleInstance = Class.forName("smartin.miapi.modules.ModuleInstance");
|
||||
event = Class.forName(EVENT);
|
||||
} catch (ClassNotFoundException absent) {
|
||||
// One of the two mods is not installed, which is the ordinary case
|
||||
// for a pack without them and nothing to say anything about. The
|
||||
// classes answer that better than ModList does, which is not
|
||||
// reliably populated this early.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getModules = itemModule.getMethod("getModules", ItemStack.class);
|
||||
getFlatList = moduleInstance.getMethod("getFlatList");
|
||||
getModule = moduleInstance.getMethod("getModule");
|
||||
moduleId = itemModule.getMethod("id");
|
||||
getStack = event.getMethod("getStack");
|
||||
getSockets = event.getMethod("getSockets");
|
||||
setSockets = event.getMethod("setSockets", int.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<Event> type = (Class<Event>) event;
|
||||
NeoForge.EVENT_BUS.addListener(type, (Consumer<Event>) ApothicSockets::onGetSockets);
|
||||
} catch (ReflectiveOperationException | ClassCastException moved) {
|
||||
// Both mods are here but something has been renamed, which is worth
|
||||
// saying out loud: the cases still work, they just stop counting.
|
||||
LOGGER.warn("Apothic gem cases will hold one socket a piece rather than one"
|
||||
+ " each - Apotheosis or MIAPI has moved something: {}", moved.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Answers with a socket a case, added to whatever the stack already had.
|
||||
* Nothing else here writes that component now, so a socket cut by a Sigil of
|
||||
* Socketing survives and the cases stack on top of it, which is a better
|
||||
* trade than the component's own answer of overwriting it.
|
||||
*/
|
||||
private static void onGetSockets(Event event) {
|
||||
try {
|
||||
int cases = countCases((ItemStack) getStack.invoke(event));
|
||||
if (cases > 0) {
|
||||
setSockets.invoke(event, (int) getSockets.invoke(event) + cases);
|
||||
}
|
||||
} catch (ReflectiveOperationException | RuntimeException e) {
|
||||
// Answering wrongly is worse than not answering: leave the count be.
|
||||
}
|
||||
}
|
||||
|
||||
private static int countCases(ItemStack stack) throws ReflectiveOperationException {
|
||||
Object root = getModules.invoke(null, stack);
|
||||
if (root == null) {
|
||||
return 0;
|
||||
}
|
||||
int found = 0;
|
||||
for (Object module : (List<?>) getFlatList.invoke(root)) {
|
||||
Object id = moduleId.invoke(getModule.invoke(module));
|
||||
if (id != null && CASES.contains(id.toString())) {
|
||||
found++;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package eu.abdelbaki.cmmodular;
|
||||
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Rarity;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
|
||||
import net.neoforged.neoforge.registries.DeferredItem;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
/**
|
||||
* The one item this mod adds: an endgame gem that undoes Iron's Spells' three
|
||||
* limits at once - mana, cooldown and cast time - and multiplies spell power
|
||||
* tenfold.
|
||||
*
|
||||
* <p>The item is always registered, because an item cannot be added
|
||||
* conditionally the way a datapack can. Everything that makes it worth having
|
||||
* is not: its material and its recipe both live in the Iron's Spells pack, so
|
||||
* without that mod the core is neither craftable nor a material, and the
|
||||
* attributes it grants are never asked for.
|
||||
*/
|
||||
public final class ArcanaCore {
|
||||
private static final DeferredRegister.Items ITEMS =
|
||||
DeferredRegister.createItems(CMModular.MOD_ID);
|
||||
|
||||
public static final DeferredItem<Item> ITEM = ITEMS.registerSimpleItem(
|
||||
"arcana_core", new Item.Properties().rarity(Rarity.EPIC).fireResistant().stacksTo(16));
|
||||
|
||||
private ArcanaCore() {
|
||||
}
|
||||
|
||||
static void register(IEventBus modBus) {
|
||||
ITEMS.register(modBus);
|
||||
modBus.addListener(ArcanaCore::addToCreativeTab);
|
||||
}
|
||||
|
||||
/** With the other crafting ingredients, which is what it is. */
|
||||
private static void addToCreativeTab(BuildCreativeModeTabContentsEvent event) {
|
||||
if (event.getTabKey() == CreativeModeTabs.INGREDIENTS) {
|
||||
event.accept(ITEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,15 +33,10 @@ import net.neoforged.neoforge.event.AddPackFindersEvent;
|
|||
public class CMModular {
|
||||
public static final String MOD_ID = "cmmodular";
|
||||
|
||||
/** A built-in datapack - materials or modules - and the mods it needs. */
|
||||
private record MaterialPack(String id, String name, String... requires) {
|
||||
/** A built-in datapack - materials or modules - and the mod it needs. */
|
||||
private record MaterialPack(String id, String name, String requires) {
|
||||
boolean present() {
|
||||
for (String mod : requires) {
|
||||
if (!ModList.get().isLoaded(mod)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return ModList.get().isLoaded(requires);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,21 +68,11 @@ public class CMModular {
|
|||
// Modules, not materials, and gated for the same reason: they name
|
||||
// slots - and inherit modules - that only exist with these installed.
|
||||
new MaterialPack("arsenal", "Arsenal Modules", "tm_arsenal"),
|
||||
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"));
|
||||
new MaterialPack("armory", "Armory Modules", "tm_armory"));
|
||||
|
||||
public CMModular(IEventBus modBus) {
|
||||
FireImmunity.register(modBus);
|
||||
VacuumSeal.register(modBus);
|
||||
WaterBreathing.register(modBus);
|
||||
WaterCombat.register(modBus);
|
||||
SandPhasing.register(modBus);
|
||||
EquipmentAttributes.register(modBus);
|
||||
ArcanaCore.register(modBus);
|
||||
ApothicSockets.register();
|
||||
modBus.addListener(this::addMaterialPacks);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
package eu.abdelbaki.cmmodular;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
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.AttributeMap;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||
|
||||
/**
|
||||
* Keeps an attribute that a piece of equipment grants from quietly going
|
||||
* missing.
|
||||
*
|
||||
* <p>Modifiers off worn and held items are transient: vanilla reads them when
|
||||
* the equipment changes and puts them in the entity's attribute map, and that
|
||||
* map is the only place they live. Change dimension and they can be gone - the
|
||||
* player is rebuilt on the way through, no equipment change follows, and so
|
||||
* nothing puts them back until the piece is taken off and put on again. Cinder
|
||||
* essence's fire immunity is the one that gets noticed, because the Nether is
|
||||
* exactly where it was wanted.
|
||||
*
|
||||
* <p>Two answers, because a modifier can be missing in two places. The map is
|
||||
* rebuilt from the equipment after a dimension change or a respawn, which is
|
||||
* what re-equipping would have done and which fixes every attribute rather than
|
||||
* only this mod's. And when one of this mod's own attributes is read, the
|
||||
* equipment is consulted as a fallback - the same data vanilla applies from, so
|
||||
* the answer is the same whether or not the map ever lost it.
|
||||
*/
|
||||
public final class EquipmentAttributes {
|
||||
private EquipmentAttributes() {
|
||||
}
|
||||
|
||||
static void register(IEventBus modBus) {
|
||||
NeoForge.EVENT_BUS.addListener(EquipmentAttributes::onChangedDimension);
|
||||
NeoForge.EVENT_BUS.addListener(EquipmentAttributes::onRespawn);
|
||||
}
|
||||
|
||||
private static void onChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
|
||||
reapply(event.getEntity());
|
||||
}
|
||||
|
||||
private static void onRespawn(PlayerEvent.PlayerRespawnEvent event) {
|
||||
reapply(event.getEntity());
|
||||
}
|
||||
|
||||
/** Puts every modifier the worn and held items grant back into the map. */
|
||||
private static void reapply(Player player) {
|
||||
AttributeMap attributes = player.getAttributes();
|
||||
for (EquipmentSlot slot : EquipmentSlot.values()) {
|
||||
ItemStack stack = player.getItemBySlot(slot);
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Multimap<Holder<Attribute>, AttributeModifier> modifiers = HashMultimap.create();
|
||||
stack.forEachModifier(slot, modifiers::put);
|
||||
// Each modifier is removed and added again, so this is a no-op
|
||||
// wherever one survived and a repair wherever one did not.
|
||||
attributes.addTransientAttributeModifiers(modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What one of this mod's attributes is worth on this entity, falling back
|
||||
* to the equipment when the attribute map has lost it.
|
||||
*
|
||||
* <p>Only additive modifiers are counted, which is all this mod grants.
|
||||
*/
|
||||
static double value(LivingEntity entity, Holder<Attribute> attribute) {
|
||||
AttributeInstance instance = entity.getAttribute(attribute);
|
||||
double live = instance == null ? 0.0D : instance.getValue();
|
||||
if (live > 0.0D) {
|
||||
return live;
|
||||
}
|
||||
double[] worn = {0.0D};
|
||||
for (EquipmentSlot slot : EquipmentSlot.values()) {
|
||||
ItemStack stack = entity.getItemBySlot(slot);
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
stack.forEachModifier(slot, (holder, modifier) -> {
|
||||
if (holder.value() == attribute.value()
|
||||
&& modifier.operation() == AttributeModifier.Operation.ADD_VALUE) {
|
||||
worn[0] += modifier.amount();
|
||||
}
|
||||
});
|
||||
}
|
||||
return worn[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package eu.abdelbaki.cmmodular;
|
|||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.tags.DamageTypeTags;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
|
|
@ -46,7 +47,8 @@ public final class FireImmunity {
|
|||
if (!event.getSource().is(DamageTypeTags.IS_FIRE)) {
|
||||
return;
|
||||
}
|
||||
if (EquipmentAttributes.value(event.getEntity(), FIRE_IMMUNITY) >= 1.0D) {
|
||||
AttributeInstance instance = event.getEntity().getAttribute(FIRE_IMMUNITY);
|
||||
if (instance != null && instance.getValue() >= 1.0D) {
|
||||
// Put the wearer out as well, otherwise they burn harmlessly but
|
||||
// permanently, which reads as a bug.
|
||||
event.getEntity().clearFire();
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
package eu.abdelbaki.cmmodular;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.damagesource.DamageTypes;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
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.RangedAttribute;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.EntityCollisionContext;
|
||||
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.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
/**
|
||||
* Deathworm chitin lets its bearer move through sand the way the worm it came
|
||||
* off does.
|
||||
*
|
||||
* <p>Which blocks count is a tag rather than a list, so a pack can widen it:
|
||||
* {@code cmmodular:sand_like} is the common {@code c:sands} and {@code c:gravels}
|
||||
* plus the vanilla loose blocks by name, in case the common tags are not there.
|
||||
*
|
||||
* <p>Collision is not an attribute, an event or a property - it is asked of the
|
||||
* block. So this is the one place the mod needs a mixin, and it is a small one:
|
||||
* a sand block returns no collision shape to an entity carrying the attribute.
|
||||
*
|
||||
* <p>What it deliberately does <em>not</em> do is drop the bearer through the
|
||||
* ground. A block entirely below their feet still holds them up, so a desert is
|
||||
* still something to walk on and a dune is something to walk into: step into a
|
||||
* dune and you keep the level you were at, jump and the sand under you turns
|
||||
* solid again and puts you back on top. Without that rule the first sand block
|
||||
* a wearer stood on would swallow them down to the sandstone and leave them
|
||||
* there, which is a trap rather than an ability. Sand you are standing inside
|
||||
* of cannot suffocate you either - that check is below.
|
||||
*/
|
||||
public final class SandPhasing {
|
||||
private static final DeferredRegister<Attribute> ATTRIBUTES =
|
||||
DeferredRegister.create(Registries.ATTRIBUTE, CMModular.MOD_ID);
|
||||
|
||||
/** At or above 1 the bearer passes through sand; chitin grants a whole one. */
|
||||
public static final DeferredHolder<Attribute, Attribute> SAND_PHASING =
|
||||
ATTRIBUTES.register("sand_phasing", id -> new RangedAttribute(
|
||||
"attribute.cmmodular.sand_phasing", 0.0D, 0.0D, 1.0D).setSyncable(true));
|
||||
|
||||
/** The blocks that are loose enough to move through. */
|
||||
public static final TagKey<Block> SAND_LIKE = TagKey.create(
|
||||
Registries.BLOCK, ResourceLocation.fromNamespaceAndPath(CMModular.MOD_ID, "sand_like"));
|
||||
|
||||
private SandPhasing() {
|
||||
}
|
||||
|
||||
static void register(IEventBus modBus) {
|
||||
ATTRIBUTES.register(modBus);
|
||||
modBus.addListener(SandPhasing::attachToEntities);
|
||||
NeoForge.EVENT_BUS.addListener(SandPhasing::onIncomingDamage);
|
||||
}
|
||||
|
||||
/** 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, SAND_PHASING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this block should let this entity through. Called from the
|
||||
* collision mixin for every block anything is about to collide with, so
|
||||
* the cheap tests come first and the attribute is read straight off the
|
||||
* entity rather than through {@link EquipmentAttributes}.
|
||||
*/
|
||||
public static boolean phasesThrough(BlockState state, BlockPos pos, CollisionContext context) {
|
||||
if (!(context instanceof EntityCollisionContext entityContext)) {
|
||||
return false;
|
||||
}
|
||||
Entity entity = entityContext.getEntity();
|
||||
if (!(entity instanceof LivingEntity) || !state.is(SAND_LIKE)) {
|
||||
return false;
|
||||
}
|
||||
// Anything wholly under the feet is floor, and floor stays solid.
|
||||
if (pos.getY() + 1 <= entity.getBoundingBox().minY + 1.0E-7D) {
|
||||
return false;
|
||||
}
|
||||
return grants((LivingEntity) entity);
|
||||
}
|
||||
|
||||
/** Sand you are standing inside of is not sand that can crush you. */
|
||||
private static void onIncomingDamage(LivingIncomingDamageEvent event) {
|
||||
if (!event.getSource().is(DamageTypes.IN_WALL)) {
|
||||
return;
|
||||
}
|
||||
LivingEntity entity = event.getEntity();
|
||||
BlockPos eye = BlockPos.containing(entity.getEyePosition());
|
||||
if (entity.level().getBlockState(eye).is(SAND_LIKE)
|
||||
&& EquipmentAttributes.value(entity, SAND_PHASING) >= 1.0D) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean grants(LivingEntity entity) {
|
||||
AttributeInstance instance = entity.getAttribute(SAND_PHASING);
|
||||
return instance != null && instance.getValue() >= 1.0D;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package eu.abdelbaki.cmmodular;
|
|||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
|
|
@ -55,7 +56,8 @@ public final class VacuumSeal {
|
|||
if (event.canBreathe()) {
|
||||
return;
|
||||
}
|
||||
if (EquipmentAttributes.value(event.getEntity(), VACUUM_SEAL) >= 1.0D) {
|
||||
AttributeInstance instance = event.getEntity().getAttribute(VACUUM_SEAL);
|
||||
if (instance != null && instance.getValue() >= 1.0D) {
|
||||
event.setCanBreathe(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
package eu.abdelbaki.cmmodular;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
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.LivingBreatheEvent;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
/**
|
||||
* Sea serpent fang armour lets its wearer breathe underwater, which is what Ice
|
||||
* 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
|
||||
* closest data can get is a fake Respiration enchantment, which only postpones
|
||||
* drowning. So this follows the pattern {@link FireImmunity} and
|
||||
* {@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.
|
||||
*
|
||||
* <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
|
||||
* a fang is a fang and not half a spacesuit.
|
||||
*/
|
||||
public final class WaterBreathing {
|
||||
private static final DeferredRegister<Attribute> ATTRIBUTES =
|
||||
DeferredRegister.create(Registries.ATTRIBUTE, CMModular.MOD_ID);
|
||||
|
||||
/** At or above 1 the bearer never drowns; each worn fang module is 0.5. */
|
||||
public static final DeferredHolder<Attribute, Attribute> WATER_BREATHING =
|
||||
ATTRIBUTES.register("water_breathing", id -> new RangedAttribute(
|
||||
"attribute.cmmodular.water_breathing", 0.0D, 0.0D, 1.0D).setSyncable(true));
|
||||
|
||||
private WaterBreathing() {
|
||||
}
|
||||
|
||||
static void register(IEventBus modBus) {
|
||||
ATTRIBUTES.register(modBus);
|
||||
modBus.addListener(WaterBreathing::attachToEntities);
|
||||
// Last, so it answers after whatever said there was no air.
|
||||
NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, WaterBreathing::onBreathe);
|
||||
}
|
||||
|
||||
/** 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_BREATHING));
|
||||
}
|
||||
|
||||
private static void onBreathe(LivingBreatheEvent event) {
|
||||
if (event.canBreathe() || !event.getEntity().isEyeInFluid(FluidTags.WATER)) {
|
||||
return;
|
||||
}
|
||||
if (EquipmentAttributes.value(event.getEntity(), WATER_BREATHING) >= 1.0D) {
|
||||
event.setCanBreathe(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package eu.abdelbaki.cmmodular.mixin;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import eu.abdelbaki.cmmodular.SandPhasing;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
/**
|
||||
* The one mixin in this mod, and the only way in: whether a block collides is
|
||||
* asked of the block, with the entity along for the ride in the collision
|
||||
* context, and no event or property sits in between. Deathworm chitin answers
|
||||
* for sand - see {@link SandPhasing}, which owns the decision so that this
|
||||
* stays a two-line hook into someone else's class.
|
||||
*/
|
||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||
public abstract class BlockStateBaseMixin {
|
||||
@Inject(
|
||||
method = "getCollisionShape(Lnet/minecraft/world/level/BlockGetter;"
|
||||
+ "Lnet/minecraft/core/BlockPos;"
|
||||
+ "Lnet/minecraft/world/phys/shapes/CollisionContext;)"
|
||||
+ "Lnet/minecraft/world/phys/shapes/VoxelShape;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true)
|
||||
private void cmmodular$phaseThroughSand(BlockGetter level, BlockPos pos,
|
||||
CollisionContext context,
|
||||
CallbackInfoReturnable<VoxelShape> callback) {
|
||||
if (SandPhasing.phasesThrough((BlockState) (Object) this, pos, context)) {
|
||||
callback.setReturnValue(Shapes.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,6 @@ loaderVersion = "[4,)"
|
|||
license = "MIT"
|
||||
issueTrackerURL = "https://github.com/themiro/create-mekanism-modular/issues"
|
||||
|
||||
# Collision is asked of the block, not raised as an event, so deathworm chitin's
|
||||
# way through sand needs one small mixin. Nothing else here does.
|
||||
[[mixins]]
|
||||
config = "cmmodular.mixins.json"
|
||||
|
||||
[[mods]]
|
||||
modId = "${mod_id}"
|
||||
version = "${mod_version}"
|
||||
|
|
|
|||
|
|
@ -1,166 +1,13 @@
|
|||
{
|
||||
"attribute.cmmodular.fire_immunity": "Fire Immunity",
|
||||
"attribute.cmmodular.vacuum_seal": "Vacuum Seal",
|
||||
"attribute.cmmodular.water_breathing": "Water Breathing",
|
||||
"attribute.cmmodular.water_combat": "Underwater Fighting",
|
||||
"attribute.cmmodular.sand_phasing": "Sand Phasing",
|
||||
"item.cmmodular.arcana_core": "Arcana Core",
|
||||
|
||||
"miapi.module.cmmodular.blade.khopesh.name": "Khopesh Blade",
|
||||
"miapi.module.cmmodular.pommel.gem_socket.name": "Socketed Pommel",
|
||||
"miapi.module.cmmodular.pommel.great_socket.name": "Great Socketed Pommel",
|
||||
|
||||
"miapi.module.cmmodular.armor.space.helmet.name": "Sealed Helmet",
|
||||
"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_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.module.cmmodular.gem.case_small.name": "Small Apothic Gem Case",
|
||||
"miapi.module.cmmodular.gem.case_great.name": "Great Apothic Gem Case",
|
||||
"miapi.module.cmmodular.gem.limb.gem_arm_left.name": "%s Left Pauldron Gemstone",
|
||||
"miapi.module.cmmodular.gem.limb.case_arm_left.name": "Left Pauldron Apothic Gem Case",
|
||||
"miapi.module.cmmodular.gem.limb.gem_arm_right.name": "%s Right Pauldron Gemstone",
|
||||
"miapi.module.cmmodular.gem.limb.case_arm_right.name": "Right Pauldron Apothic Gem Case",
|
||||
"miapi.module.cmmodular.gem.limb.gem_leg_left.name": "%s Left Knee Gemstone",
|
||||
"miapi.module.cmmodular.gem.limb.case_leg_left.name": "Left Knee Apothic Gem Case",
|
||||
"miapi.module.cmmodular.gem.limb.gem_leg_right.name": "%s Right Knee Gemstone",
|
||||
"miapi.module.cmmodular.gem.limb.case_leg_right.name": "Right Knee Apothic Gem Case",
|
||||
"miapi.material.cmmodular.bone.dragonbone": "Dragonbone",
|
||||
"miapi.material.cmmodular.bone.hippogryph_talon": "Talon",
|
||||
"miapi.material.cmmodular.bone.sea_serpent_fang": "Sea Serpent Fang",
|
||||
"miapi.material.cmmodular.bone.witherbone": "Witherbone",
|
||||
"miapi.material.cmmodular.crystal.arcana_core": "Arcana Core",
|
||||
"miapi.material.cmmodular.crystal.arcane_essence": "Arcane",
|
||||
"miapi.material.cmmodular.crystal.avaricite_shard": "Avaricite",
|
||||
"miapi.material.cmmodular.crystal.bismuth_crystal": "Bismuth",
|
||||
"miapi.material.cmmodular.crystal.blazing_crystal": "Blazing",
|
||||
"miapi.material.cmmodular.crystal.certus_quartz": "Certus Quartz",
|
||||
"miapi.material.cmmodular.crystal.charged_certus_quartz": "Charged Certus",
|
||||
"miapi.material.cmmodular.crystal.cinder_essence": "Cinder",
|
||||
"miapi.material.cmmodular.crystal.citrine_shard": "Citrine",
|
||||
"miapi.material.cmmodular.crystal.dimensional_shard": "Dimensional",
|
||||
"miapi.material.cmmodular.crystal.divinite_shard": "Divinite",
|
||||
"miapi.material.cmmodular.crystal.dread_shard": "Dread",
|
||||
"miapi.material.cmmodular.crystal.ender_crystal": "Ender Crystal",
|
||||
"miapi.material.cmmodular.crystal.enticing_crystal": "Enticing Crystal",
|
||||
"miapi.material.cmmodular.crystal.floralite_shard": "Floralite",
|
||||
"miapi.material.cmmodular.crystal.fluix": "Fluix",
|
||||
"miapi.material.cmmodular.crystal.fluorite": "Fluorite",
|
||||
"miapi.material.cmmodular.crystal.frostbite_crystal": "Frostbite",
|
||||
"miapi.material.cmmodular.crystal.glow_shard": "Glowshard",
|
||||
"miapi.material.cmmodular.crystal.hemalite_shard": "Hemalite",
|
||||
"miapi.material.cmmodular.crystal.ice_shard": "Ice",
|
||||
"miapi.material.cmmodular.crystal.moonstone_shard": "Moonstone",
|
||||
"miapi.material.cmmodular.crystal.onyx_shard": "Onyx",
|
||||
"miapi.material.cmmodular.crystal.paltaeria_gem": "Paltaeria",
|
||||
"miapi.material.cmmodular.crystal.pixie_dust": "Pixie Dust",
|
||||
"miapi.material.cmmodular.crystal.prescient_crystal": "Prescient Crystal",
|
||||
"miapi.material.cmmodular.crystal.pulsating_crystal": "Pulsating Crystal",
|
||||
"miapi.material.cmmodular.crystal.sapphire": "Sapphire",
|
||||
"miapi.material.cmmodular.crystal.shimmerstone_gem": "Shimmerstone",
|
||||
"miapi.material.cmmodular.crystal.silicon_carbide": "Silicon Carbide",
|
||||
"miapi.material.cmmodular.crystal.source_gem": "Source",
|
||||
"miapi.material.cmmodular.crystal.spirit_attuned_gem": "Spirit Attuned",
|
||||
"miapi.material.cmmodular.crystal.stratine_gem": "Stratine",
|
||||
"miapi.material.cmmodular.crystal.sulfur": "Sulfur",
|
||||
"miapi.material.cmmodular.crystal.topaz_shard": "Topaz",
|
||||
"miapi.material.cmmodular.crystal.vibrant_crystal": "Vibrant Crystal",
|
||||
"miapi.material.cmmodular.crystal.voidstone_shard": "Voidstone",
|
||||
"miapi.material.cmmodular.crystal.volcanite_shard": "Volcanite",
|
||||
"miapi.material.cmmodular.crystal.voltite_shard": "Voltite",
|
||||
"miapi.material.cmmodular.crystal.weather_crystal": "Weather Crystal",
|
||||
"miapi.material.cmmodular.fabric.troll_leather_forest": "Forest Troll Leather",
|
||||
"miapi.material.cmmodular.fabric.troll_leather_frost": "Frost Troll Leather",
|
||||
"miapi.material.cmmodular.fabric.troll_leather_mountain": "Mountain Troll Leather",
|
||||
"miapi.material.cmmodular.fletching.amphithere_feather": "Amphithere",
|
||||
"miapi.material.cmmodular.fletching.stymphalian_feather": "Stymphalian",
|
||||
"miapi.material.cmmodular.metal.aluminum": "Aluminium",
|
||||
"miapi.material.cmmodular.metal.amalgam": "Amalgam",
|
||||
"miapi.material.cmmodular.metal.andesite_alloy": "Andesite Alloy",
|
||||
"miapi.material.cmmodular.metal.antimatter": "Antimatter",
|
||||
"miapi.material.cmmodular.metal.aquarine_steel": "Aquarine Steel",
|
||||
"miapi.material.cmmodular.metal.arcane_metal": "Arcane Metal",
|
||||
"miapi.material.cmmodular.metal.atlantic_gold": "Atlantic Gold",
|
||||
"miapi.material.cmmodular.metal.atomic_alloy": "Atomic Alloy",
|
||||
"miapi.material.cmmodular.metal.aviation_alloy": "Aviation Alloy",
|
||||
"miapi.material.cmmodular.metal.brass": "Brass",
|
||||
"miapi.material.cmmodular.metal.bronze": "Bronze",
|
||||
"miapi.material.cmmodular.metal.cast_iron": "Cast Iron",
|
||||
"miapi.material.cmmodular.metal.cemented_carbide": "Cemented Carbide",
|
||||
"miapi.material.cmmodular.metal.computronic_substrate": "Computronic",
|
||||
"miapi.material.cmmodular.metal.conductive_alloy": "Conductive Alloy",
|
||||
"miapi.material.cmmodular.metal.constantan": "Constantan",
|
||||
"miapi.material.cmmodular.metal.dark_steel": "Dark Steel",
|
||||
"miapi.material.cmmodular.metal.dragonsteel_fire": "Fire Dragonsteel",
|
||||
"miapi.material.cmmodular.metal.dragonsteel_ice": "Ice Dragonsteel",
|
||||
"miapi.material.cmmodular.metal.dragonsteel_lightning": "Lightning Dragonsteel",
|
||||
"miapi.material.cmmodular.metal.electrum": "Electrum",
|
||||
"miapi.material.cmmodular.metal.end_steel": "End Steel",
|
||||
"miapi.material.cmmodular.metal.energetic_alloy": "Energetic Alloy",
|
||||
"miapi.material.cmmodular.metal.ferdonzor": "Ferdonzor",
|
||||
"miapi.material.cmmodular.metal.ghost_ingot": "Phantasmal",
|
||||
"miapi.material.cmmodular.metal.hdpe": "HDPE",
|
||||
"miapi.material.cmmodular.metal.hop_graphite": "Graphite",
|
||||
"miapi.material.cmmodular.metal.iesnium": "Iesnium",
|
||||
"miapi.material.cmmodular.metal.industrial_iron": "Industrial Iron",
|
||||
"miapi.material.cmmodular.metal.infused_alloy": "Infused Alloy",
|
||||
"miapi.material.cmmodular.metal.lead": "Lead",
|
||||
"miapi.material.cmmodular.metal.microlattice": "Microlattice",
|
||||
"miapi.material.cmmodular.metal.mithril": "Mithril",
|
||||
"miapi.material.cmmodular.metal.nickel": "Nickel",
|
||||
"miapi.material.cmmodular.metal.osmium": "Osmium",
|
||||
"miapi.material.cmmodular.metal.pink_slime": "Pink Slime",
|
||||
"miapi.material.cmmodular.metal.platinum": "Platinum",
|
||||
"miapi.material.cmmodular.metal.plutonium": "Plutonium",
|
||||
"miapi.material.cmmodular.metal.polonium": "Polonium",
|
||||
"miapi.material.cmmodular.metal.pulsating_alloy": "Pulsating Alloy",
|
||||
"miapi.material.cmmodular.metal.pyrium": "Pyrium",
|
||||
"miapi.material.cmmodular.metal.redstone_alloy": "Redstone Alloy",
|
||||
"miapi.material.cmmodular.metal.refined_glowstone": "Refined Glowstone",
|
||||
"miapi.material.cmmodular.metal.refined_obsidian": "Refined Obsidian",
|
||||
"miapi.material.cmmodular.metal.refined_radiance": "Refined Radiance",
|
||||
"miapi.material.cmmodular.metal.reinforced_alloy": "Reinforced Alloy",
|
||||
"miapi.material.cmmodular.metal.shadow_steel": "Shadow Steel",
|
||||
"miapi.material.cmmodular.metal.silver": "Silver",
|
||||
"miapi.material.cmmodular.metal.soularium": "Soularium",
|
||||
"miapi.material.cmmodular.metal.steel": "Steel",
|
||||
"miapi.material.cmmodular.metal.tin": "Tin",
|
||||
"miapi.material.cmmodular.metal.titanium": "Titanium",
|
||||
"miapi.material.cmmodular.metal.titanium_alloy": "Titanium Alloy",
|
||||
"miapi.material.cmmodular.metal.tungsten": "Tungsten",
|
||||
"miapi.material.cmmodular.metal.uranium": "Uranium",
|
||||
"miapi.material.cmmodular.metal.vibrant_alloy": "Vibrant Alloy",
|
||||
"miapi.material.cmmodular.metal.zinc": "Zinc",
|
||||
"miapi.material.cmmodular.scute.deathworm_chitin_red": "Red Chitin",
|
||||
"miapi.material.cmmodular.scute.deathworm_chitin_white": "White Chitin",
|
||||
"miapi.material.cmmodular.scute.deathworm_chitin_yellow": "Tan Chitin",
|
||||
"miapi.material.cmmodular.scute.dragonscales_amethyst": "Amethyst Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_black": "Black Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_blue": "Blue Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_bronze": "Bronze Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_copper": "Copper Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_electric": "Electric Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_gray": "Gray Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_green": "Green Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_red": "Red Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_sapphire": "Sapphire Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_silver": "Silver Dragonscale",
|
||||
"miapi.material.cmmodular.scute.dragonscales_white": "White Dragonscale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_blue": "Blue Serpent Scale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_bronze": "Bronze Serpent Scale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_deepblue": "Deep Blue Serpent Scale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_green": "Green Serpent Scale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_purple": "Purple Serpent Scale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_red": "Red Serpent Scale",
|
||||
"miapi.material.cmmodular.scute.sea_serpent_scales_teal": "Teal Serpent Scale"
|
||||
"miapi.module.cmmodular.armor.space.life_support_socket.name": "Life Support Pack with Socket"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "cmmodular:items/arcana_core"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "cmmodular:items/gem_case"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "cmmodular:items/gem_case_great"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "cmmodular:items/gem_case_small"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
{
|
||||
"comment": "Gem socket drawn under left_arm, so it moves with the limb. MIAPI draws a model in the pass its own origin names, and Armory's gemstone names none, so the socket has to be ours for anything here to follow the limb at all.",
|
||||
"texture_size": [
|
||||
16,
|
||||
8
|
||||
],
|
||||
"textures": {
|
||||
"0": "cmmodular:equipment/arm_left_socket",
|
||||
"particle": "cmmodular:equipment/arm_left_socket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "pauldron socket",
|
||||
"from": [
|
||||
-2.5,
|
||||
-0.25,
|
||||
-1.5
|
||||
],
|
||||
"to": [
|
||||
-2.0,
|
||||
1.75,
|
||||
1.5
|
||||
],
|
||||
"rotation": {
|
||||
"angle": -22.5,
|
||||
"axis": "z",
|
||||
"origin": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"faces": {
|
||||
"up": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
3.0,
|
||||
0.0,
|
||||
4.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"down": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
0.0,
|
||||
5.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"west": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
0.0,
|
||||
3.0,
|
||||
3.0,
|
||||
5.0
|
||||
]
|
||||
},
|
||||
"north": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
3.0,
|
||||
3.0,
|
||||
4.0,
|
||||
5.0
|
||||
]
|
||||
},
|
||||
"east": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
3.0,
|
||||
7.0,
|
||||
5.0
|
||||
]
|
||||
},
|
||||
"south": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
7.0,
|
||||
3.0,
|
||||
8.0,
|
||||
5.0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
{
|
||||
"comment": "Gem socket drawn under right_arm, so it moves with the limb. MIAPI draws a model in the pass its own origin names, and Armory's gemstone names none, so the socket has to be ours for anything here to follow the limb at all.",
|
||||
"texture_size": [
|
||||
16,
|
||||
8
|
||||
],
|
||||
"textures": {
|
||||
"0": "cmmodular:equipment/arm_right_socket",
|
||||
"particle": "cmmodular:equipment/arm_right_socket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "pauldron socket",
|
||||
"from": [
|
||||
2.0,
|
||||
-0.25,
|
||||
-1.5
|
||||
],
|
||||
"to": [
|
||||
2.5,
|
||||
1.75,
|
||||
1.5
|
||||
],
|
||||
"rotation": {
|
||||
"angle": 22.5,
|
||||
"axis": "z",
|
||||
"origin": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"faces": {
|
||||
"up": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
3.0,
|
||||
0.0,
|
||||
4.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"down": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
0.0,
|
||||
5.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"west": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
0.0,
|
||||
3.0,
|
||||
3.0,
|
||||
5.0
|
||||
]
|
||||
},
|
||||
"north": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
3.0,
|
||||
3.0,
|
||||
4.0,
|
||||
5.0
|
||||
]
|
||||
},
|
||||
"east": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
3.0,
|
||||
7.0,
|
||||
5.0
|
||||
]
|
||||
},
|
||||
"south": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
7.0,
|
||||
3.0,
|
||||
8.0,
|
||||
5.0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
{
|
||||
"comment": "Gem socket drawn under left_leg, so it moves with the limb. MIAPI draws a model in the pass its own origin names, and Armory's gemstone names none, so the socket has to be ours for anything here to follow the limb at all.",
|
||||
"texture_size": [
|
||||
8,
|
||||
4
|
||||
],
|
||||
"textures": {
|
||||
"0": "cmmodular:equipment/leg_left_socket",
|
||||
"particle": "cmmodular:equipment/leg_left_socket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "knee socket",
|
||||
"from": [
|
||||
-1.5,
|
||||
-2.95,
|
||||
-2.7
|
||||
],
|
||||
"to": [
|
||||
1.5,
|
||||
-0.95,
|
||||
-2.2
|
||||
],
|
||||
"faces": {
|
||||
"up": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
1.0,
|
||||
0.0,
|
||||
4.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"down": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
0.0,
|
||||
7.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"west": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
0.0,
|
||||
1.0,
|
||||
1.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"north": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
1.0,
|
||||
1.0,
|
||||
4.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"east": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
1.0,
|
||||
5.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"south": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
5.0,
|
||||
1.0,
|
||||
8.0,
|
||||
3.0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
{
|
||||
"comment": "Gem socket drawn under right_leg, so it moves with the limb. MIAPI draws a model in the pass its own origin names, and Armory's gemstone names none, so the socket has to be ours for anything here to follow the limb at all.",
|
||||
"texture_size": [
|
||||
8,
|
||||
4
|
||||
],
|
||||
"textures": {
|
||||
"0": "cmmodular:equipment/leg_right_socket",
|
||||
"particle": "cmmodular:equipment/leg_right_socket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "knee socket",
|
||||
"from": [
|
||||
-1.5,
|
||||
-2.95,
|
||||
-2.7
|
||||
],
|
||||
"to": [
|
||||
1.5,
|
||||
-0.95,
|
||||
-2.2
|
||||
],
|
||||
"faces": {
|
||||
"up": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
1.0,
|
||||
0.0,
|
||||
4.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"down": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
0.0,
|
||||
7.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"west": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
0.0,
|
||||
1.0,
|
||||
1.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"north": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
1.0,
|
||||
1.0,
|
||||
4.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"east": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
4.0,
|
||||
1.0,
|
||||
5.0,
|
||||
3.0
|
||||
]
|
||||
},
|
||||
"south": {
|
||||
"texture": "#0",
|
||||
"uv": [
|
||||
5.0,
|
||||
1.0,
|
||||
8.0,
|
||||
3.0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 126 B |
|
Before Width: | Height: | Size: 126 B |
|
Before Width: | Height: | Size: 356 B |
|
Before Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 147 B |
|
Before Width: | Height: | Size: 119 B |
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 128 B |
|
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 152 B |
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "eu.abdelbaki.cmmodular.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"BlockStateBaseMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"values": [
|
||||
{ "id": "#c:sands", "required": false },
|
||||
{ "id": "#c:gravels", "required": false },
|
||||
{ "id": "#c:concrete_powders", "required": false },
|
||||
"minecraft:sand",
|
||||
"minecraft:red_sand",
|
||||
"minecraft:suspicious_sand",
|
||||
"minecraft:gravel",
|
||||
"minecraft:suspicious_gravel",
|
||||
"minecraft:soul_sand",
|
||||
"minecraft:soul_soil"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Certus Quartz ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Charged Certus ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Fluix ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Aviation Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Sulfur ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem_melee",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Amalgam ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"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",
|
||||
"gem_melee_medium"
|
||||
],
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic",
|
||||
"gem_melee",
|
||||
"gem_melee_medium",
|
||||
"gem_melee_generic",
|
||||
"gem_melee_medium_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"comment": "The great socket takes a large gem and nothing else, so the case that goes in it has to be a large one too - `gem_melee_large` is the key the great pommel asks for, and a medium case does not answer to it.",
|
||||
"display_name": "miapi.module.cmmodular.gem.case_great.name",
|
||||
"model": {
|
||||
"path": "miapi:models/item/armor/gems/large/[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_melee_large"
|
||||
],
|
||||
"tag": [
|
||||
"gem_melee",
|
||||
"gem_melee_large",
|
||||
"gem_melee_generic",
|
||||
"gem_melee_large_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"comment": "The small case, for the slots a small gemstone goes in - Arsenal's own socket pommel and the dual socket guard, which is two of them. Both size keys are named because a small slot is a small slot whether it was cut into a weapon or into armour, and a key nothing asks for costs nothing.",
|
||||
"display_name": "miapi.module.cmmodular.gem.case_small.name",
|
||||
"model": {
|
||||
"path": "miapi:models/item/armor/gems/small/[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_melee_small",
|
||||
"gem_armor_small"
|
||||
],
|
||||
"tag": [
|
||||
"gem_melee",
|
||||
"gem_melee_small",
|
||||
"gem_melee_generic",
|
||||
"gem_melee_small_generic",
|
||||
"gem_armor",
|
||||
"gem_armor_small",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_small_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.case_arm_left.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},
|
||||
"origin": "left_arm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 4.5, "y": 3.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_arm_left"
|
||||
],
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.case_arm_right.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},
|
||||
"origin": "right_arm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": -4.5, "y": 3.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_arm_right"
|
||||
],
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.case_leg_left.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},
|
||||
"origin": "left_leg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 2.5, "y": -4.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_leg_left"
|
||||
],
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.case_leg_right.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},
|
||||
"origin": "right_leg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": -2.5, "y": -4.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_leg_right"
|
||||
],
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"material_property": [
|
||||
"default"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "rare",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"metal",
|
||||
"crystal",
|
||||
"glass",
|
||||
"bone",
|
||||
"stone"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"pack": {
|
||||
"description": "Apothic Gem Cases for Truly Modular",
|
||||
"pack_format": 48
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
"parent": "tm_armory:armor/heavy/arm_left",
|
||||
"data": {
|
||||
"merge": {
|
||||
"module_tag": [
|
||||
"socket_armor"
|
||||
],
|
||||
"attributes": [
|
||||
{
|
||||
"attribute": "minecraft:generic.armor",
|
||||
"value": "-0.05",
|
||||
"operation": "*",
|
||||
"slot": "chest"
|
||||
}
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"path": "cmmodular:models/item/armor/model/arm_left/socket/[material.texture].json",
|
||||
"trim_mode": "ARMOR_LAYER_ONE",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 0, "y": 0, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1},
|
||||
"origin": "left_arm"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"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_arm_left"
|
||||
],
|
||||
"translationKey": "miapi.slot.gemstone.medium",
|
||||
"transform": {
|
||||
"rotation": {"x": 90.0, "y": 55.7523, "z": -90.0},
|
||||
"translation": {"x": -1.7667, "y": 1.5983, "z": 0.0},
|
||||
"scale": {"x": 0.6667, "y": 0.8171, "z": 0.7123},
|
||||
"origin": "left_arm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"display_name": "miapi.module.cmmodular.armor.socket.arm_left.name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
"parent": "tm_armory:armor/heavy/arm_right",
|
||||
"data": {
|
||||
"merge": {
|
||||
"module_tag": [
|
||||
"socket_armor"
|
||||
],
|
||||
"attributes": [
|
||||
{
|
||||
"attribute": "minecraft:generic.armor",
|
||||
"value": "-0.05",
|
||||
"operation": "*",
|
||||
"slot": "chest"
|
||||
}
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"path": "cmmodular:models/item/armor/model/arm_right/socket/[material.texture].json",
|
||||
"trim_mode": "ARMOR_LAYER_ONE",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 0, "y": 0, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1},
|
||||
"origin": "right_arm"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"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_arm_right"
|
||||
],
|
||||
"translationKey": "miapi.slot.gemstone.medium",
|
||||
"transform": {
|
||||
"rotation": {"x": 90.0, "y": -55.7523, "z": 90.0},
|
||||
"translation": {"x": 1.7667, "y": 1.5983, "z": 0.0},
|
||||
"scale": {"x": 0.6667, "y": 0.8171, "z": 0.7123},
|
||||
"origin": "right_arm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"display_name": "miapi.module.cmmodular.armor.socket.arm_right.name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"parent": "tm_armory:armor/heavy/leg_left",
|
||||
"data": {
|
||||
"merge": {
|
||||
"module_tag": [
|
||||
"socket_armor"
|
||||
],
|
||||
"attributes": [
|
||||
{
|
||||
"attribute": "minecraft:generic.armor",
|
||||
"value": "-0.05",
|
||||
"operation": "*",
|
||||
"slot": "legs"
|
||||
}
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"path": "cmmodular:models/item/armor/model/leg_left/socket/[material.texture].json",
|
||||
"trim_mode": "ARMOR_LAYER_ONE",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 0, "y": 0, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1},
|
||||
"origin": "left_leg"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"replace": {
|
||||
"slots": {
|
||||
"gem": {
|
||||
"allowed": [
|
||||
"gem_armor_medium_leg_left"
|
||||
],
|
||||
"translationKey": "miapi.slot.gemstone.medium",
|
||||
"transform": {
|
||||
"rotation": {"x": 0.0, "y": 0.0, "z": -180.0},
|
||||
"translation": {"x": 0.0, "y": -1.95, "z": -2.4917},
|
||||
"scale": {"x": 0.8333, "y": 1.0, "z": 0.8333},
|
||||
"origin": "left_leg"
|
||||
}
|
||||
}
|
||||
},
|
||||
"display_name": "miapi.module.cmmodular.armor.socket.leg_left.name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"parent": "tm_armory:armor/heavy/leg_right",
|
||||
"data": {
|
||||
"merge": {
|
||||
"module_tag": [
|
||||
"socket_armor"
|
||||
],
|
||||
"attributes": [
|
||||
{
|
||||
"attribute": "minecraft:generic.armor",
|
||||
"value": "-0.05",
|
||||
"operation": "*",
|
||||
"slot": "legs"
|
||||
}
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"path": "cmmodular:models/item/armor/model/leg_right/socket/[material.texture].json",
|
||||
"trim_mode": "ARMOR_LAYER_ONE",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 0, "y": -2, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1},
|
||||
"origin": "right_leg"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"replace": {
|
||||
"slots": {
|
||||
"gem": {
|
||||
"allowed": [
|
||||
"gem_armor_medium_leg_right"
|
||||
],
|
||||
"translationKey": "miapi.slot.gemstone.medium",
|
||||
"transform": {
|
||||
"rotation": {"x": 0.0, "y": 0.0, "z": -180.0},
|
||||
"translation": {"x": 0.0, "y": -1.95, "z": -2.4917},
|
||||
"scale": {"x": 0.8333, "y": 1.0, "z": 0.8333},
|
||||
"origin": "right_leg"
|
||||
}
|
||||
}
|
||||
},
|
||||
"display_name": "miapi.module.cmmodular.armor.socket.leg_right.name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
"trim_mode": "ARMOR_LAYER_ONE",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 0, "y": 4.1387, "z": 0},
|
||||
"translation": {"x": 0, "y": 0, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1},
|
||||
"origin": "head"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"trim_mode": "ARMOR_LAYER_ONE",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 0, "y": -2.1901, "z": 0},
|
||||
"translation": {"x": 0, "y": 0, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1},
|
||||
"origin": "body"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.gem_arm_left.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},
|
||||
"origin": "left_arm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 4.5, "y": 3.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_arm_left"
|
||||
],
|
||||
"material_property": [
|
||||
"default",
|
||||
"gem_armor_handheld",
|
||||
"gem_armor_generic"
|
||||
],
|
||||
"module_stats": {
|
||||
"gem_power": 1
|
||||
},
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "uncommon",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"gem_armor"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.gem_arm_right.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},
|
||||
"origin": "right_arm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": -4.5, "y": 3.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_arm_right"
|
||||
],
|
||||
"material_property": [
|
||||
"default",
|
||||
"gem_armor_handheld",
|
||||
"gem_armor_generic"
|
||||
],
|
||||
"module_stats": {
|
||||
"gem_power": 1
|
||||
},
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "uncommon",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"gem_armor"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.gem_leg_left.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},
|
||||
"origin": "left_leg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": 2.5, "y": -4.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_leg_left"
|
||||
],
|
||||
"material_property": [
|
||||
"default",
|
||||
"gem_armor_handheld",
|
||||
"gem_armor_generic"
|
||||
],
|
||||
"module_stats": {
|
||||
"gem_power": 1
|
||||
},
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "uncommon",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"gem_armor"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"display_name": "miapi.module.cmmodular.gem.limb.gem_leg_right.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},
|
||||
"origin": "right_leg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "miapi:models/item/armor/gems/medium/[material.texture].json",
|
||||
"transform": {
|
||||
"rotation": {"x": 0, "y": 0, "z": 0},
|
||||
"translation": {"x": -2.5, "y": -4.5, "z": 0},
|
||||
"scale": {"x": 1, "y": 1, "z": 1}
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_in_slots": [
|
||||
"gem_armor_medium_leg_right"
|
||||
],
|
||||
"material_property": [
|
||||
"default",
|
||||
"gem_armor_handheld",
|
||||
"gem_armor_generic"
|
||||
],
|
||||
"module_stats": {
|
||||
"gem_power": 1
|
||||
},
|
||||
"tag": [
|
||||
"gem_armor",
|
||||
"gem_armor_medium",
|
||||
"gem_armor_generic",
|
||||
"gem_armor_medium_generic"
|
||||
],
|
||||
"priority": 0,
|
||||
"repair_priority": 0,
|
||||
"rarity": "uncommon",
|
||||
"allowed_material": {
|
||||
"allowedMaterials": [
|
||||
"gem_armor"
|
||||
],
|
||||
"cost": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Source ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Titanium ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Titanium Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Andesite Alloy ",
|
||||
"groups": [
|
||||
"metal",
|
||||
"stone"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Brass ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Refined Radiance ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Shadow Steel ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Zinc ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Industrial Iron ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Platinum ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Avaricite ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Divinite ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Floralite ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Hemalite ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Ice ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Voidstone ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Volcanite ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Voltite ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Ender Crystal ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Enticing Crystal ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Prescient Crystal ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Pulsating Crystal ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Vibrant Crystal ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Weather Crystal ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Conductive Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Dark Steel ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "End Steel ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Energetic Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Pulsating Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Redstone Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Soularium ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Vibrant Alloy ",
|
||||
"groups": [
|
||||
"metal"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
{
|
||||
"translation": "Dragonbone ",
|
||||
"groups": [
|
||||
"bone"
|
||||
],
|
||||
"hidden_groups": [
|
||||
"gem_armor"
|
||||
],
|
||||
"icon": {
|
||||
"type": "item",
|
||||
"item": "iceandfire:dragonbone"
|
||||
|
|
@ -39,11 +37,6 @@
|
|||
"properties": {
|
||||
"head": {
|
||||
"fracturing": "10"
|
||||
},
|
||||
"handheld": {
|
||||
"fake_enchants": {
|
||||
"minecraft:fire_aspect": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"items": [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Talon ",
|
||||
"groups": [
|
||||
"bone"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
{
|
||||
"groups": [
|
||||
"bone"
|
||||
],
|
||||
"hidden_groups": [
|
||||
"gem_armor"
|
||||
],
|
||||
"icon": {
|
||||
"type": "item",
|
||||
"item": "iceandfire:sea_serpent_fang"
|
||||
},
|
||||
"hardness": 6.0,
|
||||
"density": 3.0,
|
||||
"flexibility": 4,
|
||||
"durability": 760,
|
||||
"enchantability": 16,
|
||||
"toughness": 2,
|
||||
"tier": 4,
|
||||
"mining_speed": 7,
|
||||
"mining_level": "minecraft:incorrect_for_diamond_tool",
|
||||
"armor_durability": 26,
|
||||
"color": "C8C8C9",
|
||||
"color_palette": {
|
||||
"type": "grayscale_map",
|
||||
"colors": {
|
||||
"24": "12131d",
|
||||
"68": "222538",
|
||||
"107": "525f68",
|
||||
"150": "525f68",
|
||||
"190": "8c8c8c",
|
||||
"216": "c8c8c9",
|
||||
"255": "e2fffa"
|
||||
},
|
||||
"filler": "interpolate"
|
||||
},
|
||||
"textures": [
|
||||
"rough",
|
||||
"shiny"
|
||||
],
|
||||
"properties": {
|
||||
"default": {
|
||||
"attributes": [
|
||||
{
|
||||
"attribute": "cmmodular:water_breathing",
|
||||
"value": "0.5",
|
||||
"operation": "+",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
"boot": {
|
||||
"fake_enchants": {
|
||||
"minecraft:depth_strider": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"item": "iceandfire:sea_serpent_fang",
|
||||
"value": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Witherbone ",
|
||||
"groups": [
|
||||
"bone"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Dread ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"metal",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"translation": "Pixie Dust ",
|
||||
"groups": [
|
||||
"crystal",
|
||||
"gem_melee",
|
||||
|
|
|
|||