280 lines
15 KiB
Markdown
280 lines
15 KiB
Markdown
# Create/Mekanism Modular
|
|
|
|
A [Truly Modular](https://modrinth.com/mod/modular-item-api) addon for Minecraft
|
|
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
|
|
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, 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.
|
|
|
|
## How it is put together
|
|
|
|
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 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,
|
|
and a material whose ingredient item is missing fails to parse. So each source
|
|
mod's materials are a separate built-in datapack, registered in
|
|
`CMModular.java` only when that mod is present.
|
|
|
|
**Identical materials are one material.** Several metals and gems come from
|
|
more than one mod - silver from Ice and Fire, Immersive Engineering, Occultism
|
|
and Superb Warfare, steel from Mekanism, Immersive Engineering and Superb
|
|
Warfare, and so on. There is one entry per metal, and every provider's version
|
|
feeds it: alongside the primary provider's own items each material also accepts
|
|
the `c:` common tag that all of them register into. Any mod's silver ingot is
|
|
just silver.
|
|
|
|
Nothing records where a stack came from, because there is nothing to record -
|
|
MIAPI stores only the material id on the item, so a tool built from Immersive
|
|
Engineering silver is indistinguishable from one built with Ice and Fire silver.
|
|
The primary provider's item is listed first, so anywhere a single representative
|
|
item is read back out - repair and deconstruct previews - it resolves to the
|
|
default rather than to whichever variant was used. Primaries are Mekanism for
|
|
steel/lead/uranium, Ice and Fire for silver, Cosmonautics for titanium; each is
|
|
a one-line change in `tools/materials.py`.
|
|
|
|
The material still lives in its primary provider's pack, so its icon and its
|
|
first ingredient always point at an item that exists. Create: The Air War is the
|
|
one provider that ships no common tags, so `data/c/` adds its titanium to
|
|
`c:ingots/titanium` with optional entries.
|
|
|
|
**Fire immunity.** Cinder essence grants immunity to fire, which is not an
|
|
attribute the game has and not a property MIAPI offers. Instead of coupling to
|
|
MIAPI's internals, `FireImmunity.java` registers a `cmmodular:fire_immunity`
|
|
attribute, the material grants it like any other attribute, and a damage handler
|
|
watches for it. Neither side knows about the other.
|
|
|
|
**Breathing in a vacuum.** The same trick, for the space set - see below.
|
|
|
|
## Modules
|
|
|
|
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
|
|
like: axe damage off hardness *and* density, a full point of knockback, an axe's
|
|
mining level so it fells trees and strips logs, and a fast swing. The cost is
|
|
everything a hook gives up - shorter reach than a sword, no sweep, and a quarter
|
|
less durability than the blade it replaces.
|
|
|
|
There was no khopesh to borrow. Nothing in the Truly Modular ecosystem ships
|
|
one, and the khopesh art that exists elsewhere is either another mod's item or a
|
|
retexture of a vanilla sword - neither is a MIAPI module and neither is ours to
|
|
ship, so the blade is drawn here.
|
|
|
|
**Socketed pommels** (`packs/arsenal`) give the bigger gems somewhere to sit.
|
|
Arsenal does ship a socket pommel, but it takes a small gem, and a sweep of
|
|
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.
|
|
|
|
**The space set** (`packs/armory`) is Create: Cosmonautics' answer to vacuum,
|
|
rebuilt as modules. Cosmonautics suffocates you above the atmosphere by
|
|
answering NeoForge's `LivingBreatheEvent` with "no", and exempts its own space
|
|
helmet through Create's diving code, which asks whether the worn helmet *is a*
|
|
`DivingHelmetItem`. A modular helmet cannot be one, so the exemption is
|
|
unreachable and the set brings its own: `VacuumSeal.java` registers a
|
|
`cmmodular:vacuum_seal` attribute and answers the same event last.
|
|
|
|
It takes two modules to reach the seal. The sealed helmet grants 0.5 and the
|
|
life support pack on the chest grants 0.5, and only at 1 does the wearer
|
|
breathe - so it is a suit, not a hat, which is the same trade Cosmonautics makes
|
|
by pairing its helmet with a jetpack. Both come in a socketed version that
|
|
trades 5% armour for a medium gem slot, exactly as Armory's own socket pieces
|
|
do, and each socketed version has its own model and texture with the setting
|
|
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.
|
|
|
|
## Stats
|
|
|
|
Tinkers' Construct 3 is the reference, re-scaled onto MIAPI's vanilla-derived
|
|
numbers (iron 235 durability, diamond 1546, netherite 2016). Materials with no
|
|
Tinkers counterpart are built from what the material actually is, then fitted to
|
|
the nearest tier band:
|
|
|
|
| Tinkers | here | shape |
|
|
| --- | --- | --- |
|
|
| cobalt | titanium | light, fast, durable, unremarkable damage |
|
|
| steel | steel | balanced workhorse, poor enchantability |
|
|
| hepatizon | brass, bronze | copper alloys - quick and enchantable, not durable |
|
|
| manyullyn | dragonsteel | top damage, endgame gate |
|
|
| rose gold | refined glowstone, pixie dust | fragile, huge enchantability |
|
|
| ancient hide | dragon scales, chitin | armour-first, high toughness |
|
|
|
|
Tiers 6 and 7 sit above netherite: 6 is refined obsidian, shadow steel, refined
|
|
radiance, atomic alloy and the two Iron's Spells essences; 7 is the three
|
|
dragonsteels and antimatter.
|
|
|
|
Real-material character does the rest of the work - lead is the heaviest thing
|
|
here and swings slowest, osmium is dense and tough, HDPE weighs nothing and
|
|
cannot cut rock, silver smites the undead.
|
|
|
|
All of it lives in `tools/materials.py`, one entry per material with the
|
|
reasoning in comments next to the numbers.
|
|
|
|
## Palettes
|
|
|
|
Colours are not hand-picked. `tools/generate_materials.py` opens the source
|
|
mods' jars, resolves each material's item model to its texture, and samples that
|
|
texture to build MIAPI's seven-stop `grayscale_map`.
|
|
|
|
The sampling deliberately does *not* normalise a stop's colour to its brightness
|
|
number. A stop key is the brightness of MIAPI's grayscale template; its value is
|
|
whatever the material looks like at that point in its own shading, which is why
|
|
MIAPI's own netherite tops out at a murky `847a84` while iron runs to white.
|
|
Samples are taken at percentiles of the texture's brightness order, so a texture
|
|
with four shades still yields a full ramp and any hue drift between shadow and
|
|
highlight survives. Pure-black outline pixels are excluded - they are not a
|
|
shade of the material - and the darkest stop is extrapolated below the texture,
|
|
which is what MIAPI's own palettes do.
|
|
|
|
The sample points were tuned against MIAPI's hand-authored copper palette and
|
|
reproduce it to within about 2% per channel.
|
|
|
|
## Textures
|
|
|
|
The modules' own art is hand-drawn and lives where it can be found and changed:
|
|
|
|
```
|
|
assets/cmmodular/textures/items/ flat 16x16 art - gui icons, the khopesh blade
|
|
assets/cmmodular/textures/equipment/ 32x32 cube nets for the worn armour models
|
|
assets/cmmodular/textures/templates_guide.png
|
|
```
|
|
|
|
Everything is greyscale, because MIAPI recolours a module's texture through the
|
|
material's palette: the seven shades a texture may use are the brightness stops
|
|
of that palette (24, 68, 107, 150, 190, 216, 255), and a pixel drawn between two
|
|
of them lands somewhere interpolated and muddy.
|
|
|
|
`textures/*/templates/` holds blanks to draw over - the same size as the real
|
|
thing, with every region a model samples outlined and the seven shades parked in
|
|
a corner no model reads, so they can be picked straight out of the file.
|
|
`templates_guide.png` is a single sheet showing the shades with their values,
|
|
the cube net with each face labelled, and the vanilla armour layout, for when a
|
|
new piece is wanted rather than a repaint of an existing one.
|
|
|
|
## Building
|
|
|
|
```sh
|
|
./gradlew build
|
|
```
|
|
|
|
Needs nothing but NeoForge - Truly Modular is a runtime dependency only, since
|
|
this mod talks to it purely through datapack JSON.
|
|
|
|
## Regenerating materials
|
|
|
|
Only needed after editing `tools/materials.py` or when a source mod changes its
|
|
textures. The generated JSON is committed, so a plain build does not need this.
|
|
|
|
```sh
|
|
python3 tools/generate_materials.py --jars ~/path/to/mod/jars
|
|
```
|
|
|
|
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
|
|
committed files are left untouched - the pack this follows adds and drops mods,
|
|
and neither losing the materials of a mod you no longer have nor being unable to
|
|
regenerate anything without a complete set of jars is useful. Nothing else is
|
|
relaxed: a typo in a mod you *do* have still fails the run. Where several jars
|
|
claim the same asset namespace - Applied Mekanistics writes into `ae2`, half the
|
|
Create addons into `create` - the one with the most files in it wins, which is
|
|
the mod that owns the namespace rather than whichever sorted first.
|
|
|
|
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.
|
|
|
|
## Notes
|
|
|
|
- Titanium's palette comes from Cosmonautics' ingot, which is violet. Air War's
|
|
is blue-white; swap `palette_from` on the titanium entry to prefer it.
|
|
- Cinder essence is nearly black, because its texture is a charred ember. It is
|
|
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.
|
|
- 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.
|
|
- Gem sockets do not check the `gem` group. Arsenal gates melee gems on
|
|
`gem_melee` and Armory gates armour gems on `gem_armor`, so every gem-like
|
|
material carries both and fits any gem slot.
|
|
- Immersive Engineering contributes aluminium, nickel, constantan, electrum and
|
|
graphite. Its steel, lead, uranium and silver are not separate materials - see
|
|
overlapping metals above. Superb Warfare's steel/lead/silver and Create Crafts
|
|
& Additions' electrum are skipped for the same reason.
|
|
- Crystal Chronicles and Pastel add whole families of gems that differ by colour
|
|
and lore rather than by kind, so those are generated from a table with shared
|
|
stat bands per tier. What actually tells them apart in game is the palette,
|
|
and that still comes from each gem's own texture.
|
|
- Arcane essence gives max mana per slot: 1300 on a helmet, 900 chest, 800
|
|
legs, 700 boots, 900 held in the main hand, 400 offhand. Separate per-slot
|
|
modifiers rather than one `armor`-wide one, so a weapon reads as a held bonus
|
|
instead of claiming something "when worn". They stack per module - a piece
|
|
built out of arcane essence that also carries an arcane gem counts twice.
|
|
- Titanium adds `miapi:generic.elytra_glide_efficiency` on the chest. Heavy
|
|
armour subtracts `-30 - density*2` from that stat, so this hands a little
|
|
back rather than cancelling it; titanium's flexibility is nudged up too,
|
|
since Armory's wing modules read flexibility and density for glide.
|
|
- 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.
|
|
- 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 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 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 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
|
|
is heat proof because platinum melts at 1768C.
|
|
- Aviation alloy is AeroEngine's, iron superheated with netherite powder. A
|
|
turbine superalloy: light and heat proof like titanium, harder, worse to
|
|
enchant. It sits between titanium and titanium alloy, which is where its
|
|
recipe sits too.
|
|
- Extensions are material files with a different shape - `parent` naming the
|
|
material to edit and `data` merged onto a copy of it, the same form MIAPI's
|
|
stained glass uses to vary plain glass. The merge replaces a field rather than
|
|
appending to it, so an extension that adds an ingredient repeats the ones it
|
|
keeps; `_extend_with_extra_forms` builds those lists from the material itself.
|
|
- Ice and Fire's amethyst gem is spelled `amythest_gem` in Ice and Fire. The
|
|
extension named the correct spelling, which is an item that does not exist, so
|
|
it did nothing. Extensions are item-checked like materials now.
|
|
- Pastel's paltaeria and stratine gems break into eight fragments on an anvil,
|
|
so a fragment counts as an eighth of a gem, the way a nugget counts as a ninth
|
|
of an ingot.
|