diff --git a/README.md b/README.md index d511ea4..d807ac0 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ # Create/Mekanism Modular A [Truly Modular](https://modrinth.com/mod/modular-item-api) addon for Minecraft -1.21.1 / NeoForge. It adds 124 materials drawn from 20 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 three -Create aerospace addons - so their metals, gems, scales and essences can all be +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. @@ -52,6 +56,51 @@ 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 @@ -98,6 +147,28 @@ 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 @@ -116,12 +187,24 @@ textures. The generated JSON is committed, so a plain build does not need this. python3 tools/generate_materials.py --jars ~/path/to/mod/jars ``` -The folder needs one jar per source mod. 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. +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 @@ -161,3 +244,36 @@ Requires `pillow` and `numpy`. 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. diff --git a/gradle.properties b/gradle.properties index d4a0f0f..12c446b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ neo_version=21.1.248 mod_id=cmmodular mod_name=Create/Mekanism Modular -mod_version=1.1.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. +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. diff --git a/src/main/java/eu/abdelbaki/cmmodular/CMModular.java b/src/main/java/eu/abdelbaki/cmmodular/CMModular.java index 1690263..4691cbe 100644 --- a/src/main/java/eu/abdelbaki/cmmodular/CMModular.java +++ b/src/main/java/eu/abdelbaki/cmmodular/CMModular.java @@ -33,7 +33,7 @@ import net.neoforged.neoforge.event.AddPackFindersEvent; public class CMModular { public static final String MOD_ID = "cmmodular"; - /** A set of materials, and the mod it needs in order to load. */ + /** A built-in datapack - materials or modules - and the mod it needs. */ private record MaterialPack(String id, String name, String requires) { boolean present() { return ModList.get().isLoaded(requires); @@ -61,10 +61,18 @@ public class CMModular { new MaterialPack("rftoolsbase", "RFTools Materials", "rftoolsbase"), new MaterialPack("createdeco", "Create Deco Materials", "createdeco"), new MaterialPack("crystal_chronicles", "Crystal Chronicles Materials", "crystal_chronicles"), - new MaterialPack("pastel", "Pastel Materials", "pastel")); + new MaterialPack("pastel", "Pastel Materials", "pastel"), + new MaterialPack("createpropulsion", "Create Propulsion Materials", "createpropulsion"), + new MaterialPack("aeroengineering", "AeroEngine Materials", "aeroengineering"), + new MaterialPack("create_wizardry", "Create Wizardry Materials", "create_wizardry"), + // 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")); public CMModular(IEventBus modBus) { FireImmunity.register(modBus); + VacuumSeal.register(modBus); modBus.addListener(this::addMaterialPacks); } diff --git a/src/main/java/eu/abdelbaki/cmmodular/VacuumSeal.java b/src/main/java/eu/abdelbaki/cmmodular/VacuumSeal.java new file mode 100644 index 0000000..a761c7b --- /dev/null +++ b/src/main/java/eu/abdelbaki/cmmodular/VacuumSeal.java @@ -0,0 +1,64 @@ +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; +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; + +/** + * The space set: a sealed helmet and a life support pack that together let the + * wearer breathe where there is nothing to breathe. + * + *

Create: Cosmonautics suffocates you in space by answering + * {@link LivingBreatheEvent} with "no", the same event vanilla uses underwater, + * and Create decides who is exempt by asking whether the helmet + * is a {@code DivingHelmetItem}. A modular helmet never can be, so + * neither mod's own answer can be reused - hence an attribute of this mod's own + * and a listener for it, exactly as {@link FireImmunity} does for fire. + * + *

Half a seal per piece is the point: one module is never enough, so a + * player wanting to breathe in a vacuum has to build both halves of the suit, + * which is the trade Cosmonautics itself makes with its helmet and jetpack. + */ +public final class VacuumSeal { + private static final DeferredRegister ATTRIBUTES = + DeferredRegister.create(Registries.ATTRIBUTE, CMModular.MOD_ID); + + /** At or above 1 the wearer always has air; each piece of the set is 0.5. */ + public static final DeferredHolder VACUUM_SEAL = + ATTRIBUTES.register("vacuum_seal", id -> new RangedAttribute( + "attribute.cmmodular.vacuum_seal", 0.0D, 0.0D, 1.0D).setSyncable(true)); + + private VacuumSeal() { + } + + static void register(IEventBus modBus) { + ATTRIBUTES.register(modBus); + modBus.addListener(VacuumSeal::attachToEntities); + // Last, so it answers after whatever said there was no air - Cosmonautics + // in space, vanilla underwater. + NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, VacuumSeal::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, VACUUM_SEAL)); + } + + private static void onBreathe(LivingBreatheEvent event) { + if (event.canBreathe()) { + return; + } + AttributeInstance instance = event.getEntity().getAttribute(VACUUM_SEAL); + if (instance != null && instance.getValue() >= 1.0D) { + event.setCanBreathe(true); + } + } +} diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 0dce34b..c3c5547 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -182,3 +182,41 @@ type = "optional" versionRange = "[0,)" ordering = "AFTER" side = "BOTH" + +[[dependencies.${mod_id}]] +modId = "createpropulsion" +type = "optional" +versionRange = "[0,)" +ordering = "AFTER" +side = "BOTH" + +[[dependencies.${mod_id}]] +modId = "aeroengineering" +type = "optional" +versionRange = "[0,)" +ordering = "AFTER" +side = "BOTH" + +[[dependencies.${mod_id}]] +modId = "create_wizardry" +type = "optional" +versionRange = "[0,)" +ordering = "AFTER" +side = "BOTH" + +# The two Truly Modular addons that own the slots this mod's modules go in, and +# the modules they inherit from. Optional like everything else - without them +# the module packs are simply never registered. +[[dependencies.${mod_id}]] +modId = "tm_arsenal" +type = "optional" +versionRange = "[0,)" +ordering = "AFTER" +side = "BOTH" + +[[dependencies.${mod_id}]] +modId = "tm_armory" +type = "optional" +versionRange = "[0,)" +ordering = "AFTER" +side = "BOTH" diff --git a/src/main/resources/assets/cmmodular/lang/en_us.json b/src/main/resources/assets/cmmodular/lang/en_us.json index 9b9e703..6f136e7 100644 --- a/src/main/resources/assets/cmmodular/lang/en_us.json +++ b/src/main/resources/assets/cmmodular/lang/en_us.json @@ -1,3 +1,13 @@ { - "attribute.cmmodular.fire_immunity": "Fire Immunity" + "attribute.cmmodular.fire_immunity": "Fire Immunity", + "attribute.cmmodular.vacuum_seal": "Vacuum Seal", + + "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" } diff --git a/src/main/resources/assets/cmmodular/models/item/armor/gui/space/helmet/default.json b/src/main/resources/assets/cmmodular/models/item/armor/gui/space/helmet/default.json new file mode 100644 index 0000000..af95c95 --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/armor/gui/space/helmet/default.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "cmmodular:items/space_helmet" + } +} diff --git a/src/main/resources/assets/cmmodular/models/item/armor/gui/space/helmet_socket/default.json b/src/main/resources/assets/cmmodular/models/item/armor/gui/space/helmet_socket/default.json new file mode 100644 index 0000000..3d96c95 --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/armor/gui/space/helmet_socket/default.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "cmmodular:items/space_helmet_socket" + } +} diff --git a/src/main/resources/assets/cmmodular/models/item/armor/model/chest_back/space/default.json b/src/main/resources/assets/cmmodular/models/item/armor/model/chest_back/space/default.json new file mode 100644 index 0000000..3907ee4 --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/armor/model/chest_back/space/default.json @@ -0,0 +1,31 @@ +{ + "comment": "Life support pack, worn. Twin tanks sitting on the back plate; the same 32x32 cube net as the helmet, so both are edited the same way.", + "texture_size": [32, 32], + "textures": { + "1": "cmmodular:equipment/life_support_pack", + "particle": "cmmodular:equipment/life_support_pack" + }, + "elements": [ + { + "name": "tanks", + "from": [-3.5, -6.5, 2], + "to": [3.5, 1.5, 5], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#1"}, + "east": {"uv": [16, 8, 24, 16], "texture": "#1"}, + "south": {"uv": [24, 8, 32, 16], "texture": "#1"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#1"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#1"}, + "down": {"uv": [16, 0, 24, 8], "texture": "#1"} + } + } + ], + "groups": [ + { + "name": "Life Support Pack", + "origin": [0, 0, 0], + "color": 0, + "children": [0] + } + ] +} diff --git a/src/main/resources/assets/cmmodular/models/item/armor/model/chest_back/space_socket/default.json b/src/main/resources/assets/cmmodular/models/item/armor/model/chest_back/space_socket/default.json new file mode 100644 index 0000000..831866c --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/armor/model/chest_back/space_socket/default.json @@ -0,0 +1,96 @@ +{ + "comment": "Life support pack with a gem setting between the tanks, worn. Same box as the plain pack, socket stamped into the back face of the net.", + "texture_size": [ + 32, + 32 + ], + "textures": { + "1": "cmmodular:equipment/life_support_pack_socket", + "particle": "cmmodular:equipment/life_support_pack_socket" + }, + "elements": [ + { + "name": "tanks", + "from": [ + -3.5, + -6.5, + 2 + ], + "to": [ + 3.5, + 1.5, + 5 + ], + "faces": { + "north": { + "uv": [ + 8, + 8, + 16, + 16 + ], + "texture": "#1" + }, + "east": { + "uv": [ + 16, + 8, + 24, + 16 + ], + "texture": "#1" + }, + "south": { + "uv": [ + 24, + 8, + 32, + 16 + ], + "texture": "#1" + }, + "west": { + "uv": [ + 0, + 8, + 8, + 16 + ], + "texture": "#1" + }, + "up": { + "uv": [ + 8, + 0, + 16, + 8 + ], + "texture": "#1" + }, + "down": { + "uv": [ + 16, + 0, + 24, + 8 + ], + "texture": "#1" + } + } + } + ], + "groups": [ + { + "name": "Life Support Pack, socketed", + "origin": [ + 0, + 0, + 0 + ], + "color": 0, + "children": [ + 0 + ] + } + ] +} diff --git a/src/main/resources/assets/cmmodular/models/item/armor/model/helmet/space/default.json b/src/main/resources/assets/cmmodular/models/item/armor/model/helmet/space/default.json new file mode 100644 index 0000000..af2b71e --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/armor/model/helmet/space/default.json @@ -0,0 +1,31 @@ +{ + "comment": "Sealed helmet, worn. One cube around the head, textured from a 32x32 cube net so the faces can be edited without touching a vanilla armour atlas.", + "texture_size": [32, 32], + "textures": { + "1": "cmmodular:equipment/space_helmet", + "particle": "cmmodular:equipment/space_helmet" + }, + "elements": [ + { + "name": "dome", + "from": [-4.25, -4.25, -4.25], + "to": [4.25, 4.25, 4.25], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#1"}, + "east": {"uv": [16, 8, 24, 16], "texture": "#1"}, + "south": {"uv": [24, 8, 32, 16], "texture": "#1"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#1"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#1"}, + "down": {"uv": [16, 0, 24, 8], "texture": "#1"} + } + } + ], + "groups": [ + { + "name": "Space Helmet", + "origin": [0, 0, 0], + "color": 0, + "children": [0] + } + ] +} diff --git a/src/main/resources/assets/cmmodular/models/item/armor/model/helmet/space_socket/default.json b/src/main/resources/assets/cmmodular/models/item/armor/model/helmet/space_socket/default.json new file mode 100644 index 0000000..7728666 --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/armor/model/helmet/space_socket/default.json @@ -0,0 +1,96 @@ +{ + "comment": "Sealed helmet with a gem setting on the crown, worn. Same cube as the plain helmet, socket stamped into the top face of the net.", + "texture_size": [ + 32, + 32 + ], + "textures": { + "1": "cmmodular:equipment/space_helmet_socket", + "particle": "cmmodular:equipment/space_helmet_socket" + }, + "elements": [ + { + "name": "dome", + "from": [ + -4.25, + -4.25, + -4.25 + ], + "to": [ + 4.25, + 4.25, + 4.25 + ], + "faces": { + "north": { + "uv": [ + 8, + 8, + 16, + 16 + ], + "texture": "#1" + }, + "east": { + "uv": [ + 16, + 8, + 24, + 16 + ], + "texture": "#1" + }, + "south": { + "uv": [ + 24, + 8, + 32, + 16 + ], + "texture": "#1" + }, + "west": { + "uv": [ + 0, + 8, + 8, + 16 + ], + "texture": "#1" + }, + "up": { + "uv": [ + 8, + 0, + 16, + 8 + ], + "texture": "#1" + }, + "down": { + "uv": [ + 16, + 0, + 24, + 8 + ], + "texture": "#1" + } + } + } + ], + "groups": [ + { + "name": "Space Helmet, socketed", + "origin": [ + 0, + 0, + 0 + ], + "color": 0, + "children": [ + 0 + ] + } + ] +} diff --git a/src/main/resources/assets/cmmodular/models/item/sword/blade/khopesh/default.json b/src/main/resources/assets/cmmodular/models/item/sword/blade/khopesh/default.json new file mode 100644 index 0000000..f3f536e --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/sword/blade/khopesh/default.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "cmmodular:items/khopesh_blade" + } +} diff --git a/src/main/resources/assets/cmmodular/models/item/sword/pommel/gem_socket/default.json b/src/main/resources/assets/cmmodular/models/item/sword/pommel/gem_socket/default.json new file mode 100644 index 0000000..2e568d5 --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/sword/pommel/gem_socket/default.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "cmmodular:items/pommel_gem_socket" + } +} diff --git a/src/main/resources/assets/cmmodular/models/item/sword/pommel/great_socket/default.json b/src/main/resources/assets/cmmodular/models/item/sword/pommel/great_socket/default.json new file mode 100644 index 0000000..590529c --- /dev/null +++ b/src/main/resources/assets/cmmodular/models/item/sword/pommel/great_socket/default.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "cmmodular:items/pommel_great_socket" + } +} diff --git a/src/main/resources/assets/cmmodular/textures/equipment/life_support_pack.png b/src/main/resources/assets/cmmodular/textures/equipment/life_support_pack.png new file mode 100644 index 0000000..ae382e6 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/equipment/life_support_pack.png differ diff --git a/src/main/resources/assets/cmmodular/textures/equipment/life_support_pack_socket.png b/src/main/resources/assets/cmmodular/textures/equipment/life_support_pack_socket.png new file mode 100644 index 0000000..a4f722c Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/equipment/life_support_pack_socket.png differ diff --git a/src/main/resources/assets/cmmodular/textures/equipment/space_helmet.png b/src/main/resources/assets/cmmodular/textures/equipment/space_helmet.png new file mode 100644 index 0000000..65b74f6 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/equipment/space_helmet.png differ diff --git a/src/main/resources/assets/cmmodular/textures/equipment/space_helmet_socket.png b/src/main/resources/assets/cmmodular/textures/equipment/space_helmet_socket.png new file mode 100644 index 0000000..08b6d56 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/equipment/space_helmet_socket.png differ diff --git a/src/main/resources/assets/cmmodular/textures/equipment/templates/armor_layer.png b/src/main/resources/assets/cmmodular/textures/equipment/templates/armor_layer.png new file mode 100644 index 0000000..3a8f00d Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/equipment/templates/armor_layer.png differ diff --git a/src/main/resources/assets/cmmodular/textures/equipment/templates/cube_net.png b/src/main/resources/assets/cmmodular/textures/equipment/templates/cube_net.png new file mode 100644 index 0000000..9b794fb Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/equipment/templates/cube_net.png differ diff --git a/src/main/resources/assets/cmmodular/textures/items/khopesh_blade.png b/src/main/resources/assets/cmmodular/textures/items/khopesh_blade.png new file mode 100644 index 0000000..6c764ba Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/items/khopesh_blade.png differ diff --git a/src/main/resources/assets/cmmodular/textures/items/pommel_gem_socket.png b/src/main/resources/assets/cmmodular/textures/items/pommel_gem_socket.png new file mode 100644 index 0000000..3cb3451 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/items/pommel_gem_socket.png differ diff --git a/src/main/resources/assets/cmmodular/textures/items/pommel_great_socket.png b/src/main/resources/assets/cmmodular/textures/items/pommel_great_socket.png new file mode 100644 index 0000000..24c31de Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/items/pommel_great_socket.png differ diff --git a/src/main/resources/assets/cmmodular/textures/items/space_helmet.png b/src/main/resources/assets/cmmodular/textures/items/space_helmet.png new file mode 100644 index 0000000..588cdbb Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/items/space_helmet.png differ diff --git a/src/main/resources/assets/cmmodular/textures/items/space_helmet_socket.png b/src/main/resources/assets/cmmodular/textures/items/space_helmet_socket.png new file mode 100644 index 0000000..fed04a4 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/items/space_helmet_socket.png differ diff --git a/src/main/resources/assets/cmmodular/textures/items/templates/icon.png b/src/main/resources/assets/cmmodular/textures/items/templates/icon.png new file mode 100644 index 0000000..8048a96 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/items/templates/icon.png differ diff --git a/src/main/resources/assets/cmmodular/textures/templates_guide.png b/src/main/resources/assets/cmmodular/textures/templates_guide.png new file mode 100644 index 0000000..1b01717 Binary files /dev/null and b/src/main/resources/assets/cmmodular/textures/templates_guide.png differ diff --git a/src/main/resources/packs/aeroengineering/data/cmmodular/miapi/materials/metal/aviation_alloy.json b/src/main/resources/packs/aeroengineering/data/cmmodular/miapi/materials/metal/aviation_alloy.json new file mode 100644 index 0000000..eb70e64 --- /dev/null +++ b/src/main/resources/packs/aeroengineering/data/cmmodular/miapi/materials/metal/aviation_alloy.json @@ -0,0 +1,64 @@ +{ + "translation": "Aviation Alloy ", + "groups": [ + "metal" + ], + "icon": { + "type": "item", + "item": "aeroengineering:aviation_alloy_ingot" + }, + "hardness": 6.6, + "density": 2.4, + "flexibility": 4, + "durability": 1650, + "enchantability": 14, + "toughness": 3, + "tier": 5, + "mining_speed": 10, + "mining_level": "minecraft:incorrect_for_netherite_tool", + "armor_durability": 35, + "armor_toughness": 2.0, + "color": "889295", + "color_palette": { + "type": "grayscale_map", + "colors": { + "24": "1f2122", + "68": "3b4041", + "107": "4f575a", + "150": "575e60", + "190": "666f72", + "216": "889295", + "255": "a5aeb0" + }, + "filler": "interpolate" + }, + "textures": [ + "metallic", + "bright" + ], + "properties": { + "default": { + "fire_proof": true + }, + "handheld": { + "attributes": [ + { + "attribute": "generic.attack_speed", + "value": "0.08", + "operation": "+", + "slot": "mainhand" + } + ] + } + }, + "items": [ + { + "item": "aeroengineering:aviation_alloy_ingot", + "value": 1.0 + }, + { + "item": "aeroengineering:aviation_alloy_sheet", + "value": 1.0 + } + ] +} diff --git a/src/main/resources/packs/aeroengineering/pack.mcmeta b/src/main/resources/packs/aeroengineering/pack.mcmeta new file mode 100644 index 0000000..0179fc6 --- /dev/null +++ b/src/main/resources/packs/aeroengineering/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "AeroEngine Materials for Truly Modular", + "pack_format": 48 + } +} diff --git a/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/helmet.json b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/helmet.json new file mode 100644 index 0000000..bcacefd --- /dev/null +++ b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/helmet.json @@ -0,0 +1,52 @@ +{ + "parent": "tm_armory:armor/default/helmet", + "data": { + "merge": { + "module_tag": [ + "space", + "sealed" + ], + "attributes": [ + { + "attribute": "cmmodular:vacuum_seal", + "value": "0.5", + "operation": "+", + "slot": "head" + } + ] + }, + "replace": { + "model": [ + { + "path": "cmmodular:models/item/armor/model/helmet/space/[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": "head" + } + }, + { + "path": "cmmodular:models/item/armor/gui/space/helmet/[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": "item" + } + } + ], + "priority": 4, + "display_name": "miapi.module.cmmodular.armor.space.helmet.name", + "allowed_material": { + "allowedMaterials": [ + "metal", + "crystal", + "glass" + ], + "cost": 6 + } + } + } +} diff --git a/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/helmet_socket.json b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/helmet_socket.json new file mode 100644 index 0000000..a792f89 --- /dev/null +++ b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/helmet_socket.json @@ -0,0 +1,68 @@ +{ + "parent": "cmmodular:armor/space/helmet", + "data": { + "merge": { + "module_tag": [ + "socket_armor" + ], + "attributes": [ + { + "attribute": "minecraft:generic.armor", + "value": "-0.05", + "operation": "*", + "slot": "head" + } + ] + }, + "replace": { + "model": [ + { + "path": "cmmodular:models/item/armor/model/helmet/space_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": "head" + } + }, + { + "path": "cmmodular:models/item/armor/gui/space/helmet_socket/[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": "item" + } + } + ], + "slots": { + "hat": { + "allowed": [ + "hat", + "sub_hat" + ], + "translationKey": "miapi.slot.hat.name", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": 0, "y": -1, "z": 0}, + "scale": {"x": 1.1, "y": 1.1, "z": 1.1}, + "origin": "head" + } + }, + "gem": { + "allowed": [ + "gem_armor_medium" + ], + "translationKey": "miapi.slot.gemstone.medium", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": 0, "y": 3, "z": 0}, + "scale": {"x": 1, "y": 1, "z": 1} + } + } + }, + "display_name": "miapi.module.cmmodular.armor.space.helmet_socket.name" + } + } +} diff --git a/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/life_support.json b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/life_support.json new file mode 100644 index 0000000..4d0ef65 --- /dev/null +++ b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/life_support.json @@ -0,0 +1,42 @@ +{ + "parent": "tm_armory:armor/default/back_chest", + "data": { + "merge": { + "module_tag": [ + "space", + "sealed" + ], + "attributes": [ + { + "attribute": "cmmodular:vacuum_seal", + "value": "0.5", + "operation": "+", + "slot": "chest" + } + ] + }, + "replace": { + "model": [ + { + "path": "cmmodular:models/item/armor/model/chest_back/space/[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": "body" + } + } + ], + "display_name": "miapi.module.cmmodular.armor.space.life_support.name", + "allowed_material": { + "allowedMaterials": [ + "metal", + "crystal", + "glass" + ], + "cost": 4 + } + } + } +} diff --git a/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/life_support_socket.json b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/life_support_socket.json new file mode 100644 index 0000000..9441183 --- /dev/null +++ b/src/main/resources/packs/armory/data/cmmodular/miapi/modules/armor/space/life_support_socket.json @@ -0,0 +1,61 @@ +{ + "parent": "cmmodular:armor/space/life_support", + "data": { + "merge": { + "module_tag": [ + "socket_armor" + ], + "attributes": [ + { + "attribute": "minecraft:generic.armor", + "value": "-0.05", + "operation": "*", + "slot": "chest" + } + ] + }, + "replace": { + "model": [ + { + "path": "cmmodular:models/item/armor/model/chest_back/space_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": "body" + } + } + ], + "slots": { + "banner-back": { + "allowed": [ + "cosmetic_4x8", + "banner_4x8" + ], + "slotType": "cosmetic", + "translationKey": "miapi.slot.chest_back_center.name", + "transform": { + "rotation": {"x": 180, "y": 0, "z": 180}, + "translation": {"x": 0, "y": 1.7, "z": 2.35}, + "scale": {"x": 1, "y": 0.775, "z": 1.1}, + "origin": "body" + } + }, + "gem": { + "allowed": [ + "gem_armor_medium" + ], + "translationKey": "miapi.slot.gemstone.medium", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": 0, "y": -1, "z": 0}, + "scale": {"x": 1.1, "y": 1.1, "z": 1.1}, + "origin": "body" + } + } + }, + "display_name": "miapi.module.cmmodular.armor.space.life_support_socket.name" + } + } +} diff --git a/src/main/resources/packs/armory/pack.mcmeta b/src/main/resources/packs/armory/pack.mcmeta new file mode 100644 index 0000000..2000eb7 --- /dev/null +++ b/src/main/resources/packs/armory/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Armory Modules for Truly Modular", + "pack_format": 48 + } +} diff --git a/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/blade/khopesh.json b/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/blade/khopesh.json new file mode 100644 index 0000000..d5172e6 --- /dev/null +++ b/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/blade/khopesh.json @@ -0,0 +1,155 @@ +{ + "display_name": "miapi.module.cmmodular.blade.khopesh.name", + "model": [ + { + "path": "cmmodular:models/item/sword/blade/khopesh/[material.texture].json", + "color": "[material.color]", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": -1, "y": -1, "z": -0.001}, + "scale": {"x": 1, "y": 1, "z": 1} + } + } + ], + "is_weapon": "true", + "priority": 3, + "repair_priority": -1, + "allowed_in_slots": [ + "sword_blade" + ], + "module_stats": { + "blade_size": 1 + }, + "allowed_material": { + "allowedMaterials": [ + "wood", + "metal", + "flint", + "bone", + "stone", + "crystal", + "glass" + ], + "cost": 2 + }, + "material_indication": { + "durability": 1, + "hardness": { + "strength": 3, + "info": { + "type": "translatable", + "translate": "tm_arsenal.material_indication.blade.damage", + "color": "dark_gray" + } + }, + "density": { + "strength": 2, + "info": { + "type": "translatable", + "translate": "tm_arsenal.material_indication.blade.damage", + "color": "dark_gray" + } + }, + "mining_speed": { + "strength": 1, + "info": { + "type": "translatable", + "translate": "tm_arsenal.material_indication.default.mining_speed", + "color": "dark_gray" + } + } + }, + "enchantments": { + "allowed": [ + "minecraft:enchantable/sword", + "minecraft:enchantable/fire_aspect", + "minecraft:enchantable/sharp_weapon", + "minecraft:enchantable/weapon", + "minecraft:enchantable/mining", + "minecraft:enchantable/mining_loot" + ] + }, + "mining_level": { + "axe": { + "allowed": [ + "#minecraft:mineable/axe" + ], + "use_material": true, + "speed": "[material.mining_speed] * 0.75" + }, + "sword": { + "allowed": [ + "#minecraft:sword_efficient" + ], + "use_material": false, + "speed": "1.5" + }, + "cobweb": { + "allowed": [ + [ + "minecraft:cobweb" + ] + ], + "use_material": false, + "correct_for_drops": true, + "speed": "15" + } + }, + "ability_context": { + "axe_ability": {} + }, + "attributes": [ + { + "attribute": "generic.attack_damage", + "value": "[material.hardness]*0.9 + [material.density]*0.5", + "operation": "+", + "slot": "mainhand" + }, + { + "attribute": "generic.attack_speed", + "value": "0.15", + "operation": "+", + "slot": "mainhand" + }, + { + "attribute": "generic.attack_knockback", + "value": "1", + "operation": "+", + "slot": "mainhand" + }, + { + "attribute": "miapi:generic.attack_range", + "value": "-0.4", + "operation": "+", + "slot": "mainhand" + }, + { + "attribute": "miapi:generic.shield_break", + "value": "4", + "operation": "+", + "slot": "mainhand" + } + ], + "durability": "[material.durability] * 0.2", + "better_combat_config": "bettercombat:axe", + "item_id": "miapi:modular_sword", + "fake_item_tag": [ + "minecraft:swords", + "c:swords", + "minecraft:axes", + "c:axes", + "c:tools", + "c:tool/axe" + ], + "module_tag": [ + "default", + "handheld", + "blade", + "head", + "blade", + "blade_medium", + "axe", + "polearm_glaive", + "sharp" + ] +} diff --git a/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/pommel/gem_socket.json b/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/pommel/gem_socket.json new file mode 100644 index 0000000..50fb6bf --- /dev/null +++ b/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/pommel/gem_socket.json @@ -0,0 +1,59 @@ +{ + "display_name": "miapi.module.cmmodular.pommel.gem_socket.name", + "model": { + "path": "cmmodular:models/item/sword/pommel/gem_socket/[material.texture].json", + "color": "[material.color]", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": -4, "y": -5, "z": 0.001}, + "scale": {"x": 1, "y": 1, "z": 1} + } + }, + "allowed_in_slots": [ + "sword_pommel" + ], + "slots": { + "socket_medium": { + "allowed": [ + "gem_melee_medium" + ], + "translationKey": "miapi.slot.gem.name", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": -5, "y": -5, "z": 0}, + "scale": {"x": 1, "y": 1, "z": 1} + } + } + }, + "attributes": [ + { + "attribute": "generic.attack_speed", + "value": "-0.06", + "operation": "*", + "slot": "mainhand" + } + ], + "priority": 0, + "repair_priority": 0, + "durability": "[material.durability]* 0.25", + "material_indication": { + "durability": 1 + }, + "allowed_material": { + "allowedMaterials": [ + "wood", + "metal", + "flint", + "bone", + "stone", + "rod", + "crystal", + "glass" + ], + "cost": 2 + }, + "module_tag": [ + "default", + "handheld" + ] +} diff --git a/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/pommel/great_socket.json b/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/pommel/great_socket.json new file mode 100644 index 0000000..b00c411 --- /dev/null +++ b/src/main/resources/packs/arsenal/data/cmmodular/miapi/modules/pommel/great_socket.json @@ -0,0 +1,57 @@ +{ + "display_name": "miapi.module.cmmodular.pommel.great_socket.name", + "model": { + "path": "cmmodular:models/item/sword/pommel/great_socket/[material.texture].json", + "color": "[material.color]", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": -4, "y": -5, "z": 0.001}, + "scale": {"x": 1, "y": 1, "z": 1} + } + }, + "allowed_in_slots": [ + "sword_pommel" + ], + "slots": { + "socket_large": { + "allowed": [ + "gem_melee_large" + ], + "translationKey": "miapi.slot.gem.name", + "transform": { + "rotation": {"x": 0, "y": 0, "z": 0}, + "translation": {"x": -5, "y": -5, "z": 0}, + "scale": {"x": 1, "y": 1, "z": 1} + } + } + }, + "attributes": [ + { + "attribute": "generic.attack_speed", + "value": "-0.12", + "operation": "*", + "slot": "mainhand" + } + ], + "priority": 0, + "repair_priority": 0, + "durability": "[material.durability]* 0.3", + "material_indication": { + "durability": 1 + }, + "allowed_material": { + "allowedMaterials": [ + "metal", + "flint", + "bone", + "stone", + "crystal", + "glass" + ], + "cost": 3 + }, + "module_tag": [ + "default", + "handheld" + ] +} diff --git a/src/main/resources/packs/arsenal/pack.mcmeta b/src/main/resources/packs/arsenal/pack.mcmeta new file mode 100644 index 0000000..71feed1 --- /dev/null +++ b/src/main/resources/packs/arsenal/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Arsenal Modules for Truly Modular", + "pack_format": 48 + } +} diff --git a/src/main/resources/packs/create_wizardry/data/cmmodular/miapi/materials/metal/arcane_metal.json b/src/main/resources/packs/create_wizardry/data/cmmodular/miapi/materials/metal/arcane_metal.json new file mode 100644 index 0000000..c435920 --- /dev/null +++ b/src/main/resources/packs/create_wizardry/data/cmmodular/miapi/materials/metal/arcane_metal.json @@ -0,0 +1,19 @@ +{ + "parent": "cmmodular:metal/arcane_metal", + "data": { + "items": [ + { + "item": "irons_spellbooks:arcane_ingot", + "value": 1.0 + }, + { + "item": "create_wizardry:arcane_sheet", + "value": 1.0 + }, + { + "item": "create_wizardry:arcane_block", + "value": 9.0 + } + ] + } +} diff --git a/src/main/resources/packs/create_wizardry/data/cmmodular/miapi/materials/metal/mithril.json b/src/main/resources/packs/create_wizardry/data/cmmodular/miapi/materials/metal/mithril.json new file mode 100644 index 0000000..5a7ec14 --- /dev/null +++ b/src/main/resources/packs/create_wizardry/data/cmmodular/miapi/materials/metal/mithril.json @@ -0,0 +1,27 @@ +{ + "parent": "cmmodular:metal/mithril", + "data": { + "items": [ + { + "item": "irons_spellbooks:mithril_ingot", + "value": 1.0 + }, + { + "tag": "c:ingots/mithril", + "value": 1.0 + }, + { + "tag": "c:nuggets/mithril", + "value": 0.11112 + }, + { + "tag": "c:storage_blocks/mithril", + "value": 9.0 + }, + { + "item": "create_wizardry:mithril_nugget", + "value": 0.11112 + } + ] + } +} diff --git a/src/main/resources/packs/create_wizardry/pack.mcmeta b/src/main/resources/packs/create_wizardry/pack.mcmeta new file mode 100644 index 0000000..c193cf9 --- /dev/null +++ b/src/main/resources/packs/create_wizardry/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Create Wizardry Materials for Truly Modular", + "pack_format": 48 + } +} diff --git a/src/main/resources/packs/createpropulsion/data/cmmodular/miapi/materials/metal/platinum.json b/src/main/resources/packs/createpropulsion/data/cmmodular/miapi/materials/metal/platinum.json new file mode 100644 index 0000000..32a7d90 --- /dev/null +++ b/src/main/resources/packs/createpropulsion/data/cmmodular/miapi/materials/metal/platinum.json @@ -0,0 +1,84 @@ +{ + "translation": "Platinum ", + "groups": [ + "metal" + ], + "icon": { + "type": "item", + "item": "createpropulsion:platinum_ingot" + }, + "hardness": 5.0, + "density": 5.7, + "flexibility": 3, + "durability": 680, + "enchantability": 24, + "toughness": 2, + "tier": 4, + "mining_speed": 7, + "mining_level": "minecraft:incorrect_for_diamond_tool", + "armor_durability": 24, + "armor_toughness": 1.0, + "color": "ADCCDE", + "color_palette": { + "type": "grayscale_map", + "colors": { + "24": "1b2731", + "68": "334b5e", + "107": "547086", + "150": "6896a4", + "190": "8dabc2", + "216": "adccde", + "255": "dbf5e9" + }, + "filler": "interpolate" + }, + "textures": [ + "metallic", + "shiny" + ], + "properties": { + "default": { + "fire_proof": true + }, + "handheld": { + "attributes": [ + { + "attribute": "generic.attack_speed", + "value": "-0.08", + "operation": "+", + "slot": "mainhand" + } + ] + } + }, + "items": [ + { + "item": "createpropulsion:platinum_ingot", + "value": 1.0 + }, + { + "item": "createpropulsion:platinum_nugget", + "value": 0.11112 + }, + { + "item": "createpropulsion:platinum_sheet", + "value": 1.0 + }, + { + "item": "createpropulsion:platinum_block", + "value": 9.0 + }, + { + "tag": "c:ingots/platinum", + "value": 1.0 + }, + { + "tag": "c:nuggets/platinum", + "value": 0.11112 + }, + { + "tag": "c:storage_blocks/platinum", + "value": 9.0 + } + ] +} diff --git a/src/main/resources/packs/createpropulsion/pack.mcmeta b/src/main/resources/packs/createpropulsion/pack.mcmeta new file mode 100644 index 0000000..330e10f --- /dev/null +++ b/src/main/resources/packs/createpropulsion/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Create Propulsion Materials for Truly Modular", + "pack_format": 48 + } +} diff --git a/src/main/resources/packs/iceandfire/data/cmmodular/miapi/materials/crystal/amethyst.json b/src/main/resources/packs/iceandfire/data/cmmodular/miapi/materials/crystal/amethyst.json index 35fb7d0..798508a 100644 --- a/src/main/resources/packs/iceandfire/data/cmmodular/miapi/materials/crystal/amethyst.json +++ b/src/main/resources/packs/iceandfire/data/cmmodular/miapi/materials/crystal/amethyst.json @@ -1,9 +1,23 @@ { - "key": "miapi:crystal/amethyst", - "items": [ - { - "item": "iceandfire:amethyst_gem", - "value": 1.0 - } - ] + "parent": "miapi:crystal/amethyst", + "data": { + "items": [ + { + "item": "minecraft:amethyst_shard", + "value": 0.25 + }, + { + "item": "minecraft:amethyst_cluster", + "value": 0.25 + }, + { + "item": "minecraft:amethyst_block", + "value": 1.0 + }, + { + "item": "iceandfire:amythest_gem", + "value": 0.25 + } + ] + } } diff --git a/src/main/resources/packs/irons_spellbooks/data/cmmodular/miapi/materials/metal/arcane_metal.json b/src/main/resources/packs/irons_spellbooks/data/cmmodular/miapi/materials/metal/arcane_metal.json index f5726d3..3a08da5 100644 --- a/src/main/resources/packs/irons_spellbooks/data/cmmodular/miapi/materials/metal/arcane_metal.json +++ b/src/main/resources/packs/irons_spellbooks/data/cmmodular/miapi/materials/metal/arcane_metal.json @@ -1,5 +1,5 @@ { - "translation": "Arcane Steel ", + "translation": "Arcane Alloy ", "groups": [ "metal" ], @@ -36,8 +36,44 @@ "metallic" ], "properties": { - "handheld": { + "default": { "attributes": [ + { + "attribute": "irons_spellbooks:cooldown_reduction", + "value": "0.1", + "operation": "*", + "slot": "mainhand" + }, + { + "attribute": "irons_spellbooks:cooldown_reduction", + "value": "0.05", + "operation": "*", + "slot": "head" + }, + { + "attribute": "irons_spellbooks:cooldown_reduction", + "value": "0.04", + "operation": "*", + "slot": "chest" + }, + { + "attribute": "irons_spellbooks:cooldown_reduction", + "value": "0.03", + "operation": "*", + "slot": "legs" + }, + { + "attribute": "irons_spellbooks:cooldown_reduction", + "value": "0.03", + "operation": "*", + "slot": "feet" + }, + { + "attribute": "irons_spellbooks:cooldown_reduction", + "value": "0.03", + "operation": "*", + "slot": "offhand" + }, { "attribute": "irons_spellbooks:spell_power", "value": "0.05", diff --git a/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/paltaeria_gem.json b/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/paltaeria_gem.json index 5679de7..2f8ea08 100644 --- a/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/paltaeria_gem.json +++ b/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/paltaeria_gem.json @@ -43,6 +43,10 @@ { "item": "pastel:paltaeria_gem", "value": 1.0 + }, + { + "item": "pastel:paltaeria_fragments", + "value": 0.125 } ] } diff --git a/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/stratine_gem.json b/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/stratine_gem.json index 0d2d735..353efbd 100644 --- a/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/stratine_gem.json +++ b/src/main/resources/packs/pastel/data/cmmodular/miapi/materials/crystal/stratine_gem.json @@ -43,6 +43,10 @@ { "item": "pastel:stratine_gem", "value": 1.0 + }, + { + "item": "pastel:stratine_fragments", + "value": 0.125 } ] } diff --git a/tools/generate_materials.py b/tools/generate_materials.py index 71a5c0d..211badc 100644 --- a/tools/generate_materials.py +++ b/tools/generate_materials.py @@ -6,19 +6,21 @@ the mod that adds it, so a material always looks like the thing it is made of. python3 tools/generate_materials.py --jars ~/mods -The jar directory needs one jar per source mod (Mekanism, Create, Create: The -Air War, Create: Cosmonautics, Ice and Fire). Generated files are committed, so -this only needs re-running when a material definition or a source texture -changes. +Point --jars at the pack's mods folder. Generated files are committed, so this +only needs re-running when a material definition or a source texture changes. Every item id in materials.py is checked against the jars; a typo fails the run -rather than shipping a material that silently refuses to load. +rather than shipping a material that silently refuses to load. A mod whose jar +is not in the folder is skipped instead, keeping its committed files, so the set +of jars does not have to be complete to regenerate the rest. + +Only the material folders are rewritten. The hand-written module JSON that lives +in the same packs is left alone. """ import argparse import json import re -import shutil import sys import zipfile @@ -31,6 +33,7 @@ sys.path.insert(0, str(Path(__file__).parent)) from materials import EXTENSIONS, MATERIALS, PACKS # noqa: E402 NAMESPACE = "cmmodular" +VANILLA = "minecraft:" PACK_FORMAT = 48 # 1.21.1 # Brightness levels of MIAPI's grayscale templates. Each gets a colour; values @@ -56,14 +59,24 @@ class Jars: def __init__(self, folder: Path): self.zips = [] self.by_namespace = {} + # Addons ship a handful of files under the namespace of the mod they add + # to - Applied Mekanistics into ae2, half the Create addons into create - + # so a namespace goes to whichever jar has the most of it, which is the + # mod that actually owns it rather than whichever sorted first. + counts = {} for path in sorted(folder.glob("*.jar")): zf = zipfile.ZipFile(path) names = zf.namelist() self.zips.append((path.name, zf, set(names))) + here = {} for name in names: m = re.match(r"assets/([^/]+)/", name) if m: - self.by_namespace.setdefault(m.group(1), (path.name, zf, set(names))) + here[m.group(1)] = here.get(m.group(1), 0) + 1 + for namespace, count in here.items(): + if count > counts.get(namespace, 0): + counts[namespace] = count + self.by_namespace[namespace] = (path.name, zf, set(names)) def _for(self, namespace): hit = self.by_namespace.get(namespace) @@ -199,6 +212,38 @@ def check_items(spec, jars: Jars): return problems +def check_extension_items(ext, jars: Jars): + """Same check for extensions - they name items too, and used to escape it.""" + problems = [] + for entry in ext["data"].get("data", {}).get("items", []): + item = entry.get("item", "") + if item.startswith(VANILLA): + continue + if item and not jars.has_item(item): + problems.append(f"{ext['path']}: no such item {item}") + return problems + + +def materials_path(out: Path, spec) -> Path: + """Where a material or extension is written.""" + path = spec["path"] if "path" in spec else f"{spec['group']}/{spec['name']}" + return out / spec["pack"] / "data" / NAMESPACE / "miapi" / "materials" / f"{path}.json" + + +def namespaces_of(spec): + """Every namespace a material or extension needs a jar for. + + Vanilla is not one of them - there is no Minecraft jar in the folder, and + the `minecraft` namespace a mod's resource overrides create is not it. + """ + entries = (spec["items"] if "items" in spec + else spec["data"].get("data", {}).get("items", [])) + names = {entry["item"].split(":", 1)[0] for entry in entries if "item" in entry} + if "palette_from" in spec: + names.add(spec["palette_from"].split(":", 1)[0]) + return names - {VANILLA.rstrip(":")} + + def main(): parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) @@ -212,20 +257,45 @@ def main(): jars = Jars(args.jars) print(f"namespaces found: {', '.join(sorted(jars.by_namespace))}\n") + # A mod whose jar is not in the folder is skipped rather than fatal, and its + # committed file is left alone: the pack this addon follows drops and adds + # mods, and losing every material of a mod you no longer have - or being + # unable to regenerate anything without a complete set of jars - is worse + # than regenerating what you can. Anything the jars *can* be checked against + # is still checked, so a typo in a mod you do have still fails the run. + def present(spec): + return namespaces_of(spec) <= set(jars.by_namespace) + + materials = [spec for spec in MATERIALS if present(spec)] + extensions = [ext for ext in EXTENSIONS if present(ext)] + skipped = [spec for spec in MATERIALS if not present(spec)] + skipped += [ext for ext in EXTENSIONS if not present(ext)] + if skipped: + missing = sorted({ns for spec in skipped for ns in namespaces_of(spec) + if ns not in jars.by_namespace}) + print(f"no jar for {', '.join(missing)} - leaving " + f"{len(skipped)} committed file(s) as they are\n") + problems = [] - for spec in MATERIALS: + for spec in materials: problems += check_items(spec, jars) + for ext in extensions: + problems += check_extension_items(ext, jars) if problems: print("aborting, item ids do not check out:", file=sys.stderr) for p in problems: print(" " + p, file=sys.stderr) 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) + # Clear out the material files 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. Only the generated material folders + # go: the packs also hold hand-written module JSON, and the files of the + # skipped mods above have to survive a run that cannot rebuild them. + keep = {materials_path(args.out, spec) for spec in skipped} + for stale in sorted(args.out.glob(f"*/data/{NAMESPACE}/miapi/materials/**/*.json")): + if stale not in keep: + stale.unlink() written = 0 for pack_id, pack in PACKS.items(): @@ -239,23 +309,21 @@ def main(): root.mkdir(parents=True, exist_ok=True) (root / "pack.mcmeta").write_text(json.dumps(mcmeta, indent=2) + "\n") - for spec in MATERIALS: + for spec in materials: material = build_material(spec, jars) - path = (args.out / spec["pack"] / "data" / NAMESPACE / "miapi" / "materials" - / spec["group"] / f"{spec['name']}.json") + path = materials_path(args.out, spec) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(material, indent=2) + "\n") written += 1 swatch = " ".join(material["color_palette"]["colors"][str(s)] for s in STOPS) - print(f" {spec['pack']:<13} {NAMESPACE}:{spec['group']}/{spec['name']:<28} {swatch}") + print(f" {spec['pack']:<16} {NAMESPACE}:{spec['group']}/{spec['name']:<28} {swatch}") - for ext in EXTENSIONS: - path = (args.out / ext["pack"] / "data" / NAMESPACE / "miapi" / "materials" - / f"{ext['path']}.json") + for ext in extensions: + path = materials_path(args.out, ext) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(ext["data"], indent=2) + "\n") written += 1 - print(f" {ext['pack']:<13} extends {ext['data']['key']}") + print(f" {ext['pack']:<16} extends {ext['data']['parent']}") print(f"\n{written} files written to {args.out}") return 0 diff --git a/tools/materials.py b/tools/materials.py index 9ebdfe2..5c40a64 100644 --- a/tools/materials.py +++ b/tools/materials.py @@ -67,6 +67,17 @@ PACKS = { "crystal_chronicles": {"mod": "crystal_chronicles", "name": "Crystal Chronicles Materials"}, "pastel": {"mod": "pastel", "name": "Pastel Materials"}, + "createpropulsion": {"mod": "createpropulsion", + "name": "Create Propulsion Materials"}, + "aeroengineering": {"mod": "aeroengineering", "name": "AeroEngine Materials"}, + "create_wizardry": {"mod": "create_wizardry", "name": "Create Wizardry Materials"}, + # Modules rather than materials, but the same problem: a module that names + # Arsenal's or Armory's slots - or inherits one of their modules - is only + # useful when that mod is installed. Nothing is generated into these two; + # they hold the hand-written module JSON under packs/arsenal and + # packs/armory, and are listed here so their pack.mcmeta is written. + "arsenal": {"mod": "tm_arsenal", "name": "Arsenal Modules"}, + "armory": {"mod": "tm_armory", "name": "Armory Modules"}, } # Attribute this mod registers itself, so a material can grant something the @@ -341,6 +352,38 @@ MATERIALS = [ enchantability=12, mining_speed=10, toughness=3, armor_durability=36, armor_toughness=2.0, properties={"default": {"fire_proof": True}}), + # Create Propulsion: Simulated's own ore, and the only platinum in the pack. + M("platinum", "createpropulsion", "metal", "Platinum", + "createpropulsion:platinum_ingot", + [{"item": "createpropulsion:platinum_ingot", "value": 1.0}, + {"item": "createpropulsion:platinum_nugget", "value": NUGGET}, + {"item": "createpropulsion:platinum_sheet", "value": 1.0}, + {"item": "createpropulsion:platinum_block", "value": BLOCK}], + tier=4, hardness=5.0, density=5.7, flexibility=3, durability=680, + enchantability=24, mining_speed=7, toughness=2, armor_durability=24, + armor_toughness=1.0, textures=("metallic", "shiny"), + properties={"default": {"fire_proof": True}, + "handheld": speed(-0.08)}), + # Real platinum: nearly as dense as osmium, but soft, so it swings slow + # and hits no harder than steel. What it is actually for is the precious + # metal's bargain - gold-grade enchantability without gold's fragility - + # and it melts at 1768C, hence the heat proofing. + + # -------------------------------------------------------------- AeroEngine + M("aviation_alloy", "aeroengineering", "metal", "Aviation Alloy", + "aeroengineering:aviation_alloy_ingot", + [{"item": "aeroengineering:aviation_alloy_ingot", "value": 1.0}, + {"item": "aeroengineering:aviation_alloy_sheet", "value": 1.0}], + tier=5, hardness=6.6, density=2.4, flexibility=4, durability=1650, + enchantability=14, mining_speed=10, toughness=3, armor_durability=35, + armor_toughness=2.0, textures=("metallic", "bright"), + properties={"default": {"fire_proof": True}, + "handheld": speed(0.08)}), + # Iron superheated with netherite powder: a turbine superalloy. Light and + # heat-proof like titanium, harder and more flexible, worse to enchant - + # it sits between titanium and titanium alloy, which is where its recipe + # sits too. + # ------------------------------------------------------- Create: The Air War M("sulfur", "air_war", "crystal", "Sulfur", "create_the_air_wars:raw_sulfur", [{"item": "create_the_air_wars:raw_sulfur", "value": 1.0}, @@ -589,14 +632,44 @@ MATERIALS = [ properties={"handheld": speed(0.12)}), # Light enough to swing fast and enchant well, in the Tolkien sense. - M("arcane_metal", "irons_spellbooks", "metal", "Arcane Steel", + # "Arcane Alloy" rather than Arcane Steel: Create: Wizardry ships a resource + # pack that renames the ingot, and that is what the item is called in game. + M("arcane_metal", "irons_spellbooks", "metal", "Arcane Alloy", "irons_spellbooks:arcane_ingot", [{"item": "irons_spellbooks:arcane_ingot", "value": 1.0}], tier=5, hardness=6.2, density=2.8, flexibility=3, durability=1700, enchantability=26, mining_speed=9, toughness=3, armor_durability=34, armor_toughness=2.0, - properties={"handheld": {"attributes": [ - attribute("irons_spellbooks:spell_power", 0.05, "mainhand", "*")]}}), + # The one thing arcane alloy is for: spells come back faster. Cooldown + # reduction is the whole reason to build out of it rather than out of + # mithril, so it is deliberately the strongest effect on any metal here. + # + # Under `default`, not `handheld`, so armour plated in it counts too - a + # caster wants the set, not just the sword. Per-slot rather than one + # `armor`-wide modifier, for the same reasons as arcane essence: a helmet + # can be worth more than boots, and a weapon reads as a held bonus + # instead of claiming something "when worn". + # + # Iron's Spells treats the attribute as a factor on a base of 1, hence the + # `*` operation - a flat `+` would be added to a base of zero and do + # nothing - and spends it as `cooldown * (2 - value)`. So +0.10 is 10% off + # the wait, and +1.00 in total would be instant casting. + # + # These stack per module, and that is what sets the numbers. A weapon is + # about four modules, so one built entirely of arcane alloy is 40% - well + # past the 25% of Iron's Spells' own best staff, which is the point. The + # armour values are smaller because a full set is a dozen modules: all of + # it in arcane alloy lands near 45%, so even a player wearing the set and + # holding the staff stays under the 100% the formula bottoms out at. + properties={"default": {"attributes": [ + attribute("irons_spellbooks:cooldown_reduction", 0.10, "mainhand", "*"), + attribute("irons_spellbooks:cooldown_reduction", 0.05, "head", "*"), + attribute("irons_spellbooks:cooldown_reduction", 0.04, "chest", "*"), + attribute("irons_spellbooks:cooldown_reduction", 0.03, "legs", "*"), + attribute("irons_spellbooks:cooldown_reduction", 0.03, "feet", "*"), + attribute("irons_spellbooks:cooldown_reduction", 0.03, "offhand", "*"), + attribute("irons_spellbooks:spell_power", 0.05, "mainhand", "*"), + ]}}), # ------------------------------------------------- Immersive Engineering # Steel, lead, uranium and silver are not here - Immersive Engineering adds @@ -1064,14 +1137,26 @@ _GEM_BAND = { } +# Nuggets, in effect: Pastel's two float-gems break into eight fragments on an +# anvil and nine come back out of a floatblock, so a fragment is an eighth of a +# gem and worth accepting as one. +GEM_FRAGMENTS = { + "paltaeria_gem": ("pastel:paltaeria_fragments", 0.125), + "stratine_gem": ("pastel:stratine_fragments", 0.125), +} + + def _gem_family(pack, namespace, table): out = [] for item, (tier, props) in table.items(): label = item.replace("_shard", "").replace("_crystal", "").replace("_gem", "") + items = [{"item": f"{namespace}:{item}", "value": 1.0}] + if item in GEM_FRAGMENTS: + fragment, value = GEM_FRAGMENTS[item] + items.append({"item": fragment, "value": value}) out.append(M( item, pack, "crystal", label.replace("_", " ").title(), - f"{namespace}:{item}", - [{"item": f"{namespace}:{item}", "value": 1.0}], + f"{namespace}:{item}", items, tier=tier, groups=("crystal", "gem"), textures=("crystal", "bright"), properties={"default": props} if props else None, **_GEM_BAND[tier])) @@ -1100,21 +1185,50 @@ for _material in MATERIALS: 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. -# Ice and Fire's amethyst is the same stone MIAPI already ships, so it just -# becomes another way to craft it. +# Extensions edit a material that already exists instead of adding a new one. +# The file goes in the materials folder like any other and is told apart by its +# shape: `parent` names the material it edits and `data` is merged onto a copy +# of it - the same form MIAPI's own stained glass uses to vary plain glass. +# +# The merge overwrites whole fields rather than appending to them, so an +# extension that adds an ingredient has to repeat the ones it wants to keep. EXTENSIONS = [ + # Ice and Fire's amethyst is the same stone MIAPI already ships, so it just + # becomes another way to craft it. The item id really is spelled + # "amythest_gem" - that typo is in Ice and Fire itself. { "pack": "iceandfire", "path": "crystal/amethyst", "data": { - "key": "miapi:crystal/amethyst", - "items": [{"item": "iceandfire:amethyst_gem", "value": 1.0}], + "parent": "miapi:crystal/amethyst", + "data": { + "items": [ + {"item": "minecraft:amethyst_shard", "value": 0.25}, + {"item": "minecraft:amethyst_cluster", "value": 0.25}, + {"item": "minecraft:amethyst_block", "value": 1.0}, + {"item": "iceandfire:amythest_gem", "value": 0.25}, + ], + }, }, }, ] +# Forms of a material that a *different* mod adds. Create: Wizardry presses +# Iron's Spells' arcane ingots into sheets and stacks them into blocks, and adds +# the mithril nugget the base mod never had, so all of those are the same metal. +# +# They cannot simply be listed on the material: it lives in the Iron's Spells +# pack, and an item id belonging to a mod that is not installed stops the whole +# material from loading. An extension in Create: Wizardry's own pack is only +# read when that mod is there. +EXTRA_ITEM_FORMS = { + "arcane_metal": [("create_wizardry:arcane_sheet", 1.0), + ("create_wizardry:arcane_block", BLOCK)], + "mithril": [("create_wizardry:mithril_nugget", NUGGET)], +} + + # ------------------------------------------------- identical materials, one entry # Several of these metals and gems are added by more than one mod. Every # variant should behave as the same material rather than as a near-duplicate, @@ -1143,7 +1257,7 @@ COMMON_TAGS = { "energetic_alloy": "energetic_alloy", "vibrant_alloy": "vibrant_alloy", "conductive_alloy": "conductive_alloy", "pulsating_alloy": "pulsating_alloy", "redstone_alloy": "redstone_alloy", "soularium": "soularium", - "mithril": "mithril", + "mithril": "mithril", "platinum": "platinum", } COMMON_GEM_TAGS = { @@ -1178,3 +1292,30 @@ def _accept_every_provider(): _accept_every_provider() + + +def _extend_with_extra_forms(pack="create_wizardry"): + """Turn EXTRA_ITEM_FORMS into material extensions. + + Built after the common tags are appended, and out of the material's own item + list, because the merge replaces the ingredient list rather than adding to + it - everything the material already accepted has to be carried over. + """ + by_name = {material["name"]: material for material in MATERIALS} + for name, extras in EXTRA_ITEM_FORMS.items(): + base = by_name[name] + path = f"{base['group']}/{name}" + EXTENSIONS.append({ + "pack": pack, + "path": path, + "data": { + "parent": f"cmmodular:{path}", + "data": { + "items": base["items"] + [{"item": item, "value": value} + for item, value in extras], + }, + }, + }) + + +_extend_with_extra_forms()