├── src ├── main │ ├── resources │ │ ├── logo.png │ │ ├── assets │ │ │ ├── mechano │ │ │ │ ├── models │ │ │ │ │ └── block │ │ │ │ │ │ ├── catenary │ │ │ │ │ │ └── hookup.json │ │ │ │ │ │ ├── tool_forge.json │ │ │ │ │ │ ├── rotor │ │ │ │ │ │ └── big_rotor_dummy │ │ │ │ │ │ │ └── base.json │ │ │ │ │ │ ├── tool_station │ │ │ │ │ │ └── dummy.json │ │ │ │ │ │ ├── test_block │ │ │ │ │ │ └── cube.json │ │ │ │ │ │ ├── diagonal_girder │ │ │ │ │ │ ├── partials │ │ │ │ │ │ │ ├── short_up_flat.json │ │ │ │ │ │ │ ├── long_up_flat.json │ │ │ │ │ │ │ ├── short_down_vert.json │ │ │ │ │ │ │ ├── short_up_vert.json │ │ │ │ │ │ │ ├── short_down_flat.json │ │ │ │ │ │ │ ├── long_down_vert.json │ │ │ │ │ │ │ ├── long_up_vert.json │ │ │ │ │ │ │ └── long_down_flat.json │ │ │ │ │ │ ├── middle.json │ │ │ │ │ │ ├── long_double.json │ │ │ │ │ │ ├── long_end_down.json │ │ │ │ │ │ ├── long_end_up.json │ │ │ │ │ │ ├── short_double.json │ │ │ │ │ │ ├── short_end_down.json │ │ │ │ │ │ ├── short_end_up.json │ │ │ │ │ │ └── item.json │ │ │ │ │ │ ├── generic │ │ │ │ │ │ ├── chevron_cutout.json │ │ │ │ │ │ └── chevron_cutout_2.json │ │ │ │ │ │ ├── voltometer │ │ │ │ │ │ └── needle.json │ │ │ │ │ │ ├── stator │ │ │ │ │ │ └── small_stator │ │ │ │ │ │ │ ├── hitbox │ │ │ │ │ │ │ ├── corner_middle.json │ │ │ │ │ │ │ ├── base_middle.json │ │ │ │ │ │ │ ├── base_end_a.json │ │ │ │ │ │ │ ├── base_end_b.json │ │ │ │ │ │ │ ├── base_single.json │ │ │ │ │ │ │ ├── corner_end_a.json │ │ │ │ │ │ │ └── corner_end_b.json │ │ │ │ │ │ │ ├── base_middle.json │ │ │ │ │ │ │ ├── base_middle_side.json │ │ │ │ │ │ │ └── base_end_a.json │ │ │ │ │ │ └── test_axis │ │ │ │ │ │ ├── base.json │ │ │ │ │ │ ├── cube.json │ │ │ │ │ │ └── base_side.json │ │ │ │ ├── sounds │ │ │ │ │ ├── cable_create.ogg │ │ │ │ │ ├── connector_click_up.ogg │ │ │ │ │ ├── connector_click_deny.ogg │ │ │ │ │ └── connector_click_down.ogg │ │ │ │ ├── textures │ │ │ │ │ ├── block │ │ │ │ │ │ ├── axis.png │ │ │ │ │ │ ├── chev.png │ │ │ │ │ │ ├── debug_red.png │ │ │ │ │ │ ├── stator │ │ │ │ │ │ │ ├── big.png │ │ │ │ │ │ │ └── base.png │ │ │ │ │ │ ├── voltometer.png │ │ │ │ │ │ ├── indrum_block.png │ │ │ │ │ │ ├── rotor │ │ │ │ │ │ │ ├── single.png │ │ │ │ │ │ │ └── single_large.png │ │ │ │ │ │ ├── catenary │ │ │ │ │ │ │ ├── hookup.png │ │ │ │ │ │ │ └── missing.png │ │ │ │ │ │ ├── diagonal_girder.png │ │ │ │ │ │ ├── test_axis │ │ │ │ │ │ │ ├── red.png │ │ │ │ │ │ │ └── tex.png │ │ │ │ │ │ ├── tool_station │ │ │ │ │ │ │ ├── base.png │ │ │ │ │ │ │ ├── forge.png │ │ │ │ │ │ │ ├── forged.png │ │ │ │ │ │ │ ├── heated.png │ │ │ │ │ │ │ └── maximized.png │ │ │ │ │ │ ├── connector │ │ │ │ │ │ │ └── connector.png │ │ │ │ │ │ └── slip_ring_shaft │ │ │ │ │ │ │ └── base.png │ │ │ │ │ └── item │ │ │ │ │ │ ├── spool_empty.png │ │ │ │ │ │ ├── wire_hookup.png │ │ │ │ │ │ ├── indrum_ingot.png │ │ │ │ │ │ ├── spool_hookup.png │ │ │ │ │ │ ├── spool_hookup_empty.png │ │ │ │ │ │ └── spool_hookup_depleted.png │ │ │ │ ├── sounds.json │ │ │ │ └── lang │ │ │ │ │ └── custom │ │ │ │ │ └── ui.json │ │ │ └── minecraft │ │ │ │ └── atlases │ │ │ │ └── blocks.json │ │ ├── pack.mcmeta │ │ ├── data │ │ │ └── mechano │ │ │ │ ├── structure │ │ │ │ └── empty.nbt │ │ │ │ └── hitboxes │ │ │ │ ├── test_axis.json │ │ │ │ └── connector │ │ │ │ └── connector_single.json │ │ ├── META-INF │ │ │ └── accesstransformer.cfg │ │ └── mechano.mixins.json │ ├── java-templates │ │ └── com │ │ │ └── quattage │ │ │ └── mechano │ │ │ └── MechanoBuildParameters.java.peb │ ├── java │ │ └── com │ │ │ └── quattage │ │ │ └── mechano │ │ │ ├── MechanoTags.java │ │ │ ├── MechanoPartials.java │ │ │ ├── foundation │ │ │ ├── numeric │ │ │ │ ├── VectorRotationRepresentable.java │ │ │ │ ├── ClientOnlySupplier.java │ │ │ │ ├── ExtendedCodecs.java │ │ │ │ ├── Duo.java │ │ │ │ └── EsoMath.java │ │ │ ├── block │ │ │ │ ├── hitbox │ │ │ │ │ ├── HitboxRepresentable.java │ │ │ │ │ └── MechanoHitboxes.java │ │ │ │ ├── orientation │ │ │ │ │ ├── OrientationUpdatable.java │ │ │ │ │ ├── RelativeDirection.java │ │ │ │ │ └── Relative.java │ │ │ │ ├── ConnectorHostOverridable.java │ │ │ │ ├── CircleGetter.java │ │ │ │ ├── ShapeGetter.java │ │ │ │ ├── SimpleOrientedBlock.java │ │ │ │ └── VerticallyOrientedBlock.java │ │ │ ├── mixin │ │ │ │ ├── client │ │ │ │ │ ├── accessor │ │ │ │ │ │ ├── PlayerInfoAccessor.java │ │ │ │ │ │ └── RenderBuffersAccessor.java │ │ │ │ │ ├── ItemInHandRendererInvoker.java │ │ │ │ │ └── ItemInHandRendererMixin.java │ │ │ │ └── MechanoMixins.java │ │ │ ├── LeftClickCapturable.java │ │ │ ├── item │ │ │ │ └── MechanoItemProperties.java │ │ │ ├── CreativeTabOverridable.java │ │ │ ├── MechanoRegistrate.java │ │ │ └── tracking │ │ │ │ └── GridIdentifiable.java │ │ │ ├── content │ │ │ ├── spool │ │ │ │ ├── EmptySpoolItem.java │ │ │ │ └── HookupSpoolItem.java │ │ │ └── connector │ │ │ │ ├── ConnectorBlockEntity.java │ │ │ │ ├── SingleConnectorBlockEntity.java │ │ │ │ └── SingleConnectorBlock.java │ │ │ ├── MechanoEntities.java │ │ │ ├── MechanoSounds.java │ │ │ ├── api │ │ │ ├── grid │ │ │ │ ├── topology │ │ │ │ │ ├── CircuitProvider.java │ │ │ │ │ ├── CircuitComponentProvider.java │ │ │ │ │ └── vertex │ │ │ │ │ │ └── WireJack.java │ │ │ │ ├── functional │ │ │ │ │ ├── Passthrough.java │ │ │ │ │ ├── VoltageSource.java │ │ │ │ │ ├── Capacitor.java │ │ │ │ │ └── Resistor.java │ │ │ │ ├── Watt.java │ │ │ │ └── solver │ │ │ │ │ ├── NodeUnionSet.java │ │ │ │ │ └── NodalSolver.java │ │ │ ├── ClientGrid.java │ │ │ ├── transmitter │ │ │ │ ├── TransmitterEntry.java │ │ │ │ └── TransmitterFactory.java │ │ │ ├── blockEntity │ │ │ │ └── renderer │ │ │ │ │ ├── SimpleBlockEntityRenderer.java │ │ │ │ │ └── GriddableBlockEntityRenderer.java │ │ │ ├── switchboard │ │ │ │ ├── task │ │ │ │ │ ├── DummyTask.java │ │ │ │ │ ├── JointUnlinkTask.java │ │ │ │ │ └── GridDumpTask.java │ │ │ │ └── action │ │ │ │ │ └── GridTaskExecuteEvent.java │ │ │ └── catenary │ │ │ │ ├── MeshExtruder.java │ │ │ │ └── EntropyTracker.java │ │ │ ├── infrastructure │ │ │ ├── gametest │ │ │ │ ├── ElectricityTests.java │ │ │ │ └── DisjointSetTests.java │ │ │ ├── command │ │ │ │ ├── GridDumpCommand.java │ │ │ │ └── MechanoCommands.java │ │ │ └── datagen │ │ │ │ ├── SpoolDataProvider.java │ │ │ │ ├── MechanoDataGen.java │ │ │ │ ├── SimpleDataProvider.java │ │ │ │ └── DynamicStateGenerator.java │ │ │ ├── MechanoClient.java │ │ │ ├── MechanoBlockEntities.java │ │ │ ├── MechanoTransmitters.java │ │ │ ├── MechanoItems.java │ │ │ ├── MechanoBlocks.java │ │ │ ├── MechanoData.java │ │ │ ├── MechanoPackets.java │ │ │ └── MechanoGroups.java │ └── templates │ │ └── META-INF │ │ └── neoforge.mods.toml └── generated │ └── resources │ ├── .cache │ ├── 82b4a4fdfa8e298fd72741ab142103c4f0fa5f56 │ └── cd7babe69a67160d475a70a03dc769be764b5e61 │ ├── assets │ └── mechano │ │ ├── models │ │ └── item │ │ │ ├── connector_single.json │ │ │ ├── spool_empty.json │ │ │ ├── spool_hookup_full.json │ │ │ ├── spool_hookup_empty.json │ │ │ ├── spool_hookup_depleted.json │ │ │ └── spool_hookup.json │ │ └── lang │ │ ├── en_ud.json │ │ └── en_us.json │ └── data │ ├── minecraft │ └── tags │ │ └── block │ │ └── mineable │ │ └── pickaxe.json │ └── mechano │ └── loot_table │ └── blocks │ └── connector_single.json ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── java.gradle ├── .gitattributes ├── settings.gradle ├── .gitignore ├── README.md ├── .vscode ├── settings.json └── launch.json ├── .github └── workflows │ └── build.yml ├── gradle.properties └── gradlew.bat /src/main/resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/logo.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/generated/resources/.cache/82b4a4fdfa8e298fd72741ab142103c4f0fa5f56: -------------------------------------------------------------------------------- 1 | // 1.21.1 2025-11-12T18:10:43.1844226 hitbox 2 | -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/models/item/connector_single.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "mechano:block/connector_single/base" 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/catenary/hookup.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "mechano:textures/block/catenary/hookup" 3 | } -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Mechano Resources", 4 | "pack_format": 15 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "mechano:connector_single" 4 | ] 5 | } -------------------------------------------------------------------------------- /src/main/resources/data/mechano/structure/empty.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/data/mechano/structure/empty.nbt -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/sounds/cable_create.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/sounds/cable_create.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/axis.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/chev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/chev.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/sounds/connector_click_up.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/sounds/connector_click_up.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/debug_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/debug_red.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/stator/big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/stator/big.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/voltometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/voltometer.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/item/spool_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/item/spool_empty.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/item/wire_hookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/item/wire_hookup.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/sounds/connector_click_deny.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/sounds/connector_click_deny.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/sounds/connector_click_down.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/sounds/connector_click_down.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/indrum_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/indrum_block.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/rotor/single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/rotor/single.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/stator/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/stator/base.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/item/indrum_ingot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/item/indrum_ingot.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/item/spool_hookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/item/spool_hookup.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/catenary/hookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/catenary/hookup.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/diagonal_girder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/diagonal_girder.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/test_axis/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/test_axis/red.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/test_axis/tex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/test_axis/tex.png -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/models/item/spool_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "mechano:item/spool_empty" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/catenary/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/catenary/missing.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/tool_station/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/tool_station/base.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/item/spool_hookup_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/item/spool_hookup_empty.png -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/models/item/spool_hookup_full.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "mechano:item/spool_hookup" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/tool_forge.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "mechano:item/tool_forge" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/connector/connector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/connector/connector.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/rotor/single_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/rotor/single_large.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/slip_ring_shaft/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/slip_ring_shaft/base.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/tool_station/forge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/tool_station/forge.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/tool_station/forged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/tool_station/forged.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/tool_station/heated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/tool_station/heated.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/item/spool_hookup_depleted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/item/spool_hookup_depleted.png -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/rotor/big_rotor_dummy/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "particle": "mechano:block/rotor/single_large" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/textures/block/tool_station/maximized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quattage/mechano/HEAD/src/main/resources/assets/mechano/textures/block/tool_station/maximized.png -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/models/item/spool_hookup_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "mechano:item/spool_hookup_empty" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/models/item/spool_hookup_depleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "mechano:item/spool_hookup_depleted" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/java-templates/com/quattage/mechano/MechanoBuildParameters.java.peb: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | public class MechanoBuildParameters { 4 | public static String VERSION = "{{ version }}"; 5 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/tool_station/dummy.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [16, 16], 4 | "textures": { 5 | "particle": "block/dark_oak_planks" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/minecraft/atlases/blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { 4 | "type": "directory", 5 | "source": "block/catenary", 6 | "prefix": "block/catenary/" 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoTags.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import net.neoforged.bus.api.IEventBus; 4 | 5 | public class MechanoTags { 6 | public static void register(IEventBus modBus) { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoPartials.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import net.neoforged.bus.api.IEventBus; 4 | 5 | public class MechanoPartials { 6 | 7 | 8 | public static void register(IEventBus modBus) { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/numeric/VectorRotationRepresentable.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.numeric; 2 | 3 | import net.minecraft.core.Vec3i; 4 | 5 | public interface VectorRotationRepresentable { 6 | public abstract Vec3i getRotation(); 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Disable autocrlf on generated files, they always generate with LF 2 | # Add any extra files or paths here to make git stop saying they 3 | # are changed when only line endings change. 4 | src/generated/**/.cache/cache text eol=lf 5 | src/generated/**/*.json text eol=lf 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenLocal() 4 | gradlePluginPortal() 5 | maven { url = 'https://maven.neoforged.net/releases' } 6 | } 7 | } 8 | 9 | plugins { 10 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' 11 | } 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/content/spool/EmptySpoolItem.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.content.spool; 2 | 3 | import net.minecraft.world.item.Item; 4 | 5 | public class EmptySpoolItem extends Item { 6 | public EmptySpoolItem(Properties properties) { 7 | super(properties); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoEntities.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import net.neoforged.bus.api.IEventBus; 4 | 5 | public class MechanoEntities { 6 | 7 | public static void register(IEventBus modBus) { 8 | Mechano.LOGGER.debug("registering block entities"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoSounds.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import net.neoforged.bus.api.IEventBus; 4 | 5 | public class MechanoSounds { 6 | 7 | public static void register(IEventBus modBus) { 8 | // CatenaryAttributable.Soundscape.registerResources(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vs 2 | *.vscode 3 | 4 | # eclipse 5 | bin 6 | *.launch 7 | .settings 8 | .metadata 9 | .classpath 10 | .project 11 | 12 | # idea 13 | out 14 | *.ipr 15 | *.iws 16 | *.iml 17 | .idea 18 | 19 | # gradle 20 | build 21 | .gradle 22 | 23 | # other 24 | presskit 25 | eclipse 26 | run 27 | runs 28 | run-data 29 | 30 | repo -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.client.multiplayer.ClientLevel entityStorage 2 | public net.minecraft.server.level.ChunkMap entityMap 3 | public net.minecraft.server.level.ChunkMap$TrackedEntity 4 | public net.minecraft.server.level.ChunkMap$TrackedEntity seenBy 5 | public net.minecraft.server.level.ServerLevel$EntityCallbacks -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/topology/CircuitProvider.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.topology; 2 | 3 | 4 | 5 | /** 6 | * Indicates that this class instantiates/caches/provides some kind of 7 | * {@link CircuitComponent} object for use with the Grid API. 8 | */ 9 | public interface CircuitProvider { 10 | CircuitComponent getCircuit(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/gametest/ElectricityTests.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.gametest; 2 | 3 | import com.quattage.mechano.Mechano; 4 | 5 | import net.neoforged.neoforge.gametest.GameTestHolder; 6 | import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; 7 | 8 | @GameTestHolder(Mechano.ID) 9 | @PrefixGameTestTemplate(false) 10 | public class ElectricityTests { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | 4 | --- 5 | 6 |

hi :)

7 |

Mechano provides a fundamental basis for the expansion Create’s conventions and systems into other mods in an extensible and customizible way.

8 |

join the discord

9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/gametest/DisjointSetTests.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.gametest; 2 | 3 | import com.quattage.mechano.Mechano; 4 | 5 | import net.neoforged.neoforge.gametest.GameTestHolder; 6 | import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; 7 | 8 | @GameTestHolder(Mechano.ID) 9 | @PrefixGameTestTemplate(false) 10 | public class DisjointSetTests { 11 | 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/ClientGrid.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api; 2 | 3 | import net.minecraft.world.level.Level; 4 | 5 | public final class ClientGrid extends Grid { 6 | 7 | protected ClientGrid(Level world) { 8 | super(world); 9 | } 10 | 11 | @Override 12 | protected void onLoad() { 13 | 14 | } 15 | 16 | @Override 17 | protected void onUnload() { 18 | 19 | } 20 | 21 | @Override 22 | protected void tick() { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "automatic", 3 | "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx32G -Xms100m -Xlog:disable", 4 | "java.debug.settings.onBuildFailureProceed": true, 5 | "java.dependency.packagePresentation": "hierarchical", 6 | "java.compile.nullAnalysis.mode": "disabled", 7 | "java.format.settings.url": "eclipse-formatter.xml", 8 | "editor.inlayHints.enabled": "off" 9 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoClient.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import net.neoforged.api.distmarker.Dist; 4 | import net.neoforged.bus.api.IEventBus; 5 | import net.neoforged.fml.common.Mod; 6 | 7 | @Mod(value = Mechano.ID, dist = Dist.CLIENT) 8 | public class MechanoClient { 9 | 10 | public MechanoClient(IEventBus modBus) { 11 | modBus.addListener(MechanoClientEvents::onRegisterLayers); 12 | modBus.addListener(MechanoClientEvents::onRegisterReloadListeners); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/generated/resources/data/mechano/loot_table/blocks/connector_single.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "bonus_rolls": 0.0, 6 | "conditions": [ 7 | { 8 | "condition": "minecraft:survives_explosion" 9 | } 10 | ], 11 | "entries": [ 12 | { 13 | "type": "minecraft:item", 14 | "name": "mechano:connector_single" 15 | } 16 | ], 17 | "rolls": 1.0 18 | } 19 | ], 20 | "random_sequence": "mechano:blocks/connector_single" 21 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/hitbox/HitboxRepresentable.java: -------------------------------------------------------------------------------- 1 | 2 | package com.quattage.mechano.foundation.block.hitbox; 3 | 4 | import java.util.Arrays; 5 | 6 | import net.minecraft.world.level.ClipContext.ShapeGetter; 7 | import net.minecraft.world.phys.shapes.VoxelShape; 8 | 9 | public interface HitboxRepresentable extends ShapeGetter { 10 | public VoxelShape get(Object... tokens); 11 | 12 | public VoxelShape get(); 13 | 14 | default Object[] minusFirst(Object[] tokens) { 15 | return Arrays.copyOfRange(tokens, 1, tokens.length); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/content/connector/ConnectorBlockEntity.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.content.connector; 2 | 3 | import com.quattage.mechano.api.blockEntity.GriddableBlockEntity; 4 | 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.world.level.block.entity.BlockEntityType; 7 | import net.minecraft.world.level.block.state.BlockState; 8 | 9 | public abstract class ConnectorBlockEntity extends GriddableBlockEntity { 10 | public ConnectorBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { 11 | super(type, pos, state); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/mixin/client/accessor/PlayerInfoAccessor.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.mixin.client.accessor; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | import net.minecraft.client.multiplayer.PlayerInfo; 8 | import net.minecraft.client.player.AbstractClientPlayer; 9 | 10 | @Mixin(AbstractClientPlayer.class) 11 | public interface PlayerInfoAccessor { 12 | @Accessor("playerInfo") 13 | @Nullable PlayerInfo mechano$getPlayerInfo(); 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout repository 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | fetch-tags: true 14 | 15 | - name: Setup JDK 21 16 | uses: actions/setup-java@v4 17 | with: 18 | java-version: '21' 19 | distribution: 'temurin' 20 | 21 | - name: Setup Gradle 22 | uses: gradle/actions/setup-gradle@v4 23 | 24 | - name: Build with Gradle 25 | run: ./gradlew build 26 | -------------------------------------------------------------------------------- /src/main/resources/mechano.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "priority": 1100, 5 | "package": "com.quattage.mechano.foundation.mixin", 6 | "compatibilityLevel": "JAVA_21", 7 | "refmap": "mechano.refmap.json", 8 | "plugin": "com.quattage.mechano.foundation.mixin.MechanoMixins", 9 | "mixins": [ 10 | 11 | ], 12 | "client": [ 13 | "client.accessor.RenderBuffersAccessor", 14 | "client.accessor.PlayerInfoAccessor", 15 | "client.ItemInHandRendererInvoker", 16 | "client.ItemInHandRendererMixin" 17 | ], 18 | "injectors": { 19 | "defaultRequire": 1 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/transmitter/TransmitterEntry.java: -------------------------------------------------------------------------------- 1 | 2 | package com.quattage.mechano.api.transmitter; 3 | 4 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 5 | import com.tterrag.registrate.AbstractRegistrate; 6 | import com.tterrag.registrate.util.entry.RegistryEntry; 7 | 8 | import net.neoforged.neoforge.registries.DeferredHolder; 9 | 10 | public class TransmitterEntry extends RegistryEntry, TransmitterType> { 11 | 12 | public TransmitterEntry(AbstractRegistrate owner, DeferredHolder, TransmitterType> delegate) { 13 | super(owner, delegate); 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/test_block/cube.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "0": "mechano:block/debug_red", 5 | "particle": "mechano:block/debug_red" 6 | }, 7 | "elements": [ 8 | { 9 | "from": [6, 6, 6], 10 | "to": [10, 10, 10], 11 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 2, 8]}, 12 | "faces": { 13 | "north": {"uv": [0, 0, 4, 4], "texture": "#0"}, 14 | "east": {"uv": [0, 0, 4, 4], "texture": "#0"}, 15 | "south": {"uv": [0, 0, 4, 4], "texture": "#0"}, 16 | "west": {"uv": [0, 0, 4, 4], "texture": "#0"}, 17 | "up": {"uv": [0, 0, 4, 4], "texture": "#0"}, 18 | "down": {"uv": [0, 0, 4, 4], "texture": "#0"} 19 | } 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/content/spool/HookupSpoolItem.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.content.spool; 2 | 3 | import com.quattage.mechano.MechanoTransmitters; 4 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 5 | import com.quattage.mechano.api.transmitter.SpoolItem; 6 | import com.quattage.mechano.api.transmitter.TransmitterType; 7 | 8 | public class HookupSpoolItem extends SpoolItem { 9 | 10 | public HookupSpoolItem(Properties properties) { 11 | super(properties); 12 | } 13 | 14 | @Override 15 | public CircuitComponent getComponent() { 16 | return null; 17 | } 18 | 19 | @Override 20 | public TransmitterType getTransmitter() { 21 | return MechanoTransmitters.HOOKUP.get(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "cable_create": { 3 | "subtitle": "sounds.mechano.cable_create", 4 | "sounds": [ 5 | "mechano:cable_create" 6 | ] 7 | }, 8 | "connector_click_up": { 9 | "subtitle": "sounds.mechano.connector_click", 10 | "sounds": [ 11 | "mechano:connector_click_up" 12 | ] 13 | }, 14 | "connector_click_down": { 15 | "subtitle": "sounds.mechano.connector_click", 16 | "sounds": [ 17 | "mechano:connector_click_down" 18 | ] 19 | }, 20 | "connector_click_deny": { 21 | "subtitle": "sounds.mechano.connector_click", 22 | "sounds": [ 23 | "mechano:connector_click_deny" 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/mixin/client/accessor/RenderBuffersAccessor.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.mixin.client.accessor; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | import net.minecraft.client.renderer.LevelRenderer; 8 | import net.minecraft.client.renderer.RenderBuffers; 9 | 10 | 11 | @Mixin(LevelRenderer.class) 12 | public interface RenderBuffersAccessor { 13 | /** 14 | * Accesses and returns {@link LevelRenderer#renderBuffers}. 15 | * @return {@link RenderBuffers} instance belonging to this {@link LevelRenderer} 16 | */ 17 | @Accessor("renderBuffers") 18 | @Nullable RenderBuffers mechano$getRenderBuffers(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/transmitter/TransmitterFactory.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.transmitter; 2 | 3 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 4 | import com.quattage.mechano.api.grid.topology.vertex.WireJack; 5 | import com.tterrag.registrate.util.nullness.NonNullBiFunction; 6 | import com.tterrag.registrate.util.nullness.NonnullType; 7 | 8 | @FunctionalInterface 9 | public interface TransmitterFactory extends NonNullBiFunction { 10 | /** 11 | * Instantiates a single transmitter bound to the given start and end 12 | * @param start 13 | * @param end 14 | * @return 15 | */ 16 | @Override 17 | @NonnullType T apply(@NonnullType WireJack t, @NonnullType WireJack u); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/short_up_flat.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3, 11.5, 6], 11 | "to": [13.1, 16, 17], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [0, 6, 5, 10], "texture": "#0"}, 15 | "east": {"uv": [5, 6, 10.5, 10], "texture": "#0"}, 16 | "south": {"uv": [0, 6, 5, 10], "texture": "#0"}, 17 | "west": {"uv": [5, 6, 10.5, 10], "texture": "#0"}, 18 | "up": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 19 | "down": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/models/item/spool_hookup.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "overrides": [ 4 | { 5 | "model": "mechano:item/spool_empty", 6 | "predicate": { 7 | "mechano:full": 0.0 8 | } 9 | }, 10 | { 11 | "model": "mechano:item/spool_hookup_empty", 12 | "predicate": { 13 | "mechano:full": 0.1 14 | } 15 | }, 16 | { 17 | "model": "mechano:item/spool_hookup_depleted", 18 | "predicate": { 19 | "mechano:full": 0.5 20 | } 21 | }, 22 | { 23 | "model": "mechano:item/spool_hookup_full", 24 | "predicate": { 25 | "mechano:full": 0.75 26 | } 27 | }, 28 | { 29 | "model": "mechano:item/spool_hookup_full", 30 | "predicate": { 31 | "mechano:full": 0.999 32 | } 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/middle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, -3.35, 5], 11 | "to": [11, 19.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/long_double.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, -6.35, 5], 11 | "to": [11, 22.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/long_end_down.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, -3.35, 5], 11 | "to": [11, 22.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/long_end_up.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, -6.35, 5], 11 | "to": [11, 19.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/short_double.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, 0.65, 5], 11 | "to": [11, 15.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 10.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 10.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 10.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 10.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/short_end_down.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, -3.35, 5], 11 | "to": [11, 15.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/short_end_up.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, 0.65, 5], 11 | "to": [11, 19.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/long_up_flat.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3, 16, 10.75], 11 | "to": [13.1, 20.5, 21.75], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "color": 2, 14 | "faces": { 15 | "north": {"uv": [0, 6, 5, 10], "texture": "#0"}, 16 | "east": {"uv": [5, 6, 10.5, 10], "texture": "#0"}, 17 | "south": {"uv": [0, 6, 5, 10], "texture": "#0"}, 18 | "west": {"uv": [5, 6, 10.5, 10], "texture": "#0"}, 19 | "up": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 20 | "down": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"} 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoBlockEntities.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import com.quattage.mechano.api.blockEntity.renderer.GriddableBlockEntityRenderer; 4 | import com.quattage.mechano.content.connector.SingleConnectorBlockEntity; 5 | import com.tterrag.registrate.util.entry.BlockEntityEntry; 6 | 7 | import net.neoforged.bus.api.IEventBus; 8 | 9 | public class MechanoBlockEntities { 10 | 11 | public static final BlockEntityEntry CONNECTOR_SINGLE = 12 | Mechano.REGISTRATE.blockEntity("connector_single", SingleConnectorBlockEntity::new) 13 | .validBlocks(MechanoBlocks.CONNECTOR_SINGLE) 14 | .renderer(() -> GriddableBlockEntityRenderer::new) 15 | .register(); 16 | 17 | public static void register(IEventBus modBus) { 18 | Mechano.LOGGER.debug("registering block entities"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/short_down_vert.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3, -1, 0], 11 | "to": [13.1, 10, 4.5], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 17 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 270, "texture": "#0"}, 18 | "up": {"uv": [0, 6, 5, 10], "texture": "#0"}, 19 | "down": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/short_up_vert.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3, 6, 11.5], 11 | "to": [13.1, 17, 16], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 270, "texture": "#0"}, 16 | "south": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 17 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"}, 19 | "down": {"uv": [0, 6, 5, 10], "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | # environment 3 | org.gradle.jvmargs=-Xmx6G 4 | org.gradle.daemon=false 5 | org.gradle.parallel=true 6 | org.gradle.caching=true 7 | org.gradle.configuration-cache=false 8 | 9 | 10 | # minecraft 11 | minecraft_version=1.21.1 12 | minecraft_version_range=[1.21.1, 1.22) 13 | parchment_mappings_version=2024.11.17 14 | neo_version=21.1.194 15 | neo_version_range=[21.1.0,) 16 | loader_version_range=[4,) 17 | 18 | # dependencies 19 | ejml_version=0.44.0 20 | create_version=6.0.6-98 21 | ponder_version=1.0.59 22 | flywheel_version=1.0.4 23 | registrate_version=MC1.21-1.3.0+62 24 | 25 | # optionals 26 | jei_version=19.21.0.247 27 | spark_version=1.10.142-SNAPSHOT 28 | 29 | 30 | # mechano 31 | mod_id=mechano 32 | mod_version=0.1.2 33 | mod_group_id=com.quattage.mechano 34 | 35 | mod_name=Mechano 36 | mod_authors=quat 37 | mod_description=Bringing Create's polish to other mods. 38 | mod_license=MIT -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/blockEntity/renderer/SimpleBlockEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.blockEntity.renderer; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.quattage.mechano.api.blockEntity.SimpleBlockEntity; 5 | 6 | import net.minecraft.client.renderer.MultiBufferSource; 7 | import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; 8 | import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; 9 | 10 | public class SimpleBlockEntityRenderer implements BlockEntityRenderer { 11 | 12 | public SimpleBlockEntityRenderer(BlockEntityRendererProvider.Context context) { 13 | super(); 14 | } 15 | 16 | @Override 17 | public void render(T blockEntity, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/short_down_flat.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3, 0, -1], 11 | "to": [13.1, 4.5, 10], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"}, 15 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 180, "texture": "#0"}, 16 | "south": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"}, 17 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 180, "texture": "#0"}, 18 | "up": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 19 | "down": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"} 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/LeftClickCapturable.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation; 2 | 3 | import com.quattage.mechano.MechanoClientEvents; 4 | 5 | import net.minecraft.world.InteractionHand; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraft.world.item.ItemStack; 8 | 9 | /** 10 | * funny lil thing 11 | * TODO what happens if the player rebinds left click lol 12 | */ 13 | public interface LeftClickCapturable { 14 | /** 15 | * Fired by the {@link MechanoClientEvents#onLeftClick client event} 16 | * whenever the player left clicks holding this item 17 | * @param player 18 | * @param stack 19 | * @return true if vanilla left click behaviour with this 20 | * item should be cancelled in favor of the implementation in this method. 21 | */ 22 | abstract boolean onLeftClick(Player player, ItemStack stack, InteractionHand hand); 23 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/long_down_vert.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3.1, -5.5, -4.85], 11 | "to": [13.2, 5.5, -0.35], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "color": 2, 14 | "faces": { 15 | "north": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"}, 16 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 90, "texture": "#0"}, 17 | "south": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 18 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 270, "texture": "#0"}, 19 | "up": {"uv": [0, 6, 5, 10], "texture": "#0"}, 20 | "down": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"} 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/long_up_vert.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3.1, 10.5, 16.35], 11 | "to": [13.2, 21.5, 20.85], 12 | "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]}, 13 | "color": 2, 14 | "faces": { 15 | "north": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"}, 16 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 270, "texture": "#0"}, 17 | "south": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 18 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 90, "texture": "#0"}, 19 | "up": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"}, 20 | "down": {"uv": [0, 6, 5, 10], "texture": "#0"} 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/partials/long_down_flat.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [3, -4.5, -5.75], 11 | "to": [13.1, 0, 5.25], 12 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 13 | "color": 2, 14 | "faces": { 15 | "north": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"}, 16 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 180, "texture": "#0"}, 17 | "south": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"}, 18 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 180, "texture": "#0"}, 19 | "up": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 20 | "down": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"} 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/numeric/ClientOnlySupplier.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.numeric; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import net.neoforged.api.distmarker.Dist; 9 | import net.neoforged.api.distmarker.OnlyIn; 10 | 11 | /** 12 | * A LazySupplier which can only be accessed on the client 13 | */ 14 | public class ClientOnlySupplier implements Supplier { 15 | 16 | @NotNull private final Supplier supplier; 17 | @Nullable private T cachedResult; 18 | 19 | public ClientOnlySupplier(Supplier supplier) { 20 | this.supplier = supplier; 21 | } 22 | 23 | @Override 24 | @OnlyIn(Dist.CLIENT) 25 | public T get() { 26 | if(cachedResult == null) 27 | cachedResult = supplier.get(); 28 | return cachedResult; 29 | } 30 | 31 | public void invalidate() { 32 | this.cachedResult = null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/orientation/OrientationUpdatable.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block.orientation; 2 | 3 | import net.minecraft.world.level.block.state.BlockState; 4 | 5 | /** 6 | * Indicates that implementing objects represent face-specific data that 7 | * rotates with the BlockState of the voxel itself. 8 | */ 9 | public interface OrientationUpdatable { 10 | /** 11 | * Updates the oriented data within this object to 12 | * match the orientation described by the given BlockState. 13 | * @param state state to extract orientation data 14 | */ 15 | default void updateOrientation(BlockState state) { 16 | updateOrientation(DirectionTransformer.extract(state)); 17 | } 18 | /** 19 | * Updates the oriented data within this object to 20 | * match the given orientation 21 | * @param dir Orientation to use when transforming this object 22 | */ 23 | void updateOrientation(CombinedOrientation dir); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoTransmitters.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import com.quattage.mechano.api.catenary.Catenaries; 4 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 5 | import com.quattage.mechano.api.grid.topology.vertex.Node.Joint; 6 | import com.quattage.mechano.api.transmitter.TransmitterEntry; 7 | 8 | import net.neoforged.bus.api.IEventBus; 9 | 10 | public class MechanoTransmitters { 11 | 12 | public static final TransmitterEntry HOOKUP = 13 | Mechano.REGISTRATE.transmitter("hookup") 14 | .component((start, end) -> { 15 | Joint j = new Joint(null); 16 | return null; 17 | }).renderer(() -> p -> { 18 | p.extruder(Catenaries.renderPipeline().SQUARE_EXTRUDER); 19 | p.thickness(Catenaries.RenderPipeline.Thickness.TRIPLE); 20 | }).register(); 21 | 22 | public static void register(IEventBus modBus) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/functional/Passthrough.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.functional; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | import java.util.function.Consumer; 6 | 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 10 | import com.quattage.mechano.api.grid.topology.CircuitComponent.FunctionalComponent; 11 | import com.quattage.mechano.api.grid.topology.vertex.Node; 12 | import com.quattage.mechano.api.grid.topology.vertex.Terminal; 13 | 14 | /** 15 | * A component that does literally nothing 16 | */ 17 | public class Passthrough extends FunctionalComponent { 18 | public Passthrough() { super("Empty"); } 19 | @Override public Collection getTerminals() { return Collections.emptyList(); } 20 | @Override public void forEachNode(Consumer cons) { return; } 21 | @Override public @Nullable CircuitComponent getParentComponent() { return null; } 22 | @Override public boolean isSignificant() { return false; } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/mixin/client/ItemInHandRendererInvoker.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.mixin.client; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | 9 | import net.minecraft.client.renderer.ItemInHandRenderer; 10 | import net.minecraft.client.renderer.MultiBufferSource; 11 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 12 | import net.minecraft.world.entity.HumanoidArm; 13 | 14 | @Mixin(ItemInHandRenderer.class) 15 | public interface ItemInHandRendererInvoker { 16 | 17 | @Invoker("renderMapHand") 18 | public abstract void mechano$renderMapHand(PoseStack poseStac, MultiBufferSource buffer, int packedLight, HumanoidArm side); 19 | 20 | @Invoker("calculateMapTilt") 21 | public abstract float mechano$calculateMapTilt(float pitch); 22 | 23 | @Accessor("entityRenderDispatcher") 24 | public abstract EntityRenderDispatcher mechano$getEntityRenderDispatcher(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/numeric/ExtendedCodecs.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.numeric; 2 | 3 | import java.util.Base64; 4 | 5 | import com.mojang.serialization.Codec; 6 | import com.mojang.serialization.DataResult; 7 | import com.mojang.serialization.DynamicOps; 8 | import com.mojang.serialization.codecs.PrimitiveCodec; 9 | 10 | public class ExtendedCodecs { 11 | 12 | public static final Codec B64 = Codec.STRING.xmap( 13 | s -> Base64.getDecoder().decode(s), 14 | bytes -> Base64.getEncoder().encodeToString(bytes) 15 | ); 16 | 17 | public static final Codec CHAR = new PrimitiveCodec() { 18 | @Override public DataResult read(DynamicOps ops, T input) { 19 | return ops.getNumberValue(input).map(number -> (char)number.intValue()); 20 | } 21 | @Override public T write(DynamicOps ops, Character value) { 22 | return ops.createString("" + value); 23 | } 24 | @Override public String toString() { 25 | return "Character"; 26 | } 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/content/connector/SingleConnectorBlockEntity.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.content.connector; 2 | 3 | import com.quattage.mechano.api.grid.CircuitFactory; 4 | import com.quattage.mechano.api.grid.topology.vertex.Node; 5 | import com.quattage.mechano.foundation.block.orientation.Relative; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.world.level.block.entity.BlockEntityType; 9 | import net.minecraft.world.level.block.state.BlockState; 10 | 11 | public class SingleConnectorBlockEntity extends ConnectorBlockEntity { 12 | 13 | public SingleConnectorBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { 14 | super(type, pos, state); 15 | } 16 | 17 | @Override 18 | public void constructCircuit(CircuitFactory circuit) { 19 | Node passive = circuit.newNode(); 20 | circuit.wireJack("Wire Attachment") 21 | .attachedTo(passive) 22 | .x(0).y(17).z(0) 23 | .size(3.5f).make(); 24 | circuit.blockJack("Bottom Face") 25 | .attachedTo(passive) 26 | .face(Relative.BOTTOM) 27 | .make(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/main/templates/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | 2 | modLoader="javafml" 3 | loaderVersion="${loader_version_range}" 4 | issueTrackerURL="https://github.com/quattage/mechano/issues" 5 | license="${mod_license}" 6 | 7 | [[mods]] 8 | modId="${mod_id}" 9 | version="${mod_version}" 10 | displayName="${mod_name}" 11 | 12 | displayURL="https://github.com/quattage/mechano" 13 | logoFile="logo.png" 14 | credits="" 15 | authors="${mod_authors}" 16 | description='''${mod_description}''' 17 | 18 | [[mixins]] 19 | config="${mod_id}.mixins.json" 20 | 21 | [[accessTransformers]] 22 | file="META-INF/accesstransformer.cfg" 23 | 24 | # base dependencies 25 | [[dependencies.${mod_id}]] 26 | modId="create" 27 | type="required" 28 | versionRange="[6.0.6,6.1.0)" 29 | ordering="NONE" 30 | side="BOTH" 31 | 32 | [[dependencies.${mod_id}]] 33 | modId="neoforge" 34 | type="required" 35 | versionRange="${neo_version_range}" 36 | ordering="NONE" 37 | side="BOTH" 38 | 39 | # Here's another dependency 40 | [[dependencies.${mod_id}]] 41 | modId="minecraft" 42 | type="required" 43 | versionRange="${minecraft_version_range}" 44 | ordering="NONE" 45 | side="BOTH" -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoItems.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import com.quattage.mechano.content.spool.EmptySpoolItem; 4 | import com.quattage.mechano.content.spool.HookupSpoolItem; 5 | import com.quattage.mechano.infrastructure.datagen.SpoolDataProvider; 6 | import com.tterrag.registrate.providers.ProviderType; 7 | import com.tterrag.registrate.util.entry.ItemEntry; 8 | 9 | import net.neoforged.bus.api.IEventBus; 10 | 11 | public class MechanoItems { 12 | 13 | static { Mechano.REGISTRATE.setCreativeTab(MechanoGroups.BASE); } 14 | 15 | public static final ItemEntry SPOOL_EMPTY = Mechano.REGISTRATE.item("spool_empty", EmptySpoolItem::new) 16 | .register(); 17 | 18 | public static final ItemEntry SPOOL_HOOKUP = Mechano.REGISTRATE.item("spool_hookup", HookupSpoolItem::new) 19 | .setData(ProviderType.ITEM_MODEL, SpoolDataProvider::generate) 20 | .properties(p -> p.durability(512).stacksTo(1).setNoRepair().craftRemainder(MechanoItems.SPOOL_EMPTY.get().asItem())) 21 | .register(); 22 | 23 | public static void register(IEventBus modBus) { 24 | Mechano.LOGGER.debug("registering items"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/hitbox/MechanoHitboxes.java: -------------------------------------------------------------------------------- 1 | 2 | package com.quattage.mechano.foundation.block.hitbox; 3 | 4 | import javax.annotation.processing.Generated; 5 | 6 | import net.neoforged.bus.api.IEventBus; 7 | 8 | @SuppressWarnings("unused") 9 | @Generated("com.quattage.mechano.infrastructure.datagen.HitboxDataProvider.Provider") 10 | public class MechanoHitboxes { 11 | 12 | public void register(IEventBus modBus) {} 13 | 14 | public static final LazyRotatableHitbox TEST_AXIS = new LazyRotatableHitbox( 15 | VoxelShapeBuilder 16 | .start(1.0, 0.0, 1.0, 3.0, 5.0, 3.0) 17 | .addBox(13.0, 0.0, 13.0, 15.0, 2.0, 15.0) 18 | .addBox(7.0, 7.0, 7.0, 9.0, 9.0, 9.0) 19 | .addBox(1.0, 10.0, 13.0, 3.0, 12.0, 15.0) 20 | .addBox(13.0, 5.0, 1.0, 15.0, 7.0, 3.0) 21 | .optimize().make() 22 | ); 23 | 24 | public static final LazyRotatableHitbox CONNECTOR_SINGLE = new LazyRotatableHitbox( 25 | VoxelShapeBuilder 26 | .start(2.0, 0.0, 3.0, 14.0, 4.0, 13.0) 27 | .addBox(5.5, 6.0, 5.5, 10.5, 16.0, 10.5) 28 | .addBox(4.0, 12.95, 4.0, 12.0, 14.95, 12.0) 29 | .addBox(4.0, 4.0, 4.0, 12.0, 9.0, 12.0) 30 | .addBox(4.0, 10.0, 4.0, 12.0, 12.0, 12.0) 31 | .optimize().make() 32 | ); 33 | 34 | 35 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/switchboard/task/DummyTask.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.switchboard.task; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import com.quattage.mechano.api.ClientGrid; 6 | import com.quattage.mechano.api.ServerGrid; 7 | import com.quattage.mechano.api.switchboard.action.ActionTask; 8 | import com.quattage.mechano.api.switchboard.action.GridAction; 9 | 10 | import io.netty.buffer.ByteBuf; 11 | 12 | public class DummyTask implements ActionTask { 13 | 14 | @Override 15 | public @Nullable Class[] getArgumentTemplate() { 16 | return new Class[0]; 17 | } 18 | 19 | @Override 20 | public void dynamicEncode(Object[] args, ByteBuf buffer) { 21 | 22 | } 23 | 24 | @Override 25 | public @Nullable Object[] dynamicDecode(ByteBuf buffer) { 26 | return new Class[0]; 27 | } 28 | 29 | @Override 30 | public GridAction executeAsServer(int attempt, ServerGrid grid, Object... args) { 31 | return GridAction.RESPONSE_SUCCESS; 32 | } 33 | 34 | @Override 35 | public GridAction executeAsClient(int attempt, ClientGrid grid, Object... args) { 36 | return GridAction.RESPONSE_SUCCESS; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/ConnectorHostOverridable.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.world.level.LevelReader; 5 | import net.minecraft.world.level.block.state.BlockState; 6 | 7 | /** 8 | * Allows implementing blocks to override the default behaviour when determining whether or not 9 | * a connector is allowed to be placed on a specific block. 10 | */ 11 | public interface ConnectorHostOverridable { 12 | 13 | /** 14 | * Called internally by {@link com.quattage.mechano.content.connector.BlockWithConnections#canSurvive ConnectorBlock.canSurvive()}

15 | * Implement your own logic here to determine whether or not your block 16 | * can host a connector. 17 | * @param world World to operate within 18 | * @param connectorPos Position of the connector that called this method 19 | * @param connectorState BlockState of the connector that called this method 20 | * @param thisPos Position of this block 21 | * @param thisState BlockState of this block 22 | * @return 23 | */ 24 | abstract boolean isConnectorAllowed(LevelReader world, BlockPos connectorPos, BlockState connectorState, BlockPos thisPos, BlockState thisState); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/mixin/MechanoMixins.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.mixin; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 8 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 9 | 10 | public class MechanoMixins implements IMixinConfigPlugin { 11 | 12 | @Override 13 | public void onLoad(String mixinPackage) { 14 | 15 | } 16 | 17 | @Override 18 | public String getRefMapperConfig() { 19 | return null; 20 | } 21 | 22 | @Override 23 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { 24 | return true; 25 | } 26 | 27 | @Override 28 | public void acceptTargets(Set myTargets, Set otherTargets) { 29 | 30 | } 31 | 32 | @Override 33 | public List getMixins() { return null; } 34 | 35 | @Override 36 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 37 | 38 | } 39 | 40 | @Override 41 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/generic/chevron_cutout.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "0": "mechano:block/chev", 5 | "particle": "mechano:block/chev" 6 | }, 7 | "elements": [ 8 | { 9 | "from": [-0.75, 0, -1], 10 | "to": [0.75, 1, 1.75], 11 | "rotation": {"angle": -45, "axis": "x", "origin": [0, 0, 1.75]}, 12 | "faces": { 13 | "north": {"uv": [2, 12, 4, 14], "rotation": 90, "texture": "#0"}, 14 | "east": {"uv": [0, 14, 5.5, 16], "texture": "#0"}, 15 | "south": {"uv": [2, 12, 4, 14], "rotation": 270, "texture": "#0"}, 16 | "west": {"uv": [0, 14, 5.5, 16], "rotation": 180, "texture": "#0"}, 17 | "up": {"uv": [2, 8, 4, 14], "rotation": 180, "texture": "#0"}, 18 | "down": {"uv": [2, 12, 8, 14], "rotation": 270, "texture": "#0"} 19 | } 20 | }, 21 | { 22 | "from": [-0.75, 1, 0.75], 23 | "to": [0.75, 2.75, 1.75], 24 | "rotation": {"angle": -45, "axis": "x", "origin": [0, 0, 1.75]}, 25 | "faces": { 26 | "north": {"uv": [2, 12, 6, 14], "rotation": 90, "texture": "#0"}, 27 | "east": {"uv": [0, 10, 2, 14], "texture": "#0"}, 28 | "south": {"uv": [2, 12, 6, 14], "rotation": 90, "texture": "#0"}, 29 | "west": {"uv": [0, 10, 2, 14], "texture": "#0"}, 30 | "up": {"uv": [2, 12, 4, 14], "rotation": 270, "texture": "#0"} 31 | } 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /src/generated/resources/.cache/cd7babe69a67160d475a70a03dc769be764b5e61: -------------------------------------------------------------------------------- 1 | // 1.21.1 2025-11-12T18:10:43.1834218 Registrate Provider for mechano [Registries, Data Maps, Recipes, Advancements, Loot Tables, Tags (enchantments), Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), generic_server_provider, Blockstates, Item models, Lang (en_us/en_ud), generic_client_provider] 2 | 9d6ed09d4ed06c5c03cabead556e3a4ad5006e24 assets/mechano/blockstates/connector_single.json 3 | 7183d6071d453347ca1faa5f0328468cb688d5a2 assets/mechano/lang/en_ud.json 4 | 4ee87fe0077e4ba4de3ef8c0ed498d85b2fd6350 assets/mechano/lang/en_us.json 5 | 0c83e6577a1cdcb44045b1e73fe05525848f690a assets/mechano/models/item/connector_single.json 6 | 45556d71a23741b54f53f1be8ac8b6616134ff2a assets/mechano/models/item/spool_empty.json 7 | 52689b6a6aa9c333886c6a84954bde94c30f1ed2 assets/mechano/models/item/spool_hookup.json 8 | 81785c75bb45e4759c89d22e6e7cd6227ddf45f6 assets/mechano/models/item/spool_hookup_depleted.json 9 | 1093752f49f42a074c69c3e2aac19146b224303b assets/mechano/models/item/spool_hookup_empty.json 10 | d8f22d86e859bc727636b6a54c4d1778f1c5f545 assets/mechano/models/item/spool_hookup_full.json 11 | 3c8203ac6b15a07d5c0d49e79ae090e431014e56 data/mechano/loot_table/blocks/connector_single.json 12 | cae50aa0844debc16bbe00664505f48edfa8ae65 data/minecraft/tags/block/mineable/pickaxe.json 13 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/item/MechanoItemProperties.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.item; 2 | 3 | import java.util.ArrayList; 4 | 5 | import com.quattage.mechano.Mechano; 6 | import com.quattage.mechano.api.transmitter.SpoolItem; 7 | import com.simibubi.create.foundation.data.CreateRegistrate; 8 | import com.tterrag.registrate.builders.ItemBuilder; 9 | 10 | import net.minecraft.client.multiplayer.ClientLevel; 11 | import net.minecraft.client.renderer.item.ItemPropertyFunction; 12 | import net.minecraft.core.component.DataComponents; 13 | import net.minecraft.resources.ResourceLocation; 14 | import net.minecraft.world.entity.LivingEntity; 15 | import net.minecraft.world.item.ItemStack; 16 | 17 | public class MechanoItemProperties { 18 | public static final ResourceLocation FULLNESS = Mechano.asResource("full"); 19 | public static ArrayList> spools = new ArrayList<>(); 20 | @SuppressWarnings("deprecation") 21 | public static class SpoolFullnessProperty implements ItemPropertyFunction { 22 | @Override 23 | public float call(ItemStack stack, ClientLevel level, LivingEntity entity, int seed) { 24 | float out = 1f - ((float)stack.getDamageValue() / stack.getOrDefault(DataComponents.MAX_DAMAGE, 512)); 25 | return out; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/content/connector/SingleConnectorBlock.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.content.connector; 2 | 3 | import com.quattage.mechano.MechanoBlockEntities; 4 | import com.quattage.mechano.foundation.block.hitbox.MechanoHitboxes; 5 | 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.world.level.BlockGetter; 8 | import net.minecraft.world.level.block.entity.BlockEntityType; 9 | import net.minecraft.world.level.block.state.BlockState; 10 | import net.minecraft.world.phys.shapes.CollisionContext; 11 | import net.minecraft.world.phys.shapes.VoxelShape; 12 | 13 | public class SingleConnectorBlock extends BlockWithConnections { 14 | 15 | public SingleConnectorBlock(Properties pProperties) { 16 | super(pProperties); 17 | } 18 | 19 | @Override 20 | protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { 21 | return MechanoHitboxes.CONNECTOR_SINGLE.get(state); 22 | } 23 | 24 | @Override 25 | public Class getBlockEntityClass() { 26 | return SingleConnectorBlockEntity.class; 27 | } 28 | 29 | @Override 30 | public BlockEntityType getBlockEntityType() { 31 | return MechanoBlockEntities.CONNECTOR_SINGLE.get(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/command/GridDumpCommand.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.command; 2 | 3 | import com.mojang.brigadier.builder.ArgumentBuilder; 4 | import com.quattage.mechano.api.Grid; 5 | import com.quattage.mechano.api.switchboard.action.GridAction; 6 | 7 | import net.minecraft.commands.CommandSourceStack; 8 | import net.minecraft.commands.Commands; 9 | import net.minecraft.network.chat.Component; 10 | import net.minecraft.server.level.ServerPlayer; 11 | 12 | public class GridDumpCommand { 13 | 14 | public static ArgumentBuilder make() { 15 | return Commands.literal("dump") 16 | .requires(stack -> stack.hasPermission(2)) 17 | .executes(ctx -> { 18 | CommandSourceStack source = ctx.getSource(); 19 | if(!(source.getEntity() instanceof ServerPlayer sp)) { 20 | source.sendFailure(Component.literal("Couldn't dump from non-player source")); 21 | return 1; 22 | } 23 | Grid.server(sp).initiateTask(GridAction.TASK_GRID_DUMP) 24 | .from() 25 | .withArguments(sp.getUUID()) 26 | .executeAs(sp); 27 | return 1; 28 | }); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/command/MechanoCommands.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.command; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import com.quattage.mechano.Mechano; 6 | 7 | import net.createmod.catnip.command.CatnipCommands; 8 | import net.minecraft.commands.CommandSourceStack; 9 | import net.minecraft.commands.Commands; 10 | import net.neoforged.bus.api.SubscribeEvent; 11 | import net.neoforged.fml.common.EventBusSubscriber; 12 | import net.neoforged.neoforge.event.RegisterCommandsEvent; 13 | 14 | @EventBusSubscriber 15 | public class MechanoCommands { 16 | 17 | @SubscribeEvent 18 | public static void register(RegisterCommandsEvent evt) { 19 | LiteralCommandNode root = evt.getDispatcher().register(MechanoCommands.makeRoot()); 20 | root.addChild(CatnipCommands.buildRedirect("grid", MechanoCommands.makeGrid())); 21 | } 22 | 23 | private static LiteralArgumentBuilder makeRoot() { 24 | return Commands.literal(Mechano.ID) 25 | .requires(stack -> stack.hasPermission(0)); 26 | } 27 | 28 | private static LiteralCommandNode makeGrid() { 29 | return Commands.literal("grid") 30 | .then(GridDumpCommand.make()) 31 | .build(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/Watt.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid; 2 | 3 | import java.util.Objects; 4 | 5 | public class Watt { 6 | 7 | private double volts = 0d; 8 | private double amps = 0d; 9 | 10 | public static Watt zero() { 11 | return new Watt(0, 0); 12 | } 13 | 14 | public Watt(double volts, double current) { 15 | this.volts = Math.abs(volts); 16 | this.amps = Math.abs(current); 17 | } 18 | 19 | public double get() { 20 | return volts * amps; 21 | } 22 | 23 | public double getAsJoules() { 24 | return get() * (1 / 20f); 25 | } 26 | 27 | public double getVoltage() { 28 | return volts; 29 | } 30 | 31 | public double getAmps() { 32 | return amps; 33 | } 34 | 35 | public boolean isZero() { 36 | return getVoltage() <= 0 || getAmps() <= 0; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) { 41 | if(this == obj) return true; 42 | if(!(obj instanceof Watt that)) return false; 43 | return Math.abs(this.volts - that.volts) < 0.001f && Math.abs(this.amps - that.amps) < 0.001f; 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | return Objects.hash(volts, amps); 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "Watt[" + volts + "v, " + amps + "A]"; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/functional/VoltageSource.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.functional; 2 | 3 | import com.quattage.mechano.api.ServerGrid; 4 | import com.quattage.mechano.api.grid.VoltageDecay; 5 | import com.quattage.mechano.api.grid.topology.CircuitComponent.StampingComponent; 6 | 7 | public abstract class VoltageSource extends StampingComponent { 8 | 9 | private final VoltageDecay volts; 10 | private int cIndex = -1; 11 | 12 | public VoltageSource(String name, VoltageDecay decayFunction) { 13 | super(name); 14 | this.volts = decayFunction; 15 | } 16 | 17 | public abstract double getStateOfCharge(); 18 | 19 | @Override 20 | public boolean isVoltageSource() { 21 | return true; 22 | } 23 | 24 | @Override 25 | public abstract int getContributionFactor(); 26 | 27 | @Override 28 | public void stamp(ServerGrid grid) { 29 | int pI = terminals[0].getNode().getIndex(); 30 | int nI = terminals[1].getNode().getIndex(); 31 | cIndex = grid.allocateSource(); 32 | if(pI >= 0) { 33 | grid.stampMatrix(pI, cIndex, 1); 34 | grid.stampMatrix(cIndex, pI, 1); 35 | } 36 | if(nI >= 0) { 37 | grid.stampMatrix(nI, cIndex, -1); 38 | grid.stampMatrix(cIndex, nI, -1); 39 | } 40 | grid.stampRHS(cIndex, volts.apply(getStateOfCharge())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoBlocks.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import com.quattage.mechano.content.connector.SingleConnectorBlock; 4 | import com.quattage.mechano.infrastructure.datagen.DynamicStateGenerator; 5 | import com.simibubi.create.foundation.data.ModelGen; 6 | import com.simibubi.create.foundation.data.TagGen; 7 | import com.tterrag.registrate.util.entry.BlockEntry; 8 | import com.tterrag.registrate.util.nullness.NonnullType; 9 | 10 | import net.minecraft.world.level.block.Blocks; 11 | import net.minecraft.world.level.block.state.BlockBehaviour.Properties; 12 | import net.neoforged.bus.api.IEventBus; 13 | 14 | 15 | 16 | public class MechanoBlocks { 17 | 18 | static { 19 | Mechano.REGISTRATE.setCreativeTab(MechanoGroups.BASE); 20 | } 21 | 22 | public static final BlockEntry CONNECTOR_SINGLE = 23 | Mechano.REGISTRATE.block("connector_single", SingleConnectorBlock::new) 24 | .initialProperties(() -> Blocks.IRON_BARS) 25 | .properties(@NonnullType Properties::noOcclusion) 26 | .transform(TagGen.pickaxeOnly()) 27 | .blockstate(new DynamicStateGenerator()::generate) 28 | .item() 29 | .transform(ModelGen.customItemModel("connector_single", "base")) 30 | .register(); 31 | 32 | public static void register(IEventBus modBus) { 33 | Mechano.LOGGER.debug("registering blocks"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/generic/chevron_cutout_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "0": "mechano:block/chev", 5 | "particle": "mechano:block/chev" 6 | }, 7 | "elements": [ 8 | { 9 | "from": [-1, -0.75, -1], 10 | "to": [0, 0.75, 1.75], 11 | "rotation": {"angle": -45, "axis": "y", "origin": [0, 0, 1.75]}, 12 | "faces": { 13 | "north": {"uv": [2, 12, 4, 14], "rotation": 180, "texture": "#0"}, 14 | "east": {"uv": [2, 12, 8, 14], "rotation": 180, "texture": "#0"}, 15 | "south": {"uv": [2, 12, 4, 14], "rotation": 180, "texture": "#0"}, 16 | "west": {"uv": [2, 8, 4, 14], "rotation": 90, "texture": "#0"}, 17 | "up": {"uv": [0, 14, 5.5, 16], "rotation": 270, "texture": "#0"}, 18 | "down": {"uv": [0, 14, 5.5, 16], "rotation": 90, "texture": "#0"} 19 | } 20 | }, 21 | { 22 | "from": [-2.75, -0.75, 0.75], 23 | "to": [-1, 0.75, 1.75], 24 | "rotation": {"angle": -45, "axis": "y", "origin": [0, 0, 1.75]}, 25 | "faces": { 26 | "north": {"uv": [2, 12, 6, 14], "rotation": 180, "texture": "#0"}, 27 | "east": {"uv": [0, 0, 0, 0], "rotation": 270, "texture": "#0"}, 28 | "south": {"uv": [2, 12, 6, 14], "texture": "#0"}, 29 | "west": {"uv": [2, 12, 4, 14], "rotation": 180, "texture": "#0"}, 30 | "up": {"uv": [0, 10, 2, 14], "rotation": 270, "texture": "#0"}, 31 | "down": {"uv": [0, 10, 2, 14], "rotation": 270, "texture": "#0"} 32 | } 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/switchboard/task/JointUnlinkTask.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.switchboard.task; 2 | 3 | import com.quattage.mechano.api.ClientGrid; 4 | import com.quattage.mechano.api.Grid; 5 | import com.quattage.mechano.api.grid.topology.vertex.AncillaryNode; 6 | import com.quattage.mechano.api.switchboard.action.GridAction; 7 | import com.quattage.mechano.api.transmitter.TransmitterType; 8 | import com.quattage.mechano.foundation.tracking.GridUUID; 9 | 10 | import net.minecraft.client.player.LocalPlayer; 11 | 12 | public class JointUnlinkTask extends JointLinkTask { 13 | 14 | @Override 15 | protected void clientSelfHandle(LocalPlayer player, ClientGrid grid, GridAction serverResult) { 16 | // special behaviours for the client caller are not implemented for manual unlinking yet 17 | } 18 | 19 | @Override 20 | protected GridAction unsidedHandle(Grid grid, GridUUID startID, AncillaryNode startNode, GridUUID endID, AncillaryNode endNode, TransmitterType trns) { 21 | GridAction runResult = grid.removeLink(startID, endID); 22 | GridAction runResultInverted = grid.removeLink(endID, startID); 23 | if(runResult.getActionType().indicatesFailure() || runResultInverted.getActionType().indicatesFailure()) { 24 | // always consume the failure case should one exist 25 | if(!runResult.getActionType().indicatesFailure()) 26 | runResult = runResultInverted; 27 | } 28 | return runResult; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /gradle/java.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | java.toolchain.languageVersion = JavaLanguageVersion.of(21) 4 | java.withSourcesJar() 5 | java.withJavadocJar() 6 | 7 | jar { 8 | manifest { 9 | attributes([ 10 | 'Specification-Title' : mod_name, 11 | 'Specification-Vendor' : mod_author, 12 | 'Specification-Version' : project.jar.archiveVersion, 13 | 'Implementation-Title' : project.name, 14 | 'Implementation-Version' : project.jar.archiveVersion, 15 | 'Implementation-Vendor' : mod_author, 16 | 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), 17 | 'Timestampe' : System.currentTimeMillis(), 18 | 'Built-On-Java' : "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", 19 | 'Built-On-Minecraft' : minecraft_version 20 | ]) 21 | } 22 | } 23 | 24 | tasks.withType(JavaCompile).configureEach { 25 | it.options.encoding = 'UTF-8' 26 | it.options.release = 21 27 | } 28 | 29 | // Disables Gradle's custom module metadata from being published to maven. The 30 | // metadata includes mapped dependencies which are not reasonably consumable by 31 | // other mod developers. 32 | tasks.withType(GenerateModuleMetadata) { 33 | enabled = false 34 | } 35 | 36 | javadoc { 37 | // Suppress annoying warnings when generating JavaDoc files. 38 | options.addStringOption('Xdoclint:none', '-quiet') 39 | } 40 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/voltometer/needle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 32], 4 | "textures": { 5 | "0": "mechano:block/voltometer", 6 | "particle": "mechano:block/voltometer" 7 | }, 8 | "elements": [ 9 | { 10 | "name": "needle", 11 | "from": [8, 5, 0.25], 12 | "to": [13, 6, 1.25], 13 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 5.5, 0]}, 14 | "faces": { 15 | "north": {"uv": [9, 15, 11.5, 15.5], "texture": "#0"}, 16 | "east": {"uv": [9, 15, 9.5, 15.5], "texture": "#0"}, 17 | "west": {"uv": [9, 15, 9.5, 15.5], "texture": "#0"}, 18 | "up": {"uv": [11.5, 15, 9, 15.5], "texture": "#0"}, 19 | "down": {"uv": [11.5, 15, 9, 15.5], "texture": "#0"} 20 | } 21 | } 22 | ], 23 | "display": { 24 | "thirdperson_righthand": { 25 | "rotation": [75, 45, 0], 26 | "translation": [0, 2.5, 0], 27 | "scale": [0.375, 0.375, 0.375] 28 | }, 29 | "thirdperson_lefthand": { 30 | "rotation": [75, 45, 0], 31 | "translation": [0, 2.5, 0], 32 | "scale": [0.375, 0.375, 0.375] 33 | }, 34 | "firstperson_righthand": { 35 | "rotation": [0, 45, 0], 36 | "scale": [0.4, 0.4, 0.4] 37 | }, 38 | "firstperson_lefthand": { 39 | "rotation": [0, 225, 0], 40 | "scale": [0.4, 0.4, 0.4] 41 | }, 42 | "ground": { 43 | "translation": [0, 3, 0], 44 | "scale": [0.25, 0.25, 0.25] 45 | }, 46 | "gui": { 47 | "rotation": [30, 130, 0], 48 | "scale": [0.625, 0.625, 0.625] 49 | }, 50 | "fixed": { 51 | "scale": [0.5, 0.5, 0.5] 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/CreativeTabOverridable.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import net.minecraft.world.item.CreativeModeTab; 6 | import net.minecraft.world.item.Item; 7 | import net.neoforged.neoforge.registries.DeferredHolder; 8 | 9 | /*** 10 | * Allows Items or BlockItems to override their creative tab or suppress their visibility entirely. 11 | * Blocks/Items that do not implement this interface are added to MechanoGroups.BASE 12 | * by default. 13 | */ 14 | public interface CreativeTabOverridable { 15 | 16 | /*** 17 | * Define an overridden creative mode tab to put this block/item into. 18 | * @return A DeferredHolder registry object containing the tab's registry, or null if 19 | * this block/item shouldn't appear in the creative menu at all. 20 | */ 21 | public default @Nullable DeferredHolder getTab() { 22 | return null; 23 | } 24 | 25 | /*** 26 | * Checks whether or not the given item is configured to be placed in the 27 | * provided creative mode tab 28 | * @param item Item to check 29 | * @param tab Registry object containing a creative mode tab 30 | * @return true if the provided item belongs to the tab 31 | */ 32 | public static boolean belongsTo(Item item, DeferredHolder tab) { 33 | if(!(item instanceof CreativeTabOverridable cto)) return true; 34 | if(cto.getTab() == null) return false; 35 | return cto.getTab().equals(tab); 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/functional/Capacitor.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.functional; 2 | 3 | import com.quattage.mechano.api.ServerGrid; 4 | import com.quattage.mechano.api.grid.topology.Circuit; 5 | import com.quattage.mechano.api.grid.topology.CircuitComponent.StampingComponent; 6 | import com.quattage.mechano.api.grid.topology.vertex.Terminal; 7 | 8 | public class Capacitor extends StampingComponent { 9 | 10 | private final float capacitance; 11 | private double prevVoltage; 12 | 13 | public Capacitor(float capacitance) { 14 | super("Capacitor"); 15 | this.capacitance = capacitance; 16 | } 17 | 18 | @Override 19 | protected Terminal[] defineTerminals() { 20 | return Terminal.polarPair(this); 21 | } 22 | 23 | @Override 24 | public void stamp(ServerGrid grid) { 25 | prevVoltage = terminals[0].getNode().getVoltage() - terminals[1].getNode().getVoltage(); 26 | double g = (double)capacitance / Circuit.DELTA; 27 | int pI = terminals[0].getNode().getIndex(); 28 | int nI = terminals[1].getNode().getIndex(); 29 | double ieq = g * prevVoltage; 30 | if(pI >= 0) { 31 | grid.stampMatrix(pI, pI, g); 32 | grid.stampRHS(pI, ieq); 33 | } 34 | if(nI >= 0) { 35 | grid.stampMatrix(nI, nI, g); 36 | grid.stampRHS(nI, ieq); 37 | } 38 | if(pI >= 0 && nI >= 0) { 39 | grid.stampMatrix(pI, nI, -g); 40 | grid.stampMatrix(nI, pI, -g); 41 | } 42 | } 43 | 44 | @Override 45 | public boolean isVoltageSource() { 46 | return false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/MechanoRegistrate.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation; 2 | 3 | 4 | import com.quattage.mechano.Mechano; 5 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 6 | import com.quattage.mechano.api.transmitter.TransmitterBuilder; 7 | import com.quattage.mechano.api.transmitter.TransmitterType; 8 | import com.simibubi.create.foundation.data.CreateRegistrate; 9 | 10 | import net.minecraft.core.Registry; 11 | import net.minecraft.resources.ResourceKey; 12 | import net.minecraft.resources.ResourceLocation; 13 | import net.neoforged.neoforge.registries.RegistryBuilder; 14 | 15 | public final class MechanoRegistrate extends CreateRegistrate { 16 | 17 | public static final ResourceKey>> 18 | TRANSMITTER_KEY = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(Mechano.ID, "transmitter_type")); 19 | 20 | public static MechanoRegistrate make(String modid) { 21 | return new MechanoRegistrate(modid); 22 | } 23 | 24 | protected MechanoRegistrate(String modid) { 25 | super(modid); 26 | makeRegistry("transmitter_type", this::supplyTransmitterRegistry); 27 | } 28 | 29 | private RegistryBuilder> supplyTransmitterRegistry(ResourceKey>> key) { 30 | return new RegistryBuilder>(key).maxId(256).sync(true); 31 | } 32 | 33 | public TransmitterBuilder transmitter(String name) { 34 | return entry(name, callback -> TransmitterBuilder.create(self(), name, callback, MechanoRegistrate.TRANSMITTER_KEY)); 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/functional/Resistor.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.functional; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import com.quattage.mechano.api.ServerGrid; 6 | import com.quattage.mechano.api.grid.topology.CircuitComponent; 7 | import com.quattage.mechano.api.grid.topology.CircuitComponent.StampingComponent; 8 | import com.quattage.mechano.api.grid.topology.vertex.Terminal; 9 | 10 | public class Resistor extends StampingComponent { 11 | 12 | private final float ohms; 13 | 14 | public Resistor(float ohms) { 15 | super("Resistor"); 16 | this.ohms = ohms; 17 | } 18 | 19 | @Override 20 | protected Terminal[] defineTerminals() { 21 | return Terminal.pair(this); 22 | } 23 | 24 | @Override 25 | public @Nullable CircuitComponent getParentComponent() { 26 | return terminals[0].getParentComponent(); 27 | } 28 | 29 | @Override 30 | public boolean isVoltageSource() { 31 | return false; 32 | } 33 | 34 | @Override 35 | public void stamp(ServerGrid grid) { 36 | double g = 1d / (double)ohms; 37 | int aI = terminals[0].getNode().getIndex(); 38 | int bI = terminals[0].getNode().getIndex(); 39 | if(aI >= 0) grid.stampMatrix(aI, aI, g); 40 | if(bI >- 0) grid.stampMatrix(bI, bI, g); 41 | if(aI >= 0 && bI >= 0) { 42 | grid.stampMatrix(aI, bI, -g); 43 | grid.stampMatrix(bI, aI, -g); 44 | } 45 | } 46 | 47 | @Override 48 | public @Nullable Terminal pinA() { 49 | return terminals[0]; 50 | } 51 | 52 | @Override 53 | public @Nullable Terminal pinB() { 54 | return terminals[1]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/corner_middle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [10, 0, 0], 6 | "to": [16, 12, 16], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, 8 | "color": 2, 9 | "faces": { 10 | "north": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 11 | "east": {"uv": [44, 26, 28, 32], "texture": "#missing"}, 12 | "south": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 13 | "west": {"uv": [40, 56, 55, 50], "texture": "#missing"}, 14 | "up": {"uv": [16, 22, 30, 16], "rotation": 90, "texture": "#missing"}, 15 | "down": {"uv": [13, 31, 28, 37], "rotation": 90, "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [0, 10, 0], 20 | "to": [12, 16, 16], 21 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, 22 | "color": 1, 23 | "faces": { 24 | "north": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 25 | "east": {"uv": [0, 0, 14, 6], "texture": "#missing"}, 26 | "south": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 27 | "west": {"uv": [13, 37, 28, 31], "texture": "#missing"}, 28 | "up": {"uv": [28, 26, 44, 32], "rotation": 90, "texture": "#missing"}, 29 | "down": {"uv": [54, 56, 40, 50], "rotation": 90, "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [4, 4, 0], 34 | "to": [10, 10, 16], 35 | "color": 9, 36 | "faces": { 37 | "north": {"uv": [0, 0, 6, 6], "texture": "#missing"}, 38 | "east": {"uv": [0, 0, 16, 6], "texture": "#missing"}, 39 | "south": {"uv": [0, 0, 6, 6], "texture": "#missing"}, 40 | "west": {"uv": [0, 0, 16, 6], "texture": "#missing"}, 41 | "up": {"uv": [0, 0, 6, 16], "texture": "#missing"}, 42 | "down": {"uv": [0, 0, 6, 16], "texture": "#missing"} 43 | } 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/numeric/Duo.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.numeric; 2 | 3 | import java.util.Objects; 4 | 5 | public class Duo { 6 | 7 | private final T a; 8 | private final T b; 9 | 10 | public static Duo of(T a, T b) { 11 | return new Duo(a, b); 12 | } 13 | 14 | private Duo(T a, T b) { 15 | this.a = a; this.b = b; 16 | } 17 | 18 | /** @return {@link #a() member A} */ 19 | public T a() { return a; } 20 | /** @return {@link #a() member A} */ 21 | public T x() { return a(); } 22 | /** @return {@link #a() member A} */ 23 | public T first() { return a(); } 24 | /** @return {@link #a() member A} */ 25 | public T key() { return a(); } 26 | /** @return {@link #a() member A} */ 27 | public T left() { return a(); } 28 | /** @return {@link #a() member A} */ 29 | public T start() { return a(); } 30 | 31 | /** @return {@link #b() member B} */ 32 | public T b() { return b; } 33 | /** @return {@link #b() member B} */ 34 | public T y() { return b(); } 35 | /** @return {@link #b() member B} */ 36 | public T second() { return b(); } 37 | /** @return {@link #b() member B} */ 38 | public T value() { return b(); } 39 | /** @return {@link #b() member B} */ 40 | public T right() { return b(); } 41 | /** @return {@link #b() member B} */ 42 | public T end() { return b(); } 43 | 44 | 45 | @Override 46 | public boolean equals(Object obj) { 47 | if(this == obj) return true; 48 | if(!(obj instanceof Duo that)) return false; 49 | return this.a.equals(that.a) && this.b.equals(that.b); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | return Objects.hash(this.a.hashCode(), this.b.hashCode()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/mixin/client/ItemInHandRendererMixin.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.mixin.client; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import com.quattage.mechano.foundation.MapLikeItemHoldable; 10 | 11 | import net.minecraft.client.player.AbstractClientPlayer; 12 | import net.minecraft.client.renderer.ItemInHandRenderer; 13 | import net.minecraft.client.renderer.MultiBufferSource; 14 | import net.minecraft.world.InteractionHand; 15 | import net.minecraft.world.item.ItemStack; 16 | 17 | @Mixin(ItemInHandRenderer.class) 18 | public abstract class ItemInHandRendererMixin { 19 | 20 | @Inject(method = "renderArmWithItem(Lnet/minecraft/client/player/AbstractClientPlayer;FFLnet/minecraft/world/InteractionHand;FLnet/minecraft/world/item/ItemStack;FLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = {@At(value = "HEAD")}, cancellable = true) 21 | public void mechano$specialSpoolFirstPersonRendering(AbstractClientPlayer player, float partialTicks, float pitch, InteractionHand hand, float swingProgress, ItemStack stack, float equippedProgress, PoseStack poseStack, MultiBufferSource buffer, int combinedLight, CallbackInfo info) { 22 | // TODO registry for this 23 | if(!(stack.getItem() instanceof MapLikeItemHoldable mlih)) return; 24 | if(!mlih.shouldRenderSpecial(stack)) return; 25 | if(MapLikeItemHoldable.renderInHands(stack, buffer, poseStack, player, (ItemInHandRenderer)(Object)this, swingProgress, equippedProgress, pitch, partialTicks, combinedLight)); 26 | info.cancel(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoData.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.quattage.mechano; 4 | 5 | 6 | import java.util.function.Supplier; 7 | 8 | import com.quattage.mechano.api.Grid; 9 | import com.quattage.mechano.foundation.tracking.GridUUID; 10 | import com.quattage.mechano.foundation.tracking.UUIDSourceType; 11 | 12 | import net.minecraft.core.component.DataComponentType; 13 | import net.minecraft.core.registries.Registries; 14 | import net.neoforged.bus.api.IEventBus; 15 | import net.neoforged.neoforge.attachment.AttachmentType; 16 | import net.neoforged.neoforge.registries.DeferredHolder; 17 | import net.neoforged.neoforge.registries.DeferredRegister; 18 | import net.neoforged.neoforge.registries.NeoForgeRegistries; 19 | 20 | 21 | // DataComponents, DataAttachments, and Capabilities 22 | public class MechanoData { 23 | 24 | private static final DeferredRegister> ATTACHMENT_REGISTRY = DeferredRegister.create( 25 | NeoForgeRegistries.ATTACHMENT_TYPES, Mechano.ID); 26 | public static final DeferredRegister.DataComponents COMPONENT_REGISTRY = DeferredRegister.createDataComponents( 27 | Registries.DATA_COMPONENT_TYPE, Mechano.ID); 28 | 29 | public static final Supplier> GRID 30 | = MechanoData.ATTACHMENT_REGISTRY.register( 31 | Mechano.ID + "_grid", () -> AttachmentType 32 | .builder(Grid::createNew) 33 | .build() 34 | ); 35 | 36 | public static final DeferredHolder, DataComponentType> UUID = 37 | MechanoData.COMPONENT_REGISTRY.registerComponentType( 38 | "grid_identifier", 39 | b -> b.persistent(UUIDSourceType.CODEC).networkSynchronized(UUIDSourceType.STREAM_CODEC) 40 | ); 41 | 42 | 43 | public static void register(IEventBus modBus) { 44 | MechanoData.ATTACHMENT_REGISTRY.register(modBus); 45 | MechanoData.COMPONENT_REGISTRY.register(modBus); 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/base_middle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [0, 0, 0], 6 | "to": [16, 7, 16], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 8 | "faces": { 9 | "east": {"uv": [28, 0, 44, 7], "texture": "#missing"}, 10 | "west": {"uv": [44, 0, 28, 7], "texture": "#missing"}, 11 | "up": {"uv": [16, 64, 0, 48], "rotation": 270, "texture": "#missing"}, 12 | "down": {"uv": [44, 6, 28, 22], "rotation": 90, "texture": "#missing"} 13 | } 14 | }, 15 | { 16 | "from": [0, 7, 4], 17 | "to": [3, 9, 12], 18 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 19 | "faces": { 20 | "north": {"uv": [10, 64, 12, 61], "rotation": 90, "texture": "#missing"}, 21 | "south": {"uv": [4, 61, 6, 64], "rotation": 90, "texture": "#missing"}, 22 | "west": {"uv": [4, 62, 12, 64], "texture": "#missing"}, 23 | "up": {"uv": [4, 61, 12, 64], "rotation": 90, "texture": "#missing"} 24 | } 25 | }, 26 | { 27 | "from": [3, 7, 0], 28 | "to": [13, 10, 16], 29 | "faces": { 30 | "north": {"uv": [26, 48, 36, 51], "texture": "#missing"}, 31 | "east": {"uv": [22, 48, 25, 64], "rotation": 90, "texture": "#missing"}, 32 | "west": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 33 | "up": {"uv": [16, 48, 26, 64], "texture": "#missing"}, 34 | "down": {"uv": [0, 0, 10, 16], "texture": "#missing"} 35 | } 36 | }, 37 | { 38 | "from": [13, 7, 4], 39 | "to": [16, 9, 12], 40 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 41 | "faces": { 42 | "north": {"uv": [10, 51, 12, 48], "rotation": 90, "texture": "#missing"}, 43 | "east": {"uv": [4, 50, 12, 48], "texture": "#missing"}, 44 | "south": {"uv": [4, 48, 6, 51], "rotation": 90, "texture": "#missing"}, 45 | "up": {"uv": [4, 51, 12, 48], "rotation": 270, "texture": "#missing"} 46 | } 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/switchboard/action/GridTaskExecuteEvent.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.switchboard.action; 2 | 3 | import com.quattage.mechano.api.ClientGrid; 4 | import com.quattage.mechano.api.Grid; 5 | import com.quattage.mechano.api.ServerGrid; 6 | 7 | import net.neoforged.bus.api.Event; 8 | import net.neoforged.bus.api.ICancellableEvent; 9 | 10 | /** 11 | * This event is fired just before a {@link ActionTask grid action task} is executed 12 | * on both sides. This event is cancellable. 13 | */ 14 | public abstract class GridTaskExecuteEvent extends Event implements ICancellableEvent { 15 | 16 | private final Grid grid; 17 | private final GridAction action; 18 | 19 | public GridTaskExecuteEvent(Grid grid, GridAction action) { 20 | this.grid = grid; 21 | this.action = action; 22 | } 23 | 24 | @SuppressWarnings("unchecked") protected T getGrid() { return (T)grid; } 25 | protected GridAction getAction() { return action; } 26 | protected ActionType getActionType() { return action.getActionType(); } 27 | 28 | @Override 29 | public void setCanceled(boolean canceled) { 30 | ICancellableEvent.super.setCanceled(canceled); 31 | } 32 | 33 | /** 34 | * Fired every time a {@link ActionTask grid action task} is executed 35 | * on the client. This event is called before the task has 36 | * started executing, and can be used to modify or cancel the task. 37 | */ 38 | public static class Client extends GridTaskExecuteEvent { 39 | public Client(ClientGrid grid, GridAction action) { 40 | super(grid, action); 41 | } 42 | } 43 | /** 44 | * Fired every time a {@link ActionTask grid action task} is executed 45 | * on the server. This event is called before the action has 46 | * started executing, and can be used to modify or cancel the task. 47 | */ 48 | public static class Server extends GridTaskExecuteEvent { 49 | public Server(ServerGrid grid, GridAction action) { 50 | super(grid, action); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoPackets.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.quattage.mechano; 4 | 5 | import java.util.Locale; 6 | 7 | import com.quattage.mechano.api.switchboard.GridActionC2SPacket; 8 | import com.quattage.mechano.api.switchboard.GridActionS2CPacket; 9 | 10 | import net.createmod.catnip.net.base.BasePacketPayload; 11 | import net.createmod.catnip.net.base.CatnipPacketRegistry; 12 | import net.minecraft.network.RegistryFriendlyByteBuf; 13 | import net.minecraft.network.codec.StreamCodec; 14 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 15 | import net.minecraft.util.StringRepresentable; 16 | import net.neoforged.bus.api.IEventBus; 17 | 18 | public enum MechanoPackets implements BasePacketPayload.PacketTypeProvider, StringRepresentable { 19 | 20 | GRID_ACTION_S2C(GridActionS2CPacket.class, GridActionS2CPacket.STREAM_CODEC), 21 | GRID_ACTION_C2S(GridActionC2SPacket.class, GridActionC2SPacket.STREAM_CODEC), 22 | ; 23 | 24 | @Override 25 | @SuppressWarnings("unchecked") 26 | public CustomPacketPayload.Type getType() { 27 | return (CustomPacketPayload.Type)this.type.type(); 28 | } 29 | 30 | 31 | private final CatnipPacketRegistry.PacketType type; 32 | 33 | MechanoPackets(Class cl, StreamCodec codec) { 34 | this.type = new CatnipPacketRegistry.PacketType<>( 35 | new CustomPacketPayload.Type<>(Mechano.asResource(this.toString())), 36 | cl, codec 37 | ); 38 | } 39 | 40 | public static void register(IEventBus modBus) { 41 | CatnipPacketRegistry registrar = new CatnipPacketRegistry(Mechano.ID, MechanoBuildParameters.VERSION); 42 | for(MechanoPackets packet : MechanoPackets.values()) 43 | registrar.registerPacket(packet.type); 44 | registrar.registerAllPackets(); 45 | } 46 | 47 | @Override 48 | public String getSerializedName() { 49 | return name().toLowerCase(Locale.ROOT); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return getSerializedName(); 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/datagen/SpoolDataProvider.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.datagen; 2 | 3 | import com.quattage.mechano.api.transmitter.SpoolItem; 4 | import com.quattage.mechano.foundation.item.MechanoItemProperties; 5 | import com.tterrag.registrate.providers.DataGenContext; 6 | import com.tterrag.registrate.providers.RegistrateItemModelProvider; 7 | 8 | import net.minecraft.resources.ResourceLocation; 9 | import net.minecraft.world.item.Item; 10 | import net.neoforged.neoforge.client.model.generators.ModelFile; 11 | 12 | public class SpoolDataProvider { 13 | 14 | public static void generate(DataGenContext ctx, RegistrateItemModelProvider prov) { 15 | ResourceLocation loc = ctx.getId(); 16 | ModelFile generated = new ModelFile.UncheckedModelFile("item/generated"); 17 | ResourceLocation full = ResourceLocation.fromNamespaceAndPath(loc.getNamespace(), loc.getPath() + "_full"); 18 | ResourceLocation depleted = ResourceLocation.fromNamespaceAndPath(loc.getNamespace(), loc.getPath() + "_depleted"); 19 | ResourceLocation empty = ResourceLocation.fromNamespaceAndPath(loc.getNamespace(), loc.getPath() + "_empty"); 20 | ResourceLocation totallyEmpty = ResourceLocation.fromNamespaceAndPath(loc.getNamespace(), "spool_empty"); 21 | prov.getBuilder(full.toString()).parent(generated).texture("layer0", ResourceLocation.fromNamespaceAndPath(loc.getNamespace(), "item/" + loc.getPath())); 22 | prov.basicItem(depleted); 23 | prov.basicItem(empty); 24 | prov.getBuilder(loc.toString()).parent(generated) 25 | .override().predicate(MechanoItemProperties.FULLNESS, 0.00f).model(prov.getExistingFile(totallyEmpty)).end() 26 | .override().predicate(MechanoItemProperties.FULLNESS, 0.10f).model(prov.getExistingFile(empty)).end() 27 | .override().predicate(MechanoItemProperties.FULLNESS, 0.50f).model(prov.getExistingFile(depleted)).end() 28 | .override().predicate(MechanoItemProperties.FULLNESS, 0.75f).model(prov.getExistingFile(full)).end() 29 | .override().predicate(MechanoItemProperties.FULLNESS, 0.999f).model(prov.getExistingFile(full)).end(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/datagen/MechanoDataGen.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.datagen; 2 | 3 | import java.util.Map; 4 | 5 | import com.google.gson.JsonElement; 6 | import com.quattage.mechano.Mechano; 7 | import com.simibubi.create.foundation.utility.FilesHelper; 8 | import com.tterrag.registrate.providers.ProviderType; 9 | import com.tterrag.registrate.providers.RegistrateLangProvider; 10 | 11 | import net.minecraft.data.DataGenerator; 12 | import net.neoforged.bus.api.IEventBus; 13 | import net.neoforged.neoforge.data.event.GatherDataEvent; 14 | 15 | public class MechanoDatagen { 16 | 17 | public static final String[] targetFilenames = new String[] { 18 | "ui" 19 | }; 20 | 21 | public static void collectHighPriority(GatherDataEvent event) { 22 | if(!event.getMods().contains(Mechano.ID)) return; 23 | Mechano.REGISTRATE.addDataGenerator(ProviderType.LANG, provider -> { 24 | MechanoDatagen.provideDefaultLang(provider); 25 | }); 26 | } 27 | 28 | private static void provideDefaultLang(RegistrateLangProvider prov) { 29 | for(int x = 0; x < MechanoDatagen.targetFilenames.length; x++) { 30 | String path = "assets/" + Mechano.ID + "/lang/custom/" + MechanoDatagen.targetFilenames[x] + ".json"; 31 | Mechano.LOGGER.debug("Loading data from '" + path + "'"); 32 | JsonElement file = FilesHelper.loadJsonResource(path); 33 | if(file == null) { 34 | Mechano.LOGGER.error("Couldn't merge lang file '" + path + "' - File couldn't be found!"); 35 | return; 36 | } 37 | for(Map.Entry element : file.getAsJsonObject().entrySet()) { 38 | if(element == null) continue; 39 | prov.add(element.getKey(), element.getValue().getAsString()); 40 | } 41 | } 42 | } 43 | 44 | public static void collectLowPriority(GatherDataEvent event) { 45 | if(!event.getMods().contains(Mechano.ID)) return; 46 | DataGenerator generator = event.getGenerator(); 47 | generator.addProvider(event.includeDev(), new HitboxDataProvider(generator, event)); 48 | } 49 | 50 | public static void register(IEventBus modBus) {} 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/switchboard/task/GridDumpTask.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.switchboard.task; 2 | 3 | import java.util.UUID; 4 | 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import com.quattage.mechano.api.ClientGrid; 8 | import com.quattage.mechano.api.ServerGrid; 9 | import com.quattage.mechano.api.switchboard.action.ActionTask; 10 | import com.quattage.mechano.api.switchboard.action.GridAction; 11 | 12 | import io.netty.buffer.ByteBuf; 13 | import net.minecraft.ChatFormatting; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.network.chat.MutableComponent; 16 | import net.minecraft.server.level.ServerPlayer; 17 | import net.neoforged.api.distmarker.Dist; 18 | import net.neoforged.api.distmarker.OnlyIn; 19 | 20 | public class GridDumpTask implements ActionTask { 21 | 22 | @Override 23 | public @Nullable Class[] getArgumentTemplate() { 24 | return new Class[] { UUID.class }; 25 | } 26 | 27 | @Override 28 | public void dynamicEncode(Object[] args, ByteBuf buffer) { 29 | buffer.writeLong(((UUID)args[0]).getMostSignificantBits()); 30 | buffer.writeLong(((UUID)args[0]).getLeastSignificantBits()); 31 | } 32 | 33 | @Override 34 | public @Nullable Object[] dynamicDecode(ByteBuf buffer) { 35 | return new Object[] { new UUID(buffer.readLong(), buffer.readLong()) }; 36 | } 37 | 38 | @Override 39 | public GridAction executeAsServer(int attempt, ServerGrid grid, Object... args) { 40 | ServerPlayer sp = grid.getServer().getPlayerList().getPlayer((UUID)args[0]); 41 | if(sp == null) return GridAction.RESPONSE_FAIL_GENERIC; 42 | MutableComponent message = Component.literal("-- Server-sided dump:\n" + grid.writeAllLinks() + "\n--").withStyle(ChatFormatting.GRAY); 43 | sp.sendSystemMessage(message); 44 | return GridAction.TASK_GRID_DUMP; 45 | } 46 | 47 | @Override 48 | @OnlyIn(Dist.CLIENT) 49 | public GridAction executeAsClient(int attempt, ClientGrid grid, Object... args) { 50 | MutableComponent message = Component.literal("-- Client-sided dump:\n" + grid.writeAllLinks() + "\n--").withStyle(ChatFormatting.GRAY); 51 | self().sendSystemMessage(message); 52 | return GridAction.RESPONSE_SUCCESS; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/base_end_a.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [0, 0, 1], 6 | "to": [16, 7, 16], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 8 | "faces": { 9 | "north": {"uv": [0, 53, 16, 60], "texture": "#missing"}, 10 | "east": {"uv": [28, 0, 13, 7], "texture": "#missing"}, 11 | "west": {"uv": [13, 0, 28, 7], "texture": "#missing"}, 12 | "up": {"uv": [15, 64, 0, 48], "rotation": 90, "texture": "#missing"}, 13 | "down": {"uv": [28, 6, 13, 22], "rotation": 90, "texture": "#missing"} 14 | } 15 | }, 16 | { 17 | "from": [0, 7, 4], 18 | "to": [3, 9, 12], 19 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 20 | "faces": { 21 | "north": {"uv": [10, 64, 12, 61], "rotation": 90, "texture": "#missing"}, 22 | "south": {"uv": [4, 61, 6, 64], "rotation": 90, "texture": "#missing"}, 23 | "west": {"uv": [4, 62, 12, 64], "texture": "#missing"}, 24 | "up": {"uv": [4, 61, 12, 64], "rotation": 90, "texture": "#missing"} 25 | } 26 | }, 27 | { 28 | "from": [13, 7, 4], 29 | "to": [16, 9, 12], 30 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 31 | "faces": { 32 | "north": {"uv": [10, 51, 12, 48], "rotation": 90, "texture": "#missing"}, 33 | "east": {"uv": [4, 50, 12, 48], "texture": "#missing"}, 34 | "south": {"uv": [4, 48, 6, 51], "rotation": 90, "texture": "#missing"}, 35 | "up": {"uv": [4, 51, 12, 48], "rotation": 270, "texture": "#missing"} 36 | } 37 | }, 38 | { 39 | "from": [3, 7, 0], 40 | "to": [13, 10, 16], 41 | "faces": { 42 | "north": {"uv": [26, 48, 36, 51], "texture": "#missing"}, 43 | "east": {"uv": [22, 48, 25, 64], "rotation": 90, "texture": "#missing"}, 44 | "west": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 45 | "up": {"uv": [16, 48, 26, 64], "texture": "#missing"}, 46 | "down": {"uv": [16, 48, 26, 64], "texture": "#missing"} 47 | } 48 | }, 49 | { 50 | "from": [6, 4, 0], 51 | "to": [10, 7, 16], 52 | "faces": { 53 | "north": {"uv": [26, 48, 30, 51], "texture": "#missing"}, 54 | "east": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 55 | "west": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 56 | "down": {"uv": [16, 48, 20, 64], "texture": "#missing"} 57 | } 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/orientation/RelativeDirection.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block.orientation; 2 | 3 | import net.minecraft.core.Direction; 4 | 5 | public class RelativeDirection implements OrientationUpdatable { 6 | 7 | private final Relative rel; 8 | private Direction facingDir; 9 | 10 | public static RelativeDirection[] all() { 11 | Relative[] allRels = Relative.values(); 12 | RelativeDirection[] out = new RelativeDirection[allRels.length]; 13 | for(int x = 0; x < out.length; x++) 14 | out[x] = new RelativeDirection(allRels[x]); 15 | return out; 16 | } 17 | 18 | public RelativeDirection(Relative rel) { 19 | this.rel = rel; 20 | this.facingDir = rel.getDefaultDir(); 21 | } 22 | 23 | @Override 24 | public void updateOrientation(CombinedOrientation dir) { 25 | this.facingDir = rel.apply(dir); 26 | } 27 | 28 | /** 29 | * Gets this RelativeDirection's actual, globally-oriented 30 | * {@link Direction} in its current state. 31 | * @param dir (Optional) a {@link CombinedOrientation} to update this RelativeDirection with 32 | * @return The current global facing {@link Direction} that this RelativeDirection represents 33 | * @see #updateOrientation 34 | */ 35 | public Direction get() { 36 | return facingDir; 37 | } 38 | 39 | /** 40 | * Gets this RelativeDirection's actual, globally-oriented 41 | * {@link Direction} in its current state. 42 | * @param dir (Optional) a {@link CombinedOrientation} to update this RelativeDirection with 43 | * @return The current global facing {@link Direction} that this RelativeDirection represents 44 | * @see #updateOrientation 45 | */ 46 | public Direction get(CombinedOrientation dir) { 47 | updateOrientation(dir); 48 | return facingDir; 49 | } 50 | 51 | public Relative getRaw() { 52 | return rel; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return facingDir + " [relative to local " + rel + "]"; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if(this == obj) return true; 63 | if(!(obj instanceof RelativeDirection that)) return false; 64 | return this.rel == that.rel; 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return this.rel.hashCode(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/lang/en_ud.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.mechano.connector_single": "ǝןbuıS ɹoʇɔǝuuoƆ", 3 | "item.mechano.spool_empty": "ʎʇdɯƎ ןoodS", 4 | "item.mechano.spool_hookup": "dnʞooH ןoodS", 5 | "itemGroup.mechano.main": "ouɐɥɔǝW", 6 | "mechano.generic.unit.gigawatthours": "ɥM⅁", 7 | "mechano.generic.unit.gigawatts": "M⅁", 8 | "mechano.generic.unit.kilowatthours": "ɥMʞ", 9 | "mechano.generic.unit.kilowatts": "Mʞ", 10 | "mechano.generic.unit.pertick": "ʇ/", 11 | "mechano.generic.unit.watthours": "ɥM", 12 | "mechano.generic.unit.watts": "M", 13 | "mechano.gui.alternator.status.badCoverage": "ǝbɐɹǝʌoɔ ʇuǝıɔıɟɟnsuI", 14 | "mechano.gui.alternator.status.decentCoverage": "ǝbɐɹǝʌoɔ ǝןqɐssɐԀ", 15 | "mechano.gui.alternator.status.detailedTitle": ":ʎןqɯǝssⱯ", 16 | "mechano.gui.alternator.status.dimensions": "suoısuǝɯıᗡ", 17 | "mechano.gui.alternator.status.hasLength": "ɹoʇoɹ Ɩ ʇsɐǝן ʇɐ sɐH", 18 | "mechano.gui.alternator.status.hasOpposing": "ʇɟɐɥS buıᴚ dıןS buısoddo sɐH", 19 | "mechano.gui.alternator.status.headroom": ":ɯooɹpɐǝH", 20 | "mechano.gui.alternator.status.max": "˙xɐɯ", 21 | "mechano.gui.alternator.status.noLength": "ɹoʇoɹ Ɩ ʇsɐǝן ʇɐ spǝǝN", 22 | "mechano.gui.alternator.status.noOpposing": "ʇɟɐɥS buıᴚ dıןS buısoddo spǝǝN", 23 | "mechano.gui.alternator.status.perfectCoverage": "ǝbɐɹǝʌoɔ ʇɔǝɟɹǝԀ", 24 | "mechano.gui.alternator.status.prediction.in": " :uı", 25 | "mechano.gui.alternator.status.prediction.out": " :ʇno", 26 | "mechano.gui.alternator.status.predictiveSubtitle": ":uoıʇɐɹnbıɟuoɔ sıɥʇ ɥʇıM", 27 | "mechano.gui.alternator.status.predictiveTitle": ":ןɐıʇuǝʇoԀ", 28 | "mechano.gui.alternator.status.stators": "sɹoʇɐʇS", 29 | "mechano.gui.alternator.status.statusTitle": ":snʇɐʇS", 30 | "mechano.gui.alternator.status.title": ":uoıʇɐɯɹoɟuI ɹoʇɐuɹǝʇןⱯ", 31 | "mechano.gui.alternator.status.unfinishedTitle": ":ʇsıןʞɔǝɥƆ ʎןqɯǝssⱯ", 32 | "mechano.gui.anchorpoint.status.connections": "sǝɹıʍ pǝɥɔɐʇʇɐ", 33 | "mechano.gui.anchorpoint.status.title": ":uoıʇɐɯɹoɟuI ɹoʇɔǝuuoƆ", 34 | "mechano.gui.connector.mode": "ǝpoW ɹoʇɔǝuuoƆ", 35 | "mechano.gui.connector.mode.pull_in": "⯇", 36 | "mechano.gui.connector.mode.push_out": "⯈", 37 | "mechano.gui.generic.consuming": "buıɯnsuoƆ", 38 | "mechano.gui.generic.converting": "buıʇɹǝʌuoƆ", 39 | "mechano.gui.generic.into": "oʇuı", 40 | "mechano.gui.generic.moreinfo": "˙˙˙oɟuı ǝɹoɯ ɹoɟ ʞɐǝuS", 41 | "mechano.gui.generic.producing": "buıɔnpoɹԀ", 42 | "mechano.gui.slipring.state.child": "pןıɥƆ", 43 | "mechano.gui.slipring.state.invalid": "pıןɐʌuI", 44 | "mechano.gui.slipring.state.parent": "ʇuǝɹɐԀ", 45 | "mechano.gui.slipring.state.perfect": "ʇɔǝɟɹǝԀ", 46 | "mechano.gui.voltometer.no_power": "ɹǝʍoԀ oN" 47 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/mechano/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.mechano.connector_single": "Connector Single", 3 | "item.mechano.spool_empty": "Spool Empty", 4 | "item.mechano.spool_hookup": "Spool Hookup", 5 | "itemGroup.mechano.main": "Mechano", 6 | "mechano.generic.unit.gigawatthours": "GWh", 7 | "mechano.generic.unit.gigawatts": "GW", 8 | "mechano.generic.unit.kilowatthours": "kWh", 9 | "mechano.generic.unit.kilowatts": "kW", 10 | "mechano.generic.unit.pertick": "/t", 11 | "mechano.generic.unit.watthours": "Wh", 12 | "mechano.generic.unit.watts": "W", 13 | "mechano.gui.alternator.status.badCoverage": "Insufficient coverage", 14 | "mechano.gui.alternator.status.decentCoverage": "Passable coverage", 15 | "mechano.gui.alternator.status.detailedTitle": "Assembly:", 16 | "mechano.gui.alternator.status.dimensions": "Dimensions", 17 | "mechano.gui.alternator.status.hasLength": "Has at least 1 rotor", 18 | "mechano.gui.alternator.status.hasOpposing": "Has opposing Slip Ring Shaft", 19 | "mechano.gui.alternator.status.headroom": "Headroom:", 20 | "mechano.gui.alternator.status.max": "max.", 21 | "mechano.gui.alternator.status.noLength": "Needs at least 1 rotor", 22 | "mechano.gui.alternator.status.noOpposing": "Needs opposing Slip Ring Shaft", 23 | "mechano.gui.alternator.status.perfectCoverage": "Perfect coverage", 24 | "mechano.gui.alternator.status.prediction.in": "in: ", 25 | "mechano.gui.alternator.status.prediction.out": "out: ", 26 | "mechano.gui.alternator.status.predictiveSubtitle": "With this configuration:", 27 | "mechano.gui.alternator.status.predictiveTitle": "Potential:", 28 | "mechano.gui.alternator.status.stators": "Stators", 29 | "mechano.gui.alternator.status.statusTitle": "Status:", 30 | "mechano.gui.alternator.status.title": "Alternator Information:", 31 | "mechano.gui.alternator.status.unfinishedTitle": "Assembly Checklist:", 32 | "mechano.gui.anchorpoint.status.connections": "attached wires", 33 | "mechano.gui.anchorpoint.status.title": "Connector Information:", 34 | "mechano.gui.connector.mode": "Connector Mode", 35 | "mechano.gui.connector.mode.pull_in": "⯇", 36 | "mechano.gui.connector.mode.push_out": "⯈", 37 | "mechano.gui.generic.consuming": "Consuming", 38 | "mechano.gui.generic.converting": "Converting", 39 | "mechano.gui.generic.into": "into", 40 | "mechano.gui.generic.moreinfo": "Sneak for more info...", 41 | "mechano.gui.generic.producing": "Producing", 42 | "mechano.gui.slipring.state.child": "Child", 43 | "mechano.gui.slipring.state.invalid": "Invalid", 44 | "mechano.gui.slipring.state.parent": "Parent", 45 | "mechano.gui.slipring.state.perfect": "Perfect", 46 | "mechano.gui.voltometer.no_power": "No Power" 47 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/base_end_b.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [0, 0, 0], 6 | "to": [16, 7, 15], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 8 | "color": 0, 9 | "faces": { 10 | "east": {"uv": [13, 0, 28, 7], "texture": "#missing"}, 11 | "south": {"uv": [0, 53, 16, 60], "texture": "#missing"}, 12 | "west": {"uv": [28, 0, 13, 7], "texture": "#missing"}, 13 | "up": {"uv": [15, 64, 0, 48], "rotation": 270, "texture": "#missing"}, 14 | "down": {"uv": [28, 6, 13, 22], "rotation": 270, "texture": "#missing"} 15 | } 16 | }, 17 | { 18 | "from": [13, 7, 4], 19 | "to": [16, 9, 12], 20 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 21 | "color": 7, 22 | "faces": { 23 | "north": {"uv": [4, 61, 6, 64], "rotation": 90, "texture": "#missing"}, 24 | "east": {"uv": [4, 62, 12, 64], "texture": "#missing"}, 25 | "south": {"uv": [10, 64, 12, 61], "rotation": 90, "texture": "#missing"}, 26 | "up": {"uv": [4, 61, 12, 64], "rotation": 270, "texture": "#missing"} 27 | } 28 | }, 29 | { 30 | "from": [0, 7, 4], 31 | "to": [3, 9, 12], 32 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 33 | "color": 4, 34 | "faces": { 35 | "north": {"uv": [4, 48, 6, 51], "rotation": 90, "texture": "#missing"}, 36 | "south": {"uv": [10, 51, 12, 48], "rotation": 90, "texture": "#missing"}, 37 | "west": {"uv": [4, 50, 12, 48], "texture": "#missing"}, 38 | "up": {"uv": [4, 51, 12, 48], "rotation": 90, "texture": "#missing"} 39 | } 40 | }, 41 | { 42 | "from": [3, 7, 0], 43 | "to": [13, 10, 16], 44 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 45 | "color": 4, 46 | "faces": { 47 | "east": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 48 | "south": {"uv": [26, 48, 36, 51], "texture": "#missing"}, 49 | "west": {"uv": [22, 48, 25, 64], "rotation": 90, "texture": "#missing"}, 50 | "up": {"uv": [16, 48, 26, 64], "rotation": 180, "texture": "#missing"}, 51 | "down": {"uv": [16, 48, 26, 64], "rotation": 180, "texture": "#missing"} 52 | } 53 | }, 54 | { 55 | "from": [6, 4, 0], 56 | "to": [10, 7, 16], 57 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 58 | "color": 9, 59 | "faces": { 60 | "east": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 61 | "south": {"uv": [26, 48, 30, 51], "texture": "#missing"}, 62 | "west": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 63 | "down": {"uv": [16, 48, 20, 64], "rotation": 180, "texture": "#missing"} 64 | } 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/lang/custom/ui.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "itemGroup.mechano.main": "Mechano", 4 | 5 | "mechano.generic.unit.pertick": "/t", 6 | 7 | "mechano.generic.unit.watts": "W", 8 | "mechano.generic.unit.kilowatts": "kW", 9 | "mechano.generic.unit.gigawatts": "GW", 10 | 11 | "mechano.generic.unit.watthours": "Wh", 12 | "mechano.generic.unit.kilowatthours": "kWh", 13 | "mechano.generic.unit.gigawatthours": "GWh", 14 | 15 | 16 | "mechano.gui.connector.mode": "Connector Mode", 17 | "mechano.gui.connector.mode.pull_in": "⯇", 18 | "mechano.gui.connector.mode.push_out": "⯈", 19 | 20 | "mechano.gui.generic.converting": "Converting", 21 | "mechano.gui.generic.consuming": "Consuming", 22 | "mechano.gui.generic.producing": "Producing", 23 | "mechano.gui.generic.into": "into", 24 | 25 | "mechano.gui.generic.moreinfo": "Sneak for more info...", 26 | 27 | "mechano.gui.alternator.status.title": "Alternator Information:", 28 | 29 | "mechano.gui.alternator.status.unfinishedTitle": "Assembly Checklist:", 30 | 31 | "mechano.gui.alternator.status.statusTitle": "Status:", 32 | "mechano.gui.alternator.status.detailedTitle": "Assembly:", 33 | "mechano.gui.alternator.status.predictiveTitle": "Potential:", 34 | "mechano.gui.alternator.status.predictiveSubtitle": "With this configuration:", 35 | 36 | "mechano.gui.alternator.status.stators": "Stators", 37 | "mechano.gui.alternator.status.dimensions": "Dimensions", 38 | "mechano.gui.alternator.status.max": "max.", 39 | 40 | "mechano.gui.slipring.state.parent": "Parent", 41 | "mechano.gui.slipring.state.child": "Child", 42 | "mechano.gui.slipring.state.invalid": "Invalid", 43 | "mechano.gui.slipring.state.perfect": "Perfect", 44 | 45 | "mechano.gui.alternator.status.hasLength": "Has at least 1 rotor", 46 | "mechano.gui.alternator.status.noLength": "Needs at least 1 rotor", 47 | "mechano.gui.alternator.status.hasOpposing": "Has opposing Slip Ring Shaft", 48 | "mechano.gui.alternator.status.noOpposing": "Needs opposing Slip Ring Shaft", 49 | 50 | "mechano.gui.alternator.status.prediction.in": "in: ", 51 | "mechano.gui.alternator.status.prediction.out": "out: ", 52 | "mechano.gui.alternator.status.headroom": "Headroom:", 53 | 54 | "mechano.gui.alternator.status.badCoverage": "Insufficient coverage", 55 | "mechano.gui.alternator.status.decentCoverage": "Passable coverage", 56 | "mechano.gui.alternator.status.perfectCoverage": "Perfect coverage", 57 | 58 | "mechano.gui.anchorpoint.status.title": "Connector Information:", 59 | "mechano.gui.anchorpoint.status.connections": "attached wires", 60 | 61 | "mechano.gui.voltometer.no_power": "No Power" 62 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/topology/CircuitComponentProvider.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.topology; 2 | 3 | import com.quattage.mechano.api.grid.topology.vertex.AncillaryNode; 4 | import com.quattage.mechano.api.grid.topology.vertex.Node; 5 | import com.quattage.mechano.api.switchboard.JackSelector; 6 | import com.quattage.mechano.api.switchboard.action.GridAction; 7 | 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.multiplayer.ClientLevel; 10 | import net.neoforged.api.distmarker.Dist; 11 | import net.neoforged.api.distmarker.OnlyIn; 12 | 13 | public interface CircuitComponentProvider { 14 | /** 15 | * Evalutaes the provided {@link Node} and returns a {@link GridAction response} 16 | * indicating whether or not the targeted joint should be highlighted by the {@link JackSelector selector} 17 | *

18 | *

Remember to tag implementations with

19 | *
@OnlyIn(Dist.CLIENT)
20 | * @param world world to operate whithin 21 | * @param target the {@link AncillaryNode} currently targeted by the {@link Minecraft#player local player} 22 | * @return A {@link GridAction action} 23 | */ 24 | @OnlyIn(Dist.CLIENT) 25 | default GridAction evaluateTarget(ClientLevel world, AncillaryNode target) { 26 | return GridAction.RESPONSE_SUCCESS; 27 | } 28 | 29 | /** 30 | * Gets the CircuitComponent associated with this object, and throws errors if anything goes wrong. 31 | * @return A new CircuitComponent instance constructed by this object. 32 | */ 33 | default CircuitComponent getComponentSafe() { 34 | CircuitComponent output = null; 35 | try { output = getComponent(); } 36 | catch(RuntimeException e) { 37 | e.printStackTrace(); 38 | throw new CircuitComponentProviderException(this, "Construction failed! (see exception above)"); 39 | } 40 | if(output == null) 41 | throw new CircuitComponentProviderException(this, "Construction failed (factory returned null)"); 42 | return output; 43 | } 44 | 45 | CircuitComponent getComponent(); 46 | 47 | public static class CircuitComponentProviderException extends RuntimeException { 48 | public CircuitComponentProviderException(Object source, String message) { 49 | super("Failed while getting CircuitComponent from '" + source.getClass().getSimpleName() 50 | + ((message == null || message.isEmpty()) ? "" : ("' - " + message))); 51 | } 52 | public CircuitComponentProviderException(Object source) { 53 | super("Failed while getting CircuitComponent from '" + source.getClass().getSimpleName()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/base_single.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [0, 0, 1], 6 | "to": [16, 7, 15], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 8 | "faces": { 9 | "north": {"uv": [0, 53, 16, 60], "texture": "#missing"}, 10 | "east": {"uv": [0, 0, 14, 7], "texture": "#missing"}, 11 | "south": {"uv": [0, 53, 16, 60], "texture": "#missing"}, 12 | "west": {"uv": [0, 0, 14, 7], "texture": "#missing"}, 13 | "up": {"uv": [15, 64, 1, 48], "rotation": 90, "texture": "#missing"}, 14 | "down": {"uv": [14, 6, 0, 22], "rotation": 90, "texture": "#missing"} 15 | } 16 | }, 17 | { 18 | "from": [6, 4, 0], 19 | "to": [10, 7, 16], 20 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 21 | "faces": { 22 | "north": {"uv": [26, 48, 30, 51], "texture": "#missing"}, 23 | "east": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 24 | "south": {"uv": [26, 51, 30, 48], "texture": "#missing"}, 25 | "west": {"uv": [22, 48, 25, 64], "rotation": 90, "texture": "#missing"}, 26 | "down": {"uv": [16, 48, 20, 64], "texture": "#missing"} 27 | } 28 | }, 29 | { 30 | "from": [0, 7, 4], 31 | "to": [3, 9, 12], 32 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 33 | "faces": { 34 | "north": {"uv": [10, 64, 12, 61], "rotation": 90, "texture": "#missing"}, 35 | "south": {"uv": [4, 61, 6, 64], "rotation": 90, "texture": "#missing"}, 36 | "west": {"uv": [4, 62, 12, 64], "texture": "#missing"}, 37 | "up": {"uv": [4, 61, 12, 64], "rotation": 90, "texture": "#missing"} 38 | } 39 | }, 40 | { 41 | "from": [3, 7, 0], 42 | "to": [13, 10, 16], 43 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 44 | "faces": { 45 | "north": {"uv": [26, 48, 36, 51], "texture": "#missing"}, 46 | "east": {"uv": [22, 48, 25, 64], "rotation": 90, "texture": "#missing"}, 47 | "south": {"uv": [26, 48, 36, 51], "texture": "#missing"}, 48 | "west": {"uv": [16, 48, 19, 64], "rotation": 90, "texture": "#missing"}, 49 | "up": {"uv": [16, 48, 26, 64], "texture": "#missing"}, 50 | "down": {"uv": [16, 48, 26, 64], "texture": "#missing"} 51 | } 52 | }, 53 | { 54 | "from": [13, 7, 4], 55 | "to": [16, 9, 12], 56 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 57 | "faces": { 58 | "north": {"uv": [10, 51, 12, 48], "rotation": 90, "texture": "#missing"}, 59 | "east": {"uv": [4, 50, 12, 48], "texture": "#missing"}, 60 | "south": {"uv": [4, 48, 6, 51], "rotation": 90, "texture": "#missing"}, 61 | "up": {"uv": [4, 51, 12, 48], "rotation": 270, "texture": "#missing"} 62 | } 63 | } 64 | ] 65 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/catenary/MeshExtruder.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.catenary; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import com.mojang.blaze3d.vertex.PoseStack.Pose; 6 | import com.mojang.blaze3d.vertex.VertexConsumer; 7 | import com.quattage.mechano.api.catenary.Catenaries.Stick; 8 | 9 | import net.neoforged.api.distmarker.Dist; 10 | import net.neoforged.api.distmarker.OnlyIn; 11 | 12 | /** 13 | * Allows API users to define unique meshing behaviour 14 | * for catenaries. Instances should provide 15 | * a unique implementation of the {@link #extrude} method. 16 | */ 17 | @FunctionalInterface 18 | @OnlyIn(Dist.CLIENT) 19 | public interface MeshExtruder { 20 | /** 21 | * Defines a method for lofting a profile across a given 22 | * {@link Stick}. Implementations can define unique topology, 23 | * lighting, and normal behaviours for the resulting geometry. 24 | * The stick, called current, has 4 or more 25 | * vertices placed at each of its endpoints. The offset 26 | * positions of these vertices are averaged across the 27 | * adjacent sticks, previous and 28 | * next, so that adjacent profiles line up 29 | * exactly at their ends. Alternatively, endpoint averaging 30 | * can be ignored by simply passing null in 31 | * place of either adjacent stick. 32 | * @param buffer VertexConsumer to push geometry to 33 | * @param pose Pose to use for transforming 34 | * @param geo {@link CatenaryMeshBuffer} to store and process vertex data. You don't actually have to use this if you don't want to - it's a helper that aids in emiting quads based on stick dimensions quickly. 35 | * @param previous (Optional, can be null) The previous stick in the chain 36 | * @param current (Required) The stick to create a profile of 37 | * @param next (Optional, can be null) The next stick in the chain 38 | * @param loftLength An arbitrary float representing the total arclength covered during successive calls to this method. 39 | * Useful for when multiple extrusions are created in one mesh and need to distinguish between one another or for 40 | * panning UVs across an atlas. 41 | * @param recomputeNormals true if new face normals should be computed here. If false, 42 | * the normals contained in geo will not be recomputed, but reused. 43 | * @param pTicks Partial ticks (accessible in most rendering contexts) for lerping from a fixed update cycle. 44 | */ 45 | void extrude(VertexConsumer buffer, Pose pose, CatenaryMeshBuffer geo, @Nullable Stick previous, Stick current, @Nullable Stick next, float loftLength, boolean recomputeNormals, float pTicks); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/CircleGetter.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block; 2 | 3 | import java.util.function.Function; 4 | 5 | import net.createmod.catnip.placement.PlacementOffset; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.Direction.Axis; 8 | 9 | public class CircleGetter extends ShapeGetter { 10 | 11 | public CircleGetter(Integer radius, Axis axis, BlockPos centerPos) { 12 | super(radius, axis, centerPos); 13 | } 14 | 15 | @Override 16 | protected PlacementOffset evalSafe(Function action) { 17 | return makeCircle(action); 18 | } 19 | 20 | private PlacementOffset makeCircle(Function action) { 21 | int iterX = this.radius; 22 | int iterY = 0; 23 | int step = 1 - iterX; 24 | 25 | while(iterX >= iterY) { 26 | 27 | PlacementOffset current; 28 | 29 | // lol 30 | current = evalBlock(action, iterX, iterY); 31 | if(current != null) return current; 32 | 33 | current = evalBlock(action, -iterX, iterY); 34 | if(current != null) return current; 35 | 36 | current = evalBlock(action, iterX, -iterY); 37 | if(current != null) return current; 38 | 39 | current = evalBlock(action, -iterX, -iterY); 40 | if(current != null) return current; 41 | 42 | current = evalBlock(action, iterY, iterX); 43 | if(current != null) return current; 44 | 45 | current = evalBlock(action, -iterY, iterX); 46 | if(current != null) return current; 47 | 48 | current = evalBlock(action, iterY, -iterX); 49 | if(current != null) return current; 50 | 51 | current = evalBlock(action, -iterY, -iterX); 52 | if(current != null) return current; 53 | 54 | if(step <= 0) { 55 | iterY++; 56 | step += 2 * iterY + 1; 57 | } else { 58 | iterX--; 59 | step -= 2 * iterX + 1; 60 | } 61 | } 62 | 63 | return PlacementOffset.fail(); 64 | } 65 | 66 | private PlacementOffset evalBlock(Function action, int stepX, int stepY) { 67 | 68 | int x = this.centerPos.getX(); 69 | int y = this.centerPos.getY(); 70 | int z = this.centerPos.getZ(); 71 | 72 | BlockPos checkPos; 73 | 74 | if(this.axis == Axis.X) checkPos = new BlockPos(x, y + stepX, z + stepY); 75 | else if(this.axis == Axis.Y) checkPos = new BlockPos(x + stepX, y, z + stepY); 76 | else checkPos = new BlockPos(x + stepX, y + stepY, z); 77 | 78 | return action.apply(checkPos); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/datagen/SimpleDataProvider.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.infrastructure.datagen; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.List; 8 | import java.util.concurrent.CompletableFuture; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import com.quattage.mechano.Mechano; 15 | 16 | import net.minecraft.data.CachedOutput; 17 | import net.minecraft.data.DataProvider; 18 | import net.minecraft.data.PackOutput; 19 | import net.neoforged.neoforge.common.data.ExistingFileHelper; 20 | 21 | public abstract class SimpleDataProvider implements DataProvider { 22 | 23 | private final Path outputDir; 24 | private final ExistingFileHelper fileHelper; 25 | 26 | public SimpleDataProvider(PackOutput output, ExistingFileHelper fileHelper) { 27 | this.outputDir = configureOutputDirectory(output); 28 | this.fileHelper = fileHelper; 29 | } 30 | 31 | protected abstract Path configureOutputDirectory(PackOutput output); 32 | 33 | @Override 34 | public CompletableFuture run(CachedOutput output) { 35 | return CompletableFuture.runAsync(() -> prepare(output)); 36 | } 37 | 38 | public final void prepare(CachedOutput output) { 39 | createDirectory(outputDir); 40 | generate(); 41 | } 42 | 43 | protected ExistingFileHelper getFileHelper() { 44 | return fileHelper; 45 | } 46 | 47 | protected Path getOutputDir() { 48 | return outputDir; 49 | } 50 | 51 | /** 52 | * The implementing 53 | * @param output 54 | */ 55 | public abstract void generate(); 56 | 57 | protected final void createDirectory(Path dir) { 58 | try { Files.createDirectories(dir); } catch(IOException e) { 59 | Mechano.LOGGER.error("Failure while creating hitbox directory!"); 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | public PrintWriter createWriter() throws IOException { 65 | return new PrintWriter(Files.newBufferedWriter(getOutputDir())); 66 | } 67 | 68 | protected final @Nullable List listFiles(Path dir) { 69 | List output = null; 70 | try(Stream fileStream = Files.walk(dir, 8)) { 71 | output = fileStream 72 | .filter(Files::isRegularFile) 73 | .filter(path -> path.toString().endsWith(".json")) 74 | .collect(Collectors.toList()); 75 | } catch(IOException e) { 76 | Mechano.LOGGER.error("Failure while traversing hitbox directory!"); 77 | e.printStackTrace(); 78 | } 79 | return output; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/infrastructure/datagen/DynamicStateGenerator.java: -------------------------------------------------------------------------------- 1 | 2 | package com.quattage.mechano.infrastructure.datagen; 3 | 4 | import javax.annotation.Nullable; 5 | 6 | import com.quattage.mechano.Mechano; 7 | import com.quattage.mechano.foundation.block.orientation.DirectionTransformer; 8 | import com.simibubi.create.foundation.data.SpecialBlockStateGen; 9 | import com.tterrag.registrate.providers.DataGenContext; 10 | import com.tterrag.registrate.providers.RegistrateBlockstateProvider; 11 | 12 | import net.minecraft.world.level.block.Block; 13 | import net.minecraft.world.level.block.state.BlockState; 14 | import net.minecraft.world.level.block.state.properties.EnumProperty; 15 | import net.neoforged.neoforge.client.model.generators.ModelFile; 16 | 17 | public class DynamicStateGenerator extends SpecialBlockStateGen { 18 | 19 | private final @Nullable EnumProperty typeDelegate; 20 | private @Nullable String[] customIn; 21 | private @Nullable String[] customSub; 22 | 23 | 24 | public DynamicStateGenerator(EnumProperty typeDelegate) { 25 | this.typeDelegate = typeDelegate; 26 | } 27 | 28 | public DynamicStateGenerator() { 29 | this.typeDelegate = null; 30 | this.customIn = null; 31 | this.customSub = null; 32 | } 33 | 34 | public DynamicStateGenerator in(String... customIn) { 35 | this.customIn = customIn; 36 | return this; 37 | } 38 | 39 | public DynamicStateGenerator sub(String... customSub) { 40 | this.customSub = customSub; 41 | return this; 42 | } 43 | 44 | @Override 45 | protected int getXRotation(BlockState state) { 46 | return DirectionTransformer.getStateRotation(state).getX(); 47 | } 48 | 49 | @Override 50 | protected int getYRotation(BlockState state) { 51 | return DirectionTransformer.getStateRotation(state).getY(); 52 | } 53 | 54 | @Override 55 | public ModelFile getModel(DataGenContext ctx, 56 | RegistrateBlockstateProvider provider, BlockState state) { 57 | 58 | String typeName = (typeDelegate == null) ? "base" : 59 | state.getValue(typeDelegate).getSerializedName(); 60 | Mechano.LOGGER.info("Getting model " + typeName + " for state " + state); 61 | 62 | String orientSuffix = 63 | (DirectionTransformer.isDistinctionRequired(state) && 64 | DirectionTransformer.isHorizontal(state)) 65 | ? "_side" : ""; 66 | 67 | if(customIn == null && customSub == null) 68 | return provider.models().getExistingFile(Mechano.asResource("block/" + ctx.getName() + "/" + (typeName + orientSuffix))); 69 | return provider.models().getExistingFile(Mechano.extend(ctx, "block", customIn, customSub, typeName + orientSuffix)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/data/mechano/hitboxes/test_axis.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [1, 0, 1], 6 | "to": [3, 5, 3], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]}, 8 | "color": 3, 9 | "faces": { 10 | "north": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 11 | "east": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 12 | "south": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 13 | "west": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 14 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 15 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [13, 0, 13], 20 | "to": [15, 2, 15], 21 | "rotation": {"angle": 0, "axis": "y", "origin": [13, 0, 13]}, 22 | "color": 5, 23 | "faces": { 24 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 25 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 26 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 27 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 28 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 29 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [7, 7, 7], 34 | "to": [9, 9, 9], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [7, 7, 7]}, 36 | "color": 9, 37 | "faces": { 38 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 39 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 41 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 42 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 43 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [1, 10, 13], 48 | "to": [3, 12, 15], 49 | "rotation": {"angle": 0, "axis": "y", "origin": [1, 10, 13]}, 50 | "color": 6, 51 | "faces": { 52 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 53 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 54 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 55 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 56 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 57 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 58 | } 59 | }, 60 | { 61 | "from": [13, 5, 1], 62 | "to": [15, 7, 3], 63 | "rotation": {"angle": 0, "axis": "y", "origin": [13, 5, 1]}, 64 | "color": 6, 65 | "faces": { 66 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 67 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 68 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 69 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 70 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 71 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/test_axis/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [1, 0, 1], 6 | "to": [3, 5, 3], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]}, 8 | "color": 3, 9 | "faces": { 10 | "north": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 11 | "east": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 12 | "south": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 13 | "west": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 14 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 15 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [13, 0, 13], 20 | "to": [15, 2, 15], 21 | "rotation": {"angle": 0, "axis": "y", "origin": [13, 0, 13]}, 22 | "color": 5, 23 | "faces": { 24 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 25 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 26 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 27 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 28 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 29 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [7, 7, 7], 34 | "to": [9, 9, 9], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [7, 7, 7]}, 36 | "color": 9, 37 | "faces": { 38 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 39 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 41 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 42 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 43 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [1, 10, 13], 48 | "to": [3, 12, 15], 49 | "rotation": {"angle": 0, "axis": "y", "origin": [1, 10, 13]}, 50 | "color": 6, 51 | "faces": { 52 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 53 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 54 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 55 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 56 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 57 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 58 | } 59 | }, 60 | { 61 | "from": [13, 5, 1], 62 | "to": [15, 7, 3], 63 | "rotation": {"angle": 0, "axis": "y", "origin": [13, 5, 1]}, 64 | "color": 6, 65 | "faces": { 66 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 67 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 68 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 69 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 70 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 71 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/test_axis/cube.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [1, 0, 1], 6 | "to": [3, 5, 3], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]}, 8 | "color": 3, 9 | "faces": { 10 | "north": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 11 | "east": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 12 | "south": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 13 | "west": {"uv": [0, 0, 2, 5], "texture": "#missing"}, 14 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 15 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [13, 0, 13], 20 | "to": [15, 2, 15], 21 | "rotation": {"angle": 0, "axis": "y", "origin": [13, 0, 13]}, 22 | "color": 5, 23 | "faces": { 24 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 25 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 26 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 27 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 28 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 29 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [7, 7, 7], 34 | "to": [9, 9, 9], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [7, 7, 7]}, 36 | "color": 9, 37 | "faces": { 38 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 39 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 41 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 42 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 43 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [1, 10, 13], 48 | "to": [3, 12, 15], 49 | "rotation": {"angle": 0, "axis": "y", "origin": [1, 10, 13]}, 50 | "color": 6, 51 | "faces": { 52 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 53 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 54 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 55 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 56 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 57 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 58 | } 59 | }, 60 | { 61 | "from": [13, 5, 1], 62 | "to": [15, 7, 3], 63 | "rotation": {"angle": 0, "axis": "y", "origin": [13, 5, 1]}, 64 | "color": 6, 65 | "faces": { 66 | "north": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 67 | "east": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 68 | "south": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 69 | "west": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 70 | "up": {"uv": [0, 0, 2, 2], "texture": "#missing"}, 71 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/base_middle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [64, 64], 4 | "textures": { 5 | "0": "mechano:block/stator/base", 6 | "particle": "mechano:block/stator/base" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [0, 0, 0], 11 | "to": [16, 7, 16], 12 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "east": {"uv": [7, 0, 11, 1.75], "texture": "#0"}, 15 | "west": {"uv": [11, 0, 7, 1.75], "texture": "#0"}, 16 | "up": {"uv": [4, 16, 0, 12], "rotation": 270, "texture": "#0"}, 17 | "down": {"uv": [11, 1.5, 7, 5.5], "rotation": 90, "texture": "#0"} 18 | } 19 | }, 20 | { 21 | "from": [0, 7, 4], 22 | "to": [3, 9, 12], 23 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 24 | "faces": { 25 | "north": {"uv": [2.5, 16, 3, 15.25], "rotation": 90, "texture": "#0"}, 26 | "south": {"uv": [1, 15.25, 1.5, 16], "rotation": 90, "texture": "#0"}, 27 | "west": {"uv": [1, 15.5, 3, 16], "texture": "#0"}, 28 | "up": {"uv": [1, 15.25, 3, 16], "rotation": 90, "texture": "#0"} 29 | } 30 | }, 31 | { 32 | "from": [3, 7, 0], 33 | "to": [13, 10, 16], 34 | "faces": { 35 | "north": {"uv": [6.5, 12, 9, 12.75], "texture": "#0"}, 36 | "east": {"uv": [5.5, 12, 6.25, 16], "rotation": 90, "texture": "#0"}, 37 | "west": {"uv": [4, 12, 4.75, 16], "rotation": 90, "texture": "#0"}, 38 | "up": {"uv": [4, 12, 6.5, 16], "texture": "#0"}, 39 | "down": {"uv": [0, 0, 2.5, 4], "texture": "#0"} 40 | } 41 | }, 42 | { 43 | "from": [13, 7, 4], 44 | "to": [16, 9, 12], 45 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 46 | "faces": { 47 | "north": {"uv": [2.5, 12.75, 3, 12], "rotation": 90, "texture": "#0"}, 48 | "east": {"uv": [1, 12.5, 3, 12], "texture": "#0"}, 49 | "south": {"uv": [1, 12, 1.5, 12.75], "rotation": 90, "texture": "#0"}, 50 | "up": {"uv": [1, 12.75, 3, 12], "rotation": 270, "texture": "#0"} 51 | } 52 | } 53 | ], 54 | "display": { 55 | "thirdperson_righthand": { 56 | "rotation": [75, 45, 0], 57 | "translation": [0, 2.5, 0], 58 | "scale": [0.375, 0.375, 0.375] 59 | }, 60 | "thirdperson_lefthand": { 61 | "rotation": [75, 45, 0], 62 | "translation": [0, 2.5, 0], 63 | "scale": [0.375, 0.375, 0.375] 64 | }, 65 | "firstperson_righthand": { 66 | "rotation": [0, 45, 0], 67 | "scale": [0.4, 0.4, 0.4] 68 | }, 69 | "firstperson_lefthand": { 70 | "rotation": [0, 225, 0], 71 | "scale": [0.4, 0.4, 0.4] 72 | }, 73 | "ground": { 74 | "translation": [0, 3, 0], 75 | "scale": [0.25, 0.25, 0.25] 76 | }, 77 | "gui": { 78 | "rotation": [30, 225, 0], 79 | "scale": [0.625, 0.625, 0.625] 80 | }, 81 | "head": { 82 | "translation": [0, 11, 0] 83 | }, 84 | "fixed": { 85 | "scale": [0.5, 0.5, 0.5] 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/diagonal_girder/item.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 16], 4 | "textures": { 5 | "0": "mechano:block/diagonal_girder", 6 | "particle": "mechano:block/diagonal_girder" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [5, -0.35, 5], 11 | "to": [11, 16.35, 11], 12 | "rotation": {"angle": 45, "axis": "x", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 15 | "east": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 16 | "south": {"uv": [3, 0, 14.35, 6], "rotation": 90, "texture": "#0"}, 17 | "west": {"uv": [3.15, 0, 14.5, 6], "rotation": 90, "texture": "#0"}, 18 | "up": {"uv": [0, 0, 3, 6], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 3, 6], "texture": "#0"} 20 | } 21 | }, 22 | { 23 | "from": [3, -2.15, 0], 24 | "to": [13.1, 8.85, 4], 25 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 26 | "faces": { 27 | "north": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"}, 28 | "east": {"uv": [5, 6, 10.5, 10], "rotation": 90, "texture": "#0"}, 29 | "south": {"uv": [10.5, 6, 16, 16], "rotation": 270, "texture": "#0"}, 30 | "west": {"uv": [5, 6, 10.5, 10], "rotation": 270, "texture": "#0"}, 31 | "up": {"uv": [0, 6, 5, 10], "texture": "#0"}, 32 | "down": {"uv": [0, 6, 5, 10], "rotation": 180, "texture": "#0"} 33 | } 34 | }, 35 | { 36 | "from": [3.1, 12, 7.15], 37 | "to": [13, 16, 18.15], 38 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 39 | "faces": { 40 | "north": {"uv": [0, 6, 5, 10], "texture": "#0"}, 41 | "east": {"uv": [5, 6, 10.5, 10], "texture": "#0"}, 42 | "south": {"uv": [0, 6, 5, 10], "texture": "#0"}, 43 | "west": {"uv": [5, 6, 10.5, 10], "texture": "#0"}, 44 | "up": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"}, 45 | "down": {"uv": [10.5, 6, 16, 16], "rotation": 90, "texture": "#0"} 46 | } 47 | } 48 | ], 49 | "display": { 50 | "thirdperson_righthand": { 51 | "rotation": [75, 45, 0], 52 | "translation": [0, 2.5, 0], 53 | "scale": [0.375, 0.375, 0.375] 54 | }, 55 | "thirdperson_lefthand": { 56 | "rotation": [75, 45, 0], 57 | "translation": [0, 2.5, 0], 58 | "scale": [0.375, 0.375, 0.375] 59 | }, 60 | "firstperson_righthand": { 61 | "rotation": [-56, 45, 0], 62 | "scale": [0.4, 0.4, 0.4] 63 | }, 64 | "firstperson_lefthand": { 65 | "rotation": [-56, 45, 0], 66 | "scale": [0.4, 0.4, 0.4] 67 | }, 68 | "ground": { 69 | "translation": [0, 3, 0], 70 | "scale": [0.42, 0.42, 0.42] 71 | }, 72 | "gui": { 73 | "rotation": [30, 225, 0], 74 | "scale": [0.625, 0.625, 0.625] 75 | }, 76 | "head": { 77 | "rotation": [47.5, 0, 0], 78 | "translation": [0, 9.25, 0] 79 | }, 80 | "fixed": { 81 | "rotation": [180, -90, 180], 82 | "scale": [0.5, 0.5, 0.5] 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/orientation/Relative.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block.orientation; 2 | 3 | import java.util.Locale; 4 | 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.core.Direction.Axis; 7 | import net.minecraft.core.Vec3i; 8 | import net.minecraft.util.StringRepresentable; 9 | 10 | /*** 11 | * Represents a direction in the local space. Modifiable by global directions 12 | * to achieve rotations. 13 | */ 14 | public enum Relative implements StringRepresentable { 15 | FRONT(0, 0, 0, Direction.NORTH), 16 | BACK(0, 180, 0, Direction.SOUTH), 17 | LEFT(0, 90, 0, Direction.WEST), 18 | RIGHT(0, 270, 0, Direction.EAST), 19 | TOP(270, 0, 0, Direction.UP), 20 | BOTTOM(90, 0, 0, Direction.DOWN); 21 | 22 | private final Direction defaultDir; 23 | 24 | Relative(int x, int y, int z, Direction defaultDir) { 25 | this.defaultDir = defaultDir; 26 | } 27 | 28 | public static Relative of(Direction dir) { 29 | return switch (dir) { 30 | case NORTH -> FRONT; 31 | case SOUTH -> BACK; 32 | case WEST -> LEFT; 33 | case EAST -> RIGHT; 34 | case UP -> TOP; 35 | case DOWN -> BOTTOM; 36 | }; 37 | } 38 | 39 | /** 40 | * Applies this Relative given the provided {@link CombinedOrientation orientation} 41 | * @param orientation Orientation to apply 42 | * @return The global {@link Direction} relative to the provided orientation 43 | */ 44 | public Direction apply(CombinedOrientation orientation) { 45 | Vec3i up = orientation.getLocalUp().getNormal(); 46 | Vec3i fn = orientation.getLocalForward().getNormal(); 47 | Vec3i tan = Relative.cross(up, fn); 48 | Vec3i ln = this.defaultDir.getNormal(); 49 | return Direction.getNearest( 50 | ln.getX() * tan.getX() + ln.getY() * up.getX() + ln.getZ() * fn.getX(), 51 | ln.getX() * tan.getY() + ln.getY() * up.getY() + ln.getZ() * fn.getY(), 52 | ln.getX() * tan.getZ() + ln.getY() * up.getZ() + ln.getZ() * fn.getZ() 53 | ); 54 | } 55 | 56 | private static Vec3i cross(Vec3i a, Vec3i b) { 57 | return new Vec3i( 58 | a.getY() * b.getZ() - a.getZ() * b.getY(), 59 | a.getZ() * b.getX() - a.getX() * b.getZ(), 60 | a.getX() * b.getY() - a.getY() * b.getX() 61 | ); 62 | } 63 | 64 | public Direction getDefaultDir() { 65 | return defaultDir; 66 | } 67 | 68 | public Axis getAxis() { 69 | return defaultDir.getAxis(); 70 | } 71 | 72 | public Relative copy() { 73 | return Relative.values()[this.ordinal()]; 74 | } 75 | 76 | @Override 77 | public String getSerializedName() { 78 | return name().toLowerCase(Locale.ROOT); 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return getSerializedName(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/base_middle_side.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [64, 64], 4 | "textures": { 5 | "0": "mechano:block/stator/base", 6 | "particle": "mechano:block/stator/base" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [0, 0, 0], 11 | "to": [7, 16, 16], 12 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "east": {"uv": [4, 16, 0, 12], "texture": "#0"}, 15 | "west": {"uv": [11, 1.5, 7, 5.5], "rotation": 180, "texture": "#0"}, 16 | "up": {"uv": [11, 0, 7, 1.75], "rotation": 90, "texture": "#0"}, 17 | "down": {"uv": [7, 0, 11, 1.75], "rotation": 90, "texture": "#0"} 18 | } 19 | }, 20 | { 21 | "from": [7, 13, 4], 22 | "to": [9, 16, 12], 23 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 24 | "faces": { 25 | "north": {"uv": [2.5, 16, 3, 15.25], "texture": "#0"}, 26 | "east": {"uv": [1, 15.25, 3, 16], "rotation": 180, "texture": "#0"}, 27 | "south": {"uv": [1, 15.25, 1.5, 16], "rotation": 180, "texture": "#0"}, 28 | "up": {"uv": [1, 15.5, 3, 16], "rotation": 90, "texture": "#0"} 29 | } 30 | }, 31 | { 32 | "from": [7, 3, 0], 33 | "to": [10, 13, 16], 34 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 35 | "faces": { 36 | "north": {"uv": [6.5, 12, 9, 12.75], "rotation": 270, "texture": "#0"}, 37 | "east": {"uv": [4, 12, 6.5, 16], "rotation": 90, "texture": "#0"}, 38 | "west": {"uv": [0, 0, 2.5, 4], "rotation": 90, "texture": "#0"}, 39 | "up": {"uv": [4, 12, 4.75, 16], "rotation": 180, "texture": "#0"}, 40 | "down": {"uv": [5.5, 12, 6.25, 16], "rotation": 180, "texture": "#0"} 41 | } 42 | }, 43 | { 44 | "from": [7, 0, 4], 45 | "to": [9, 3, 12], 46 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 47 | "faces": { 48 | "north": {"uv": [2.5, 12.75, 3, 12], "texture": "#0"}, 49 | "east": {"uv": [1, 12.75, 3, 12], "texture": "#0"}, 50 | "south": {"uv": [1, 12, 1.5, 12.75], "rotation": 180, "texture": "#0"}, 51 | "down": {"uv": [1, 12.5, 3, 12], "rotation": 90, "texture": "#0"} 52 | } 53 | } 54 | ], 55 | "display": { 56 | "thirdperson_righthand": { 57 | "rotation": [75, 45, 0], 58 | "translation": [0, 2.5, 0], 59 | "scale": [0.375, 0.375, 0.375] 60 | }, 61 | "thirdperson_lefthand": { 62 | "rotation": [75, 45, 0], 63 | "translation": [0, 2.5, 0], 64 | "scale": [0.375, 0.375, 0.375] 65 | }, 66 | "firstperson_righthand": { 67 | "rotation": [0, 45, 0], 68 | "scale": [0.4, 0.4, 0.4] 69 | }, 70 | "firstperson_lefthand": { 71 | "rotation": [0, 225, 0], 72 | "scale": [0.4, 0.4, 0.4] 73 | }, 74 | "ground": { 75 | "translation": [0, 3, 0], 76 | "scale": [0.25, 0.25, 0.25] 77 | }, 78 | "gui": { 79 | "rotation": [30, 225, 0], 80 | "scale": [0.625, 0.625, 0.625] 81 | }, 82 | "head": { 83 | "translation": [0, 11, 0] 84 | }, 85 | "fixed": { 86 | "scale": [0.5, 0.5, 0.5] 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/ShapeGetter.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.util.function.Consumer; 5 | import java.util.function.Function; 6 | 7 | import net.createmod.catnip.placement.PlacementOffset; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.core.Direction.Axis; 10 | 11 | public abstract class ShapeGetter { 12 | 13 | protected final int radius; 14 | protected Axis axis; 15 | protected BlockPos centerPos; 16 | protected boolean isInitialized = true; 17 | 18 | public ShapeGetter(Integer radius, Axis axis, BlockPos centerPos) { 19 | this.radius = radius; 20 | this.axis = axis; 21 | this.centerPos = centerPos; 22 | } 23 | 24 | /*** 25 | * Evaluates this ShapeGetter iteratively 26 | * @param action Action to perform at the given BlockPos. Implementations should return a null PlacementOffset to indicate a skipped iteration. 27 | * @return A PlacementOffset at this shape 28 | */ 29 | public PlacementOffset evaluatePlacement(Function action) { 30 | if(!isInitialized) throw new IllegalStateException("Cannot evaluate ShapeGetter before it is initialized!"); 31 | return evalSafe(action); 32 | } 33 | 34 | public void iteratePlacement(Consumer action) { 35 | 36 | } 37 | 38 | protected abstract PlacementOffset evalSafe(Function action); 39 | 40 | public static ShapeGetterBuilder ofShape(Class shape) { 41 | return new ShapeGetterBuilder(shape); 42 | } 43 | 44 | public ShapeGetter setAxis(Axis axis) { 45 | this.axis = axis; 46 | return this; 47 | } 48 | 49 | public ShapeGetter moveTo(BlockPos centerPos) { 50 | this.centerPos = centerPos; 51 | return this; 52 | } 53 | 54 | public static class ShapeGetterBuilder { 55 | 56 | final Class shape; 57 | BlockPos pos = BlockPos.ZERO; 58 | int radius = 1; 59 | Axis axis = Axis.Y; 60 | 61 | public ShapeGetterBuilder(Class shape) { 62 | this.shape = shape; 63 | } 64 | 65 | public ShapeGetterBuilder at(BlockPos pos) { 66 | this.pos = pos; 67 | return this; 68 | } 69 | 70 | public ShapeGetterBuilder withRadius(int radius) { 71 | this.radius = radius; 72 | return this; 73 | } 74 | 75 | public ShapeGetterBuilder onAxis(Axis axis) { 76 | this.axis = axis; 77 | return this; 78 | } 79 | 80 | public ShapeGetter build() { 81 | try { 82 | Class[] args = new Class[3]; 83 | args[0] = Integer.class; 84 | args[1] = Axis.class; 85 | args[2] = BlockPos.class; 86 | 87 | return shape.getDeclaredConstructor(args).newInstance(radius, axis, pos); 88 | } catch (InstantiationException | IllegalAccessException | IllegalArgumentException 89 | | InvocationTargetException | NoSuchMethodException | SecurityException e) { 90 | 91 | // TODO this is dumb 92 | e.printStackTrace(); 93 | return null; 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/solver/NodeUnionSet.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.solver; 2 | 3 | import java.util.function.Consumer; 4 | 5 | import com.quattage.mechano.api.grid.topology.vertex.Node; 6 | 7 | import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; 8 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 9 | import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; 10 | 11 | public class NodeUnionSet { 12 | 13 | private Object2ObjectOpenHashMap relations; 14 | private Object2IntOpenHashMap ranks; 15 | private ObjectOpenHashSet roots; 16 | 17 | public NodeUnionSet() { 18 | relations = new Object2ObjectOpenHashMap<>(); 19 | ranks = new Object2IntOpenHashMap<>(); 20 | roots = new ObjectOpenHashSet<>(); 21 | } 22 | 23 | public Node find(Node n) { 24 | Node p = relations.get(n); 25 | if(p == null) return null; 26 | if(!p.equals(n)) relations.put(n, find(p)); 27 | return p; 28 | } 29 | 30 | public void union(Node a, Node b) { 31 | add(a); add(b); 32 | Node aP = find(a); 33 | Node bP = find(b); 34 | if(aP.equals(bP)) return; 35 | int rankA = ranks.getInt(aP); 36 | int rankB = ranks.getInt(bP); 37 | if(a.isGrounded() && !b.isGrounded()) { 38 | relations.put(bP, aP); 39 | roots.remove(bP); 40 | return; 41 | } 42 | if(b.isGrounded() && !a.isGrounded()) { 43 | relations.put(aP, bP); 44 | roots.remove(aP); 45 | return; 46 | } 47 | if(rankA < rankB) { 48 | relations.put(aP, bP); 49 | roots.remove(aP); 50 | return; 51 | } 52 | if(rankA > rankB) { 53 | relations.put(bP, aP); 54 | roots.remove(bP); 55 | return; 56 | } 57 | relations.put(bP, aP); 58 | ranks.put(aP, rankA + 1); 59 | roots.remove(bP); 60 | } 61 | 62 | public void assignIndices() { 63 | int index = 0; 64 | for(Node node : roots) 65 | node.setIndex(node.isGrounded() ? -1 : index++); 66 | } 67 | 68 | public boolean add(Node n) { 69 | if(relations.containsKey(n)) return false; 70 | relations.put(n, n); 71 | ranks.put(n, n.isGrounded() ? -1 : 0); 72 | roots.add(n); 73 | return true; 74 | } 75 | 76 | public void remove(Node n) { 77 | relations.remove(n); 78 | ranks.removeInt(n); 79 | } 80 | 81 | public int rootCount() { 82 | return roots.size(); 83 | } 84 | 85 | public int uniqueCount() { 86 | return ranks.size(); 87 | } 88 | 89 | public void forEachRoot(Consumer cons) { 90 | roots.forEach(cons); 91 | } 92 | 93 | public void reset() { 94 | relations = new Object2ObjectOpenHashMap<>(); 95 | ranks = new Object2IntOpenHashMap<>(); 96 | roots = new ObjectOpenHashSet<>(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/corner_end_a.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [1.5, 11.5, 0], 6 | "to": [4.5, 14.5, 1], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 8 | "color": 1, 9 | "faces": { 10 | "north": {"uv": [26, 57, 30, 61], "rotation": 270, "texture": "#missing"}, 11 | "east": {"uv": [26, 57, 30, 58], "rotation": 90, "texture": "#missing"}, 12 | "south": {"uv": [0, 0, 3.5, 3.5], "rotation": 90, "texture": "#missing"}, 13 | "west": {"uv": [26, 60, 30, 61], "rotation": 90, "texture": "#missing"}, 14 | "up": {"uv": [29, 57, 30, 61], "rotation": 90, "texture": "#missing"}, 15 | "down": {"uv": [26, 57, 27, 61], "rotation": 90, "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [11.5, 1.5, 0], 20 | "to": [14.5, 4.5, 1], 21 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 22 | "color": 9, 23 | "faces": { 24 | "north": {"uv": [26, 57, 30, 61], "texture": "#missing"}, 25 | "east": {"uv": [26, 57, 27, 61], "texture": "#missing"}, 26 | "south": {"uv": [0, 0, 3.5, 3.5], "texture": "#missing"}, 27 | "west": {"uv": [29, 57, 30, 61], "texture": "#missing"}, 28 | "up": {"uv": [26, 57, 30, 58], "texture": "#missing"}, 29 | "down": {"uv": [26, 60, 30, 61], "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [10, 0, 1], 34 | "to": [16, 12, 16], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, 36 | "color": 2, 37 | "faces": { 38 | "north": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 39 | "east": {"uv": [44, 26, 28, 32], "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 41 | "west": {"uv": [40, 56, 55, 50], "texture": "#missing"}, 42 | "up": {"uv": [16, 22, 30, 16], "rotation": 90, "texture": "#missing"}, 43 | "down": {"uv": [13, 31, 28, 37], "rotation": 90, "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [4, 4, 1], 48 | "to": [10, 10, 16], 49 | "color": 9, 50 | "faces": { 51 | "north": {"uv": [0, 0, 6, 6], "texture": "#missing"}, 52 | "east": {"uv": [0, 0, 15, 6], "texture": "#missing"}, 53 | "south": {"uv": [0, 0, 6, 6], "texture": "#missing"}, 54 | "west": {"uv": [0, 0, 15, 6], "texture": "#missing"}, 55 | "up": {"uv": [0, 0, 6, 15], "texture": "#missing"}, 56 | "down": {"uv": [0, 0, 6, 15], "texture": "#missing"} 57 | } 58 | }, 59 | { 60 | "from": [0, 10, 1], 61 | "to": [12, 16, 16], 62 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, 63 | "color": 1, 64 | "faces": { 65 | "north": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 66 | "east": {"uv": [0, 0, 14, 6], "texture": "#missing"}, 67 | "south": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 68 | "west": {"uv": [13, 37, 28, 31], "texture": "#missing"}, 69 | "up": {"uv": [28, 26, 44, 32], "rotation": 90, "texture": "#missing"}, 70 | "down": {"uv": [54, 56, 40, 50], "rotation": 90, "texture": "#missing"} 71 | } 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/topology/vertex/WireJack.java: -------------------------------------------------------------------------------- 1 | 2 | package com.quattage.mechano.api.grid.topology.vertex; 3 | 4 | import org.joml.Vector3d; 5 | import org.joml.Vector3f; 6 | 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import com.quattage.mechano.api.grid.CircuitFactory; 9 | import com.quattage.mechano.foundation.block.orientation.CombinedOrientation; 10 | import com.quattage.mechano.foundation.block.orientation.OrientationUpdatable; 11 | import com.quattage.mechano.foundation.numeric.EsoMath; 12 | 13 | import net.minecraft.world.phys.AABB; 14 | import net.minecraft.world.phys.Vec3; 15 | 16 | /** 17 | * An attachment point for catenaries. This class is designed specifically to be added to 18 | * Circuits directly via the {@link CircuitFactory factory.} 19 | * WireJacks are pushed to the selector so that their hitboxes can be highlighted when 20 | * players look at them. 21 | */ 22 | public class WireJack extends AncillaryNode implements OrientationUpdatable { 23 | 24 | private final long data; 25 | private final Vector3f offset; 26 | 27 | public WireJack(String name, boolean isVisible, long data) { 28 | super(name, isVisible); 29 | this.data = data; 30 | this.offset = makeOffsetVector(); 31 | } 32 | 33 | private Vector3f makeOffsetVector() { 34 | return new Vector3f(toOffset(EsoMath.long2shortA(data)) / 32f, toOffset(EsoMath.long2shortB(data)) / 32f, toOffset(EsoMath.long2shortC(data)) / 32f); 35 | } 36 | 37 | @Override public float getXO() { return offset.x; } 38 | @Override public float getYO() { return offset.y; } 39 | @Override public float getZO() { return offset.z; } 40 | @Override public float getSize() { return toOffset(EsoMath.long2shortD(data)) / 32f; } 41 | 42 | private float toOffset(short x) { 43 | return ((((int)x) - Short.MIN_VALUE) * (48f / (Short.MAX_VALUE - Short.MIN_VALUE))) - 16f; 44 | } 45 | 46 | @Override 47 | public AABB makeAABB(Vector3d basis, float size) { 48 | return new AABB( 49 | (0.5d + basis.x + offset.x) - size, 50 | (0.5d + basis.y + offset.y) - size, 51 | (0.5d + basis.z + offset.z) - size, 52 | (0.5d + basis.x + offset.x) + size, 53 | (0.5d + basis.y + offset.y) + size, 54 | (0.5d + basis.z + offset.z) + size 55 | ); 56 | } 57 | 58 | @Override 59 | void translateStack(Vector3d basis, Vec3 cameraPos, PoseStack matrixStack) { 60 | matrixStack.translate( 61 | (0.5d + basis.x) - cameraPos.x, 62 | (0.5d + basis.y) - cameraPos.y, 63 | (0.5d + basis.z) - cameraPos.z 64 | ); 65 | } 66 | 67 | @Override 68 | public void updateOrientation(CombinedOrientation dir) { 69 | // TODO FIX 70 | makeOffsetVector().rotate(dir.getLocalUp().getRotation(), offset); 71 | } 72 | 73 | @Override 74 | public String describeState() { 75 | return "(" + getXO() + ", " + getYO() + ", " + getZO() + ", " + getSize() + ") " + super.describeState(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "java", 6 | "request": "launch", 7 | "name": "Client", 8 | "presentation": { 9 | "group": "Mod Development - mechano", 10 | "order": 0 11 | }, 12 | "projectName": "mechano", 13 | "mainClass": "net.neoforged.devlaunch.Main", 14 | "args": [ 15 | "@D:\\Projects\\mechano\\build\\moddev\\clientRunProgramArgs.txt" 16 | ], 17 | "vmArgs": [ 18 | "@D:\\Projects\\mechano\\build\\moddev\\clientRunVmArgs.txt", 19 | "-Dfml.modFolders\u003dmechano%%D:\\Projects\\mechano\\bin\\main" 20 | ], 21 | "cwd": "${workspaceFolder}\\run", 22 | "env": {}, 23 | "console": "internalConsole", 24 | "shortenCommandLine": "none" 25 | }, 26 | { 27 | "type": "java", 28 | "request": "launch", 29 | "name": "Data", 30 | "presentation": { 31 | "group": "Mod Development - mechano", 32 | "order": 1 33 | }, 34 | "projectName": "mechano", 35 | "mainClass": "net.neoforged.devlaunch.Main", 36 | "args": [ 37 | "@D:\\Projects\\mechano\\build\\moddev\\dataRunProgramArgs.txt" 38 | ], 39 | "vmArgs": [ 40 | "@D:\\Projects\\mechano\\build\\moddev\\dataRunVmArgs.txt", 41 | "-Dfml.modFolders\u003dmechano%%D:\\Projects\\mechano\\bin\\main" 42 | ], 43 | "cwd": "${workspaceFolder}\\run", 44 | "env": {}, 45 | "console": "internalConsole", 46 | "shortenCommandLine": "none" 47 | }, 48 | { 49 | "type": "java", 50 | "request": "launch", 51 | "name": "GameTestServer", 52 | "presentation": { 53 | "group": "Mod Development - mechano", 54 | "order": 2 55 | }, 56 | "projectName": "mechano", 57 | "mainClass": "net.neoforged.devlaunch.Main", 58 | "args": [ 59 | "@D:\\Projects\\mechano\\build\\moddev\\gameTestServerRunProgramArgs.txt" 60 | ], 61 | "vmArgs": [ 62 | "@D:\\Projects\\mechano\\build\\moddev\\gameTestServerRunVmArgs.txt", 63 | "-Dfml.modFolders\u003dmechano%%D:\\Projects\\mechano\\bin\\main" 64 | ], 65 | "cwd": "${workspaceFolder}\\run", 66 | "env": {}, 67 | "console": "internalConsole", 68 | "shortenCommandLine": "none" 69 | }, 70 | { 71 | "type": "java", 72 | "request": "launch", 73 | "name": "Server", 74 | "presentation": { 75 | "group": "Mod Development - mechano", 76 | "order": 3 77 | }, 78 | "projectName": "mechano", 79 | "mainClass": "net.neoforged.devlaunch.Main", 80 | "args": [ 81 | "@D:\\Projects\\mechano\\build\\moddev\\serverRunProgramArgs.txt" 82 | ], 83 | "vmArgs": [ 84 | "@D:\\Projects\\mechano\\build\\moddev\\serverRunVmArgs.txt", 85 | "-Dfml.modFolders\u003dmechano%%D:\\Projects\\mechano\\bin\\main" 86 | ], 87 | "cwd": "${workspaceFolder}\\run", 88 | "env": {}, 89 | "console": "internalConsole", 90 | "shortenCommandLine": "none" 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/hitbox/corner_end_b.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [1.5, 11.5, 15], 6 | "to": [4.5, 14.5, 16], 7 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 9]}, 8 | "color": 0, 9 | "faces": { 10 | "north": {"uv": [0, 0, 3.5, 3.5], "rotation": 270, "texture": "#missing"}, 11 | "east": {"uv": [26, 57, 30, 58], "rotation": 270, "texture": "#missing"}, 12 | "south": {"uv": [26, 57, 30, 61], "rotation": 90, "texture": "#missing"}, 13 | "west": {"uv": [26, 60, 30, 61], "rotation": 270, "texture": "#missing"}, 14 | "up": {"uv": [26, 57, 27, 61], "rotation": 90, "texture": "#missing"}, 15 | "down": {"uv": [29, 57, 30, 61], "rotation": 90, "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [11.5, 1.5, 15], 20 | "to": [14.5, 4.5, 16], 21 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 9]}, 22 | "color": 0, 23 | "faces": { 24 | "north": {"uv": [0, 0, 3.5, 3.5], "texture": "#missing"}, 25 | "east": {"uv": [29, 57, 30, 61], "texture": "#missing"}, 26 | "south": {"uv": [26, 57, 30, 61], "texture": "#missing"}, 27 | "west": {"uv": [26, 57, 27, 61], "texture": "#missing"}, 28 | "up": {"uv": [26, 57, 30, 58], "rotation": 180, "texture": "#missing"}, 29 | "down": {"uv": [26, 60, 30, 61], "rotation": 180, "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [10, 0, 0], 34 | "to": [16, 12, 15], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, 36 | "color": 2, 37 | "faces": { 38 | "north": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 39 | "east": {"uv": [44, 26, 28, 32], "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 41 | "west": {"uv": [40, 56, 55, 50], "texture": "#missing"}, 42 | "up": {"uv": [16, 22, 30, 16], "rotation": 90, "texture": "#missing"}, 43 | "down": {"uv": [13, 31, 28, 37], "rotation": 90, "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [4, 4, 0], 48 | "to": [10, 10, 15], 49 | "color": 9, 50 | "faces": { 51 | "north": {"uv": [0, 0, 6, 6], "texture": "#missing"}, 52 | "east": {"uv": [0, 0, 15, 6], "texture": "#missing"}, 53 | "south": {"uv": [0, 0, 6, 6], "texture": "#missing"}, 54 | "west": {"uv": [0, 0, 15, 6], "texture": "#missing"}, 55 | "up": {"uv": [0, 0, 6, 15], "texture": "#missing"}, 56 | "down": {"uv": [0, 0, 6, 15], "texture": "#missing"} 57 | } 58 | }, 59 | { 60 | "from": [0, 10, 0], 61 | "to": [12, 16, 15], 62 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, 63 | "color": 1, 64 | "faces": { 65 | "north": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 66 | "east": {"uv": [0, 0, 14, 6], "texture": "#missing"}, 67 | "south": {"uv": [0, 0, 0, 0], "texture": "#missing"}, 68 | "west": {"uv": [13, 37, 28, 31], "texture": "#missing"}, 69 | "up": {"uv": [28, 26, 44, 32], "rotation": 90, "texture": "#missing"}, 70 | "down": {"uv": [54, 56, 40, 50], "rotation": 90, "texture": "#missing"} 71 | } 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/MechanoGroups.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano; 2 | 3 | import com.quattage.mechano.foundation.CreativeTabOverridable; 4 | 5 | import net.minecraft.core.registries.Registries; 6 | import net.minecraft.network.chat.Component; 7 | import net.minecraft.world.item.CreativeModeTab; 8 | import net.minecraft.world.item.CreativeModeTabs; 9 | import net.minecraft.world.item.Item; 10 | import net.minecraft.world.item.Items; 11 | import net.minecraft.world.item.CreativeModeTab.DisplayItemsGenerator; 12 | import net.minecraft.world.item.CreativeModeTab.ItemDisplayParameters; 13 | import net.minecraft.world.item.CreativeModeTab.Output; 14 | import net.neoforged.bus.api.IEventBus; 15 | import net.neoforged.neoforge.registries.DeferredHolder; 16 | import net.neoforged.neoforge.registries.DeferredRegister; 17 | 18 | public class MechanoGroups { 19 | 20 | private static final DeferredRegister TAB_REGISTRY = DeferredRegister. 21 | create(Registries.CREATIVE_MODE_TAB, Mechano.ID); 22 | 23 | public static final DeferredHolder BASE = 24 | TAB_REGISTRY.register("base", () -> CreativeModeTab.builder() 25 | .withTabsBefore(CreativeModeTabs.SPAWN_EGGS) 26 | .icon(() -> MechanoBlocks.CONNECTOR_SINGLE.asItem().getDefaultInstance()) 27 | .title(Component.translatable("itemGroup." + Mechano.ID + ".base")) 28 | .displayItems(new GroupExclusionsGenerator(MechanoGroups.BASE)) 29 | .build() 30 | ); 31 | 32 | public static void register(IEventBus modBus) { 33 | TAB_REGISTRY.register(modBus); 34 | Mechano.LOGGER.debug("registering groups"); 35 | } 36 | 37 | private static class GroupExclusionsGenerator implements DisplayItemsGenerator { 38 | 39 | private final DeferredHolder tab; 40 | 41 | public GroupExclusionsGenerator(DeferredHolder tab) { 42 | this.tab = tab; 43 | } 44 | 45 | @Override 46 | public void accept(ItemDisplayParameters parameters, Output output) { 47 | Mechano.REGISTRATE.getAll(Registries.ITEM).forEach(entry -> { 48 | Item item = entry.get(); 49 | if(item == Items.AIR) return; 50 | if(!CreativeTabOverridable.belongsTo(item, tab)) return; 51 | output.accept(item); 52 | }); 53 | } 54 | 55 | // private ReferenceLinkedOpenHashSet collectBlocks() { 56 | // for(RegistryEntry blockEntry : Mechano.REGISTRATE.getAll(Registries.BLOCK)) { 57 | // if(!CreateRegistrate.isInCreativeTab(blockEntry, tab)) continue; 58 | // Item blockItem = blockEntry.get().asItem(); 59 | // if(blockItem == Items.AIR) continue; 60 | // if(!CreativeTabOverridable.belongsTo(blockItem, tab)) continue; 61 | // collectedItems.add(blockItem); 62 | // } 63 | // return collectedItems; 64 | // } 65 | 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/SimpleOrientedBlock.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block; 2 | 3 | import com.quattage.mechano.foundation.block.orientation.CombinedOrientation; 4 | import com.quattage.mechano.foundation.block.orientation.SimpleOrientation; 5 | import com.simibubi.create.content.equipment.wrench.IWrenchable; 6 | 7 | import net.minecraft.core.Direction; 8 | import net.minecraft.world.InteractionResult; 9 | import net.minecraft.world.item.context.BlockPlaceContext; 10 | import net.minecraft.world.item.context.UseOnContext; 11 | import net.minecraft.world.level.Level; 12 | import net.minecraft.world.level.block.Block; 13 | import net.minecraft.world.level.block.state.BlockState; 14 | import net.minecraft.world.level.block.state.StateDefinition; 15 | import net.minecraft.world.level.block.state.properties.EnumProperty; 16 | 17 | public class SimpleOrientedBlock extends Block implements IWrenchable { 18 | 19 | public static final EnumProperty ORIENTATION = EnumProperty.create("orientation", SimpleOrientation.class); //accomodates for up and down PER CARDINAL, ex. UP_NORTH, or DOWN_EAST 20 | 21 | public SimpleOrientedBlock(Properties pProperties) { 22 | super(pProperties); 23 | this.registerDefaultState(this.stateDefinition.any() 24 | .setValue(ORIENTATION, SimpleOrientation.UP_X)); 25 | } 26 | 27 | @Override 28 | protected void createBlockStateDefinition(StateDefinition.Builder builder) { 29 | builder.add(ORIENTATION); 30 | } 31 | 32 | @Override 33 | public InteractionResult onWrenched(BlockState state, UseOnContext context) { 34 | Level world = context.getLevel(); 35 | SimpleOrientation rotatedOrient = SimpleOrientation.cycle(state.getValue(ORIENTATION)); 36 | BlockState rotated = state.setValue(ORIENTATION, rotatedOrient); 37 | 38 | if(!rotated.canSurvive(world, context.getClickedPos())) 39 | return InteractionResult.PASS; 40 | 41 | context.getLevel().setBlockAndUpdate(context.getClickedPos(), 42 | Block.updateFromNeighbourShapes(rotated, context.getLevel(), 43 | context.getClickedPos())); 44 | 45 | if(world.getBlockState(context.getClickedPos()) != state) 46 | IWrenchable.playRotateSound(world, context.getClickedPos()); 47 | 48 | return InteractionResult.SUCCESS; 49 | } 50 | 51 | @Override 52 | public BlockState getStateForPlacement(BlockPlaceContext context) { 53 | Direction orientation = context.getClickedFace(); 54 | Direction followingDir = CombinedOrientation.getClickedQuadrant(context, orientation, true); 55 | 56 | if(orientation == followingDir) followingDir = context.getHorizontalDirection(); 57 | if(orientation.getAxis() == followingDir.getAxis()) followingDir = followingDir.getClockWise(); 58 | if(context.getPlayer().isCrouching()) orientation = orientation.getOpposite(); 59 | 60 | return this.defaultBlockState().setValue(ORIENTATION, 61 | SimpleOrientation.combine(orientation, followingDir.getAxis())); 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/grid/solver/NodalSolver.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.grid.solver; 2 | 3 | import java.util.Locale; 4 | 5 | import com.google.common.base.Stopwatch; 6 | import com.quattage.mechano.Mechano; 7 | import com.quattage.mechano.api.ServerGrid; 8 | import com.quattage.mechano.api.grid.topology.Circuit; 9 | 10 | import net.minecraft.util.StringRepresentable; 11 | 12 | /** 13 | * A NodalSolver is an object that can be used to solve the Grid API's 14 | * voltage smatrix using a discrete implementation. 15 | */ 16 | public interface NodalSolver { 17 | 18 | double EPSILON = 0.001d; 19 | int STEP_LIMIT = 255; 20 | Stopwatch profiler = Stopwatch.createUnstarted(); 21 | 22 | /** 23 | * Runs this solver until completion, either 24 | * when the iteration limit exceeds STEP_LIMIT 25 | * or until the residual has been reduced to a value below 26 | * EPSILON 27 | * @param snapshot 28 | * @return FINISHED_SOLVED_EARLY or FINISH_SOLVED_LATE if convergence was reached, see {@link ConvergenceStatus} 29 | */ 30 | default ConvergenceStatus solve(ServerGrid grid) { 31 | grid.setStatus(ConvergenceStatus.UNFINISHED_COMPUTING); 32 | ConvergenceStatus status = null; 33 | if(NodalSolver.profiler != null) { 34 | NodalSolver.profiler.start(); 35 | status = run(grid); 36 | NodalSolver.profiler.reset(); 37 | } else { 38 | status = run(grid); 39 | if(!status.indicatesSuccess()) 40 | Mechano.LOGGER.warn("The active NodalSolver couldn't converge in " + NodalSolver.STEP_LIMIT + " iterations"); 41 | } 42 | grid.setStatus(status); 43 | return status; 44 | } 45 | 46 | /** 47 | * Runs this solver until completeion, either 48 | * when the iteration limit exceeds STEP_LIMIT 49 | * or until the error has been reduced to a value below 50 | * EPSILON 51 | * @param snapshot 52 | * @return SOLVED if convergence was reached 53 | */ 54 | ConvergenceStatus run(ServerGrid grid); 55 | void apply(Circuit circuit); 56 | 57 | /** 58 | * Used when a world is unloaded to ensure that the footprint of this solver is minimized. 59 | */ 60 | void reset(); 61 | 62 | public enum ConvergenceStatus implements StringRepresentable { 63 | FINISHED_SOLVED_EARLY(true), 64 | FINISHED_SOLVED_LATE(true), 65 | FINISHED_LIMIT_REACHED(false), 66 | UNFINISHED_PROBLEMATIC_DATA(false), 67 | UNFINISHED_COMPUTING(false), 68 | UNFINISHED_GENERIC_ERROR(false), 69 | UNFINISHED_UNPOPULATED(false); 70 | private final boolean success; 71 | ConvergenceStatus(boolean success) { this.success = success; } 72 | public boolean indicatesSuccess() { return success; } 73 | @Override public String getSerializedName() { return name().toLowerCase(Locale.ROOT); } 74 | @Override public String toString() { return getSerializedName(); } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/block/VerticallyOrientedBlock.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.block; 2 | 3 | import com.quattage.mechano.foundation.block.orientation.VerticalOrientation; 4 | import com.simibubi.create.content.equipment.wrench.IWrenchable; 5 | import com.simibubi.create.content.kinetics.base.KineticBlockEntity; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.core.Direction; 9 | import net.minecraft.world.InteractionResult; 10 | import net.minecraft.world.item.context.BlockPlaceContext; 11 | import net.minecraft.world.item.context.UseOnContext; 12 | import net.minecraft.world.level.Level; 13 | import net.minecraft.world.level.block.Block; 14 | import net.minecraft.world.level.block.state.BlockState; 15 | import net.minecraft.world.level.block.state.StateDefinition; 16 | import net.minecraft.world.level.block.state.properties.EnumProperty; 17 | import net.minecraft.world.phys.Vec3; 18 | 19 | public class VerticallyOrientedBlock extends Block implements IWrenchable{ 20 | public static final EnumProperty ORIENTATION = EnumProperty.create("orientation", VerticalOrientation.class); 21 | 22 | public VerticallyOrientedBlock(Properties pProperties) { 23 | super(pProperties); 24 | this.registerDefaultState(this.stateDefinition.any() 25 | .setValue(ORIENTATION, VerticalOrientation.WEST_UP)); 26 | } 27 | 28 | @Override 29 | protected void createBlockStateDefinition(StateDefinition.Builder builder) { 30 | builder.add(ORIENTATION); 31 | } 32 | 33 | @Override 34 | public BlockState getStateForPlacement(BlockPlaceContext context) { 35 | Direction localFacing = context.getClickedFace(); 36 | Direction localVertical = getVerticalHalf(context.getClickedPos(), context.getClickLocation()); 37 | if(localFacing.getAxis() == Direction.Axis.Y) 38 | localFacing = context.getHorizontalDirection(); 39 | if(context.getPlayer().isCrouching()) localFacing = localFacing.getOpposite(); 40 | 41 | return this.defaultBlockState().setValue(ORIENTATION, VerticalOrientation.combine(localFacing, localVertical)); 42 | } 43 | 44 | @Override 45 | public InteractionResult onWrenched(BlockState state, UseOnContext context) { 46 | Level world = context.getLevel(); 47 | 48 | BlockState rotated = state.setValue(ORIENTATION, VerticalOrientation.cycle(state.getValue(ORIENTATION))); 49 | 50 | if (!rotated.canSurvive(world, context.getClickedPos())) 51 | return InteractionResult.PASS; 52 | 53 | KineticBlockEntity.switchToBlockState(world, context.getClickedPos(), updateAfterWrenched(rotated, context)); 54 | 55 | if (world.getBlockState(context.getClickedPos()) != state) 56 | IWrenchable.playRotateSound(world, context.getClickedPos()); 57 | 58 | return InteractionResult.SUCCESS; 59 | } 60 | 61 | private Direction getVerticalHalf(BlockPos absolutePos, Vec3 clickedPos) { 62 | double deviation = absolutePos.getY() - clickedPos.y; 63 | if(deviation >= 0.5) return Direction.UP; 64 | return Direction.DOWN; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/numeric/EsoMath.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.numeric; 2 | 3 | import net.minecraft.core.Vec3i; 4 | 5 | /** 6 | * my beautiful util class 7 | */ 8 | public class EsoMath { 9 | 10 | public static long hash64(Object obj) { 11 | if(obj instanceof Vec3i vi) return EsoMath.hash64(vi.getX(), vi.getY(), vi.getZ()); 12 | return EsoMath.hash64(System.identityHashCode(obj)); 13 | } 14 | 15 | public static long hash64(int... members) { 16 | // start with random seed and acculumate 17 | long h = 712857823; 18 | for(int x = 0; x < members.length; x++) 19 | h = h * 31 + members[x]; 20 | // shift pseudorandomly to achieve overflow distribution 21 | h ^= (h >>> 33); 22 | h *= 0xff51afd7ed558ccdL; 23 | h ^= (h >>> 33); 24 | h *= 0xc4ceb9fe1a85ec53L; 25 | h ^= (h >>> 33); 26 | return h; 27 | } 28 | 29 | private static final float easeconst = (2f * (float)Math.PI) / 3f; 30 | public static float easeInElastic(float x) { 31 | return x <= 0f ? 0f : x >= 1f ? 1f : (float)Math.pow(2f, -10f * x * Math.sin((x * 10f - 0.75f) * EsoMath.easeconst)) + 1f; 32 | } 33 | public static float easeOutElastic(float x) { 34 | return x <= 0f ? 0f : x >= 1f ? 1f : (float)-Math.pow(2f, 10f * x - 10) * (float)Math.sin((x * 10f - 10.75f) * EsoMath.easeconst); 35 | } 36 | 37 | public static double innerProduct(double[] a, double[] b) { 38 | double sum = 0; 39 | for(int x = 0; x < a.length; x++) sum += a[x] * b[x]; 40 | return sum; 41 | } 42 | 43 | public static long quadShort2Long(short a, short b, short c, short d) { 44 | return 45 | ((long)(a & 0xFFFF) << 48) | 46 | ((long)(b & 0xFFFF) << 32) | 47 | ((long)(c & 0xFFFF) << 16) | 48 | ((long)(d & 0xFFFF)); 49 | } 50 | 51 | public static short long2shortA(long l) { 52 | return (short)(l >> 48); 53 | } 54 | 55 | public static short long2shortB(long l) { 56 | return (short)(l >> 32); 57 | } 58 | 59 | public static short long2shortC(long l) { 60 | return (short)(l >> 16); 61 | } 62 | 63 | public static short long2shortD(long l) { 64 | return (short)(l >> 0); 65 | } 66 | 67 | public static short[] long2QuadShort(long l) { 68 | return new short[] { 69 | EsoMath.long2shortA(l), 70 | EsoMath.long2shortB(l), 71 | EsoMath.long2shortC(l), 72 | EsoMath.long2shortD(l) 73 | }; 74 | } 75 | 76 | public static int dualShort2Int(short a, short b) { 77 | return 78 | ((int)(a & 0xFFFF) << 16) | 79 | ((int)(b & 0xFFFF)); 80 | } 81 | 82 | public static short int2shortA(int i) { 83 | return (short)(i >> 16); 84 | } 85 | 86 | public static short int2shortB(int i) { 87 | return (short)(i >> 0); 88 | } 89 | 90 | public static short[] int2DualShort(int i) { 91 | return new short[] { 92 | EsoMath.int2shortA(i), 93 | EsoMath.int2shortB(i) 94 | }; 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/foundation/tracking/GridIdentifiable.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.foundation.tracking; 2 | 3 | import java.util.Collection; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | import com.quattage.mechano.api.Grid; 10 | import com.quattage.mechano.api.grid.Griddable; 11 | 12 | import net.minecraft.server.level.ServerLevel; 13 | import net.minecraft.server.level.ServerPlayer; 14 | import net.minecraft.world.level.LevelReader; 15 | 16 | public interface GridIdentifiable { 17 | 18 | // TODO make this use a stream instead because this could have really bad iteration performance in worst case scenarios 19 | static Set collectTrackers(ServerLevel world, Collection> objs) { 20 | Set senders = new HashSet<>(); 21 | for(ServerPlayer sp : world.getServer().getPlayerList().getPlayers()) { 22 | for(GridIdentifiable id : objs) { 23 | Griddable source = id.getTargetSource(world); 24 | if(source == null) continue; 25 | if(source.isBeingTrackedBy(sp)) { 26 | senders.add(sp); 27 | break; 28 | } 29 | } 30 | } 31 | return senders; 32 | } 33 | 34 | static Set collectTrackers(ServerLevel world, GridIdentifiable... objs) { 35 | Set senders = new HashSet<>(); 36 | for(ServerPlayer sp : world.getServer().getPlayerList().getPlayers()) { 37 | for(GridIdentifiable id : objs) { 38 | Griddable source = id.getTargetSource(world); 39 | if(source == null) continue; 40 | if(source.isBeingTrackedBy(sp)) { 41 | senders.add(sp); 42 | break; 43 | } 44 | } 45 | } 46 | return senders; 47 | } 48 | 49 | /** 50 | * Provides a (new or pre-existing) {@link GridUUID} instance 51 | * that points towards this object. Can be used by the {@link Grid} 52 | * to look this object up.

53 | * For API users: Use {@link #getUUIDSafe() the checked version} 54 | * of this method instead. 55 | * @return The UUID associated with this identifiable object. 56 | * @see #getUUIDSafe() 57 | */ 58 | T getUUID(); 59 | 60 | /** 61 | * Provides a (new or pre-existing) {@link GridUUID} instance 62 | * that points towards this object. Can be used by the {@link Grid} 63 | * to look this object up.

64 | * This method will throw exceptions for null or invalid returns. 65 | * @return The UUID associated with this identifiable object. Will never be null 66 | */ 67 | @ApiStatus.NonExtendable 68 | default T getUUIDSafe() { 69 | T uuid = getUUID(); 70 | if(uuid == null) 71 | throw new NullPointerException("GridIdentifiable '" + this.getClass().getSimpleName() + " failed to provide a vlaid UUID! (got " + uuid + ")"); 72 | return uuid; 73 | } 74 | 75 | Griddable getTargetSource(LevelReader world); 76 | } 77 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/catenary/EntropyTracker.java: -------------------------------------------------------------------------------- 1 | package com.quattage.mechano.api.catenary; 2 | 3 | import org.joml.Vector3f; 4 | 5 | import com.quattage.mechano.api.catenary.model.CatenaryModel; 6 | 7 | /** 8 | * A helper class for determining whether or not a verlet-based 9 | * catenary simulation has reached a state of minimum potential 10 | * energy. This class is designed to be instantiated by 11 | * {@link CatenaryModel instances} for convenience. 12 | */ 13 | public class EntropyTracker { 14 | 15 | // TODO this class is LOD unfriendly and a little expensive to store in every single catenary instance 16 | 17 | private int tick = 0; 18 | private float avgVelocity = 0f; 19 | private float previousAccumulatedError = 0; 20 | private float accumulatedError = 0; 21 | 22 | public EntropyTracker() {} 23 | 24 | public boolean isResting() { 25 | if(avgVelocity < Catenaries.renderPipeline().getRestitutionSpeed() && (Math.abs(accumulatedError - previousAccumulatedError) < Catenaries.renderPipeline().getRestitutionEpsilon())) { 26 | if(tick > 42) return true; 27 | tick++; 28 | return false; 29 | } 30 | tick = 0; 31 | return false; 32 | } 33 | 34 | public void accumulate(float error) { 35 | accumulatedError += error; 36 | } 37 | 38 | public void walk(float newError) { 39 | previousAccumulatedError = accumulatedError; 40 | accumulatedError = newError; 41 | } 42 | 43 | public void walk(float newError, int steps) { 44 | previousAccumulatedError = accumulatedError; 45 | accumulatedError = (newError / (float)(steps * Catenaries.renderPipeline().getSolverSteps())); 46 | this.avgVelocity /= (float)steps; 47 | } 48 | 49 | public void apply(Vector3f velocity) { 50 | this.avgVelocity += velocity.length(); 51 | } 52 | 53 | /** 54 | * A catenary is considered to be cascading when it receives a change 55 | * in velocity that is too great, or if its accumulated constraint error 56 | * is too high. This can occur in cases where catenary simulations 57 | * become unstable or too long/complex, where they're unable to resolve 58 | * to a stable output. In cases like this, steps should be taken to ensure 59 | * that the catenary is removed from the world before it causes extreme 60 | * visual artifacts that are generally unpleasant, but may also affect 61 | * people with photosensitivity. 62 | * @return true if this EntropyTracker contains data that 63 | * suggests its instantiating catenary is cascading. 64 | */ 65 | public boolean isCascading() { 66 | return avgVelocity > 1e10 || Float.isNaN(avgVelocity) || accumulatedError > 500; 67 | } 68 | 69 | public void softReset() { 70 | this.avgVelocity = 0; 71 | } 72 | 73 | public void reset() { 74 | this.avgVelocity = 0; 75 | this.tick = 0; 76 | this.previousAccumulatedError = 0; 77 | this.accumulatedError = 0; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "RestitutionTracker[" + avgVelocity + "m/t, " + accumulatedError + " :: " + tick + "t]"; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/quattage/mechano/api/blockEntity/renderer/GriddableBlockEntityRenderer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.quattage.mechano.api.blockEntity.renderer; 3 | 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import com.quattage.mechano.api.blockEntity.GriddableBlockEntity; 6 | import com.quattage.mechano.api.switchboard.JackSelector; 7 | 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.player.LocalPlayer; 10 | import net.minecraft.client.renderer.MultiBufferSource; 11 | import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; 12 | import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider.Context; 13 | import net.minecraft.world.entity.ai.attributes.Attributes; 14 | import net.minecraft.world.phys.AABB; 15 | 16 | public class GriddableBlockEntityRenderer implements BlockEntityRenderer { 17 | 18 | public GriddableBlockEntityRenderer(Context context) {} 19 | 20 | @Override 21 | public void render(T be, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, 22 | int packedLight, int packedOverlay) { 23 | LocalPlayer player = Minecraft.getInstance().player; 24 | if(player == null) return; 25 | double reach = player.getAttributes().getValue(Attributes.ENTITY_INTERACTION_RANGE); 26 | tickAnchors(player, be, reach); 27 | // renderMovingWires(be, bufferSource, poseStack, partialTick); 28 | } 29 | 30 | /** 31 | * This method continuously evaluates the visibility of 32 | * {@link AnchorPoint AnchorPoints} within this PGBE. 33 | *

34 | * This is done in the renderer for a few reasons: 35 | *

    36 | *
  • This process only needs to occur on the client
  • 37 | *
  • BERs have built-in frustum culling
  • 38 | *
  • This code is executed at the framerate of the game rather than a fixed rate
  • 39 | *
40 | * The visibility and interaction status of each anchor is evaluated in the {@link AnchorSelector#INSTANCE Anchor Selector} 41 | * @param be 42 | */ 43 | public void tickAnchors(LocalPlayer player, T be, double reach) { 44 | be.provideTerminus().forEach(joint -> { 45 | JackSelector.getInstance().trackForThisFrame(player, be, joint); 46 | }); 47 | } 48 | 49 | // public void renderMovingWires(T be, MultiBufferSource bufferSource, PoseStack matrixStack, float pTicks) { 50 | // be.forEachCatenary(cat -> { 51 | // if(!WindManager.INSTANCE.isEnabled() && !cat.isMoving(be.getLevel())) return; 52 | // cat.render(be, bufferSource, matrixStack, pTicks); 53 | // }); 54 | // } 55 | 56 | @Override 57 | public AABB getRenderBoundingBox(T be) { 58 | return be.getRenderBoundingBox(); 59 | } 60 | 61 | // @Override 62 | // public boolean shouldRender(T be, Vec3 cameraPos) { 63 | // if(be.getSurrogate() != null && be.getSurrogate().isSynced()) return true; 64 | // return Vec3.atCenterOf(be.getBlockPos()).closerThan(cameraPos, (double)this.getViewDistance()); 65 | // } 66 | 67 | // @Override 68 | // public boolean shouldRenderOffScreen(T be) { 69 | // return be.getSurrogate() != null && be.getSurrogate().isSynced(); 70 | // } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/stator/small_stator/base_end_a.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [64, 64], 4 | "textures": { 5 | "0": "mechano:block/stator/base", 6 | "particle": "mechano:block/stator/base" 7 | }, 8 | "elements": [ 9 | { 10 | "from": [0, 0, 1], 11 | "to": [16, 7, 16], 12 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 13 | "faces": { 14 | "north": {"uv": [0, 13.25, 4, 15], "texture": "#0"}, 15 | "east": {"uv": [7, 0, 3.25, 1.75], "texture": "#0"}, 16 | "west": {"uv": [3.25, 0, 7, 1.75], "texture": "#0"}, 17 | "up": {"uv": [3.75, 16, 0, 12], "rotation": 90, "texture": "#0"}, 18 | "down": {"uv": [7, 1.5, 3.25, 5.5], "rotation": 90, "texture": "#0"} 19 | } 20 | }, 21 | { 22 | "from": [0, 7, 4], 23 | "to": [3, 9, 12], 24 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 25 | "faces": { 26 | "north": {"uv": [2.5, 16, 3, 15.25], "rotation": 90, "texture": "#0"}, 27 | "south": {"uv": [1, 15.25, 1.5, 16], "rotation": 90, "texture": "#0"}, 28 | "west": {"uv": [1, 15.5, 3, 16], "texture": "#0"}, 29 | "up": {"uv": [1, 15.25, 3, 16], "rotation": 90, "texture": "#0"} 30 | } 31 | }, 32 | { 33 | "from": [13, 7, 4], 34 | "to": [16, 9, 12], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 36 | "faces": { 37 | "north": {"uv": [2.5, 12.75, 3, 12], "rotation": 90, "texture": "#0"}, 38 | "east": {"uv": [1, 12.5, 3, 12], "texture": "#0"}, 39 | "south": {"uv": [1, 12, 1.5, 12.75], "rotation": 90, "texture": "#0"}, 40 | "up": {"uv": [1, 12.75, 3, 12], "rotation": 270, "texture": "#0"} 41 | } 42 | }, 43 | { 44 | "from": [3, 7, 0], 45 | "to": [13, 10, 16], 46 | "faces": { 47 | "north": {"uv": [6.5, 12, 9, 12.75], "texture": "#0"}, 48 | "east": {"uv": [5.5, 12, 6.25, 16], "rotation": 90, "texture": "#0"}, 49 | "west": {"uv": [4, 12, 4.75, 16], "rotation": 90, "texture": "#0"}, 50 | "up": {"uv": [4, 12, 6.5, 16], "texture": "#0"}, 51 | "down": {"uv": [4, 12, 6.5, 16], "texture": "#0"} 52 | } 53 | }, 54 | { 55 | "from": [6, 4, 0], 56 | "to": [10, 7, 16], 57 | "faces": { 58 | "north": {"uv": [6.5, 12, 7.5, 12.75], "texture": "#0"}, 59 | "east": {"uv": [4, 12, 4.75, 16], "rotation": 90, "texture": "#0"}, 60 | "west": {"uv": [4, 12, 4.75, 16], "rotation": 90, "texture": "#0"}, 61 | "down": {"uv": [4, 12, 5, 16], "texture": "#0"} 62 | } 63 | } 64 | ], 65 | "display": { 66 | "thirdperson_righthand": { 67 | "rotation": [75, 45, 0], 68 | "translation": [0, 2.5, 0], 69 | "scale": [0.375, 0.375, 0.375] 70 | }, 71 | "thirdperson_lefthand": { 72 | "rotation": [75, 45, 0], 73 | "translation": [0, 2.5, 0], 74 | "scale": [0.375, 0.375, 0.375] 75 | }, 76 | "firstperson_righthand": { 77 | "rotation": [0, 45, 0], 78 | "scale": [0.4, 0.4, 0.4] 79 | }, 80 | "firstperson_lefthand": { 81 | "rotation": [0, 225, 0], 82 | "scale": [0.4, 0.4, 0.4] 83 | }, 84 | "ground": { 85 | "translation": [0, 3, 0], 86 | "scale": [0.25, 0.25, 0.25] 87 | }, 88 | "gui": { 89 | "rotation": [30, 225, 0], 90 | "scale": [0.625, 0.625, 0.625] 91 | }, 92 | "head": { 93 | "translation": [0, 11, 0] 94 | }, 95 | "fixed": { 96 | "scale": [0.5, 0.5, 0.5] 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mechano/models/block/test_axis/base_side.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "elements": [ 4 | { 5 | "from": [0, 13, 1], 6 | "to": [5, 15, 3], 7 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 8 | "color": 3, 9 | "faces": { 10 | "north": {"uv": [0, 0, 2, 5], "rotation": 270, "texture": "#missing"}, 11 | "east": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 12 | "south": {"uv": [0, 0, 2, 5], "rotation": 90, "texture": "#missing"}, 13 | "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 14 | "up": {"uv": [0, 0, 2, 5], "rotation": 90, "texture": "#missing"}, 15 | "down": {"uv": [0, 0, 2, 5], "rotation": 90, "texture": "#missing"} 16 | } 17 | }, 18 | { 19 | "from": [0, 1, 13], 20 | "to": [2, 3, 15], 21 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 22 | "color": 5, 23 | "faces": { 24 | "north": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#missing"}, 25 | "east": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 26 | "south": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 27 | "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 28 | "up": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 29 | "down": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [7, 7, 7], 34 | "to": [9, 9, 9], 35 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 36 | "color": 9, 37 | "faces": { 38 | "north": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#missing"}, 39 | "east": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 41 | "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 42 | "up": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 43 | "down": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [10, 13, 13], 48 | "to": [12, 15, 15], 49 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 50 | "color": 6, 51 | "faces": { 52 | "north": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#missing"}, 53 | "east": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 54 | "south": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 55 | "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 56 | "up": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 57 | "down": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"} 58 | } 59 | }, 60 | { 61 | "from": [5, 1, 1], 62 | "to": [7, 3, 3], 63 | "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, 64 | "color": 6, 65 | "faces": { 66 | "north": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#missing"}, 67 | "east": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 68 | "south": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 69 | "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 70 | "up": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"}, 71 | "down": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#missing"} 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /src/main/resources/data/mechano/hitboxes/connector/connector_single.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "texture_size": [32, 32], 4 | "elements": [ 5 | { 6 | "from": [2, 0, 3], 7 | "to": [14, 4, 13], 8 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 9 | "color": 4, 10 | "faces": { 11 | "north": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"}, 12 | "east": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"}, 13 | "south": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"}, 14 | "west": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"}, 15 | "up": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"}, 16 | "down": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"} 17 | } 18 | }, 19 | { 20 | "from": [5.5, 6, 5.5], 21 | "to": [10.5, 16, 10.5], 22 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 23 | "color": 6, 24 | "faces": { 25 | "north": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 26 | "east": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 27 | "south": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 28 | "west": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 29 | "up": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#missing"} 30 | } 31 | }, 32 | { 33 | "from": [4, 12.95, 4], 34 | "to": [12, 14.95, 12], 35 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 36 | "color": 8, 37 | "faces": { 38 | "north": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 39 | "east": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 40 | "south": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 41 | "west": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 42 | "up": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#missing"}, 43 | "down": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"} 44 | } 45 | }, 46 | { 47 | "from": [4, 4, 4], 48 | "to": [12, 9, 12], 49 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 50 | "color": 3, 51 | "faces": { 52 | "north": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 53 | "east": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 54 | "south": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 55 | "west": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 56 | "up": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#missing"}, 57 | "down": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"} 58 | } 59 | }, 60 | { 61 | "from": [4, 10, 4], 62 | "to": [12, 12, 12], 63 | "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, 64 | "color": 3, 65 | "faces": { 66 | "north": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 67 | "east": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 68 | "south": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 69 | "west": {"uv": [0, 0, 16, 16], "texture": "#missing"}, 70 | "up": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#missing"}, 71 | "down": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#missing"} 72 | } 73 | } 74 | ], 75 | "display": { 76 | "thirdperson_righthand": { 77 | "translation": [0, 3, 1], 78 | "scale": [0.55, 0.55, 0.55] 79 | }, 80 | "head": { 81 | "rotation": [0, 90, 0], 82 | "translation": [0, 0.25, 0] 83 | } 84 | } 85 | } --------------------------------------------------------------------------------