Add Immersive Engineering materials, fix gem sockets, pick one provider per metal

Gem sockets never accepted the two Iron's Spells essences: Arsenal gates its
melee gem modules on the `gem_melee` material group and Armory gates its armour
ones on `gem_armor`, neither of which is the `gem` group they carried. Every
gem-like material now carries both, so it fits any gem slot.

Immersive Engineering adds aluminium, nickel, constantan, electrum and
graphite. Its steel, lead, uranium and silver overlap mods already covered, so
rather than one material accepting either mod's ingot, each metal simply names
one provider - Mekanism for steel/lead/uranium, Ice and Fire for silver. That
keeps one entry per metal in the crafting screen.

Titanium moves to the same rule with Cosmonautics as its provider, which drops
the c: tag files and the any-of pack machinery along with it. That also removes
a latent crash: a tag-fed material still needed an item for its icon, and MIAPI
parses icon items through the registry and throws when one is missing.

The generator now clears its output directory first, so a material that moves
between packs cannot leave a stale copy claiming the same id.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
modules-and-missing-materials
Amir Alexander Abdelbaki 2026-08-12 19:19:36 +02:00
parent c55caa1d1e
commit e3accaf83f
24 changed files with 459 additions and 105 deletions

View File

@ -1,10 +1,10 @@
# Create/Mekanism Modular # Create/Mekanism Modular
A [Truly Modular](https://modrinth.com/mod/modular-item-api) addon for Minecraft A [Truly Modular](https://modrinth.com/mod/modular-item-api) addon for Minecraft
1.21.1 / NeoForge. It adds 72 materials drawn from Create, Mekanism, Ice and 1.21.1 / NeoForge. It adds 77 materials drawn from Create, Mekanism, Ice and
Fire, Iron's Spells 'n Spellbooks, Create: The Air War, Create: Cosmonautics and Fire, Immersive Engineering, Iron's Spells 'n Spellbooks, Create: The Air War,
Aeronautics: Interstellar Expansion, so those mods' metals, gems, scales and Create: Cosmonautics and Aeronautics: Interstellar Expansion, so those mods'
essences can be used to build modular tools and armour. metals, gems, scales and essences can be used to build modular tools and armour.
Every source mod is optional. Install any subset - the materials for mods you do Every source mod is optional. Install any subset - the materials for mods you do
not have are never registered. not have are never registered.
@ -22,11 +22,13 @@ 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 mod's materials are a separate built-in datapack, registered in
`CMModular.java` only when that mod is present. `CMModular.java` only when that mod is present.
Titanium is the exception: Air War and Cosmonautics both add a titanium ingot, **Overlapping metals.** Several metals come from more than one mod - steel,
so one material covers both and draws its ingredients from `c:ingots/titanium` lead and uranium from Mekanism and Immersive Engineering, silver from Ice and
rather than an item id. Cosmonautics already declares that tag; the tag files in Fire and Immersive Engineering, titanium from both aerospace addons. Each one
`data/c/` fill it in for Air War, which ships no common tags, using optional picks a single provider rather than accepting either, so the crafting screen
entries so a missing mod is not an error. shows one entry per metal instead of two identical-looking ones. Providers are
Mekanism for steel/lead/uranium, Ice and Fire for silver, Cosmonautics for
titanium; swapping one is a one-line change in `tools/materials.py`.
**Fire immunity.** Cinder essence grants immunity to fire, which is not an **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 attribute the game has and not a property MIAPI offers. Instead of coupling to
@ -115,3 +117,9 @@ Requires `pillow` and `numpy`.
- Interstellar Expansion is mostly machinery. Only three of its items are a - 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 material rather than a component; solid E-710 is left out because it is rocket
fuel. 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.

View File

@ -13,8 +13,8 @@ import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.event.AddPackFindersEvent; import net.neoforged.neoforge.event.AddPackFindersEvent;
/** /**
* Adds the materials of Create, Mekanism, Ice and Fire, Iron's Spells and the * Adds the materials of Create, Mekanism, Ice and Fire, Immersive Engineering,
* Create aerospace addons to Truly Modular. * Iron's Spells and the Create aerospace addons to Truly Modular.
* *
* <p>Almost all of this mod is data. The one thing data cannot do is load * <p>Almost all of this mod is data. The one thing data cannot do is load
* conditionally: MIAPI reads materials with its own loader, which honours * conditionally: MIAPI reads materials with its own loader, which honours
@ -22,28 +22,21 @@ import net.neoforged.neoforge.event.AddPackFindersEvent;
* file off, and a material whose ingredient item does not exist fails to parse * file off, and a material whose ingredient item does not exist fails to parse
* outright. So each source mod's materials live in their own built-in datapack * outright. So each source mod's materials live in their own built-in datapack
* and only get registered when that mod is actually installed. * and only get registered when that mod is actually installed.
*
* <p>Where two mods add the same metal - steel, lead and uranium are in both
* Mekanism and Immersive Engineering, silver in both Ice and Fire and Immersive
* Engineering, titanium in both aerospace addons - one of them is simply picked
* as the provider rather than the material accepting either. That keeps one
* entry per metal in the crafting screen.
*/ */
@Mod(CMModular.MOD_ID) @Mod(CMModular.MOD_ID)
public class CMModular { public class CMModular {
public static final String MOD_ID = "cmmodular"; public static final String MOD_ID = "cmmodular";
/** /** A set of materials, and the mod it needs in order to load. */
* A set of materials, and the mods it needs in order to load. private record MaterialPack(String id, String name, String requires) {
*
* @param anyOf when true one of {@code requires} is enough, rather than all
* of them. Such a pack cannot name items directly - it has no
* way to know which mod turned up - so it sources ingredients
* from item tags instead.
*/
private record MaterialPack(String id, String name, List<String> requires, boolean anyOf) {
MaterialPack(String id, String name, String... requires) {
this(id, name, List.of(requires), false);
}
boolean present() { boolean present() {
return anyOf return ModList.get().isLoaded(requires);
? requires.stream().anyMatch(ModList.get()::isLoaded)
: requires.stream().allMatch(ModList.get()::isLoaded);
} }
} }
@ -51,14 +44,12 @@ public class CMModular {
new MaterialPack("mekanism", "Mekanism Materials", "mekanism"), new MaterialPack("mekanism", "Mekanism Materials", "mekanism"),
new MaterialPack("create", "Create Materials", "create"), new MaterialPack("create", "Create Materials", "create"),
new MaterialPack("iceandfire", "Ice and Fire Materials", "iceandfire"), new MaterialPack("iceandfire", "Ice and Fire Materials", "iceandfire"),
new MaterialPack("immersiveengineering", "Immersive Engineering Materials",
"immersiveengineering"),
new MaterialPack("irons_spellbooks", "Iron's Spells Materials", "irons_spellbooks"), new MaterialPack("irons_spellbooks", "Iron's Spells Materials", "irons_spellbooks"),
new MaterialPack("interstellar", "Interstellar Expansion Materials", "vsie"), new MaterialPack("interstellar", "Interstellar Expansion Materials", "vsie"),
new MaterialPack("air_war", "Create: The Air War Materials", "create_the_air_wars"), new MaterialPack("air_war", "Create: The Air War Materials", "create_the_air_wars"),
new MaterialPack("cosmonautics", "Create: Cosmonautics Materials", "rocketnautics"), new MaterialPack("cosmonautics", "Create: Cosmonautics Materials", "rocketnautics"));
// Both aerospace addons add a titanium ingot. One material covers
// them both, fed by the c:ingots/titanium tag.
new MaterialPack("aerospace", "Aerospace Materials",
List.of("create_the_air_wars", "rocketnautics"), true));
public CMModular(IEventBus modBus) { public CMModular(IEventBus modBus) {
FireImmunity.register(modBus); FireImmunity.register(modBus);

View File

@ -71,6 +71,13 @@ versionRange = "[0,)"
ordering = "AFTER" ordering = "AFTER"
side = "BOTH" side = "BOTH"
[[dependencies.${mod_id}]]
modId = "immersiveengineering"
type = "optional"
versionRange = "[0,)"
ordering = "AFTER"
side = "BOTH"
[[dependencies.${mod_id}]] [[dependencies.${mod_id}]]
modId = "vsie" modId = "vsie"
type = "optional" type = "optional"

View File

@ -1,9 +0,0 @@
{
"replace": false,
"values": [
{
"id": "create_the_air_wars:titaniumingot",
"required": false
}
]
}

View File

@ -1,9 +0,0 @@
{
"replace": false,
"values": [
{
"id": "create_the_air_wars:titaniumnugget",
"required": false
}
]
}

View File

@ -1,9 +0,0 @@
{
"replace": false,
"values": [
{
"id": "create_the_air_wars:titaniumsheet",
"required": false
}
]
}

View File

@ -1,6 +0,0 @@
{
"pack": {
"description": "Aerospace Materials for Truly Modular",
"pack_format": 48
}
}

View File

@ -1,7 +1,9 @@
{ {
"translation": "Sulfur ", "translation": "Sulfur ",
"groups": [ "groups": [
"crystal" "crystal",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -52,16 +52,20 @@
}, },
"items": [ "items": [
{ {
"tag": "c:ingots/titanium", "item": "rocketnautics:titanium_ingot",
"value": 1.0 "value": 1.0
}, },
{ {
"tag": "c:nuggets/titanium", "item": "rocketnautics:titanium_nugget",
"value": 0.11112 "value": 0.11112
}, },
{ {
"tag": "c:plates/titanium", "item": "rocketnautics:titanium_sheet",
"value": 1.0 "value": 1.0
},
{
"item": "rocketnautics:titanium_block",
"value": 9.0
} }
] ]
} }

View File

@ -2,7 +2,9 @@
"translation": "Dread ", "translation": "Dread ",
"groups": [ "groups": [
"crystal", "crystal",
"metal" "metal",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -1,7 +1,9 @@
{ {
"translation": "Pixie Dust ", "translation": "Pixie Dust ",
"groups": [ "groups": [
"crystal" "crystal",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -2,7 +2,9 @@
"translation": "Sapphire ", "translation": "Sapphire ",
"groups": [ "groups": [
"crystal", "crystal",
"gem" "gem",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -0,0 +1,64 @@
{
"translation": "Aluminium ",
"groups": [
"metal"
],
"icon": {
"type": "item",
"item": "immersiveengineering:ingot_aluminum"
},
"hardness": 4.0,
"density": 1.2,
"flexibility": 3,
"durability": 260,
"enchantability": 17,
"toughness": 0,
"tier": 3,
"mining_speed": 6,
"mining_level": "minecraft:incorrect_for_iron_tool",
"armor_durability": 14,
"color": "B9C0C3",
"color_palette": {
"type": "grayscale_map",
"colors": {
"24": "3c3d3e",
"68": "737677",
"107": "7a7e7f",
"150": "818587",
"190": "93989a",
"216": "b9c0c3",
"255": "c5cdd0"
},
"filler": "interpolate"
},
"textures": [
"metallic",
"bright"
],
"properties": {
"handheld": {
"attributes": [
{
"attribute": "generic.attack_speed",
"value": "0.1",
"operation": "+",
"slot": "mainhand"
}
]
}
},
"items": [
{
"item": "immersiveengineering:ingot_aluminum",
"value": 1.0
},
{
"item": "immersiveengineering:nugget_aluminum",
"value": 0.11112
},
{
"item": "immersiveengineering:plate_aluminum",
"value": 1.0
}
]
}

View File

@ -0,0 +1,51 @@
{
"translation": "Constantan ",
"groups": [
"metal"
],
"icon": {
"type": "item",
"item": "immersiveengineering:ingot_constantan"
},
"hardness": 5.6,
"density": 4.0,
"flexibility": 2,
"durability": 640,
"enchantability": 14,
"toughness": 2,
"tier": 4,
"mining_speed": 7,
"mining_level": "minecraft:incorrect_for_diamond_tool",
"armor_durability": 24,
"color": "FE8E70",
"color_palette": {
"type": "grayscale_map",
"colors": {
"24": "321c16",
"68": "60352a",
"107": "733e32",
"150": "7f4537",
"190": "aa5c4a",
"216": "fe8e70",
"255": "ff9f7e"
},
"filler": "interpolate"
},
"textures": [
"metallic"
],
"items": [
{
"item": "immersiveengineering:ingot_constantan",
"value": 1.0
},
{
"item": "immersiveengineering:nugget_constantan",
"value": 0.11112
},
{
"item": "immersiveengineering:plate_constantan",
"value": 1.0
}
]
}

View File

@ -0,0 +1,57 @@
{
"translation": "Electrum ",
"groups": [
"metal"
],
"icon": {
"type": "item",
"item": "immersiveengineering:ingot_electrum"
},
"hardness": 4.4,
"density": 3.6,
"flexibility": 2,
"durability": 260,
"enchantability": 26,
"toughness": 0,
"tier": 3,
"mining_speed": 9,
"mining_level": "minecraft:incorrect_for_iron_tool",
"armor_durability": 14,
"color": "FFBC4C",
"color_palette": {
"type": "grayscale_map",
"colors": {
"24": "362a17",
"68": "67512c",
"107": "785c30",
"150": "7d5f31",
"190": "b0833b",
"216": "ffbc4c",
"255": "ffd051"
},
"filler": "interpolate"
},
"textures": [
"metallic",
"bright"
],
"properties": {
"tool": {
"luminious_learning": "0.5"
}
},
"items": [
{
"item": "immersiveengineering:ingot_electrum",
"value": 1.0
},
{
"item": "immersiveengineering:nugget_electrum",
"value": 0.11112
},
{
"item": "immersiveengineering:plate_electrum",
"value": 1.0
}
]
}

View File

@ -0,0 +1,63 @@
{
"translation": "Graphite ",
"groups": [
"metal"
],
"icon": {
"type": "item",
"item": "immersiveengineering:ingot_hop_graphite"
},
"hardness": 4.2,
"density": 2.0,
"flexibility": 1,
"durability": 520,
"enchantability": 12,
"toughness": 2,
"tier": 4,
"mining_speed": 10,
"mining_level": "minecraft:incorrect_for_diamond_tool",
"armor_durability": 18,
"color": "373737",
"color_palette": {
"type": "grayscale_map",
"colors": {
"24": "080808",
"68": "101010",
"107": "121212",
"150": "171717",
"190": "212121",
"216": "373737",
"255": "3f3f3f"
},
"filler": "interpolate"
},
"textures": [
"rough",
"metallic"
],
"properties": {
"default": {
"fire_proof": true
},
"handheld": {
"attributes": [
{
"attribute": "generic.attack_speed",
"value": "0.08",
"operation": "+",
"slot": "mainhand"
}
]
}
},
"items": [
{
"item": "immersiveengineering:ingot_hop_graphite",
"value": 1.0
},
{
"item": "immersiveengineering:plate_hop_graphite",
"value": 1.0
}
]
}

View File

@ -0,0 +1,52 @@
{
"translation": "Nickel ",
"groups": [
"metal"
],
"icon": {
"type": "item",
"item": "immersiveengineering:ingot_nickel"
},
"hardness": 5.4,
"density": 4.4,
"flexibility": 1,
"durability": 480,
"enchantability": 10,
"toughness": 1,
"tier": 3,
"mining_speed": 6,
"mining_level": "minecraft:incorrect_for_iron_tool",
"armor_durability": 20,
"armor_toughness": 0.5,
"color": "C8CEBD",
"color_palette": {
"type": "grayscale_map",
"colors": {
"24": "2b2c28",
"68": "52544d",
"107": "60635a",
"150": "686c62",
"190": "878c80",
"216": "c8cebd",
"255": "dfe5d2"
},
"filler": "interpolate"
},
"textures": [
"metallic"
],
"items": [
{
"item": "immersiveengineering:ingot_nickel",
"value": 1.0
},
{
"item": "immersiveengineering:nugget_nickel",
"value": 0.11112
},
{
"item": "immersiveengineering:plate_nickel",
"value": 1.0
}
]
}

View File

@ -0,0 +1,6 @@
{
"pack": {
"description": "Immersive Engineering Materials for Truly Modular",
"pack_format": 48
}
}

View File

@ -1,7 +1,9 @@
{ {
"translation": "Silicon Carbide ", "translation": "Silicon Carbide ",
"groups": [ "groups": [
"crystal" "crystal",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -2,7 +2,9 @@
"translation": "Arcane ", "translation": "Arcane ",
"groups": [ "groups": [
"crystal", "crystal",
"gem" "gem",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -2,7 +2,9 @@
"translation": "Cinder ", "translation": "Cinder ",
"groups": [ "groups": [
"crystal", "crystal",
"gem" "gem",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -2,7 +2,9 @@
"translation": "Fluorite ", "translation": "Fluorite ",
"groups": [ "groups": [
"crystal", "crystal",
"gem" "gem",
"gem_melee",
"gem_armor"
], ],
"icon": { "icon": {
"type": "item", "type": "item",

View File

@ -18,6 +18,7 @@ rather than shipping a material that silently refuses to load.
import argparse import argparse
import json import json
import re import re
import shutil
import sys import sys
import zipfile import zipfile
@ -185,18 +186,13 @@ def build_material(spec, jars: Jars):
return out return out
def check_items(spec, jars: Jars, any_of_pack: bool): def check_items(spec, jars: Jars):
"""Item ids must exist; tags are allowed to be missing at build time.""" """Every item a material names has to exist in the jar that provides it."""
problems = [] problems = []
for entry in spec["items"]: for entry in spec["items"]:
if "item" not in entry: if "item" not in entry:
continue continue
if any_of_pack: if not jars.has_item(entry["item"]):
problems.append(
f"{spec['name']}: pack '{spec['pack']}' loads when any one of its "
f"mods is present, so it must use tags, not the item id {entry['item']}"
)
elif not jars.has_item(entry["item"]):
problems.append(f"{spec['name']}: no such item {entry['item']}") problems.append(f"{spec['name']}: no such item {entry['item']}")
if not jars.has_item(spec["icon"]): if not jars.has_item(spec["icon"]):
problems.append(f"{spec['name']}: icon item {spec['icon']} does not exist") problems.append(f"{spec['name']}: icon item {spec['icon']} does not exist")
@ -218,14 +214,19 @@ def main():
problems = [] problems = []
for spec in MATERIALS: for spec in MATERIALS:
pack = PACKS[spec["pack"]] problems += check_items(spec, jars)
problems += check_items(spec, jars, pack.get("any_of", False))
if problems: if problems:
print("aborting, item ids do not check out:", file=sys.stderr) print("aborting, item ids do not check out:", file=sys.stderr)
for p in problems: for p in problems:
print(" " + p, file=sys.stderr) print(" " + p, file=sys.stderr)
return 1 return 1
# Wipe first. A material that moves between packs would otherwise leave a
# stale copy behind, and two files claiming the same material id is a silent
# mess to debug.
if args.out.exists():
shutil.rmtree(args.out)
written = 0 written = 0
for pack_id, pack in PACKS.items(): for pack_id, pack in PACKS.items():
root = args.out / pack_id root = args.out / pack_id

View File

@ -39,22 +39,19 @@ NUGGET = 0.11112 # 1/9, matching MIAPI's own nugget values
BLOCK = 9.0 BLOCK = 9.0
# Sub-packs. Each becomes a NeoForge built-in datapack that is only registered # Sub-packs. Each becomes a NeoForge built-in datapack that is only registered
# when its `mods` are present, so a material never references a missing item. # when its mod is present, so a material never references a missing item.
# `any_of` packs load when at least one listed mod is installed; those materials # Where two mods add the same metal, one is picked as the provider and the
# therefore have to source their ingredients from tags, not item ids. # material lives in that mod's pack - see the Immersive Engineering section.
PACKS = { PACKS = {
"mekanism": {"mods": ["mekanism"], "name": "Mekanism Materials"}, "mekanism": {"mod": "mekanism", "name": "Mekanism Materials"},
"create": {"mods": ["create"], "name": "Create Materials"}, "create": {"mod": "create", "name": "Create Materials"},
"iceandfire": {"mods": ["iceandfire"], "name": "Ice and Fire Materials"}, "iceandfire": {"mod": "iceandfire", "name": "Ice and Fire Materials"},
"air_war": {"mods": ["create_the_air_wars"], "name": "Create: The Air War Materials"}, "air_war": {"mod": "create_the_air_wars", "name": "Create: The Air War Materials"},
"cosmonautics": {"mods": ["rocketnautics"], "name": "Create: Cosmonautics Materials"}, "cosmonautics": {"mod": "rocketnautics", "name": "Create: Cosmonautics Materials"},
"irons_spellbooks": {"mods": ["irons_spellbooks"], "name": "Iron's Spells Materials"}, "irons_spellbooks": {"mod": "irons_spellbooks", "name": "Iron's Spells Materials"},
"interstellar": {"mods": ["vsie"], "name": "Interstellar Expansion Materials"}, "interstellar": {"mod": "vsie", "name": "Interstellar Expansion Materials"},
"aerospace": { "immersiveengineering": {"mod": "immersiveengineering",
"mods": ["create_the_air_wars", "rocketnautics"], "name": "Immersive Engineering Materials"},
"any_of": True,
"name": "Aerospace Materials",
},
} }
# Attribute this mod registers itself, so a material can grant something the # Attribute this mod registers itself, so a material can grant something the
@ -110,6 +107,7 @@ def ingots(mod, ingot, nugget=None, block=None, extra=()):
return out return out
def speed(value): def speed(value):
"""Attack speed tweak on held modules: positive is faster.""" """Attack speed tweak on held modules: positive is faster."""
return {"attributes": [{"attribute": "generic.attack_speed", "value": str(value), return {"attributes": [{"attribute": "generic.attack_speed", "value": str(value),
@ -295,10 +293,11 @@ MATERIALS = [
# ----------------------------------------------------------------- Aerospace # ----------------------------------------------------------------- Aerospace
# Both Air War and Cosmonautics add titanium. This is one material fed by # Both Air War and Cosmonautics add titanium. This is one material fed by
# tags so either mod (or both) supplies it; see the c: tag files. # tags so either mod (or both) supplies it; see the c: tag files.
M("titanium", "aerospace", "metal", "Titanium", "rocketnautics:titanium_ingot", M("titanium", "cosmonautics", "metal", "Titanium", "rocketnautics:titanium_ingot",
[{"tag": "c:ingots/titanium", "value": 1.0}, [{"item": "rocketnautics:titanium_ingot", "value": 1.0},
{"tag": "c:nuggets/titanium", "value": NUGGET}, {"item": "rocketnautics:titanium_nugget", "value": NUGGET},
{"tag": "c:plates/titanium", "value": 1.0}], {"item": "rocketnautics:titanium_sheet", "value": 1.0},
{"item": "rocketnautics:titanium_block", "value": BLOCK}],
tier=4, hardness=5.8, density=1.6, flexibility=3, durability=1150, tier=4, hardness=5.8, density=1.6, flexibility=3, durability=1150,
enchantability=11, mining_speed=9, toughness=2, armor_durability=32, enchantability=11, mining_speed=9, toughness=2, armor_durability=32,
armor_toughness=1.5, armor_toughness=1.5,
@ -552,6 +551,60 @@ MATERIALS = [
properties={"handheld": {"attributes": [ properties={"handheld": {"attributes": [
attribute("irons_spellbooks:spell_power", 0.05, "mainhand", "*")]}}), attribute("irons_spellbooks:spell_power", 0.05, "mainhand", "*")]}}),
# ------------------------------------------------- Immersive Engineering
# Steel, lead, uranium and silver are not here - Immersive Engineering adds
# those too, so they live in the tag-fed shared packs above and accept
# either mod's ingots.
M("aluminum", "immersiveengineering", "metal", "Aluminium",
"immersiveengineering:ingot_aluminum",
ingots("immersiveengineering", "ingot_aluminum", "nugget_aluminum",
extra=[{"item": "immersiveengineering:plate_aluminum", "value": 1.0}]),
tier=3, hardness=4.0, density=1.2, flexibility=3, durability=260,
enchantability=17, mining_speed=6, armor_durability=14,
textures=("metallic", "bright"), properties={"handheld": speed(0.10)}),
# Light and corrosion-proof but soft - the cheap way to a fast tool, at
# the cost of everything else.
M("nickel", "immersiveengineering", "metal", "Nickel",
"immersiveengineering:ingot_nickel",
ingots("immersiveengineering", "ingot_nickel", "nugget_nickel",
extra=[{"item": "immersiveengineering:plate_nickel", "value": 1.0}]),
tier=3, hardness=5.4, density=4.4, flexibility=1, durability=480,
enchantability=10, mining_speed=6, toughness=1, armor_durability=20,
armor_toughness=0.5),
# Hard, tough and stubbornly corrosion resistant; dull to enchant.
M("constantan", "immersiveengineering", "metal", "Constantan",
"immersiveengineering:ingot_constantan",
ingots("immersiveengineering", "ingot_constantan", "nugget_constantan",
extra=[{"item": "immersiveengineering:plate_constantan", "value": 1.0}]),
tier=4, hardness=5.6, density=4.0, flexibility=2, durability=640,
enchantability=14, mining_speed=7, toughness=2, armor_durability=24),
# A copper-nickel alloy prized for not changing under heat or strain,
# so it is the steady middle option: no weakness, no standout.
M("electrum", "immersiveengineering", "metal", "Electrum",
"immersiveengineering:ingot_electrum",
ingots("immersiveengineering", "ingot_electrum", "nugget_electrum",
extra=[{"item": "immersiveengineering:plate_electrum", "value": 1.0}]),
tier=3, hardness=4.4, density=3.6, flexibility=2, durability=260,
enchantability=26, mining_speed=9, armor_durability=14,
textures=("metallic", "bright"),
properties={"tool": {"luminious_learning": "0.5"}}),
# Gold-silver alloy, so it inherits gold's bargain: superb to enchant,
# falls apart quickly.
M("hop_graphite", "immersiveengineering", "metal", "Graphite",
"immersiveengineering:ingot_hop_graphite",
ingots("immersiveengineering", "ingot_hop_graphite", None,
extra=[{"item": "immersiveengineering:plate_hop_graphite", "value": 1.0}]),
tier=4, hardness=4.2, density=2.0, flexibility=1, durability=520,
enchantability=12, mining_speed=10, toughness=2, armor_durability=18,
textures=("rough", "metallic"),
properties={"default": {"fire_proof": True}, "handheld": speed(0.08)}),
# Graphite is a dry lubricant and shrugs off heat, hence the quick swing,
# high mining speed and fireproofing - but it is soft, so it hits weakly.
# --------------------------------- Aeronautics: Interstellar Expansion (vsie) # --------------------------------- Aeronautics: Interstellar Expansion (vsie)
# Mostly a machinery mod; these are the three items that are a material # Mostly a machinery mod; these are the three items that are a material
# rather than a component. Solid E-710 is deliberately left out - it is # rather than a component. Solid E-710 is deliberately left out - it is
@ -671,6 +724,20 @@ def _scales():
MATERIALS.extend(_scales()) MATERIALS.extend(_scales())
# Gem sockets do not look for the "gem" group. Truly Modular: Arsenal gates its
# melee gem modules on `gem_melee` and Armory gates its armour ones on
# `gem_armor`, so a material tagged only "crystal"/"gem" is rejected by every
# socket - which is exactly what happened to the two essences. Anything gem-like
# gets both, so it fits any gem slot regardless of size or whether the item is
# a weapon or a piece of armour.
GEM_SOCKET_GROUPS = ("gem_melee", "gem_armor")
for _material in MATERIALS:
if {"gem", "crystal"} & set(_material["groups"]):
_material["groups"] = list(_material["groups"]) + [
g for g in GEM_SOCKET_GROUPS if g not in _material["groups"]]
# Extensions add data to a material that already exists rather than replacing it. # Extensions add data to a material that already exists rather than replacing it.
# Ice and Fire's amethyst is the same stone MIAPI already ships, so it just # Ice and Fire's amethyst is the same stone MIAPI already ships, so it just
# becomes another way to craft it. # becomes another way to craft it.